From fa04feefdcb22f76944475f572d8bad9519aa723 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Tue, 17 Dec 2024 13:05:24 +0100 Subject: [PATCH 01/28] chore: Add sbt plugin to copy from a jar --- project/Build.scala | 1 + project/BuildStdLib.scala | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 project/BuildStdLib.scala diff --git a/project/Build.scala b/project/Build.scala index b67974f4405d..9ba29a042cc1 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -36,6 +36,7 @@ import scala.util.Properties.isJavaAtLeast import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ import org.scalajs.linker.interface.{ModuleInitializer, StandardConfig} +import BuildStdLib.autoImport.* object DottyJSPlugin extends AutoPlugin { import Build._ diff --git a/project/BuildStdLib.scala b/project/BuildStdLib.scala new file mode 100644 index 000000000000..ede4dcec4e69 --- /dev/null +++ b/project/BuildStdLib.scala @@ -0,0 +1,52 @@ +import sbt._ +import sbt.Keys._ +import java.util.jar.JarFile +import java.nio.file.{Files, Paths, StandardCopyOption} + +object BuildStdLib extends AutoPlugin { + + object autoImport { + val library = settingKey[ModuleID]("The jar dependency to copy.") + val jarExtractTarget = settingKey[File]("The target directory to extract jar content.") + val copyJarContent = taskKey[Unit]("Copy the content of the specified jar to the target directory.") + } + + import autoImport._ + + override def projectSettings: Seq[Setting[_]] = Seq( + copyJarContent := { + val log = streams.value.log + val jarDep = library.value + val targetDir = jarExtractTarget.value + + val jarFiles = update.value.matching(moduleFilter(organization = jarDep.organization, name = jarDep.name)) + + if (jarFiles.isEmpty) { + log.error(s"Jar not found for dependency: ${jarDep}") + } else { + val jarFile = jarFiles.head + log.info(s"Extracting ${jarFile} to ${targetDir}") + + extractJarContent(jarFile, targetDir, log) + } + } + ) + + private def extractJarContent(jarFile: File, targetDir: File, log: Logger): Unit = { + val jar = new JarFile(jarFile) + try { + jar.entries().asIterator().forEachRemaining { entry => + val entryPath = targetDir.toPath.resolve(entry.getName) + if (entry.isDirectory) { + Files.createDirectories(entryPath) + } else { + Files.createDirectories(entryPath.getParent) + Files.copy(jar.getInputStream(entry), entryPath, StandardCopyOption.REPLACE_EXISTING) + } + } + log.info(s"Extraction complete: ${targetDir}") + } finally { + jar.close() + } + } +} From 448edd259fa9ae7a6fdafd583fdccd07bb7d92bd Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 16:22:24 +0100 Subject: [PATCH 02/28] chore: enable the stdlib plugin in stdlib --- project/Build.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 9ba29a042cc1..ec0c4f5d09e9 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1106,6 +1106,10 @@ object Build { // Settings shared between scala3-library, scala3-library-bootstrapped and scala3-library-bootstrappedJS lazy val dottyLibrarySettings = Seq( + library := "org.scala-lang" % "scala-library" % "2.13.16", + libraryDependencies += library.value, // TODO: We should not require a dependency to the scala2's stdlib + jarExtractTarget := new File((Compile / target).value + "/scala-3.6.3-RC2/classes"), + //(Compile / compile) := (Compile / compile).dependsOn(copyJarContent).value, (Compile / scalacOptions) ++= Seq( // Needed so that the library sources are visible when `dotty.tools.dotc.core.Definitions#init` is called "-sourcepath", (Compile / sourceDirectories).value.map(_.getAbsolutePath).distinct.mkString(File.pathSeparator), @@ -2418,10 +2422,7 @@ object Build { def asDottyLibrary(implicit mode: Mode): Project = { val base = project.withCommonSettings. - settings( - versionScheme := Some("semver-spec"), - libraryDependencies += "org.scala-lang" % "scala-library" % stdlibVersion, - ). + settings(versionScheme := Some("semver-spec")). settings(dottyLibrarySettings) if (mode == Bootstrapped) { base.settings( From 2e586c310482305f3fcbcb5256a17f8acb5ab5a2 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:27:35 +0100 Subject: [PATCH 03/28] chore: add all the files in scala.annotation --- library/src/scala/annotation/Annotation.scala | 26 +++ .../annotation/ClassfileAnnotation.scala | 20 +++ .../scala/annotation/ConstantAnnotation.scala | 44 +++++ .../scala/annotation/StaticAnnotation.scala | 23 +++ .../src/scala/annotation/TypeConstraint.scala | 26 +++ .../scala/annotation/compileTimeOnly.scala | 33 ++++ library/src/scala/annotation/elidable.scala | 156 ++++++++++++++++++ .../scala/annotation/implicitAmbiguous.scala | 42 +++++ .../scala/annotation/implicitNotFound.scala | 56 +++++++ library/src/scala/annotation/migration.scala | 30 ++++ library/src/scala/annotation/nowarn.scala | 36 ++++ .../src/scala/annotation/showAsInfix.scala | 39 +++++ library/src/scala/annotation/strictfp.scala | 19 +++ library/src/scala/annotation/switch.scala | 33 ++++ library/src/scala/annotation/tailrec.scala | 21 +++ .../src/scala/annotation/unspecialized.scala | 20 +++ library/src/scala/annotation/unused.scala | 26 +++ library/src/scala/annotation/varargs.scala | 19 +++ 18 files changed, 669 insertions(+) create mode 100644 library/src/scala/annotation/Annotation.scala create mode 100644 library/src/scala/annotation/ClassfileAnnotation.scala create mode 100644 library/src/scala/annotation/ConstantAnnotation.scala create mode 100644 library/src/scala/annotation/StaticAnnotation.scala create mode 100644 library/src/scala/annotation/TypeConstraint.scala create mode 100644 library/src/scala/annotation/compileTimeOnly.scala create mode 100644 library/src/scala/annotation/elidable.scala create mode 100644 library/src/scala/annotation/implicitAmbiguous.scala create mode 100644 library/src/scala/annotation/implicitNotFound.scala create mode 100644 library/src/scala/annotation/migration.scala create mode 100644 library/src/scala/annotation/nowarn.scala create mode 100644 library/src/scala/annotation/showAsInfix.scala create mode 100644 library/src/scala/annotation/strictfp.scala create mode 100644 library/src/scala/annotation/switch.scala create mode 100644 library/src/scala/annotation/tailrec.scala create mode 100644 library/src/scala/annotation/unspecialized.scala create mode 100644 library/src/scala/annotation/unused.scala create mode 100644 library/src/scala/annotation/varargs.scala diff --git a/library/src/scala/annotation/Annotation.scala b/library/src/scala/annotation/Annotation.scala new file mode 100644 index 000000000000..a78842cbf1c4 --- /dev/null +++ b/library/src/scala/annotation/Annotation.scala @@ -0,0 +1,26 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * A base class for annotations. + * + * Annotations extending this class directly are not preserved in the classfile. To enable storing + * annotations in the classfile's Scala signature and make it available to Scala reflection and + * other tools, the annotation needs to inherit from [[scala.annotation.StaticAnnotation]]. + * + * Annotation classes defined in Scala are not stored in classfiles in a Java-compatible manner + * and therefore not visible in Java reflection. In order to achieve this, the annotation has to + * be written in Java. + */ +abstract class Annotation diff --git a/library/src/scala/annotation/ClassfileAnnotation.scala b/library/src/scala/annotation/ClassfileAnnotation.scala new file mode 100644 index 000000000000..be3c98b6130a --- /dev/null +++ b/library/src/scala/annotation/ClassfileAnnotation.scala @@ -0,0 +1,20 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** A base class for classfile annotations. These are stored as + * [[https://docs.oracle.com/javase/8/docs/technotes/guides/language/annotations.html Java annotations]] + * in classfiles. + */ +@deprecated("Annotation classes need to be written in Java in order to be stored in classfiles in a Java-compatible manner", "2.13.0") +trait ClassfileAnnotation extends ConstantAnnotation diff --git a/library/src/scala/annotation/ConstantAnnotation.scala b/library/src/scala/annotation/ConstantAnnotation.scala new file mode 100644 index 000000000000..b9a933371cc8 --- /dev/null +++ b/library/src/scala/annotation/ConstantAnnotation.scala @@ -0,0 +1,44 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * Annotation classes extending this trait only accept constant values as arguments. + * + * Note that this trait extends [[StaticAnnotation]], so constant annotations are persisted in the + * classfile. + * + * The implementation requires arguments of constant annotations to be passed as named arguments, + * except if there is a single argument, which then defines the annotation's parameter named + * `value`. + * + * Constant annotations may use default arguments. Note that the internal representation of an + * annotation usage (which is visible for compiler plugins, for example) only contains arguments + * that are explicitly provided. + * + * Constant annotations are not allowed to define auxiliary constructors, and the primary + * constructor is required to have a single parameter list. + * + * Example: + * + * {{{ + * class Ann(value: Int, x: Int = 0) extends scala.annotation.ConstantAnnotation + * class Test { + * def someInt = 0 + * @Ann(value = 0, x = 1) def g = 0 + * @Ann(0) def f = 0 // Internal representation contains `@Ann(value = 0)` + * @Ann(someInt) // error: argument needs to be a compile-time constant + * } + * }}} + */ +trait ConstantAnnotation extends StaticAnnotation diff --git a/library/src/scala/annotation/StaticAnnotation.scala b/library/src/scala/annotation/StaticAnnotation.scala new file mode 100644 index 000000000000..dc0136db70af --- /dev/null +++ b/library/src/scala/annotation/StaticAnnotation.scala @@ -0,0 +1,23 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * A base class for static annotations. These are available to the Scala type checker or Scala + * reflection, even across different compilation units. + * + * Annotation classes defined in Scala are not stored in classfiles in a Java-compatible manner + * and therefore not visible in Java reflection. In order to achieve this, the annotation has to + * be written in Java. + */ +trait StaticAnnotation extends Annotation diff --git a/library/src/scala/annotation/TypeConstraint.scala b/library/src/scala/annotation/TypeConstraint.scala new file mode 100644 index 000000000000..b9b5a62aa3c9 --- /dev/null +++ b/library/src/scala/annotation/TypeConstraint.scala @@ -0,0 +1,26 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** A marker for annotations that, when applied to a type, should be treated + * as a constraint on the annotated type. + * + * A proper constraint should restrict the type based only on information + * mentioned within the type. A Scala compiler can use this assumption to + * rewrite the contents of the constraint as necessary. To contrast, a type + * annotation whose meaning depends on the context where it is written + * down is not a proper constrained type, and this marker should not be + * applied. A Scala compiler will drop such annotations in cases where it + * would rewrite a type constraint. + */ +trait TypeConstraint extends Annotation diff --git a/library/src/scala/annotation/compileTimeOnly.scala b/library/src/scala/annotation/compileTimeOnly.scala new file mode 100644 index 000000000000..e2eb7560b8bf --- /dev/null +++ b/library/src/scala/annotation/compileTimeOnly.scala @@ -0,0 +1,33 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +import scala.annotation.meta._ + +/** + * An annotation that designates that an annottee should not be referred to after + * type checking (which includes macro expansion). + * + * Examples of potential use: + * 1) The annottee can only appear in the arguments of some other macro + * that will eliminate it from the AST during expansion. + * 2) The annottee is a macro and should have been expanded away, + * so if hasn't, something wrong has happened. + * (Comes in handy to provide better support for new macro flavors, + * e.g. macro annotations, that can't be expanded by the vanilla compiler). + * + * @param message the error message to print during compilation if a reference remains + * after type checking + */ +@getter @setter @beanGetter @beanSetter @companionClass @companionMethod +final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/elidable.scala b/library/src/scala/annotation/elidable.scala new file mode 100644 index 000000000000..e15f0de8d9f1 --- /dev/null +++ b/library/src/scala/annotation/elidable.scala @@ -0,0 +1,156 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** An annotation for methods whose bodies may be excluded + * from compiler-generated bytecode. + * + * Behavior is influenced by passing `-Xelide-below ` to `scalac`. + * Calls to methods marked elidable (as well as the method body) will + * be omitted from generated code if the priority given the annotation + * is lower than that given on the command line. + * + * {{{ + * @elidable(123) // annotation priority + * scalac -Xelide-below 456 // command line priority + * }}} + * + * The method call will be replaced with an expression which depends on + * the type of the elided expression. In decreasing order of precedence: + * + * {{{ + * Unit () + * Boolean false + * T <: AnyVal 0 + * T >: Null null + * T >: Nothing Predef.??? + * }}} + * + * Complete example: + * {{{ + * import scala.annotation._, elidable._ + * object Test extends App { + * def expensiveComputation(): Int = { Thread.sleep(1000) ; 172 } + * + * @elidable(WARNING) def warning(msg: String) = println(msg) + * @elidable(FINE) def debug(msg: String) = println(msg) + * @elidable(FINE) def computedValue = expensiveComputation() + * + * warning("Warning! Danger! Warning!") + * debug("Debug! Danger! Debug!") + * println("I computed a value: " + computedValue) + * } + * % scalac example.scala && scala Test + * Warning! Danger! Warning! + * Debug! Danger! Debug! + * I computed a value: 172 + * + * // INFO lies between WARNING and FINE + * % scalac -Xelide-below INFO example.scala && scala Test + * Warning! Danger! Warning! + * I computed a value: 0 + * }}} + * + * Note that only concrete methods can be marked `@elidable`. A non-annotated method + * is not elided, even if it overrides / implements a method that has the annotation. + * + * Also note that the static type determines which annotations are considered: + * + * {{{ + * import scala.annotation._, elidable._ + * class C { @elidable(0) def f(): Unit = ??? } + * object O extends C { override def f(): Unit = println("O.f") } + * object Test extends App { + * O.f() // not elided + * (O: C).f() // elided if compiled with `-Xelide-below 1` + * } + * }}} + * + * Note for Scala 3 users: + * If you're using Scala 3, the annotation exists since Scala 3 uses the Scala 2 + * standard library, but it's unsupported by the Scala 3 compiler. Instead, to + * achieve the same result you'd want to utilize the `inline if` feature to + * introduce behavior that makes a method de facto elided at compile-time. + * {{{ + * type LogLevel = Int + * + * object LogLevel: + * inline val Info = 0 + * inline val Warn = 1 + * inline val Debug = 2 + * + * inline val appLogLevel = LogLevel.Warn + * + * inline def log(msg: String, inline level: LogLevel): Unit = + * inline if (level <= appLogLevel) then println(msg) + * + * log("Warn log", LogLevel.Warn) + * + * log("Debug log", LogLevel. Debug) + * }}} + */ +final class elidable(final val level: Int) extends scala.annotation.ConstantAnnotation + +/** This useless appearing code was necessary to allow people to use + * named constants for the elidable annotation. This is what it takes + * to convince the compiler to fold the constants: otherwise when it's + * time to check an elision level it's staring at a tree like + * {{{ + * (Select(Level, Select(FINEST, Apply(intValue, Nil)))) + * }}} + * instead of the number `300`. + */ +object elidable { + /** The levels `ALL` and `OFF` are confusing in this context because + * the sentiment being expressed when using the annotation is at cross + * purposes with the one being expressed via `-Xelide-below`. This + * confusion reaches its zenith at level `OFF`, where the annotation means + * ''never elide this method'' but `-Xelide-below OFF` is how you would + * say ''elide everything possible''. + * + * With no simple remedy at hand, the issue is now at least documented, + * and aliases `MAXIMUM` and `MINIMUM` are offered. + */ + final val ALL = Int.MinValue // Level.ALL.intValue() + final val FINEST = 300 // Level.FINEST.intValue() + final val FINER = 400 // Level.FINER.intValue() + final val FINE = 500 // Level.FINE.intValue() + final val CONFIG = 700 // Level.CONFIG.intValue() + final val INFO = 800 // Level.INFO.intValue() + final val WARNING = 900 // Level.WARNING.intValue() + final val SEVERE = 1000 // Level.SEVERE.intValue() + final val OFF = Int.MaxValue // Level.OFF.intValue() + + // a couple aliases for the confusing ALL and OFF + final val MAXIMUM = OFF + final val MINIMUM = ALL + + // and we can add a few of our own + final val ASSERTION = 2000 // we should make this more granular + + // for command line parsing so we can use names or ints + val byName: Map[String, Int] = Map( + "FINEST" -> FINEST, + "FINER" -> FINER, + "FINE" -> FINE, + "CONFIG" -> CONFIG, + "INFO" -> INFO, + "WARNING" -> WARNING, + "SEVERE" -> SEVERE, + "ASSERTION" -> ASSERTION, + "ALL" -> ALL, + "OFF" -> OFF, + "MAXIMUM" -> MAXIMUM, + "MINIMUM" -> MINIMUM + ) +} diff --git a/library/src/scala/annotation/implicitAmbiguous.scala b/library/src/scala/annotation/implicitAmbiguous.scala new file mode 100644 index 000000000000..5520c945fef2 --- /dev/null +++ b/library/src/scala/annotation/implicitAmbiguous.scala @@ -0,0 +1,42 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * To customize the error message that's emitted when an implicit search finds + * multiple ambiguous values, annotate at least one of the implicit values + * `@implicitAmbiguous`. Assuming the implicit value is a method with type + * parameters `X1,..., XN`, the error message will be the result of replacing + * all occurrences of `\${Xi}` in the string `msg` with the string representation + * of the corresponding type argument `Ti`. + * + * If more than one `@implicitAmbiguous` annotation is collected, the compiler is + * free to pick any of them to display. + * + * Nice errors can direct users to fix imports or even tell them why code + * intentionally doesn't compile. + * + * {{{ + * trait =!=[C, D] + * + * implicit def neq[E, F] : E =!= F = null + * + * @annotation.implicitAmbiguous("Could not prove \${J} =!= \${J}") + * implicit def neqAmbig1[G, H, J] : J =!= J = null + * implicit def neqAmbig2[I] : I =!= I = null + * + * implicitly[Int =!= Int] + * }}} + */ +@meta.getter +final class implicitAmbiguous(msg: String) extends scala.annotation.ConstantAnnotation diff --git a/library/src/scala/annotation/implicitNotFound.scala b/library/src/scala/annotation/implicitNotFound.scala new file mode 100644 index 000000000000..55a9179a394f --- /dev/null +++ b/library/src/scala/annotation/implicitNotFound.scala @@ -0,0 +1,56 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * To customize the error message that's emitted when an implicit of type + * `C[T1,..., TN]` cannot be found, annotate the class `C` with `@implicitNotFound`. + * Assuming `C` has type parameters `X1, ..., XN`, the error message will be the + * result of replacing all occurrences of `\${Xi}` in the string `msg` with the + * string representation of the corresponding type argument `Ti`. + * The annotation is effectively inherited by subtypes if they are not annotated. + * + * The annotation can also be attached to implicit parameters. In this case, `\${Xi}` + * can refer to type parameters in the current scope. The `@implicitNotFound` message + * on the parameter takes precedence over the one on the parameter's type. + * + * {{{ + * import scala.annotation.implicitNotFound + * + * @implicitNotFound("Could not find an implicit C[\${T}, \${U}]") + * class C[T, U] + * + * class K[A] { + * def m[B](implicit c: C[List[A], B]) = 0 + * def n[B](implicit @implicitNotFound("Specific message for C of list of \${A} and \${B}") c: C[List[A], B]) = 1 + * } + * + * object Test { + * val k = new K[Int] + * k.m[String] + * k.n[String] + * } + * }}} + * + * The compiler issues the following error messages: + * + *
+ * Test.scala:13: error: Could not find an implicit C[List[Int], String]
+ *   k.m[String]
+ *      ^
+ * Test.scala:14: error: Specific message for C of list of Int and String
+ *   k.n[String]
+ *      ^
+ * 
+ */ +final class implicitNotFound(msg: String) extends scala.annotation.ConstantAnnotation diff --git a/library/src/scala/annotation/migration.scala b/library/src/scala/annotation/migration.scala new file mode 100644 index 000000000000..81ef78dbd367 --- /dev/null +++ b/library/src/scala/annotation/migration.scala @@ -0,0 +1,30 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * An annotation that marks a member as having changed semantics + * between versions. This is intended for methods which for one + * reason or another retain the same name and type signature, + * but some aspect of their behavior is different. An illustrative + * examples is Stack.iterator, which reversed from LIFO to FIFO + * order between Scala 2.7 and 2.8. + * + * @param message A message describing the change, which is emitted + * by the compiler if the flag `-Xmigration` indicates a version + * prior to the changedIn version. + * + * @param changedIn The version, in which the behaviour change was + * introduced. + */ +private[scala] final class migration(message: String, changedIn: String) extends scala.annotation.ConstantAnnotation diff --git a/library/src/scala/annotation/nowarn.scala b/library/src/scala/annotation/nowarn.scala new file mode 100644 index 000000000000..a1cc7a056abb --- /dev/null +++ b/library/src/scala/annotation/nowarn.scala @@ -0,0 +1,36 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** An annotation for local warning suppression. + * + * The optional `value` parameter allows selectively silencing messages, see `scalac -Wconf:help` + * for help. Examples: + * + * {{{ + * def f = { + * 1: @nowarn // don't warn "a pure expression does nothing in statement position" + * 2 + * } + * + * @nowarn def f = { 1; deprecated() } // don't warn + * + * @nowarn("msg=pure expression does nothing") + * def f = { 1; deprecated() } // show deprecation warning + * }}} + * + * To ensure that a `@nowarn` annotation actually suppresses a warning, enable `-Xlint:unused` or `-Wunused:nowarn`. + * The unused annotation warning is emitted in category `unused-nowarn` and can be selectively managed + * using `-Wconf:cat=unused-nowarn:s`. + */ +class nowarn(value: String = "") extends ConstantAnnotation diff --git a/library/src/scala/annotation/showAsInfix.scala b/library/src/scala/annotation/showAsInfix.scala new file mode 100644 index 000000000000..9c4ecbe2eea1 --- /dev/null +++ b/library/src/scala/annotation/showAsInfix.scala @@ -0,0 +1,39 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * This annotation configures how Scala prints two-parameter generic types. + * + * By default, types with symbolic names are printed infix; while types without + * them are printed using the regular generic type syntax. + * + * Example of usage: + {{{ + scala> class Map[T, U] + defined class Map + + scala> def foo: Int Map Int = ??? + foo: Map[Int,Int] + + scala> @showAsInfix class Map[T, U] + defined class Map + + scala> def foo: Int Map Int = ??? + foo: Int Map Int + }}} + * + * @param enabled whether to show this type as an infix type operator. + */ +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class showAsInfix(enabled: Boolean = true) extends annotation.StaticAnnotation diff --git a/library/src/scala/annotation/strictfp.scala b/library/src/scala/annotation/strictfp.scala new file mode 100644 index 000000000000..40e297fc4d97 --- /dev/null +++ b/library/src/scala/annotation/strictfp.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** If this annotation is present on a method or its enclosing class, + * the strictfp flag will be emitted. + */ +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class strictfp extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/switch.scala b/library/src/scala/annotation/switch.scala new file mode 100644 index 000000000000..f4af17741fe6 --- /dev/null +++ b/library/src/scala/annotation/switch.scala @@ -0,0 +1,33 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** An annotation to be applied to a match expression. If present, + * the compiler will verify that the match has been compiled to a + * [[https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10 tableswitch or lookupswitch]] + * and issue a warning if it instead compiles into a series of conditional expressions. + * Example usage: +{{{ + val Constant = 'Q' + def tokenMe(ch: Char) = (ch: @switch) match { + case ' ' | '\t' | '\n' => 1 + case 'A' | 'Z' | '$' => 2 + case '5' | Constant => 3 // a non-literal may prevent switch generation: this would not compile + case _ => 4 + } +}}} + * + * Note: for pattern matches with one or two cases, the compiler generates jump instructions. + * Annotating such a match with `@switch` does not issue any warning. + */ +final class switch extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/tailrec.scala b/library/src/scala/annotation/tailrec.scala new file mode 100644 index 000000000000..7b3f80dbf66f --- /dev/null +++ b/library/src/scala/annotation/tailrec.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** A method annotation which verifies that the method will be compiled + * with tail call optimization. + * + * If it is present, the compiler will issue an error if the method cannot + * be optimized into a loop. + */ +final class tailrec extends StaticAnnotation diff --git a/library/src/scala/annotation/unspecialized.scala b/library/src/scala/annotation/unspecialized.scala new file mode 100644 index 000000000000..c0f668a75298 --- /dev/null +++ b/library/src/scala/annotation/unspecialized.scala @@ -0,0 +1,20 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** A method annotation which suppresses the creation of + * additional specialized forms based on enclosing specialized + * type parameters. + */ +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class unspecialized extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/unused.scala b/library/src/scala/annotation/unused.scala new file mode 100644 index 000000000000..270286864822 --- /dev/null +++ b/library/src/scala/annotation/unused.scala @@ -0,0 +1,26 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** Mark an element unused for a given context. + * + * Unused warnings are suppressed for elements known to be unused. + * + * For example, a method parameter may be marked `@unused` + * because the method is designed to be overridden by + * an implementation that does use the parameter. + */ +@meta.getter @meta.setter +class unused(message: String) extends StaticAnnotation { + def this() = this("") +} diff --git a/library/src/scala/annotation/varargs.scala b/library/src/scala/annotation/varargs.scala new file mode 100644 index 000000000000..68d1080a53f8 --- /dev/null +++ b/library/src/scala/annotation/varargs.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** A method annotation which instructs the compiler to generate a + * Java varargs-style forwarder method for interop. This annotation can + * only be applied to methods with repeated parameters. + */ +final class varargs extends scala.annotation.StaticAnnotation From 849d18997e6439bdf8c774960e86fe49866d2f85 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:28:50 +0100 Subject: [PATCH 04/28] chore: add all the files in scala.annotation.meta --- .../scala/annotation/meta/beanGetter.scala | 18 +++++ .../scala/annotation/meta/beanSetter.scala | 18 +++++ .../annotation/meta/companionClass.scala | 22 +++++ .../annotation/meta/companionMethod.scala | 22 +++++ .../annotation/meta/companionObject.scala | 19 +++++ library/src/scala/annotation/meta/field.scala | 18 +++++ .../src/scala/annotation/meta/getter.scala | 18 +++++ .../annotation/meta/languageFeature.scala | 18 +++++ .../src/scala/annotation/meta/package.scala | 80 +++++++++++++++++++ library/src/scala/annotation/meta/param.scala | 18 +++++ .../src/scala/annotation/meta/setter.scala | 18 +++++ 11 files changed, 269 insertions(+) create mode 100644 library/src/scala/annotation/meta/beanGetter.scala create mode 100644 library/src/scala/annotation/meta/beanSetter.scala create mode 100644 library/src/scala/annotation/meta/companionClass.scala create mode 100644 library/src/scala/annotation/meta/companionMethod.scala create mode 100644 library/src/scala/annotation/meta/companionObject.scala create mode 100644 library/src/scala/annotation/meta/field.scala create mode 100644 library/src/scala/annotation/meta/getter.scala create mode 100644 library/src/scala/annotation/meta/languageFeature.scala create mode 100644 library/src/scala/annotation/meta/package.scala create mode 100644 library/src/scala/annotation/meta/param.scala create mode 100644 library/src/scala/annotation/meta/setter.scala diff --git a/library/src/scala/annotation/meta/beanGetter.scala b/library/src/scala/annotation/meta/beanGetter.scala new file mode 100644 index 000000000000..58d37bfb1cde --- /dev/null +++ b/library/src/scala/annotation/meta/beanGetter.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * Consult the documentation in package [[scala.annotation.meta]]. + */ +final class beanGetter extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/beanSetter.scala b/library/src/scala/annotation/meta/beanSetter.scala new file mode 100644 index 000000000000..670c67259fa8 --- /dev/null +++ b/library/src/scala/annotation/meta/beanSetter.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * Consult the documentation in package [[scala.annotation.meta]]. + */ +final class beanSetter extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/companionClass.scala b/library/src/scala/annotation/meta/companionClass.scala new file mode 100644 index 000000000000..0a7b072b521d --- /dev/null +++ b/library/src/scala/annotation/meta/companionClass.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * When defining an implicit class, the Scala compiler creates an implicit + * conversion method for it. Annotations `@companionClass` and `@companionMethod` + * control where an annotation on the implicit class will go. By default, annotations + * on an implicit class end up only on the class. + * + */ +final class companionClass extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/companionMethod.scala b/library/src/scala/annotation/meta/companionMethod.scala new file mode 100644 index 000000000000..2e0080a1d61f --- /dev/null +++ b/library/src/scala/annotation/meta/companionMethod.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * When defining an implicit class, the Scala compiler creates an implicit + * conversion method for it. Annotations `@companionClass` and `@companionMethod` + * control where an annotation on the implicit class will go. By default, annotations + * on an implicit class end up only on the class. + * + */ +final class companionMethod extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/companionObject.scala b/library/src/scala/annotation/meta/companionObject.scala new file mode 100644 index 000000000000..dc817b138707 --- /dev/null +++ b/library/src/scala/annotation/meta/companionObject.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * Currently unused; intended as an annotation target for classes such as case classes + * that automatically generate a companion object + */ +final class companionObject extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/field.scala b/library/src/scala/annotation/meta/field.scala new file mode 100644 index 000000000000..ccd64a0179f7 --- /dev/null +++ b/library/src/scala/annotation/meta/field.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * Consult the documentation in package [[scala.annotation.meta]]. + */ +final class field extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/getter.scala b/library/src/scala/annotation/meta/getter.scala new file mode 100644 index 000000000000..acbc7989c901 --- /dev/null +++ b/library/src/scala/annotation/meta/getter.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * Consult the documentation in package [[scala.annotation.meta]]. + */ +final class getter extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/languageFeature.scala b/library/src/scala/annotation/meta/languageFeature.scala new file mode 100644 index 000000000000..f2d9c7890d47 --- /dev/null +++ b/library/src/scala/annotation/meta/languageFeature.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * An annotation giving particulars for a language feature in object `scala.language`. + */ +final class languageFeature(feature: String, enableRequired: Boolean) extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/package.scala b/library/src/scala/annotation/meta/package.scala new file mode 100644 index 000000000000..ab315e412e07 --- /dev/null +++ b/library/src/scala/annotation/meta/package.scala @@ -0,0 +1,80 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation + +/** + * When defining a field, the Scala compiler creates up to four accessors + * for it: a getter, a setter, and if the field is annotated with + * `@BeanProperty`, a bean getter and a bean setter. + * + * For instance in the following class definition + * + * {{{ + * class C(@myAnnot @BeanProperty var c: Int) + * }}} + * + * there are six entities which can carry the annotation `@myAnnot`: the + * constructor parameter, the generated field and the four accessors. + * + * By default, annotations on (`val`-, `var`- or plain) constructor parameters + * end up on the parameter, not on any other entity. Annotations on fields + * by default only end up on the field. + * + * The meta-annotations in package `scala.annotation.meta` are used + * to control where annotations on fields and class parameters are copied. + * This is done by annotating either the annotation type or the annotation + * class with one or several of the meta-annotations in this package. + * + * ==Annotating the annotation type== + * + * The target meta-annotations can be put on the annotation type when + * instantiating the annotation. In the following example, the annotation + * `@Id` will be added only to the bean getter `getX`. + * + * {{{ + * import javax.persistence.Id + * class A { + * @(Id @beanGetter) @BeanProperty val x = 0 + * } + * }}} + * + * In order to annotate the field as well, the meta-annotation `@field` + * would need to be added. + * + * The syntax can be improved using a type alias: + * + * {{{ + * object ScalaJPA { + * type Id = javax.persistence.Id @beanGetter + * } + * import ScalaJPA.Id + * class A { + * @Id @BeanProperty val x = 0 + * } + * }}} + * + * ==Annotating the annotation class== + * + * For annotations defined in Scala, a default target can be specified + * in the annotation class itself, for example + * + * {{{ + * @getter + * class myAnnotation extends Annotation + * }}} + * + * This only changes the default target for the annotation `myAnnotation`. + * When instantiating the annotation, the target can still be specified + * as described in the last section. + */ +package object meta diff --git a/library/src/scala/annotation/meta/param.scala b/library/src/scala/annotation/meta/param.scala new file mode 100644 index 000000000000..8c69a3b644b1 --- /dev/null +++ b/library/src/scala/annotation/meta/param.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * Consult the documentation in package [[scala.annotation.meta]]. + */ +final class param extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/meta/setter.scala b/library/src/scala/annotation/meta/setter.scala new file mode 100644 index 000000000000..b37979bec597 --- /dev/null +++ b/library/src/scala/annotation/meta/setter.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.meta + +/** + * Consult the documentation in package [[scala.annotation.meta]]. + */ +final class setter extends scala.annotation.StaticAnnotation From cdf3771628c3d20342f1f51e5f393ab3af6f2ead Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:31:26 +0100 Subject: [PATCH 05/28] chore: add all the files in scala.annotation.unchecked --- .../unchecked/uncheckedStable.scala | 21 +++++++++++++++++++ .../unchecked/uncheckedVariance.scala | 17 +++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 library/src/scala/annotation/unchecked/uncheckedStable.scala create mode 100644 library/src/scala/annotation/unchecked/uncheckedVariance.scala diff --git a/library/src/scala/annotation/unchecked/uncheckedStable.scala b/library/src/scala/annotation/unchecked/uncheckedStable.scala new file mode 100644 index 000000000000..12a18d635fe4 --- /dev/null +++ b/library/src/scala/annotation/unchecked/uncheckedStable.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.unchecked + +import scala.annotation.meta.{field, getter} + +/** An annotation for values that are assumed to be stable even though their + * types are volatile. + */ +@getter @field +final class uncheckedStable extends scala.annotation.StaticAnnotation {} diff --git a/library/src/scala/annotation/unchecked/uncheckedVariance.scala b/library/src/scala/annotation/unchecked/uncheckedVariance.scala new file mode 100644 index 000000000000..60f06ad95f7d --- /dev/null +++ b/library/src/scala/annotation/unchecked/uncheckedVariance.scala @@ -0,0 +1,17 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.annotation.unchecked + +/** An annotation for type arguments for which one wants to suppress variance checking. + */ +final class uncheckedVariance extends scala.annotation.StaticAnnotation {} From 0c081cf3607a7fad6b21842fc039b199df1f64e6 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:32:38 +0100 Subject: [PATCH 06/28] chore: add all the files in scala.beans --- library/src/scala/beans/BeanProperty.scala | 38 +++++++++++++++++++ .../src/scala/beans/BooleanBeanProperty.scala | 23 +++++++++++ 2 files changed, 61 insertions(+) create mode 100644 library/src/scala/beans/BeanProperty.scala create mode 100644 library/src/scala/beans/BooleanBeanProperty.scala diff --git a/library/src/scala/beans/BeanProperty.scala b/library/src/scala/beans/BeanProperty.scala new file mode 100644 index 000000000000..7b2ef0c9d516 --- /dev/null +++ b/library/src/scala/beans/BeanProperty.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.beans + +import scala.annotation.meta.{beanGetter, beanSetter, field} + +/** When attached to a field, this annotation adds a setter and a getter + * method following the Java Bean convention. For example: + * {{{ + * @BeanProperty + * var status = "" + * }}} + * adds the following methods to the class: + * {{{ + * def setStatus(s: String): Unit = { this.status = s } + * def getStatus(): String = this.status + * }}} + * For fields of type `Boolean`, if you need a getter named `isStatus`, + * use the `scala.beans.BooleanBeanProperty` annotation instead. + * + * In Scala 2, the added methods are visible from both Scala and Java. + * + * In Scala 3, that has changed. The added methods are only visible from + * Java (including via Java reflection). + */ +@field @beanGetter @beanSetter +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class BeanProperty extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/beans/BooleanBeanProperty.scala b/library/src/scala/beans/BooleanBeanProperty.scala new file mode 100644 index 000000000000..72d253df2f8b --- /dev/null +++ b/library/src/scala/beans/BooleanBeanProperty.scala @@ -0,0 +1,23 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.beans + +import scala.annotation.meta.{beanGetter, beanSetter, field} + +/** This annotation has the same functionality as + * `scala.beans.BeanProperty`, but the generated Bean getter will be + * named `isFieldName` instead of `getFieldName`. + */ +@field @beanGetter @beanSetter +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class BooleanBeanProperty extends scala.annotation.StaticAnnotation From 7470ea55bf14651b78ba3988a90637dfb99f8ec1 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:33:21 +0100 Subject: [PATCH 07/28] chore: add all the files in scala.collection --- library/src/scala/collection/ArrayOps.scala | 1666 +++++++++++ library/src/scala/collection/BitSet.scala | 348 +++ .../scala/collection/BufferedIterator.scala | 32 + library/src/scala/collection/BuildFrom.scala | 122 + library/src/scala/collection/DefaultMap.scala | 21 + library/src/scala/collection/Factory.scala | 784 ++++++ library/src/scala/collection/Hashing.scala | 62 + library/src/scala/collection/IndexedSeq.scala | 147 + .../src/scala/collection/IndexedSeqView.scala | 180 ++ library/src/scala/collection/Iterable.scala | 1045 +++++++ .../src/scala/collection/IterableOnce.scala | 1514 ++++++++++ library/src/scala/collection/Iterator.scala | 1306 +++++++++ .../src/scala/collection/JavaConverters.scala | 335 +++ library/src/scala/collection/LazyZipOps.scala | 422 +++ library/src/scala/collection/LinearSeq.scala | 310 ++ library/src/scala/collection/Map.scala | 420 +++ library/src/scala/collection/MapView.scala | 188 ++ library/src/scala/collection/Searching.scala | 57 + library/src/scala/collection/Seq.scala | 1190 ++++++++ library/src/scala/collection/SeqMap.scala | 40 + library/src/scala/collection/SeqView.scala | 214 ++ library/src/scala/collection/Set.scala | 269 ++ library/src/scala/collection/SortedMap.scala | 220 ++ library/src/scala/collection/SortedOps.scala | 90 + library/src/scala/collection/SortedSet.scala | 189 ++ library/src/scala/collection/Stepper.scala | 368 +++ .../src/scala/collection/StepperShape.scala | 114 + .../StrictOptimizedIterableOps.scala | 284 ++ .../collection/StrictOptimizedMapOps.scala | 48 + .../collection/StrictOptimizedSeqOps.scala | 108 + .../collection/StrictOptimizedSetOps.scala | 29 + .../StrictOptimizedSortedMapOps.scala | 46 + .../StrictOptimizedSortedSetOps.scala | 42 + library/src/scala/collection/StringOps.scala | 1653 +++++++++++ .../src/scala/collection/StringParsers.scala | 321 +++ library/src/scala/collection/View.scala | 535 ++++ library/src/scala/collection/WithFilter.scala | 70 + .../collection/concurrent/BasicNode.java | 19 + .../collection/concurrent/CNodeBase.java | 37 + .../src/scala/collection/concurrent/Gen.java | 15 + .../collection/concurrent/INodeBase.java | 39 + .../scala/collection/concurrent/MainNode.java | 46 + .../src/scala/collection/concurrent/Map.scala | 190 ++ .../scala/collection/concurrent/TrieMap.scala | 1200 ++++++++ .../collection/convert/AsJavaConverters.scala | 260 ++ .../collection/convert/AsJavaExtensions.scala | 108 + .../convert/AsScalaConverters.scala | 207 ++ .../convert/AsScalaExtensions.scala | 93 + .../convert/ImplicitConversions.scala | 181 ++ .../convert/JavaCollectionWrappers.scala | 635 +++++ .../collection/convert/StreamExtensions.scala | 480 ++++ .../convert/impl/ArrayStepper.scala | 79 + .../convert/impl/BinaryTreeStepper.scala | 248 ++ .../convert/impl/BitSetStepper.scala | 118 + .../convert/impl/ChampStepper.scala | 245 ++ .../convert/impl/InOrderStepperBase.scala | 53 + .../convert/impl/IndexedSeqStepper.scala | 44 + .../convert/impl/IndexedStepperBase.scala | 40 + .../convert/impl/IteratorStepper.scala | 129 + .../convert/impl/NumericRangeStepper.scala | 38 + .../convert/impl/RangeStepper.scala | 40 + .../convert/impl/StringStepper.scala | 58 + .../convert/impl/TableStepper.scala | 138 + .../convert/impl/VectorStepper.scala | 131 + .../collection/generic/BitOperations.scala | 50 + .../collection/generic/CommonErrors.scala | 29 + .../generic/DefaultSerializationProxy.scala | 87 + .../scala/collection/generic/IsIterable.scala | 164 ++ .../collection/generic/IsIterableOnce.scala | 71 + .../src/scala/collection/generic/IsMap.scala | 115 + .../src/scala/collection/generic/IsSeq.scala | 114 + .../collection/generic/Subtractable.scala | 62 + .../scala/collection/generic/package.scala | 34 + .../scala/collection/immutable/ArraySeq.scala | 694 +++++ .../scala/collection/immutable/BitSet.scala | 375 +++ .../collection/immutable/ChampCommon.scala | 252 ++ .../scala/collection/immutable/HashMap.scala | 2422 ++++++++++++++++ .../scala/collection/immutable/HashSet.scala | 2116 ++++++++++++++ .../scala/collection/immutable/IntMap.scala | 502 ++++ .../scala/collection/immutable/Iterable.scala | 37 + .../scala/collection/immutable/LazyList.scala | 1405 ++++++++++ .../src/scala/collection/immutable/List.scala | 693 +++++ .../scala/collection/immutable/ListMap.scala | 372 +++ .../scala/collection/immutable/ListSet.scala | 138 + .../scala/collection/immutable/LongMap.scala | 490 ++++ .../src/scala/collection/immutable/Map.scala | 712 +++++ .../collection/immutable/NumericRange.scala | 541 ++++ .../scala/collection/immutable/Queue.scala | 217 ++ .../scala/collection/immutable/Range.scala | 674 +++++ .../collection/immutable/RedBlackTree.scala | 1244 +++++++++ .../src/scala/collection/immutable/Seq.scala | 155 + .../scala/collection/immutable/SeqMap.scala | 284 ++ .../src/scala/collection/immutable/Set.scala | 407 +++ .../collection/immutable/SortedMap.scala | 177 ++ .../collection/immutable/SortedSet.scala | 57 + .../scala/collection/immutable/Stream.scala | 569 ++++ .../immutable/StrictOptimizedSeqOps.scala | 84 + .../scala/collection/immutable/TreeMap.scala | 370 +++ .../collection/immutable/TreeSeqMap.scala | 650 +++++ .../scala/collection/immutable/TreeSet.scala | 296 ++ .../scala/collection/immutable/Vector.scala | 2483 +++++++++++++++++ .../collection/immutable/VectorMap.scala | 278 ++ .../collection/immutable/WrappedString.scala | 139 + .../scala/collection/immutable/package.scala | 29 + .../scala/collection/mutable/AnyRefMap.scala | 627 +++++ .../collection/mutable/ArrayBuffer.scala | 401 +++ .../collection/mutable/ArrayBuilder.scala | 536 ++++ .../scala/collection/mutable/ArrayDeque.scala | 648 +++++ .../scala/collection/mutable/ArraySeq.scala | 353 +++ .../src/scala/collection/mutable/BitSet.scala | 392 +++ .../src/scala/collection/mutable/Buffer.scala | 314 +++ .../scala/collection/mutable/Builder.scala | 103 + .../mutable/CheckedIndexedSeqView.scala | 117 + .../scala/collection/mutable/Cloneable.scala | 22 + .../mutable/CollisionProofHashMap.scala | 887 ++++++ .../scala/collection/mutable/Growable.scala | 101 + .../collection/mutable/GrowableBuilder.scala | 37 + .../scala/collection/mutable/HashMap.scala | 654 +++++ .../scala/collection/mutable/HashSet.scala | 456 +++ .../scala/collection/mutable/HashTable.scala | 417 +++ .../collection/mutable/ImmutableBuilder.scala | 31 + .../scala/collection/mutable/IndexedSeq.scala | 83 + .../scala/collection/mutable/Iterable.scala | 34 + .../collection/mutable/LinkedHashMap.scala | 509 ++++ .../collection/mutable/LinkedHashSet.scala | 348 +++ .../scala/collection/mutable/ListBuffer.scala | 420 +++ .../scala/collection/mutable/ListMap.scala | 82 + .../scala/collection/mutable/LongMap.scala | 691 +++++ .../src/scala/collection/mutable/Map.scala | 268 ++ .../scala/collection/mutable/MultiMap.scala | 115 + .../collection/mutable/MutationTracker.scala | 78 + .../collection/mutable/OpenHashMap.scala | 306 ++ .../collection/mutable/PriorityQueue.scala | 413 +++ .../src/scala/collection/mutable/Queue.scala | 138 + .../collection/mutable/RedBlackTree.scala | 652 +++++ .../collection/mutable/ReusableBuilder.scala | 55 + .../src/scala/collection/mutable/Seq.scala | 67 + .../src/scala/collection/mutable/SeqMap.scala | 38 + .../src/scala/collection/mutable/Set.scala | 122 + .../scala/collection/mutable/Shrinkable.scala | 79 + .../scala/collection/mutable/SortedMap.scala | 103 + .../scala/collection/mutable/SortedSet.scala | 48 + .../src/scala/collection/mutable/Stack.scala | 142 + .../collection/mutable/StringBuilder.scala | 495 ++++ .../scala/collection/mutable/TreeMap.scala | 257 ++ .../scala/collection/mutable/TreeSet.scala | 218 ++ .../collection/mutable/UnrolledBuffer.scala | 447 +++ .../collection/mutable/WeakHashMap.scala | 55 + .../scala/collection/mutable/package.scala | 41 + library/src/scala/collection/package.scala | 80 + 150 files changed, 51728 insertions(+) create mode 100644 library/src/scala/collection/ArrayOps.scala create mode 100644 library/src/scala/collection/BitSet.scala create mode 100644 library/src/scala/collection/BufferedIterator.scala create mode 100644 library/src/scala/collection/BuildFrom.scala create mode 100644 library/src/scala/collection/DefaultMap.scala create mode 100644 library/src/scala/collection/Factory.scala create mode 100644 library/src/scala/collection/Hashing.scala create mode 100644 library/src/scala/collection/IndexedSeq.scala create mode 100644 library/src/scala/collection/IndexedSeqView.scala create mode 100644 library/src/scala/collection/Iterable.scala create mode 100644 library/src/scala/collection/IterableOnce.scala create mode 100644 library/src/scala/collection/Iterator.scala create mode 100644 library/src/scala/collection/JavaConverters.scala create mode 100644 library/src/scala/collection/LazyZipOps.scala create mode 100644 library/src/scala/collection/LinearSeq.scala create mode 100644 library/src/scala/collection/Map.scala create mode 100644 library/src/scala/collection/MapView.scala create mode 100644 library/src/scala/collection/Searching.scala create mode 100644 library/src/scala/collection/Seq.scala create mode 100644 library/src/scala/collection/SeqMap.scala create mode 100644 library/src/scala/collection/SeqView.scala create mode 100644 library/src/scala/collection/Set.scala create mode 100644 library/src/scala/collection/SortedMap.scala create mode 100644 library/src/scala/collection/SortedOps.scala create mode 100644 library/src/scala/collection/SortedSet.scala create mode 100644 library/src/scala/collection/Stepper.scala create mode 100644 library/src/scala/collection/StepperShape.scala create mode 100644 library/src/scala/collection/StrictOptimizedIterableOps.scala create mode 100644 library/src/scala/collection/StrictOptimizedMapOps.scala create mode 100644 library/src/scala/collection/StrictOptimizedSeqOps.scala create mode 100644 library/src/scala/collection/StrictOptimizedSetOps.scala create mode 100644 library/src/scala/collection/StrictOptimizedSortedMapOps.scala create mode 100644 library/src/scala/collection/StrictOptimizedSortedSetOps.scala create mode 100644 library/src/scala/collection/StringOps.scala create mode 100644 library/src/scala/collection/StringParsers.scala create mode 100644 library/src/scala/collection/View.scala create mode 100644 library/src/scala/collection/WithFilter.scala create mode 100644 library/src/scala/collection/concurrent/BasicNode.java create mode 100644 library/src/scala/collection/concurrent/CNodeBase.java create mode 100644 library/src/scala/collection/concurrent/Gen.java create mode 100644 library/src/scala/collection/concurrent/INodeBase.java create mode 100644 library/src/scala/collection/concurrent/MainNode.java create mode 100644 library/src/scala/collection/concurrent/Map.scala create mode 100644 library/src/scala/collection/concurrent/TrieMap.scala create mode 100644 library/src/scala/collection/convert/AsJavaConverters.scala create mode 100644 library/src/scala/collection/convert/AsJavaExtensions.scala create mode 100644 library/src/scala/collection/convert/AsScalaConverters.scala create mode 100644 library/src/scala/collection/convert/AsScalaExtensions.scala create mode 100644 library/src/scala/collection/convert/ImplicitConversions.scala create mode 100644 library/src/scala/collection/convert/JavaCollectionWrappers.scala create mode 100644 library/src/scala/collection/convert/StreamExtensions.scala create mode 100644 library/src/scala/collection/convert/impl/ArrayStepper.scala create mode 100644 library/src/scala/collection/convert/impl/BinaryTreeStepper.scala create mode 100644 library/src/scala/collection/convert/impl/BitSetStepper.scala create mode 100644 library/src/scala/collection/convert/impl/ChampStepper.scala create mode 100644 library/src/scala/collection/convert/impl/InOrderStepperBase.scala create mode 100644 library/src/scala/collection/convert/impl/IndexedSeqStepper.scala create mode 100644 library/src/scala/collection/convert/impl/IndexedStepperBase.scala create mode 100644 library/src/scala/collection/convert/impl/IteratorStepper.scala create mode 100644 library/src/scala/collection/convert/impl/NumericRangeStepper.scala create mode 100644 library/src/scala/collection/convert/impl/RangeStepper.scala create mode 100644 library/src/scala/collection/convert/impl/StringStepper.scala create mode 100644 library/src/scala/collection/convert/impl/TableStepper.scala create mode 100644 library/src/scala/collection/convert/impl/VectorStepper.scala create mode 100644 library/src/scala/collection/generic/BitOperations.scala create mode 100644 library/src/scala/collection/generic/CommonErrors.scala create mode 100644 library/src/scala/collection/generic/DefaultSerializationProxy.scala create mode 100644 library/src/scala/collection/generic/IsIterable.scala create mode 100644 library/src/scala/collection/generic/IsIterableOnce.scala create mode 100644 library/src/scala/collection/generic/IsMap.scala create mode 100644 library/src/scala/collection/generic/IsSeq.scala create mode 100644 library/src/scala/collection/generic/Subtractable.scala create mode 100644 library/src/scala/collection/generic/package.scala create mode 100644 library/src/scala/collection/immutable/ArraySeq.scala create mode 100644 library/src/scala/collection/immutable/BitSet.scala create mode 100644 library/src/scala/collection/immutable/ChampCommon.scala create mode 100644 library/src/scala/collection/immutable/HashMap.scala create mode 100644 library/src/scala/collection/immutable/HashSet.scala create mode 100644 library/src/scala/collection/immutable/IntMap.scala create mode 100644 library/src/scala/collection/immutable/Iterable.scala create mode 100644 library/src/scala/collection/immutable/LazyList.scala create mode 100644 library/src/scala/collection/immutable/List.scala create mode 100644 library/src/scala/collection/immutable/ListMap.scala create mode 100644 library/src/scala/collection/immutable/ListSet.scala create mode 100644 library/src/scala/collection/immutable/LongMap.scala create mode 100644 library/src/scala/collection/immutable/Map.scala create mode 100644 library/src/scala/collection/immutable/NumericRange.scala create mode 100644 library/src/scala/collection/immutable/Queue.scala create mode 100644 library/src/scala/collection/immutable/Range.scala create mode 100644 library/src/scala/collection/immutable/RedBlackTree.scala create mode 100644 library/src/scala/collection/immutable/Seq.scala create mode 100644 library/src/scala/collection/immutable/SeqMap.scala create mode 100644 library/src/scala/collection/immutable/Set.scala create mode 100644 library/src/scala/collection/immutable/SortedMap.scala create mode 100644 library/src/scala/collection/immutable/SortedSet.scala create mode 100644 library/src/scala/collection/immutable/Stream.scala create mode 100644 library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala create mode 100644 library/src/scala/collection/immutable/TreeMap.scala create mode 100644 library/src/scala/collection/immutable/TreeSeqMap.scala create mode 100644 library/src/scala/collection/immutable/TreeSet.scala create mode 100644 library/src/scala/collection/immutable/Vector.scala create mode 100644 library/src/scala/collection/immutable/VectorMap.scala create mode 100644 library/src/scala/collection/immutable/WrappedString.scala create mode 100644 library/src/scala/collection/immutable/package.scala create mode 100644 library/src/scala/collection/mutable/AnyRefMap.scala create mode 100644 library/src/scala/collection/mutable/ArrayBuffer.scala create mode 100644 library/src/scala/collection/mutable/ArrayBuilder.scala create mode 100644 library/src/scala/collection/mutable/ArrayDeque.scala create mode 100644 library/src/scala/collection/mutable/ArraySeq.scala create mode 100644 library/src/scala/collection/mutable/BitSet.scala create mode 100644 library/src/scala/collection/mutable/Buffer.scala create mode 100644 library/src/scala/collection/mutable/Builder.scala create mode 100644 library/src/scala/collection/mutable/CheckedIndexedSeqView.scala create mode 100644 library/src/scala/collection/mutable/Cloneable.scala create mode 100644 library/src/scala/collection/mutable/CollisionProofHashMap.scala create mode 100644 library/src/scala/collection/mutable/Growable.scala create mode 100644 library/src/scala/collection/mutable/GrowableBuilder.scala create mode 100644 library/src/scala/collection/mutable/HashMap.scala create mode 100644 library/src/scala/collection/mutable/HashSet.scala create mode 100644 library/src/scala/collection/mutable/HashTable.scala create mode 100644 library/src/scala/collection/mutable/ImmutableBuilder.scala create mode 100644 library/src/scala/collection/mutable/IndexedSeq.scala create mode 100644 library/src/scala/collection/mutable/Iterable.scala create mode 100644 library/src/scala/collection/mutable/LinkedHashMap.scala create mode 100644 library/src/scala/collection/mutable/LinkedHashSet.scala create mode 100644 library/src/scala/collection/mutable/ListBuffer.scala create mode 100644 library/src/scala/collection/mutable/ListMap.scala create mode 100644 library/src/scala/collection/mutable/LongMap.scala create mode 100644 library/src/scala/collection/mutable/Map.scala create mode 100644 library/src/scala/collection/mutable/MultiMap.scala create mode 100644 library/src/scala/collection/mutable/MutationTracker.scala create mode 100644 library/src/scala/collection/mutable/OpenHashMap.scala create mode 100644 library/src/scala/collection/mutable/PriorityQueue.scala create mode 100644 library/src/scala/collection/mutable/Queue.scala create mode 100644 library/src/scala/collection/mutable/RedBlackTree.scala create mode 100644 library/src/scala/collection/mutable/ReusableBuilder.scala create mode 100644 library/src/scala/collection/mutable/Seq.scala create mode 100644 library/src/scala/collection/mutable/SeqMap.scala create mode 100644 library/src/scala/collection/mutable/Set.scala create mode 100644 library/src/scala/collection/mutable/Shrinkable.scala create mode 100644 library/src/scala/collection/mutable/SortedMap.scala create mode 100644 library/src/scala/collection/mutable/SortedSet.scala create mode 100644 library/src/scala/collection/mutable/Stack.scala create mode 100644 library/src/scala/collection/mutable/StringBuilder.scala create mode 100644 library/src/scala/collection/mutable/TreeMap.scala create mode 100644 library/src/scala/collection/mutable/TreeSet.scala create mode 100644 library/src/scala/collection/mutable/UnrolledBuffer.scala create mode 100644 library/src/scala/collection/mutable/WeakHashMap.scala create mode 100644 library/src/scala/collection/mutable/package.scala create mode 100644 library/src/scala/collection/package.scala diff --git a/library/src/scala/collection/ArrayOps.scala b/library/src/scala/collection/ArrayOps.scala new file mode 100644 index 000000000000..08758e2ab46a --- /dev/null +++ b/library/src/scala/collection/ArrayOps.scala @@ -0,0 +1,1666 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import java.lang.Math.{max, min} +import java.util.Arrays + +import scala.Predef.{ // unimport all array-related implicit conversions to avoid triggering them accidentally + genericArrayOps => _, + booleanArrayOps => _, + byteArrayOps => _, + charArrayOps => _, + doubleArrayOps => _, + floatArrayOps => _, + intArrayOps => _, + longArrayOps => _, + refArrayOps => _, + shortArrayOps => _, + unitArrayOps => _, + genericWrapArray => _, + wrapRefArray => _, + wrapIntArray => _, + wrapDoubleArray => _, + wrapLongArray => _, + wrapFloatArray => _, + wrapCharArray => _, + wrapByteArray => _, + wrapShortArray => _, + wrapBooleanArray => _, + wrapUnitArray => _, + wrapString => _, + copyArrayToImmutableIndexedSeq => _, + _ +} +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.CommonErrors +import scala.collection.immutable.Range +import scala.collection.mutable.ArrayBuilder +import scala.math.Ordering +import scala.reflect.ClassTag +import scala.util.Sorting + +object ArrayOps { + + @SerialVersionUID(3L) + private class ArrayView[A](xs: Array[A]) extends AbstractIndexedSeqView[A] { + def length = xs.length + def apply(n: Int) = xs(n) + override def toString: String = immutable.ArraySeq.unsafeWrapArray(xs).mkString("ArrayView(", ", ", ")") + } + + /** A lazy filtered array. No filtering is applied until one of `foreach`, `map` or `flatMap` is called. */ + class WithFilter[A](p: A => Boolean, xs: Array[A]) { + + /** Apply `f` to each element for its side effects. + * Note: [U] parameter needed to help scalac's type inference. + */ + def foreach[U](f: A => U): Unit = { + val len = xs.length + var i = 0 + while(i < len) { + val x = xs(i) + if(p(x)) f(x) + i += 1 + } + } + + /** Builds a new array by applying a function to all elements of this array. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned array. + * @return a new array resulting from applying the given function + * `f` to each element of this array and collecting the results. + */ + def map[B: ClassTag](f: A => B): Array[B] = { + val b = ArrayBuilder.make[B] + var i = 0 + while (i < xs.length) { + val x = xs(i) + if(p(x)) b += f(x) + i = i + 1 + } + b.result() + } + + /** Builds a new array by applying a function to all elements of this array + * and using the elements of the resulting collections. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned array. + * @return a new array resulting from applying the given collection-valued function + * `f` to each element of this array and concatenating the results. + */ + def flatMap[B: ClassTag](f: A => IterableOnce[B]): Array[B] = { + val b = ArrayBuilder.make[B] + var i = 0 + while(i < xs.length) { + val x = xs(i) + if(p(x)) b ++= f(xs(i)) + i += 1 + } + b.result() + } + + def flatMap[BS, B](f: A => BS)(implicit asIterable: BS => Iterable[B], m: ClassTag[B]): Array[B] = + flatMap[B](x => asIterable(f(x))) + + /** Creates a new non-strict filter which combines this filter with the given predicate. */ + def withFilter(q: A => Boolean): WithFilter[A] = new WithFilter[A](a => p(a) && q(a), xs) + } + + @SerialVersionUID(3L) + private[collection] final class ArrayIterator[@specialized(Specializable.Everything) A](xs: Array[A]) extends AbstractIterator[A] with Serializable { + private[this] var pos = 0 + private[this] val len = xs.length + override def knownSize: Int = len - pos + def hasNext: Boolean = pos < len + def next(): A = { + if (pos >= xs.length) Iterator.empty.next() + val r = xs(pos) + pos += 1 + r + } + override def drop(n: Int): Iterator[A] = { + if (n > 0) { + val newPos = pos + n + pos = + if (newPos < 0 /* overflow */) len + else Math.min(len, newPos) + } + this + } + } + + @SerialVersionUID(3L) + private final class ReverseIterator[@specialized(Specializable.Everything) A](xs: Array[A]) extends AbstractIterator[A] with Serializable { + private[this] var pos = xs.length-1 + def hasNext: Boolean = pos >= 0 + def next(): A = { + if (pos < 0) Iterator.empty.next() + val r = xs(pos) + pos -= 1 + r + } + + override def drop(n: Int): Iterator[A] = { + if (n > 0) pos = Math.max( -1, pos - n) + this + } + } + + @SerialVersionUID(3L) + private final class GroupedIterator[A](xs: Array[A], groupSize: Int) extends AbstractIterator[Array[A]] with Serializable { + private[this] var pos = 0 + def hasNext: Boolean = pos < xs.length + def next(): Array[A] = { + if(pos >= xs.length) throw new NoSuchElementException + val r = new ArrayOps(xs).slice(pos, pos+groupSize) + pos += groupSize + r + } + } + + /** The cut-off point for the array size after which we switch from `Sorting.stableSort` to + * an implementation that copies the data to a boxed representation for use with `Arrays.sort`. + */ + private final val MaxStableSortLength = 300 + + /** Avoid an allocation in [[collect]]. */ + private val fallback: Any => Any = _ => fallback +} + +/** This class serves as a wrapper for `Array`s with many of the operations found in + * indexed sequences. Where needed, instances of arrays are implicitly converted + * into this class. There is generally no reason to create an instance explicitly or use + * an `ArrayOps` type. It is better to work with plain `Array` types instead and rely on + * the implicit conversion to `ArrayOps` when calling a method (which does not actually + * allocate an instance of `ArrayOps` because it is a value class). + * + * Neither `Array` nor `ArrayOps` are proper collection types + * (i.e. they do not extend `Iterable` or even `IterableOnce`). `mutable.ArraySeq` and + * `immutable.ArraySeq` serve this purpose. + * + * The difference between this class and `ArraySeq`s is that calling transformer methods such as + * `filter` and `map` will yield an array, whereas an `ArraySeq` will remain an `ArraySeq`. + * + * @tparam A type of the elements contained in this array. + */ +final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { + + @`inline` private[this] implicit def elemTag: ClassTag[A] = ClassTag(xs.getClass.getComponentType) + + /** The size of this array. + * + * @return the number of elements in this array. + */ + @`inline` def size: Int = xs.length + + /** The size of this array. + * + * @return the number of elements in this array. + */ + @`inline` def knownSize: Int = xs.length + + /** Tests whether the array is empty. + * + * @return `true` if the array contains no elements, `false` otherwise. + */ + @`inline` def isEmpty: Boolean = xs.length == 0 + + /** Tests whether the array is not empty. + * + * @return `true` if the array contains at least one element, `false` otherwise. + */ + @`inline` def nonEmpty: Boolean = xs.length != 0 + + /** Selects the first element of this array. + * + * @return the first element of this array. + * @throws NoSuchElementException if the array is empty. + */ + def head: A = if (nonEmpty) xs.apply(0) else throw new NoSuchElementException("head of empty array") + + /** Selects the last element. + * + * @return The last element of this array. + * @throws NoSuchElementException If the array is empty. + */ + def last: A = if (nonEmpty) xs.apply(xs.length-1) else throw new NoSuchElementException("last of empty array") + + /** Optionally selects the first element. + * + * @return the first element of this array if it is nonempty, + * `None` if it is empty. + */ + def headOption: Option[A] = if(isEmpty) None else Some(head) + + /** Optionally selects the last element. + * + * @return the last element of this array$ if it is nonempty, + * `None` if it is empty. + */ + def lastOption: Option[A] = if(isEmpty) None else Some(last) + + /** Compares the size of this array to a test value. + * + * @param otherSize the test value that gets compared with the size. + * @return A value `x` where + * {{{ + * x < 0 if this.size < otherSize + * x == 0 if this.size == otherSize + * x > 0 if this.size > otherSize + * }}} + */ + def sizeCompare(otherSize: Int): Int = Integer.compare(xs.length, otherSize) + + /** Compares the length of this array to a test value. + * + * @param len the test value that gets compared with the length. + * @return A value `x` where + * {{{ + * x < 0 if this.length < len + * x == 0 if this.length == len + * x > 0 if this.length > len + * }}} + */ + def lengthCompare(len: Int): Int = Integer.compare(xs.length, len) + + /** Method mirroring [[SeqOps.sizeIs]] for consistency, except it returns an `Int` + * because `size` is known and comparison is constant-time. + * + * These operations are equivalent to [[sizeCompare(Int) `sizeCompare(Int)`]], and + * allow the following more readable usages: + * + * {{{ + * this.sizeIs < size // this.sizeCompare(size) < 0 + * this.sizeIs <= size // this.sizeCompare(size) <= 0 + * this.sizeIs == size // this.sizeCompare(size) == 0 + * this.sizeIs != size // this.sizeCompare(size) != 0 + * this.sizeIs >= size // this.sizeCompare(size) >= 0 + * this.sizeIs > size // this.sizeCompare(size) > 0 + * }}} + */ + def sizeIs: Int = xs.length + + /** Method mirroring [[SeqOps.lengthIs]] for consistency, except it returns an `Int` + * because `length` is known and comparison is constant-time. + * + * These operations are equivalent to [[lengthCompare(Int) `lengthCompare(Int)`]], and + * allow the following more readable usages: + * + * {{{ + * this.lengthIs < len // this.lengthCompare(len) < 0 + * this.lengthIs <= len // this.lengthCompare(len) <= 0 + * this.lengthIs == len // this.lengthCompare(len) == 0 + * this.lengthIs != len // this.lengthCompare(len) != 0 + * this.lengthIs >= len // this.lengthCompare(len) >= 0 + * this.lengthIs > len // this.lengthCompare(len) > 0 + * }}} + */ + def lengthIs: Int = xs.length + + /** Selects an interval of elements. The returned array is made up + * of all elements `x` which satisfy the invariant: + * {{{ + * from <= indexOf(x) < until + * }}} + * + * @param from the lowest index to include from this array. + * @param until the lowest index to EXCLUDE from this array. + * @return an array containing the elements greater than or equal to + * index `from` extending up to (but not including) index `until` + * of this array. + */ + def slice(from: Int, until: Int): Array[A] = { + import java.util.Arrays.copyOfRange + val lo = max(from, 0) + val hi = min(until, xs.length) + if (hi > lo) { + (((xs: Array[_]): @unchecked) match { + case x: Array[AnyRef] => copyOfRange(x, lo, hi) + case x: Array[Int] => copyOfRange(x, lo, hi) + case x: Array[Double] => copyOfRange(x, lo, hi) + case x: Array[Long] => copyOfRange(x, lo, hi) + case x: Array[Float] => copyOfRange(x, lo, hi) + case x: Array[Char] => copyOfRange(x, lo, hi) + case x: Array[Byte] => copyOfRange(x, lo, hi) + case x: Array[Short] => copyOfRange(x, lo, hi) + case x: Array[Boolean] => copyOfRange(x, lo, hi) + }).asInstanceOf[Array[A]] + } else new Array[A](0) + } + + /** The rest of the array without its first element. */ + def tail: Array[A] = + if(xs.length == 0) throw new UnsupportedOperationException("tail of empty array") else slice(1, xs.length) + + /** The initial part of the array without its last element. */ + def init: Array[A] = + if(xs.length == 0) throw new UnsupportedOperationException("init of empty array") else slice(0, xs.length-1) + + /** Iterates over the tails of this array. The first value will be this + * array and the final one will be an empty array, with the intervening + * values the results of successive applications of `tail`. + * + * @return an iterator over all the tails of this array + */ + def tails: Iterator[Array[A]] = iterateUntilEmpty(xs => new ArrayOps(xs).tail) + + /** Iterates over the inits of this array. The first value will be this + * array and the final one will be an empty array, with the intervening + * values the results of successive applications of `init`. + * + * @return an iterator over all the inits of this array + */ + def inits: Iterator[Array[A]] = iterateUntilEmpty(xs => new ArrayOps(xs).init) + + // A helper for tails and inits. + private[this] def iterateUntilEmpty(f: Array[A] => Array[A]): Iterator[Array[A]] = + Iterator.iterate(xs)(f).takeWhile(x => x.length != 0) ++ Iterator.single(Array.empty[A]) + + /** An array containing the first `n` elements of this array. */ + def take(n: Int): Array[A] = slice(0, n) + + /** The rest of the array without its `n` first elements. */ + def drop(n: Int): Array[A] = slice(n, xs.length) + + /** An array containing the last `n` elements of this array. */ + def takeRight(n: Int): Array[A] = drop(xs.length - max(n, 0)) + + /** The rest of the array without its `n` last elements. */ + def dropRight(n: Int): Array[A] = take(xs.length - max(n, 0)) + + /** Takes longest prefix of elements that satisfy a predicate. + * + * @param p The predicate used to test elements. + * @return the longest prefix of this array whose elements all satisfy + * the predicate `p`. + */ + def takeWhile(p: A => Boolean): Array[A] = { + val i = indexWhere(x => !p(x)) + val hi = if(i < 0) xs.length else i + slice(0, hi) + } + + /** Drops longest prefix of elements that satisfy a predicate. + * + * @param p The predicate used to test elements. + * @return the longest suffix of this array whose first element + * does not satisfy the predicate `p`. + */ + def dropWhile(p: A => Boolean): Array[A] = { + val i = indexWhere(x => !p(x)) + val lo = if(i < 0) xs.length else i + slice(lo, xs.length) + } + + def iterator: Iterator[A] = + ((xs: Any @unchecked) match { + case xs: Array[AnyRef] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Int] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Double] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Long] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Float] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Char] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Byte] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Short] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Boolean] => new ArrayOps.ArrayIterator(xs) + case xs: Array[Unit] => new ArrayOps.ArrayIterator(xs) + case null => throw new NullPointerException + }).asInstanceOf[Iterator[A]] + + def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + import convert.impl._ + val s = (shape.shape: @unchecked) match { + case StepperShape.ReferenceShape => (xs: Any) match { + case bs: Array[Boolean] => new BoxedBooleanArrayStepper(bs, 0, xs.length) + case _ => new ObjectArrayStepper[AnyRef](xs.asInstanceOf[Array[AnyRef ]], 0, xs.length) + } + case StepperShape.IntShape => new IntArrayStepper (xs.asInstanceOf[Array[Int ]], 0, xs.length) + case StepperShape.LongShape => new LongArrayStepper (xs.asInstanceOf[Array[Long ]], 0, xs.length) + case StepperShape.DoubleShape => new DoubleArrayStepper (xs.asInstanceOf[Array[Double ]], 0, xs.length) + case StepperShape.ByteShape => new WidenedByteArrayStepper (xs.asInstanceOf[Array[Byte ]], 0, xs.length) + case StepperShape.ShortShape => new WidenedShortArrayStepper (xs.asInstanceOf[Array[Short ]], 0, xs.length) + case StepperShape.CharShape => new WidenedCharArrayStepper (xs.asInstanceOf[Array[Char ]], 0, xs.length) + case StepperShape.FloatShape => new WidenedFloatArrayStepper (xs.asInstanceOf[Array[Float ]], 0, xs.length) + } + s.asInstanceOf[S with EfficientSplit] + } + + /** Partitions elements in fixed size arrays. + * @see [[scala.collection.Iterator]], method `grouped` + * + * @param size the number of elements per group + * @return An iterator producing arrays of size `size`, except the + * last will be less than size `size` if the elements don't divide evenly. + */ + def grouped(size: Int): Iterator[Array[A]] = new ArrayOps.GroupedIterator[A](xs, size) + + /** Splits this array into a prefix/suffix pair according to a predicate. + * + * Note: `c span p` is equivalent to (but more efficient than) + * `(c takeWhile p, c dropWhile p)`, provided the evaluation of the + * predicate `p` does not cause any side-effects. + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of this array whose + * elements all satisfy `p`, and the rest of this array. + */ + def span(p: A => Boolean): (Array[A], Array[A]) = { + val i = indexWhere(x => !p(x)) + val idx = if(i < 0) xs.length else i + (slice(0, idx), slice(idx, xs.length)) + } + + /** Splits this array into two at a given position. + * Note: `c splitAt n` is equivalent to `(c take n, c drop n)`. + * + * @param n the position at which to split. + * @return a pair of arrays consisting of the first `n` + * elements of this array, and the other elements. + */ + def splitAt(n: Int): (Array[A], Array[A]) = (take(n), drop(n)) + + /** A pair of, first, all elements that satisfy predicate `p` and, second, all elements that do not. */ + def partition(p: A => Boolean): (Array[A], Array[A]) = { + val res1, res2 = ArrayBuilder.make[A] + var i = 0 + while(i < xs.length) { + val x = xs(i) + (if(p(x)) res1 else res2) += x + i += 1 + } + (res1.result(), res2.result()) + } + + /** Applies a function `f` to each element of the array and returns a pair of arrays: the first one + * made of those values returned by `f` that were wrapped in [[scala.util.Left]], and the second + * one made of those wrapped in [[scala.util.Right]]. + * + * Example: + * {{{ + * val xs = Array(1, "one", 2, "two", 3, "three") partitionMap { + * case i: Int => Left(i) + * case s: String => Right(s) + * } + * // xs == (Array(1, 2, 3), + * // Array(one, two, three)) + * }}} + * + * @tparam A1 the element type of the first resulting collection + * @tparam A2 the element type of the second resulting collection + * @param f the 'split function' mapping the elements of this array to an [[scala.util.Either]] + * + * @return a pair of arrays: the first one made of those values returned by `f` that were wrapped in [[scala.util.Left]], + * and the second one made of those wrapped in [[scala.util.Right]]. */ + def partitionMap[A1: ClassTag, A2: ClassTag](f: A => Either[A1, A2]): (Array[A1], Array[A2]) = { + val res1 = ArrayBuilder.make[A1] + val res2 = ArrayBuilder.make[A2] + var i = 0 + while(i < xs.length) { + f(xs(i)) match { + case Left(x) => res1 += x + case Right(x) => res2 += x + } + i += 1 + } + (res1.result(), res2.result()) + } + + /** Returns a new array with the elements in reversed order. */ + @inline def reverse: Array[A] = { + val len = xs.length + val res = new Array[A](len) + var i = 0 + while(i < len) { + res(len-i-1) = xs(i) + i += 1 + } + res + } + + /** An iterator yielding elements in reversed order. + * + * Note: `xs.reverseIterator` is the same as `xs.reverse.iterator` but implemented more efficiently. + * + * @return an iterator yielding the elements of this array in reversed order + */ + def reverseIterator: Iterator[A] = + ((xs: Any @unchecked) match { + case xs: Array[AnyRef] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Int] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Double] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Long] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Float] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Char] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Byte] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Short] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Boolean] => new ArrayOps.ReverseIterator(xs) + case xs: Array[Unit] => new ArrayOps.ReverseIterator(xs) + case null => throw new NullPointerException + }).asInstanceOf[Iterator[A]] + + /** Selects all elements of this array which satisfy a predicate. + * + * @param p the predicate used to test elements. + * @return a new array consisting of all elements of this array that satisfy the given predicate `p`. + */ + def filter(p: A => Boolean): Array[A] = { + val res = ArrayBuilder.make[A] + var i = 0 + while(i < xs.length) { + val x = xs(i) + if(p(x)) res += x + i += 1 + } + res.result() + } + + /** Selects all elements of this array which do not satisfy a predicate. + * + * @param p the predicate used to test elements. + * @return a new array consisting of all elements of this array that do not satisfy the given predicate `p`. + */ + def filterNot(p: A => Boolean): Array[A] = filter(x => !p(x)) + + /** Sorts this array according to an Ordering. + * + * The sort is stable. That is, elements that are equal (as determined by + * `lt`) appear in the same order in the sorted sequence as in the original. + * + * @see [[scala.math.Ordering]] + * + * @param ord the ordering to be used to compare elements. + * @return an array consisting of the elements of this array + * sorted according to the ordering `ord`. + */ + def sorted[B >: A](implicit ord: Ordering[B]): Array[A] = { + val len = xs.length + def boxed = if(len < ArrayOps.MaxStableSortLength) { + val a = xs.clone() + Sorting.stableSort(a)(using ord.asInstanceOf[Ordering[A]]) + a + } else { + val a = Array.copyAs[AnyRef](xs, len)(ClassTag.AnyRef) + Arrays.sort(a, ord.asInstanceOf[Ordering[AnyRef]]) + Array.copyAs[A](a, len) + } + if(len <= 1) xs.clone() + else ((xs: Array[_]) match { + case xs: Array[AnyRef] => + val a = Arrays.copyOf(xs, len); Arrays.sort(a, ord.asInstanceOf[Ordering[AnyRef]]); a + case xs: Array[Int] => + if(ord eq Ordering.Int) { val a = Arrays.copyOf(xs, len); Arrays.sort(a); a } + else boxed + case xs: Array[Long] => + if(ord eq Ordering.Long) { val a = Arrays.copyOf(xs, len); Arrays.sort(a); a } + else boxed + case xs: Array[Char] => + if(ord eq Ordering.Char) { val a = Arrays.copyOf(xs, len); Arrays.sort(a); a } + else boxed + case xs: Array[Byte] => + if(ord eq Ordering.Byte) { val a = Arrays.copyOf(xs, len); Arrays.sort(a); a } + else boxed + case xs: Array[Short] => + if(ord eq Ordering.Short) { val a = Arrays.copyOf(xs, len); Arrays.sort(a); a } + else boxed + case xs: Array[Boolean] => + if(ord eq Ordering.Boolean) { val a = Arrays.copyOf(xs, len); Sorting.stableSort(a); a } + else boxed + case xs => boxed + }).asInstanceOf[Array[A]] + } + + /** Sorts this array according to a comparison function. + * + * The sort is stable. That is, elements that are equal (as determined by + * `lt`) appear in the same order in the sorted sequence as in the original. + * + * @param lt the comparison function which tests whether + * its first argument precedes its second argument in + * the desired ordering. + * @return an array consisting of the elements of this array + * sorted according to the comparison function `lt`. + */ + def sortWith(lt: (A, A) => Boolean): Array[A] = sorted(Ordering.fromLessThan(lt)) + + /** Sorts this array according to the Ordering which results from transforming + * an implicitly given Ordering with a transformation function. + * + * @see [[scala.math.Ordering]] + * @param f the transformation function mapping elements + * to some other domain `B`. + * @param ord the ordering assumed on domain `B`. + * @tparam B the target type of the transformation `f`, and the type where + * the ordering `ord` is defined. + * @return an array consisting of the elements of this array + * sorted according to the ordering where `x < y` if + * `ord.lt(f(x), f(y))`. + */ + def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Array[A] = sorted(ord on f) + + /** Creates a non-strict filter of this array. + * + * Note: the difference between `c filter p` and `c withFilter p` is that + * the former creates a new array, whereas the latter only + * restricts the domain of subsequent `map`, `flatMap`, `foreach`, + * and `withFilter` operations. + * + * @param p the predicate used to test elements. + * @return an object of class `ArrayOps.WithFilter`, which supports + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * All these operations apply to those elements of this array + * which satisfy the predicate `p`. + */ + def withFilter(p: A => Boolean): ArrayOps.WithFilter[A] = new ArrayOps.WithFilter[A](p, xs) + + /** Finds index of first occurrence of some value in this array after or at some start index. + * + * @param elem the element value to search for. + * @param from the start index + * @return the index `>= from` of the first element of this array that is equal (as determined by `==`) + * to `elem`, or `-1`, if none exists. + */ + def indexOf(elem: A, from: Int = 0): Int = { + var i = from + while(i < xs.length) { + if(elem == xs(i)) return i + i += 1 + } + -1 + } + + /** Finds index of the first element satisfying some predicate after or at some start index. + * + * @param p the predicate used to test elements. + * @param from the start index + * @return the index `>= from` of the first element of this array that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + def indexWhere(@deprecatedName("f", "2.13.3") p: A => Boolean, from: Int = 0): Int = { + var i = from + while(i < xs.length) { + if(p(xs(i))) return i + i += 1 + } + -1 + } + + /** Finds index of last occurrence of some value in this array before or at a given end index. + * + * @param elem the element value to search for. + * @param end the end index. + * @return the index `<= end` of the last element of this array that is equal (as determined by `==`) + * to `elem`, or `-1`, if none exists. + */ + def lastIndexOf(elem: A, end: Int = xs.length - 1): Int = { + var i = min(end, xs.length-1) + while(i >= 0) { + if(elem == xs(i)) return i + i -= 1 + } + -1 + } + + /** Finds index of last element satisfying some predicate before or at given end index. + * + * @param p the predicate used to test elements. + * @return the index `<= end` of the last element of this array that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + def lastIndexWhere(p: A => Boolean, end: Int = xs.length - 1): Int = { + var i = min(end, xs.length-1) + while(i >= 0) { + if(p(xs(i))) return i + i -= 1 + } + -1 + } + + /** Finds the first element of the array satisfying a predicate, if any. + * + * @param p the predicate used to test elements. + * @return an option value containing the first element in the array + * that satisfies `p`, or `None` if none exists. + */ + def find(@deprecatedName("f", "2.13.3") p: A => Boolean): Option[A] = { + val idx = indexWhere(p) + if(idx == -1) None else Some(xs(idx)) + } + + /** Tests whether a predicate holds for at least one element of this array. + * + * @param p the predicate used to test elements. + * @return `true` if the given predicate `p` is satisfied by at least one element of this array, otherwise `false` + */ + def exists(@deprecatedName("f", "2.13.3") p: A => Boolean): Boolean = indexWhere(p) >= 0 + + /** Tests whether a predicate holds for all elements of this array. + * + * @param p the predicate used to test elements. + * @return `true` if this array is empty or the given predicate `p` + * holds for all elements of this array, otherwise `false`. + */ + def forall(@deprecatedName("f", "2.13.3") p: A => Boolean): Boolean = { + var i = 0 + while(i < xs.length) { + if(!p(xs(i))) return false + i += 1 + } + true + } + + /** Applies the given binary operator `op` to the given initial value `z` and + * all elements of this array, going left to right. Returns the initial value + * if this array is empty. + * + * If `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this array, the + * result is `op( op( ... op( op(z, x,,1,,), x,,2,,) ... ), x,,n,,)`. + * + * @param z An initial value. + * @param op A binary operator. + * @tparam B The result type of the binary operator. + * @return The result of applying `op` to `z` and all elements of this array, + * going left to right. Returns `z` if this array is empty. + */ + def foldLeft[B](z: B)(op: (B, A) => B): B = { + def f[@specialized(Specializable.Everything) T](xs: Array[T], op: (Any, Any) => Any, z: Any): Any = { + val length = xs.length + var v: Any = z + var i = 0 + while(i < length) { + v = op(v, xs(i)) + i += 1 + } + v + } + ((xs: Any @unchecked) match { + case null => throw new NullPointerException // null-check first helps static analysis of instanceOf + case xs: Array[AnyRef] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Int] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Double] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Long] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Float] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Char] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Byte] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Short] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Boolean] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Unit] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + }).asInstanceOf[B] + } + + /** Produces an array containing cumulative results of applying the binary + * operator going left to right. + * + * @param z the start value. + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return array with intermediate values. + * + * Example: + * {{{ + * Array(1, 2, 3, 4).scanLeft(0)(_ + _) == Array(0, 1, 3, 6, 10) + * }}} + * + */ + def scanLeft[ B : ClassTag ](z: B)(op: (B, A) => B): Array[B] = { + var v = z + var i = 0 + val res = new Array[B](xs.length + 1) + while(i < xs.length) { + res(i) = v + v = op(v, xs(i)) + i += 1 + } + res(i) = v + res + } + + /** Computes a prefix scan of the elements of the array. + * + * Note: The neutral element `z` may be applied more than once. + * + * @tparam B element type of the resulting array + * @param z neutral element for the operator `op` + * @param op the associative operator for the scan + * + * @return a new array containing the prefix scan of the elements in this array + */ + def scan[B >: A : ClassTag](z: B)(op: (B, B) => B): Array[B] = scanLeft(z)(op) + + /** Produces an array containing cumulative results of applying the binary + * operator going right to left. + * + * @param z the start value. + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return array with intermediate values. + * + * Example: + * {{{ + * Array(4, 3, 2, 1).scanRight(0)(_ + _) == Array(10, 6, 3, 1, 0) + * }}} + * + */ + def scanRight[ B : ClassTag ](z: B)(op: (A, B) => B): Array[B] = { + var v = z + var i = xs.length - 1 + val res = new Array[B](xs.length + 1) + res(xs.length) = z + while(i >= 0) { + v = op(xs(i), v) + res(i) = v + i -= 1 + } + res + } + + /** Applies the given binary operator `op` to all elements of this array and + * the given initial value `z`, going right to left. Returns the initial + * value if this array is empty. + * + * If `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this array, the + * result is `op(x,,1,,, op(x,,2,,, op( ... op(x,,n,,, z) ... )))`. + * + * @param z An initial value. + * @param op A binary operator. + * @tparam B The result type of the binary operator. + * @return The result of applying `op` to all elements of this array + * and `z`, going right to left. Returns `z` if this array + * is empty. + */ + def foldRight[B](z: B)(op: (A, B) => B): B = { + def f[@specialized(Specializable.Everything) T](xs: Array[T], op: (Any, Any) => Any, z: Any): Any = { + var v = z + var i = xs.length - 1 + while(i >= 0) { + v = op(xs(i), v) + i -= 1 + } + v + } + ((xs: Any @unchecked) match { + case null => throw new NullPointerException + case xs: Array[AnyRef] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Int] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Double] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Long] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Float] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Char] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Byte] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Short] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Boolean] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + case xs: Array[Unit] => f(xs, op.asInstanceOf[(Any, Any) => Any], z) + }).asInstanceOf[B] + + } + + /** Alias for [[foldLeft]]. + * + * The type parameter is more restrictive than for `foldLeft` to be + * consistent with [[IterableOnceOps.fold]]. + * + * @tparam A1 The type parameter for the binary operator, a supertype of `A`. + * @param z An initial value. + * @param op A binary operator. + * @return The result of applying `op` to `z` and all elements of this array, + * going left to right. Returns `z` if this string is empty. + */ + def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op) + + /** Builds a new array by applying a function to all elements of this array. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned array. + * @return a new array resulting from applying the given function + * `f` to each element of this array and collecting the results. + */ + def map[B](f: A => B)(implicit ct: ClassTag[B]): Array[B] = { + val len = xs.length + val ys = new Array[B](len) + if(len > 0) { + var i = 0 + (xs: Any @unchecked) match { + case xs: Array[AnyRef] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Int] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Double] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Long] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Float] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Char] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Byte] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Short] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Boolean] => while (i < len) { ys(i) = f(xs(i).asInstanceOf[A]); i = i+1 } + } + } + ys + } + + def mapInPlace(f: A => A): Array[A] = { + var i = 0 + while (i < xs.length) { + xs.update(i, f(xs(i))) + i = i + 1 + } + xs + } + + /** Builds a new array by applying a function to all elements of this array + * and using the elements of the resulting collections. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned array. + * @return a new array resulting from applying the given collection-valued function + * `f` to each element of this array and concatenating the results. + */ + def flatMap[B : ClassTag](f: A => IterableOnce[B]): Array[B] = { + val b = ArrayBuilder.make[B] + var i = 0 + while(i < xs.length) { + b ++= f(xs(i)) + i += 1 + } + b.result() + } + + def flatMap[BS, B](f: A => BS)(implicit asIterable: BS => Iterable[B], m: ClassTag[B]): Array[B] = + flatMap[B](x => asIterable(f(x))) + + /** Flattens a two-dimensional array by concatenating all its rows + * into a single array. + * + * @tparam B Type of row elements. + * @param asIterable A function that converts elements of this array to rows - Iterables of type `B`. + * @return An array obtained by concatenating rows of this array. + */ + def flatten[B](implicit asIterable: A => IterableOnce[B], m: ClassTag[B]): Array[B] = { + val b = ArrayBuilder.make[B] + val len = xs.length + var size = 0 + var i = 0 + while(i < len) { + xs(i) match { + case it: IterableOnce[_] => + val k = it.knownSize + if(k > 0) size += k + case a: Array[_] => size += a.length + case _ => + } + i += 1 + } + if(size > 0) b.sizeHint(size) + i = 0 + while(i < len) { + b ++= asIterable(xs(i)) + i += 1 + } + b.result() + } + + /** Builds a new array by applying a partial function to all elements of this array + * on which the function is defined. + * + * @param pf the partial function which filters and maps the array. + * @tparam B the element type of the returned array. + * @return a new array resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ + def collect[B: ClassTag](pf: PartialFunction[A, B]): Array[B] = { + val fallback: Any => Any = ArrayOps.fallback + val b = ArrayBuilder.make[B] + var i = 0 + while (i < xs.length) { + val v = pf.applyOrElse(xs(i), fallback) + if (v.asInstanceOf[AnyRef] ne fallback) b.addOne(v.asInstanceOf[B]) + i += 1 + } + b.result() + } + + /** Finds the first element of the array for which the given partial function is defined, and applies the + * partial function to it. */ + def collectFirst[B](@deprecatedName("f","2.13.9") pf: PartialFunction[A, B]): Option[B] = { + val fallback: Any => Any = ArrayOps.fallback + var i = 0 + while (i < xs.length) { + val v = pf.applyOrElse(xs(i), fallback) + if (v.asInstanceOf[AnyRef] ne fallback) return Some(v.asInstanceOf[B]) + i += 1 + } + None + } + + /** Returns an array formed from this array and another iterable collection + * by combining corresponding elements in pairs. + * If one of the two collections is longer than the other, its remaining elements are ignored. + * + * @param that The iterable providing the second half of each result pair + * @tparam B the type of the second half of the returned pairs + * @return a new array containing pairs consisting of corresponding elements of this array and `that`. + * The length of the returned array is the minimum of the lengths of this array and `that`. + */ + def zip[B](that: IterableOnce[B]): Array[(A, B)] = { + val b = new ArrayBuilder.ofRef[(A, B)]() + val k = that.knownSize + b.sizeHint(if(k >= 0) min(k, xs.length) else xs.length) + var i = 0 + val it = that.iterator + while(i < xs.length && it.hasNext) { + b += ((xs(i), it.next())) + i += 1 + } + b.result() + } + + /** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is + * invoked on the returned `LazyZip2` decorator. + * + * Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of + * constructing and deconstructing intermediary tuples. + * + * {{{ + * val xs = List(1, 2, 3) + * val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d) + * // res == List(4, 8, 12) + * }}} + * + * @param that the iterable providing the second element of each eventual pair + * @tparam B the type of the second element in each eventual pair + * @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs + * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. + */ + def lazyZip[B](that: Iterable[B]): LazyZip2[A, B, Array[A]] = new LazyZip2(xs, immutable.ArraySeq.unsafeWrapArray(xs), that) + + /** Returns an array formed from this array and another iterable collection + * by combining corresponding elements in pairs. + * If one of the two collections is shorter than the other, + * placeholder elements are used to extend the shorter collection to the length of the longer. + * + * @param that the iterable providing the second half of each result pair + * @param thisElem the element to be used to fill up the result if this array is shorter than `that`. + * @param thatElem the element to be used to fill up the result if `that` is shorter than this array. + * @return a new array containing pairs consisting of corresponding elements of this array and `that`. + * The length of the returned array is the maximum of the lengths of this array and `that`. + * If this array is shorter than `that`, `thisElem` values are used to pad the result. + * If `that` is shorter than this array, `thatElem` values are used to pad the result. + */ + def zipAll[A1 >: A, B](that: Iterable[B], thisElem: A1, thatElem: B): Array[(A1, B)] = { + val b = new ArrayBuilder.ofRef[(A1, B)]() + val k = that.knownSize + b.sizeHint(max(k, xs.length)) + var i = 0 + val it = that.iterator + while(i < xs.length && it.hasNext) { + b += ((xs(i), it.next())) + i += 1 + } + while(it.hasNext) { + b += ((thisElem, it.next())) + i += 1 + } + while(i < xs.length) { + b += ((xs(i), thatElem)) + i += 1 + } + b.result() + } + + /** Zips this array with its indices. + * + * @return A new array containing pairs consisting of all elements of this array paired with their index. + * Indices start at `0`. + */ + def zipWithIndex: Array[(A, Int)] = { + val b = new Array[(A, Int)](xs.length) + var i = 0 + while(i < xs.length) { + b(i) = ((xs(i), i)) + i += 1 + } + b + } + + /** A copy of this array with an element appended. */ + def appended[B >: A : ClassTag](x: B): Array[B] = { + val dest = Array.copyAs[B](xs, xs.length+1) + dest(xs.length) = x + dest + } + + @`inline` final def :+ [B >: A : ClassTag](x: B): Array[B] = appended(x) + + /** A copy of this array with an element prepended. */ + def prepended[B >: A : ClassTag](x: B): Array[B] = { + val dest = new Array[B](xs.length + 1) + dest(0) = x + Array.copy(xs, 0, dest, 1, xs.length) + dest + } + + @`inline` final def +: [B >: A : ClassTag](x: B): Array[B] = prepended(x) + + /** A copy of this array with all elements of a collection prepended. */ + def prependedAll[B >: A : ClassTag](prefix: IterableOnce[B]): Array[B] = { + val b = ArrayBuilder.make[B] + val k = prefix.knownSize + if(k >= 0) b.sizeHint(k + xs.length) + b.addAll(prefix) + if(k < 0) b.sizeHint(b.length + xs.length) + b.addAll(xs) + b.result() + } + + /** A copy of this array with all elements of an array prepended. */ + def prependedAll[B >: A : ClassTag](prefix: Array[_ <: B]): Array[B] = { + val dest = Array.copyAs[B](prefix, prefix.length+xs.length) + Array.copy(xs, 0, dest, prefix.length, xs.length) + dest + } + + @`inline` final def ++: [B >: A : ClassTag](prefix: IterableOnce[B]): Array[B] = prependedAll(prefix) + + @`inline` final def ++: [B >: A : ClassTag](prefix: Array[_ <: B]): Array[B] = prependedAll(prefix) + + /** A copy of this array with all elements of a collection appended. */ + def appendedAll[B >: A : ClassTag](suffix: IterableOnce[B]): Array[B] = { + val b = ArrayBuilder.make[B] + b.sizeHint(suffix, delta = xs.length) + b.addAll(xs) + b.addAll(suffix) + b.result() + } + + /** A copy of this array with all elements of an array appended. */ + def appendedAll[B >: A : ClassTag](suffix: Array[_ <: B]): Array[B] = { + val dest = Array.copyAs[B](xs, xs.length+suffix.length) + Array.copy(suffix, 0, dest, xs.length, suffix.length) + dest + } + + @`inline` final def :++ [B >: A : ClassTag](suffix: IterableOnce[B]): Array[B] = appendedAll(suffix) + + @`inline` final def :++ [B >: A : ClassTag](suffix: Array[_ <: B]): Array[B] = appendedAll(suffix) + + @`inline` final def concat[B >: A : ClassTag](suffix: IterableOnce[B]): Array[B] = appendedAll(suffix) + + @`inline` final def concat[B >: A : ClassTag](suffix: Array[_ <: B]): Array[B] = appendedAll(suffix) + + @`inline` final def ++[B >: A : ClassTag](xs: IterableOnce[B]): Array[B] = appendedAll(xs) + + @`inline` final def ++[B >: A : ClassTag](xs: Array[_ <: B]): Array[B] = appendedAll(xs) + + /** Tests whether this array contains a given value as an element. + * + * @param elem the element to test. + * @return `true` if this array has an element that is equal (as + * determined by `==`) to `elem`, `false` otherwise. + */ + def contains(elem: A): Boolean = exists (_ == elem) + + /** Returns a copy of this array with patched values. + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original array appends the patch to the end. + * If more values are replaced than actually exist, the excess is ignored. + * + * @param from The start index from which to patch + * @param other The patch values + * @param replaced The number of values in the original array that are replaced by the patch. + */ + def patch[B >: A : ClassTag](from: Int, other: IterableOnce[B], replaced: Int): Array[B] = { + val b = ArrayBuilder.make[B] + val k = other.knownSize + val r = if(replaced < 0) 0 else replaced + if(k >= 0) b.sizeHint(xs.length + k - r) + val chunk1 = if(from > 0) min(from, xs.length) else 0 + if(chunk1 > 0) b.addAll(xs, 0, chunk1) + b ++= other + val remaining = xs.length - chunk1 - r + if(remaining > 0) b.addAll(xs, xs.length - remaining, remaining) + b.result() + } + + /** Converts an array of pairs into an array of first elements and an array of second elements. + * + * @tparam A1 the type of the first half of the element pairs + * @tparam A2 the type of the second half of the element pairs + * @param asPair an implicit conversion which asserts that the element type + * of this Array is a pair. + * @param ct1 a class tag for `A1` type parameter that is required to create an instance + * of `Array[A1]` + * @param ct2 a class tag for `A2` type parameter that is required to create an instance + * of `Array[A2]` + * @return a pair of Arrays, containing, respectively, the first and second half + * of each element pair of this Array. + */ + def unzip[A1, A2](implicit asPair: A => (A1, A2), ct1: ClassTag[A1], ct2: ClassTag[A2]): (Array[A1], Array[A2]) = { + val a1 = new Array[A1](xs.length) + val a2 = new Array[A2](xs.length) + var i = 0 + while (i < xs.length) { + val e = asPair(xs(i)) + a1(i) = e._1 + a2(i) = e._2 + i += 1 + } + (a1, a2) + } + + /** Converts an array of triples into three arrays, one containing the elements from each position of the triple. + * + * @tparam A1 the type of the first of three elements in the triple + * @tparam A2 the type of the second of three elements in the triple + * @tparam A3 the type of the third of three elements in the triple + * @param asTriple an implicit conversion which asserts that the element type + * of this Array is a triple. + * @param ct1 a class tag for T1 type parameter that is required to create an instance + * of Array[T1] + * @param ct2 a class tag for T2 type parameter that is required to create an instance + * of Array[T2] + * @param ct3 a class tag for T3 type parameter that is required to create an instance + * of Array[T3] + * @return a triple of Arrays, containing, respectively, the first, second, and third + * elements from each element triple of this Array. + */ + def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3), ct1: ClassTag[A1], ct2: ClassTag[A2], + ct3: ClassTag[A3]): (Array[A1], Array[A2], Array[A3]) = { + val a1 = new Array[A1](xs.length) + val a2 = new Array[A2](xs.length) + val a3 = new Array[A3](xs.length) + var i = 0 + while (i < xs.length) { + val e = asTriple(xs(i)) + a1(i) = e._1 + a2(i) = e._2 + a3(i) = e._3 + i += 1 + } + (a1, a2, a3) + } + + /** Transposes a two dimensional array. + * + * @tparam B Type of row elements. + * @param asArray A function that converts elements of this array to rows - arrays of type `B`. + * @return An array obtained by replacing elements of this arrays with rows the represent. + */ + def transpose[B](implicit asArray: A => Array[B]): Array[Array[B]] = { + val aClass = xs.getClass.getComponentType + val bb = new ArrayBuilder.ofRef[Array[B]]()(ClassTag[Array[B]](aClass)) + if (xs.length == 0) bb.result() + else { + def mkRowBuilder() = ArrayBuilder.make[B](using ClassTag[B](aClass.getComponentType)) + val bs = new ArrayOps(asArray(xs(0))).map((x: B) => mkRowBuilder()) + for (xs <- this) { + var i = 0 + for (x <- new ArrayOps(asArray(xs))) { + bs(i) += x + i += 1 + } + } + for (b <- new ArrayOps(bs)) bb += b.result() + bb.result() + } + } + + /** Apply `f` to each element for its side effects. + * Note: [U] parameter needed to help scalac's type inference. + */ + def foreach[U](f: A => U): Unit = { + val len = xs.length + var i = 0 + (xs: Any @unchecked) match { + case xs: Array[AnyRef] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Int] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Double] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Long] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Float] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Char] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Byte] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Short] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + case xs: Array[Boolean] => while (i < len) { f(xs(i).asInstanceOf[A]); i = i+1 } + } + } + + /** Selects all the elements of this array ignoring the duplicates. + * + * @return a new array consisting of all the elements of this array without duplicates. + */ + def distinct: Array[A] = distinctBy(identity) + + /** Selects all the elements of this array ignoring the duplicates as determined by `==` after applying + * the transforming function `f`. + * + * @param f The transforming function whose result is used to determine the uniqueness of each element + * @tparam B the type of the elements after being transformed by `f` + * @return a new array consisting of all the elements of this array without duplicates. + */ + def distinctBy[B](f: A => B): Array[A] = + ArrayBuilder.make[A].addAll(iterator.distinctBy(f)).result() + + /** A copy of this array with an element value appended until a given target length is reached. + * + * @param len the target length + * @param elem the padding value + * @tparam B the element type of the returned array. + * @return a new array consisting of + * all elements of this array followed by the minimal number of occurrences of `elem` so + * that the resulting collection has a length of at least `len`. + */ + def padTo[B >: A : ClassTag](len: Int, elem: B): Array[B] = { + var i = xs.length + val newlen = max(i, len) + val dest = Array.copyAs[B](xs, newlen) + while(i < newlen) { + dest(i) = elem + i += 1 + } + dest + } + + /** Produces the range of all indices of this sequence. + * + * @return a `Range` value from `0` to one less than the length of this array. + */ + def indices: Range = Range(0, xs.length) + + /** Partitions this array into a map of arrays according to some discriminator function. + * + * @param f the discriminator function. + * @tparam K the type of keys returned by the discriminator function. + * @return A map from keys to arrays such that the following invariant holds: + * {{{ + * (xs groupBy f)(k) = xs filter (x => f(x) == k) + * }}} + * That is, every key `k` is bound to an array of those elements `x` + * for which `f(x)` equals `k`. + */ + def groupBy[K](f: A => K): immutable.Map[K, Array[A]] = { + val m = mutable.Map.empty[K, ArrayBuilder[A]] + val len = xs.length + var i = 0 + while(i < len) { + val elem = xs(i) + val key = f(elem) + val bldr = m.getOrElseUpdate(key, ArrayBuilder.make[A]) + bldr += elem + i += 1 + } + m.view.mapValues(_.result()).toMap + } + + /** + * Partitions this array into a map of arrays according to a discriminator function `key`. + * Each element in a group is transformed into a value of type `B` using the `value` function. + * + * It is equivalent to `groupBy(key).mapValues(_.map(f))`, but more efficient. + * + * {{{ + * case class User(name: String, age: Int) + * + * def namesByAge(users: Array[User]): Map[Int, Array[String]] = + * users.groupMap(_.age)(_.name) + * }}} + * + * @param key the discriminator function + * @param f the element transformation function + * @tparam K the type of keys returned by the discriminator function + * @tparam B the type of values returned by the transformation function + */ + def groupMap[K, B : ClassTag](key: A => K)(f: A => B): immutable.Map[K, Array[B]] = { + val m = mutable.Map.empty[K, ArrayBuilder[B]] + val len = xs.length + var i = 0 + while(i < len) { + val elem = xs(i) + val k = key(elem) + val bldr = m.getOrElseUpdate(k, ArrayBuilder.make[B]) + bldr += f(elem) + i += 1 + } + m.view.mapValues(_.result()).toMap + } + + @`inline` final def toSeq: immutable.Seq[A] = toIndexedSeq + + def toIndexedSeq: immutable.IndexedSeq[A] = + immutable.ArraySeq.unsafeWrapArray(Array.copyOf(xs, xs.length)) + + /** Copy elements of this array to another array. + * Fills the given array `xs` starting at index 0. + * Copying will stop once either all the elements of this array have been copied, + * or the end of the array is reached. + * + * @param xs the array to fill. + * @tparam B the type of the elements of the array. + */ + def copyToArray[B >: A](xs: Array[B]): Int = copyToArray(xs, 0) + + /** Copy elements of this array to another array. + * Fills the given array `xs` starting at index `start`. + * Copying will stop once either all the elements of this array have been copied, + * or the end of the array is reached. + * + * @param xs the array to fill. + * @param start the starting index within the destination array. + * @tparam B the type of the elements of the array. + */ + def copyToArray[B >: A](xs: Array[B], start: Int): Int = copyToArray(xs, start, Int.MaxValue) + + /** Copy elements of this array to another array. + * Fills the given array `xs` starting at index `start` with at most `len` values. + * Copying will stop once either all the elements of this array have been copied, + * or the end of the array is reached, or `len` elements have been copied. + * + * @param xs the array to fill. + * @param start the starting index within the destination array. + * @param len the maximal number of elements to copy. + * @tparam B the type of the elements of the array. + */ + def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { + val copied = IterableOnce.elemsToCopyToArray(this.xs.length, xs.length, start, len) + if (copied > 0) { + Array.copy(this.xs, 0, xs, start, copied) + } + copied + } + + /** Create a copy of this array with the specified element type. */ + def toArray[B >: A: ClassTag]: Array[B] = { + val destination = new Array[B](xs.length) + @annotation.unused val copied = copyToArray(destination, 0) + //assert(copied == xs.length) + destination + } + + /** Counts the number of elements in this array which satisfy a predicate */ + def count(p: A => Boolean): Int = { + var i, res = 0 + val len = xs.length + while(i < len) { + if(p(xs(i))) res += 1 + i += 1 + } + res + } + + // can't use a default arg because we already have another overload with a default arg + /** Tests whether this array starts with the given array. */ + @`inline` def startsWith[B >: A](that: Array[B]): Boolean = startsWith(that, 0) + + /** Tests whether this array contains the given array at a given index. + * + * @param that the array to test + * @param offset the index where the array is searched. + * @return `true` if the array `that` is contained in this array at + * index `offset`, otherwise `false`. + */ + def startsWith[B >: A](that: Array[B], offset: Int): Boolean = { + val safeOffset = offset.max(0) + val thatl = that.length + if(thatl > xs.length-safeOffset) thatl == 0 + else { + var i = 0 + while(i < thatl) { + if(xs(i+safeOffset) != that(i)) return false + i += 1 + } + true + } + } + + /** Tests whether this array ends with the given array. + * + * @param that the array to test + * @return `true` if this array has `that` as a suffix, `false` otherwise. + */ + def endsWith[B >: A](that: Array[B]): Boolean = { + val thatl = that.length + val off = xs.length - thatl + if(off < 0) false + else { + var i = 0 + while(i < thatl) { + if(xs(i+off) != that(i)) return false + i += 1 + } + true + } + } + + /** A copy of this array with one single replaced element. + * @param index the position of the replacement + * @param elem the replacing element + * @return a new array which is a copy of this array with the element at position `index` replaced by `elem`. + * @throws IndexOutOfBoundsException if `index` does not satisfy `0 <= index < length`. + */ + def updated[B >: A : ClassTag](index: Int, elem: B): Array[B] = { + if(index < 0 || index >= xs.length) + throw CommonErrors.indexOutOfBounds(index = index, max = xs.length-1) + val dest = toArray[B] + dest(index) = elem + dest + } + + @`inline` def view: IndexedSeqView[A] = new ArrayOps.ArrayView[A](xs) + + + /* ************************************************************************************************************ + The remaining methods are provided for completeness but they delegate to mutable.ArraySeq implementations which + may not provide the best possible performance. We need them in `ArrayOps` because their return type + mentions `C` (which is `Array[A]` in `StringOps` and `mutable.ArraySeq[A]` in `mutable.ArraySeq`). + ************************************************************************************************************ */ + + + /** Computes the multiset difference between this array and another sequence. + * + * @param that the sequence of elements to remove + * @return a new array which contains all elements of this array + * except some of occurrences of elements that also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form + * part of the result, but any following occurrences will. + */ + def diff[B >: A](that: Seq[B]): Array[A] = mutable.ArraySeq.make(xs).diff(that).toArray[A] + + /** Computes the multiset intersection between this array and another sequence. + * + * @param that the sequence of elements to intersect with. + * @return a new array which contains all elements of this array + * which also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained + * in the result, but any following occurrences will be omitted. + */ + def intersect[B >: A](that: Seq[B]): Array[A] = mutable.ArraySeq.make(xs).intersect(that).toArray[A] + + /** Groups elements in fixed size blocks by passing a "sliding window" + * over them (as opposed to partitioning them, as is done in grouped.) + * @see [[scala.collection.Iterator]], method `sliding` + * + * @param size the number of elements per group + * @param step the distance between the first elements of successive groups + * @return An iterator producing arrays of size `size`, except the + * last element (which may be the only element) will be truncated + * if there are fewer than `size` elements remaining to be grouped. + */ + def sliding(size: Int, step: Int = 1): Iterator[Array[A]] = mutable.ArraySeq.make(xs).sliding(size, step).map(_.toArray[A]) + + /** Iterates over combinations of elements. + * + * A '''combination''' of length `n` is a sequence of `n` elements selected in order of their first index in this sequence. + * + * For example, `"xyx"` has two combinations of length 2. The `x` is selected first: `"xx"`, `"xy"`. + * The sequence `"yx"` is not returned as a combination because it is subsumed by `"xy"`. + * + * If there is more than one way to generate the same combination, only one will be returned. + * + * For example, the result `"xy"` arbitrarily selected one of the `x` elements. + * + * As a further illustration, `"xyxx"` has three different ways to generate `"xy"` because there are three elements `x` + * to choose from. Moreover, there are three unordered pairs `"xx"` but only one is returned. + * + * It is not specified which of these equal combinations is returned. It is an implementation detail + * that should not be relied on. For example, the combination `"xx"` does not necessarily contain + * the first `x` in this sequence. This behavior is observable if the elements compare equal + * but are not identical. + * + * As a consequence, `"xyx".combinations(3).next()` is `"xxy"`: the combination does not reflect the order + * of the original sequence, but the order in which elements were selected, by "first index"; + * the order of each `x` element is also arbitrary. + * + * @return An Iterator which traverses the n-element combinations of this array + * @example {{{ + * Array('a', 'b', 'b', 'b', 'c').combinations(2).map(runtime.ScalaRunTime.stringOf).foreach(println) + * // Array(a, b) + * // Array(a, c) + * // Array(b, b) + * // Array(b, c) + * Array('b', 'a', 'b').combinations(2).map(runtime.ScalaRunTime.stringOf).foreach(println) + * // Array(b, b) + * // Array(b, a) + * }}} + */ + def combinations(n: Int): Iterator[Array[A]] = mutable.ArraySeq.make(xs).combinations(n).map(_.toArray[A]) + + /** Iterates over distinct permutations of elements. + * + * @return An Iterator which traverses the distinct permutations of this array. + * @example {{{ + * Array('a', 'b', 'b').permutations.map(runtime.ScalaRunTime.stringOf).foreach(println) + * // Array(a, b, b) + * // Array(b, a, b) + * // Array(b, b, a) + * }}} + */ + def permutations: Iterator[Array[A]] = mutable.ArraySeq.make(xs).permutations.map(_.toArray[A]) + + // we have another overload here, so we need to duplicate this method + /** Tests whether this array contains the given sequence at a given index. + * + * @param that the sequence to test + * @param offset the index where the sequence is searched. + * @return `true` if the sequence `that` is contained in this array at + * index `offset`, otherwise `false`. + */ + def startsWith[B >: A](that: IterableOnce[B], offset: Int = 0): Boolean = mutable.ArraySeq.make(xs).startsWith(that, offset) + + // we have another overload here, so we need to duplicate this method + /** Tests whether this array ends with the given sequence. + * + * @param that the sequence to test + * @return `true` if this array has `that` as a suffix, `false` otherwise. + */ + def endsWith[B >: A](that: Iterable[B]): Boolean = mutable.ArraySeq.make(xs).endsWith(that) +} diff --git a/library/src/scala/collection/BitSet.scala b/library/src/scala/collection/BitSet.scala new file mode 100644 index 000000000000..a2da58ea3b9b --- /dev/null +++ b/library/src/scala/collection/BitSet.scala @@ -0,0 +1,348 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import java.io.{ObjectInputStream, ObjectOutputStream} + +import scala.annotation.nowarn +import scala.collection.Stepper.EfficientSplit +import scala.collection.mutable.Builder + + +/** Base type of bitsets. + * + * This trait provides most of the operations of a `BitSet` independently of its representation. + * It is inherited by all concrete implementations of bitsets. + * + * @define bitsetinfo + * Bitsets are sets of non-negative integers which are represented as + * variable-size arrays of bits packed into 64-bit words. The lower bound of memory footprint of a bitset is + * determined by the largest number stored in it. + * @define coll bitset + * @define Coll `BitSet` + */ +trait BitSet extends SortedSet[Int] with BitSetOps[BitSet] { + override protected def fromSpecific(coll: IterableOnce[Int]): BitSet = bitSetFactory.fromSpecific(coll) + override protected def newSpecificBuilder: Builder[Int, BitSet] = bitSetFactory.newBuilder + override def empty: BitSet = bitSetFactory.empty + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "BitSet" + override def unsorted: Set[Int] = this +} + +@SerialVersionUID(3L) +object BitSet extends SpecificIterableFactory[Int, BitSet] { + private[collection] final val ordMsg = "No implicit Ordering[${B}] found to build a SortedSet[${B}]. You may want to upcast to a Set[Int] first by calling `unsorted`." + private[collection] final val zipOrdMsg = "No implicit Ordering[${B}] found to build a SortedSet[(Int, ${B})]. You may want to upcast to a Set[Int] first by calling `unsorted`." + + def empty: BitSet = immutable.BitSet.empty + def newBuilder: Builder[Int, BitSet] = immutable.BitSet.newBuilder + def fromSpecific(it: IterableOnce[Int]): BitSet = immutable.BitSet.fromSpecific(it) + + @SerialVersionUID(3L) + private[collection] abstract class SerializationProxy(@transient protected val coll: BitSet) extends Serializable { + + @transient protected var elems: Array[Long] = _ + + private[this] def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + val nwords = coll.nwords + out.writeInt(nwords) + var i = 0 + while(i < nwords) { + out.writeLong(coll.word(i)) + i += 1 + } + } + + private[this] def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val nwords = in.readInt() + elems = new Array[Long](nwords) + var i = 0 + while(i < nwords) { + elems(i) = in.readLong() + i += 1 + } + } + + protected[this] def readResolve(): Any + } +} + +/** Base implementation type of bitsets */ +trait BitSetOps[+C <: BitSet with BitSetOps[C]] + extends SortedSetOps[Int, SortedSet, C] { self => + import BitSetOps._ + + def bitSetFactory: SpecificIterableFactory[Int, C] + + def unsorted: Set[Int] + + final def ordering: Ordering[Int] = Ordering.Int + + /** The number of words (each with 64 bits) making up the set */ + protected[collection] def nwords: Int + + /** The words at index `idx`, or 0L if outside the range of the set + * '''Note:''' requires `idx >= 0` + */ + protected[collection] def word(idx: Int): Long + + /** Creates a new set of this kind from an array of longs + */ + protected[collection] def fromBitMaskNoCopy(elems: Array[Long]): C + + def contains(elem: Int): Boolean = + 0 <= elem && (word(elem >> LogWL) & (1L << elem)) != 0L + + def iterator: Iterator[Int] = iteratorFrom(0) + + def iteratorFrom(start: Int): Iterator[Int] = new AbstractIterator[Int] { + private[this] var currentPos = if (start > 0) start >> LogWL else 0 + private[this] var currentWord = if (start > 0) word(currentPos) & (-1L << (start & (WordLength - 1))) else word(0) + final override def hasNext: Boolean = { + while (currentWord == 0) { + if (currentPos + 1 >= nwords) return false + currentPos += 1 + currentWord = word(currentPos) + } + true + } + final override def next(): Int = { + if (hasNext) { + val bitPos = java.lang.Long.numberOfTrailingZeros(currentWord) + currentWord &= currentWord - 1 + (currentPos << LogWL) + bitPos + } else Iterator.empty.next() + } + } + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Int, S]): S with EfficientSplit = { + val st = scala.collection.convert.impl.BitSetStepper.from(this) + val r = + if (shape.shape == StepperShape.IntShape) st + else { + assert(shape.shape == StepperShape.ReferenceShape, s"unexpected StepperShape: $shape") + AnyStepper.ofParIntStepper(st) + } + r.asInstanceOf[S with EfficientSplit] + } + + override def size: Int = { + var s = 0 + var i = nwords + while (i > 0) { + i -= 1 + s += java.lang.Long.bitCount(word(i)) + } + s + } + + override def isEmpty: Boolean = 0 until nwords forall (i => word(i) == 0) + + @inline private[this] def smallestInt: Int = { + val thisnwords = nwords + var i = 0 + while(i < thisnwords) { + val currentWord = word(i) + if (currentWord != 0L) { + return java.lang.Long.numberOfTrailingZeros(currentWord) + (i * WordLength) + } + i += 1 + } + throw new UnsupportedOperationException("empty.smallestInt") + } + + @inline private[this] def largestInt: Int = { + var i = nwords - 1 + while(i >= 0) { + val currentWord = word(i) + if (currentWord != 0L) { + return ((i + 1) * WordLength) - java.lang.Long.numberOfLeadingZeros(currentWord) - 1 + } + i -= 1 + } + throw new UnsupportedOperationException("empty.largestInt") + } + + override def max[B >: Int](implicit ord: Ordering[B]): Int = + if (Ordering.Int eq ord) largestInt + else if (Ordering.Int isReverseOf ord) smallestInt + else super.max(ord) + + + override def min[B >: Int](implicit ord: Ordering[B]): Int = + if (Ordering.Int eq ord) smallestInt + else if (Ordering.Int isReverseOf ord) largestInt + else super.min(ord) + + override def foreach[U](f: Int => U): Unit = { + /* NOTE: while loops are significantly faster as of 2.11 and + one major use case of bitsets is performance. Also, there + is nothing to do when all bits are clear, so use that as + the inner loop condition. */ + var i = 0 + while (i < nwords) { + var w = word(i) + var j = i * WordLength + while (w != 0L) { + if ((w&1L) == 1L) f(j) + w = w >>> 1 + j += 1 + } + i += 1 + } + } + + /** Creates a bit mask for this set as a new array of longs + */ + def toBitMask: Array[Long] = { + val a = new Array[Long](nwords) + var i = a.length + while(i > 0) { + i -= 1 + a(i) = word(i) + } + a + } + + def rangeImpl(from: Option[Int], until: Option[Int]): C = { + val a = coll.toBitMask + val len = a.length + if (from.isDefined) { + val f = from.get + val w = f >> LogWL + val b = f & (WordLength - 1) + if (w >= 0) { + java.util.Arrays.fill(a, 0, math.min(w, len), 0) + if (b > 0 && w < len) a(w) &= ~((1L << b) - 1) + } + } + if (until.isDefined) { + val u = until.get + val w = u >> LogWL + val b = u & (WordLength - 1) + if (w < len) { + java.util.Arrays.fill(a, math.max(w + 1, 0), len, 0) + if (w >= 0) a(w) &= (1L << b) - 1 + } + } + coll.fromBitMaskNoCopy(a) + } + + override def concat(other: collection.IterableOnce[Int]): C = other match { + case otherBitset: BitSet => + val len = coll.nwords max otherBitset.nwords + val words = new Array[Long](len) + for (idx <- 0 until len) + words(idx) = this.word(idx) | otherBitset.word(idx) + fromBitMaskNoCopy(words) + case _ => super.concat(other) + } + + override def intersect(other: Set[Int]): C = other match { + case otherBitset: BitSet => + val len = coll.nwords min otherBitset.nwords + val words = new Array[Long](len) + for (idx <- 0 until len) + words(idx) = this.word(idx) & otherBitset.word(idx) + fromBitMaskNoCopy(words) + case _ => super.intersect(other) + } + + abstract override def diff(other: Set[Int]): C = other match { + case otherBitset: BitSet => + val len = coll.nwords + val words = new Array[Long](len) + for (idx <- 0 until len) + words(idx) = this.word(idx) & ~otherBitset.word(idx) + fromBitMaskNoCopy(words) + case _ => super.diff(other) + } + + /** Computes the symmetric difference of this bitset and another bitset by performing + * a bitwise "exclusive-or". + * + * @param other the other bitset to take part in the symmetric difference. + * @return a bitset containing those bits of this + * bitset or the other bitset that are not contained in both bitsets. + */ + def xor(other: BitSet): C = { + val len = coll.nwords max other.nwords + val words = new Array[Long](len) + for (idx <- 0 until len) + words(idx) = coll.word(idx) ^ other.word(idx) + coll.fromBitMaskNoCopy(words) + } + + @`inline` final def ^ (other: BitSet): C = xor(other) + + /** + * Builds a new bitset by applying a function to all elements of this bitset + * @param f the function to apply to each element. + * @return a new bitset resulting from applying the given function ''f'' to + * each element of this bitset and collecting the results + */ + def map(f: Int => Int): C = fromSpecific(new View.Map(this, f)) + + def flatMap(f: Int => IterableOnce[Int]): C = fromSpecific(new View.FlatMap(this, f)) + + def collect(pf: PartialFunction[Int, Int]): C = fromSpecific(super[SortedSetOps].collect(pf)) + + override def partition(p: Int => Boolean): (C, C) = { + val left = filter(p) + (left, this &~ left) + } +} + +object BitSetOps { + + /* Final vals can sometimes be inlined as constants (faster) */ + private[collection] final val LogWL = 6 + private[collection] final val WordLength = 64 + private[collection] final val MaxSize = (Int.MaxValue >> LogWL) + 1 + + private[collection] def updateArray(elems: Array[Long], idx: Int, w: Long): Array[Long] = { + var len = elems.length + while (len > 0 && (elems(len - 1) == 0L || w == 0L && idx == len - 1)) len -= 1 + var newlen = len + if (idx >= newlen && w != 0L) newlen = idx + 1 + val newelems = new Array[Long](newlen) + Array.copy(elems, 0, newelems, 0, len) + if (idx < newlen) newelems(idx) = w + else assert(w == 0L) + newelems + } + + private[collection] def computeWordForFilter(pred: Int => Boolean, isFlipped: Boolean, oldWord: Long, wordIndex: Int): Long = + if (oldWord == 0L) 0L else { + var w = oldWord + val trailingZeroes = java.lang.Long.numberOfTrailingZeros(w) + var jmask = 1L << trailingZeroes + var j = wordIndex * BitSetOps.WordLength + trailingZeroes + val maxJ = (wordIndex + 1) * BitSetOps.WordLength - java.lang.Long.numberOfLeadingZeros(w) + while (j != maxJ) { + if ((w & jmask) != 0L) { + if (pred(j) == isFlipped) { + // j did not pass the filter here + w = w & ~jmask + } + } + jmask = jmask << 1 + j += 1 + } + w + } +} diff --git a/library/src/scala/collection/BufferedIterator.scala b/library/src/scala/collection/BufferedIterator.scala new file mode 100644 index 000000000000..b5a7f9658422 --- /dev/null +++ b/library/src/scala/collection/BufferedIterator.scala @@ -0,0 +1,32 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + + +/** Buffered iterators are iterators which provide a method `head` + * that inspects the next element without discarding it. + */ +trait BufferedIterator[+A] extends Iterator[A] { + + /** Returns next element of iterator without advancing beyond it. + */ + def head: A + + /** Returns an option of the next element of an iterator without advancing beyond it. + * @return the next element of this iterator if it has a next element + * `None` if it does not + */ + def headOption : Option[A] = if (hasNext) Some(head) else None + + override def buffered: this.type = this +} diff --git a/library/src/scala/collection/BuildFrom.scala b/library/src/scala/collection/BuildFrom.scala new file mode 100644 index 000000000000..0530e4445bd5 --- /dev/null +++ b/library/src/scala/collection/BuildFrom.scala @@ -0,0 +1,122 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.annotation.implicitNotFound +import scala.collection.mutable.Builder +import scala.collection.immutable.WrappedString +import scala.reflect.ClassTag + +/** Builds a collection of type `C` from elements of type `A` when a source collection of type `From` is available. + * Implicit instances of `BuildFrom` are available for all collection types. + * + * @tparam From Type of source collection + * @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.) + * @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.) + */ +@implicitNotFound(msg = "Cannot construct a collection of type ${C} with elements of type ${A} based on a collection of type ${From}.") +trait BuildFrom[-From, -A, +C] extends Any { self => + def fromSpecific(from: From)(it: IterableOnce[A]): C + + /** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer. + * Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */ + def newBuilder(from: From): Builder[A, C] + + @deprecated("Use newBuilder() instead of apply()", "2.13.0") + @`inline` def apply(from: From): Builder[A, C] = newBuilder(from) + + /** Partially apply a BuildFrom to a Factory */ + def toFactory(from: From): Factory[A, C] = new Factory[A, C] { + def fromSpecific(it: IterableOnce[A]): C = self.fromSpecific(from)(it) + def newBuilder: Builder[A, C] = self.newBuilder(from) + } +} + +object BuildFrom extends BuildFromLowPriority1 { + + /** Build the source collection type from a MapOps */ + implicit def buildFromMapOps[CC[X, Y] <: Map[X, Y] with MapOps[X, Y, CC, _], K0, V0, K, V]: BuildFrom[CC[K0, V0] with Map[K0, V0], (K, V), CC[K, V] with Map[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { + //TODO: Reuse a prototype instance + def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: MapOps[K0, V0, CC, _]).mapFactory.newBuilder[K, V] + def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: MapOps[K0, V0, CC, _]).mapFactory.from(it) + } + + /** Build the source collection type from a SortedMapOps */ + implicit def buildFromSortedMapOps[CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], K0, V0, K : Ordering, V]: BuildFrom[CC[K0, V0] with SortedMap[K0, V0], (K, V), CC[K, V] with SortedMap[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { + def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.newBuilder[K, V] + def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.from(it) + } + + implicit def buildFromBitSet[C <: BitSet with BitSetOps[C]]: BuildFrom[C, Int, C] = + new BuildFrom[C, Int, C] { + def fromSpecific(from: C)(it: IterableOnce[Int]): C = from.bitSetFactory.fromSpecific(it) + def newBuilder(from: C): Builder[Int, C] = from.bitSetFactory.newBuilder + } + + implicit val buildFromString: BuildFrom[String, Char, String] = + new BuildFrom[String, Char, String] { + def fromSpecific(from: String)(it: IterableOnce[Char]): String = Factory.stringFactory.fromSpecific(it) + def newBuilder(from: String): Builder[Char, String] = Factory.stringFactory.newBuilder + } + + implicit val buildFromWrappedString: BuildFrom[WrappedString, Char, WrappedString] = + new BuildFrom[WrappedString, Char, WrappedString] { + def fromSpecific(from: WrappedString)(it: IterableOnce[Char]): WrappedString = WrappedString.fromSpecific(it) + def newBuilder(from: WrappedString): mutable.Builder[Char, WrappedString] = WrappedString.newBuilder + } + + implicit def buildFromArray[A : ClassTag]: BuildFrom[Array[_], A, Array[A]] = + new BuildFrom[Array[_], A, Array[A]] { + def fromSpecific(from: Array[_])(it: IterableOnce[A]): Array[A] = Factory.arrayFactory[A].fromSpecific(it) + def newBuilder(from: Array[_]): Builder[A, Array[A]] = Factory.arrayFactory[A].newBuilder + } + + implicit def buildFromView[A, B]: BuildFrom[View[A], B, View[B]] = + new BuildFrom[View[A], B, View[B]] { + def fromSpecific(from: View[A])(it: IterableOnce[B]): View[B] = View.from(it) + def newBuilder(from: View[A]): Builder[B, View[B]] = View.newBuilder + } + +} + +trait BuildFromLowPriority1 extends BuildFromLowPriority2 { + + /** Build the source collection type from an Iterable with SortedOps */ + // Restating the upper bound of CC in the result type seems redundant, but it serves to prune the + // implicit search space for faster compilation and reduced change of divergence. See the compilation + // test in test/junit/scala/collection/BuildFromTest.scala and discussion in https://github.com/scala/scala/pull/10209 + implicit def buildFromSortedSetOps[CC[X] <: SortedSet[X] with SortedSetOps[X, CC, _], A0, A : Ordering]: BuildFrom[CC[A0] with SortedSet[A0], A, CC[A] with SortedSet[A]] = new BuildFrom[CC[A0], A, CC[A]] { + def newBuilder(from: CC[A0]): Builder[A, CC[A]] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.newBuilder[A] + def fromSpecific(from: CC[A0])(it: IterableOnce[A]): CC[A] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.from(it) + } + + implicit def fallbackStringCanBuildFrom[A]: BuildFrom[String, A, immutable.IndexedSeq[A]] = + new BuildFrom[String, A, immutable.IndexedSeq[A]] { + def fromSpecific(from: String)(it: IterableOnce[A]): immutable.IndexedSeq[A] = immutable.IndexedSeq.from(it) + def newBuilder(from: String): Builder[A, immutable.IndexedSeq[A]] = immutable.IndexedSeq.newBuilder[A] + } +} + +trait BuildFromLowPriority2 { + /** Build the source collection type from an IterableOps */ + implicit def buildFromIterableOps[CC[X] <: Iterable[X] with IterableOps[X, CC, _], A0, A]: BuildFrom[CC[A0], A, CC[A]] = new BuildFrom[CC[A0], A, CC[A]] { + //TODO: Reuse a prototype instance + def newBuilder(from: CC[A0]): Builder[A, CC[A]] = (from: IterableOps[A0, CC, _]).iterableFactory.newBuilder[A] + def fromSpecific(from: CC[A0])(it: IterableOnce[A]): CC[A] = (from: IterableOps[A0, CC, _]).iterableFactory.from(it) + } + + implicit def buildFromIterator[A]: BuildFrom[Iterator[_], A, Iterator[A]] = new BuildFrom[Iterator[_], A, Iterator[A]] { + def newBuilder(from: Iterator[_]): mutable.Builder[A, Iterator[A]] = Iterator.newBuilder + def fromSpecific(from: Iterator[_])(it: IterableOnce[A]): Iterator[A] = Iterator.from(it) + } +} diff --git a/library/src/scala/collection/DefaultMap.scala b/library/src/scala/collection/DefaultMap.scala new file mode 100644 index 000000000000..ca7d2a67f757 --- /dev/null +++ b/library/src/scala/collection/DefaultMap.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + + +/** A default map which builds a default `immutable.Map` implementation for all + * transformations. + */ +@deprecated("DefaultMap is no longer necessary; extend Map directly", "2.13.0") +trait DefaultMap[K, +V] extends Map[K, V] diff --git a/library/src/scala/collection/Factory.scala b/library/src/scala/collection/Factory.scala new file mode 100644 index 000000000000..7f56c20f7c9c --- /dev/null +++ b/library/src/scala/collection/Factory.scala @@ -0,0 +1,784 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.collection.immutable.NumericRange +import scala.language.implicitConversions +import scala.collection.mutable.Builder +import scala.annotation.unchecked.uncheckedVariance +import scala.reflect.ClassTag + +/** + * A factory that builds a collection of type `C` with elements of type `A`. + * + * This is a general form of any factory ([[IterableFactory]], + * [[SortedIterableFactory]], [[MapFactory]] and [[SortedMapFactory]]) whose + * element type is fixed. + * + * @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.) + * @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.) + */ +trait Factory[-A, +C] extends Any { + + /** + * @return A collection of type `C` containing the same elements + * as the source collection `it`. + * @param it Source collection + */ + def fromSpecific(it: IterableOnce[A]): C + + /** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer. + * Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */ + def newBuilder: Builder[A, C] +} + +object Factory { + + implicit val stringFactory: Factory[Char, String] = new StringFactory + @SerialVersionUID(3L) + private class StringFactory extends Factory[Char, String] with Serializable { + def fromSpecific(it: IterableOnce[Char]): String = { + val b = new mutable.StringBuilder(scala.math.max(0, it.knownSize)) + b ++= it + b.result() + } + def newBuilder: Builder[Char, String] = new mutable.StringBuilder() + } + + implicit def arrayFactory[A: ClassTag]: Factory[A, Array[A]] = new ArrayFactory[A] + @SerialVersionUID(3L) + private class ArrayFactory[A: ClassTag] extends Factory[A, Array[A]] with Serializable { + def fromSpecific(it: IterableOnce[A]): Array[A] = { + val b = newBuilder + b.sizeHint(it, delta = 0) + b ++= it + b.result() + } + def newBuilder: Builder[A, Array[A]] = mutable.ArrayBuilder.make[A] + } + +} + +/** Base trait for companion objects of unconstrained collection types that may require + * multiple traversals of a source collection to build a target collection `CC`. + * + * @tparam CC Collection type constructor (e.g. `List`) + * @define factoryInfo + * This object provides a set of operations to create $Coll values. + * + * @define coll collection + * @define Coll `Iterable` + */ +trait IterableFactory[+CC[_]] extends Serializable { + + /** Creates a target $coll from an existing source collection + * + * @param source Source collection + * @tparam A the type of the collection’s elements + * @return a new $coll with the elements of `source` + */ + def from[A](source: IterableOnce[A]): CC[A] + + /** An empty collection + * @tparam A the type of the ${coll}'s elements + */ + def empty[A]: CC[A] + + /** Creates a $coll with the specified elements. + * @tparam A the type of the ${coll}'s elements + * @param elems the elements of the created $coll + * @return a new $coll with elements `elems` + */ + def apply[A](elems: A*): CC[A] = from(elems) + + /** Produces a $coll containing repeated applications of a function to a start value. + * + * @param start the start value of the $coll + * @param len the number of elements contained in the $coll + * @param f the function that's repeatedly applied + * @return a $coll with `len` values in the sequence `start, f(start), f(f(start)), ...` + */ + def iterate[A](start: A, len: Int)(f: A => A): CC[A] = from(new View.Iterate(start, len)(f)) + + /** Produces a $coll that uses a function `f` to produce elements of type `A` + * and update an internal state of type `S`. + * + * @param init State initial value + * @param f Computes the next element (or returns `None` to signal + * the end of the collection) + * @tparam A Type of the elements + * @tparam S Type of the internal state + * @return a $coll that produces elements using `f` until `f` returns `None` + */ + def unfold[A, S](init: S)(f: S => Option[(A, S)]): CC[A] = from(new View.Unfold(init)(f)) + + /** Produces a $coll containing a sequence of increasing of integers. + * + * @param start the first element of the $coll + * @param end the end value of the $coll (the first value NOT contained) + * @return a $coll with values `start, start + 1, ..., end - 1` + */ + def range[A : Integral](start: A, end: A): CC[A] = from(NumericRange(start, end, implicitly[Integral[A]].one)) + + /** Produces a $coll containing equally spaced values in some integer interval. + * @param start the start value of the $coll + * @param end the end value of the $coll (the first value NOT contained) + * @param step the difference between successive elements of the $coll (must be positive or negative) + * @return a $coll with values `start, start + step, ...` up to, but excluding `end` + */ + def range[A : Integral](start: A, end: A, step: A): CC[A] = from(NumericRange(start, end, step)) + + /** + * @return A builder for $Coll objects. + * @tparam A the type of the ${coll}’s elements + */ + def newBuilder[A]: Builder[A, CC[A]] + + /** Produces a $coll containing the results of some element computation a number of times. + * @param n the number of elements contained in the $coll. + * @param elem the element computation + * @return A $coll that contains the results of `n` evaluations of `elem`. + */ + def fill[A](n: Int)(elem: => A): CC[A] = from(new View.Fill(n)(elem)) + + /** Produces a two-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2` evaluations of `elem`. + */ + def fill[A](n1: Int, n2: Int)(elem: => A): CC[CC[A] @uncheckedVariance] = fill(n1)(fill(n2)(elem)) + + /** Produces a three-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2 x n3` evaluations of `elem`. + */ + def fill[A](n1: Int, n2: Int, n3: Int)(elem: => A): CC[CC[CC[A]] @uncheckedVariance] = fill(n1)(fill(n2, n3)(elem)) + + /** Produces a four-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2 x n3 x n4` evaluations of `elem`. + */ + def fill[A](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + fill(n1)(fill(n2, n3, n4)(elem)) + + /** Produces a five-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2 x n3 x n4 x n5` evaluations of `elem`. + */ + def fill[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + fill(n1)(fill(n2, n3, n4, n5)(elem)) + + /** Produces a $coll containing values of a given function over a range of integer values starting from 0. + * @param n The number of elements in the $coll + * @param f The function computing element values + * @return A $coll consisting of elements `f(0), ..., f(n -1)` + */ + def tabulate[A](n: Int)(f: Int => A): CC[A] = from(new View.Tabulate(n)(f)) + + /** Produces a two-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2)` + * for `0 <= i1 < n1` and `0 <= i2 < n2`. + */ + def tabulate[A](n1: Int, n2: Int)(f: (Int, Int) => A): CC[CC[A] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2)(f(i1, _))) + + /** Produces a three-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2, i3)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, and `0 <= i3 < n3`. + */ + def tabulate[A](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A): CC[CC[CC[A]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3)(f(i1, _, _))) + + /** Produces a four-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2, i3, i4)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, and `0 <= i4 < n4`. + */ + def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3, n4)(f(i1, _, _, _))) + + /** Produces a five-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2, i3, i4, i5)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, `0 <= i4 < n4`, and `0 <= i5 < n5`. + */ + def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _))) + + /** Concatenates all argument collections into a single $coll. + * + * @param xss the collections that are to be concatenated. + * @return the concatenation of all the collections. + */ + def concat[A](xss: Iterable[A]*): CC[A] = { + from(xss.foldLeft(View.empty[A])(_ ++ _)) + } + + implicit def iterableFactory[A]: Factory[A, CC[A]] = IterableFactory.toFactory(this) +} + +object IterableFactory { + + /** + * Fixes the element type of `factory` to `A` + * @param factory The factory to fix the element type + * @tparam A Type of elements + * @tparam CC Collection type constructor of the factory (e.g. `Seq`, `List`) + * @return A [[Factory]] that uses the given `factory` to build a collection of elements + * of type `A` + */ + implicit def toFactory[A, CC[_]](factory: IterableFactory[CC]): Factory[A, CC[A]] = new ToFactory[A, CC](factory) + + @SerialVersionUID(3L) + private[this] class ToFactory[A, CC[_]](factory: IterableFactory[CC]) extends Factory[A, CC[A]] with Serializable { + def fromSpecific(it: IterableOnce[A]): CC[A] = factory.from[A](it) + def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A] + } + + implicit def toBuildFrom[A, CC[_]](factory: IterableFactory[CC]): BuildFrom[Any, A, CC[A]] = + new BuildFrom[Any, A, CC[A]] { + def fromSpecific(from: Any)(it: IterableOnce[A]) = factory.from(it) + def newBuilder(from: Any) = factory.newBuilder + } + + @SerialVersionUID(3L) + class Delegate[CC[_]](delegate: IterableFactory[CC]) extends IterableFactory[CC] { + override def apply[A](elems: A*): CC[A] = delegate.apply(elems: _*) + def empty[A]: CC[A] = delegate.empty + def from[E](it: IterableOnce[E]): CC[E] = delegate.from(it) + def newBuilder[A]: Builder[A, CC[A]] = delegate.newBuilder[A] + } +} + +/** + * @tparam CC Collection type constructor (e.g. `List`) + */ +trait SeqFactory[+CC[A] <: SeqOps[A, Seq, Seq[A]]] extends IterableFactory[CC] { + import SeqFactory.UnapplySeqWrapper + final def unapplySeq[A](x: CC[A] @uncheckedVariance): UnapplySeqWrapper[A] = new UnapplySeqWrapper(x) // TODO is uncheckedVariance sound here? +} + +object SeqFactory { + @SerialVersionUID(3L) + class Delegate[CC[A] <: SeqOps[A, Seq, Seq[A]]](delegate: SeqFactory[CC]) extends SeqFactory[CC] { + override def apply[A](elems: A*): CC[A] = delegate.apply(elems: _*) + def empty[A]: CC[A] = delegate.empty + def from[E](it: IterableOnce[E]): CC[E] = delegate.from(it) + def newBuilder[A]: Builder[A, CC[A]] = delegate.newBuilder[A] + } + + final class UnapplySeqWrapper[A](private val c: SeqOps[A, Seq, Seq[A]]) extends AnyVal { + def isEmpty: false = false + def get: UnapplySeqWrapper[A] = this + def lengthCompare(len: Int): Int = c.lengthCompare(len) + def apply(i: Int): A = c(i) + def drop(n: Int): scala.Seq[A] = c match { + case seq: scala.Seq[A] => seq.drop(n) + case _ => c.view.drop(n).toSeq + } + def toSeq: scala.Seq[A] = c.toSeq + } +} + +trait StrictOptimizedSeqFactory[+CC[A] <: SeqOps[A, Seq, Seq[A]]] extends SeqFactory[CC] { + + override def fill[A](n: Int)(elem: => A): CC[A] = { + val b = newBuilder[A] + b.sizeHint(n) + var i = 0 + while (i < n) { + b += elem + i += 1 + } + b.result() + } + + override def tabulate[A](n: Int)(f: Int => A): CC[A] = { + val b = newBuilder[A] + b.sizeHint(n) + var i = 0 + while (i < n) { + b += f(i) + i += 1 + } + b.result() + } + + override def concat[A](xss: Iterable[A]*): CC[A] = { + val b = newBuilder[A] + val knownSizes = xss.view.map(_.knownSize) + if (knownSizes forall (_ >= 0)) { + b.sizeHint(knownSizes.sum) + } + for (xs <- xss) b ++= xs + b.result() + } + +} + +/** + * @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.) + * @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.) + * @define factoryInfo + * This object provides a set of operations to create $Coll values. + * + * @define coll collection + * @define Coll `Iterable` + */ +trait SpecificIterableFactory[-A, +C] extends Factory[A, C] { + def empty: C + def apply(xs: A*): C = fromSpecific(xs) + def fill(n: Int)(elem: => A): C = fromSpecific(new View.Fill(n)(elem)) + def newBuilder: Builder[A, C] + + implicit def specificIterableFactory: Factory[A, C] = this +} + +/** + * @define factoryInfo + * This object provides a set of operations to create $Coll values. + * + * @define coll collection + * @define Coll `Iterable` + */ +trait MapFactory[+CC[_, _]] extends Serializable { + + /** + * An empty Map + */ + def empty[K, V]: CC[K, V] + + /** + * A collection of type Map generated from given iterable object. + */ + def from[K, V](it: IterableOnce[(K, V)]): CC[K, V] + + /** + * A collection of type Map that contains given key/value bindings. + */ + def apply[K, V](elems: (K, V)*): CC[K, V] = from(elems) + + /** + * The default builder for Map objects. + */ + def newBuilder[K, V]: Builder[(K, V), CC[K, V]] + + /** + * The default Factory instance for maps. + */ + implicit def mapFactory[K, V]: Factory[(K, V), CC[K, V]] = MapFactory.toFactory(this) +} + +object MapFactory { + + /** + * Fixes the key and value types of `factory` to `K` and `V`, respectively + * @param factory The factory to fix the key and value types + * @tparam K Type of keys + * @tparam V Type of values + * @tparam CC Collection type constructor of the factory (e.g. `Map`, `HashMap`, etc.) + * @return A [[Factory]] that uses the given `factory` to build a map with keys of type `K` + * and values of type `V` + */ + implicit def toFactory[K, V, CC[_, _]](factory: MapFactory[CC]): Factory[(K, V), CC[K, V]] = new ToFactory[K, V, CC](factory) + + @SerialVersionUID(3L) + private[this] class ToFactory[K, V, CC[_, _]](factory: MapFactory[CC]) extends Factory[(K, V), CC[K, V]] with Serializable { + def fromSpecific(it: IterableOnce[(K, V)]): CC[K, V] = factory.from[K, V](it) + def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V] + } + + implicit def toBuildFrom[K, V, CC[_, _]](factory: MapFactory[CC]): BuildFrom[Any, (K, V), CC[K, V]] = + new BuildFrom[Any, (K, V), CC[K, V]] { + def fromSpecific(from: Any)(it: IterableOnce[(K, V)]) = factory.from(it) + def newBuilder(from: Any) = factory.newBuilder[K, V] + } + + @SerialVersionUID(3L) + class Delegate[C[_, _]](delegate: MapFactory[C]) extends MapFactory[C] { + override def apply[K, V](elems: (K, V)*): C[K, V] = delegate.apply(elems: _*) + def from[K, V](it: IterableOnce[(K, V)]): C[K, V] = delegate.from(it) + def empty[K, V]: C[K, V] = delegate.empty + def newBuilder[K, V]: Builder[(K, V), C[K, V]] = delegate.newBuilder + } +} + +/** Base trait for companion objects of collections that require an implicit evidence. + * @tparam CC Collection type constructor (e.g. `ArraySeq`) + * @tparam Ev Unary type constructor for the implicit evidence required for an element type + * (typically `Ordering` or `ClassTag`) + * + * @define factoryInfo + * This object provides a set of operations to create $Coll values. + * + * @define coll collection + * @define Coll `Iterable` + */ +trait EvidenceIterableFactory[+CC[_], Ev[_]] extends Serializable { + + def from[E : Ev](it: IterableOnce[E]): CC[E] + + def empty[A : Ev]: CC[A] + + def apply[A : Ev](xs: A*): CC[A] = from(xs) + + /** Produces a $coll containing the results of some element computation a number of times. + * @param n the number of elements contained in the $coll. + * @param elem the element computation + * @return A $coll that contains the results of `n` evaluations of `elem`. + */ + def fill[A : Ev](n: Int)(elem: => A): CC[A] = from(new View.Fill(n)(elem)) + + /** Produces a $coll containing values of a given function over a range of integer values starting from 0. + * @param n The number of elements in the $coll + * @param f The function computing element values + * @return A $coll consisting of elements `f(0), ..., f(n -1)` + */ + def tabulate[A : Ev](n: Int)(f: Int => A): CC[A] = from(new View.Tabulate(n)(f)) + + /** Produces a $coll containing repeated applications of a function to a start value. + * + * @param start the start value of the $coll + * @param len the number of elements contained in the $coll + * @param f the function that's repeatedly applied + * @return a $coll with `len` values in the sequence `start, f(start), f(f(start)), ...` + */ + def iterate[A : Ev](start: A, len: Int)(f: A => A): CC[A] = from(new View.Iterate(start, len)(f)) + + /** Produces a $coll that uses a function `f` to produce elements of type `A` + * and update an internal state of type `S`. + * + * @param init State initial value + * @param f Computes the next element (or returns `None` to signal + * the end of the collection) + * @tparam A Type of the elements + * @tparam S Type of the internal state + * @return a $coll that produces elements using `f` until `f` returns `None` + */ + def unfold[A : Ev, S](init: S)(f: S => Option[(A, S)]): CC[A] = from(new View.Unfold(init)(f)) + + def newBuilder[A : Ev]: Builder[A, CC[A]] + + implicit def evidenceIterableFactory[A : Ev]: Factory[A, CC[A]] = EvidenceIterableFactory.toFactory(this) +} + +object EvidenceIterableFactory { + + /** + * Fixes the element type of `factory` to `A` + * @param factory The factory to fix the element type + * @tparam A Type of elements + * @tparam CC Collection type constructor of the factory (e.g. `TreeSet`) + * @tparam Ev Type constructor of the evidence (usually `Ordering` or `ClassTag`) + * @return A [[Factory]] that uses the given `factory` to build a collection of elements + * of type `A` + */ + implicit def toFactory[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]): Factory[A, CC[A]] = new ToFactory[Ev, A, CC](factory) + + @SerialVersionUID(3L) + private[this] class ToFactory[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]) extends Factory[A, CC[A]] with Serializable { + def fromSpecific(it: IterableOnce[A]): CC[A] = factory.from[A](it) + def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A] + } + + implicit def toBuildFrom[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]): BuildFrom[Any, A, CC[A]] = new EvidenceIterableFactoryToBuildFrom(factory) + private class EvidenceIterableFactoryToBuildFrom[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]) extends BuildFrom[Any, A, CC[A]] { + def fromSpecific(from: Any)(it: IterableOnce[A]): CC[A] = factory.from[A](it) + def newBuilder(from: Any): Builder[A, CC[A]] = factory.newBuilder[A] + } + + @SerialVersionUID(3L) + class Delegate[CC[_], Ev[_]](delegate: EvidenceIterableFactory[CC, Ev]) extends EvidenceIterableFactory[CC, Ev] { + override def apply[A: Ev](xs: A*): CC[A] = delegate.apply(xs: _*) + def empty[A : Ev]: CC[A] = delegate.empty + def from[E : Ev](it: IterableOnce[E]): CC[E] = delegate.from(it) + def newBuilder[A : Ev]: Builder[A, CC[A]] = delegate.newBuilder[A] + } +} + +/** Base trait for companion objects of collections that require an implicit `Ordering`. + * @tparam CC Collection type constructor (e.g. `SortedSet`) + */ +trait SortedIterableFactory[+CC[_]] extends EvidenceIterableFactory[CC, Ordering] + +object SortedIterableFactory { + @SerialVersionUID(3L) + class Delegate[CC[_]](delegate: EvidenceIterableFactory[CC, Ordering]) + extends EvidenceIterableFactory.Delegate[CC, Ordering](delegate) with SortedIterableFactory[CC] +} + +/** Base trait for companion objects of collections that require an implicit `ClassTag`. + * @tparam CC Collection type constructor (e.g. `ArraySeq`) + */ +trait ClassTagIterableFactory[+CC[_]] extends EvidenceIterableFactory[CC, ClassTag] { + + @`inline` private[this] implicit def ccClassTag[X]: ClassTag[CC[X]] = + ClassTag.AnyRef.asInstanceOf[ClassTag[CC[X]]] // Good enough for boxed vs primitive arrays + + /** Produces a $coll containing a sequence of increasing of integers. + * + * @param start the first element of the $coll + * @param end the end value of the $coll (the first value NOT contained) + * @return a $coll with values `start, start + 1, ..., end - 1` + */ + def range[A : Integral : ClassTag](start: A, end: A): CC[A] = from(NumericRange(start, end, implicitly[Integral[A]].one)) + + /** Produces a $coll containing equally spaced values in some integer interval. + * @param start the start value of the $coll + * @param end the end value of the $coll (the first value NOT contained) + * @param step the difference between successive elements of the $coll (must be positive or negative) + * @return a $coll with values `start, start + step, ...` up to, but excluding `end` + */ + def range[A : Integral : ClassTag](start: A, end: A, step: A): CC[A] = from(NumericRange(start, end, step)) + + /** Produces a two-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2` evaluations of `elem`. + */ + def fill[A : ClassTag](n1: Int, n2: Int)(elem: => A): CC[CC[A] @uncheckedVariance] = fill(n1)(fill(n2)(elem)) + + /** Produces a three-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2 x n3` evaluations of `elem`. + */ + def fill[A : ClassTag](n1: Int, n2: Int, n3: Int)(elem: => A): CC[CC[CC[A]] @uncheckedVariance] = fill(n1)(fill(n2, n3)(elem)) + + /** Produces a four-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2 x n3 x n4` evaluations of `elem`. + */ + def fill[A : ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + fill(n1)(fill(n2, n3, n4)(elem)) + + /** Produces a five-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param elem the element computation + * @return A $coll that contains the results of `n1 x n2 x n3 x n4 x n5` evaluations of `elem`. + */ + def fill[A : ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + fill(n1)(fill(n2, n3, n4, n5)(elem)) + + /** Produces a two-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2)` + * for `0 <= i1 < n1` and `0 <= i2 < n2`. + */ + def tabulate[A : ClassTag](n1: Int, n2: Int)(f: (Int, Int) => A): CC[CC[A] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2)(f(i1, _))) + + /** Produces a three-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2, i3)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, and `0 <= i3 < n3`. + */ + def tabulate[A : ClassTag](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A): CC[CC[CC[A]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3)(f(i1, _, _))) + + /** Produces a four-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2, i3, i4)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, and `0 <= i4 < n4`. + */ + def tabulate[A : ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3, n4)(f(i1, _, _, _))) + + /** Produces a five-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param f The function computing element values + * @return A $coll consisting of elements `f(i1, i2, i3, i4, i5)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, `0 <= i4 < n4`, and `0 <= i5 < n5`. + */ + def tabulate[A : ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _))) +} + +object ClassTagIterableFactory { + @SerialVersionUID(3L) + class Delegate[CC[_]](delegate: EvidenceIterableFactory[CC, ClassTag]) + extends EvidenceIterableFactory.Delegate[CC, ClassTag](delegate) with ClassTagIterableFactory[CC] + + /** An IterableFactory that uses ClassTag.Any as the evidence for every element type. This may or may not be + * sound depending on the use of the `ClassTag` by the collection implementation. */ + @SerialVersionUID(3L) + class AnyIterableDelegate[CC[_]](delegate: ClassTagIterableFactory[CC]) extends IterableFactory[CC] { + def empty[A]: CC[A] = delegate.empty(using ClassTag.Any).asInstanceOf[CC[A]] + def from[A](it: IterableOnce[A]): CC[A] = delegate.from[Any](it)(using ClassTag.Any).asInstanceOf[CC[A]] + def newBuilder[A]: Builder[A, CC[A]] = delegate.newBuilder(using ClassTag.Any).asInstanceOf[Builder[A, CC[A]]] + override def apply[A](elems: A*): CC[A] = delegate.apply[Any](elems: _*)(using ClassTag.Any).asInstanceOf[CC[A]] + override def iterate[A](start: A, len: Int)(f: A => A): CC[A] = delegate.iterate[A](start, len)(f)(using ClassTag.Any.asInstanceOf[ClassTag[A]]) + override def unfold[A, S](init: S)(f: S => Option[(A, S)]): CC[A] = delegate.unfold[A, S](init)(f)(using ClassTag.Any.asInstanceOf[ClassTag[A]]) + override def range[A](start: A, end: A)(implicit i: Integral[A]): CC[A] = delegate.range[A](start, end)(using i, ClassTag.Any.asInstanceOf[ClassTag[A]]) + override def range[A](start: A, end: A, step: A)(implicit i: Integral[A]): CC[A] = delegate.range[A](start, end, step)(using i, ClassTag.Any.asInstanceOf[ClassTag[A]]) + override def fill[A](n: Int)(elem: => A): CC[A] = delegate.fill[Any](n)(elem)(using ClassTag.Any).asInstanceOf[CC[A]] + override def tabulate[A](n: Int)(f: Int => A): CC[A] = delegate.tabulate[Any](n)(f)(using ClassTag.Any).asInstanceOf[CC[A]] + } +} + +/** + * @tparam CC Collection type constructor (e.g. `ArraySeq`) + */ +trait ClassTagSeqFactory[+CC[A] <: SeqOps[A, Seq, Seq[A]]] extends ClassTagIterableFactory[CC] { + import SeqFactory.UnapplySeqWrapper + final def unapplySeq[A](x: CC[A] @uncheckedVariance): UnapplySeqWrapper[A] = new UnapplySeqWrapper(x) // TODO is uncheckedVariance sound here? +} + +object ClassTagSeqFactory { + @SerialVersionUID(3L) + class Delegate[CC[A] <: SeqOps[A, Seq, Seq[A]]](delegate: ClassTagSeqFactory[CC]) + extends ClassTagIterableFactory.Delegate[CC](delegate) with ClassTagSeqFactory[CC] + + /** A SeqFactory that uses ClassTag.Any as the evidence for every element type. This may or may not be + * sound depending on the use of the `ClassTag` by the collection implementation. */ + @SerialVersionUID(3L) + class AnySeqDelegate[CC[A] <: SeqOps[A, Seq, Seq[A]]](delegate: ClassTagSeqFactory[CC]) + extends ClassTagIterableFactory.AnyIterableDelegate[CC](delegate) with SeqFactory[CC] +} + +trait StrictOptimizedClassTagSeqFactory[+CC[A] <: SeqOps[A, Seq, Seq[A]]] extends ClassTagSeqFactory[CC] { + + override def fill[A : ClassTag](n: Int)(elem: => A): CC[A] = { + val b = newBuilder[A] + b.sizeHint(n) + var i = 0 + while (i < n) { + b += elem + i += 1 + } + b.result() + } + + override def tabulate[A : ClassTag](n: Int)(f: Int => A): CC[A] = { + val b = newBuilder[A] + b.sizeHint(n) + var i = 0 + while (i < n) { + b += f(i) + i += 1 + } + b.result() + } + +} + +/** + * @define factoryInfo + * This object provides a set of operations to create $Coll values. + * + * @define coll collection + * @define Coll `Iterable` + */ +trait SortedMapFactory[+CC[_, _]] extends Serializable { + + def empty[K : Ordering, V]: CC[K, V] + + def from[K : Ordering, V](it: IterableOnce[(K, V)]): CC[K, V] + + def apply[K : Ordering, V](elems: (K, V)*): CC[K, V] = from(elems) + + def newBuilder[K : Ordering, V]: Builder[(K, V), CC[K, V]] + + implicit def sortedMapFactory[K : Ordering, V]: Factory[(K, V), CC[K, V]] = SortedMapFactory.toFactory(this) + +} + +object SortedMapFactory { + + /** + * Implicit conversion that fixes the key and value types of `factory` to `K` and `V`, + * respectively. + * + * @param factory The factory to fix the key and value types + * @tparam K Type of keys + * @tparam V Type of values + * @tparam CC Collection type constructor of the factory (e.g. `TreeMap`) + * @return A [[Factory]] that uses the given `factory` to build a map with keys of + * type `K` and values of type `V` + */ + implicit def toFactory[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]): Factory[(K, V), CC[K, V]] = new ToFactory[K, V, CC](factory) + + @SerialVersionUID(3L) + private[this] class ToFactory[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]) extends Factory[(K, V), CC[K, V]] with Serializable { + def fromSpecific(it: IterableOnce[(K, V)]): CC[K, V] = factory.from[K, V](it) + def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V] + } + + implicit def toBuildFrom[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]): BuildFrom[Any, (K, V), CC[K, V]] = new SortedMapFactoryToBuildFrom(factory) + private class SortedMapFactoryToBuildFrom[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]) extends BuildFrom[Any, (K, V), CC[K, V]] { + def fromSpecific(from: Any)(it: IterableOnce[(K, V)]) = factory.from(it) + def newBuilder(from: Any) = factory.newBuilder[K, V] + } + + @SerialVersionUID(3L) + class Delegate[CC[_, _]](delegate: SortedMapFactory[CC]) extends SortedMapFactory[CC] { + override def apply[K: Ordering, V](elems: (K, V)*): CC[K, V] = delegate.apply(elems: _*) + def from[K : Ordering, V](it: IterableOnce[(K, V)]): CC[K, V] = delegate.from(it) + def empty[K : Ordering, V]: CC[K, V] = delegate.empty + def newBuilder[K : Ordering, V]: Builder[(K, V), CC[K, V]] = delegate.newBuilder + } +} diff --git a/library/src/scala/collection/Hashing.scala b/library/src/scala/collection/Hashing.scala new file mode 100644 index 000000000000..7d3702d26e43 --- /dev/null +++ b/library/src/scala/collection/Hashing.scala @@ -0,0 +1,62 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + + +protected[collection] object Hashing { + + def elemHashCode(key: Any): Int = key.## + + def improve(hcode: Int): Int = { + var h: Int = hcode + ~(hcode << 9) + h = h ^ (h >>> 14) + h = h + (h << 4) + h ^ (h >>> 10) + } + + def computeHash(key: Any): Int = + improve(elemHashCode(key)) + + /** + * Utility method to keep a subset of all bits in a given bitmap + * + * Example + * bitmap (binary): 00000001000000010000000100000001 + * keep (binary): 1010 + * result (binary): 00000001000000000000000100000000 + * + * @param bitmap the bitmap + * @param keep a bitmask containing which bits to keep + * @return the original bitmap with all bits where keep is not 1 set to 0 + */ + def keepBits(bitmap: Int, keep: Int): Int = { + var result = 0 + var current = bitmap + var kept = keep + while (kept != 0) { + // lowest remaining bit in current + val lsb = current ^ (current & (current - 1)) + if ((kept & 1) != 0) { + // mark bit in result bitmap + result |= lsb + } + // clear lowest remaining one bit in abm + current &= ~lsb + // look at the next kept bit + kept >>>= 1 + } + result + } + +} diff --git a/library/src/scala/collection/IndexedSeq.scala b/library/src/scala/collection/IndexedSeq.scala new file mode 100644 index 000000000000..b4e8012ab63e --- /dev/null +++ b/library/src/scala/collection/IndexedSeq.scala @@ -0,0 +1,147 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.{nowarn, tailrec} +import scala.collection.Searching.{Found, InsertionPoint, SearchResult} +import scala.collection.Stepper.EfficientSplit +import scala.math.Ordering + +/** Base trait for indexed sequences that have efficient `apply` and `length` */ +trait IndexedSeq[+A] extends Seq[A] + with IndexedSeqOps[A, IndexedSeq, IndexedSeq[A]] + with IterableFactoryDefaults[A, IndexedSeq] { + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "IndexedSeq" + + override def iterableFactory: SeqFactory[IndexedSeq] = IndexedSeq +} + +@SerialVersionUID(3L) +object IndexedSeq extends SeqFactory.Delegate[IndexedSeq](immutable.IndexedSeq) + +/** Base trait for indexed Seq operations */ +trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C] { self => + + def iterator: Iterator[A] = view.iterator + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntIndexedSeqStepper (this.asInstanceOf[IndexedSeqOps[Int, AnyConstr, _]], 0, length) + case StepperShape.LongShape => new LongIndexedSeqStepper (this.asInstanceOf[IndexedSeqOps[Long, AnyConstr, _]], 0, length) + case StepperShape.DoubleShape => new DoubleIndexedSeqStepper(this.asInstanceOf[IndexedSeqOps[Double, AnyConstr, _]], 0, length) + case _ => shape.parUnbox(new AnyIndexedSeqStepper[A](this, 0, length)) + } + s.asInstanceOf[S with EfficientSplit] + } + + override def reverseIterator: Iterator[A] = view.reverseIterator + + /* TODO 2.14+ uncomment and delete related code in IterableOnce + @tailrec private def foldl[B](start: Int, end: Int, z: B, op: (B, A) => B): B = + if (start == end) z + else foldl(start + 1, end, op(z, apply(start)), op) + */ + + @tailrec private def foldr[B](start: Int, end: Int, z: B, op: (A, B) => B): B = + if (start == end) z + else foldr(start, end - 1, op(apply(end - 1), z), op) + + //override def foldLeft[B](z: B)(op: (B, A) => B): B = foldl(0, length, z, op) + + override def foldRight[B](z: B)(op: (A, B) => B): B = foldr(0, length, z, op) + + //override def reduceLeft[B >: A](op: (B, A) => B): B = if (length > 0) foldl(1, length, apply(0), op) else super.reduceLeft(op) + + //override def reduceRight[B >: A](op: (A, B) => B): B = if (length > 0) foldr(0, length - 1, apply(length - 1), op) else super.reduceRight(op) + + override def view: IndexedSeqView[A] = new IndexedSeqView.Id[A](this) + + @deprecated("Use .view.slice(from, until) instead of .view(from, until)", "2.13.0") + override def view(from: Int, until: Int): IndexedSeqView[A] = view.slice(from, until) + + override protected def reversed: Iterable[A] = new IndexedSeqView.Reverse(this) + + // Override transformation operations to use more efficient views than the default ones + override def prepended[B >: A](elem: B): CC[B] = iterableFactory.from(new IndexedSeqView.Prepended(elem, this)) + + override def take(n: Int): C = fromSpecific(new IndexedSeqView.Take(this, n)) + + override def takeRight(n: Int): C = fromSpecific(new IndexedSeqView.TakeRight(this, n)) + + override def drop(n: Int): C = fromSpecific(new IndexedSeqView.Drop(this, n)) + + override def dropRight(n: Int): C = fromSpecific(new IndexedSeqView.DropRight(this, n)) + + override def map[B](f: A => B): CC[B] = iterableFactory.from(new IndexedSeqView.Map(this, f)) + + override def reverse: C = fromSpecific(new IndexedSeqView.Reverse(this)) + + override def slice(from: Int, until: Int): C = fromSpecific(new IndexedSeqView.Slice(this, from, until)) + + override def head: A = + if (!isEmpty) apply(0) + else throw new NoSuchElementException(s"head of empty ${ + self match { + case self: IndexedSeq[_] => self.collectionClassName + case _ => toString + } + }") + + override def headOption: Option[A] = if (isEmpty) None else Some(head) + + override def last: A = + if (!isEmpty) apply(length - 1) + else throw new NoSuchElementException(s"last of empty ${ + self match { + case self: IndexedSeq[_] => self.collectionClassName + case _ => toString + } + }") + + // We already inherit an efficient `lastOption = if (isEmpty) None else Some(last)` + + override final def lengthCompare(len: Int): Int = Integer.compare(length, len) + + override def knownSize: Int = length + + override final def lengthCompare(that: Iterable[_]): Int = { + val res = that.sizeCompare(length) + // can't just invert the result, because `-Int.MinValue == Int.MinValue` + if (res == Int.MinValue) 1 else -res + } + + override def search[B >: A](elem: B)(implicit ord: Ordering[B]): SearchResult = + binarySearch(elem, 0, length)(ord) + + override def search[B >: A](elem: B, from: Int, to: Int)(implicit ord: Ordering[B]): SearchResult = + binarySearch(elem, from, to)(ord) + + @tailrec + private[this] def binarySearch[B >: A](elem: B, from: Int, to: Int) + (implicit ord: Ordering[B]): SearchResult = { + if (from < 0) binarySearch(elem, 0, to) + else if (to > length) binarySearch(elem, from, length) + else if (to <= from) InsertionPoint(from) + else { + val idx = from + (to - from - 1) / 2 + math.signum(ord.compare(elem, apply(idx))) match { + case -1 => binarySearch(elem, from, idx)(ord) + case 1 => binarySearch(elem, idx + 1, to)(ord) + case _ => Found(idx) + } + } + } +} diff --git a/library/src/scala/collection/IndexedSeqView.scala b/library/src/scala/collection/IndexedSeqView.scala new file mode 100644 index 000000000000..4fd99c1080af --- /dev/null +++ b/library/src/scala/collection/IndexedSeqView.scala @@ -0,0 +1,180 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.nowarn + + +/** View defined in terms of indexing a range */ +trait IndexedSeqView[+A] extends IndexedSeqOps[A, View, View[A]] with SeqView[A] { self => + + override def view: IndexedSeqView[A] = this + + @deprecated("Use .view.slice(from, until) instead of .view(from, until)", "2.13.0") + override def view(from: Int, until: Int): IndexedSeqView[A] = view.slice(from, until) + + override def iterator: Iterator[A] = new IndexedSeqView.IndexedSeqViewIterator(this) + override def reverseIterator: Iterator[A] = new IndexedSeqView.IndexedSeqViewReverseIterator(this) + + override def appended[B >: A](elem: B): IndexedSeqView[B] = new IndexedSeqView.Appended(this, elem) + override def prepended[B >: A](elem: B): IndexedSeqView[B] = new IndexedSeqView.Prepended(elem, this) + override def take(n: Int): IndexedSeqView[A] = new IndexedSeqView.Take(this, n) + override def takeRight(n: Int): IndexedSeqView[A] = new IndexedSeqView.TakeRight(this, n) + override def drop(n: Int): IndexedSeqView[A] = new IndexedSeqView.Drop(this, n) + override def dropRight(n: Int): IndexedSeqView[A] = new IndexedSeqView.DropRight(this, n) + override def map[B](f: A => B): IndexedSeqView[B] = new IndexedSeqView.Map(this, f) + override def reverse: IndexedSeqView[A] = new IndexedSeqView.Reverse(this) + override def slice(from: Int, until: Int): IndexedSeqView[A] = new IndexedSeqView.Slice(this, from, until) + override def tapEach[U](f: A => U): IndexedSeqView[A] = new IndexedSeqView.Map(this, { (a: A) => f(a); a}) + + def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new IndexedSeqView.Concat(this, suffix) + def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new IndexedSeqView.Concat(this, suffix) + def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new IndexedSeqView.Concat(prefix, this) + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "IndexedSeqView" +} + +object IndexedSeqView { + + @SerialVersionUID(3L) + private[collection] class IndexedSeqViewIterator[A](self: IndexedSeqView[A]) extends AbstractIterator[A] with Serializable { + private[this] var current = 0 + private[this] var remainder = self.length + override def knownSize: Int = remainder + @inline private[this] def _hasNext: Boolean = remainder > 0 + def hasNext: Boolean = _hasNext + def next(): A = + if (_hasNext) { + val r = self(current) + current += 1 + remainder -= 1 + r + } else Iterator.empty.next() + + override def drop(n: Int): Iterator[A] = { + if (n > 0) { + current += n + remainder = Math.max(0, remainder - n) + } + this + } + + override protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + + def formatRange(value : Int) : Int = if (value < 0) 0 else if (value > remainder) remainder else value + + val formatFrom = formatRange(from) + val formatUntil = formatRange(until) + remainder = Math.max(0, formatUntil - formatFrom) + current = current + formatFrom + this + } + } + @SerialVersionUID(3L) + private[collection] class IndexedSeqViewReverseIterator[A](self: IndexedSeqView[A]) extends AbstractIterator[A] with Serializable { + private[this] var remainder = self.length + private[this] var pos = remainder - 1 + @inline private[this] def _hasNext: Boolean = remainder > 0 + def hasNext: Boolean = _hasNext + def next(): A = + if (_hasNext) { + val r = self(pos) + pos -= 1 + remainder -= 1 + r + } else Iterator.empty.next() + + // from < 0 means don't move pos, until < 0 means don't limit remainder + // + override protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + if (_hasNext) { + if (remainder <= from) remainder = 0 // exhausted by big skip + else if (from <= 0) { // no skip, pos is same + if (until >= 0 && until < remainder) remainder = until // ...limited by until + } + else { + pos -= from // skip ahead + if (until >= 0 && until < remainder) { // ...limited by until + if (until <= from) remainder = 0 // ...exhausted if limit is smaller than skip + else remainder = until - from // ...limited by until, less the skip + } + else remainder -= from // ...otherwise just less the skip + } + } + this + } + } + + /** An `IndexedSeqOps` whose collection type and collection type constructor are unknown */ + type SomeIndexedSeqOps[A] = IndexedSeqOps[A, AnyConstr, _] + + @SerialVersionUID(3L) + class Id[+A](underlying: SomeIndexedSeqOps[A]) + extends SeqView.Id(underlying) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class Appended[+A](underlying: SomeIndexedSeqOps[A], elem: A) + extends SeqView.Appended(underlying, elem) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A]) + extends SeqView.Prepended(elem, underlying) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class Concat[A](prefix: SomeIndexedSeqOps[A], suffix: SomeIndexedSeqOps[A]) + extends SeqView.Concat[A](prefix, suffix) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class Take[A](underlying: SomeIndexedSeqOps[A], n: Int) + extends SeqView.Take(underlying, n) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class TakeRight[A](underlying: SomeIndexedSeqOps[A], n: Int) + extends SeqView.TakeRight(underlying, n) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class Drop[A](underlying: SomeIndexedSeqOps[A], n: Int) + extends SeqView.Drop[A](underlying, n) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class DropRight[A](underlying: SomeIndexedSeqOps[A], n: Int) + extends SeqView.DropRight[A](underlying, n) with IndexedSeqView[A] + + @SerialVersionUID(3L) + class Map[A, B](underlying: SomeIndexedSeqOps[A], f: A => B) + extends SeqView.Map(underlying, f) with IndexedSeqView[B] + + @SerialVersionUID(3L) + class Reverse[A](underlying: SomeIndexedSeqOps[A]) extends SeqView.Reverse[A](underlying) with IndexedSeqView[A] { + override def reverse: IndexedSeqView[A] = underlying match { + case x: IndexedSeqView[A] => x + case _ => super.reverse + } + } + + @SerialVersionUID(3L) + class Slice[A](underlying: SomeIndexedSeqOps[A], from: Int, until: Int) extends AbstractIndexedSeqView[A] { + protected val lo = from max 0 + protected val hi = (until max 0) min underlying.length + protected val len = (hi - lo) max 0 + @throws[IndexOutOfBoundsException] + def apply(i: Int): A = underlying(lo + i) + def length: Int = len + } +} + +/** Explicit instantiation of the `IndexedSeqView` trait to reduce class file size in subclasses. */ +@SerialVersionUID(3L) +abstract class AbstractIndexedSeqView[+A] extends AbstractSeqView[A] with IndexedSeqView[A] diff --git a/library/src/scala/collection/Iterable.scala b/library/src/scala/collection/Iterable.scala new file mode 100644 index 000000000000..bec0a1211e28 --- /dev/null +++ b/library/src/scala/collection/Iterable.scala @@ -0,0 +1,1045 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.nowarn +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.mutable.Builder +import scala.collection.View.{LeftPartitionMapped, RightPartitionMapped} + +/** Base trait for generic collections. + * + * @tparam A the element type of the collection + * + * @define Coll `Iterable` + * @define coll iterable collection + */ +trait Iterable[+A] extends IterableOnce[A] + with IterableOps[A, Iterable, Iterable[A]] + with IterableFactoryDefaults[A, Iterable] { + + // The collection itself + @deprecated("toIterable is internal and will be made protected; its name is similar to `toList` or `toSeq`, but it doesn't copy non-immutable collections", "2.13.7") + final def toIterable: this.type = this + + final protected def coll: this.type = this + + def iterableFactory: IterableFactory[Iterable] = Iterable + + @deprecated("Iterable.seq always returns the iterable itself", "2.13.0") + def seq: this.type = this + + /** Defines the prefix of this object's `toString` representation. + * + * It is recommended to return the name of the concrete collection type, but + * not implementation subclasses. For example, for `ListMap` this method should + * return `"ListMap"`, not `"Map"` (the supertype) or `"Node"` (an implementation + * subclass). + * + * The default implementation returns "Iterable". It is overridden for the basic + * collection kinds "Seq", "IndexedSeq", "LinearSeq", "Buffer", "Set", "Map", + * "SortedSet", "SortedMap" and "View". + * + * @return a string representation which starts the result of `toString` + * applied to this $coll. By default the string prefix is the + * simple name of the collection class $coll. + */ + protected[this] def className: String = stringPrefix + + /** Forwarder to `className` for use in `scala.runtime.ScalaRunTime`. + * + * This allows the proper visibility for `className` to be + * published, but provides the exclusive access needed by + * `scala.runtime.ScalaRunTime.stringOf` (and a few tests in + * the test suite). + */ + private[scala] final def collectionClassName: String = className + + @deprecatedOverriding("Override className instead", "2.13.0") + protected[this] def stringPrefix: String = "Iterable" + + /** Converts this $coll to a string. + * + * @return a string representation of this collection. By default this + * string consists of the `className` of this $coll, followed + * by all elements separated by commas and enclosed in parentheses. + */ + override def toString = mkString(className + "(", ", ", ")") + + /** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is + * invoked on the returned `LazyZip2` decorator. + * + * Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of + * constructing and deconstructing intermediary tuples. + * + * {{{ + * val xs = List(1, 2, 3) + * val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d) + * // res == List(4, 8, 12) + * }}} + * + * @param that the iterable providing the second element of each eventual pair + * @tparam B the type of the second element in each eventual pair + * @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs + * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. + */ + def lazyZip[B](that: Iterable[B]): LazyZip2[A, B, this.type] = new LazyZip2(this, this, that) +} + +/** Base trait for Iterable operations + * + * =VarianceNote= + * + * We require that for all child classes of Iterable the variance of + * the child class and the variance of the `C` parameter passed to `IterableOps` + * are the same. We cannot express this since we lack variance polymorphism. That's + * why we have to resort at some places to write `C[A @uncheckedVariance]`. + * + * @tparam CC type constructor of the collection (e.g. `List`, `Set`). Operations returning a collection + * with a different type of element `B` (e.g. `map`) return a `CC[B]`. + * @tparam C type of the collection (e.g. `List[Int]`, `String`, `BitSet`). Operations returning a collection + * with the same type of element (e.g. `drop`, `filter`) return a `C`. + * + * @define Coll Iterable + * @define coll iterable collection + * @define orderDependent + * + * Note: might return different results for different runs, unless the underlying collection type is ordered. + * @define orderDependentFold + * + * Note: might return different results for different runs, unless the + * underlying collection type is ordered or the operator is associative + * and commutative. + * @define mayNotTerminateInf + * + * Note: may not terminate for infinite-sized collections. + * @define willNotTerminateInf + * + * Note: will not terminate for infinite-sized collections. + * @define undefinedorder + * The order in which operations are performed on elements is unspecified + * and may be nondeterministic. + */ +trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] with IterableOnceOps[A, CC, C] { + /** + * @return This collection as an `Iterable[A]`. No new collection will be built if `this` is already an `Iterable[A]`. + */ + // Should be `protected def asIterable`, or maybe removed altogether if it's not needed + @deprecated("toIterable is internal and will be made protected; its name is similar to `toList` or `toSeq`, but it doesn't copy non-immutable collections", "2.13.7") + def toIterable: Iterable[A] + + /** Converts this $coll to an unspecified Iterable. Will return + * the same collection if this instance is already Iterable. + * @return An Iterable containing all elements of this $coll. + */ + @deprecated("toTraversable is internal and will be made protected; its name is similar to `toList` or `toSeq`, but it doesn't copy non-immutable collections", "2.13.0") + final def toTraversable: Traversable[A] = toIterable + + override def isTraversableAgain: Boolean = true + + /** + * @return This collection as a `C`. + */ + protected def coll: C + + @deprecated("Use coll instead of repr in a collection implementation, use the collection value itself from the outside", "2.13.0") + final def repr: C = coll + + /** + * Defines how to turn a given `Iterable[A]` into a collection of type `C`. + * + * This process can be done in a strict way or a non-strict way (ie. without evaluating + * the elements of the resulting collections). In other words, this methods defines + * the evaluation model of the collection. + * + * @note When implementing a custom collection type and refining `C` to the new type, this + * method needs to be overridden (the compiler will issue an error otherwise). In the + * common case where `C =:= CC[A]`, this can be done by mixing in the + * [[scala.collection.IterableFactoryDefaults]] trait, which implements the method using + * [[iterableFactory]]. + * + * @note As witnessed by the `@uncheckedVariance` annotation, using this method + * might be unsound. However, as long as it is called with an + * `Iterable[A]` obtained from `this` collection (as it is the case in the + * implementations of operations where we use a `View[A]`), it is safe. + */ + protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): C + + /** The companion object of this ${coll}, providing various factory methods. + * + * @note When implementing a custom collection type and refining `CC` to the new type, this + * method needs to be overridden to return a factory for the new type (the compiler will + * issue an error otherwise). + */ + def iterableFactory: IterableFactory[CC] + + @deprecated("Use iterableFactory instead", "2.13.0") + @deprecatedOverriding("Use iterableFactory instead", "2.13.0") + @`inline` def companion: IterableFactory[CC] = iterableFactory + + /** + * @return a strict builder for the same collection type. + * + * Note that in the case of lazy collections (e.g. [[scala.collection.View]] or [[scala.collection.immutable.LazyList]]), + * it is possible to implement this method but the resulting `Builder` will break laziness. + * As a consequence, operations should preferably be implemented with `fromSpecific` + * instead of this method. + * + * @note When implementing a custom collection type and refining `C` to the new type, this + * method needs to be overridden (the compiler will issue an error otherwise). In the + * common case where `C =:= CC[A]`, this can be done by mixing in the + * [[scala.collection.IterableFactoryDefaults]] trait, which implements the method using + * [[iterableFactory]]. + * + * @note As witnessed by the `@uncheckedVariance` annotation, using this method might + * be unsound. However, as long as the returned builder is only fed + * with `A` values taken from `this` instance, it is safe. + */ + protected def newSpecificBuilder: Builder[A @uncheckedVariance, C] + + /** The empty iterable of the same type as this iterable. + * + * @return an empty iterable of type `C`. + */ + def empty: C = fromSpecific(Nil) + + /** Selects the first element of this $coll. + * $orderDependent + * @return the first element of this $coll. + * @throws NoSuchElementException if the $coll is empty. + */ + def head: A = iterator.next() + + /** Optionally selects the first element. + * $orderDependent + * @return the first element of this $coll if it is nonempty, + * `None` if it is empty. + */ + def headOption: Option[A] = { + val it = iterator + if (it.hasNext) Some(it.next()) else None + } + + /** Selects the last element. + * $orderDependent + * @return The last element of this $coll. + * @throws NoSuchElementException If the $coll is empty. + */ + def last: A = { + val it = iterator + var lst = it.next() + while (it.hasNext) lst = it.next() + lst + } + + /** Optionally selects the last element. + * $orderDependent + * @return the last element of this $coll$ if it is nonempty, + * `None` if it is empty. + */ + def lastOption: Option[A] = if (isEmpty) None else Some(last) + + /** A view over the elements of this collection. */ + def view: View[A] = View.fromIteratorProvider(() => iterator) + + /** Compares the size of this $coll to a test value. + * + * @param otherSize the test value that gets compared with the size. + * @return A value `x` where + * {{{ + * x < 0 if this.size < otherSize + * x == 0 if this.size == otherSize + * x > 0 if this.size > otherSize + * }}} + * + * The method as implemented here does not call `size` directly; its running time + * is `O(size min otherSize)` instead of `O(size)`. The method should be overridden + * if computing `size` is cheap and `knownSize` returns `-1`. + * + * @see [[sizeIs]] + */ + def sizeCompare(otherSize: Int): Int = { + if (otherSize < 0) 1 + else { + val known = knownSize + if (known >= 0) Integer.compare(known, otherSize) + else { + var i = 0 + val it = iterator + while (it.hasNext) { + if (i == otherSize) return 1 + it.next() + i += 1 + } + i - otherSize + } + } + } + + /** Returns a value class containing operations for comparing the size of this $coll to a test value. + * + * These operations are implemented in terms of [[sizeCompare(Int) `sizeCompare(Int)`]], and + * allow the following more readable usages: + * + * {{{ + * this.sizeIs < size // this.sizeCompare(size) < 0 + * this.sizeIs <= size // this.sizeCompare(size) <= 0 + * this.sizeIs == size // this.sizeCompare(size) == 0 + * this.sizeIs != size // this.sizeCompare(size) != 0 + * this.sizeIs >= size // this.sizeCompare(size) >= 0 + * this.sizeIs > size // this.sizeCompare(size) > 0 + * }}} + */ + @inline final def sizeIs: IterableOps.SizeCompareOps = new IterableOps.SizeCompareOps(this) + + /** Compares the size of this $coll to the size of another `Iterable`. + * + * @param that the `Iterable` whose size is compared with this $coll's size. + * @return A value `x` where + * {{{ + * x < 0 if this.size < that.size + * x == 0 if this.size == that.size + * x > 0 if this.size > that.size + * }}} + * + * The method as implemented here does not call `size` directly; its running time + * is `O(this.size min that.size)` instead of `O(this.size + that.size)`. + * The method should be overridden if computing `size` is cheap and `knownSize` returns `-1`. + */ + def sizeCompare(that: Iterable[_]): Int = { + val thatKnownSize = that.knownSize + + if (thatKnownSize >= 0) this sizeCompare thatKnownSize + else { + val thisKnownSize = this.knownSize + + if (thisKnownSize >= 0) { + val res = that sizeCompare thisKnownSize + // can't just invert the result, because `-Int.MinValue == Int.MinValue` + if (res == Int.MinValue) 1 else -res + } else { + val thisIt = this.iterator + val thatIt = that.iterator + while (thisIt.hasNext && thatIt.hasNext) { + thisIt.next() + thatIt.next() + } + java.lang.Boolean.compare(thisIt.hasNext, thatIt.hasNext) + } + } + } + + /** A view over a slice of the elements of this collection. */ + @deprecated("Use .view.slice(from, until) instead of .view(from, until)", "2.13.0") + def view(from: Int, until: Int): View[A] = view.slice(from, until) + + /** Transposes this $coll of iterable collections into + * a $coll of ${coll}s. + * + * The resulting collection's type will be guided by the + * static type of $coll. For example: + * + * {{{ + * val xs = List( + * Set(1, 2, 3), + * Set(4, 5, 6)).transpose + * // xs == List( + * // List(1, 4), + * // List(2, 5), + * // List(3, 6)) + * + * val ys = Vector( + * List(1, 2, 3), + * List(4, 5, 6)).transpose + * // ys == Vector( + * // Vector(1, 4), + * // Vector(2, 5), + * // Vector(3, 6)) + * }}} + * + * $willForceEvaluation + * + * @tparam B the type of the elements of each iterable collection. + * @param asIterable an implicit conversion which asserts that the + * element type of this $coll is an `Iterable`. + * @return a two-dimensional $coll of ${coll}s which has as ''n''th row + * the ''n''th column of this $coll. + * @throws IllegalArgumentException if all collections in this $coll + * are not of the same size. + */ + def transpose[B](implicit asIterable: A => /*<:= headSize) fail + bs(i) += x + i += 1 + } + if (i != headSize) + fail + } + iterableFactory.from(bs.map(_.result())) + } + + def filter(pred: A => Boolean): C = fromSpecific(new View.Filter(this, pred, isFlipped = false)) + + def filterNot(pred: A => Boolean): C = fromSpecific(new View.Filter(this, pred, isFlipped = true)) + + /** Creates a non-strict filter of this $coll. + * + * Note: the difference between `c filter p` and `c withFilter p` is that + * the former creates a new collection, whereas the latter only + * restricts the domain of subsequent `map`, `flatMap`, `foreach`, + * and `withFilter` operations. + * $orderDependent + * + * @param p the predicate used to test elements. + * @return an object of class `WithFilter`, which supports + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * All these operations apply to those elements of this $coll + * which satisfy the predicate `p`. + */ + def withFilter(p: A => Boolean): collection.WithFilter[A, CC] = new IterableOps.WithFilter(this, p) + + /** A pair of, first, all elements that satisfy predicate `p` and, second, + * all elements that do not. + * + * The two $coll correspond to the result of [[filter]] and [[filterNot]], respectively. + * + * The default implementation provided here needs to traverse the collection twice. + * Strict collections have an overridden version of `partition` in `StrictOptimizedIterableOps`, + * which requires only a single traversal. + */ + def partition(p: A => Boolean): (C, C) = { + val first = new View.Filter(this, p, isFlipped = false) + val second = new View.Filter(this, p, isFlipped = true) + (fromSpecific(first), fromSpecific(second)) + } + + override def splitAt(n: Int): (C, C) = (take(n), drop(n)) + + def take(n: Int): C = fromSpecific(new View.Take(this, n)) + + /** Selects the last ''n'' elements. + * $orderDependent + * @param n the number of elements to take from this $coll. + * @return a $coll consisting only of the last `n` elements of this $coll, + * or else the whole $coll, if it has less than `n` elements. + * If `n` is negative, returns an empty $coll. + */ + def takeRight(n: Int): C = fromSpecific(new View.TakeRight(this, n)) + + /** Takes longest prefix of elements that satisfy a predicate. + * $orderDependent + * @param p The predicate used to test elements. + * @return the longest prefix of this $coll whose elements all satisfy + * the predicate `p`. + */ + def takeWhile(p: A => Boolean): C = fromSpecific(new View.TakeWhile(this, p)) + + def span(p: A => Boolean): (C, C) = (takeWhile(p), dropWhile(p)) + + def drop(n: Int): C = fromSpecific(new View.Drop(this, n)) + + /** Selects all elements except last ''n'' ones. + * $orderDependent + * @param n the number of elements to drop from this $coll. + * @return a $coll consisting of all elements of this $coll except the last `n` ones, or else the + * empty $coll, if this $coll has less than `n` elements. + * If `n` is negative, don't drop any elements. + */ + def dropRight(n: Int): C = fromSpecific(new View.DropRight(this, n)) + + def dropWhile(p: A => Boolean): C = fromSpecific(new View.DropWhile(this, p)) + + /** Partitions elements in fixed size ${coll}s. + * @see [[scala.collection.Iterator]], method `grouped` + * + * @param size the number of elements per group + * @return An iterator producing ${coll}s of size `size`, except the + * last will be less than size `size` if the elements don't divide evenly. + */ + def grouped(size: Int): Iterator[C] = + iterator.grouped(size).map(fromSpecific) + + /** Groups elements in fixed size blocks by passing a "sliding window" + * over them (as opposed to partitioning them, as is done in `grouped`). + * + * An empty collection returns an empty iterator, and a non-empty + * collection containing fewer elements than the window size returns + * an iterator that will produce the original collection as its only + * element. + * @see [[scala.collection.Iterator]], method `sliding` + * + * @param size the number of elements per group + * @return An iterator producing ${coll}s of size `size`, except for a + * non-empty collection with less than `size` elements, which + * returns an iterator that produces the source collection itself + * as its only element. + * @example `List().sliding(2) = empty iterator` + * @example `List(1).sliding(2) = Iterator(List(1))` + * @example `List(1, 2).sliding(2) = Iterator(List(1, 2))` + * @example `List(1, 2, 3).sliding(2) = Iterator(List(1, 2), List(2, 3))` + */ + def sliding(size: Int): Iterator[C] = sliding(size, 1) + + /** Groups elements in fixed size blocks by passing a "sliding window" + * over them (as opposed to partitioning them, as is done in `grouped`). + * + * The returned iterator will be empty when called on an empty collection. + * The last element the iterator produces may be smaller than the window + * size when the original collection isn't exhausted by the window before + * it and its last element isn't skipped by the step before it. + * + * @see [[scala.collection.Iterator]], method `sliding` + * + * @param size the number of elements per group + * @param step the distance between the first elements of successive + * groups + * @return An iterator producing ${coll}s of size `size`, except the last + * element (which may be the only element) will be smaller + * if there are fewer than `size` elements remaining to be grouped. + * @example `List(1, 2, 3, 4, 5).sliding(2, 2) = Iterator(List(1, 2), List(3, 4), List(5))` + * @example `List(1, 2, 3, 4, 5, 6).sliding(2, 3) = Iterator(List(1, 2), List(4, 5))` + */ + def sliding(size: Int, step: Int): Iterator[C] = + iterator.sliding(size, step).map(fromSpecific) + + /** The rest of the collection without its first element. */ + def tail: C = { + if (isEmpty) throw new UnsupportedOperationException + drop(1) + } + + /** The initial part of the collection without its last element. + * $willForceEvaluation + */ + def init: C = { + if (isEmpty) throw new UnsupportedOperationException + dropRight(1) + } + + def slice(from: Int, until: Int): C = + fromSpecific(new View.Drop(new View.Take(this, until), from)) + + /** Partitions this $coll into a map of ${coll}s according to some discriminator function. + * + * $willForceEvaluation + * + * @param f the discriminator function. + * @tparam K the type of keys returned by the discriminator function. + * @return A map from keys to ${coll}s such that the following invariant holds: + * {{{ + * (xs groupBy f)(k) = xs filter (x => f(x) == k) + * }}} + * That is, every key `k` is bound to a $coll of those elements `x` + * for which `f(x)` equals `k`. + * + */ + def groupBy[K](f: A => K): immutable.Map[K, C] = { + val m = mutable.Map.empty[K, Builder[A, C]] + val it = iterator + while (it.hasNext) { + val elem = it.next() + val key = f(elem) + val bldr = m.getOrElseUpdate(key, newSpecificBuilder) + bldr += elem + } + var result = immutable.HashMap.empty[K, C] + val mapIt = m.iterator + while (mapIt.hasNext) { + val (k, v) = mapIt.next() + result = result.updated(k, v.result()) + } + result + } + + /** + * Partitions this $coll into a map of ${coll}s according to a discriminator function `key`. + * Each element in a group is transformed into a value of type `B` using the `value` function. + * + * It is equivalent to `groupBy(key).mapValues(_.map(f))`, but more efficient. + * + * {{{ + * case class User(name: String, age: Int) + * + * def namesByAge(users: Seq[User]): Map[Int, Seq[String]] = + * users.groupMap(_.age)(_.name) + * }}} + * + * $willForceEvaluation + * + * @param key the discriminator function + * @param f the element transformation function + * @tparam K the type of keys returned by the discriminator function + * @tparam B the type of values returned by the transformation function + */ + def groupMap[K, B](key: A => K)(f: A => B): immutable.Map[K, CC[B]] = { + val m = mutable.Map.empty[K, Builder[B, CC[B]]] + for (elem <- this) { + val k = key(elem) + val bldr = m.getOrElseUpdate(k, iterableFactory.newBuilder[B]) + bldr += f(elem) + } + class Result extends runtime.AbstractFunction1[(K, Builder[B, CC[B]]), Unit] { + var built = immutable.Map.empty[K, CC[B]] + def apply(kv: (K, Builder[B, CC[B]])) = + built = built.updated(kv._1, kv._2.result()) + } + val result = new Result + m.foreach(result) + result.built + } + + /** + * Partitions this $coll into a map according to a discriminator function `key`. All the values that + * have the same discriminator are then transformed by the `f` function and then reduced into a + * single value with the `reduce` function. + * + * It is equivalent to `groupBy(key).mapValues(_.map(f).reduce(reduce))`, but more efficient. + * + * {{{ + * def occurrences[A](as: Seq[A]): Map[A, Int] = + * as.groupMapReduce(identity)(_ => 1)(_ + _) + * }}} + * + * $willForceEvaluation + */ + def groupMapReduce[K, B](key: A => K)(f: A => B)(reduce: (B, B) => B): immutable.Map[K, B] = { + val m = mutable.Map.empty[K, B] + for (elem <- this) { + val k = key(elem) + val v = + m.get(k) match { + case Some(b) => reduce(b, f(elem)) + case None => f(elem) + } + m.put(k, v) + } + m.to(immutable.Map) + } + + /** Computes a prefix scan of the elements of the collection. + * + * Note: The neutral element `z` may be applied more than once. + * + * @tparam B element type of the resulting collection + * @param z neutral element for the operator `op` + * @param op the associative operator for the scan + * + * @return a new $coll containing the prefix scan of the elements in this $coll + */ + def scan[B >: A](z: B)(op: (B, B) => B): CC[B] = scanLeft(z)(op) + + def scanLeft[B](z: B)(op: (B, A) => B): CC[B] = iterableFactory.from(new View.ScanLeft(this, z, op)) + + /** Produces a collection containing cumulative results of applying the operator going right to left. + * The head of the collection is the last cumulative result. + * $willNotTerminateInf + * $orderDependent + * $willForceEvaluation + * + * Example: + * {{{ + * List(1, 2, 3, 4).scanRight(0)(_ + _) == List(10, 9, 7, 4, 0) + * }}} + * + * @tparam B the type of the elements in the resulting collection + * @param z the initial value + * @param op the binary operator applied to the intermediate result and the element + * @return collection with intermediate results + */ + def scanRight[B](z: B)(op: (A, B) => B): CC[B] = { + class Scanner extends runtime.AbstractFunction1[A, Unit] { + var acc = z + var scanned = acc :: immutable.Nil + def apply(x: A) = { + acc = op(x, acc) + scanned ::= acc + } + } + val scanner = new Scanner + reversed.foreach(scanner) + iterableFactory.from(scanner.scanned) + } + + def map[B](f: A => B): CC[B] = iterableFactory.from(new View.Map(this, f)) + + def flatMap[B](f: A => IterableOnce[B]): CC[B] = iterableFactory.from(new View.FlatMap(this, f)) + + def flatten[B](implicit asIterable: A => IterableOnce[B]): CC[B] = flatMap(asIterable) + + def collect[B](pf: PartialFunction[A, B]): CC[B] = + iterableFactory.from(new View.Collect(this, pf)) + + /** Applies a function `f` to each element of the $coll and returns a pair of ${coll}s: the first one + * made of those values returned by `f` that were wrapped in [[scala.util.Left]], and the second + * one made of those wrapped in [[scala.util.Right]]. + * + * Example: + * {{{ + * val xs = $Coll(1, "one", 2, "two", 3, "three") partitionMap { + * case i: Int => Left(i) + * case s: String => Right(s) + * } + * // xs == ($Coll(1, 2, 3), + * // $Coll(one, two, three)) + * }}} + * + * @tparam A1 the element type of the first resulting collection + * @tparam A2 the element type of the second resulting collection + * @param f the 'split function' mapping the elements of this $coll to an [[scala.util.Either]] + * + * @return a pair of ${coll}s: the first one made of those values returned by `f` that were wrapped in [[scala.util.Left]], + * and the second one made of those wrapped in [[scala.util.Right]]. + */ + def partitionMap[A1, A2](f: A => Either[A1, A2]): (CC[A1], CC[A2]) = { + val left: View[A1] = new LeftPartitionMapped(this, f) + val right: View[A2] = new RightPartitionMapped(this, f) + (iterableFactory.from(left), iterableFactory.from(right)) + } + + /** Returns a new $coll containing the elements from the left hand operand followed by the elements from the + * right hand operand. The element type of the $coll is the most specific superclass encompassing + * the element types of the two operands. + * + * @param suffix the iterable to append. + * @tparam B the element type of the returned collection. + * @return a new $coll which contains all elements + * of this $coll followed by all elements of `suffix`. + */ + def concat[B >: A](suffix: IterableOnce[B]): CC[B] = iterableFactory.from(suffix match { + case xs: Iterable[B] => new View.Concat(this, xs) + case xs => iterator ++ suffix.iterator + }) + + /** Alias for `concat` */ + @`inline` final def ++ [B >: A](suffix: IterableOnce[B]): CC[B] = concat(suffix) + + /** Returns a $coll formed from this $coll and another iterable collection + * by combining corresponding elements in pairs. + * If one of the two collections is longer than the other, its remaining elements are ignored. + * + * @param that The iterable providing the second half of each result pair + * @tparam B the type of the second half of the returned pairs + * @return a new $coll containing pairs consisting of corresponding elements of this $coll and `that`. + * The length of the returned collection is the minimum of the lengths of this $coll and `that`. + */ + def zip[B](that: IterableOnce[B]): CC[(A @uncheckedVariance, B)] = iterableFactory.from(that match { // sound bcs of VarianceNote + case that: Iterable[B] => new View.Zip(this, that) + case _ => iterator.zip(that) + }) + + def zipWithIndex: CC[(A @uncheckedVariance, Int)] = iterableFactory.from(new View.ZipWithIndex(this)) + + /** Returns a $coll formed from this $coll and another iterable collection + * by combining corresponding elements in pairs. + * If one of the two collections is shorter than the other, + * placeholder elements are used to extend the shorter collection to the length of the longer. + * + * @param that the iterable providing the second half of each result pair + * @param thisElem the element to be used to fill up the result if this $coll is shorter than `that`. + * @param thatElem the element to be used to fill up the result if `that` is shorter than this $coll. + * @return a new $coll containing pairs consisting of + * corresponding elements of this $coll and `that`. The length + * of the returned collection is the maximum of the lengths of this $coll and `that`. + * If this $coll is shorter than `that`, `thisElem` values are used to pad the result. + * If `that` is shorter than this $coll, `thatElem` values are used to pad the result. + */ + def zipAll[A1 >: A, B](that: Iterable[B], thisElem: A1, thatElem: B): CC[(A1, B)] = iterableFactory.from(new View.ZipAll(this, that, thisElem, thatElem)) + + /** Converts this $coll of pairs into two collections of the first and second + * half of each pair. + * + * {{{ + * val xs = $Coll( + * (1, "one"), + * (2, "two"), + * (3, "three")).unzip + * // xs == ($Coll(1, 2, 3), + * // $Coll(one, two, three)) + * }}} + * + * @tparam A1 the type of the first half of the element pairs + * @tparam A2 the type of the second half of the element pairs + * @param asPair an implicit conversion which asserts that the element type + * of this $coll is a pair. + * @return a pair of ${coll}s, containing the first, respectively second + * half of each element pair of this $coll. + */ + def unzip[A1, A2](implicit asPair: A => (A1, A2)): (CC[A1], CC[A2]) = { + val first: View[A1] = new View.Map[A, A1](this, asPair(_)._1) + val second: View[A2] = new View.Map[A, A2](this, asPair(_)._2) + (iterableFactory.from(first), iterableFactory.from(second)) + } + + /** Converts this $coll of triples into three collections of the first, second, + * and third element of each triple. + * + * {{{ + * val xs = $Coll( + * (1, "one", '1'), + * (2, "two", '2'), + * (3, "three", '3')).unzip3 + * // xs == ($Coll(1, 2, 3), + * // $Coll(one, two, three), + * // $Coll(1, 2, 3)) + * }}} + * + * @tparam A1 the type of the first member of the element triples + * @tparam A2 the type of the second member of the element triples + * @tparam A3 the type of the third member of the element triples + * @param asTriple an implicit conversion which asserts that the element type + * of this $coll is a triple. + * @return a triple of ${coll}s, containing the first, second, respectively + * third member of each element triple of this $coll. + */ + def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = { + val first: View[A1] = new View.Map[A, A1](this, asTriple(_)._1) + val second: View[A2] = new View.Map[A, A2](this, asTriple(_)._2) + val third: View[A3] = new View.Map[A, A3](this, asTriple(_)._3) + (iterableFactory.from(first), iterableFactory.from(second), iterableFactory.from(third)) + } + + /** Iterates over the tails of this $coll. The first value will be this + * $coll and the final one will be an empty $coll, with the intervening + * values the results of successive applications of `tail`. + * + * @return an iterator over all the tails of this $coll + * @example `List(1,2,3).tails = Iterator(List(1,2,3), List(2,3), List(3), Nil)` + */ + def tails: Iterator[C] = iterateUntilEmpty(_.tail) + + /** Iterates over the inits of this $coll. The first value will be this + * $coll and the final one will be an empty $coll, with the intervening + * values the results of successive applications of `init`. + * + * $willForceEvaluation + * + * @return an iterator over all the inits of this $coll + * @example `List(1,2,3).inits = Iterator(List(1,2,3), List(1,2), List(1), Nil)` + */ + def inits: Iterator[C] = iterateUntilEmpty(_.init) + + override def tapEach[U](f: A => U): C = fromSpecific(new View.Map(this, { (a: A) => f(a); a })) + + // A helper for tails and inits. + private[this] def iterateUntilEmpty(f: Iterable[A] => Iterable[A]): Iterator[C] = { + // toIterable ties the knot between `this: IterableOnceOps[A, CC, C]` and `this.tail: C` + // `this.tail.tail` doesn't compile as `C` is unbounded + // `Iterable.from(this)` would eagerly copy non-immutable collections + val it = Iterator.iterate(toIterable: @nowarn("cat=deprecation"))(f).takeWhile(_.nonEmpty) + (it ++ Iterator.single(Iterable.empty)).map(fromSpecific) + } + + @deprecated("Use ++ instead of ++: for collections of type Iterable", "2.13.0") + def ++:[B >: A](that: IterableOnce[B]): CC[B] = iterableFactory.from(that match { + case xs: Iterable[B] => new View.Concat(xs, this) + case _ => that.iterator ++ iterator + }) +} + +object IterableOps { + + /** Operations for comparing the size of a collection to a test value. + * + * These operations are implemented in terms of + * [[scala.collection.IterableOps.sizeCompare(Int) `sizeCompare(Int)`]]. + */ + final class SizeCompareOps private[collection](val it: IterableOps[_, AnyConstr, _]) extends AnyVal { + /** Tests if the size of the collection is less than some value. */ + @inline def <(size: Int): Boolean = it.sizeCompare(size) < 0 + /** Tests if the size of the collection is less than or equal to some value. */ + @inline def <=(size: Int): Boolean = it.sizeCompare(size) <= 0 + /** Tests if the size of the collection is equal to some value. */ + @inline def ==(size: Int): Boolean = it.sizeCompare(size) == 0 + /** Tests if the size of the collection is not equal to some value. */ + @inline def !=(size: Int): Boolean = it.sizeCompare(size) != 0 + /** Tests if the size of the collection is greater than or equal to some value. */ + @inline def >=(size: Int): Boolean = it.sizeCompare(size) >= 0 + /** Tests if the size of the collection is greater than some value. */ + @inline def >(size: Int): Boolean = it.sizeCompare(size) > 0 + } + + /** A trait that contains just the `map`, `flatMap`, `foreach` and `withFilter` methods + * of trait `Iterable`. + * + * @tparam A Element type (e.g. `Int`) + * @tparam CC Collection type constructor (e.g. `List`) + * + * @define coll collection + */ + @SerialVersionUID(3L) + class WithFilter[+A, +CC[_]]( + self: IterableOps[A, CC, _], + p: A => Boolean + ) extends collection.WithFilter[A, CC] with Serializable { + + protected def filtered: Iterable[A] = + new View.Filter(self, p, isFlipped = false) + + def map[B](f: A => B): CC[B] = + self.iterableFactory.from(new View.Map(filtered, f)) + + def flatMap[B](f: A => IterableOnce[B]): CC[B] = + self.iterableFactory.from(new View.FlatMap(filtered, f)) + + def foreach[U](f: A => U): Unit = filtered.foreach(f) + + def withFilter(q: A => Boolean): WithFilter[A, CC] = + new WithFilter(self, (a: A) => p(a) && q(a)) + + } + +} + +@SerialVersionUID(3L) +object Iterable extends IterableFactory.Delegate[Iterable](immutable.Iterable) { + + def single[A](a: A): Iterable[A] = new AbstractIterable[A] { + override def iterator = Iterator.single(a) + override def knownSize = 1 + override def head = a + override def headOption: Some[A] = Some(a) + override def last = a + override def lastOption: Some[A] = Some(a) + override def view: View.Single[A] = new View.Single(a) + override def take(n: Int) = if (n > 0) this else Iterable.empty + override def takeRight(n: Int) = if (n > 0) this else Iterable.empty + override def drop(n: Int) = if (n > 0) Iterable.empty else this + override def dropRight(n: Int) = if (n > 0) Iterable.empty else this + override def tail: Iterable[Nothing] = Iterable.empty + override def init: Iterable[Nothing] = Iterable.empty + } +} + +/** Explicit instantiation of the `Iterable` trait to reduce class file size in subclasses. */ +abstract class AbstractIterable[+A] extends Iterable[A] + +/** This trait provides default implementations for the factory methods `fromSpecific` and + * `newSpecificBuilder` that need to be refined when implementing a collection type that refines + * the `CC` and `C` type parameters. + * + * The default implementations in this trait can be used in the common case when `CC[A]` is the + * same as `C`. + */ +trait IterableFactoryDefaults[+A, +CC[x] <: IterableOps[x, CC, CC[x]]] extends IterableOps[A, CC, CC[A @uncheckedVariance]] { + protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): CC[A @uncheckedVariance] = iterableFactory.from(coll) + protected def newSpecificBuilder: Builder[A @uncheckedVariance, CC[A @uncheckedVariance]] = iterableFactory.newBuilder[A] + + // overridden for efficiency, since we know CC[A] =:= C + override def empty: CC[A @uncheckedVariance] = iterableFactory.empty +} + +/** This trait provides default implementations for the factory methods `fromSpecific` and + * `newSpecificBuilder` that need to be refined when implementing a collection type that refines + * the `CC` and `C` type parameters. It is used for collections that have an additional constraint, + * expressed by the `evidenceIterableFactory` method. + * + * The default implementations in this trait can be used in the common case when `CC[A]` is the + * same as `C`. + */ +trait EvidenceIterableFactoryDefaults[+A, +CC[x] <: IterableOps[x, CC, CC[x]], Ev[_]] extends IterableOps[A, CC, CC[A @uncheckedVariance]] { + protected def evidenceIterableFactory: EvidenceIterableFactory[CC, Ev] + implicit protected def iterableEvidence: Ev[A @uncheckedVariance] + override protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): CC[A @uncheckedVariance] = evidenceIterableFactory.from(coll) + override protected def newSpecificBuilder: Builder[A @uncheckedVariance, CC[A @uncheckedVariance]] = evidenceIterableFactory.newBuilder[A] + override def empty: CC[A @uncheckedVariance] = evidenceIterableFactory.empty +} + +/** This trait provides default implementations for the factory methods `fromSpecific` and + * `newSpecificBuilder` that need to be refined when implementing a collection type that refines + * the `CC` and `C` type parameters. It is used for sorted sets. + * + * Note that in sorted sets, the `CC` type of the set is not the same as the `CC` type for the + * underlying iterable (which is fixed to `Set` in [[SortedSetOps]]). This trait has therefore + * two type parameters `CC` and `WithFilterCC`. The `withFilter` method inherited from + * `IterableOps` is overridden with a compatible default implementation. + * + * The default implementations in this trait can be used in the common case when `CC[A]` is the + * same as `C`. + */ +trait SortedSetFactoryDefaults[+A, + +CC[X] <: SortedSet[X] with SortedSetOps[X, CC, CC[X]], + +WithFilterCC[x] <: IterableOps[x, WithFilterCC, WithFilterCC[x]] with Set[x]] extends SortedSetOps[A @uncheckedVariance, CC, CC[A @uncheckedVariance]] { + self: IterableOps[A, WithFilterCC, _] => + + override protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): CC[A @uncheckedVariance] = sortedIterableFactory.from(coll)(using ordering) + override protected def newSpecificBuilder: mutable.Builder[A @uncheckedVariance, CC[A @uncheckedVariance]] = sortedIterableFactory.newBuilder[A](using ordering) + override def empty: CC[A @uncheckedVariance] = sortedIterableFactory.empty(using ordering) + + override def withFilter(p: A => Boolean): SortedSetOps.WithFilter[A, WithFilterCC, CC] = + new SortedSetOps.WithFilter[A, WithFilterCC, CC](this, p) +} + + +/** This trait provides default implementations for the factory methods `fromSpecific` and + * `newSpecificBuilder` that need to be refined when implementing a collection type that refines + * the `CC` and `C` type parameters. It is used for maps. + * + * Note that in maps, the `CC` type of the map is not the same as the `CC` type for the + * underlying iterable (which is fixed to `Map` in [[MapOps]]). This trait has therefore + * two type parameters `CC` and `WithFilterCC`. The `withFilter` method inherited from + * `IterableOps` is overridden with a compatible default implementation. + * + * The default implementations in this trait can be used in the common case when `CC[A]` is the + * same as `C`. + */ +trait MapFactoryDefaults[K, +V, + +CC[x, y] <: IterableOps[(x, y), Iterable, Iterable[(x, y)]], + +WithFilterCC[x] <: IterableOps[x, WithFilterCC, WithFilterCC[x]] with Iterable[x]] extends MapOps[K, V, CC, CC[K, V @uncheckedVariance]] with IterableOps[(K, V), WithFilterCC, CC[K, V @uncheckedVariance]] { + override protected def fromSpecific(coll: IterableOnce[(K, V @uncheckedVariance)]): CC[K, V @uncheckedVariance] = mapFactory.from(coll) + override protected def newSpecificBuilder: mutable.Builder[(K, V @uncheckedVariance), CC[K, V @uncheckedVariance]] = mapFactory.newBuilder[K, V] + override def empty: CC[K, V @uncheckedVariance] = (this: AnyRef) match { + // Implemented here instead of in TreeSeqMap since overriding empty in TreeSeqMap is not forwards compatible (should be moved) + case self: immutable.TreeSeqMap[_, _] => immutable.TreeSeqMap.empty(self.orderedBy).asInstanceOf[CC[K, V]] + case _ => mapFactory.empty + } + + override def withFilter(p: ((K, V)) => Boolean): MapOps.WithFilter[K, V, WithFilterCC, CC] = + new MapOps.WithFilter[K, V, WithFilterCC, CC](this, p) +} + +/** This trait provides default implementations for the factory methods `fromSpecific` and + * `newSpecificBuilder` that need to be refined when implementing a collection type that refines + * the `CC` and `C` type parameters. It is used for sorted maps. + * + * Note that in sorted maps, the `CC` type of the map is not the same as the `CC` type for the + * underlying map (which is fixed to `Map` in [[SortedMapOps]]). This trait has therefore + * three type parameters `CC`, `WithFilterCC` and `UnsortedCC`. The `withFilter` method inherited + * from `IterableOps` is overridden with a compatible default implementation. + * + * The default implementations in this trait can be used in the common case when `CC[A]` is the + * same as `C`. + */ +trait SortedMapFactoryDefaults[K, +V, + +CC[x, y] <: Map[x, y] with SortedMapOps[x, y, CC, CC[x, y]] with UnsortedCC[x, y], + +WithFilterCC[x] <: IterableOps[x, WithFilterCC, WithFilterCC[x]] with Iterable[x], + +UnsortedCC[x, y] <: Map[x, y]] extends SortedMapOps[K, V, CC, CC[K, V @uncheckedVariance]] with MapOps[K, V, UnsortedCC, CC[K, V @uncheckedVariance]] { + self: IterableOps[(K, V), WithFilterCC, _] => + + override def empty: CC[K, V @uncheckedVariance] = sortedMapFactory.empty(using ordering) + override protected def fromSpecific(coll: IterableOnce[(K, V @uncheckedVariance)]): CC[K, V @uncheckedVariance] = sortedMapFactory.from(coll)(using ordering) + override protected def newSpecificBuilder: mutable.Builder[(K, V @uncheckedVariance), CC[K, V @uncheckedVariance]] = sortedMapFactory.newBuilder[K, V](using ordering) + + override def withFilter(p: ((K, V)) => Boolean): collection.SortedMapOps.WithFilter[K, V, WithFilterCC, UnsortedCC, CC] = + new collection.SortedMapOps.WithFilter[K, V, WithFilterCC, UnsortedCC, CC](this, p) +} diff --git a/library/src/scala/collection/IterableOnce.scala b/library/src/scala/collection/IterableOnce.scala new file mode 100644 index 000000000000..fc0f73ae7d7f --- /dev/null +++ b/library/src/scala/collection/IterableOnce.scala @@ -0,0 +1,1514 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.tailrec +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.mutable.StringBuilder +import scala.language.implicitConversions +import scala.math.{Numeric, Ordering} +import scala.reflect.ClassTag +import scala.runtime.{AbstractFunction1, AbstractFunction2} + +/** + * A template trait for collections which can be traversed either once only + * or one or more times. + * + * Note: `IterableOnce` does not extend [[IterableOnceOps]]. This is different than the general + * design of the collections library, which uses the following pattern: + * {{{ + * trait Seq extends Iterable with SeqOps + * trait SeqOps extends IterableOps + * + * trait IndexedSeq extends Seq with IndexedSeqOps + * trait IndexedSeqOps extends SeqOps + * }}} + * + * The goal is to provide a minimal interface without any sequential operations. This allows + * third-party extension like Scala parallel collections to integrate at the level of IterableOnce + * without inheriting unwanted implementations. + * + * @define coll collection + */ +trait IterableOnce[+A] extends Any { + + /** An [[scala.collection.Iterator]] over the elements of this $coll. + * + * If an `IterableOnce` object is in fact an [[scala.collection.Iterator]], this method always returns itself, + * in its current state, but if it is an [[scala.collection.Iterable]], this method always returns a new + * [[scala.collection.Iterator]]. + */ + def iterator: Iterator[A] + + /** Returns a [[scala.collection.Stepper]] for the elements of this collection. + * + * The Stepper enables creating a Java stream to operate on the collection, see + * [[scala.jdk.StreamConverters]]. For collections holding primitive values, the Stepper can be + * used as an iterator which doesn't box the elements. + * + * The implicit [[scala.collection.StepperShape]] parameter defines the resulting Stepper type according to the + * element type of this collection. + * + * - For collections of `Int`, `Short`, `Byte` or `Char`, an [[scala.collection.IntStepper]] is returned + * - For collections of `Double` or `Float`, a [[scala.collection.DoubleStepper]] is returned + * - For collections of `Long` a [[scala.collection.LongStepper]] is returned + * - For any other element type, an [[scala.collection.AnyStepper]] is returned + * + * Note that this method is overridden in subclasses and the return type is refined to + * `S with EfficientSplit`, for example [[scala.collection.IndexedSeqOps.stepper]]. For Steppers marked with + * [[scala.collection.Stepper.EfficientSplit]], the converters in [[scala.jdk.StreamConverters]] + * allow creating parallel streams, whereas bare Steppers can be converted only to sequential + * streams. + */ + def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntIteratorStepper (iterator.asInstanceOf[Iterator[Int]]) + case StepperShape.LongShape => new LongIteratorStepper (iterator.asInstanceOf[Iterator[Long]]) + case StepperShape.DoubleShape => new DoubleIteratorStepper(iterator.asInstanceOf[Iterator[Double]]) + case _ => shape.seqUnbox(new AnyIteratorStepper[A](iterator)) + } + s.asInstanceOf[S] + } + + /** The number of elements in this $coll, if it can be cheaply computed, + * -1 otherwise. Cheaply usually means: Not requiring a collection traversal. + */ + def knownSize: Int = -1 +} + +final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) extends AnyVal { + @deprecated("Use .iterator.withFilter(...) instead", "2.13.0") + def withFilter(f: A => Boolean): Iterator[A] = it.iterator.withFilter(f) + + @deprecated("Use .iterator.reduceLeftOption(...) instead", "2.13.0") + def reduceLeftOption(f: (A, A) => A): Option[A] = it.iterator.reduceLeftOption(f) + + @deprecated("Use .iterator.min instead", "2.13.0") + def min(implicit ord: Ordering[A]): A = it.iterator.min + + @deprecated("Use .iterator.nonEmpty instead", "2.13.0") + def nonEmpty: Boolean = it.iterator.nonEmpty + + @deprecated("Use .iterator.max instead", "2.13.0") + def max(implicit ord: Ordering[A]): A = it.iterator.max + + @deprecated("Use .iterator.reduceRight(...) instead", "2.13.0") + def reduceRight(f: (A, A) => A): A = it.iterator.reduceRight(f) + + @deprecated("Use .iterator.maxBy(...) instead", "2.13.0") + def maxBy[B](f: A => B)(implicit cmp: Ordering[B]): A = it.iterator.maxBy(f) + + @deprecated("Use .iterator.reduceLeft(...) instead", "2.13.0") + def reduceLeft(f: (A, A) => A): A = it.iterator.reduceLeft(f) + + @deprecated("Use .iterator.sum instead", "2.13.0") + def sum(implicit num: Numeric[A]): A = it.iterator.sum + + @deprecated("Use .iterator.product instead", "2.13.0") + def product(implicit num: Numeric[A]): A = it.iterator.product + + @deprecated("Use .iterator.count(...) instead", "2.13.0") + def count(f: A => Boolean): Int = it.iterator.count(f) + + @deprecated("Use .iterator.reduceOption(...) instead", "2.13.0") + def reduceOption(f: (A, A) => A): Option[A] = it.iterator.reduceOption(f) + + @deprecated("Use .iterator.minBy(...) instead", "2.13.0") + def minBy[B](f: A => B)(implicit cmp: Ordering[B]): A = it.iterator.minBy(f) + + @deprecated("Use .iterator.size instead", "2.13.0") + def size: Int = it.iterator.size + + @deprecated("Use .iterator.forall(...) instead", "2.13.0") + def forall(f: A => Boolean): Boolean = it.iterator.forall(f) + + @deprecated("Use .iterator.collectFirst(...) instead", "2.13.0") + def collectFirst[B](f: PartialFunction[A, B]): Option[B] = it.iterator.collectFirst(f) + + @deprecated("Use .iterator.filter(...) instead", "2.13.0") + def filter(f: A => Boolean): Iterator[A] = it.iterator.filter(f) + + @deprecated("Use .iterator.exists(...) instead", "2.13.0") + def exists(f: A => Boolean): Boolean = it.iterator.exists(f) + + @deprecated("Use .iterator.copyToBuffer(...) instead", "2.13.0") + def copyToBuffer(dest: mutable.Buffer[A]): Unit = it.iterator.copyToBuffer(dest) + + @deprecated("Use .iterator.reduce(...) instead", "2.13.0") + def reduce(f: (A, A) => A): A = it.iterator.reduce(f) + + @deprecated("Use .iterator.reduceRightOption(...) instead", "2.13.0") + def reduceRightOption(f: (A, A) => A): Option[A] = it.iterator.reduceRightOption(f) + + @deprecated("Use .iterator.toIndexedSeq instead", "2.13.0") + def toIndexedSeq: IndexedSeq[A] = it.iterator.toIndexedSeq + + @deprecated("Use .iterator.foreach(...) instead", "2.13.0") + @`inline` def foreach[U](f: A => U): Unit = it match { + case it: Iterable[A] => it.foreach(f) + case _ => it.iterator.foreach(f) + } + + @deprecated("Use .iterator.to(factory) instead", "2.13.0") + def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(it) + + @deprecated("Use .iterator.to(ArrayBuffer) instead", "2.13.0") + def toBuffer[B >: A]: mutable.Buffer[B] = mutable.ArrayBuffer.from(it) + + @deprecated("Use .iterator.toArray", "2.13.0") + def toArray[B >: A: ClassTag]: Array[B] = it match { + case it: Iterable[B] => it.toArray[B] + case _ => it.iterator.toArray[B] + } + + @deprecated("Use .iterator.to(List) instead", "2.13.0") + def toList: immutable.List[A] = immutable.List.from(it) + + @deprecated("Use .iterator.to(Set) instead", "2.13.0") + @`inline` def toSet[B >: A]: immutable.Set[B] = immutable.Set.from(it) + + @deprecated("Use .iterator.to(Iterable) instead", "2.13.0") + @`inline` final def toTraversable: Traversable[A] = toIterable + + @deprecated("Use .iterator.to(Iterable) instead", "2.13.0") + @`inline` final def toIterable: Iterable[A] = Iterable.from(it) + + @deprecated("Use .iterator.to(Seq) instead", "2.13.0") + @`inline` def toSeq: immutable.Seq[A] = immutable.Seq.from(it) + + @deprecated("Use .iterator.to(LazyList) instead", "2.13.0") + @`inline` def toStream: immutable.Stream[A] = immutable.Stream.from(it) + + @deprecated("Use .iterator.to(Vector) instead", "2.13.0") + @`inline` def toVector: immutable.Vector[A] = immutable.Vector.from(it) + + @deprecated("Use .iterator.to(Map) instead", "2.13.0") + def toMap[K, V](implicit ev: A <:< (K, V)): immutable.Map[K, V] = + immutable.Map.from(it.asInstanceOf[IterableOnce[(K, V)]]) + + @deprecated("Use .iterator instead", "2.13.0") + @`inline` def toIterator: Iterator[A] = it.iterator + + @deprecated("Use .iterator.isEmpty instead", "2.13.0") + def isEmpty: Boolean = it match { + case it: Iterable[A] => it.isEmpty + case _ => it.iterator.isEmpty + } + + @deprecated("Use .iterator.mkString instead", "2.13.0") + def mkString(start: String, sep: String, end: String): String = it match { + case it: Iterable[A] => it.mkString(start, sep, end) + case _ => it.iterator.mkString(start, sep, end) + } + + @deprecated("Use .iterator.mkString instead", "2.13.0") + def mkString(sep: String): String = it match { + case it: Iterable[A] => it.mkString(sep) + case _ => it.iterator.mkString(sep) + } + + @deprecated("Use .iterator.mkString instead", "2.13.0") + def mkString: String = it match { + case it: Iterable[A] => it.mkString + case _ => it.iterator.mkString + } + + @deprecated("Use .iterator.find instead", "2.13.0") + def find(p: A => Boolean): Option[A] = it.iterator.find(p) + + @deprecated("Use .iterator.foldLeft instead", "2.13.0") + @`inline` def foldLeft[B](z: B)(op: (B, A) => B): B = it.iterator.foldLeft(z)(op) + + @deprecated("Use .iterator.foldRight instead", "2.13.0") + @`inline` def foldRight[B](z: B)(op: (A, B) => B): B = it.iterator.foldRight(z)(op) + + @deprecated("Use .iterator.fold instead", "2.13.0") + def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = it.iterator.fold(z)(op) + + @deprecated("Use .iterator.foldLeft instead", "2.13.0") + @`inline` def /: [B](z: B)(op: (B, A) => B): B = foldLeft[B](z)(op) + + @deprecated("Use .iterator.foldRight instead", "2.13.0") + @`inline` def :\ [B](z: B)(op: (A, B) => B): B = foldRight[B](z)(op) + + @deprecated("Use .iterator.map instead or consider requiring an Iterable", "2.13.0") + def map[B](f: A => B): IterableOnce[B] = it match { + case it: Iterable[A] => it.map(f) + case _ => it.iterator.map(f) + } + + @deprecated("Use .iterator.flatMap instead or consider requiring an Iterable", "2.13.0") + def flatMap[B](f: A => IterableOnce[B]): IterableOnce[B] = it match { + case it: Iterable[A] => it.flatMap(f) + case _ => it.iterator.flatMap(f) + } + + @deprecated("Use .iterator.sameElements instead", "2.13.0") + def sameElements[B >: A](that: IterableOnce[B]): Boolean = it.iterator.sameElements(that) +} + +object IterableOnce { + @inline implicit def iterableOnceExtensionMethods[A](it: IterableOnce[A]): IterableOnceExtensionMethods[A] = + new IterableOnceExtensionMethods[A](it) + + /** Computes the number of elements to copy to an array from a source IterableOnce + * + * @param srcLen the length of the source collection + * @param destLen the length of the destination array + * @param start the index in the destination array at which to start copying elements to + * @param len the requested number of elements to copy (we may only be able to copy less than this) + * @return the number of elements that will be copied to the destination array + */ + @inline private[collection] def elemsToCopyToArray(srcLen: Int, destLen: Int, start: Int, len: Int): Int = + math.max(math.min(math.min(len, srcLen), destLen - start), 0) + + /** Calls `copyToArray` on the given collection, regardless of whether or not it is an `Iterable`. */ + @inline private[collection] def copyElemsToArray[A, B >: A](elems: IterableOnce[A], + xs: Array[B], + start: Int = 0, + len: Int = Int.MaxValue): Int = + elems match { + case src: Iterable[A] => src.copyToArray[B](xs, start, len) + case src => src.iterator.copyToArray[B](xs, start, len) + } +} + +/** This implementation trait can be mixed into an `IterableOnce` to get the basic methods that are shared between + * `Iterator` and `Iterable`. The `IterableOnce` must support multiple calls to `iterator` but may or may not + * return the same `Iterator` every time. + * + * @define orderDependent + * + * Note: might return different results for different runs, unless the underlying collection type is ordered. + * @define orderDependentReduce + * + * Note: might return different results for different runs, unless the + * underlying collection type is ordered or the operator is associative + * and commutative. + * @define orderIndependentReduce + * + * Note: might return different results for different runs, unless either + * of the following conditions is met: (1) the operator is associative, + * and the underlying collection type is ordered; or (2) the operator is + * associative and commutative. + * @define mayNotTerminateInf + * + * Note: may not terminate for infinite-sized collections. + * @define willNotTerminateInf + * + * Note: will not terminate for infinite-sized collections. + * @define willForceEvaluation + * Note: Even when applied to a view or a lazy collection it will always force the elements. + * @define consumesIterator + * After calling this method, one should discard the iterator it was called + * on. Using it is undefined and subject to change. + * @define undefinedOrder + * The order of applications of the operator is unspecified and may be nondeterministic. + * @define exactlyOnce + * Each element appears exactly once in the computation. + * @define coll collection + * + */ +trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => + /////////////////////////////////////////////////////////////// Abstract methods that must be implemented + + /** Produces a $coll containing cumulative results of applying the + * operator going left to right, including the initial value. + * + * $willNotTerminateInf + * $orderDependent + * + * @tparam B the type of the elements in the resulting collection + * @param z the initial value + * @param op the binary operator applied to the intermediate result and the element + * @return collection with intermediate results + */ + def scanLeft[B](z: B)(op: (B, A) => B): CC[B] + + /** Selects all elements of this $coll which satisfy a predicate. + * + * @param p the predicate used to test elements. + * @return a new $coll consisting of all elements of this $coll that satisfy the given + * predicate `p`. The order of the elements is preserved. + */ + def filter(p: A => Boolean): C + + /** Selects all elements of this $coll which do not satisfy a predicate. + * + * @param pred the predicate used to test elements. + * @return a new $coll consisting of all elements of this $coll that do not satisfy the given + * predicate `pred`. Their order may not be preserved. + */ + def filterNot(pred: A => Boolean): C + + /** Selects the first `n` elements. + * $orderDependent + * @param n the number of elements to take from this $coll. + * @return a $coll consisting only of the first `n` elements of this $coll, + * or else the whole $coll, if it has less than `n` elements. + * If `n` is negative, returns an empty $coll. + */ + def take(n: Int): C + + /** Selects the longest prefix of elements that satisfy a predicate. + * + * The matching prefix starts with the first element of this $coll, + * and the element following the prefix is the first element that + * does not satisfy the predicate. The matching prefix may empty, + * so that this method returns an empty $coll. + * + * Example: + * + * {{{ + * scala> List(1, 2, 3, 100, 4).takeWhile(n => n < 10) + * val res0: List[Int] = List(1, 2, 3) + * + * scala> List(1, 2, 3, 100, 4).takeWhile(n => n == 0) + * val res1: List[Int] = List() + * }}} + * + * Use [[span]] to obtain both the prefix and suffix. + * Use [[filter]] to retain only those elements from the entire $coll that satisfy the predicate. + * $orderDependent + * @param p The predicate used to test elements. + * @return the longest prefix of this $coll whose elements all satisfy + * the predicate `p`. + */ + def takeWhile(p: A => Boolean): C + + /** Selects all elements except the first `n` ones. + * $orderDependent + * @param n the number of elements to drop from this $coll. + * @return a $coll consisting of all elements of this $coll except the first `n` ones, or else the + * empty $coll, if this $coll has less than `n` elements. + * If `n` is negative, don't drop any elements. + */ + def drop(n: Int): C + + /** Selects all elements except the longest prefix that satisfies a predicate. + * + * The matching prefix starts with the first element of this $coll, + * and the element following the prefix is the first element that + * does not satisfy the predicate. The matching prefix may be empty, + * so that this method returns the entire $coll. + * + * Example: + * + * {{{ + * scala> List(1, 2, 3, 100, 4).dropWhile(n => n < 10) + * val res0: List[Int] = List(100, 4) + * + * scala> List(1, 2, 3, 100, 4).dropWhile(n => n == 0) + * val res1: List[Int] = List(1, 2, 3, 100, 4) + * }}} + * + * Use [[span]] to obtain both the prefix and suffix. + * Use [[filterNot]] to drop all elements that satisfy the predicate. + * + * $orderDependent + * @param p The predicate used to test elements. + * @return the longest suffix of this $coll whose first element + * does not satisfy the predicate `p`. + */ + def dropWhile(p: A => Boolean): C + + /** Selects an interval of elements. The returned $coll is made up + * of all elements `x` which satisfy the invariant: + * {{{ + * from <= indexOf(x) < until + * }}} + * $orderDependent + * + * @param from the lowest index to include from this $coll. + * @param until the lowest index to EXCLUDE from this $coll. + * @return a $coll containing the elements greater than or equal to + * index `from` extending up to (but not including) index `until` + * of this $coll. + */ + def slice(from: Int, until: Int): C + + /** Builds a new $coll by applying a function to all elements of this $coll. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned $coll. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. + */ + def map[B](f: A => B): CC[B] + + /** Builds a new $coll by applying a function to all elements of this $coll + * and using the elements of the resulting collections. + * + * For example: + * + * {{{ + * def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+") + * }}} + * + * The type of the resulting collection is guided by the static type of $coll. This might + * cause unexpected results sometimes. For example: + * + * {{{ + * // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set + * def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet) + * + * // lettersOf will return a Set[Char], not a Seq + * def lettersOf(words: Seq[String]) = words.toSet flatMap ((word: String) => word.toSeq) + * + * // xs will be an Iterable[Int] + * val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2) + * + * // ys will be a Map[Int, Int] + * val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2) + * }}} + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ + def flatMap[B](f: A => IterableOnce[B]): CC[B] + + /** Converts this $coll of iterable collections into + * a $coll formed by the elements of these iterable + * collections. + * + * The resulting collection's type will be guided by the + * type of $coll. For example: + * + * {{{ + * val xs = List( + * Set(1, 2, 3), + * Set(1, 2, 3) + * ).flatten + * // xs == List(1, 2, 3, 1, 2, 3) + * + * val ys = Set( + * List(1, 2, 3), + * List(3, 2, 1) + * ).flatten + * // ys == Set(1, 2, 3) + * }}} + * + * @tparam B the type of the elements of each iterable collection. + * @param asIterable an implicit conversion which asserts that the element + * type of this $coll is an `Iterable`. + * @return a new $coll resulting from concatenating all element ${coll}s. + */ + def flatten[B](implicit asIterable: A => IterableOnce[B]): CC[B] + + /** Builds a new $coll by applying a partial function to all elements of this $coll + * on which the function is defined. + * + * @param pf the partial function which filters and maps the $coll. + * @tparam B the element type of the returned $coll. + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ + def collect[B](pf: PartialFunction[A, B]): CC[B] + + /** Zips this $coll with its indices. + * + * @return A new $coll containing pairs consisting of all elements of this $coll paired with their index. + * Indices start at `0`. + * @example + * `List("a", "b", "c").zipWithIndex == List(("a", 0), ("b", 1), ("c", 2))` + */ + def zipWithIndex: CC[(A @uncheckedVariance, Int)] + + /** Splits this $coll into a prefix/suffix pair according to a predicate. + * + * Note: `c span p` is equivalent to (but possibly more efficient than) + * `(c takeWhile p, c dropWhile p)`, provided the evaluation of the + * predicate `p` does not cause any side-effects. + * $orderDependent + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of this $coll whose + * elements all satisfy `p`, and the rest of this $coll. + */ + def span(p: A => Boolean): (C, C) + + /** Splits this $coll into a prefix/suffix pair at a given position. + * + * Note: `c splitAt n` is equivalent to (but possibly more efficient than) + * `(c take n, c drop n)`. + * $orderDependent + * + * @param n the position at which to split. + * @return a pair of ${coll}s consisting of the first `n` + * elements of this $coll, and the other elements. + */ + def splitAt(n: Int): (C, C) = { + class Spanner extends runtime.AbstractFunction1[A, Boolean] { + var i = 0 + def apply(a: A) = i < n && { i += 1 ; true } + } + val spanner = new Spanner + span(spanner) + } + + /** Applies a side-effecting function to each element in this collection. + * Strict collections will apply `f` to their elements immediately, while lazy collections + * like Views and LazyLists will only apply `f` on each element if and when that element + * is evaluated, and each time that element is evaluated. + * + * @param f a function to apply to each element in this $coll + * @tparam U the return type of f + * @return The same logical collection as this + */ + def tapEach[U](f: A => U): C + + /////////////////////////////////////////////////////////////// Concrete methods based on iterator + + /** Tests whether this $coll is known to have a finite size. + * All strict collections are known to have finite size. For a non-strict + * collection such as `Stream`, the predicate returns `'''true'''` if all + * elements have been computed. It returns `'''false'''` if the stream is + * not yet evaluated to the end. Non-empty Iterators usually return + * `'''false'''` even if they were created from a collection with a known + * finite size. + * + * Note: many collection methods will not work on collections of infinite sizes. + * The typical failure mode is an infinite loop. These methods always attempt a + * traversal without checking first that `hasDefiniteSize` returns `'''true'''`. + * However, checking `hasDefiniteSize` can provide an assurance that size is + * well-defined and non-termination is not a concern. + * + * @deprecated This method is deprecated in 2.13 because it does not provide any + * actionable information. As noted above, even the collection library itself + * does not use it. When there is no guarantee that a collection is finite, it + * is generally best to attempt a computation anyway and document that it will + * not terminate for infinite collections rather than backing out because this + * would prevent performing the computation on collections that are in fact + * finite even though `hasDefiniteSize` returns `false`. + * + * @see method `knownSize` for a more useful alternative + * + * @return `'''true'''` if this collection is known to have finite size, + * `'''false'''` otherwise. + */ + @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0") + def hasDefiniteSize: Boolean = true + + /** Tests whether this $coll can be repeatedly traversed. Always + * true for Iterables and false for Iterators unless overridden. + * + * @return `true` if it is repeatedly traversable, `false` otherwise. + */ + def isTraversableAgain: Boolean = false + + /** Applies `f` to each element for its side effects. + * Note: `U` parameter needed to help scalac's type inference. + */ + def foreach[U](f: A => U): Unit = { + val it = iterator + while(it.hasNext) f(it.next()) + } + + /** Tests whether a predicate holds for all elements of this $coll. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return `true` if this $coll is empty or the given predicate `p` + * holds for all elements of this $coll, otherwise `false`. + */ + def forall(p: A => Boolean): Boolean = { + var res = true + val it = iterator + while (res && it.hasNext) res = p(it.next()) + res + } + + /** Tests whether a predicate holds for at least one element of this $coll. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return `true` if the given predicate `p` is satisfied by at least one element of this $coll, otherwise `false` + */ + def exists(p: A => Boolean): Boolean = { + var res = false + val it = iterator + while (!res && it.hasNext) res = p(it.next()) + res + } + + /** Counts the number of elements in the $coll which satisfy a predicate. + * + * $willNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the number of elements satisfying the predicate `p`. + */ + def count(p: A => Boolean): Int = { + var res = 0 + val it = iterator + while (it.hasNext) if (p(it.next())) res += 1 + res + } + + /** Finds the first element of the $coll satisfying a predicate, if any. + * + * $mayNotTerminateInf + * $orderDependent + * + * @param p the predicate used to test elements. + * @return an option value containing the first element in the $coll + * that satisfies `p`, or `None` if none exists. + */ + def find(p: A => Boolean): Option[A] = { + val it = iterator + while (it.hasNext) { + val a = it.next() + if (p(a)) return Some(a) + } + None + } + + // in future, move to IndexedSeqOps + private def foldl[X >: A, B](seq: IndexedSeq[X], start: Int, z: B, op: (B, X) => B): B = { + @tailrec def loop(at: Int, end: Int, acc: B): B = + if (at == end) acc + else loop(at + 1, end, op(acc, seq(at))) + loop(start, seq.length, z) + } + + private def foldr[X >: A, B >: X](seq: IndexedSeq[X], op: (X, B) => B): B = { + @tailrec def loop(at: Int, acc: B): B = + if (at == 0) acc + else loop(at - 1, op(seq(at - 1), acc)) + loop(seq.length - 1, seq(seq.length - 1)) + } + + /** Applies the given binary operator `op` to the given initial value `z` and all + * elements of this $coll, going left to right. Returns the initial value if this $coll + * is empty. + * + * "Going left to right" only makes sense if this collection is ordered: then if + * `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is + * `op( op( ... op( op(z, x,,1,,), x,,2,,) ... ), x,,n,,)`. + * + * If this collection is not ordered, then for each application of the operator, each + * right operand is an element. In addition, the leftmost operand is the initial + * value, and each other left operand is itself an application of the operator. The + * elements of this $coll and the initial value all appear exactly once in the + * computation. + * + * $orderDependent + * $willNotTerminateInf + * + * @param z An initial value. + * @param op A binary operator. + * @tparam B The result type of the binary operator. + * @return The result of applying `op` to `z` and all elements of this $coll, + * going left to right. Returns `z` if this $coll is empty. + */ + def foldLeft[B](z: B)(op: (B, A) => B): B = this match { + case seq: IndexedSeq[A @unchecked] => foldl[A, B](seq, 0, z, op) + case _ => + var result = z + val it = iterator + while (it.hasNext) { + result = op(result, it.next()) + } + result + } + + /** Applies the given binary operator `op` to all elements of this $coll and the given + * initial value `z`, going right to left. Returns the initial value if this $coll is + * empty. + * + * "Going right to left" only makes sense if this collection is ordered: then if + * `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is + * `op(x,,1,,, op(x,,2,,, op( ... op(x,,n,,, z) ... )))`. + * + * If this collection is not ordered, then for each application of the operator, each + * left operand is an element. In addition, the rightmost operand is the initial + * value, and each other right operand is itself an application of the operator. The + * elements of this $coll and the initial value all appear exactly once in the + * computation. + * + * $orderDependent + * $willNotTerminateInf + * + * @param z An initial value. + * @param op A binary operator. + * @tparam B The result type of the binary operator. + * @return The result of applying `op` to all elements of this $coll and `z`, + * going right to left. Returns `z` if this $coll is empty. + */ + def foldRight[B](z: B)(op: (A, B) => B): B = reversed.foldLeft(z)((b, a) => op(a, b)) + + @deprecated("Use foldLeft instead of /:", "2.13.0") + @`inline` final def /: [B](z: B)(op: (B, A) => B): B = foldLeft[B](z)(op) + + @deprecated("Use foldRight instead of :\\", "2.13.0") + @`inline` final def :\ [B](z: B)(op: (A, B) => B): B = foldRight[B](z)(op) + + /** Applies the given binary operator `op` to the given initial value `z` and all + * elements of this $coll. + * + * For each application of the operator, each operand is either an element of this + * $coll, the initial value, or another such application of the operator. + * $undefinedOrder $exactlyOnce The initial value may be used an arbitrary number of + * times, but at least once. + * + * If this collection is ordered, then for any application of the operator, the + * element(s) appearing in the left operand will precede those in the right. + * + * $orderIndependentReduce In either case, it is also necessary that the initial value + * be a neutral value for the operator, e.g. `Nil` for `List` concatenation or `1` for + * multiplication. + * + * The default implementation in `IterableOnce` is equivalent to `foldLeft` but may be + * overridden for more efficient traversal orders. + * + * $willNotTerminateInf + * + * @tparam A1 The type parameter for the binary operator, a supertype of `A`. + * @param z An initial value; may be used an arbitrary number of times in the + * computation of the result; must be a neutral value for `op` for the + * result to always be the same across runs. + * @param op A binary operator; must be associative for the result to always be the + * same across runs. + * @return The result of applying `op` between all the elements and `z`, or `z` + * if this $coll is empty. + */ + def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op) + + /** Applies the given binary operator `op` to all elements of this $coll. + * + * For each application of the operator, each operand is either an element of this + * $coll or another such application of the operator. $undefinedOrder $exactlyOnce + * + * If this collection is ordered, then for any application of the operator, the + * element(s) appearing in the left operand will precede those in the right. + * + * $orderIndependentReduce + * $willNotTerminateInf + * + * @tparam B The type parameter for the binary operator, a supertype of `A`. + * @param op A binary operator; must be associative for the result to always be the + * same across runs. + * @return The result of applying `op` between all the elements if the $coll is + * nonempty. + * @throws UnsupportedOperationException if this $coll is empty. + */ + def reduce[B >: A](op: (B, B) => B): B = reduceLeft(op) + + /** If this $coll is nonempty, reduces it with the given binary operator `op`. + * + * The behavior is the same as [[reduce]] except that the value is `None` if the $coll + * is empty. $undefinedOrder $exactlyOnce + * + * $orderIndependentReduce + * $willNotTerminateInf + * + * @tparam B A type parameter for the binary operator, a supertype of `A`. + * @param op A binary operator; must be associative for the result to always be the + * same across runs. + * @return The result of reducing this $coll with `op` if the $coll is nonempty, + * inside a `Some`, and `None` otherwise. + */ + def reduceOption[B >: A](op: (B, B) => B): Option[B] = reduceLeftOption(op) + + /** Applies the given binary operator `op` to all elements of this $coll, going left to + * right. + * + * "Going left to right" only makes sense if this collection is ordered: then if + * `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is + * `op( op( op( ... op(x,,1,,, x,,2,,) ... ), x,,n-1,,), x,,n,,)`. + * + * If this collection is not ordered, then for each application of the operator, each + * right operand is an element. In addition, the leftmost operand is the first element + * of this $coll and each other left operand is itself an application of the + * operator. $exactlyOnce + * + * $orderDependentReduce + * $willNotTerminateInf + * + * @param op A binary operator. + * @tparam B The result type of the binary operator, a supertype of `A`. + * @return The result of applying `op` to all elements of this $coll, going + * left to right. + * @throws UnsupportedOperationException if this $coll is empty. + */ + def reduceLeft[B >: A](op: (B, A) => B): B = this match { + case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldl(seq, 1, seq(0), op) + case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceLeft") + case _ => reduceLeftIterator[B](throw new UnsupportedOperationException("empty.reduceLeft"))(op) + } + private final def reduceLeftIterator[B >: A](onEmpty: => B)(op: (B, A) => B): B = { + val it = iterator + if (it.hasNext) { + var acc: B = it.next() + while (it.hasNext) + acc = op(acc, it.next()) + acc + } + else onEmpty + } + + /** Applies the given binary operator `op` to all elements of this $coll, going right to + * left. + * + * "Going right to left" only makes sense if this collection is ordered: then if + * `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the elements of this $coll, the result is + * `op(x,,1,,, op(x,,2,,, op( ... op(x,,n-1,,, x,,n,,) ... )))`. + * + * If this collection is not ordered, then for each application of the operator, each + * left operand is an element. In addition, the rightmost operand is the last element + * of this $coll and each other right operand is itself an application of the + * operator. $exactlyOnce + * + * $orderDependentReduce + * $willNotTerminateInf + * + * @param op A binary operator. + * @tparam B The result type of the binary operator, a supertype of `A`. + * @return The result of applying `op` to all elements of this $coll, going + * right to left. + * @throws UnsupportedOperationException if this $coll is empty. + */ + def reduceRight[B >: A](op: (A, B) => B): B = this match { + case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldr[A, B](seq, op) + case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceRight") + case _ => reversed.reduceLeft[B]((x, y) => op(y, x)) // reduceLeftIterator + } + + /** If this $coll is nonempty, reduces it with the given binary operator `op`, going + * left to right. + * + * The behavior is the same as [[reduceLeft]] except that the value is `None` if the + * $coll is empty. $exactlyOnce + * + * $orderDependentReduce + * $willNotTerminateInf + * + * @param op A binary operator. + * @tparam B The result type of the binary operator, a supertype of `A`. + * @return The result of reducing this $coll with `op` going left to right if + * the $coll is nonempty, inside a `Some`, and `None` otherwise. + */ + def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] = + knownSize match { + case -1 => reduceLeftOptionIterator[B](op) + case 0 => None + case _ => Some(reduceLeft(op)) + } + private final def reduceLeftOptionIterator[B >: A](op: (B, A) => B): Option[B] = reduceOptionIterator[A, B](iterator)(op) + private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X])(op: (B, X) => B): Option[B] = { + if (it.hasNext) { + var acc: B = it.next() + while (it.hasNext) + acc = op(acc, it.next()) + Some(acc) + } + else None + } + + /** If this $coll is nonempty, reduces it with the given binary operator `op`, going + * right to left. + * + * The behavior is the same as [[reduceRight]] except that the value is `None` if the + * $coll is empty. $exactlyOnce + * + * $orderDependentReduce + * $willNotTerminateInf + * + * @param op A binary operator. + * @tparam B The result type of the binary operator, a supertype of `A`. + * @return The result of reducing this $coll with `op` going right to left if + * the $coll is nonempty, inside a `Some`, and `None` otherwise. + */ + def reduceRightOption[B >: A](op: (A, B) => B): Option[B] = + knownSize match { + case -1 => reduceOptionIterator[A, B](reversed.iterator)((x, y) => op(y, x)) + case 0 => None + case _ => Some(reduceRight(op)) + } + + /** Tests whether the $coll is empty. + * + * Note: The default implementation creates and discards an iterator. + * + * Note: Implementations in subclasses that are not repeatedly iterable must take + * care not to consume any elements when `isEmpty` is called. + * + * @return `true` if the $coll contains no elements, `false` otherwise. + */ + def isEmpty: Boolean = + knownSize match { + case -1 => !iterator.hasNext + case 0 => true + case _ => false + } + + /** Tests whether the $coll is not empty. + * + * @return `true` if the $coll contains at least one element, `false` otherwise. + */ + @deprecatedOverriding("nonEmpty is defined as !isEmpty; override isEmpty instead", "2.13.0") + def nonEmpty: Boolean = !isEmpty + + /** The size of this $coll. + * + * $willNotTerminateInf + * + * @return the number of elements in this $coll. + */ + def size: Int = + if (knownSize >= 0) knownSize + else { + val it = iterator + var len = 0 + while (it.hasNext) { len += 1; it.next() } + len + } + + @deprecated("Use `dest ++= coll` instead", "2.13.0") + @inline final def copyToBuffer[B >: A](dest: mutable.Buffer[B]): Unit = dest ++= this + + /** Copies elements to an array, returning the number of elements written. + * + * Fills the given array `xs` starting at index `start` with values of this $coll. + * + * Copying will stop once either all the elements of this $coll have been copied, + * or the end of the array is reached. + * + * @param xs the array to fill. + * @tparam B the type of the elements of the array. + * @return the number of elements written to the array + * + * @note Reuse: $consumesIterator + */ + @deprecatedOverriding("This should always forward to the 3-arg version of this method", since = "2.13.4") + def copyToArray[B >: A](xs: Array[B]): Int = copyToArray(xs, 0, Int.MaxValue) + + /** Copies elements to an array, returning the number of elements written. + * + * Fills the given array `xs` starting at index `start` with values of this $coll. + * + * Copying will stop once either all the elements of this $coll have been copied, + * or the end of the array is reached. + * + * @param xs the array to fill. + * @param start the starting index of xs. + * @tparam B the type of the elements of the array. + * @return the number of elements written to the array + * + * @note Reuse: $consumesIterator + */ + @deprecatedOverriding("This should always forward to the 3-arg version of this method", since = "2.13.4") + def copyToArray[B >: A](xs: Array[B], start: Int): Int = copyToArray(xs, start, Int.MaxValue) + + /** Copy elements to an array, returning the number of elements written. + * + * Fills the given array `xs` starting at index `start` with at most `len` elements of this $coll. + * + * Copying will stop once either all the elements of this $coll have been copied, + * or the end of the array is reached, or `len` elements have been copied. + * + * @param xs the array to fill. + * @param start the starting index of xs. + * @param len the maximal number of elements to copy. + * @tparam B the type of the elements of the array. + * @return the number of elements written to the array + * + * @note Reuse: $consumesIterator + */ + def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { + val it = iterator + var i = start + val end = start + math.min(len, xs.length - start) + while (i < end && it.hasNext) { + xs(i) = it.next() + i += 1 + } + i - start + } + + /** Sums the elements of this collection. + * + * The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise. + * + * $willNotTerminateInf + * + * @param num an implicit parameter defining a set of numeric operations + * which includes the `+` operator to be used in forming the sum. + * @tparam B the result type of the `+` operator. + * @return the sum of all elements of this $coll with respect to the `+` operator in `num`. + */ + def sum[B >: A](implicit num: Numeric[B]): B = + knownSize match { + case -1 => foldLeft(num.zero)(num.plus) + case 0 => num.zero + case _ => reduce(num.plus) + } + + /** Multiplies together the elements of this collection. + * + * The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise. + * + * $willNotTerminateInf + * + * @param num an implicit parameter defining a set of numeric operations + * which includes the `*` operator to be used in forming the product. + * @tparam B the result type of the `*` operator. + * @return the product of all elements of this $coll with respect to the `*` operator in `num`. + */ + def product[B >: A](implicit num: Numeric[B]): B = + knownSize match { + case -1 => foldLeft(num.one)(num.times) + case 0 => num.one + case _ => reduce(num.times) + } + + /** Finds the smallest element. + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the smallest element of this $coll with respect to the ordering `ord`. + * + */ + def min[B >: A](implicit ord: Ordering[B]): A = + knownSize match { + case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.min"))(ord.min) + case 0 => throw new UnsupportedOperationException("empty.min") + case _ => reduceLeft(ord.min) + } + + /** Finds the smallest element. + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @return an option value containing the smallest element of this $coll + * with respect to the ordering `ord`. + */ + def minOption[B >: A](implicit ord: Ordering[B]): Option[A] = + knownSize match { + case -1 => reduceLeftOptionIterator[A](ord.min) + case 0 => None + case _ => Some(reduceLeft(ord.min)) + } + + /** Finds the largest element. + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the largest element of this $coll with respect to the ordering `ord`. + */ + def max[B >: A](implicit ord: Ordering[B]): A = + knownSize match { + case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.max"))(ord.max) + case 0 => throw new UnsupportedOperationException("empty.max") + case _ => reduceLeft(ord.max) + } + + /** Finds the largest element. + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @return an option value containing the largest element of this $coll with + * respect to the ordering `ord`. + */ + def maxOption[B >: A](implicit ord: Ordering[B]): Option[A] = + knownSize match { + case -1 => reduceLeftOptionIterator[A](ord.max) + case 0 => None + case _ => Some(reduceLeft(ord.max)) + } + + /** Finds the first element which yields the largest value measured by function `f`. + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function `f`. + * @param f The measuring function. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the first element of this $coll with the largest value measured by function `f` + * with respect to the ordering `cmp`. + */ + def maxBy[B](f: A => B)(implicit ord: Ordering[B]): A = + knownSize match { + case 0 => throw new UnsupportedOperationException("empty.maxBy") + case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).result + } + + private class Maximized[X, B](descriptor: String)(f: X => B)(cmp: (B, B) => Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] { + var maxElem: X = null.asInstanceOf[X] + var maxF: B = null.asInstanceOf[B] + var nonEmpty = false + def toOption: Option[X] = if (nonEmpty) Some(maxElem) else None + def result: X = if (nonEmpty) maxElem else throw new UnsupportedOperationException(s"empty.$descriptor") + def apply(m: Maximized[X, B], a: X): Maximized[X, B] = + if (m.nonEmpty) { + val fa = f(a) + if (cmp(fa, maxF)) { + maxF = fa + maxElem = a + } + m + } + else { + m.nonEmpty = true + m.maxElem = a + m.maxF = f(a) + m + } + } + + /** Finds the first element which yields the largest value measured by function `f`. + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function `f`. + * @param f The measuring function. + * @return an option value containing the first element of this $coll with the + * largest value measured by function `f` with respect to the ordering `cmp`. + */ + def maxByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + knownSize match { + case 0 => None + case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).toOption + } + + /** Finds the first element which yields the smallest value measured by function `f`. + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function `f`. + * @param f The measuring function. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the first element of this $coll with the smallest value measured by function `f` + * with respect to the ordering `cmp`. + */ + def minBy[B](f: A => B)(implicit ord: Ordering[B]): A = + knownSize match { + case 0 => throw new UnsupportedOperationException("empty.minBy") + case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).result + } + + /** Finds the first element which yields the smallest value measured by function `f`. + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function `f`. + * @param f The measuring function. + * @return an option value containing the first element of this $coll + * with the smallest value measured by function `f` + * with respect to the ordering `cmp`. + */ + def minByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + knownSize match { + case 0 => None + case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).toOption + } + + /** Finds the first element of the $coll for which the given partial + * function is defined, and applies the partial function to it. + * + * $mayNotTerminateInf + * $orderDependent + * + * @param pf the partial function + * @return an option value containing pf applied to the first + * value for which it is defined, or `None` if none exists. + * @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)` + */ + def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = { + // Presumably the fastest way to get in and out of a partial function is for a sentinel function to return itself + // (Tested to be lower-overhead than runWith. Would be better yet to not need to (formally) allocate it) + val sentinel: scala.Function1[A, Any] = new AbstractFunction1[A, Any] { + def apply(a: A): AbstractFunction1[A, Any] = this + } + val it = iterator + while (it.hasNext) { + val x = pf.applyOrElse(it.next(), sentinel) + if (x.asInstanceOf[AnyRef] ne sentinel) return Some(x.asInstanceOf[B]) + } + None + } + + /** Aggregates the results of applying an operator to subsequent elements. + * + * Since this method degenerates to `foldLeft` for sequential (non-parallel) collections, + * where the combining operation is ignored, it is advisable to prefer `foldLeft` for that case. + * + * For [[https://github.com/scala/scala-parallel-collections parallel collections]], + * use the `aggregate` method specified by `scala.collection.parallel.ParIterableLike`. + * + * @param z the start value, a neutral element for `seqop`. + * @param seqop the binary operator used to accumulate the result. + * @param combop an associative operator for combining sequential results, unused for sequential collections. + * @tparam B the result type, produced by `seqop`, `combop`, and by this function as a final result. + */ + @deprecated("For sequential collections, prefer `foldLeft(z)(seqop)`. For parallel collections, use `ParIterableLike#aggregate`.", "2.13.0") + def aggregate[B](z: => B)(seqop: (B, A) => B, combop: (B, B) => B): B = foldLeft(z)(seqop) + + /** Tests whether every element of this collection's iterator relates to the + * corresponding element of another collection by satisfying a test predicate. + * + * $willNotTerminateInf + * + * @param that the other collection + * @param p the test predicate, which relates elements from both collections + * @tparam B the type of the elements of `that` + * @return `true` if both collections have the same length and + * `p(x, y)` is `true` for all corresponding elements `x` of this iterator + * and `y` of `that`, otherwise `false` + */ + def corresponds[B](that: IterableOnce[B])(p: (A, B) => Boolean): Boolean = { + val a = iterator + val b = that.iterator + + while (a.hasNext && b.hasNext) { + if (!p(a.next(), b.next())) return false + } + + a.hasNext == b.hasNext + } + + /** Displays all elements of this $coll in a string using start, end, and separator strings. + * + * Delegates to addString, which can be overridden. + * + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return a string representation of this $coll. The resulting string + * begins with the string `start` and ends with the string + * `end`. Inside, the string representations (w.r.t. the method + * `toString`) of all elements of this $coll are separated by + * the string `sep`. + * + * @example `List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"` + */ + final def mkString(start: String, sep: String, end: String): String = + if (knownSize == 0) start + end + else addString(new StringBuilder(), start, sep, end).result() + + /** Displays all elements of this $coll in a string using a separator string. + * + * Delegates to addString, which can be overridden. + * + * @param sep the separator string. + * @return a string representation of this $coll. In the resulting string + * the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * @example `List(1, 2, 3).mkString("|") = "1|2|3"` + */ + @inline final def mkString(sep: String): String = mkString("", sep, "") + + /** Displays all elements of this $coll in a string. + * + * Delegates to addString, which can be overridden. + * + * @return a string representation of this $coll. In the resulting string + * the string representations (w.r.t. the method `toString`) + * of all elements of this $coll follow each other without any + * separator string. + */ + @inline final def mkString: String = mkString("") + + /** Appends all elements of this $coll to a string builder using start, end, and separator strings. + * The written text begins with the string `start` and ends with the string `end`. + * Inside, the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> a.addString(b , "List(" , ", " , ")") + * res5: StringBuilder = List(1, 2, 3, 4) + * }}} + * + * @param b the string builder to which elements are appended. + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return the string builder `b` to which elements were appended. + */ + def addString(b: StringBuilder, start: String, sep: String, end: String): b.type = { + val jsb = b.underlying + if (start.length != 0) jsb.append(start) + val it = iterator + if (it.hasNext) { + jsb.append(it.next()) + while (it.hasNext) { + jsb.append(sep) + jsb.append(it.next()) + } + } + if (end.length != 0) jsb.append(end) + b + } + + /** Appends all elements of this $coll to a string builder using a separator string. + * The written text consists of the string representations (w.r.t. the method `toString`) + * of all elements of this $coll, separated by the string `sep`. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> a.addString(b, ", ") + * res0: StringBuilder = 1, 2, 3, 4 + * }}} + * + * @param b the string builder to which elements are appended. + * @param sep the separator string. + * @return the string builder `b` to which elements were appended. + */ + @inline final def addString(b: StringBuilder, sep: String): b.type = addString(b, "", sep, "") + + /** Appends all elements of this $coll to a string builder. + * The written text consists of the string representations (w.r.t. the method + * `toString`) of all elements of this $coll without any separator string. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> val h = a.addString(b) + * h: StringBuilder = 1234 + * }}} + * + * @param b the string builder to which elements are appended. + * @return the string builder `b` to which elements were appended. + */ + @inline final def addString(b: StringBuilder): b.type = addString(b, "") + + /** Given a collection factory `factory`, converts this $coll to the appropriate + * representation for the current element type `A`. Example uses: + * + * {{{ + * xs.to(List) + * xs.to(ArrayBuffer) + * xs.to(BitSet) // for xs: Iterable[Int] + * }}} + */ + def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(this) + + @deprecated("Use .iterator instead of .toIterator", "2.13.0") + @`inline` final def toIterator: Iterator[A] = iterator + + /** Converts this $coll to a `List`. + * + * @return This $coll as a `List[A]`. + */ + def toList: immutable.List[A] = immutable.List.from(this) + + /** Converts this $coll to a `Vector`. + * + * @return This $coll as a `Vector[A]`. + */ + def toVector: immutable.Vector[A] = immutable.Vector.from(this) + + /** Converts this $coll to a `Map`, given an implicit coercion from the $coll's type to a key-value tuple. + * + * @tparam K The key type for the resulting map. + * @tparam V The value type for the resulting map. + * @param ev An implicit coercion from `A` to `[K, V]`. + * @return This $coll as a `Map[K, V]`. + */ + def toMap[K, V](implicit ev: A <:< (K, V)): immutable.Map[K, V] = + immutable.Map.from(this.asInstanceOf[IterableOnce[(K, V)]]) + + /** Converts this $coll to a `Set`. + * + * @tparam B The type of elements of the result, a supertype of `A`. + * @return This $coll as a `Set[B]`. + */ + def toSet[B >: A]: immutable.Set[B] = immutable.Set.from(this) + + /** @return This $coll as a `Seq[A]`. This is equivalent to `to(Seq)` but might be faster. + */ + def toSeq: immutable.Seq[A] = immutable.Seq.from(this) + + /** Converts this $coll to an `IndexedSeq`. + * + * @return This $coll as an `IndexedSeq[A]`. + */ + def toIndexedSeq: immutable.IndexedSeq[A] = immutable.IndexedSeq.from(this) + + @deprecated("Use .to(LazyList) instead of .toStream", "2.13.0") + @inline final def toStream: immutable.Stream[A] = to(immutable.Stream) + + /** Converts this $coll to a `Buffer`. + * + * @tparam B The type of elements of the result, a supertype of `A`. + * @return This $coll as a `Buffer[B]`. + */ + @inline final def toBuffer[B >: A]: mutable.Buffer[B] = mutable.Buffer.from(this) + + /** Converts this $coll to an `Array`. + * + * Implementation note: DO NOT call [[Array.from]] from this method. + * + * @tparam B The type of elements of the result, a supertype of `A`. + * @return This $coll as an `Array[B]`. + */ + def toArray[B >: A: ClassTag]: Array[B] = + if (knownSize >= 0) { + val destination = new Array[B](knownSize) + @annotation.unused val copied = copyToArray(destination, 0) + //assert(copied == destination.length) + destination + } + else mutable.ArrayBuilder.make[B].addAll(this).result() + + // For internal use + protected def reversed: Iterable[A] = { + var xs: immutable.List[A] = immutable.Nil + val it = iterator + while (it.hasNext) xs = it.next() :: xs + xs + } +} diff --git a/library/src/scala/collection/Iterator.scala b/library/src/scala/collection/Iterator.scala new file mode 100644 index 000000000000..7b402241af73 --- /dev/null +++ b/library/src/scala/collection/Iterator.scala @@ -0,0 +1,1306 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.collection.mutable.{ArrayBuffer, ArrayBuilder, Builder, ImmutableBuilder} +import scala.annotation.tailrec +import scala.annotation.unchecked.uncheckedVariance +import scala.runtime.Statics + +/** Iterators are data structures that allow to iterate over a sequence + * of elements. They have a `hasNext` method for checking + * if there is a next element available, and a `next` method + * which returns the next element and advances the iterator. + * + * An iterator is mutable: most operations on it change its state. While it is often used + * to iterate through the elements of a collection, it can also be used without + * being backed by any collection (see constructors on the companion object). + * + * It is of particular importance to note that, unless stated otherwise, ''one should never + * use an iterator after calling a method on it''. The two most important exceptions + * are also the sole abstract methods: `next` and `hasNext`. + * + * Both these methods can be called any number of times without having to discard the + * iterator. Note that even `hasNext` may cause mutation -- such as when iterating + * from an input stream, where it will block until the stream is closed or some + * input becomes available. + * + * Consider this example for safe and unsafe use: + * + * {{{ + * def f[A](it: Iterator[A]) = { + * if (it.hasNext) { // Safe to reuse "it" after "hasNext" + * it.next() // Safe to reuse "it" after "next" + * val remainder = it.drop(2) // it is *not* safe to use "it" again after this line! + * remainder.take(2) // it is *not* safe to use "remainder" after this line! + * } else it + * } + * }}} + * + * @define mayNotTerminateInf + * Note: may not terminate for infinite iterators. + * @define preservesIterator + * The iterator remains valid for further use whatever result is returned. + * @define consumesIterator + * After calling this method, one should discard the iterator it was called + * on. Using it is undefined and subject to change. + * @define consumesAndProducesIterator + * After calling this method, one should discard the iterator it was called + * on, and use only the iterator that was returned. Using the old iterator + * is undefined, subject to change, and may result in changes to the new + * iterator as well. + * @define consumesTwoAndProducesOneIterator + * After calling this method, one should discard the iterator it was called + * on, as well as the one passed as a parameter, and use only the iterator + * that was returned. Using the old iterators is undefined, subject to change, + * and may result in changes to the new iterator as well. + * @define consumesOneAndProducesTwoIterators + * After calling this method, one should discard the iterator it was called + * on, and use only the iterators that were returned. Using the old iterator + * is undefined, subject to change, and may result in changes to the new + * iterators as well. + * @define coll iterator + */ +trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Iterator[A]] { self => + + /** Check if there is a next element available. + * + * @return `true` if there is a next element, `false` otherwise + * @note Reuse: $preservesIterator + */ + def hasNext: Boolean + + @deprecated("hasDefiniteSize on Iterator is the same as isEmpty", "2.13.0") + @`inline` override final def hasDefiniteSize = isEmpty + + /** Return the next element and advance the iterator. + * + * @throws NoSuchElementException if there is no next element. + * @return the next element. + * @note Reuse: Advances the iterator, which may exhaust the elements. It is valid to + * make additional calls on the iterator. + */ + @throws[NoSuchElementException] + def next(): A + + @inline final def iterator = this + + /** Wraps the value of `next()` in an option. + * + * @return `Some(next)` if a next element exists, `None` otherwise. + */ + def nextOption(): Option[A] = if (hasNext) Some(next()) else None + + /** Tests whether this iterator contains a given value as an element. + * $mayNotTerminateInf + * + * @param elem the element to test. + * @return `true` if this iterator produces some value that is + * is equal (as determined by `==`) to `elem`, `false` otherwise. + * @note Reuse: $consumesIterator + */ + def contains(elem: Any): Boolean = exists(_ == elem) // Note--this seems faster than manual inlining! + + /** Creates a buffered iterator from this iterator. + * + * @see [[scala.collection.BufferedIterator]] + * @return a buffered iterator producing the same values as this iterator. + * @note Reuse: $consumesAndProducesIterator + */ + def buffered: BufferedIterator[A] = new AbstractIterator[A] with BufferedIterator[A] { + private[this] var hd: A = _ + private[this] var hdDefined: Boolean = false + + def head: A = { + if (!hdDefined) { + hd = next() + hdDefined = true + } + hd + } + + override def knownSize = { + val thisSize = self.knownSize + if (thisSize >= 0 && hdDefined) thisSize + 1 + else thisSize + } + + def hasNext = + hdDefined || self.hasNext + + def next() = + if (hdDefined) { + hdDefined = false + hd + } else self.next() + } + + /** A flexible iterator for transforming an `Iterator[A]` into an + * `Iterator[Seq[A]]`, with configurable sequence size, step, and + * strategy for dealing with remainder elements which don't fit evenly + * into the last group. + * + * A `GroupedIterator` is yielded by `grouped` and by `sliding`, + * where the `step` may differ from the group `size`. + */ + class GroupedIterator[B >: A](self: Iterator[B], size: Int, step: Int) extends AbstractIterator[immutable.Seq[B]] { + + require(size >= 1 && step >= 1, f"size=$size%d and step=$step%d, but both must be positive") + + private[this] var buffer: Array[B] = null // current result + private[this] var prev: Array[B] = null // if sliding, overlap from previous result + private[this] var first = true // if !first, advancing may skip ahead + private[this] var filled = false // whether the buffer is "hot" + private[this] var partial = true // whether to emit partial sequence + private[this] var padding: () => B = null // what to pad short sequences with + private[this] def pad = padding != null // irrespective of partial flag + private[this] def newBuilder = { + val b = ArrayBuilder.make[Any] + val k = self.knownSize + if (k > 0) b.sizeHint(k min size) // if k < size && !partial, buffer will grow on padding + b + } + + /** Specifies a fill element used to pad a partial segment + * so that all segments have the same size. + * + * Any previous setting of `withPartial` is ignored, + * as the last group will always be padded to `size` elements. + * + * The by-name argument is evaluated for each fill element. + * + * @param x The element that will be appended to the last segment, if necessary. + * @return The same iterator, and ''not'' a new iterator. + * @note This method mutates the iterator it is called on, which can be safely used afterwards. + * @note This method is mutually exclusive with `withPartial`. + * @group Configuration + */ + def withPadding(x: => B): this.type = { + padding = () => x + partial = true // redundant, as padding always results in complete segment + this + } + /** Specify whether to drop the last segment if it has less than `size` elements. + * + * If this flag is `false`, elements of a partial segment at the end of the iterator + * are not returned. + * + * The flag defaults to `true`. + * + * Any previous setting of `withPadding` is ignored, + * as the last group will never be padded. + * A partial segment is either retained or dropped, per the flag. + * + * @param x `true` if partial segments may be returned, `false` otherwise. + * @return The same iterator, and ''not'' a new iterator. + * @note This method mutates the iterator it is called on, which can be safely used afterwards. + * @note This method is mutually exclusive with `withPadding`. + * @group Configuration + */ + def withPartial(x: Boolean): this.type = { + partial = x + padding = null + this + } + + /** Eagerly fetch `size` elements to buffer. + * + * If buffer is dirty and stepping, copy prefix. + * If skipping, skip ahead. + * Fetch remaining elements. + * If unable to deliver size, then pad if padding enabled, otherwise drop segment. + * Returns true if successful in delivering `count` elements, + * or padded segment, or partial segment. + */ + private def fulfill(): Boolean = { + val builder = newBuilder + var done = false + // keep prefix of previous buffer if stepping + if (prev != null) builder.addAll(prev) + // skip ahead + if (!first && step > size) { + var dropping = step - size + while (dropping > 0 && self.hasNext) { + self.next(): Unit + dropping -= 1 + } + done = dropping > 0 // skip failed + } + var index = builder.length + if (!done) { + // advance to rest of segment if possible + while (index < size && self.hasNext) { + builder.addOne(self.next()) + index += 1 + } + // if unable to complete segment, pad if possible + if (index < size && pad) { + builder.sizeHint(size) + while (index < size) { + builder.addOne(padding()) + index += 1 + } + } + } + // segment must have data, and must be complete unless they allow partial + val ok = index > 0 && (partial || index == size) + if (ok) buffer = builder.result().asInstanceOf[Array[B]] + else prev = null + ok + } + + // fill() returns false if no more sequences can be produced + private def fill(): Boolean = filled || { filled = self.hasNext && fulfill() ; filled } + + def hasNext = fill() + + @throws[NoSuchElementException] + def next(): immutable.Seq[B] = + if (!fill()) Iterator.empty.next() + else { + filled = false + // if stepping, retain overlap in prev + if (step < size) { + if (first) prev = buffer.drop(step) + else if (buffer.length == size) Array.copy(src = buffer, srcPos = step, dest = prev, destPos = 0, length = size - step) + else prev = null + } + val res = immutable.ArraySeq.unsafeWrapArray(buffer).asInstanceOf[immutable.ArraySeq[B]] + buffer = null + first = false + res + } + } + + /** A copy of this $coll with an element value appended until a given target length is reached. + * + * @param len the target length + * @param elem the padding value + * @tparam B the element type of the returned $coll. + * @return a new $coll consisting of + * all elements of this $coll followed by the minimal number of occurrences of `elem` so + * that the resulting collection has a length of at least `len`. + */ + def padTo[B >: A](len: Int, elem: B): Iterator[B] = new AbstractIterator[B] { + private[this] var i = 0 + + override def knownSize: Int = { + val thisSize = self.knownSize + if (thisSize < 0) -1 + else thisSize max (len - i) + } + + def next(): B = { + val b = + if (self.hasNext) self.next() + else if (i < len) elem + else Iterator.empty.next() + i += 1 + b + } + + def hasNext: Boolean = self.hasNext || i < len + } + + /** Partitions this iterator in two iterators according to a predicate. + * + * @param p the predicate on which to partition + * @return a pair of iterators: the iterator that satisfies the predicate + * `p` and the iterator that does not. + * The relative order of the elements in the resulting iterators + * is the same as in the original iterator. + * @note Reuse: $consumesOneAndProducesTwoIterators + */ + def partition(p: A => Boolean): (Iterator[A], Iterator[A]) = { + val (a, b) = duplicate + (a filter p, b filterNot p) + } + + /** Returns an iterator which groups this iterator into fixed size + * blocks. Example usages: + * {{{ + * // Returns List(List(1, 2, 3), List(4, 5, 6), List(7))) + * (1 to 7).iterator.grouped(3).toList + * // Returns List(List(1, 2, 3), List(4, 5, 6)) + * (1 to 7).iterator.grouped(3).withPartial(false).toList + * // Returns List(List(1, 2, 3), List(4, 5, 6), List(7, 20, 25) + * // Illustrating that withPadding's argument is by-name. + * val it2 = Iterator.iterate(20)(_ + 5) + * (1 to 7).iterator.grouped(3).withPadding(it2.next).toList + * }}} + * + * @note Reuse: $consumesAndProducesIterator + */ + def grouped[B >: A](size: Int): GroupedIterator[B] = + new GroupedIterator[B](self, size, size) + + /** Returns an iterator which presents a "sliding window" view of + * this iterator. The first argument is the window size, and + * the second argument `step` is how far to advance the window + * on each iteration. The `step` defaults to `1`. + * + * The returned `GroupedIterator` can be configured to either + * pad a partial result to size `size` or suppress the partial + * result entirely. + * + * Example usages: + * {{{ + * // Returns List(ArraySeq(1, 2, 3), ArraySeq(2, 3, 4), ArraySeq(3, 4, 5)) + * (1 to 5).iterator.sliding(3).toList + * // Returns List(ArraySeq(1, 2, 3, 4), ArraySeq(4, 5)) + * (1 to 5).iterator.sliding(4, 3).toList + * // Returns List(ArraySeq(1, 2, 3, 4)) + * (1 to 5).iterator.sliding(4, 3).withPartial(false).toList + * // Returns List(ArraySeq(1, 2, 3, 4), ArraySeq(4, 5, 20, 25)) + * // Illustrating that withPadding's argument is by-name. + * val it2 = Iterator.iterate(20)(_ + 5) + * (1 to 5).iterator.sliding(4, 3).withPadding(it2.next).toList + * }}} + * + * @param size the number of elements per group + * @param step the distance between the first elements of successive + * groups + * @return A `GroupedIterator` producing `Seq[B]`s of size `size`, except the + * last element (which may be the only element) will be truncated + * if there are fewer than `size` elements remaining to be grouped. + * This behavior can be configured. + * + * @note Reuse: $consumesAndProducesIterator + */ + def sliding[B >: A](size: Int, step: Int = 1): GroupedIterator[B] = + new GroupedIterator[B](self, size, step) + + def scanLeft[B](z: B)(op: (B, A) => B): Iterator[B] = new AbstractIterator[B] { + // We use an intermediate iterator that iterates through the first element `z` + // and then that will be modified to iterate through the collection + private[this] var current: Iterator[B] = + new AbstractIterator[B] { + override def knownSize = { + val thisSize = self.knownSize + + if (thisSize < 0) -1 + else thisSize + 1 + } + def hasNext: Boolean = true + def next(): B = { + // Here we change our self-reference to a new iterator that iterates through `self` + current = new AbstractIterator[B] { + private[this] var acc = z + def next(): B = { + acc = op(acc, self.next()) + acc + } + def hasNext: Boolean = self.hasNext + override def knownSize = self.knownSize + } + z + } + } + override def knownSize = current.knownSize + def next(): B = current.next() + def hasNext: Boolean = current.hasNext + } + + @deprecated("Call scanRight on an Iterable instead.", "2.13.0") + def scanRight[B](z: B)(op: (A, B) => B): Iterator[B] = ArrayBuffer.from(this).scanRight(z)(op).iterator + + def indexWhere(p: A => Boolean, from: Int = 0): Int = { + var i = math.max(from, 0) + val dropped = drop(from) + while (dropped.hasNext) { + if (p(dropped.next())) return i + i += 1 + } + -1 + } + + /** Returns the index of the first occurrence of the specified + * object in this iterable object. + * $mayNotTerminateInf + * + * @param elem element to search for. + * @return the index of the first occurrence of `elem` in the values produced by this iterator, + * or -1 if such an element does not exist until the end of the iterator is reached. + * @note Reuse: $consumesIterator + */ + def indexOf[B >: A](elem: B): Int = indexOf(elem, 0) + + /** Returns the index of the first occurrence of the specified object in this iterable object + * after or at some start index. + * $mayNotTerminateInf + * + * @param elem element to search for. + * @param from the start index + * @return the index `>= from` of the first occurrence of `elem` in the values produced by this + * iterator, or -1 if such an element does not exist until the end of the iterator is + * reached. + * @note Reuse: $consumesIterator + */ + def indexOf[B >: A](elem: B, from: Int): Int = { + var i = 0 + while (i < from && hasNext) { + next() + i += 1 + } + + while (hasNext) { + if (next() == elem) return i + i += 1 + } + -1 + } + + @inline final def length: Int = size + + @deprecatedOverriding("isEmpty is defined as !hasNext; override hasNext instead", "2.13.0") + override def isEmpty: Boolean = !hasNext + + def filter(p: A => Boolean): Iterator[A] = filterImpl(p, isFlipped = false) + + def filterNot(p: A => Boolean): Iterator[A] = filterImpl(p, isFlipped = true) + + private[collection] def filterImpl(p: A => Boolean, isFlipped: Boolean): Iterator[A] = new AbstractIterator[A] { + private[this] var hd: A = _ + private[this] var hdDefined: Boolean = false + + def hasNext: Boolean = hdDefined || { + if (!self.hasNext) return false + hd = self.next() + while (p(hd) == isFlipped) { + if (!self.hasNext) return false + hd = self.next() + } + hdDefined = true + true + } + + def next() = + if (hasNext) { + hdDefined = false + hd + } + else Iterator.empty.next() + } + + /** Creates an iterator over all the elements of this iterator that + * satisfy the predicate `p`. The order of the elements + * is preserved. + * + * '''Note:''' `withFilter` is the same as `filter` on iterators. It exists so that + * for-expressions with filters work over iterators. + * + * @param p the predicate used to test values. + * @return an iterator which produces those values of this iterator which satisfy the predicate `p`. + * @note Reuse: $consumesAndProducesIterator + */ + def withFilter(p: A => Boolean): Iterator[A] = filter(p) + + def collect[B](pf: PartialFunction[A, B]): Iterator[B] = new AbstractIterator[B] with (A => B) { + // Manually buffer to avoid extra layer of wrapping with buffered + private[this] var hd: B = _ + + // Little state machine to keep track of where we are + // Seek = 0; Found = 1; Empty = -1 + // Not in vals because scalac won't make them static (@inline def only works with -optimize) + // BE REALLY CAREFUL TO KEEP COMMENTS AND NUMBERS IN SYNC! + private[this] var status = 0/*Seek*/ + + def apply(value: A): B = Statics.pfMarker.asInstanceOf[B] + + def hasNext = { + val marker = Statics.pfMarker + while (status == 0/*Seek*/) { + if (self.hasNext) { + val x = self.next() + val v = pf.applyOrElse(x, this) + if (marker ne v.asInstanceOf[AnyRef]) { + hd = v + status = 1/*Found*/ + } + } + else status = -1/*Empty*/ + } + status == 1/*Found*/ + } + def next() = if (hasNext) { status = 0/*Seek*/; hd } else Iterator.empty.next() + } + + /** + * Builds a new iterator from this one without any duplicated elements on it. + * @return iterator with distinct elements + * + * @note Reuse: $consumesIterator + */ + def distinct: Iterator[A] = distinctBy(identity) + + /** + * Builds a new iterator from this one without any duplicated elements as determined by `==` after applying + * the transforming function `f`. + * + * @param f The transforming function whose result is used to determine the uniqueness of each element + * @tparam B the type of the elements after being transformed by `f` + * @return iterator with distinct elements + * + * @note Reuse: $consumesIterator + */ + def distinctBy[B](f: A => B): Iterator[A] = new AbstractIterator[A] { + + private[this] val traversedValues = mutable.HashSet.empty[B] + private[this] var nextElementDefined: Boolean = false + private[this] var nextElement: A = _ + + def hasNext: Boolean = nextElementDefined || (self.hasNext && { + val a = self.next() + if (traversedValues.add(f(a))) { + nextElement = a + nextElementDefined = true + true + } + else hasNext + }) + + def next(): A = + if (hasNext) { + nextElementDefined = false + nextElement + } else { + Iterator.empty.next() + } + } + + def map[B](f: A => B): Iterator[B] = new AbstractIterator[B] { + override def knownSize = self.knownSize + def hasNext = self.hasNext + def next() = f(self.next()) + } + + def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = new AbstractIterator[B] { + private[this] var cur: Iterator[B] = Iterator.empty + /** Trillium logic boolean: -1 = unknown, 0 = false, 1 = true */ + private[this] var _hasNext: Int = -1 + + def nextCur(): Unit = { + cur = Iterator.empty + cur = f(self.next()).iterator + _hasNext = -1 + } + + def hasNext: Boolean = { + if (_hasNext == -1) { + while (!cur.hasNext) { + if (!self.hasNext) { + _hasNext = 0 + // since we know we are exhausted, we can release cur for gc, and as well replace with + // static Iterator.empty which will support efficient subsequent `hasNext`/`next` calls + cur = Iterator.empty + return false + } + nextCur() + } + _hasNext = 1 + true + } else _hasNext == 1 + } + def next(): B = { + if (hasNext) { + _hasNext = -1 + } + cur.next() + } + } + + def flatten[B](implicit ev: A => IterableOnce[B]): Iterator[B] = + flatMap[B](ev) + + def concat[B >: A](xs: => IterableOnce[B]): Iterator[B] = new Iterator.ConcatIterator[B](self).concat(xs) + + @`inline` final def ++ [B >: A](xs: => IterableOnce[B]): Iterator[B] = concat(xs) + + def take(n: Int): Iterator[A] = sliceIterator(0, n max 0) + + def takeWhile(p: A => Boolean): Iterator[A] = new AbstractIterator[A] { + private[this] var hd: A = _ + private[this] var hdDefined: Boolean = false + private[this] var tail: Iterator[A] = self + + def hasNext = hdDefined || tail.hasNext && { + hd = tail.next() + if (p(hd)) hdDefined = true + else tail = Iterator.empty + hdDefined + } + def next() = if (hasNext) { hdDefined = false; hd } else Iterator.empty.next() + } + + def drop(n: Int): Iterator[A] = sliceIterator(n, -1) + + def dropWhile(p: A => Boolean): Iterator[A] = new AbstractIterator[A] { + // Magic value: -1 = hasn't dropped, 0 = found first, 1 = defer to parent iterator + private[this] var status = -1 + // Local buffering to avoid double-wrap with .buffered + private[this] var fst: A = _ + def hasNext: Boolean = + if (status == 1) self.hasNext + else if (status == 0) true + else { + while (self.hasNext) { + val a = self.next() + if (!p(a)) { + fst = a + status = 0 + return true + } + } + status = 1 + false + } + def next() = + if (hasNext) { + if (status == 1) self.next() + else { + status = 1 + fst + } + } + else Iterator.empty.next() + } + + /** + * @inheritdoc + * + * @note Reuse: $consumesOneAndProducesTwoIterators + */ + def span(p: A => Boolean): (Iterator[A], Iterator[A]) = { + /* + * Giving a name to following iterator (as opposed to trailing) because + * anonymous class is represented as a structural type that trailing + * iterator is referring (the finish() method) and thus triggering + * handling of structural calls. It's not what's intended here. + */ + final class Leading extends AbstractIterator[A] { + private[this] var lookahead: mutable.Queue[A] = null + private[this] var hd: A = _ + /* Status is kept with magic numbers + * 1 means next element is in hd and we're still reading into this iterator + * 0 means we're still reading but haven't found a next element + * -1 means we are done reading into the iterator, so we must rely on lookahead + * -2 means we are done but have saved hd for the other iterator to use as its first element + */ + private[this] var status = 0 + private def store(a: A): Unit = { + if (lookahead == null) lookahead = new mutable.Queue[A] + lookahead += a + } + def hasNext = { + if (status < 0) (lookahead ne null) && lookahead.nonEmpty + else if (status > 0) true + else { + if (self.hasNext) { + hd = self.next() + status = if (p(hd)) 1 else -2 + } + else status = -1 + status > 0 + } + } + def next() = { + if (hasNext) { + if (status == 1) { status = 0; hd } + else lookahead.dequeue() + } + else Iterator.empty.next() + } + @tailrec + def finish(): Boolean = status match { + case -2 => status = -1 ; true + case -1 => false + case 1 => store(hd) ; status = 0 ; finish() + case 0 => + status = -1 + while (self.hasNext) { + val a = self.next() + if (p(a)) store(a) + else { + hd = a + return true + } + } + false + } + def trailer: A = hd + } + + val leading = new Leading + + val trailing = new AbstractIterator[A] { + private[this] var myLeading = leading + /* Status flag meanings: + * -1 not yet accessed + * 0 single element waiting in leading + * 1 defer to self + * 2 self.hasNext already + * 3 exhausted + */ + private[this] var status = -1 + def hasNext = status match { + case 3 => false + case 2 => true + case 1 => if (self.hasNext) { status = 2 ; true } else { status = 3 ; false } + case 0 => true + case _ => + if (myLeading.finish()) { status = 0 ; true } else { status = 1 ; myLeading = null ; hasNext } + } + def next() = { + if (hasNext) { + if (status == 0) { + status = 1 + val res = myLeading.trailer + myLeading = null + res + } else { + status = 1 + self.next() + } + } + else Iterator.empty.next() + } + } + + (leading, trailing) + } + + def slice(from: Int, until: Int): Iterator[A] = sliceIterator(from, until max 0) + + /** Creates an optionally bounded slice, unbounded if `until` is negative. */ + protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + val lo = from max 0 + val rest = + if (until < 0) -1 // unbounded + else if (until <= lo) 0 // empty + else until - lo // finite + + if (rest == 0) Iterator.empty + else new Iterator.SliceIterator(this, lo, rest) + } + + def zip[B](that: IterableOnce[B]): Iterator[(A, B)] = new AbstractIterator[(A, B)] { + val thatIterator = that.iterator + override def knownSize = self.knownSize min thatIterator.knownSize + def hasNext = self.hasNext && thatIterator.hasNext + def next() = (self.next(), thatIterator.next()) + } + + def zipAll[A1 >: A, B](that: IterableOnce[B], thisElem: A1, thatElem: B): Iterator[(A1, B)] = new AbstractIterator[(A1, B)] { + val thatIterator = that.iterator + override def knownSize = { + val thisSize = self.knownSize + val thatSize = thatIterator.knownSize + if (thisSize < 0 || thatSize < 0) -1 + else thisSize max thatSize + } + def hasNext = self.hasNext || thatIterator.hasNext + def next(): (A1, B) = { + val next1 = self.hasNext + val next2 = thatIterator.hasNext + if(!(next1 || next2)) throw new NoSuchElementException + (if(next1) self.next() else thisElem, if(next2) thatIterator.next() else thatElem) + } + } + + def zipWithIndex: Iterator[(A, Int)] = new AbstractIterator[(A, Int)] { + var idx = 0 + override def knownSize = self.knownSize + def hasNext = self.hasNext + def next() = { + val ret = (self.next(), idx) + idx += 1 + ret + } + } + + /** Checks whether corresponding elements of the given iterable collection + * compare equal (with respect to `==`) to elements of this $coll. + * + * @param that the collection to compare + * @tparam B the type of the elements of collection `that`. + * @return `true` if both collections contain equal elements in the same order, `false` otherwise. + * + * @inheritdoc + */ + def sameElements[B >: A](that: IterableOnce[B]): Boolean = { + val those = that.iterator + while (hasNext && those.hasNext) + if (next() != those.next()) + return false + // At that point we know that *at least one* iterator has no next element + // If *both* of them have no elements then the collections are the same + hasNext == those.hasNext + } + + /** Creates two new iterators that both iterate over the same elements + * as this iterator (in the same order). The duplicate iterators are + * considered equal if they are positioned at the same element. + * + * Given that most methods on iterators will make the original iterator + * unfit for further use, this methods provides a reliable way of calling + * multiple such methods on an iterator. + * + * @return a pair of iterators + * @note The implementation may allocate temporary storage for elements + * iterated by one iterator but not yet by the other. + * @note Reuse: $consumesOneAndProducesTwoIterators + */ + def duplicate: (Iterator[A], Iterator[A]) = { + val gap = new scala.collection.mutable.Queue[A] + var ahead: Iterator[A] = null + class Partner extends AbstractIterator[A] { + override def knownSize: Int = self.synchronized { + val thisSize = self.knownSize + + if (this eq ahead) thisSize + else if (thisSize < 0 || gap.knownSize < 0) -1 + else thisSize + gap.knownSize + } + def hasNext: Boolean = self.synchronized { + (this ne ahead) && !gap.isEmpty || self.hasNext + } + def next(): A = self.synchronized { + if (gap.isEmpty) ahead = this + if (this eq ahead) { + val e = self.next() + gap enqueue e + e + } else gap.dequeue() + } + // to verify partnerhood we use reference equality on gap because + // type testing does not discriminate based on origin. + private def compareGap(queue: scala.collection.mutable.Queue[A]) = gap eq queue + override def hashCode = gap.hashCode() + override def equals(other: Any) = other match { + case x: Partner => x.compareGap(gap) && gap.isEmpty + case _ => super.equals(other) + } + } + (new Partner, new Partner) + } + + /** Returns this iterator with patched values. + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original iterator appends the patch to the end. + * If more values are replaced than actually exist, the excess is ignored. + * + * @param from The start index from which to patch + * @param patchElems The iterator of patch values + * @param replaced The number of values in the original iterator that are replaced by the patch. + * @note Reuse: $consumesTwoAndProducesOneIterator + */ + def patch[B >: A](from: Int, patchElems: Iterator[B], replaced: Int): Iterator[B] = + new AbstractIterator[B] { + private[this] var origElems = self + // > 0 => that many more elems from `origElems` before switching to `patchElems` + // 0 => need to drop elems from `origElems` and start using `patchElems` + // -1 => have dropped elems from `origElems`, will be using `patchElems` until it's empty + // and then using what's left of `origElems` after the drop + private[this] var state = if (from > 0) from else 0 + + // checks state and handles 0 => -1 + @inline private[this] def switchToPatchIfNeeded(): Unit = + if (state == 0) { + origElems = origElems drop replaced + state = -1 + } + + def hasNext: Boolean = { + switchToPatchIfNeeded() + origElems.hasNext || patchElems.hasNext + } + + def next(): B = { + switchToPatchIfNeeded() + if (state < 0 /* == -1 */) { + if (patchElems.hasNext) patchElems.next() + else origElems.next() + } + else { + if (origElems.hasNext) { + state -= 1 + origElems.next() + } + else { + state = -1 + patchElems.next() + } + } + } + } + + override def tapEach[U](f: A => U): Iterator[A] = new AbstractIterator[A] { + override def knownSize = self.knownSize + override def hasNext = self.hasNext + override def next() = { + val _next = self.next() + f(_next) + _next + } + } + + /** Converts this iterator to a string. + * + * @return `""` + * @note Reuse: $preservesIterator + */ + override def toString = "" + + @deprecated("Iterator.seq always returns the iterator itself", "2.13.0") + def seq: this.type = this +} + +@SerialVersionUID(3L) +object Iterator extends IterableFactory[Iterator] { + + private[this] val _empty: Iterator[Nothing] = new AbstractIterator[Nothing] { + def hasNext = false + def next() = throw new NoSuchElementException("next on empty iterator") + override def knownSize: Int = 0 + override protected def sliceIterator(from: Int, until: Int): AbstractIterator[Nothing] = this + } + + /** Creates a target $coll from an existing source collection + * + * @param source Source collection + * @tparam A the type of the collection’s elements + * @return a new $coll with the elements of `source` + */ + override def from[A](source: IterableOnce[A]): Iterator[A] = source.iterator + + /** The iterator which produces no values. */ + @`inline` final def empty[T]: Iterator[T] = _empty + + def single[A](a: A): Iterator[A] = new AbstractIterator[A] { + private[this] var consumed: Boolean = false + def hasNext = !consumed + def next() = if (consumed) empty.next() else { consumed = true; a } + override protected def sliceIterator(from: Int, until: Int) = + if (consumed || from > 0 || until == 0) empty + else this + } + + override def apply[A](xs: A*): Iterator[A] = xs.iterator + + /** + * @return A builder for $Coll objects. + * @tparam A the type of the ${coll}’s elements + */ + def newBuilder[A]: Builder[A, Iterator[A]] = + new ImmutableBuilder[A, Iterator[A]](empty[A]) { + override def addOne(elem: A): this.type = { elems = elems ++ single(elem); this } + } + + /** Creates iterator that produces the results of some element computation a number of times. + * + * @param len the number of elements returned by the iterator. + * @param elem the element computation + * @return An iterator that produces the results of `n` evaluations of `elem`. + */ + override def fill[A](len: Int)(elem: => A): Iterator[A] = new AbstractIterator[A] { + private[this] var i = 0 + override def knownSize: Int = (len - i) max 0 + def hasNext: Boolean = i < len + def next(): A = + if (hasNext) { i += 1; elem } + else empty.next() + } + + /** Creates an iterator producing the values of a given function over a range of integer values starting from 0. + * + * @param end The number of elements returned by the iterator + * @param f The function computing element values + * @return An iterator that produces the values `f(0), ..., f(n -1)`. + */ + override def tabulate[A](end: Int)(f: Int => A): Iterator[A] = new AbstractIterator[A] { + private[this] var i = 0 + override def knownSize: Int = (end - i) max 0 + def hasNext: Boolean = i < end + def next(): A = + if (hasNext) { val result = f(i); i += 1; result } + else empty.next() + } + + /** Creates an infinite-length iterator which returns successive values from some start value. + + * @param start the start value of the iterator + * @return the iterator producing the infinite sequence of values `start, start + 1, start + 2, ...` + */ + def from(start: Int): Iterator[Int] = from(start, 1) + + /** Creates an infinite-length iterator returning values equally spaced apart. + * + * @param start the start value of the iterator + * @param step the increment between successive values + * @return the iterator producing the infinite sequence of values `start, start + 1 * step, start + 2 * step, ...` + */ + def from(start: Int, step: Int): Iterator[Int] = new AbstractIterator[Int] { + private[this] var i = start + def hasNext: Boolean = true + def next(): Int = { val result = i; i += step; result } + } + + /** Creates nn iterator returning successive values in some integer interval. + * + * @param start the start value of the iterator + * @param end the end value of the iterator (the first value NOT returned) + * @return the iterator producing values `start, start + 1, ..., end - 1` + */ + def range(start: Int, end: Int): Iterator[Int] = range(start, end, 1) + + /** An iterator producing equally spaced values in some integer interval. + * + * @param start the start value of the iterator + * @param end the end value of the iterator (the first value NOT returned) + * @param step the increment value of the iterator (must be positive or negative) + * @return the iterator producing values `start, start + step, ...` up to, but excluding `end` + */ + def range(start: Int, end: Int, step: Int): Iterator[Int] = new AbstractIterator[Int] { + if (step == 0) throw new IllegalArgumentException("zero step") + private[this] var i = start + private[this] var hasOverflowed = false + override def knownSize: Int = { + val size = math.ceil((end.toLong - i.toLong) / step.toDouble) + if (size < 0) 0 + else if (size > Int.MaxValue) -1 + else size.toInt + } + def hasNext: Boolean = { + (step <= 0 || i < end) && (step >= 0 || i > end) && !hasOverflowed + } + def next(): Int = + if (hasNext) { + val result = i + val nextValue = i + step + hasOverflowed = (step > 0) == nextValue < i + i = nextValue + result + } + else empty.next() + } + + /** Creates an infinite iterator that repeatedly applies a given function to the previous result. + * + * @param start the start value of the iterator + * @param f the function that's repeatedly applied + * @return the iterator producing the infinite sequence of values `start, f(start), f(f(start)), ...` + */ + def iterate[T](start: T)(f: T => T): Iterator[T] = new AbstractIterator[T] { + private[this] var first = true + private[this] var acc = start + def hasNext: Boolean = true + def next(): T = { + if (first) first = false + else acc = f(acc) + + acc + } + } + + /** Creates an Iterator that uses a function `f` to produce elements of type `A` + * and update an internal state of type `S`. + * + * @param init State initial value + * @param f Computes the next element (or returns `None` to signal + * the end of the collection) + * @tparam A Type of the elements + * @tparam S Type of the internal state + * @return an Iterator that produces elements using `f` until `f` returns `None` + */ + override def unfold[A, S](init: S)(f: S => Option[(A, S)]): Iterator[A] = new UnfoldIterator(init)(f) + + /** Creates an infinite-length iterator returning the results of evaluating an expression. + * The expression is recomputed for every element. + * + * @param elem the element computation. + * @return the iterator containing an infinite number of results of evaluating `elem`. + */ + def continually[A](elem: => A): Iterator[A] = new AbstractIterator[A] { + def hasNext = true + def next() = elem + } + + /** Creates an iterator to which other iterators can be appended efficiently. + * Nested ConcatIterators are merged to avoid blowing the stack. + */ + private final class ConcatIterator[+A](private var current: Iterator[A @uncheckedVariance]) extends AbstractIterator[A] { + private var tail: ConcatIteratorCell[A @uncheckedVariance] = null + private var last: ConcatIteratorCell[A @uncheckedVariance] = null + private var currentHasNextChecked = false + + def hasNext = + if (currentHasNextChecked) true + else if (current == null) false + else if (current.hasNext) { + currentHasNextChecked = true + true + } + else { + // If we advanced the current iterator to a ConcatIterator, merge it into this one + @tailrec def merge(): Unit = + if (current.isInstanceOf[ConcatIterator[_]]) { + val c = current.asInstanceOf[ConcatIterator[A]] + current = c.current + currentHasNextChecked = c.currentHasNextChecked + if (c.tail != null) { + if (last == null) last = c.last + c.last.tail = tail + tail = c.tail + } + merge() + } + + // Advance current to the next non-empty iterator + // current is set to null when all iterators are exhausted + @tailrec def advance(): Boolean = + if (tail == null) { + current = null + last = null + false + } + else { + current = tail.headIterator + if (last eq tail) last = last.tail + tail = tail.tail + merge() + if (currentHasNextChecked) true + else if (current != null && current.hasNext) { + currentHasNextChecked = true + true + } else advance() + } + + advance() + } + + def next() = + if (hasNext) { + currentHasNextChecked = false + current.next() + } else Iterator.empty.next() + + override def concat[B >: A](that: => IterableOnce[B]): Iterator[B] = { + val c = new ConcatIteratorCell[B](that, null).asInstanceOf[ConcatIteratorCell[A]] + if (tail == null) { + tail = c + last = c + } + else { + last.tail = c + last = c + } + if (current == null) current = Iterator.empty + this + } + } + + private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A], var tail: ConcatIteratorCell[A]) { + def headIterator: Iterator[A] = head.iterator + } + + /** Creates a delegating iterator capped by a limit count. Negative limit means unbounded. + * Lazily skip to start on first evaluation. Avoids daisy-chained iterators due to slicing. + */ + private[scala] final class SliceIterator[A](val underlying: Iterator[A], start: Int, limit: Int) extends AbstractIterator[A] { + private[this] var remaining = limit + private[this] var dropping = start + @inline private def unbounded = remaining < 0 + private def skip(): Unit = + while (dropping > 0) { + if (underlying.hasNext) { + underlying.next() + dropping -= 1 + } else + dropping = 0 + } + override def knownSize: Int = { + val size = underlying.knownSize + if (size < 0) -1 + else { + val dropSize = 0 max (size - dropping) + if (unbounded) dropSize + else remaining min dropSize + } + } + def hasNext = { skip(); remaining != 0 && underlying.hasNext } + def next() = { + skip() + if (remaining > 0) { + remaining -= 1 + underlying.next() + } + else if (unbounded) underlying.next() + else empty.next() + } + override protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + val lo = from max 0 + def adjustedBound = + if (unbounded) -1 + else 0 max (remaining - lo) + val rest = + if (until < 0) adjustedBound // respect current bound, if any + else if (until <= lo) 0 // empty + else if (unbounded) until - lo // now finite + else adjustedBound min (until - lo) // keep lesser bound + val sum = dropping + lo + if (rest == 0) empty + else if (sum < 0) { + dropping = Int.MaxValue + remaining = 0 + this.concat(new SliceIterator(underlying, start = sum - Int.MaxValue, limit = rest)) + } + else { + dropping = sum + remaining = rest + this + } + } + } + + /** Creates an iterator that uses a function `f` to produce elements of + * type `A` and update an internal state of type `S`. + */ + private final class UnfoldIterator[A, S](init: S)(f: S => Option[(A, S)]) extends AbstractIterator[A] { + private[this] var state: S = init + private[this] var nextResult: Option[(A, S)] = null + + override def hasNext: Boolean = { + if (nextResult eq null) { + nextResult = { + val res = f(state) + if (res eq null) throw new NullPointerException("null during unfold") + res + } + state = null.asInstanceOf[S] // allow GC + } + nextResult.isDefined + } + + override def next(): A = { + if (hasNext) { + val (value, newState) = nextResult.get + state = newState + nextResult = null + value + } else Iterator.empty.next() + } + } +} + +/** Explicit instantiation of the `Iterator` trait to reduce class file size in subclasses. */ +abstract class AbstractIterator[+A] extends Iterator[A] diff --git a/library/src/scala/collection/JavaConverters.scala b/library/src/scala/collection/JavaConverters.scala new file mode 100644 index 000000000000..7a803a685d3e --- /dev/null +++ b/library/src/scala/collection/JavaConverters.scala @@ -0,0 +1,335 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import java.util.{concurrent => juc} +import java.{lang => jl, util => ju} + +import scala.collection.convert._ +import scala.language.implicitConversions + +/** A variety of decorators that enable converting between + * Scala and Java collections using extension methods, `asScala` and `asJava`. + * + * The extension methods return adapters for the corresponding API. + * + * The following conversions are supported via `asScala` and `asJava`: + *{{{ + * scala.collection.Iterable <=> java.lang.Iterable + * scala.collection.Iterator <=> java.util.Iterator + * scala.collection.mutable.Buffer <=> java.util.List + * scala.collection.mutable.Set <=> java.util.Set + * scala.collection.mutable.Map <=> java.util.Map + * scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap + *}}} + * The following conversions are supported via `asScala` and through + * specially-named extension methods to convert to Java collections, as shown: + *{{{ + * scala.collection.Iterable <=> java.util.Collection (via asJavaCollection) + * scala.collection.Iterator <=> java.util.Enumeration (via asJavaEnumeration) + * scala.collection.mutable.Map <=> java.util.Dictionary (via asJavaDictionary) + *}}} + * In addition, the following one-way conversions are provided via `asJava`: + *{{{ + * scala.collection.Seq => java.util.List + * scala.collection.mutable.Seq => java.util.List + * scala.collection.Set => java.util.Set + * scala.collection.Map => java.util.Map + *}}} + * The following one way conversion is provided via `asScala`: + *{{{ + * java.util.Properties => scala.collection.mutable.Map + *}}} + * In all cases, converting from a source type to a target type and back + * again will return the original source object. For example: + * {{{ + * import scala.collection.JavaConverters._ + * + * val source = new scala.collection.mutable.ListBuffer[Int] + * val target: java.util.List[Int] = source.asJava + * val other: scala.collection.mutable.Buffer[Int] = target.asScala + * assert(source eq other) + * }}} + * Alternatively, the conversion methods have descriptive names and can be invoked explicitly. + * {{{ + * scala> val vs = java.util.Arrays.asList("hi", "bye") + * vs: java.util.List[String] = [hi, bye] + * + * scala> val ss = asScalaIterator(vs.iterator) + * ss: Iterator[String] = + * + * scala> .toList + * res0: List[String] = List(hi, bye) + * + * scala> val ss = asScalaBuffer(vs) + * ss: scala.collection.mutable.Buffer[String] = Buffer(hi, bye) + * }}} + */ +@deprecated("Use `scala.jdk.CollectionConverters` instead", "2.13.0") +object JavaConverters extends AsJavaConverters with AsScalaConverters { + @deprecated("Use `asJava` instead", "2.13.0") + def asJavaIterator[A](i: Iterator[A]): ju.Iterator[A] = asJava(i) + + @deprecated("Use `asJava` instead", "2.13.0") + def asJavaIterable[A](i: Iterable[A]): jl.Iterable[A] = asJava(i) + + @deprecated("Use `asJava` instead", "2.13.0") + def bufferAsJavaList[A](b: mutable.Buffer[A]): ju.List[A] = asJava(b) + + @deprecated("Use `asJava` instead", "2.13.0") + def mutableSeqAsJavaList[A](s: mutable.Seq[A]): ju.List[A] = asJava(s) + + @deprecated("Use `asJava` instead", "2.13.0") + def seqAsJavaList[A](s: Seq[A]): ju.List[A] = asJava(s) + + @deprecated("Use `asJava` instead", "2.13.0") + def mutableSetAsJavaSet[A](s: mutable.Set[A]): ju.Set[A] = asJava(s) + + @deprecated("Use `asJava` instead", "2.13.0") + def setAsJavaSet[A](s: Set[A]): ju.Set[A] = asJava(s) + + @deprecated("Use `asJava` instead", "2.13.0") + def mutableMapAsJavaMap[K, V](m: mutable.Map[K, V]): ju.Map[K, V] = asJava(m) + + @deprecated("Use `asJava` instead", "2.13.0") + def mapAsJavaMap[K, V](m: Map[K, V]): ju.Map[K, V] = asJava(m) + + @deprecated("Use `asJava` instead", "2.13.0") + def mapAsJavaConcurrentMap[K, V](m: concurrent.Map[K, V]): juc.ConcurrentMap[K, V] = asJava(m) + + + @deprecated("Use `asScala` instead", "2.13.0") + def asScalaIterator[A](i: ju.Iterator[A]): Iterator[A] = asScala(i) + + @deprecated("Use `asScala` instead", "2.13.0") + def enumerationAsScalaIterator[A](i: ju.Enumeration[A]): Iterator[A] = asScala(i) + + @deprecated("Use `asScala` instead", "2.13.0") + def iterableAsScalaIterable[A](i: jl.Iterable[A]): Iterable[A] = asScala(i) + + @deprecated("Use `asScala` instead", "2.13.0") + def collectionAsScalaIterable[A](i: ju.Collection[A]): Iterable[A] = asScala(i) + + @deprecated("Use `asScala` instead", "2.13.0") + def asScalaBuffer[A](l: ju.List[A]): mutable.Buffer[A] = asScala(l) + + @deprecated("Use `asScala` instead", "2.13.0") + def asScalaSet[A](s: ju.Set[A]): mutable.Set[A] = asScala(s) + + @deprecated("Use `asScala` instead", "2.13.0") + def mapAsScalaMap[A, B](m: ju.Map[A, B]): mutable.Map[A, B] = asScala(m) + + @deprecated("Use `asScala` instead", "2.13.0") + def mapAsScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = asScala(m) + + @deprecated("Use `asScala` instead", "2.13.0") + def dictionaryAsScalaMap[A, B](p: ju.Dictionary[A, B]): mutable.Map[A, B] = asScala(p) + + @deprecated("Use `asScala` instead", "2.13.0") + def propertiesAsScalaMap(p: ju.Properties): mutable.Map[String, String] = asScala(p) + + // Deprecated implicit conversions for code that directly imports them + + /** + * Adds an `asJava` method that implicitly converts a Scala `Iterator` to a Java `Iterator`. + * @see [[asJavaIterator]] + */ + implicit def asJavaIteratorConverter[A](i : Iterator[A]): AsJava[ju.Iterator[A]] = + new AsJava(asJavaIterator(i)) + + /** + * Adds an `asJavaEnumeration` method that implicitly converts a Scala `Iterator` to a Java `Enumeration`. + * @see [[asJavaEnumeration]] + */ + implicit def asJavaEnumerationConverter[A](i : Iterator[A]): AsJavaEnumeration[A] = + new AsJavaEnumeration(i) + + /** + * Adds an `asJava` method that implicitly converts a Scala `Iterable` to a Java `Iterable`. + * @see [[asJavaIterable]] + */ + implicit def asJavaIterableConverter[A](i : Iterable[A]): AsJava[jl.Iterable[A]] = + new AsJava(asJavaIterable(i)) + + /** + * Adds an `asJavaCollection` method that implicitly converts a Scala `Iterable` to an immutable Java `Collection`. + * @see [[asJavaCollection]] + */ + implicit def asJavaCollectionConverter[A](i : Iterable[A]): AsJavaCollection[A] = + new AsJavaCollection(i) + + /** + * Adds an `asJava` method that implicitly converts a Scala mutable `Buffer` to a Java `List`. + * @see [[bufferAsJavaList]] + */ + implicit def bufferAsJavaListConverter[A](b : mutable.Buffer[A]): AsJava[ju.List[A]] = + new AsJava(bufferAsJavaList(b)) + + /** + * Adds an `asJava` method that implicitly converts a Scala mutable `Seq` to a Java `List`. + * @see [[mutableSeqAsJavaList]] + */ + implicit def mutableSeqAsJavaListConverter[A](b : mutable.Seq[A]): AsJava[ju.List[A]] = + new AsJava(mutableSeqAsJavaList(b)) + + /** + * Adds an `asJava` method that implicitly converts a Scala `Seq` to a Java `List`. + * @see [[seqAsJavaList]] + */ + implicit def seqAsJavaListConverter[A](b : Seq[A]): AsJava[ju.List[A]] = + new AsJava(seqAsJavaList(b)) + + /** + * Adds an `asJava` method that implicitly converts a Scala mutable `Set` to a Java `Set`. + * @see [[mutableSetAsJavaSet]] + */ + implicit def mutableSetAsJavaSetConverter[A](s : mutable.Set[A]): AsJava[ju.Set[A]] = + new AsJava(mutableSetAsJavaSet(s)) + + /** + * Adds an `asJava` method that implicitly converts a Scala `Set` to a Java `Set`. + * @see [[setAsJavaSet]] + */ + implicit def setAsJavaSetConverter[A](s : Set[A]): AsJava[ju.Set[A]] = + new AsJava(setAsJavaSet(s)) + + /** + * Adds an `asJava` method that implicitly converts a Scala mutable `Map` to a Java `Map`. + * @see [[mutableMapAsJavaMap]] + */ + implicit def mutableMapAsJavaMapConverter[K, V](m : mutable.Map[K, V]): AsJava[ju.Map[K, V]] = + new AsJava(mutableMapAsJavaMap(m)) + + /** + * Adds an `asJavaDictionary` method that implicitly converts a Scala mutable `Map` to a Java `Dictionary`. + * @see [[asJavaDictionary]] + */ + implicit def asJavaDictionaryConverter[K, V](m : mutable.Map[K, V]): AsJavaDictionary[K, V] = + new AsJavaDictionary(m) + + /** + * Adds an `asJava` method that implicitly converts a Scala `Map` to a Java `Map`. + * @see [[mapAsJavaMap]] + */ + implicit def mapAsJavaMapConverter[K, V](m : Map[K, V]): AsJava[ju.Map[K, V]] = + new AsJava(mapAsJavaMap(m)) + + /** + * Adds an `asJava` method that implicitly converts a Scala mutable `concurrent.Map` to a Java `ConcurrentMap`. + * @see [[mapAsJavaConcurrentMap]]. + */ + implicit def mapAsJavaConcurrentMapConverter[K, V](m: concurrent.Map[K, V]): AsJava[juc.ConcurrentMap[K, V]] = + new AsJava(mapAsJavaConcurrentMap(m)) + + + /** + * Adds an `asScala` method that implicitly converts a Java `Iterator` to a Scala `Iterator`. + * @see [[asScalaIterator]] + */ + implicit def asScalaIteratorConverter[A](i : ju.Iterator[A]): AsScala[Iterator[A]] = + new AsScala(asScalaIterator(i)) + + /** + * Adds an `asScala` method that implicitly converts a Java `Enumeration` to a Scala `Iterator`. + * @see [[enumerationAsScalaIterator]] + */ + implicit def enumerationAsScalaIteratorConverter[A](i : ju.Enumeration[A]): AsScala[Iterator[A]] = + new AsScala(enumerationAsScalaIterator(i)) + + /** + * Adds an `asScala` method that implicitly converts a Java `Iterable` to a Scala `Iterable`. + * @see [[iterableAsScalaIterable]] + */ + implicit def iterableAsScalaIterableConverter[A](i : jl.Iterable[A]): AsScala[Iterable[A]] = + new AsScala(iterableAsScalaIterable(i)) + + /** + * Adds an `asScala` method that implicitly converts a Java `Collection` to an Scala `Iterable`. + * @see [[collectionAsScalaIterable]] + */ + implicit def collectionAsScalaIterableConverter[A](i : ju.Collection[A]): AsScala[Iterable[A]] = + new AsScala(collectionAsScalaIterable(i)) + + /** + * Adds an `asScala` method that implicitly converts a Java `List` to a Scala mutable `Buffer`. + * @see [[asScalaBuffer]] + */ + implicit def asScalaBufferConverter[A](l : ju.List[A]): AsScala[mutable.Buffer[A]] = + new AsScala(asScalaBuffer(l)) + + /** + * Adds an `asScala` method that implicitly converts a Java `Set` to a Scala mutable `Set`. + * @see [[asScalaSet]] + */ + implicit def asScalaSetConverter[A](s : ju.Set[A]): AsScala[mutable.Set[A]] = + new AsScala(asScalaSet(s)) + + /** + * Adds an `asScala` method that implicitly converts a Java `Map` to a Scala mutable `Map`. + * @see [[mapAsScalaMap]] + */ + implicit def mapAsScalaMapConverter[K, V](m : ju.Map[K, V]): AsScala[mutable.Map[K, V]] = + new AsScala(mapAsScalaMap(m)) + + /** + * Adds an `asScala` method that implicitly converts a Java `ConcurrentMap` to a Scala mutable `concurrent.Map`. + * @see [[mapAsScalaConcurrentMap]] + */ + implicit def mapAsScalaConcurrentMapConverter[K, V](m: juc.ConcurrentMap[K, V]): AsScala[concurrent.Map[K, V]] = + new AsScala(mapAsScalaConcurrentMap(m)) + + /** + * Adds an `asScala` method that implicitly converts a Java `Dictionary` to a Scala mutable `Map`. + * @see [[dictionaryAsScalaMap]] + */ + implicit def dictionaryAsScalaMapConverter[K, V](p: ju.Dictionary[K, V]): AsScala[mutable.Map[K, V]] = + new AsScala(dictionaryAsScalaMap(p)) + + /** + * Adds an `asScala` method that implicitly converts a Java `Properties` to a Scala mutable `Map[String, String]`. + * @see [[propertiesAsScalaMap]] + */ + implicit def propertiesAsScalaMapConverter(p: ju.Properties): AsScala[mutable.Map[String, String]] = + new AsScala(propertiesAsScalaMap(p)) + + + /** Generic class containing the `asJava` converter method */ + class AsJava[A](op: => A) { + /** Converts a Scala collection to the corresponding Java collection */ + def asJava: A = op + } + + /** Generic class containing the `asScala` converter method */ + class AsScala[A](op: => A) { + /** Converts a Java collection to the corresponding Scala collection */ + def asScala: A = op + } + + /** Generic class containing the `asJavaCollection` converter method */ + class AsJavaCollection[A](i: Iterable[A]) { + /** Converts a Scala `Iterable` to a Java `Collection` */ + def asJavaCollection: ju.Collection[A] = JavaConverters.asJavaCollection(i) + } + + /** Generic class containing the `asJavaEnumeration` converter method */ + class AsJavaEnumeration[A](i: Iterator[A]) { + /** Converts a Scala `Iterator` to a Java `Enumeration` */ + def asJavaEnumeration: ju.Enumeration[A] = JavaConverters.asJavaEnumeration(i) + } + + /** Generic class containing the `asJavaDictionary` converter method */ + class AsJavaDictionary[K, V](m : mutable.Map[K, V]) { + /** Converts a Scala `Map` to a Java `Dictionary` */ + def asJavaDictionary: ju.Dictionary[K, V] = JavaConverters.asJavaDictionary(m) + } +} diff --git a/library/src/scala/collection/LazyZipOps.scala b/library/src/scala/collection/LazyZipOps.scala new file mode 100644 index 000000000000..a7a72ce882a8 --- /dev/null +++ b/library/src/scala/collection/LazyZipOps.scala @@ -0,0 +1,422 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.language.implicitConversions + +/** Decorator representing lazily zipped pairs. + * + * @define coll pair + * @define willNotTerminateInf + * + * Note: will not terminate for infinite-sized collections. + */ +final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterable[El1], coll2: Iterable[El2]) { + + /** Zips `that` iterable collection with an existing `LazyZip2`. The elements in each collection are + * not consumed until a strict operation is invoked on the returned `LazyZip3` decorator. + * + * @param that the iterable providing the third element of each eventual triple + * @tparam B the type of the third element in each eventual triple + * @return a decorator `LazyZip3` that allows strict operations to be performed on the lazily evaluated tuples or + * chained calls to `lazyZip`. Implicit conversion to `Iterable[(El1, El2, B)]` is also supported. + */ + def lazyZip[B](that: Iterable[B]): LazyZip3[El1, El2, B, C1] = new LazyZip3(src, coll1, coll2, that) + + def map[B, C](f: (El1, El2) => B)(implicit bf: BuildFrom[C1, B, C]): C = { + bf.fromSpecific(src)(new AbstractView[B] { + def iterator: AbstractIterator[B] = new AbstractIterator[B] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + def hasNext = elems1.hasNext && elems2.hasNext + def next() = f(elems1.next(), elems2.next()) + } + override def knownSize: Int = zipKnownSize + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty + }) + } + + def flatMap[B, C](f: (El1, El2) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = { + bf.fromSpecific(src)(new AbstractView[B] { + def iterator: AbstractIterator[B] = new AbstractIterator[B] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] var _current: Iterator[B] = Iterator.empty + private def current = { + while (!_current.hasNext && elems1.hasNext && elems2.hasNext) + _current = f(elems1.next(), elems2.next()).iterator + _current + } + def hasNext = current.hasNext + def next() = current.next() + } + override def knownSize: Int = if (coll1.knownSize == 0 || coll2.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty + }) + } + + def filter[C](p: (El1, El2) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2), C]): C = { + bf.fromSpecific(src)(new AbstractView[(El1, El2)] { + def iterator: AbstractIterator[(El1, El2)] = new AbstractIterator[(El1, El2)] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] var _current: (El1, El2) = _ + private def current = { + while ((_current eq null) && elems1.hasNext && elems2.hasNext) { + val e1 = elems1.next() + val e2 = elems2.next() + if (p(e1, e2)) _current = (e1, e2) + } + _current + } + def hasNext = current ne null + def next() = { + val c = current + if (c ne null) { + _current = null + c + } else Iterator.empty.next() + } + } + override def knownSize: Int = if (coll1.knownSize == 0 || coll2.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.hasNext + }) + } + + def exists(p: (El1, El2) => Boolean): Boolean = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + var res = false + + while (!res && elems1.hasNext && elems2.hasNext) res = p(elems1.next(), elems2.next()) + + res + } + + def forall(p: (El1, El2) => Boolean): Boolean = !exists((el1, el2) => !p(el1, el2)) + + def foreach[U](f: (El1, El2) => U): Unit = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + + while (elems1.hasNext && elems2.hasNext) f(elems1.next(), elems2.next()) + } + + private def toIterable: View[(El1, El2)] = new AbstractView[(El1, El2)] { + def iterator: AbstractIterator[(El1, El2)] = new AbstractIterator[(El1, El2)] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + def hasNext = elems1.hasNext && elems2.hasNext + def next() = (elems1.next(), elems2.next()) + } + override def knownSize: Int = zipKnownSize + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty + } + + private def zipKnownSize: Int = { + val s1 = coll1.knownSize + if (s1 == 0) 0 else { + val s2 = coll2.knownSize + if (s2 == 0) 0 else s1 min s2 + } + } + + override def toString = s"$coll1.lazyZip($coll2)" +} + +object LazyZip2 { + implicit def lazyZip2ToIterable[El1, El2](zipped2: LazyZip2[El1, El2, _]): View[(El1, El2)] = zipped2.toIterable +} + + +/** Decorator representing lazily zipped triples. + * + * @define coll triple + * @define willNotTerminateInf + * + * Note: will not terminate for infinite-sized collections. + */ +final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, + coll1: Iterable[El1], + coll2: Iterable[El2], + coll3: Iterable[El3]) { + + /** Zips `that` iterable collection with an existing `LazyZip3`. The elements in each collection are + * not consumed until a strict operation is invoked on the returned `LazyZip4` decorator. + * + * @param that the iterable providing the fourth element of each eventual 4-tuple + * @tparam B the type of the fourth element in each eventual 4-tuple + * @return a decorator `LazyZip4` that allows strict operations to be performed on the lazily evaluated tuples. + * Implicit conversion to `Iterable[(El1, El2, El3, B)]` is also supported. + */ + def lazyZip[B](that: Iterable[B]): LazyZip4[El1, El2, El3, B, C1] = new LazyZip4(src, coll1, coll2, coll3, that) + + def map[B, C](f: (El1, El2, El3) => B)(implicit bf: BuildFrom[C1, B, C]): C = { + bf.fromSpecific(src)(new AbstractView[B] { + def iterator: AbstractIterator[B] = new AbstractIterator[B] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + def hasNext = elems1.hasNext && elems2.hasNext && elems3.hasNext + def next() = f(elems1.next(), elems2.next(), elems3.next()) + } + override def knownSize: Int = zipKnownSize + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty || coll3.isEmpty + }) + } + + def flatMap[B, C](f: (El1, El2, El3) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = { + bf.fromSpecific(src)(new AbstractView[B] { + def iterator: AbstractIterator[B] = new AbstractIterator[B] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + private[this] var _current: Iterator[B] = Iterator.empty + private def current = { + while (!_current.hasNext && elems1.hasNext && elems2.hasNext && elems3.hasNext) + _current = f(elems1.next(), elems2.next(), elems3.next()).iterator + _current + } + def hasNext = current.hasNext + def next() = current.next() + } + override def knownSize: Int = if (coll1.knownSize == 0 || coll2.knownSize == 0 || coll3.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + }) + } + + def filter[C](p: (El1, El2, El3) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3), C]): C = { + bf.fromSpecific(src)(new AbstractView[(El1, El2, El3)] { + def iterator: AbstractIterator[(El1, El2, El3)] = new AbstractIterator[(El1, El2, El3)] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + private[this] var _current: (El1, El2, El3) = _ + private def current = { + while ((_current eq null) && elems1.hasNext && elems2.hasNext && elems3.hasNext) { + val e1 = elems1.next() + val e2 = elems2.next() + val e3 = elems3.next() + if (p(e1, e2, e3)) _current = (e1, e2, e3) + } + _current + } + def hasNext = current ne null + def next() = { + val c = current + if (c ne null) { + _current = null + c + } else Iterator.empty.next() + } + } + override def knownSize: Int = if (coll1.knownSize == 0 || coll2.knownSize == 0 || coll3.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + }) + } + + def exists(p: (El1, El2, El3) => Boolean): Boolean = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + var res = false + + while (!res && elems1.hasNext && elems2.hasNext && elems3.hasNext) + res = p(elems1.next(), elems2.next(), elems3.next()) + + res + } + + def forall(p: (El1, El2, El3) => Boolean): Boolean = !exists((el1, el2, el3) => !p(el1, el2, el3)) + + def foreach[U](f: (El1, El2, El3) => U): Unit = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + + while (elems1.hasNext && elems2.hasNext && elems3.hasNext) + f(elems1.next(), elems2.next(), elems3.next()) + } + + private def toIterable: View[(El1, El2, El3)] = new AbstractView[(El1, El2, El3)] { + def iterator: AbstractIterator[(El1, El2, El3)] = new AbstractIterator[(El1, El2, El3)] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + def hasNext = elems1.hasNext && elems2.hasNext && elems3.hasNext + def next() = (elems1.next(), elems2.next(), elems3.next()) + } + override def knownSize: Int = zipKnownSize + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty || coll3.isEmpty + } + + private def zipKnownSize: Int = { + val s1 = coll1.knownSize + if (s1 == 0) 0 else { + val s2 = coll2.knownSize + if (s2 == 0) 0 else { + val s3 = coll3.knownSize + if (s3 == 0) 0 else s1 min s2 min s3 + } + } + } + + override def toString = s"$coll1.lazyZip($coll2).lazyZip($coll3)" +} + +object LazyZip3 { + implicit def lazyZip3ToIterable[El1, El2, El3](zipped3: LazyZip3[El1, El2, El3, _]): View[(El1, El2, El3)] = zipped3.toIterable +} + + + +/** Decorator representing lazily zipped 4-tuples. + * + * @define coll tuple + * @define willNotTerminateInf + * + * Note: will not terminate for infinite-sized collections. + */ +final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1, + coll1: Iterable[El1], + coll2: Iterable[El2], + coll3: Iterable[El3], + coll4: Iterable[El4]) { + + def map[B, C](f: (El1, El2, El3, El4) => B)(implicit bf: BuildFrom[C1, B, C]): C = { + bf.fromSpecific(src)(new AbstractView[B] { + def iterator: AbstractIterator[B] = new AbstractIterator[B] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + private[this] val elems4 = coll4.iterator + def hasNext = elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext + def next() = f(elems1.next(), elems2.next(), elems3.next(), elems4.next()) + } + override def knownSize: Int = zipKnownSize + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty || coll3.isEmpty || coll4.isEmpty + }) + } + + def flatMap[B, C](f: (El1, El2, El3, El4) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = { + bf.fromSpecific(src)(new AbstractView[B] { + def iterator: AbstractIterator[B] = new AbstractIterator[B] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + private[this] val elems4 = coll4.iterator + private[this] var _current: Iterator[B] = Iterator.empty + private def current = { + while (!_current.hasNext && elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext) + _current = f(elems1.next(), elems2.next(), elems3.next(), elems4.next()).iterator + _current + } + def hasNext = current.hasNext + def next() = current.next() + } + override def knownSize: Int = if (coll1.knownSize == 0 || coll2.knownSize == 0 || coll3.knownSize == 0 || coll4.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + }) + } + + def filter[C](p: (El1, El2, El3, El4) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3, El4), C]): C = { + bf.fromSpecific(src)(new AbstractView[(El1, El2, El3, El4)] { + def iterator: AbstractIterator[(El1, El2, El3, El4)] = new AbstractIterator[(El1, El2, El3, El4)] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + private[this] val elems4 = coll4.iterator + private[this] var _current: (El1, El2, El3, El4) = _ + private def current = { + while ((_current eq null) && elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext) { + val e1 = elems1.next() + val e2 = elems2.next() + val e3 = elems3.next() + val e4 = elems4.next() + if (p(e1, e2, e3, e4)) _current = (e1, e2, e3, e4) + } + _current + } + def hasNext = current ne null + def next() = { + val c = current + if (c ne null) { + _current = null + c + } else Iterator.empty.next() + } + } + override def knownSize: Int = if (coll1.knownSize == 0 || coll2.knownSize == 0 || coll3.knownSize == 0 || coll4.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + }) + } + + def exists(p: (El1, El2, El3, El4) => Boolean): Boolean = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + val elems4 = coll4.iterator + var res = false + + while (!res && elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext) + res = p(elems1.next(), elems2.next(), elems3.next(), elems4.next()) + + res + } + + def forall(p: (El1, El2, El3, El4) => Boolean): Boolean = !exists((el1, el2, el3, el4) => !p(el1, el2, el3, el4)) + + def foreach[U](f: (El1, El2, El3, El4) => U): Unit = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + val elems4 = coll4.iterator + + while (elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext) + f(elems1.next(), elems2.next(), elems3.next(), elems4.next()) + } + + private def toIterable: View[(El1, El2, El3, El4)] = new AbstractView[(El1, El2, El3, El4)] { + def iterator: AbstractIterator[(El1, El2, El3, El4)] = new AbstractIterator[(El1, El2, El3, El4)] { + private[this] val elems1 = coll1.iterator + private[this] val elems2 = coll2.iterator + private[this] val elems3 = coll3.iterator + private[this] val elems4 = coll4.iterator + def hasNext = elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext + def next() = (elems1.next(), elems2.next(), elems3.next(), elems4.next()) + } + override def knownSize: Int = zipKnownSize + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty || coll3.isEmpty || coll4.isEmpty + } + + private def zipKnownSize: Int = { + val s1 = coll1.knownSize + if (s1 == 0) 0 else { + val s2 = coll2.knownSize + if (s2 == 0) 0 else { + val s3 = coll3.knownSize + if (s3 == 0) 0 else { + val s4 = coll4.knownSize + if (s4 == 0) 0 else s1 min s2 min s3 min s4 + } + } + } + } + + override def toString = s"$coll1.lazyZip($coll2).lazyZip($coll3).lazyZip($coll4)" +} + +object LazyZip4 { + implicit def lazyZip4ToIterable[El1, El2, El3, El4](zipped4: LazyZip4[El1, El2, El3, El4, _]): View[(El1, El2, El3, El4)] = + zipped4.toIterable +} diff --git a/library/src/scala/collection/LinearSeq.scala b/library/src/scala/collection/LinearSeq.scala new file mode 100644 index 000000000000..965edecdadc7 --- /dev/null +++ b/library/src/scala/collection/LinearSeq.scala @@ -0,0 +1,310 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.{nowarn, tailrec} + +/** Base trait for linearly accessed sequences that have efficient `head` and + * `tail` operations. + * Known subclasses: List, LazyList + */ +trait LinearSeq[+A] extends Seq[A] + with LinearSeqOps[A, LinearSeq, LinearSeq[A]] + with IterableFactoryDefaults[A, LinearSeq] { + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "LinearSeq" + + override def iterableFactory: SeqFactory[LinearSeq] = LinearSeq +} + +@SerialVersionUID(3L) +object LinearSeq extends SeqFactory.Delegate[LinearSeq](immutable.LinearSeq) + +/** Base trait for linear Seq operations */ +trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with LinearSeqOps[A, CC, C]] extends Any with SeqOps[A, CC, C] { + + /** @inheritdoc + * + * Note: *Must* be overridden in subclasses. The default implementation that is inherited from [[SeqOps]] + * uses `lengthCompare`, which is defined here to use `isEmpty`. + */ + override def isEmpty: Boolean + + /** @inheritdoc + * + * Note: *Must* be overridden in subclasses. The default implementation is inherited from [[IterableOps]]. + */ + def head: A + + /** @inheritdoc + * + * Note: *Must* be overridden in subclasses. The default implementation is inherited from [[IterableOps]]. + */ + def tail: C + + override def headOption: Option[A] = + if (isEmpty) None else Some(head) + + def iterator: Iterator[A] = + if (knownSize == 0) Iterator.empty + else new LinearSeqIterator[A](this) + + def length: Int = { + var these = coll + var len = 0 + while (these.nonEmpty) { + len += 1 + these = these.tail + } + len + } + + override def last: A = { + if (isEmpty) throw new NoSuchElementException("LinearSeq.last") + else { + var these = coll + var scout = tail + while (scout.nonEmpty) { + these = scout + scout = scout.tail + } + these.head + } + } + + override def lengthCompare(len: Int): Int = { + @tailrec def loop(i: Int, xs: LinearSeq[A]): Int = { + if (i == len) + if (xs.isEmpty) 0 else 1 + else if (xs.isEmpty) + -1 + else + loop(i + 1, xs.tail) + } + if (len < 0) 1 + else loop(0, coll) + } + + override def lengthCompare(that: Iterable[_]): Int = { + val thatKnownSize = that.knownSize + + if (thatKnownSize >= 0) this lengthCompare thatKnownSize + else that match { + case that: LinearSeq[_] => + var thisSeq = this + var thatSeq = that + while (thisSeq.nonEmpty && thatSeq.nonEmpty) { + thisSeq = thisSeq.tail + thatSeq = thatSeq.tail + } + java.lang.Boolean.compare(thisSeq.nonEmpty, thatSeq.nonEmpty) + case _ => + var thisSeq = this + val thatIt = that.iterator + while (thisSeq.nonEmpty && thatIt.hasNext) { + thisSeq = thisSeq.tail + thatIt.next() + } + java.lang.Boolean.compare(thisSeq.nonEmpty, thatIt.hasNext) + } + } + + override def isDefinedAt(x: Int): Boolean = x >= 0 && lengthCompare(x) > 0 + + // `apply` is defined in terms of `drop`, which is in turn defined in + // terms of `tail`. + @throws[IndexOutOfBoundsException] + override def apply(n: Int): A = { + if (n < 0) throw new IndexOutOfBoundsException(n.toString) + val skipped = drop(n) + if (skipped.isEmpty) throw new IndexOutOfBoundsException(n.toString) + skipped.head + } + + override def foreach[U](f: A => U): Unit = { + var these: LinearSeq[A] = coll + while (!these.isEmpty) { + f(these.head) + these = these.tail + } + } + + override def forall(p: A => Boolean): Boolean = { + var these: LinearSeq[A] = coll + while (!these.isEmpty) { + if (!p(these.head)) return false + these = these.tail + } + true + } + + override def exists(p: A => Boolean): Boolean = { + var these: LinearSeq[A] = coll + while (!these.isEmpty) { + if (p(these.head)) return true + these = these.tail + } + false + } + + override def contains[A1 >: A](elem: A1): Boolean = { + var these: LinearSeq[A] = coll + while (!these.isEmpty) { + if (these.head == elem) return true + these = these.tail + } + false + } + + override def find(p: A => Boolean): Option[A] = { + var these: LinearSeq[A] = coll + while (!these.isEmpty) { + if (p(these.head)) return Some(these.head) + these = these.tail + } + None + } + + override def foldLeft[B](z: B)(op: (B, A) => B): B = { + var acc = z + var these: LinearSeq[A] = coll + while (!these.isEmpty) { + acc = op(acc, these.head) + these = these.tail + } + acc + } + + override def sameElements[B >: A](that: IterableOnce[B]): Boolean = { + @tailrec def linearSeqEq(a: LinearSeq[B], b: LinearSeq[B]): Boolean = + (a eq b) || { + if (a.nonEmpty && b.nonEmpty && a.head == b.head) { + linearSeqEq(a.tail, b.tail) + } + else { + a.isEmpty && b.isEmpty + } + } + + that match { + case that: LinearSeq[B] => linearSeqEq(coll, that) + case _ => super.sameElements(that) + } + } + + override def segmentLength(p: A => Boolean, from: Int): Int = { + var i = 0 + var seq = drop(from) + while (seq.nonEmpty && p(seq.head)) { + i += 1 + seq = seq.tail + } + i + } + + override def indexWhere(p: A => Boolean, from: Int): Int = { + var i = math.max(from, 0) + var these: LinearSeq[A] = this drop from + while (these.nonEmpty) { + if (p(these.head)) + return i + + i += 1 + these = these.tail + } + -1 + } + + override def lastIndexWhere(p: A => Boolean, end: Int): Int = { + var i = 0 + var these: LinearSeq[A] = coll + var last = -1 + while (!these.isEmpty && i <= end) { + if (p(these.head)) last = i + these = these.tail + i += 1 + } + last + } + + override def findLast(p: A => Boolean): Option[A] = { + var these: LinearSeq[A] = coll + var found = false + var last: A = null.asInstanceOf[A] // don't use `Option`, to prevent excessive `Some` allocation + while (these.nonEmpty) { + val elem = these.head + if (p(elem)) { + found = true + last = elem + } + these = these.tail + } + if (found) Some(last) else None + } + + override def tails: Iterator[C] = { + val end = Iterator.single(empty) + Iterator.iterate(coll)(_.tail).takeWhile(_.nonEmpty) ++ end + } +} + +trait StrictOptimizedLinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with StrictOptimizedLinearSeqOps[A, CC, C]] extends Any with LinearSeqOps[A, CC, C] with StrictOptimizedSeqOps[A, CC, C] { + // A more efficient iterator implementation than the default LinearSeqIterator + override def iterator: Iterator[A] = new AbstractIterator[A] { + private[this] var current = StrictOptimizedLinearSeqOps.this + def hasNext = !current.isEmpty + def next() = { val r = current.head; current = current.tail; r } + } + + // Optimized version of `drop` that avoids copying + override def drop(n: Int): C = { + @tailrec def loop(n: Int, s: C): C = + if (n <= 0 || s.isEmpty) s + else loop(n - 1, s.tail) + loop(n, coll) + } + + override def dropWhile(p: A => Boolean): C = { + @tailrec def loop(s: C): C = + if (s.nonEmpty && p(s.head)) loop(s.tail) + else s + loop(coll) + } +} + +/** A specialized Iterator for LinearSeqs that is lazy enough for Stream and LazyList. This is accomplished by not + * evaluating the tail after returning the current head. + */ +private[collection] final class LinearSeqIterator[A](coll: LinearSeqOps[A, LinearSeq, LinearSeq[A]]) extends AbstractIterator[A] { + // A call-by-need cell + private[this] final class LazyCell(st: => LinearSeqOps[A, LinearSeq, LinearSeq[A]]) { lazy val v = st } + + private[this] var these: LazyCell = { + // Reassign reference to avoid creating a private class field and holding a reference to the head. + // LazyCell would otherwise close over `coll`. + val initialHead = coll + new LazyCell(initialHead) + } + + def hasNext: Boolean = these.v.nonEmpty + + def next(): A = + if (isEmpty) Iterator.empty.next() + else { + val cur = these.v + val result = cur.head + these = new LazyCell(cur.tail) + result + } +} diff --git a/library/src/scala/collection/Map.scala b/library/src/scala/collection/Map.scala new file mode 100644 index 000000000000..1b88058d3197 --- /dev/null +++ b/library/src/scala/collection/Map.scala @@ -0,0 +1,420 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.nowarn +import scala.collection.generic.DefaultSerializable +import scala.collection.mutable.StringBuilder +import scala.util.hashing.MurmurHash3 + +/** Base Map type */ +trait Map[K, +V] + extends Iterable[(K, V)] + with MapOps[K, V, Map, Map[K, V]] + with MapFactoryDefaults[K, V, Map, Iterable] + with Equals { + + def mapFactory: scala.collection.MapFactory[Map] = Map + + def canEqual(that: Any): Boolean = true + + /** + * Equality of maps is implemented using the lookup method [[get]]. This method returns `true` if + * - the argument `o` is a `Map`, + * - the two maps have the same [[size]], and + * - for every `(key, value)` pair in this map, `other.get(key) == Some(value)`. + * + * The implementation of `equals` checks the [[canEqual]] method, so subclasses of `Map` can narrow down the equality + * to specific map types. The `Map` implementations in the standard library can all be compared, their `canEqual` + * methods return `true`. + * + * Note: The `equals` method only respects the equality laws (symmetry, transitivity) if the two maps use the same + * key equivalence function in their lookup operation. For example, the key equivalence operation in a + * [[scala.collection.immutable.TreeMap]] is defined by its ordering. Comparing a `TreeMap` with a `HashMap` leads + * to unexpected results if `ordering.equiv(k1, k2)` (used for lookup in `TreeMap`) is different from `k1 == k2` + * (used for lookup in `HashMap`). + * + * {{{ + * scala> import scala.collection.immutable._ + * scala> val ord: Ordering[String] = _ compareToIgnoreCase _ + * + * scala> TreeMap("A" -> 1)(ord) == HashMap("a" -> 1) + * val res0: Boolean = false + * + * scala> HashMap("a" -> 1) == TreeMap("A" -> 1)(ord) + * val res1: Boolean = true + * }}} + * + * + * @param o The map to which this map is compared + * @return `true` if the two maps are equal according to the description + */ + override def equals(o: Any): Boolean = + (this eq o.asInstanceOf[AnyRef]) || (o match { + case map: Map[K @unchecked, _] if map.canEqual(this) => + (this.size == map.size) && { + try this.forall(kv => map.getOrElse(kv._1, Map.DefaultSentinelFn()) == kv._2) + catch { case _: ClassCastException => false } // PR #9565 / scala/bug#12228 + } + case _ => + false + }) + + override def hashCode(): Int = MurmurHash3.mapHash(this) + + // These two methods are not in MapOps so that MapView is not forced to implement them + @deprecated("Use - or removed on an immutable Map", "2.13.0") + def - (key: K): Map[K, V] + @deprecated("Use -- or removedAll on an immutable Map", "2.13.0") + def - (key1: K, key2: K, keys: K*): Map[K, V] + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "Map" + + override def toString(): String = super[Iterable].toString() // Because `Function1` overrides `toString` too +} + +/** Base Map implementation type + * + * @tparam K Type of keys + * @tparam V Type of values + * @tparam CC type constructor of the map (e.g. `HashMap`). Operations returning a collection + * with a different type of entries `(L, W)` (e.g. `map`) return a `CC[L, W]`. + * @tparam C type of the map (e.g. `HashMap[Int, String]`). Operations returning a collection + * with the same type of element (e.g. `drop`, `filter`) return a `C`. + * @define coll map + * @define Coll `Map` + */ +// Note: the upper bound constraint on CC is useful only to +// erase CC to IterableOps instead of Object +trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] + extends IterableOps[(K, V), Iterable, C] + with PartialFunction[K, V] { + + override def view: MapView[K, V] = new MapView.Id(this) + + /** Returns a [[Stepper]] for the keys of this map. See method [[stepper]]. */ + def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntIteratorStepper (keysIterator.asInstanceOf[Iterator[Int]]) + case StepperShape.LongShape => new LongIteratorStepper (keysIterator.asInstanceOf[Iterator[Long]]) + case StepperShape.DoubleShape => new DoubleIteratorStepper(keysIterator.asInstanceOf[Iterator[Double]]) + case _ => shape.seqUnbox(new AnyIteratorStepper(keysIterator)) + } + s.asInstanceOf[S] + } + + /** Returns a [[Stepper]] for the values of this map. See method [[stepper]]. */ + def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntIteratorStepper (valuesIterator.asInstanceOf[Iterator[Int]]) + case StepperShape.LongShape => new LongIteratorStepper (valuesIterator.asInstanceOf[Iterator[Long]]) + case StepperShape.DoubleShape => new DoubleIteratorStepper(valuesIterator.asInstanceOf[Iterator[Double]]) + case _ => shape.seqUnbox(new AnyIteratorStepper(valuesIterator)) + } + s.asInstanceOf[S] + } + + /** Similar to `fromIterable`, but returns a Map collection type. + * Note that the return type is now `CC[K2, V2]`. + */ + @`inline` protected final def mapFromIterable[K2, V2](it: Iterable[(K2, V2)]): CC[K2, V2] = mapFactory.from(it) + + /** The companion object of this map, providing various factory methods. + * + * @note When implementing a custom collection type and refining `CC` to the new type, this + * method needs to be overridden to return a factory for the new type (the compiler will + * issue an error otherwise). + */ + def mapFactory: MapFactory[CC] + + /** Optionally returns the value associated with a key. + * + * @param key the key value + * @return an option value containing the value associated with `key` in this map, + * or `None` if none exists. + */ + def get(key: K): Option[V] + + /** Returns the value associated with a key, or a default value if the key is not contained in the map. + * @param key the key. + * @param default a computation that yields a default value in case no binding for `key` is + * found in the map. + * @tparam V1 the result type of the default computation. + * @return the value associated with `key` if it exists, + * otherwise the result of the `default` computation. + */ + def getOrElse[V1 >: V](key: K, default: => V1): V1 = get(key) match { + case Some(v) => v + case None => default + } + + /** Retrieves the value which is associated with the given key. This + * method invokes the `default` method of the map if there is no mapping + * from the given key to a value. Unless overridden, the `default` method throws a + * `NoSuchElementException`. + * + * @param key the key + * @return the value associated with the given key, or the result of the + * map's `default` method, if none exists. + */ + @throws[NoSuchElementException] + def apply(key: K): V = get(key) match { + case None => default(key) + case Some(value) => value + } + + override /*PartialFunction*/ def applyOrElse[K1 <: K, V1 >: V](x: K1, default: K1 => V1): V1 = getOrElse(x, default(x)) + + /** A set representing the keys contained by this map. + * + * For efficiency the resulting set may be a view (maintaining a reference to the map and reflecting modifications + * to the map), but it may also be a strict collection without reference to the map. + * + * - To ensure an independent strict collection, use `m.keysIterator.toSet` + * - To obtain a view on the keys, use `scala.collection.View.fromIteratorProvider(m.keysIterator)` + * + * @return a set representing the keys contained by this map + */ + def keySet: Set[K] = new KeySet + + /** The implementation class of the set returned by `keySet`. + */ + protected class KeySet extends AbstractSet[K] with GenKeySet with DefaultSerializable { + def diff(that: Set[K]): Set[K] = fromSpecific(this.view.filterNot(that)) + } + + /** A generic trait that is reused by keyset implementations */ + protected trait GenKeySet { this: Set[K] => + def iterator: Iterator[K] = MapOps.this.keysIterator + def contains(key: K): Boolean = MapOps.this.contains(key) + override def size: Int = MapOps.this.size + override def knownSize: Int = MapOps.this.knownSize + override def isEmpty: Boolean = MapOps.this.isEmpty + } + + /** An [[Iterable]] collection of the keys contained by this map. + * + * For efficiency the resulting collection may be a view (maintaining a reference to the map and reflecting + * modifications to the map), but it may also be a strict collection without reference to the map. + * + * - To ensure an independent strict collection, use `m.keysIterator.toSet` + * - To obtain a view on the keys, use `scala.collection.View.fromIteratorProvider(m.keysIterator)` + * + * @return an [[Iterable]] collection of the keys contained by this map + */ + @deprecatedOverriding("This method should be an alias for keySet", since="2.13.13") + def keys: Iterable[K] = keySet + + /** Collects all values of this map in an iterable collection. + * + * @return the values of this map as an iterable. + */ + def values: Iterable[V] = new AbstractIterable[V] with DefaultSerializable { + override def knownSize: Int = MapOps.this.knownSize + override def iterator: Iterator[V] = valuesIterator + } + + /** An [[Iterator]] of the keys contained by this map. + * + * @return an [[Iterator]] of the keys contained by this map + */ + def keysIterator: Iterator[K] = new AbstractIterator[K] { + val iter = MapOps.this.iterator + def hasNext = iter.hasNext + def next() = iter.next()._1 + } + + /** Creates an iterator for all values in this map. + * + * @return an iterator over all values that are associated with some key in this map. + */ + def valuesIterator: Iterator[V] = new AbstractIterator[V] { + val iter = MapOps.this.iterator + def hasNext = iter.hasNext + def next() = iter.next()._2 + } + + /** Apply `f` to each key/value pair for its side effects + * Note: [U] parameter needed to help scalac's type inference. + */ + def foreachEntry[U](f: (K, V) => U): Unit = { + val it = iterator + while (it.hasNext) { + val next = it.next() + f(next._1, next._2) + } + } + + /** Filters this map by retaining only keys satisfying a predicate. + * @param p the predicate used to test keys + * @return an immutable map consisting only of those key value pairs of this map where the key satisfies + * the predicate `p`. The resulting map wraps the original map without copying any elements. + */ + @deprecated("Use .view.filterKeys(f). A future version will include a strict version of this method (for now, .view.filterKeys(p).toMap).", "2.13.0") + def filterKeys(p: K => Boolean): MapView[K, V] = new MapView.FilterKeys(this, p) + + /** Transforms this map by applying a function to every retrieved value. + * @param f the function used to transform values of this map. + * @return a map view which maps every key of this map + * to `f(this(key))`. The resulting map wraps the original map without copying any elements. + */ + @deprecated("Use .view.mapValues(f). A future version will include a strict version of this method (for now, .view.mapValues(f).toMap).", "2.13.0") + def mapValues[W](f: V => W): MapView[K, W] = new MapView.MapValues(this, f) + + /** Defines the default value computation for the map, + * returned when a key is not found. + * + * The method implemented here throws an exception, + * but it may be overridden by subclasses. + * + * @param key the given key value for which a binding is missing. + * @throws NoSuchElementException if no default value is defined + */ + @throws[NoSuchElementException] + def default(key: K): V = + throw new NoSuchElementException("key not found: " + key) + + /** Tests whether this map contains a binding for a key. + * + * @param key the key + * @return `true` if there is a binding for `key` in this map, `false` otherwise. + */ + def contains(key: K): Boolean = get(key).isDefined + + + /** Tests whether this map contains a binding for a key. This method, + * which implements an abstract method of trait `PartialFunction`, + * is equivalent to `contains`. + * + * @param key the key + * @return `true` if there is a binding for `key` in this map, `false` otherwise. + */ + def isDefinedAt(key: K): Boolean = contains(key) + + /** Builds a new map by applying a function to all elements of this $coll. + * + * @param f the function to apply to each element. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. + */ + def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = mapFactory.from(new View.Map(this, f)) + + /** Builds a new collection by applying a partial function to all elements of this $coll + * on which the function is defined. + * + * @param pf the partial function which filters and maps the $coll. + * @tparam K2 the key type of the returned $coll. + * @tparam V2 the value type of the returned $coll. + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ + def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): CC[K2, V2] = + mapFactory.from(new View.Collect(this, pf)) + + /** Builds a new map by applying a function to all elements of this $coll + * and using the elements of the resulting collections. + * + * @param f the function to apply to each element. + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = mapFactory.from(new View.FlatMap(this, f)) + + /** Returns a new $coll containing the elements from the left hand operand followed by the elements from the + * right hand operand. The element type of the $coll is the most specific superclass encompassing + * the element types of the two operands. + * + * @param suffix the iterable to append. + * @return a new $coll which contains all elements + * of this $coll followed by all elements of `suffix`. + */ + def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]): CC[K, V2] = mapFactory.from(suffix match { + case it: Iterable[(K, V2)] => new View.Concat(this, it) + case _ => iterator.concat(suffix.iterator) + }) + + // Not final because subclasses refine the result type, e.g. in SortedMap, the result type is + // SortedMap's CC, while Map's CC is fixed to Map + /** Alias for `concat` */ + /*@`inline` final*/ def ++ [V2 >: V](xs: collection.IterableOnce[(K, V2)]): CC[K, V2] = concat(xs) + + override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = + iterator.map { case (k, v) => s"$k -> $v" }.addString(sb, start, sep, end) + + @deprecated("Consider requiring an immutable Map or fall back to Map.concat.", "2.13.0") + def + [V1 >: V](kv: (K, V1)): CC[K, V1] = + mapFactory.from(new View.Appended(this, kv)) + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = + mapFactory.from(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems)) + + @deprecated("Consider requiring an immutable Map.", "2.13.0") + @`inline` def -- (keys: IterableOnce[K]): C = { + lazy val keysSet = keys.iterator.to(immutable.Set) + fromSpecific(this.view.filterKeys(k => !keysSet.contains(k))) + } + + @deprecated("Use ++ instead of ++: for collections of type Iterable", "2.13.0") + def ++: [V1 >: V](that: IterableOnce[(K,V1)]): CC[K,V1] = { + val thatIterable: Iterable[(K, V1)] = that match { + case that: Iterable[(K, V1)] => that + case that => View.from(that) + } + mapFactory.from(new View.Concat(thatIterable, this)) + } +} + +object MapOps { + /** Specializes `WithFilter` for Map collection types by adding overloads to transformation + * operations that can return a Map. + * + * @define coll map collection + */ + @SerialVersionUID(3L) + class WithFilter[K, +V, +IterableCC[_], +CC[_, _] <: IterableOps[_, AnyConstr, _]]( + self: MapOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _], + p: ((K, V)) => Boolean + ) extends IterableOps.WithFilter[(K, V), IterableCC](self, p) with Serializable { + + def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = + self.mapFactory.from(new View.Map(filtered, f)) + + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = + self.mapFactory.from(new View.FlatMap(filtered, f)) + + override def withFilter(q: ((K, V)) => Boolean): WithFilter[K, V, IterableCC, CC] = + new WithFilter[K, V, IterableCC, CC](self, (kv: (K, V)) => p(kv) && q(kv)) + + } + +} + +/** + * $factoryInfo + * @define coll map + * @define Coll `Map` + */ +@SerialVersionUID(3L) +object Map extends MapFactory.Delegate[Map](immutable.Map) { + private val DefaultSentinel: AnyRef = new AnyRef + private val DefaultSentinelFn: () => AnyRef = () => DefaultSentinel +} + +/** Explicit instantiation of the `Map` trait to reduce class file size in subclasses. */ +abstract class AbstractMap[K, +V] extends AbstractIterable[(K, V)] with Map[K, V] diff --git a/library/src/scala/collection/MapView.scala b/library/src/scala/collection/MapView.scala new file mode 100644 index 000000000000..39742c434c41 --- /dev/null +++ b/library/src/scala/collection/MapView.scala @@ -0,0 +1,188 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.annotation.nowarn +import scala.collection.MapView.SomeMapOps +import scala.collection.mutable.Builder + +trait MapView[K, +V] + extends MapOps[K, V, ({ type l[X, Y] = View[(X, Y)] })#l, View[(K, V)]] + with View[(K, V)] { + + override def view: MapView[K, V] = this + + // Ideally this returns a `View`, but bincompat + /** Creates a view over all keys of this map. + * + * @return the keys of this map as a view. + */ + @nowarn("msg=overriding method keys") + override def keys: Iterable[K] = new MapView.Keys(this) + + // Ideally this returns a `View`, but bincompat + /** Creates a view over all values of this map. + * + * @return the values of this map as a view. + */ + override def values: Iterable[V] = new MapView.Values(this) + + /** Filters this map by retaining only keys satisfying a predicate. + * @param p the predicate used to test keys + * @return an immutable map consisting only of those key value pairs of this map where the key satisfies + * the predicate `p`. The resulting map wraps the original map without copying any elements. + */ + override def filterKeys(p: K => Boolean): MapView[K, V] = new MapView.FilterKeys(this, p) + + /** Transforms this map by applying a function to every retrieved value. + * @param f the function used to transform values of this map. + * @return a map view which maps every key of this map + * to `f(this(key))`. The resulting map wraps the original map without copying any elements. + */ + override def mapValues[W](f: V => W): MapView[K, W] = new MapView.MapValues(this, f) + + override def filter(pred: ((K, V)) => Boolean): MapView[K, V] = new MapView.Filter(this, isFlipped = false, pred) + + override def filterNot(pred: ((K, V)) => Boolean): MapView[K, V] = new MapView.Filter(this, isFlipped = true, pred) + + override def partition(p: ((K, V)) => Boolean): (MapView[K, V], MapView[K, V]) = (filter(p), filterNot(p)) + + override def tapEach[U](f: ((K, V)) => U): MapView[K, V] = new MapView.TapEach(this, f) + + def mapFactory: MapViewFactory = MapView + + override def empty: MapView[K, V] = mapFactory.empty + + override def withFilter(p: ((K, V)) => Boolean): MapOps.WithFilter[K, V, View, ({ type l[X, Y] = View[(X, Y)] })#l] = new MapOps.WithFilter(this, p) + + override def toString: String = super[View].toString + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "MapView" +} + +object MapView extends MapViewFactory { + + /** An `IterableOps` whose collection type and collection type constructor are unknown */ + type SomeIterableConstr[X, Y] = IterableOps[_, AnyConstr, _] + /** A `MapOps` whose collection type and collection type constructor are (mostly) unknown */ + type SomeMapOps[K, +V] = MapOps[K, V, SomeIterableConstr, _] + + @SerialVersionUID(3L) + private val EmptyMapView: MapView[Any, Nothing] = new AbstractMapView[Any, Nothing] { + override def get(key: Any): Option[Nothing] = None + override def iterator: Iterator[Nothing] = Iterator.empty[Nothing] + override def knownSize: Int = 0 + override def isEmpty: Boolean = true + override def filterKeys(p: Any => Boolean): MapView[Any, Nothing] = this + override def mapValues[W](f: Nothing => W): MapView[Any, Nothing] = this + override def filter(pred: ((Any, Nothing)) => Boolean): MapView[Any, Nothing] = this + override def filterNot(pred: ((Any, Nothing)) => Boolean): MapView[Any, Nothing] = this + override def partition(p: ((Any, Nothing)) => Boolean): (MapView[Any, Nothing], MapView[Any, Nothing]) = (this, this) + } + + @SerialVersionUID(3L) + class Id[K, +V](underlying: SomeMapOps[K, V]) extends AbstractMapView[K, V] { + def get(key: K): Option[V] = underlying.get(key) + def iterator: Iterator[(K, V)] = underlying.iterator + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + // Ideally this is public, but bincompat + @SerialVersionUID(3L) + private class Keys[K](underlying: SomeMapOps[K, _]) extends AbstractView[K] { + def iterator: Iterator[K] = underlying.keysIterator + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + // Ideally this is public, but bincompat + @SerialVersionUID(3L) + private class Values[+V](underlying: SomeMapOps[_, V]) extends AbstractView[V] { + def iterator: Iterator[V] = underlying.valuesIterator + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + @SerialVersionUID(3L) + class MapValues[K, +V, +W](underlying: SomeMapOps[K, V], f: V => W) extends AbstractMapView[K, W] { + def iterator: Iterator[(K, W)] = underlying.iterator.map(kv => (kv._1, f(kv._2))) + def get(key: K): Option[W] = underlying.get(key).map(f) + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + @SerialVersionUID(3L) + class FilterKeys[K, +V](underlying: SomeMapOps[K, V], p: K => Boolean) extends AbstractMapView[K, V] { + def iterator: Iterator[(K, V)] = underlying.iterator.filter { case (k, _) => p(k) } + def get(key: K): Option[V] = if (p(key)) underlying.get(key) else None + override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + } + + @SerialVersionUID(3L) + class Filter[K, +V](underlying: SomeMapOps[K, V], isFlipped: Boolean, p: ((K, V)) => Boolean) extends AbstractMapView[K, V] { + def iterator: Iterator[(K, V)] = underlying.iterator.filterImpl(p, isFlipped) + def get(key: K): Option[V] = underlying.get(key) match { + case s @ Some(v) if p((key, v)) != isFlipped => s + case _ => None + } + override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + } + + @SerialVersionUID(3L) + class TapEach[K, +V, +U](underlying: SomeMapOps[K, V], f: ((K, V)) => U) extends AbstractMapView[K, V] { + override def get(key: K): Option[V] = { + underlying.get(key) match { + case s @ Some(v) => + f((key, v)) + s + case None => None + } + } + override def iterator: Iterator[(K, V)] = underlying.iterator.tapEach(f) + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + override def newBuilder[X, Y]: Builder[(X, Y), MapView[X, Y]] = mutable.HashMap.newBuilder[X, Y].mapResult(_.view) + + override def empty[K, V]: MapView[K, V] = EmptyMapView.asInstanceOf[MapView[K, V]] + + override def from[K, V](it: IterableOnce[(K, V)]): View[(K, V)] = View.from(it) + + override def from[K, V](it: SomeMapOps[K, V]): MapView[K, V] = it match { + case mv: MapView[K, V] => mv + case other => new MapView.Id(other) + } + + override def apply[K, V](elems: (K, V)*): MapView[K, V] = from(elems.toMap) +} + +trait MapViewFactory extends collection.MapFactory[({ type l[X, Y] = View[(X, Y)]})#l] { + + def newBuilder[X, Y]: Builder[(X, Y), MapView[X, Y]] + + def empty[X, Y]: MapView[X, Y] + + def from[K, V](it: SomeMapOps[K, V]): MapView[K, V] + + override def apply[K, V](elems: (K, V)*): MapView[K, V] = from(elems.toMap) +} + +/** Explicit instantiation of the `MapView` trait to reduce class file size in subclasses. */ +@SerialVersionUID(3L) +abstract class AbstractMapView[K, +V] extends AbstractView[(K, V)] with MapView[K, V] + diff --git a/library/src/scala/collection/Searching.scala b/library/src/scala/collection/Searching.scala new file mode 100644 index 000000000000..8b8132870287 --- /dev/null +++ b/library/src/scala/collection/Searching.scala @@ -0,0 +1,57 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.language.implicitConversions +import scala.collection.generic.IsSeq + +object Searching { + + /** The result of performing a search on a sorted sequence + * + * Example usage: + * + * {{{ + * val list = List(1, 3, 4, 5) // list must be sorted before searching + * list.search(4) // Found(2) + * list.search(2) // InsertionPoint(1) + * }}} + * + * */ + sealed abstract class SearchResult { + /** The index corresponding to the element searched for in the sequence, if it was found, + * or the index where the element would be inserted in the sequence, if it was not in the sequence */ + def insertionPoint: Int + } + + /** The result of performing a search on a sorted sequence, where the element was found. + * + * @param foundIndex the index corresponding to the element searched for in the sequence + */ + case class Found(foundIndex: Int) extends SearchResult { + override def insertionPoint: Int = foundIndex + } + + /** The result of performing a search on a sorted sequence, where the element was not found + * + * @param insertionPoint the index where the element would be inserted in the sequence + */ + case class InsertionPoint(insertionPoint: Int) extends SearchResult + + @deprecated("Search methods are defined directly on SeqOps and do not require scala.collection.Searching any more", "2.13.0") + class SearchImpl[Repr, A](private val coll: SeqOps[A, AnyConstr, _]) extends AnyVal + + @deprecated("Search methods are defined directly on SeqOps and do not require scala.collection.Searching any more", "2.13.0") + implicit def search[Repr, A](coll: Repr)(implicit fr: IsSeq[Repr]): SearchImpl[Repr, fr.A] = + new SearchImpl(fr.conversion(coll)) +} diff --git a/library/src/scala/collection/Seq.scala b/library/src/scala/collection/Seq.scala new file mode 100644 index 000000000000..f9bb5c2cf483 --- /dev/null +++ b/library/src/scala/collection/Seq.scala @@ -0,0 +1,1190 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.collection.immutable.Range +import scala.util.hashing.MurmurHash3 +import Searching.{Found, InsertionPoint, SearchResult} +import scala.annotation.nowarn + +/** Base trait for sequence collections + * + * @tparam A the element type of the collection + */ +trait Seq[+A] + extends Iterable[A] + with PartialFunction[Int, A] + with SeqOps[A, Seq, Seq[A]] + with IterableFactoryDefaults[A, Seq] + with Equals { + + override def iterableFactory: SeqFactory[Seq] = Seq + + def canEqual(that: Any): Boolean = true + + override def equals(o: Any): Boolean = + (this eq o.asInstanceOf[AnyRef]) || (o match { + case seq: Seq[A @unchecked] if seq.canEqual(this) => sameElements(seq) + case _ => false + }) + + override def hashCode(): Int = MurmurHash3.seqHash(this) + + override def toString(): String = super[Iterable].toString() + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "Seq" +} + +/** + * $factoryInfo + * @define coll sequence + * @define Coll `Seq` + */ +@SerialVersionUID(3L) +object Seq extends SeqFactory.Delegate[Seq](immutable.Seq) + +/** Base trait for Seq operations + * + * @tparam A the element type of the collection + * @tparam CC type constructor of the collection (e.g. `List`, `Set`). Operations returning a collection + * with a different type of element `B` (e.g. `map`) return a `CC[B]`. + * @tparam C type of the collection (e.g. `List[Int]`, `String`, `BitSet`). Operations returning a collection + * with the same type of element (e.g. `drop`, `filter`) return a `C`. + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * + * Note: may not terminate for infinite-sized collections. + * + * @define willNotTerminateInf + * + * Note: will not terminate for infinite-sized collections. + * + * @define coll sequence + * @define Coll `Seq` + */ +trait SeqOps[+A, +CC[_], +C] extends Any + with IterableOps[A, CC, C] { self => + + override def view: SeqView[A] = new SeqView.Id[A](this) + + /** Gets the element at the specified index. This operation is provided for convenience in `Seq`. It should + * not be assumed to be efficient unless you have an `IndexedSeq`. */ + @throws[IndexOutOfBoundsException] + def apply(i: Int): A + + /** The length (number of elements) of the $coll. `size` is an alias for `length` in `Seq` collections. */ + def length: Int + + /** A copy of the $coll with an element prepended. + * + * Also, the original $coll is not modified, so you will want to capture the result. + * + * Example: + * {{{ + * scala> val x = List(1) + * x: List[Int] = List(1) + * + * scala> val y = 2 +: x + * y: List[Int] = List(2, 1) + * + * scala> println(x) + * List(1) + * }}} + * + * @param elem the prepended element + * @tparam B the element type of the returned $coll. + * + * @return a new $coll consisting of `value` followed + * by all elements of this $coll. + */ + def prepended[B >: A](elem: B): CC[B] = iterableFactory.from(new View.Prepended(elem, this)) + + /** Alias for `prepended`. + * + * Note that :-ending operators are right associative (see example). + * A mnemonic for `+:` vs. `:+` is: the COLon goes on the COLlection side. + */ + @`inline` final def +: [B >: A](elem: B): CC[B] = prepended(elem) + + /** A copy of this $coll with an element appended. + * + * $willNotTerminateInf + * + * Example: + * {{{ + * scala> val a = List(1) + * a: List[Int] = List(1) + * + * scala> val b = a :+ 2 + * b: List[Int] = List(1, 2) + * + * scala> println(a) + * List(1) + * }}} + * + * @param elem the appended element + * @tparam B the element type of the returned $coll. + * @return a new $coll consisting of + * all elements of this $coll followed by `value`. + */ + def appended[B >: A](elem: B): CC[B] = iterableFactory.from(new View.Appended(this, elem)) + + /** Alias for `appended`. + * + * Note that :-ending operators are right associative (see example). + * A mnemonic for `+:` vs. `:+` is: the COLon goes on the COLlection side. + */ + @`inline` final def :+ [B >: A](elem: B): CC[B] = appended(elem) + + /** As with `:++`, returns a new collection containing the elements from the left operand followed by the + * elements from the right operand. + * + * It differs from `:++` in that the right operand determines the type of + * the resulting collection rather than the left one. + * Mnemonic: the COLon is on the side of the new COLlection type. + * + * @param prefix the iterable to prepend. + * @tparam B the element type of the returned collection. + * @return a new $coll which contains all elements of `prefix` followed + * by all the elements of this $coll. + */ + def prependedAll[B >: A](prefix: IterableOnce[B]): CC[B] = iterableFactory.from(prefix match { + case prefix: Iterable[B] => new View.Concat(prefix, this) + case _ => prefix.iterator ++ iterator + }) + + /** Alias for `prependedAll`. */ + @`inline` override final def ++: [B >: A](prefix: IterableOnce[B]): CC[B] = prependedAll(prefix) + + /** Returns a new $coll containing the elements from the left hand operand followed by the elements from the + * right hand operand. The element type of the $coll is the most specific superclass encompassing + * the element types of the two operands. + * + * @param suffix the iterable to append. + * @tparam B the element type of the returned collection. + * @return a new collection of type `CC[B]` which contains all elements + * of this $coll followed by all elements of `suffix`. + */ + def appendedAll[B >: A](suffix: IterableOnce[B]): CC[B] = super.concat(suffix) + + /** Alias for `appendedAll`. */ + @`inline` final def :++ [B >: A](suffix: IterableOnce[B]): CC[B] = appendedAll(suffix) + + // Make `concat` an alias for `appendedAll` so that it benefits from performance + // overrides of this method + @`inline` final override def concat[B >: A](suffix: IterableOnce[B]): CC[B] = appendedAll(suffix) + + /** Produces a new sequence which contains all elements of this $coll and also all elements of + * a given sequence. `xs union ys` is equivalent to `xs ++ ys`. + * + * @param that the sequence to add. + * @tparam B the element type of the returned $coll. + * @return a new collection which contains all elements of this $coll + * followed by all elements of `that`. + */ + @deprecated("Use `concat` instead", "2.13.0") + @inline final def union[B >: A](that: Seq[B]): CC[B] = concat(that) + + final override def size: Int = length + + /** Selects all the elements of this $coll ignoring the duplicates. + * + * @return a new $coll consisting of all the elements of this $coll without duplicates. + */ + def distinct: C = distinctBy(identity) + + /** Selects all the elements of this $coll ignoring the duplicates as determined by `==` after applying + * the transforming function `f`. + * + * @param f The transforming function whose result is used to determine the uniqueness of each element + * @tparam B the type of the elements after being transformed by `f` + * @return a new $coll consisting of all the elements of this $coll without duplicates. + */ + def distinctBy[B](f: A => B): C = fromSpecific(new View.DistinctBy(this, f)) + + /** Returns a new $coll with the elements of this $coll in reverse order. + * + * $willNotTerminateInf + * $willForceEvaluation + * + * @return a new $coll with all elements of this $coll in reverse order. + */ + def reverse: C = fromSpecific(reversed) + + /** An iterator yielding the elements of this $coll in reverse order. + * + * $willNotTerminateInf + * + * Note: `xs.reverseIterator` is the same as `xs.reverse.iterator` but might be more efficient. + * + * @return an iterator yielding the elements of this $coll in reverse order. + */ + def reverseIterator: Iterator[A] = reversed.iterator + + /** Tests whether this $coll contains the given sequence at a given index. + * + * '''Note''': If the both the receiver object `this` and the argument + * `that` are infinite sequences this method may not terminate. + * + * @param that the sequence to test + * @param offset the index where the sequence is searched. + * @return `true` if the sequence `that` is contained in this $coll at + * index `offset`, otherwise `false`. + */ + def startsWith[B >: A](that: IterableOnce[B], offset: Int = 0): Boolean = { + val i = iterator drop offset + val j = that.iterator + while (j.hasNext && i.hasNext) + if (i.next() != j.next()) + return false + + !j.hasNext + } + + /** Tests whether this $coll ends with the given sequence. + * $willNotTerminateInf + * @param that the sequence to test + * @return `true` if this $coll has `that` as a suffix, `false` otherwise. + */ + def endsWith[B >: A](that: Iterable[B]): Boolean = { + if (that.isEmpty) true + else { + val i = iterator.drop(length - that.size) + val j = that.iterator + while (i.hasNext && j.hasNext) + if (i.next() != j.next()) + return false + + !j.hasNext + } + } + + /** Tests whether this $coll contains given index. + * + * The implementations of methods `apply` and `isDefinedAt` turn a `Seq[A]` into + * a `PartialFunction[Int, A]`. + * + * @param idx the index to test + * @return `true` if this $coll contains an element at position `idx`, `false` otherwise. + */ + def isDefinedAt(idx: Int): Boolean = idx >= 0 && lengthIs > idx + + /** A copy of this $coll with an element value appended until a given target length is reached. + * + * @param len the target length + * @param elem the padding value + * @tparam B the element type of the returned $coll. + * @return a new $coll consisting of + * all elements of this $coll followed by the minimal number of occurrences of `elem` so + * that the resulting collection has a length of at least `len`. + */ + def padTo[B >: A](len: Int, elem: B): CC[B] = iterableFactory.from(new View.PadTo(this, len, elem)) + + /** Computes the length of the longest segment that starts from the first element + * and whose elements all satisfy some predicate. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the length of the longest segment of this $coll that starts from the first element + * such that every element of the segment satisfies the predicate `p`. + */ + final def segmentLength(p: A => Boolean): Int = segmentLength(p, 0) + + /** Computes the length of the longest segment that starts from some index + * and whose elements all satisfy some predicate. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @param from the index where the search starts. + * @return the length of the longest segment of this $coll starting from index `from` + * such that every element of the segment satisfies the predicate `p`. + */ + def segmentLength(p: A => Boolean, from: Int): Int = { + var i = 0 + val it = iterator.drop(from) + while (it.hasNext && p(it.next())) + i += 1 + i + } + + /** Returns the length of the longest prefix whose elements all satisfy some predicate. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the length of the longest prefix of this $coll + * such that every element of the segment satisfies the predicate `p`. + */ + @deprecated("Use segmentLength instead of prefixLength", "2.13.0") + @`inline` final def prefixLength(p: A => Boolean): Int = segmentLength(p, 0) + + /** Finds index of the first element satisfying some predicate after or at some start index. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @param from the start index + * @return the index `>= from` of the first element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + def indexWhere(p: A => Boolean, from: Int): Int = iterator.indexWhere(p, from) + + /** Finds index of the first element satisfying some predicate. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the index `>= 0` of the first element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + @deprecatedOverriding("Override indexWhere(p, from) instead - indexWhere(p) calls indexWhere(p, 0)", "2.13.0") + def indexWhere(p: A => Boolean): Int = indexWhere(p, 0) + + /** Finds index of first occurrence of some value in this $coll after or at some start index. + * + * @param elem the element value to search for. + * @tparam B the type of the element `elem`. + * @param from the start index + * @return the index `>= from` of the first element of this $coll that is equal (as determined by `==`) + * to `elem`, or `-1`, if none exists. + */ + def indexOf[B >: A](elem: B, from: Int): Int = indexWhere(elem == _, from) + + /** Finds index of first occurrence of some value in this $coll. + * + * @param elem the element value to search for. + * @tparam B the type of the element `elem`. + * @return the index `>= 0` of the first element of this $coll that is equal (as determined by `==`) + * to `elem`, or `-1`, if none exists. + */ + @deprecatedOverriding("Override indexOf(elem, from) instead - indexOf(elem) calls indexOf(elem, 0)", "2.13.0") + def indexOf[B >: A](elem: B): Int = indexOf(elem, 0) + + /** Finds index of last occurrence of some value in this $coll before or at a given end index. + * + * $willNotTerminateInf + * + * @param elem the element value to search for. + * @param end the end index. + * @tparam B the type of the element `elem`. + * @return the index `<= end` of the last element of this $coll that is equal (as determined by `==`) + * to `elem`, or `-1`, if none exists. + */ + def lastIndexOf[B >: A](elem: B, end: Int = length - 1): Int = lastIndexWhere(elem == _, end) + + /** Finds index of last element satisfying some predicate before or at given end index. + * + * $willNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the index `<= end` of the last element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + def lastIndexWhere(p: A => Boolean, end: Int): Int = { + var i = length - 1 + val it = reverseIterator + while (it.hasNext && { val elem = it.next(); (i > end || !p(elem)) }) i -= 1 + i + } + + /** Finds index of last element satisfying some predicate. + * + * $willNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the index of the last element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + @deprecatedOverriding("Override lastIndexWhere(p, end) instead - lastIndexWhere(p) calls lastIndexWhere(p, Int.MaxValue)", "2.13.0") + def lastIndexWhere(p: A => Boolean): Int = lastIndexWhere(p, Int.MaxValue) + + @inline private[this] def toGenericSeq: scala.collection.Seq[A] = this match { + case s: scala.collection.Seq[A] => s + case _ => toSeq + } + + /** Finds first index after or at a start index where this $coll contains a given sequence as a slice. + * $mayNotTerminateInf + * @param that the sequence to test + * @param from the start index + * @return the first index `>= from` such that the elements of this $coll starting at this index + * match the elements of sequence `that`, or `-1` if no such subsequence exists. + */ + // TODO Should be implemented in a way that preserves laziness + def indexOfSlice[B >: A](that: Seq[B], from: Int): Int = + if (that.isEmpty && from == 0) 0 + else { + val l = knownSize + val tl = that.knownSize + if (l >= 0 && tl >= 0) { + val clippedFrom = math.max(0, from) + if (from > l) -1 + else if (tl < 1) clippedFrom + else if (l < tl) -1 + else SeqOps.kmpSearch(toGenericSeq, clippedFrom, l, that, 0, tl, forward = true) + } + else { + var i = from + var s: scala.collection.Seq[A] = toGenericSeq.drop(i) + while (!s.isEmpty) { + if (s startsWith that) + return i + + i += 1 + s = s.tail + } + -1 + } + } + + /** Finds first index where this $coll contains a given sequence as a slice. + * $mayNotTerminateInf + * @param that the sequence to test + * @return the first index `>= 0` such that the elements of this $coll starting at this index + * match the elements of sequence `that`, or `-1` if no such subsequence exists. + */ + @deprecatedOverriding("Override indexOfSlice(that, from) instead - indexOfSlice(that) calls indexOfSlice(that, 0)", "2.13.0") + def indexOfSlice[B >: A](that: Seq[B]): Int = indexOfSlice(that, 0) + + /** Finds last index before or at a given end index where this $coll contains a given sequence as a slice. + * + * $willNotTerminateInf + * + * @param that the sequence to test + * @param end the end index + * @return the last index `<= end` such that the elements of this $coll starting at this index + * match the elements of sequence `that`, or `-1` if no such subsequence exists. + */ + def lastIndexOfSlice[B >: A](that: Seq[B], end: Int): Int = { + val l = length + val tl = that.length + val clippedL = math.min(l-tl, end) + + if (end < 0) -1 + else if (tl < 1) clippedL + else if (l < tl) -1 + else SeqOps.kmpSearch(toGenericSeq, 0, clippedL+tl, that, 0, tl, forward = false) + } + + /** Finds last index where this $coll contains a given sequence as a slice. + * + * $willNotTerminateInf + * + * @param that the sequence to test + * @return the last index such that the elements of this $coll starting at this index + * match the elements of sequence `that`, or `-1` if no such subsequence exists. + */ + @deprecatedOverriding("Override lastIndexOfSlice(that, end) instead - lastIndexOfSlice(that) calls lastIndexOfSlice(that, Int.MaxValue)", "2.13.0") + def lastIndexOfSlice[B >: A](that: Seq[B]): Int = lastIndexOfSlice(that, Int.MaxValue) + + /** Finds the last element of the $coll satisfying a predicate, if any. + * + * $willNotTerminateInf + * + * @param p the predicate used to test elements. + * @return an option value containing the last element in the $coll + * that satisfies `p`, or `None` if none exists. + */ + def findLast(p: A => Boolean): Option[A] = { + val it = reverseIterator + while (it.hasNext) { + val elem = it.next() + if (p(elem)) return Some(elem) + } + None + } + + /** Tests whether this $coll contains a given sequence as a slice. + * $mayNotTerminateInf + * @param that the sequence to test + * @return `true` if this $coll contains a slice with the same elements + * as `that`, otherwise `false`. + */ + def containsSlice[B >: A](that: Seq[B]): Boolean = indexOfSlice(that) != -1 + + /** Tests whether this $coll contains a given value as an element. + * $mayNotTerminateInf + * + * @param elem the element to test. + * @return `true` if this $coll has an element that is equal (as + * determined by `==`) to `elem`, `false` otherwise. + */ + def contains[A1 >: A](elem: A1): Boolean = exists (_ == elem) + + @deprecated("Use .reverseIterator.map(f).to(...) instead of .reverseMap(f)", "2.13.0") + def reverseMap[B](f: A => B): CC[B] = iterableFactory.from(new View.Map(View.fromIteratorProvider(() => reverseIterator), f)) + + /** Iterates over distinct permutations of elements. + * + * $willForceEvaluation + * + * @return An Iterator which traverses the distinct permutations of this $coll. + * @example {{{ + * Seq('a', 'b', 'b').permutations.foreach(println) + * // List(a, b, b) + * // List(b, a, b) + * // List(b, b, a) + * }}} + */ + def permutations: Iterator[C] = + if (isEmpty) Iterator.single(coll) + else new PermutationsItr + + /** Iterates over combinations of elements. + * + * A '''combination''' of length `n` is a sequence of `n` elements selected in order of their first index in this sequence. + * + * For example, `"xyx"` has two combinations of length 2. The `x` is selected first: `"xx"`, `"xy"`. + * The sequence `"yx"` is not returned as a combination because it is subsumed by `"xy"`. + * + * If there is more than one way to generate the same combination, only one will be returned. + * + * For example, the result `"xy"` arbitrarily selected one of the `x` elements. + * + * As a further illustration, `"xyxx"` has three different ways to generate `"xy"` because there are three elements `x` + * to choose from. Moreover, there are three unordered pairs `"xx"` but only one is returned. + * + * It is not specified which of these equal combinations is returned. It is an implementation detail + * that should not be relied on. For example, the combination `"xx"` does not necessarily contain + * the first `x` in this sequence. This behavior is observable if the elements compare equal + * but are not identical. + * + * As a consequence, `"xyx".combinations(3).next()` is `"xxy"`: the combination does not reflect the order + * of the original sequence, but the order in which elements were selected, by "first index"; + * the order of each `x` element is also arbitrary. + * + * $willForceEvaluation + * + * @return An Iterator which traverses the n-element combinations of this $coll. + * @example {{{ + * Seq('a', 'b', 'b', 'b', 'c').combinations(2).foreach(println) + * // List(a, b) + * // List(a, c) + * // List(b, b) + * // List(b, c) + * Seq('b', 'a', 'b').combinations(2).foreach(println) + * // List(b, b) + * // List(b, a) + * }}} + */ + def combinations(n: Int): Iterator[C] = + if (n < 0 || n > size) Iterator.empty + else new CombinationsItr(n) + + private class PermutationsItr extends AbstractIterator[C] { + private[this] val (elms, idxs) = init() + private[this] var _hasNext = true + + def hasNext = _hasNext + @throws[NoSuchElementException] + def next(): C = { + if (!hasNext) + Iterator.empty.next() + + val forcedElms = new mutable.ArrayBuffer[A](elms.size) ++= elms + val result = (newSpecificBuilder ++= forcedElms).result() + var i = idxs.length - 2 + while(i >= 0 && idxs(i) >= idxs(i+1)) + i -= 1 + + if (i < 0) + _hasNext = false + else { + var j = idxs.length - 1 + while(idxs(j) <= idxs(i)) j -= 1 + swap(i,j) + + val len = (idxs.length - i) / 2 + var k = 1 + while (k <= len) { + swap(i+k, idxs.length - k) + k += 1 + } + } + result + } + private def swap(i: Int, j: Int): Unit = { + val tmpI = idxs(i) + idxs(i) = idxs(j) + idxs(j) = tmpI + val tmpE = elms(i) + elms(i) = elms(j) + elms(j) = tmpE + } + + private[this] def init() = { + val m = mutable.HashMap[A, Int]() + val (es, is) = (self.toGenericSeq map (e => (e, m.getOrElseUpdate(e, m.size))) sortBy (_._2)).unzip + + (es.to(mutable.ArrayBuffer), is.toArray) + } + } + + private class CombinationsItr(n: Int) extends AbstractIterator[C] { + // generating all nums such that: + // (1) nums(0) + .. + nums(length-1) = n + // (2) 0 <= nums(i) <= cnts(i), where 0 <= i <= cnts.length-1 + private[this] val (elms, cnts, nums) = init() + private[this] val offs = cnts.scanLeft(0)(_ + _) + private[this] var _hasNext = true + + def hasNext = _hasNext + def next(): C = { + if (!hasNext) + Iterator.empty.next() + + /* Calculate this result. */ + val buf = newSpecificBuilder + for(k <- 0 until nums.length; j <- 0 until nums(k)) + buf += elms(offs(k)+j) + val res = buf.result() + + /* Prepare for the next call to next. */ + var idx = nums.length - 1 + while (idx >= 0 && nums(idx) == cnts(idx)) + idx -= 1 + + idx = nums.lastIndexWhere(_ > 0, idx - 1) + + if (idx < 0) + _hasNext = false + else { + // OPT: hand rolled version of `sum = nums.view(idx + 1, nums.length).sum + 1` + var sum = 1 + var i = idx + 1 + while (i < nums.length) { + sum += nums(i) + i += 1 + } + nums(idx) -= 1 + for (k <- (idx+1) until nums.length) { + nums(k) = sum min cnts(k) + sum -= nums(k) + } + } + + res + } + + /** Rearrange seq to newSeq a0a0..a0a1..a1...ak..ak such that + * seq.count(_ == aj) == cnts(j) + * + * @return (newSeq,cnts,nums) + */ + private def init(): (IndexedSeq[A], Array[Int], Array[Int]) = { + val m = mutable.HashMap[A, Int]() + + // e => (e, weight(e)) + val (es, is) = (self.toGenericSeq map (e => (e, m.getOrElseUpdate(e, m.size))) sortBy (_._2)).unzip + val cs = new Array[Int](m.size) + is foreach (i => cs(i) += 1) + val ns = new Array[Int](cs.length) + + var r = n + 0 until ns.length foreach { k => + ns(k) = r min cs(k) + r -= ns(k) + } + (es.to(IndexedSeq), cs, ns) + } + } + + /** Sorts this $coll according to an Ordering. + * + * The sort is stable. That is, elements that are equal (as determined by + * `ord.compare`) appear in the same order in the sorted sequence as in the original. + * + * @see [[scala.math.Ordering]] + * + * $willForceEvaluation + * + * @param ord the ordering to be used to compare elements. + * @return a $coll consisting of the elements of this $coll + * sorted according to the ordering `ord`. + */ + def sorted[B >: A](implicit ord: Ordering[B]): C = { + val len = this.length + val b = newSpecificBuilder + if (len == 1) b += head + else if (len > 1) { + b.sizeHint(len) + val arr = new Array[Any](len) + @annotation.unused val copied = copyToArray(arr) + //assert(copied == len) + java.util.Arrays.sort(arr.asInstanceOf[Array[AnyRef]], ord.asInstanceOf[Ordering[AnyRef]]) + var i = 0 + while (i < len) { + b += arr(i).asInstanceOf[A] + i += 1 + } + } + b.result() + } + + /** Sorts this $coll according to a comparison function. + * $willNotTerminateInf + * $willForceEvaluation + * + * The sort is stable. That is, elements that are equal + * (`lt` returns false for both directions of comparison) + * appear in the same order in the sorted sequence as in the original. + * + * @param lt a predicate that is true if + * its first argument strictly precedes its second argument in + * the desired ordering. + * @return a $coll consisting of the elements of this $coll + * sorted according to the comparison function `lt`. + * @example {{{ + * List("Steve", "Bobby", "Tom", "John", "Bob").sortWith((x, y) => x.take(3).compareTo(y.take(3)) < 0) = + * List("Bobby", "Bob", "John", "Steve", "Tom") + * }}} + */ + def sortWith(lt: (A, A) => Boolean): C = sorted(Ordering.fromLessThan(lt)) + + /** Sorts this $coll according to the Ordering which results from transforming + * an implicitly given Ordering with a transformation function. + * $willNotTerminateInf + * $willForceEvaluation + * + * The sort is stable. That is, elements that are equal (as determined by + * `ord.compare`) appear in the same order in the sorted sequence as in the original. + * + * @see [[scala.math.Ordering]] + * @param f the transformation function mapping elements + * to some other domain `B`. + * @param ord the ordering assumed on domain `B`. + * @tparam B the target type of the transformation `f`, and the type where + * the ordering `ord` is defined. + * @return a $coll consisting of the elements of this $coll + * sorted according to the ordering where `x < y` if + * `ord.lt(f(x), f(y))`. + * + * @example {{{ + * val words = "The quick brown fox jumped over the lazy dog".split(' ') + * // this works because scala.Ordering will implicitly provide an Ordering[Tuple2[Int, Char]] + * words.sortBy(x => (x.length, x.head)) + * res0: Array[String] = Array(The, dog, fox, the, lazy, over, brown, quick, jumped) + * }}} + */ + def sortBy[B](f: A => B)(implicit ord: Ordering[B]): C = sorted(ord on f) + + /** Produces the range of all indices of this sequence. + * $willForceEvaluation + * + * @return a `Range` value from `0` to one less than the length of this $coll. + */ + def indices: Range = Range(0, length) + + override final def sizeCompare(otherSize: Int): Int = lengthCompare(otherSize) + + /** Compares the length of this $coll to a test value. + * + * @param len the test value that gets compared with the length. + * @return A value `x` where + * {{{ + * x < 0 if this.length < len + * x == 0 if this.length == len + * x > 0 if this.length > len + * }}} + * The method as implemented here does not call `length` directly; its running time + * is `O(length min len)` instead of `O(length)`. The method should be overridden + * if computing `length` is cheap and `knownSize` returns `-1`. + * + * @see [[lengthIs]] + */ + def lengthCompare(len: Int): Int = super.sizeCompare(len) + + override final def sizeCompare(that: Iterable[_]): Int = lengthCompare(that) + + /** Compares the length of this $coll to the size of another `Iterable`. + * + * @param that the `Iterable` whose size is compared with this $coll's length. + * @return A value `x` where + * {{{ + * x < 0 if this.length < that.size + * x == 0 if this.length == that.size + * x > 0 if this.length > that.size + * }}} + * The method as implemented here does not call `length` or `size` directly; its running time + * is `O(this.length min that.size)` instead of `O(this.length + that.size)`. + * The method should be overridden if computing `size` is cheap and `knownSize` returns `-1`. + */ + def lengthCompare(that: Iterable[_]): Int = super.sizeCompare(that) + + /** Returns a value class containing operations for comparing the length of this $coll to a test value. + * + * These operations are implemented in terms of [[lengthCompare(Int) `lengthCompare(Int)`]], and + * allow the following more readable usages: + * + * {{{ + * this.lengthIs < len // this.lengthCompare(len) < 0 + * this.lengthIs <= len // this.lengthCompare(len) <= 0 + * this.lengthIs == len // this.lengthCompare(len) == 0 + * this.lengthIs != len // this.lengthCompare(len) != 0 + * this.lengthIs >= len // this.lengthCompare(len) >= 0 + * this.lengthIs > len // this.lengthCompare(len) > 0 + * }}} + */ + @inline final def lengthIs: IterableOps.SizeCompareOps = new IterableOps.SizeCompareOps(this) + + override def isEmpty: Boolean = lengthCompare(0) == 0 + + /** Tests whether the elements of this collection are the same (and in the same order) + * as those of `that`. + */ + def sameElements[B >: A](that: IterableOnce[B]): Boolean = { + val thisKnownSize = knownSize + val knownSizeDifference = thisKnownSize != -1 && { + val thatKnownSize = that.knownSize + thatKnownSize != -1 && thisKnownSize != thatKnownSize + } + !knownSizeDifference && iterator.sameElements(that) + } + + /** Tests whether every element of this $coll relates to the + * corresponding element of another sequence by satisfying a test predicate. + * + * @param that the other sequence + * @param p the test predicate, which relates elements from both sequences + * @tparam B the type of the elements of `that` + * @return `true` if both sequences have the same length and + * `p(x, y)` is `true` for all corresponding elements `x` of this $coll + * and `y` of `that`, otherwise `false`. + */ + def corresponds[B](that: Seq[B])(p: (A, B) => Boolean): Boolean = { + val i = iterator + val j = that.iterator + while (i.hasNext && j.hasNext) + if (!p(i.next(), j.next())) + return false + !i.hasNext && !j.hasNext + } + + /** Computes the multiset difference between this $coll and another sequence. + * + * @param that the sequence of elements to remove + * @return a new $coll which contains all elements of this $coll + * except some of occurrences of elements that also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form + * part of the result, but any following occurrences will. + */ + def diff[B >: A](that: Seq[B]): C = { + val occ = occCounts(that) + fromSpecific(iterator.filter { x => + var include = false + occ.updateWith(x) { + case None => { + include = true + None + } + case Some(1) => None + case Some(n) => Some(n - 1) + } + include + }) + } + + /** Computes the multiset intersection between this $coll and another sequence. + * + * @param that the sequence of elements to intersect with. + * @return a new $coll which contains all elements of this $coll + * which also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained + * in the result, but any following occurrences will be omitted. + */ + def intersect[B >: A](that: Seq[B]): C = { + val occ = occCounts(that) + fromSpecific(iterator.filter { x => + var include = true + occ.updateWith(x) { + case None => { + include = false + None + } + case Some(1) => None + case Some(n) => Some(n - 1) + } + include + }) + } + + /** Produces a new $coll where a slice of elements in this $coll is replaced by another sequence. + * + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original $coll appends the patch to the end. + * If the `replaced` count would exceed the available elements, the difference in excess is ignored. + * + * @param from the index of the first replaced element + * @param other the replacement sequence + * @param replaced the number of elements to drop in the original $coll + * @tparam B the element type of the returned $coll. + * @return a new $coll consisting of all elements of this $coll + * except that `replaced` elements starting from `from` are replaced + * by all the elements of `other`. + */ + def patch[B >: A](from: Int, other: IterableOnce[B], replaced: Int): CC[B] = + iterableFactory.from(new View.Patched(this, from, other, replaced)) + + /** A copy of this $coll with one single replaced element. + * @param index the position of the replacement + * @param elem the replacing element + * @tparam B the element type of the returned $coll. + * @return a new $coll which is a copy of this $coll with the element at position `index` replaced by `elem`. + * @throws IndexOutOfBoundsException if `index` does not satisfy `0 <= index < length`. In case of a + * lazy collection this exception may be thrown at a later time or not at + * all (if the end of the collection is never evaluated). + */ + def updated[B >: A](index: Int, elem: B): CC[B] = { + if(index < 0) throw new IndexOutOfBoundsException(index.toString) + val k = knownSize + if(k >= 0 && index >= k) throw new IndexOutOfBoundsException(index.toString) + iterableFactory.from(new View.Updated(this, index, elem)) + } + + protected[collection] def occCounts[B](sq: Seq[B]): mutable.Map[B, Int] = { + val occ = new mutable.HashMap[B, Int]() + for (y <- sq) occ.updateWith(y) { + case None => Some(1) + case Some(n) => Some(n + 1) + } + occ + } + + /** Searches this sorted sequence for a specific element. If the sequence is an + * `IndexedSeq`, a binary search is used. Otherwise, a linear search is used. + * + * The sequence should be sorted with the same `Ordering` before calling; otherwise, + * the results are undefined. + * + * @see [[scala.collection.IndexedSeq]] + * @see [[scala.math.Ordering]] + * @see [[scala.collection.SeqOps]], method `sorted` + * + * @param elem the element to find. + * @param ord the ordering to be used to compare elements. + * + * @return a `Found` value containing the index corresponding to the element in the + * sequence, or the `InsertionPoint` where the element would be inserted if + * the element is not in the sequence. + */ + def search[B >: A](elem: B)(implicit ord: Ordering[B]): SearchResult = + linearSearch(view, elem, 0)(ord) + + /** Searches within an interval in this sorted sequence for a specific element. If this + * sequence is an `IndexedSeq`, a binary search is used. Otherwise, a linear search + * is used. + * + * The sequence should be sorted with the same `Ordering` before calling; otherwise, + * the results are undefined. + * + * @see [[scala.collection.IndexedSeq]] + * @see [[scala.math.Ordering]] + * @see [[scala.collection.SeqOps]], method `sorted` + * + * @param elem the element to find. + * @param from the index where the search starts. + * @param to the index following where the search ends. + * @param ord the ordering to be used to compare elements. + * + * @return a `Found` value containing the index corresponding to the element in the + * sequence, or the `InsertionPoint` where the element would be inserted if + * the element is not in the sequence. + * + * @note if `to <= from`, the search space is empty, and an `InsertionPoint` at `from` + * is returned + */ + def search[B >: A](elem: B, from: Int, to: Int) (implicit ord: Ordering[B]): SearchResult = + linearSearch(view.slice(from, to), elem, math.max(0, from))(ord) + + private[this] def linearSearch[B >: A](c: View[A], elem: B, offset: Int) + (implicit ord: Ordering[B]): SearchResult = { + var idx = offset + val it = c.iterator + while (it.hasNext) { + val cur = it.next() + if (ord.equiv(elem, cur)) return Found(idx) + else if (ord.lt(elem, cur)) return InsertionPoint(idx) + idx += 1 + } + InsertionPoint(idx) + } +} + +object SeqOps { + + // KMP search utilities + + /** A KMP implementation, based on the undoubtedly reliable wikipedia entry. + * Note: I made this private to keep it from entering the API. That can be reviewed. + * + * @param S Sequence that may contain target + * @param m0 First index of S to consider + * @param m1 Last index of S to consider (exclusive) + * @param W Target sequence + * @param n0 First index of W to match + * @param n1 Last index of W to match (exclusive) + * @param forward Direction of search (from beginning==true, from end==false) + * @return Index of start of sequence if found, -1 if not (relative to beginning of S, not m0). + */ + private def kmpSearch[B](S: scala.collection.Seq[B], m0: Int, m1: Int, W: scala.collection.Seq[B], n0: Int, n1: Int, forward: Boolean): Int = { + // Check for redundant case when target has single valid element + def clipR(x: Int, y: Int) = if (x < y) x else -1 + def clipL(x: Int, y: Int) = if (x > y) x else -1 + + if (n1 == n0+1) { + if (forward) + clipR(S.indexOf(W(n0), m0), m1) + else + clipL(S.lastIndexOf(W(n0), m1-1), m0-1) + } + + // Check for redundant case when both sequences are same size + else if (m1-m0 == n1-n0) { + // Accepting a little slowness for the uncommon case. + if (S.iterator.slice(m0, m1).sameElements(W.iterator.slice(n0, n1))) m0 + else -1 + } + // Now we know we actually need KMP search, so do it + else S match { + case xs: scala.collection.IndexedSeq[_] => + // We can index into S directly; it should be adequately fast + val Wopt = kmpOptimizeWord(W, n0, n1, forward) + val T = kmpJumpTable(Wopt, n1-n0) + var i, m = 0 + val zero = if (forward) m0 else m1-1 + val delta = if (forward) 1 else -1 + while (i+m < m1-m0) { + if (Wopt(i) == S(zero+delta*(i+m))) { + i += 1 + if (i == n1-n0) return (if (forward) m+m0 else m1-m-i) + } + else { + val ti = T(i) + m += i - ti + if (i > 0) i = ti + } + } + -1 + case _ => + // We had better not index into S directly! + val iter = S.iterator.drop(m0) + val Wopt = kmpOptimizeWord(W, n0, n1, forward = true) + val T = kmpJumpTable(Wopt, n1-n0) + val cache = new Array[AnyRef](n1-n0) // Ring buffer--need a quick way to do a look-behind + var largest = 0 + var i, m = 0 + var answer = -1 + while (m+m0+n1-n0 <= m1) { + while (i+m >= largest) { + cache(largest%(n1-n0)) = iter.next().asInstanceOf[AnyRef] + largest += 1 + } + if (Wopt(i) == cache((i+m)%(n1-n0)).asInstanceOf[B]) { + i += 1 + if (i == n1-n0) { + if (forward) return m+m0 + else { + i -= 1 + answer = m+m0 + val ti = T(i) + m += i - ti + if (i > 0) i = ti + } + } + } + else { + val ti = T(i) + m += i - ti + if (i > 0) i = ti + } + } + answer + } + } + + /** Make sure a target sequence has fast, correctly-ordered indexing for KMP. + * + * @param W The target sequence + * @param n0 The first element in the target sequence that we should use + * @param n1 The far end of the target sequence that we should use (exclusive) + * @return Target packed in an IndexedSeq (taken from iterator unless W already is an IndexedSeq) + */ + private def kmpOptimizeWord[B](W: scala.collection.Seq[B], n0: Int, n1: Int, forward: Boolean): IndexedSeqView[B] = W match { + case iso: IndexedSeq[B] => + // Already optimized for indexing--use original (or custom view of original) + if (forward && n0==0 && n1==W.length) iso.view + else if (forward) new AbstractIndexedSeqView[B] { + val length = n1 - n0 + def apply(x: Int) = iso(n0 + x) + } + else new AbstractIndexedSeqView[B] { + def length = n1 - n0 + def apply(x: Int) = iso(n1 - 1 - x) + } + case _ => + // W is probably bad at indexing. Pack in array (in correct orientation) + // Would be marginally faster to special-case each direction + new AbstractIndexedSeqView[B] { + private[this] val Warr = new Array[AnyRef](n1-n0) + private[this] val delta = if (forward) 1 else -1 + private[this] val done = if (forward) n1-n0 else -1 + val wit = W.iterator.drop(n0) + var i = if (forward) 0 else (n1-n0-1) + while (i != done) { + Warr(i) = wit.next().asInstanceOf[AnyRef] + i += delta + } + + val length = n1 - n0 + def apply(x: Int) = Warr(x).asInstanceOf[B] + } + } + + /** Make a jump table for KMP search. + * + * @param Wopt The target sequence + * @param wlen Just in case we're only IndexedSeq and not IndexedSeqOptimized + * @return KMP jump table for target sequence + */ + private def kmpJumpTable[B](Wopt: IndexedSeqView[B], wlen: Int) = { + val arr = new Array[Int](wlen) + var pos = 2 + var cnd = 0 + arr(0) = -1 + arr(1) = 0 + while (pos < wlen) { + if (Wopt(pos-1) == Wopt(cnd)) { + arr(pos) = cnd + 1 + pos += 1 + cnd += 1 + } + else if (cnd > 0) { + cnd = arr(cnd) + } + else { + arr(pos) = 0 + pos += 1 + } + } + arr + } +} + +/** Explicit instantiation of the `Seq` trait to reduce class file size in subclasses. */ +abstract class AbstractSeq[+A] extends AbstractIterable[A] with Seq[A] diff --git a/library/src/scala/collection/SeqMap.scala b/library/src/scala/collection/SeqMap.scala new file mode 100644 index 000000000000..f2b65dfbfb6f --- /dev/null +++ b/library/src/scala/collection/SeqMap.scala @@ -0,0 +1,40 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.annotation.nowarn + +/** + * A generic trait for ordered maps. Concrete classes have to provide + * functionality for the abstract methods in `SeqMap`. + * + * Note that when checking for equality [[SeqMap]] does not take into account + * ordering. + * + * @tparam K the type of the keys contained in this linked map. + * @tparam V the type of the values associated with the keys in this linked map. + * @define coll immutable seq map + * @define Coll `immutable.SeqMap` + */ + +trait SeqMap[K, +V] extends Map[K, V] + with MapOps[K, V, SeqMap, SeqMap[K, V]] + with MapFactoryDefaults[K, V, SeqMap, Iterable] { + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "SeqMap" + + override def mapFactory: MapFactory[SeqMap] = SeqMap +} + +object SeqMap extends MapFactory.Delegate[immutable.SeqMap](immutable.SeqMap) + diff --git a/library/src/scala/collection/SeqView.scala b/library/src/scala/collection/SeqView.scala new file mode 100644 index 000000000000..a45797892220 --- /dev/null +++ b/library/src/scala/collection/SeqView.scala @@ -0,0 +1,214 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.nowarn +import scala.collection.generic.CommonErrors + + +trait SeqView[+A] extends SeqOps[A, View, View[A]] with View[A] { + override def view: SeqView[A] = this + + override def map[B](f: A => B): SeqView[B] = new SeqView.Map(this, f) + override def appended[B >: A](elem: B): SeqView[B] = new SeqView.Appended(this, elem) + override def prepended[B >: A](elem: B): SeqView[B] = new SeqView.Prepended(elem, this) + override def reverse: SeqView[A] = new SeqView.Reverse(this) + override def take(n: Int): SeqView[A] = new SeqView.Take(this, n) + override def drop(n: Int): SeqView[A] = new SeqView.Drop(this, n) + override def takeRight(n: Int): SeqView[A] = new SeqView.TakeRight(this, n) + override def dropRight(n: Int): SeqView[A] = new SeqView.DropRight(this, n) + override def tapEach[U](f: A => U): SeqView[A] = new SeqView.Map(this, { (a: A) => f(a); a }) + + def concat[B >: A](suffix: SeqView.SomeSeqOps[B]): SeqView[B] = new SeqView.Concat(this, suffix) + def appendedAll[B >: A](suffix: SeqView.SomeSeqOps[B]): SeqView[B] = new SeqView.Concat(this, suffix) + def prependedAll[B >: A](prefix: SeqView.SomeSeqOps[B]): SeqView[B] = new SeqView.Concat(prefix, this) + + override def sorted[B >: A](implicit ord: Ordering[B]): SeqView[A] = new SeqView.Sorted(this, ord) + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "SeqView" +} + +object SeqView { + + /** A `SeqOps` whose collection type and collection type constructor are unknown */ + private type SomeSeqOps[+A] = SeqOps[A, AnyConstr, _] + + /** A view that doesn’t apply any transformation to an underlying sequence */ + @SerialVersionUID(3L) + class Id[+A](underlying: SomeSeqOps[A]) extends AbstractSeqView[A] { + def apply(idx: Int): A = underlying.apply(idx) + def length: Int = underlying.length + def iterator: Iterator[A] = underlying.iterator + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + @SerialVersionUID(3L) + class Map[+A, +B](underlying: SomeSeqOps[A], f: A => B) extends View.Map[A, B](underlying, f) with SeqView[B] { + def apply(idx: Int): B = f(underlying(idx)) + def length: Int = underlying.length + } + + @SerialVersionUID(3L) + class Appended[+A](underlying: SomeSeqOps[A], elem: A) extends View.Appended(underlying, elem) with SeqView[A] { + def apply(idx: Int): A = if (idx == underlying.length) elem else underlying(idx) + def length: Int = underlying.length + 1 + } + + @SerialVersionUID(3L) + class Prepended[+A](elem: A, underlying: SomeSeqOps[A]) extends View.Prepended(elem, underlying) with SeqView[A] { + def apply(idx: Int): A = if (idx == 0) elem else underlying(idx - 1) + def length: Int = underlying.length + 1 + } + + @SerialVersionUID(3L) + class Concat[A](prefix: SomeSeqOps[A], suffix: SomeSeqOps[A]) extends View.Concat[A](prefix, suffix) with SeqView[A] { + def apply(idx: Int): A = { + val l = prefix.length + if (idx < l) prefix(idx) else suffix(idx - l) + } + def length: Int = prefix.length + suffix.length + } + + @SerialVersionUID(3L) + class Reverse[A](underlying: SomeSeqOps[A]) extends AbstractSeqView[A] { + def apply(i: Int) = underlying.apply(size - 1 - i) + def length = underlying.size + def iterator: Iterator[A] = underlying.reverseIterator + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + @SerialVersionUID(3L) + class Take[+A](underlying: SomeSeqOps[A], n: Int) extends View.Take(underlying, n) with SeqView[A] { + def apply(idx: Int): A = if (idx < n) { + underlying(idx) + } else { + throw ( + if (underlying.knownSize >= 0) CommonErrors.indexOutOfBounds(index = idx, max = knownSize - 1) + else CommonErrors.indexOutOfBounds(index = idx) + ) + } + def length: Int = underlying.length min normN + } + + @SerialVersionUID(3L) + class TakeRight[+A](underlying: SomeSeqOps[A], n: Int) extends View.TakeRight(underlying, n) with SeqView[A] { + private[this] val delta = (underlying.size - (n max 0)) max 0 + def length = underlying.size - delta + @throws[IndexOutOfBoundsException] + def apply(i: Int) = underlying.apply(i + delta) + } + + @SerialVersionUID(3L) + class Drop[A](underlying: SomeSeqOps[A], n: Int) extends View.Drop[A](underlying, n) with SeqView[A] { + def length = (underlying.size - normN) max 0 + @throws[IndexOutOfBoundsException] + def apply(i: Int) = underlying.apply(i + normN) + override def drop(n: Int): SeqView[A] = new Drop(underlying, this.n + n) + } + + @SerialVersionUID(3L) + class DropRight[A](underlying: SomeSeqOps[A], n: Int) extends View.DropRight[A](underlying, n) with SeqView[A] { + private[this] val len = (underlying.size - (n max 0)) max 0 + def length = len + @throws[IndexOutOfBoundsException] + def apply(i: Int) = underlying.apply(i) + } + + @SerialVersionUID(3L) + class Sorted[A, B >: A] private (private[this] var underlying: SomeSeqOps[A], + private[this] val len: Int, + ord: Ordering[B]) + extends SeqView[A] { + outer => + + // force evaluation immediately by calling `length` so infinite collections + // hang on `sorted`/`sortWith`/`sortBy` rather than on arbitrary method calls + def this(underlying: SomeSeqOps[A], ord: Ordering[B]) = this(underlying, underlying.length, ord) + + @SerialVersionUID(3L) + private[this] class ReverseSorted extends SeqView[A] { + private[this] lazy val _reversed = new SeqView.Reverse(_sorted) + + def apply(i: Int): A = _reversed.apply(i) + def length: Int = len + def iterator: Iterator[A] = Iterator.empty ++ _reversed.iterator // very lazy + override def knownSize: Int = len + override def isEmpty: Boolean = len == 0 + override def to[C1](factory: Factory[A, C1]): C1 = _reversed.to(factory) + override def reverse: SeqView[A] = outer + override protected def reversed: Iterable[A] = outer + + override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A] = + if (ord1 == Sorted.this.ord) outer + else if (ord1.isReverseOf(Sorted.this.ord)) this + else new Sorted(elems, len, ord1) + } + + @volatile private[this] var evaluated = false + + private[this] lazy val _sorted: Seq[A] = { + val res = { + val len = this.len + if (len == 0) Nil + else if (len == 1) List(underlying.head) + else { + val arr = new Array[Any](len) // Array[Any] =:= Array[AnyRef] + @annotation.unused val copied = underlying.copyToArray(arr) + //assert(copied == len) + java.util.Arrays.sort(arr.asInstanceOf[Array[AnyRef]], ord.asInstanceOf[Ordering[AnyRef]]) + // casting the Array[AnyRef] to Array[A] and creating an ArraySeq from it + // is safe because: + // - the ArraySeq is immutable, and items that are not of type A + // cannot be added to it + // - we know it only contains items of type A (and if this collection + // contains items of another type, we'd get a CCE anyway) + // - the cast doesn't actually do anything in the runtime because the + // type of A is not known and Array[_] is Array[AnyRef] + immutable.ArraySeq.unsafeWrapArray(arr.asInstanceOf[Array[A]]) + } + } + evaluated = true + underlying = null + res + } + + private[this] def elems: SomeSeqOps[A] = { + val orig = underlying + if (evaluated) _sorted else orig + } + + def apply(i: Int): A = _sorted.apply(i) + def length: Int = len + def iterator: Iterator[A] = Iterator.empty ++ _sorted.iterator // very lazy + override def knownSize: Int = len + override def isEmpty: Boolean = len == 0 + override def to[C1](factory: Factory[A, C1]): C1 = _sorted.to(factory) + override def reverse: SeqView[A] = new ReverseSorted + // we know `_sorted` is either tiny or has efficient random access, + // so this is acceptable for `reversed` + override protected def reversed: Iterable[A] = new ReverseSorted + + override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A] = + if (ord1 == this.ord) this + else if (ord1.isReverseOf(this.ord)) reverse + else new Sorted(elems, len, ord1) + } +} + +/** Explicit instantiation of the `SeqView` trait to reduce class file size in subclasses. */ +@SerialVersionUID(3L) +abstract class AbstractSeqView[+A] extends AbstractView[A] with SeqView[A] diff --git a/library/src/scala/collection/Set.scala b/library/src/scala/collection/Set.scala new file mode 100644 index 000000000000..f682e20861ab --- /dev/null +++ b/library/src/scala/collection/Set.scala @@ -0,0 +1,269 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.util.hashing.MurmurHash3 +import java.lang.String + +import scala.annotation.nowarn + +/** Base trait for set collections. + */ +trait Set[A] + extends Iterable[A] + with SetOps[A, Set, Set[A]] + with Equals + with IterableFactoryDefaults[A, Set] { + + def canEqual(that: Any) = true + + /** + * Equality of sets is implemented using the lookup method [[contains]]. This method returns `true` if + * - the argument `that` is a `Set`, + * - the two sets have the same [[size]], and + * - for every `element` this set, `other.contains(element) == true`. + * + * The implementation of `equals` checks the [[canEqual]] method, so subclasses of `Set` can narrow down the equality + * to specific set types. The `Set` implementations in the standard library can all be compared, their `canEqual` + * methods return `true`. + * + * Note: The `equals` method only respects the equality laws (symmetry, transitivity) if the two sets use the same + * element equivalence function in their lookup operation. For example, the element equivalence operation in a + * [[scala.collection.immutable.TreeSet]] is defined by its ordering. Comparing a `TreeSet` with a `HashSet` leads + * to unexpected results if `ordering.equiv(e1, e2)` (used for lookup in `TreeSet`) is different from `e1 == e2` + * (used for lookup in `HashSet`). + * + * {{{ + * scala> import scala.collection.immutable._ + * scala> val ord: Ordering[String] = _ compareToIgnoreCase _ + * + * scala> TreeSet("A")(ord) == HashSet("a") + * val res0: Boolean = false + * + * scala> HashSet("a") == TreeSet("A")(ord) + * val res1: Boolean = true + * }}} + * + * + * @param that The set to which this set is compared + * @return `true` if the two sets are equal according to the description + */ + override def equals(that: Any): Boolean = + (this eq that.asInstanceOf[AnyRef]) || (that match { + case set: Set[A @unchecked] if set.canEqual(this) => + (this.size == set.size) && { + try this.subsetOf(set) + catch { case _: ClassCastException => false } // PR #9565 / scala/bug#12228 + } + case _ => + false + }) + + override def hashCode(): Int = MurmurHash3.setHash(this) + + override def iterableFactory: IterableFactory[Set] = Set + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "Set" + + override def toString(): String = super[Iterable].toString() // Because `Function1` overrides `toString` too +} + +/** Base trait for set operations + * + * @define coll set + * @define Coll `Set` + */ +trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]] + extends IterableOps[A, CC, C] + with (A => Boolean) { + + def contains(elem: A): Boolean + + /** Tests if some element is contained in this set. + * + * This method is equivalent to `contains`. It allows sets to be interpreted as predicates. + * @param elem the element to test for membership. + * @return `true` if `elem` is contained in this set, `false` otherwise. + */ + @`inline` final def apply(elem: A): Boolean = this.contains(elem) + + /** Tests whether this set is a subset of another set. + * + * @param that the set to test. + * @return `true` if this set is a subset of `that`, i.e. if + * every element of this set is also an element of `that`. + */ + def subsetOf(that: Set[A]): Boolean = this.forall(that) + + /** An iterator over all subsets of this set of the given size. + * If the requested size is impossible, an empty iterator is returned. + * + * @param len the size of the subsets. + * @return the iterator. + */ + def subsets(len: Int): Iterator[C] = { + if (len < 0 || len > size) Iterator.empty + else new SubsetsItr(this.to(IndexedSeq), len) + } + + /** An iterator over all subsets of this set. + * + * @return the iterator. + */ + def subsets(): Iterator[C] = new AbstractIterator[C] { + private[this] val elms = SetOps.this.to(IndexedSeq) + private[this] var len = 0 + private[this] var itr: Iterator[C] = Iterator.empty + + def hasNext = len <= elms.size || itr.hasNext + def next() = { + if (!itr.hasNext) { + if (len > elms.size) Iterator.empty.next() + else { + itr = new SubsetsItr(elms, len) + len += 1 + } + } + + itr.next() + } + } + + /** An Iterator including all subsets containing exactly len elements. + * If the elements in 'This' type is ordered, then the subsets will also be in the same order. + * ListSet(1,2,3).subsets => {{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}} + * + * $willForceEvaluation + * + */ + private class SubsetsItr(elms: IndexedSeq[A], len: Int) extends AbstractIterator[C] { + private[this] val idxs = Array.range(0, len+1) + private[this] var _hasNext = true + idxs(len) = elms.size + + def hasNext = _hasNext + @throws[NoSuchElementException] + def next(): C = { + if (!hasNext) Iterator.empty.next() + + val buf = newSpecificBuilder + idxs.slice(0, len) foreach (idx => buf += elms(idx)) + val result = buf.result() + + var i = len - 1 + while (i >= 0 && idxs(i) == idxs(i+1)-1) i -= 1 + + if (i < 0) _hasNext = false + else { + idxs(i) += 1 + for (j <- (i+1) until len) + idxs(j) = idxs(j-1) + 1 + } + + result + } + } + + /** Computes the intersection between this set and another set. + * + * @param that the set to intersect with. + * @return a new set consisting of all elements that are both in this + * set and in the given set `that`. + */ + def intersect(that: Set[A]): C = this.filter(that) + + /** Alias for `intersect` */ + @`inline` final def & (that: Set[A]): C = intersect(that) + + /** Computes the difference of this set and another set. + * + * @param that the set of elements to exclude. + * @return a set containing those elements of this + * set that are not also contained in the given set `that`. + */ + def diff(that: Set[A]): C + + /** Alias for `diff` */ + @`inline` final def &~ (that: Set[A]): C = this diff that + + @deprecated("Consider requiring an immutable Set", "2.13.0") + def -- (that: IterableOnce[A]): C = { + val toRemove = that.iterator.to(immutable.Set) + fromSpecific(view.filterNot(toRemove)) + } + + @deprecated("Consider requiring an immutable Set or fall back to Set.diff", "2.13.0") + def - (elem: A): C = diff(Set(elem)) + + @deprecated("Use &- with an explicit collection argument instead of - with varargs", "2.13.0") + def - (elem1: A, elem2: A, elems: A*): C = diff(elems.toSet + elem1 + elem2) + + /** Creates a new $coll by adding all elements contained in another collection to this $coll, omitting duplicates. + * + * This method takes a collection of elements and adds all elements, omitting duplicates, into $coll. + * + * Example: + * {{{ + * scala> val a = Set(1, 2) concat Set(2, 3) + * a: scala.collection.immutable.Set[Int] = Set(1, 2, 3) + * }}} + * + * @param that the collection containing the elements to add. + * @return a new $coll with the given elements added, omitting duplicates. + */ + def concat(that: collection.IterableOnce[A]): C = this match { + case optimizedSet @ (_ : scala.collection.immutable.Set.Set1[A] | _: scala.collection.immutable.Set.Set2[A] | _: scala.collection.immutable.Set.Set3[A] | _: scala.collection.immutable.Set.Set4[A]) => + // StrictOptimizedSetOps optimization of concat (these Sets cannot extend StrictOptimizedSetOps because of binary-incompatible return type; cf. PR #10036) + var result = optimizedSet.asInstanceOf[scala.collection.immutable.SetOps[A, scala.collection.immutable.Set, scala.collection.immutable.Set[A]]] + val it = that.iterator + while (it.hasNext) result = result + it.next() + result.asInstanceOf[C] + case _ => fromSpecific(that match { + case that: collection.Iterable[A] => new View.Concat(this, that) + case _ => iterator.concat(that.iterator) + }) + } + + @deprecated("Consider requiring an immutable Set or fall back to Set.union", "2.13.0") + def + (elem: A): C = fromSpecific(new View.Appended(this, elem)) + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + def + (elem1: A, elem2: A, elems: A*): C = fromSpecific(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems)) + + /** Alias for `concat` */ + @`inline` final def ++ (that: collection.IterableOnce[A]): C = concat(that) + + /** Computes the union between of set and another set. + * + * @param that the set to form the union with. + * @return a new set consisting of all elements that are in this + * set or in the given set `that`. + */ + @`inline` final def union(that: Set[A]): C = concat(that) + + /** Alias for `union` */ + @`inline` final def | (that: Set[A]): C = concat(that) +} + +/** + * $factoryInfo + * @define coll set + * @define Coll `Set` + */ +@SerialVersionUID(3L) +object Set extends IterableFactory.Delegate[Set](immutable.Set) + +/** Explicit instantiation of the `Set` trait to reduce class file size in subclasses. */ +abstract class AbstractSet[A] extends AbstractIterable[A] with Set[A] diff --git a/library/src/scala/collection/SortedMap.scala b/library/src/scala/collection/SortedMap.scala new file mode 100644 index 000000000000..d2ccb9e38aa9 --- /dev/null +++ b/library/src/scala/collection/SortedMap.scala @@ -0,0 +1,220 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.{implicitNotFound, nowarn} + +/** A Map whose keys are sorted according to a [[scala.math.Ordering]]*/ +trait SortedMap[K, +V] + extends Map[K, V] + with SortedMapOps[K, V, SortedMap, SortedMap[K, V]] + with SortedMapFactoryDefaults[K, V, SortedMap, Iterable, Map]{ + + def unsorted: Map[K, V] = this + + def sortedMapFactory: SortedMapFactory[SortedMap] = SortedMap + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "SortedMap" + + override def equals(that: Any): Boolean = that match { + case _ if this eq that.asInstanceOf[AnyRef] => true + case sm: SortedMap[K @unchecked, _] if sm.ordering == this.ordering => + (sm canEqual this) && + (this.size == sm.size) && { + val i1 = this.iterator + val i2 = sm.iterator + var allEqual = true + while (allEqual && i1.hasNext) { + val kv1 = i1.next() + val kv2 = i2.next() + allEqual = ordering.equiv(kv1._1, kv2._1) && kv1._2 == kv2._2 + } + allEqual + } + case _ => super.equals(that) + } +} + +trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]] + extends MapOps[K, V, Map, C] + with SortedOps[K, C] { + + /** The companion object of this sorted map, providing various factory methods. + * + * @note When implementing a custom collection type and refining `CC` to the new type, this + * method needs to be overridden to return a factory for the new type (the compiler will + * issue an error otherwise). + */ + def sortedMapFactory: SortedMapFactory[CC] + + /** Similar to `mapFromIterable`, but returns a SortedMap collection type. + * Note that the return type is now `CC[K2, V2]`. + */ + @`inline` protected final def sortedMapFromIterable[K2, V2](it: Iterable[(K2, V2)])(implicit ordering: Ordering[K2]): CC[K2, V2] = sortedMapFactory.from(it) + + def unsorted: Map[K, V] + + /** + * Creates an iterator over all the key/value pairs + * contained in this map having a key greater than or + * equal to `start` according to the ordering of + * this map. x.iteratorFrom(y) is equivalent + * to but often more efficient than x.from(y).iterator. + * + * @param start The lower bound (inclusive) + * on the keys to be returned + */ + def iteratorFrom(start: K): Iterator[(K, V)] + + /** + * Creates an iterator over all the keys(or elements) contained in this + * collection greater than or equal to `start` + * according to the ordering of this collection. x.keysIteratorFrom(y) + * is equivalent to but often more efficient than + * x.from(y).keysIterator. + * + * @param start The lower bound (inclusive) + * on the keys to be returned + */ + def keysIteratorFrom(start: K): Iterator[K] + + /** + * Creates an iterator over all the values contained in this + * map that are associated with a key greater than or equal to `start` + * according to the ordering of this map. x.valuesIteratorFrom(y) is + * equivalent to but often more efficient than + * x.from(y).valuesIterator. + * + * @param start The lower bound (inclusive) + * on the keys to be returned + */ + def valuesIteratorFrom(start: K): Iterator[V] = iteratorFrom(start).map(_._2) + + def firstKey: K = head._1 + def lastKey: K = last._1 + + /** Find the element with smallest key larger than or equal to a given key. + * @param key The given key. + * @return `None` if there is no such node. + */ + def minAfter(key: K): Option[(K, V)] = rangeFrom(key).headOption + + /** Find the element with largest key less than a given key. + * @param key The given key. + * @return `None` if there is no such node. + */ + def maxBefore(key: K): Option[(K, V)] = rangeUntil(key).lastOption + + def rangeTo(to: K): C = { + val i = keySet.rangeFrom(to).iterator + if (i.isEmpty) return coll + val next = i.next() + if (ordering.compare(next, to) == 0) + if (i.isEmpty) coll + else rangeUntil(i.next()) + else + rangeUntil(next) + } + + override def keySet: SortedSet[K] = new KeySortedSet + + /** The implementation class of the set returned by `keySet` */ + protected class KeySortedSet extends SortedSet[K] with GenKeySet with GenKeySortedSet { + def diff(that: Set[K]): SortedSet[K] = fromSpecific(view.filterNot(that)) + def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = { + val map = SortedMapOps.this.rangeImpl(from, until) + new map.KeySortedSet + } + } + + /** A generic trait that is reused by sorted keyset implementations */ + protected trait GenKeySortedSet extends GenKeySet { this: SortedSet[K] => + implicit def ordering: Ordering[K] = SortedMapOps.this.ordering + def iteratorFrom(start: K): Iterator[K] = SortedMapOps.this.keysIteratorFrom(start) + } + + // And finally, we add new overloads taking an ordering + /** Builds a new sorted map by applying a function to all elements of this $coll. + * + * @param f the function to apply to each element. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. + */ + def map[K2, V2](f: ((K, V)) => (K2, V2))(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + sortedMapFactory.from(new View.Map[(K, V), (K2, V2)](this, f)) + + /** Builds a new sorted map by applying a function to all elements of this $coll + * and using the elements of the resulting collections. + * + * @param f the function to apply to each element. + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + sortedMapFactory.from(new View.FlatMap(this, f)) + + /** Builds a new sorted map by applying a partial function to all elements of this $coll + * on which the function is defined. + * + * @param pf the partial function which filters and maps the $coll. + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ + def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + sortedMapFactory.from(new View.Collect(this, pf)) + + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): CC[K, V2] = sortedMapFactory.from(suffix match { + case it: Iterable[(K, V2)] => new View.Concat(this, it) + case _ => iterator.concat(suffix.iterator) + })(using ordering) + + /** Alias for `concat` */ + @`inline` override final def ++ [V2 >: V](xs: IterableOnce[(K, V2)]): CC[K, V2] = concat(xs) + + @deprecated("Consider requiring an immutable Map or fall back to Map.concat", "2.13.0") + override def + [V1 >: V](kv: (K, V1)): CC[K, V1] = sortedMapFactory.from(new View.Appended(this, kv))(using ordering) + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = sortedMapFactory.from(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems))(using ordering) +} + +object SortedMapOps { + private[collection] final val ordMsg = "No implicit Ordering[${K2}] found to build a SortedMap[${K2}, ${V2}]. You may want to upcast to a Map[${K}, ${V}] first by calling `unsorted`." + + /** Specializes `MapWithFilter` for sorted Map collections + * + * @define coll sorted map collection + */ + class WithFilter[K, +V, +IterableCC[_], +MapCC[X, Y] <: Map[X, Y], +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _]]( + self: SortedMapOps[K, V, CC, _] with MapOps[K, V, MapCC, _] with IterableOps[(K, V), IterableCC, _], + p: ((K, V)) => Boolean + ) extends MapOps.WithFilter[K, V, IterableCC, MapCC](self, p) { + + def map[K2 : Ordering, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = + self.sortedMapFactory.from(new View.Map(filtered, f)) + + def flatMap[K2 : Ordering, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = + self.sortedMapFactory.from(new View.FlatMap(filtered, f)) + + override def withFilter(q: ((K, V)) => Boolean): WithFilter[K, V, IterableCC, MapCC, CC] = + new WithFilter[K, V, IterableCC, MapCC, CC](self, (kv: (K, V)) => p(kv) && q(kv)) + + } + +} + +@SerialVersionUID(3L) +object SortedMap extends SortedMapFactory.Delegate[SortedMap](immutable.SortedMap) diff --git a/library/src/scala/collection/SortedOps.scala b/library/src/scala/collection/SortedOps.scala new file mode 100644 index 000000000000..bd034fbf14d6 --- /dev/null +++ b/library/src/scala/collection/SortedOps.scala @@ -0,0 +1,90 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + + +/** Base trait for sorted collections */ +trait SortedOps[A, +C] { + + def ordering: Ordering[A] + + /** Returns the first key of the collection. */ + def firstKey: A + + /** Returns the last key of the collection. */ + def lastKey: A + + /** Comparison function that orders keys. */ + @deprecated("Use ordering.compare instead", "2.13.0") + @deprecatedOverriding("Use ordering.compare instead", "2.13.0") + @inline def compare(k0: A, k1: A): Int = ordering.compare(k0, k1) + + /** Creates a ranged projection of this collection. Any mutations in the + * ranged projection will update this collection and vice versa. + * + * Note: keys are not guaranteed to be consistent between this collection + * and the projection. This is the case for buffers where indexing is + * relative to the projection. + * + * @param from The lower-bound (inclusive) of the ranged projection. + * `None` if there is no lower bound. + * @param until The upper-bound (exclusive) of the ranged projection. + * `None` if there is no upper bound. + */ + def rangeImpl(from: Option[A], until: Option[A]): C + + /** Creates a ranged projection of this collection with both a lower-bound + * and an upper-bound. + * + * @param from The lower-bound (inclusive) of the ranged projection. + * @param until The upper-bound (exclusive) of the ranged projection. + */ + def range(from: A, until: A): C = rangeImpl(Some(from), Some(until)) + + /** Creates a ranged projection of this collection with no upper-bound. + * + * @param from The lower-bound (inclusive) of the ranged projection. + */ + @deprecated("Use rangeFrom", "2.13.0") + final def from(from: A): C = rangeFrom(from) + + /** Creates a ranged projection of this collection with no upper-bound. + * + * @param from The lower-bound (inclusive) of the ranged projection. + */ + def rangeFrom(from: A): C = rangeImpl(Some(from), None) + + /** Creates a ranged projection of this collection with no lower-bound. + * + * @param until The upper-bound (exclusive) of the ranged projection. + */ + @deprecated("Use rangeUntil", "2.13.0") + final def until(until: A): C = rangeUntil(until) + + /** Creates a ranged projection of this collection with no lower-bound. + * + * @param until The upper-bound (exclusive) of the ranged projection. + */ + def rangeUntil(until: A): C = rangeImpl(None, Some(until)) + + /** Create a range projection of this collection with no lower-bound. + * @param to The upper-bound (inclusive) of the ranged projection. + */ + @deprecated("Use rangeTo", "2.13.0") + final def to(to: A): C = rangeTo(to) + + /** Create a range projection of this collection with no lower-bound. + * @param to The upper-bound (inclusive) of the ranged projection. + */ + def rangeTo(to: A): C +} diff --git a/library/src/scala/collection/SortedSet.scala b/library/src/scala/collection/SortedSet.scala new file mode 100644 index 000000000000..7c655aad128a --- /dev/null +++ b/library/src/scala/collection/SortedSet.scala @@ -0,0 +1,189 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.annotation.{implicitNotFound, nowarn} +import scala.annotation.unchecked.uncheckedVariance + +/** Base type of sorted sets */ +trait SortedSet[A] extends Set[A] + with SortedSetOps[A, SortedSet, SortedSet[A]] + with SortedSetFactoryDefaults[A, SortedSet, Set] { + + def unsorted: Set[A] = this + + def sortedIterableFactory: SortedIterableFactory[SortedSet] = SortedSet + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "SortedSet" + + override def equals(that: Any): Boolean = that match { + case _ if this eq that.asInstanceOf[AnyRef] => true + case ss: SortedSet[A @unchecked] if ss.ordering == this.ordering => + (ss canEqual this) && + (this.size == ss.size) && { + val i1 = this.iterator + val i2 = ss.iterator + var allEqual = true + while (allEqual && i1.hasNext) + allEqual = ordering.equiv(i1.next(), i2.next()) + allEqual + } + case _ => + super.equals(that) + } + +} + +trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]] + extends SetOps[A, Set, C] + with SortedOps[A, C] { + + /** The companion object of this sorted set, providing various factory methods. + * + * @note When implementing a custom collection type and refining `CC` to the new type, this + * method needs to be overridden to return a factory for the new type (the compiler will + * issue an error otherwise). + */ + def sortedIterableFactory: SortedIterableFactory[CC] + + def unsorted: Set[A] + + /** + * Creates an iterator that contains all values from this collection + * greater than or equal to `start` according to the ordering of + * this collection. x.iteratorFrom(y) is equivalent to but will usually + * be more efficient than x.from(y).iterator + * + * @param start The lower-bound (inclusive) of the iterator + */ + def iteratorFrom(start: A): Iterator[A] + + @deprecated("Use `iteratorFrom` instead.", "2.13.0") + @`inline` def keysIteratorFrom(start: A): Iterator[A] = iteratorFrom(start) + + def firstKey: A = head + def lastKey: A = last + + /** Find the smallest element larger than or equal to a given key. + * @param key The given key. + * @return `None` if there is no such node. + */ + def minAfter(key: A): Option[A] = rangeFrom(key).headOption + + /** Find the largest element less than a given key. + * @param key The given key. + * @return `None` if there is no such node. + */ + def maxBefore(key: A): Option[A] = rangeUntil(key).lastOption + + override def min[B >: A](implicit ord: Ordering[B]): A = + if (isEmpty) throw new UnsupportedOperationException("empty.min") + else if (ord == ordering) head + else if (ord isReverseOf ordering) last + else super.min[B] // need the type annotation for it to infer the correct implicit + + override def max[B >: A](implicit ord: Ordering[B]): A = + if (isEmpty) throw new UnsupportedOperationException("empty.max") + else if (ord == ordering) last + else if (ord isReverseOf ordering) head + else super.max[B] // need the type annotation for it to infer the correct implicit + + def rangeTo(to: A): C = { + val i = rangeFrom(to).iterator + if (i.isEmpty) return coll + val next = i.next() + if (ordering.compare(next, to) == 0) + if (i.isEmpty) coll + else rangeUntil(i.next()) + else + rangeUntil(next) + } + + /** Builds a new sorted collection by applying a function to all elements of this $coll. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. + */ + def map[B](f: A => B)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + sortedIterableFactory.from(new View.Map(this, f)) + + /** Builds a new sorted collection by applying a function to all elements of this $coll + * and using the elements of the resulting collections. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ + def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + sortedIterableFactory.from(new View.FlatMap(this, f)) + + /** Returns a $coll formed from this $coll and another iterable collection + * by combining corresponding elements in pairs. + * If one of the two collections is longer than the other, its remaining elements are ignored. + * + * @param that The iterable providing the second half of each result pair + * @tparam B the type of the second half of the returned pairs + * @return a new $coll containing pairs consisting of corresponding elements of this $coll and `that`. + * The length of the returned collection is the minimum of the lengths of this $coll and `that`. + */ + def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = // sound bcs of VarianceNote + sortedIterableFactory.from(that match { + case that: Iterable[B] => new View.Zip(this, that) + case _ => iterator.zip(that) + }) + + /** Builds a new sorted collection by applying a partial function to all elements of this $coll + * on which the function is defined. + * + * @param pf the partial function which filters and maps the $coll. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ + def collect[B](pf: scala.PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + sortedIterableFactory.from(new View.Collect(this, pf)) +} + +object SortedSetOps { + private[collection] final val ordMsg = "No implicit Ordering[${B}] found to build a SortedSet[${B}]. You may want to upcast to a Set[${A}] first by calling `unsorted`." + private[collection] final val zipOrdMsg = "No implicit Ordering[${B}] found to build a SortedSet[(${A}, ${B})]. You may want to upcast to a Set[${A}] first by calling `unsorted`." + + /** Specialize `WithFilter` for sorted collections + * + * @define coll sorted collection + */ + class WithFilter[+A, +IterableCC[_], +CC[X] <: SortedSet[X]]( + self: SortedSetOps[A, CC, _] with IterableOps[A, IterableCC, _], + p: A => Boolean + ) extends IterableOps.WithFilter[A, IterableCC](self, p) { + + def map[B : Ordering](f: A => B): CC[B] = + self.sortedIterableFactory.from(new View.Map(filtered, f)) + + def flatMap[B : Ordering](f: A => IterableOnce[B]): CC[B] = + self.sortedIterableFactory.from(new View.FlatMap(filtered, f)) + + override def withFilter(q: A => Boolean): WithFilter[A, IterableCC, CC] = + new WithFilter[A, IterableCC, CC](self, (a: A) => p(a) && q(a)) + } + +} + +@SerialVersionUID(3L) +object SortedSet extends SortedIterableFactory.Delegate[SortedSet](immutable.SortedSet) + diff --git a/library/src/scala/collection/Stepper.scala b/library/src/scala/collection/Stepper.scala new file mode 100644 index 000000000000..f1355e8182c3 --- /dev/null +++ b/library/src/scala/collection/Stepper.scala @@ -0,0 +1,368 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import java.util.function.{Consumer, DoubleConsumer, IntConsumer, LongConsumer} +import java.util.{PrimitiveIterator, Spliterator, Iterator => JIterator} +import java.{lang => jl} + +import scala.collection.Stepper.EfficientSplit + +/** Steppers exist to enable creating Java streams over Scala collections, see + * [[scala.jdk.StreamConverters]]. Besides that use case, they allow iterating over collections + * holding unboxed primitives (e.g., `Array[Int]`) without boxing the elements. + * + * Steppers have an iterator-like interface with methods `hasStep` and `nextStep()`. The difference + * to iterators - and the reason `Stepper` is not a subtype of `Iterator` - is that there are + * hand-specialized variants of `Stepper` for `Int`, `Long` and `Double` ([[IntStepper]], etc.). + * These enable iterating over collections holding unboxed primitives (e.g., Arrays, + * [[scala.jdk.Accumulator]]s) without boxing the elements. + * + * The selection of primitive types (`Int`, `Long` and `Double`) matches the hand-specialized + * variants of Java Streams ([[java.util.stream.Stream]], [[java.util.stream.IntStream]], etc.) + * and the corresponding Java Spliterators ([[java.util.Spliterator]], [[java.util.Spliterator.OfInt]], etc.). + * + * Steppers can be converted to Scala Iterators, Java Iterators and Java Spliterators. Primitive + * Steppers are converted to the corresponding primitive Java Iterators and Spliterators. + * + * @tparam A the element type of the Stepper + */ +trait Stepper[@specialized(Double, Int, Long) +A] { + /** Check if there's an element available. */ + def hasStep: Boolean + + /** Return the next element and advance the stepper */ + def nextStep(): A + + /** Split this stepper, if applicable. The elements of the current Stepper are split up between + * the resulting Stepper and the current stepper. + * + * May return `null`, in which case the current Stepper yields the same elements as before. + * + * See method `trySplit` in [[java.util.Spliterator]]. + */ + def trySplit(): Stepper[A] + + /** Returns an estimate of the number of elements of this Stepper, or [[Long.MaxValue]]. See + * method `estimateSize` in [[java.util.Spliterator]]. + */ + def estimateSize: Long + + /** Returns a set of characteristics of this Stepper and its elements. See method + * `characteristics` in [[java.util.Spliterator]]. + */ + def characteristics: Int + + /** Returns a [[java.util.Spliterator]] corresponding to this Stepper. + * + * Note that the return type is `Spliterator[_]` instead of `Spliterator[A]` to allow returning + * a [[java.util.Spliterator.OfInt]] (which is a `Spliterator[Integer]`) in the subclass [[IntStepper]] + * (which is a `Stepper[Int]`). + */ + def spliterator[B >: A]: Spliterator[_] + + /** Returns a Java [[java.util.Iterator]] corresponding to this Stepper. + * + * Note that the return type is `Iterator[_]` instead of `Iterator[A]` to allow returning + * a [[java.util.PrimitiveIterator.OfInt]] (which is a `Iterator[Integer]`) in the subclass + * [[IntStepper]] (which is a `Stepper[Int]`). + */ + def javaIterator[B >: A]: JIterator[_] + + /** Returns an [[Iterator]] corresponding to this Stepper. Note that Iterators corresponding to + * primitive Steppers box the elements. + */ + def iterator: Iterator[A] = new AbstractIterator[A] { + def hasNext: Boolean = hasStep + def next(): A = nextStep() + } +} + +object Stepper { + /** A marker trait that indicates that a `Stepper` can call `trySplit` with at worst O(log N) time + * and space complexity, and that the division is likely to be reasonably even. Steppers marked + * with `EfficientSplit` can be converted to parallel streams with the `asJavaParStream` method + * defined in [[scala.jdk.StreamConverters]]. + */ + trait EfficientSplit + + private[collection] final def throwNSEE(): Nothing = throw new NoSuchElementException("Empty Stepper") + + /* These adapter classes can wrap an AnyStepper of a numeric type into a possibly widened primitive Stepper type. + * This provides a basis for more efficient stream processing on unboxed values provided that the original source + * of the data is boxed. In other cases native implementations of the primitive stepper types should be provided + * (see for example IntArrayStepper and WidenedByteArrayStepper). */ + + private[collection] class UnboxingDoubleStepper(st: AnyStepper[Double]) extends DoubleStepper { + def hasStep: Boolean = st.hasStep + def nextStep(): Double = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): DoubleStepper = { + val s = st.trySplit() + if (s == null) null else new UnboxingDoubleStepper(s) + } + } + + private[collection] class UnboxingIntStepper(st: AnyStepper[Int]) extends IntStepper { + def hasStep: Boolean = st.hasStep + def nextStep(): Int = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): IntStepper = { + val s = st.trySplit() + if (s == null) null else new UnboxingIntStepper(s) + } + } + + private[collection] class UnboxingLongStepper(st: AnyStepper[Long]) extends LongStepper { + def hasStep: Boolean = st.hasStep + def nextStep(): Long = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): LongStepper = { + val s = st.trySplit() + if (s == null) null else new UnboxingLongStepper(s) + } + } + + private[collection] class UnboxingByteStepper(st: AnyStepper[Byte]) extends IntStepper { + def hasStep: Boolean = st.hasStep + def nextStep(): Int = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): IntStepper = { + val s = st.trySplit() + if (s == null) null else new UnboxingByteStepper(s) + } + } + + private[collection] class UnboxingCharStepper(st: AnyStepper[Char]) extends IntStepper { + def hasStep: Boolean = st.hasStep + def nextStep(): Int = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): IntStepper = { + val s = st.trySplit() + if (s == null) null else new UnboxingCharStepper(s) + } + } + + private[collection] class UnboxingShortStepper(st: AnyStepper[Short]) extends IntStepper { + def hasStep: Boolean = st.hasStep + def nextStep(): Int = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): IntStepper = { + val s = st.trySplit() + if (s == null) null else new UnboxingShortStepper(s) + } + } + + private[collection] class UnboxingFloatStepper(st: AnyStepper[Float]) extends DoubleStepper { + def hasStep: Boolean = st.hasStep + def nextStep(): Double = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): DoubleStepper = { + val s = st.trySplit() + if (s == null) null else new UnboxingFloatStepper(s) + } + } +} + +/** A Stepper for arbitrary element types. See [[Stepper]]. */ +trait AnyStepper[+A] extends Stepper[A] { + def trySplit(): AnyStepper[A] + + def spliterator[B >: A]: Spliterator[B] = new AnyStepper.AnyStepperSpliterator(this) + + def javaIterator[B >: A]: JIterator[B] = new JIterator[B] { + def hasNext: Boolean = hasStep + def next(): B = nextStep() + } +} + +object AnyStepper { + class AnyStepperSpliterator[A](s: AnyStepper[A]) extends Spliterator[A] { + def tryAdvance(c: Consumer[_ >: A]): Boolean = + if (s.hasStep) { c.accept(s.nextStep()); true } else false + def trySplit(): Spliterator[A] = { + val sp = s.trySplit() + if (sp == null) null else sp.spliterator + } + def estimateSize(): Long = s.estimateSize + def characteristics(): Int = s.characteristics + // Override for efficiency: implement with hasStep / nextStep instead of tryAdvance + override def forEachRemaining(c: Consumer[_ >: A]): Unit = + while (s.hasStep) { c.accept(s.nextStep()) } + } + + def ofSeqDoubleStepper(st: DoubleStepper): AnyStepper[Double] = new BoxedDoubleStepper(st) + def ofParDoubleStepper(st: DoubleStepper with EfficientSplit): AnyStepper[Double] with EfficientSplit = new BoxedDoubleStepper(st) with EfficientSplit + + def ofSeqIntStepper(st: IntStepper): AnyStepper[Int] = new BoxedIntStepper(st) + def ofParIntStepper(st: IntStepper with EfficientSplit): AnyStepper[Int] with EfficientSplit = new BoxedIntStepper(st) with EfficientSplit + + def ofSeqLongStepper(st: LongStepper): AnyStepper[Long] = new BoxedLongStepper(st) + def ofParLongStepper(st: LongStepper with EfficientSplit): AnyStepper[Long] with EfficientSplit = new BoxedLongStepper(st) with EfficientSplit + + private[collection] class BoxedDoubleStepper(st: DoubleStepper) extends AnyStepper[Double] { + def hasStep: Boolean = st.hasStep + def nextStep(): Double = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): AnyStepper[Double] = { + val s = st.trySplit() + if (s == null) null else new BoxedDoubleStepper(s) + } + } + + private[collection] class BoxedIntStepper(st: IntStepper) extends AnyStepper[Int] { + def hasStep: Boolean = st.hasStep + def nextStep(): Int = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): AnyStepper[Int] = { + val s = st.trySplit() + if (s == null) null else new BoxedIntStepper(s) + } + } + + private[collection] class BoxedLongStepper(st: LongStepper) extends AnyStepper[Long] { + def hasStep: Boolean = st.hasStep + def nextStep(): Long = st.nextStep() + def estimateSize: Long = st.estimateSize + def characteristics: Int = st.characteristics + def trySplit(): AnyStepper[Long] = { + val s = st.trySplit() + if (s == null) null else new BoxedLongStepper(s) + } + } +} + +/** A Stepper for Ints. See [[Stepper]]. */ +trait IntStepper extends Stepper[Int] { + def trySplit(): IntStepper + + def spliterator[B >: Int]: Spliterator.OfInt = new IntStepper.IntStepperSpliterator(this) + + def javaIterator[B >: Int]: PrimitiveIterator.OfInt = new PrimitiveIterator.OfInt { + def hasNext: Boolean = hasStep + def nextInt(): Int = nextStep() + } +} +object IntStepper { + class IntStepperSpliterator(s: IntStepper) extends Spliterator.OfInt { + def tryAdvance(c: IntConsumer): Boolean = + if (s.hasStep) { c.accept(s.nextStep()); true } else false + // Override for efficiency: don't wrap the function and call the `tryAdvance` overload + override def tryAdvance(c: Consumer[_ >: jl.Integer]): Boolean = (c: AnyRef) match { + case ic: IntConsumer => tryAdvance(ic) + case _ => if (s.hasStep) { c.accept(jl.Integer.valueOf(s.nextStep())); true } else false + } + // override required for dotty#6152 + override def trySplit(): Spliterator.OfInt = { + val sp = s.trySplit() + if (sp == null) null else sp.spliterator + } + def estimateSize(): Long = s.estimateSize + def characteristics(): Int = s.characteristics + // Override for efficiency: implement with hasStep / nextStep instead of tryAdvance + override def forEachRemaining(c: IntConsumer): Unit = + while (s.hasStep) { c.accept(s.nextStep()) } + // Override for efficiency: implement with hasStep / nextStep instead of tryAdvance + override def forEachRemaining(c: Consumer[_ >: jl.Integer]): Unit = (c: AnyRef) match { + case ic: IntConsumer => forEachRemaining(ic) + case _ => while (s.hasStep) { c.accept(jl.Integer.valueOf(s.nextStep())) } + } + } +} + +/** A Stepper for Doubles. See [[Stepper]]. */ +trait DoubleStepper extends Stepper[Double] { + def trySplit(): DoubleStepper + + def spliterator[B >: Double]: Spliterator.OfDouble = new DoubleStepper.DoubleStepperSpliterator(this) + + def javaIterator[B >: Double]: PrimitiveIterator.OfDouble = new PrimitiveIterator.OfDouble { + def hasNext: Boolean = hasStep + def nextDouble(): Double = nextStep() + } +} + +object DoubleStepper { + class DoubleStepperSpliterator(s: DoubleStepper) extends Spliterator.OfDouble { + def tryAdvance(c: DoubleConsumer): Boolean = + if (s.hasStep) { c.accept(s.nextStep()); true } else false + // Override for efficiency: don't wrap the function and call the `tryAdvance` overload + override def tryAdvance(c: Consumer[_ >: jl.Double]): Boolean = (c: AnyRef) match { + case ic: DoubleConsumer => tryAdvance(ic) + case _ => if (s.hasStep) { c.accept(java.lang.Double.valueOf(s.nextStep())); true } else false + } + // override required for dotty#6152 + override def trySplit(): Spliterator.OfDouble = { + val sp = s.trySplit() + if (sp == null) null else sp.spliterator + } + def estimateSize(): Long = s.estimateSize + def characteristics(): Int = s.characteristics + // Override for efficiency: implement with hasStep / nextStep instead of tryAdvance + override def forEachRemaining(c: DoubleConsumer): Unit = + while (s.hasStep) { c.accept(s.nextStep()) } + // Override for efficiency: implement with hasStep / nextStep instead of tryAdvance + override def forEachRemaining(c: Consumer[_ >: jl.Double]): Unit = (c: AnyRef) match { + case ic: DoubleConsumer => forEachRemaining(ic) + case _ => while (s.hasStep) { c.accept(jl.Double.valueOf(s.nextStep())) } + } + } +} + +/** A Stepper for Longs. See [[Stepper]]. */ +trait LongStepper extends Stepper[Long] { + def trySplit(): LongStepper + + def spliterator[B >: Long]: Spliterator.OfLong = new LongStepper.LongStepperSpliterator(this) + + def javaIterator[B >: Long]: PrimitiveIterator.OfLong = new PrimitiveIterator.OfLong { + def hasNext: Boolean = hasStep + def nextLong(): Long = nextStep() + } +} + +object LongStepper { + class LongStepperSpliterator(s: LongStepper) extends Spliterator.OfLong { + def tryAdvance(c: LongConsumer): Boolean = + if (s.hasStep) { c.accept(s.nextStep()); true } else false + // Override for efficiency: don't wrap the function and call the `tryAdvance` overload + override def tryAdvance(c: Consumer[_ >: jl.Long]): Boolean = (c: AnyRef) match { + case ic: LongConsumer => tryAdvance(ic) + case _ => if (s.hasStep) { c.accept(java.lang.Long.valueOf(s.nextStep())); true } else false + } + // override required for dotty#6152 + override def trySplit(): Spliterator.OfLong = { + val sp = s.trySplit() + if (sp == null) null else sp.spliterator + } + def estimateSize(): Long = s.estimateSize + def characteristics(): Int = s.characteristics + // Override for efficiency: implement with hasStep / nextStep instead of tryAdvance + override def forEachRemaining(c: LongConsumer): Unit = + while (s.hasStep) { c.accept(s.nextStep()) } + // Override for efficiency: implement with hasStep / nextStep instead of tryAdvance + override def forEachRemaining(c: Consumer[_ >: jl.Long]): Unit = (c: AnyRef) match { + case ic: LongConsumer => forEachRemaining(ic) + case _ => while (s.hasStep) { c.accept(jl.Long.valueOf(s.nextStep())) } + } + } +} diff --git a/library/src/scala/collection/StepperShape.scala b/library/src/scala/collection/StepperShape.scala new file mode 100644 index 000000000000..db8c00b47992 --- /dev/null +++ b/library/src/scala/collection/StepperShape.scala @@ -0,0 +1,114 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import java.{lang => jl} + +import scala.collection.Stepper.EfficientSplit + +/** An implicit StepperShape instance is used in the [[IterableOnce.stepper]] to return a possibly + * specialized Stepper `S` according to the element type `T`. + */ +sealed trait StepperShape[-T, S <: Stepper[_]] { + /** Return the Int constant (as defined in the `StepperShape` companion object) for this `StepperShape`. */ + def shape: StepperShape.Shape + + /** Create an unboxing primitive sequential Stepper from a boxed `AnyStepper`. + * This is an identity operation for reference shapes. */ + def seqUnbox(st: AnyStepper[T]): S + + /** Create an unboxing primitive parallel (i.e. `with EfficientSplit`) Stepper from a boxed `AnyStepper`. + * This is an identity operation for reference shapes. */ + def parUnbox(st: AnyStepper[T] with EfficientSplit): S with EfficientSplit +} + +object StepperShape extends StepperShapeLowPriority1 { + class Shape private[StepperShape] (private val s: Int) extends AnyVal + + // reference + val ReferenceShape = new Shape(0) + + // primitive + val IntShape = new Shape(1) + val LongShape = new Shape(2) + val DoubleShape = new Shape(3) + + // widening + val ByteShape = new Shape(4) + val ShortShape = new Shape(5) + val CharShape = new Shape(6) + val FloatShape = new Shape(7) + + implicit val intStepperShape: StepperShape[Int, IntStepper] = new StepperShape[Int, IntStepper] { + def shape = IntShape + def seqUnbox(st: AnyStepper[Int]): IntStepper = new Stepper.UnboxingIntStepper(st) + def parUnbox(st: AnyStepper[Int] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingIntStepper(st) with EfficientSplit + } + implicit val jIntegerStepperShape: StepperShape[jl.Integer, IntStepper] = intStepperShape.asInstanceOf[StepperShape[jl.Integer, IntStepper]] + + implicit val longStepperShape: StepperShape[Long, LongStepper] = new StepperShape[Long, LongStepper] { + def shape = LongShape + def seqUnbox(st: AnyStepper[Long]): LongStepper = new Stepper.UnboxingLongStepper(st) + def parUnbox(st: AnyStepper[Long] with EfficientSplit): LongStepper with EfficientSplit = new Stepper.UnboxingLongStepper(st) with EfficientSplit + } + implicit val jLongStepperShape: StepperShape[jl.Long, LongStepper] = longStepperShape.asInstanceOf[StepperShape[jl.Long, LongStepper]] + + implicit val doubleStepperShape: StepperShape[Double, DoubleStepper] = new StepperShape[Double, DoubleStepper] { + def shape = DoubleShape + def seqUnbox(st: AnyStepper[Double]): DoubleStepper = new Stepper.UnboxingDoubleStepper(st) + def parUnbox(st: AnyStepper[Double] with EfficientSplit): DoubleStepper with EfficientSplit = new Stepper.UnboxingDoubleStepper(st) with EfficientSplit + } + implicit val jDoubleStepperShape: StepperShape[jl.Double, DoubleStepper] = doubleStepperShape.asInstanceOf[StepperShape[jl.Double, DoubleStepper]] + + implicit val byteStepperShape: StepperShape[Byte, IntStepper] = new StepperShape[Byte, IntStepper] { + def shape = ByteShape + def seqUnbox(st: AnyStepper[Byte]): IntStepper = new Stepper.UnboxingByteStepper(st) + def parUnbox(st: AnyStepper[Byte] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingByteStepper(st) with EfficientSplit + } + implicit val jByteStepperShape: StepperShape[jl.Byte, IntStepper] = byteStepperShape.asInstanceOf[StepperShape[jl.Byte, IntStepper]] + + implicit val shortStepperShape: StepperShape[Short, IntStepper] = new StepperShape[Short, IntStepper] { + def shape = ShortShape + def seqUnbox(st: AnyStepper[Short]): IntStepper = new Stepper.UnboxingShortStepper(st) + def parUnbox(st: AnyStepper[Short] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingShortStepper(st) with EfficientSplit + } + implicit val jShortStepperShape: StepperShape[jl.Short, IntStepper] = shortStepperShape.asInstanceOf[StepperShape[jl.Short, IntStepper]] + + implicit val charStepperShape: StepperShape[Char, IntStepper] = new StepperShape[Char, IntStepper] { + def shape = CharShape + def seqUnbox(st: AnyStepper[Char]): IntStepper = new Stepper.UnboxingCharStepper(st) + def parUnbox(st: AnyStepper[Char] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingCharStepper(st) with EfficientSplit + } + implicit val jCharacterStepperShape: StepperShape[jl.Character, IntStepper] = charStepperShape.asInstanceOf[StepperShape[jl.Character, IntStepper]] + + implicit val floatStepperShape: StepperShape[Float, DoubleStepper] = new StepperShape[Float, DoubleStepper] { + def shape = FloatShape + def seqUnbox(st: AnyStepper[Float]): DoubleStepper = new Stepper.UnboxingFloatStepper(st) + def parUnbox(st: AnyStepper[Float] with EfficientSplit): DoubleStepper with EfficientSplit = new Stepper.UnboxingFloatStepper(st) with EfficientSplit + } + implicit val jFloatStepperShape: StepperShape[jl.Float, DoubleStepper] = floatStepperShape.asInstanceOf[StepperShape[jl.Float, DoubleStepper]] +} + +trait StepperShapeLowPriority1 extends StepperShapeLowPriority2 { + implicit def anyStepperShape[T]: StepperShape[T, AnyStepper[T]] = anyStepperShapePrototype.asInstanceOf[StepperShape[T, AnyStepper[T]]] +} + +trait StepperShapeLowPriority2 { + implicit def baseStepperShape[T]: StepperShape[T, Stepper[T]] = anyStepperShapePrototype.asInstanceOf[StepperShape[T, Stepper[T]]] + + protected val anyStepperShapePrototype: StepperShape[AnyRef, Stepper[AnyRef]] = new StepperShape[AnyRef, Stepper[AnyRef]] { + def shape = StepperShape.ReferenceShape + def seqUnbox(st: AnyStepper[AnyRef]): Stepper[AnyRef] = st + def parUnbox(st: AnyStepper[AnyRef] with EfficientSplit): Stepper[AnyRef] with EfficientSplit = st + } +} \ No newline at end of file diff --git a/library/src/scala/collection/StrictOptimizedIterableOps.scala b/library/src/scala/collection/StrictOptimizedIterableOps.scala new file mode 100644 index 000000000000..3260c1bc262e --- /dev/null +++ b/library/src/scala/collection/StrictOptimizedIterableOps.scala @@ -0,0 +1,284 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.nowarn +import scala.annotation.unchecked.uncheckedVariance +import scala.runtime.Statics + +/** + * Trait that overrides iterable operations to take advantage of strict builders. + * + * @tparam A Elements type + * @tparam CC Collection type constructor + * @tparam C Collection type + */ +trait StrictOptimizedIterableOps[+A, +CC[_], +C] + extends Any + with IterableOps[A, CC, C] { + + // Optimized, push-based version of `partition` + override def partition(p: A => Boolean): (C, C) = { + val l, r = newSpecificBuilder + iterator.foreach(x => (if (p(x)) l else r) += x) + (l.result(), r.result()) + } + + override def span(p: A => Boolean): (C, C) = { + val first = newSpecificBuilder + val second = newSpecificBuilder + val it = iterator + var inFirst = true + while (it.hasNext && inFirst) { + val a = it.next() + if (p(a)) { + first += a + } else { + second += a + inFirst = false + } + } + while (it.hasNext) { + second += it.next() + } + (first.result(), second.result()) + } + + override def unzip[A1, A2](implicit asPair: A => (A1, A2)): (CC[A1], CC[A2]) = { + val first = iterableFactory.newBuilder[A1] + val second = iterableFactory.newBuilder[A2] + foreach { a => + val pair = asPair(a) + first += pair._1 + second += pair._2 + } + (first.result(), second.result()) + } + + override def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = { + val b1 = iterableFactory.newBuilder[A1] + val b2 = iterableFactory.newBuilder[A2] + val b3 = iterableFactory.newBuilder[A3] + + foreach { xyz => + val triple = asTriple(xyz) + b1 += triple._1 + b2 += triple._2 + b3 += triple._3 + } + (b1.result(), b2.result(), b3.result()) + } + + // The implementations of the following operations are not fundamentally different from + // the view-based implementations, but they turn out to be slightly faster because + // a couple of indirection levels are removed + + override def map[B](f: A => B): CC[B] = + strictOptimizedMap(iterableFactory.newBuilder, f) + + /** + * @param b Builder to use to build the resulting collection + * @param f Element transformation function + * @tparam B Type of elements of the resulting collection (e.g. `String`) + * @tparam C2 Type of the resulting collection (e.g. `List[String]`) + * @return The resulting collection + */ + @inline protected[this] final def strictOptimizedMap[B, C2](b: mutable.Builder[B, C2], f: A => B): C2 = { + val it = iterator + while (it.hasNext) { + b += f(it.next()) + } + b.result() + } + + override def flatMap[B](f: A => IterableOnce[B]): CC[B] = + strictOptimizedFlatMap(iterableFactory.newBuilder, f) + + /** + * @param b Builder to use to build the resulting collection + * @param f Element transformation function + * @tparam B Type of elements of the resulting collection (e.g. `String`) + * @tparam C2 Type of the resulting collection (e.g. `List[String]`) + * @return The resulting collection + */ + @inline protected[this] final def strictOptimizedFlatMap[B, C2](b: mutable.Builder[B, C2], f: A => IterableOnce[B]): C2 = { + val it = iterator + while (it.hasNext) { + b ++= f(it.next()) + } + b.result() + } + + /** + * @param that Elements to concatenate to this collection + * @param b Builder to use to build the resulting collection + * @tparam B Type of elements of the resulting collections (e.g. `Int`) + * @tparam C2 Type of the resulting collection (e.g. `List[Int]`) + * @return The resulting collection + */ + @inline protected[this] final def strictOptimizedConcat[B >: A, C2](that: IterableOnce[B], b: mutable.Builder[B, C2]): C2 = { + b ++= this + b ++= that + b.result() + } + + override def collect[B](pf: PartialFunction[A, B]): CC[B] = + strictOptimizedCollect(iterableFactory.newBuilder, pf) + + /** + * @param b Builder to use to build the resulting collection + * @param pf Element transformation partial function + * @tparam B Type of elements of the resulting collection (e.g. `String`) + * @tparam C2 Type of the resulting collection (e.g. `List[String]`) + * @return The resulting collection + */ + @inline protected[this] final def strictOptimizedCollect[B, C2](b: mutable.Builder[B, C2], pf: PartialFunction[A, B]): C2 = { + val marker = Statics.pfMarker + val it = iterator + while (it.hasNext) { + val elem = it.next() + val v = pf.applyOrElse(elem, ((x: A) => marker).asInstanceOf[Function[A, B]]) + if (marker ne v.asInstanceOf[AnyRef]) b += v + } + b.result() + } + + override def flatten[B](implicit toIterableOnce: A => IterableOnce[B]): CC[B] = + strictOptimizedFlatten(iterableFactory.newBuilder) + + /** + * @param b Builder to use to build the resulting collection + * @param toIterableOnce Evidence that `A` can be seen as an `IterableOnce[B]` + * @tparam B Type of elements of the resulting collection (e.g. `Int`) + * @tparam C2 Type of the resulting collection (e.g. `List[Int]`) + * @return The resulting collection + */ + @inline protected[this] final def strictOptimizedFlatten[B, C2](b: mutable.Builder[B, C2])(implicit toIterableOnce: A => IterableOnce[B]): C2 = { + val it = iterator + while (it.hasNext) { + b ++= toIterableOnce(it.next()) + } + b.result() + } + + override def zip[B](that: IterableOnce[B]): CC[(A @uncheckedVariance, B)] = + strictOptimizedZip(that, iterableFactory.newBuilder[(A, B)]) + + /** + * @param that Collection to zip with this collection + * @param b Builder to use to build the resulting collection + * @tparam B Type of elements of the second collection (e.g. `String`) + * @tparam C2 Type of the resulting collection (e.g. `List[(Int, String)]`) + * @return The resulting collection + */ + @inline protected[this] final def strictOptimizedZip[B, C2](that: IterableOnce[B], b: mutable.Builder[(A, B), C2]): C2 = { + val it1 = iterator + val it2 = that.iterator + while (it1.hasNext && it2.hasNext) { + b += ((it1.next(), it2.next())) + } + b.result() + } + + override def zipWithIndex: CC[(A @uncheckedVariance, Int)] = { + val b = iterableFactory.newBuilder[(A, Int)] + var i = 0 + val it = iterator + while (it.hasNext) { + b += ((it.next(), i)) + i += 1 + } + b.result() + } + + override def scanLeft[B](z: B)(op: (B, A) => B): CC[B] = { + val b = iterableFactory.newBuilder[B] + b.sizeHint(this, delta = 0) + var acc = z + b += acc + val it = iterator + while (it.hasNext) { + acc = op(acc, it.next()) + b += acc + } + b.result() + } + + override def filter(pred: A => Boolean): C = filterImpl(pred, isFlipped = false) + + override def filterNot(pred: A => Boolean): C = filterImpl(pred, isFlipped = true) + + protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): C = { + val b = newSpecificBuilder + val it = iterator + while (it.hasNext) { + val elem = it.next() + if (pred(elem) != isFlipped) { + b += elem + } + } + b.result() + } + + // Optimized, push-based version of `partitionMap` + override def partitionMap[A1, A2](f: A => Either[A1, A2]): (CC[A1], CC[A2]) = { + val l = iterableFactory.newBuilder[A1] + val r = iterableFactory.newBuilder[A2] + foreach { x => + f(x) match { + case Left(x1) => l += x1 + case Right(x2) => r += x2 + } + } + (l.result(), r.result()) + } + + // Optimization avoids creation of second collection + override def tapEach[U](f: A => U): C = { + foreach(f) + coll + } + + /** A collection containing the last `n` elements of this collection. + * $willForceEvaluation + */ + override def takeRight(n: Int): C = { + val b = newSpecificBuilder + b.sizeHintBounded(n, toIterable: @nowarn("cat=deprecation")) + val lead = iterator drop n + val it = iterator + while (lead.hasNext) { + lead.next() + it.next() + } + while (it.hasNext) b += it.next() + b.result() + } + + /** The rest of the collection without its `n` last elements. For + * linear, immutable collections this should avoid making a copy. + * $willForceEvaluation + */ + override def dropRight(n: Int): C = { + val b = newSpecificBuilder + if (n >= 0) b.sizeHint(this, delta = -n) + val lead = iterator drop n + val it = iterator + while (lead.hasNext) { + b += it.next() + lead.next() + } + b.result() + } +} diff --git a/library/src/scala/collection/StrictOptimizedMapOps.scala b/library/src/scala/collection/StrictOptimizedMapOps.scala new file mode 100644 index 000000000000..a87ba3ee9e20 --- /dev/null +++ b/library/src/scala/collection/StrictOptimizedMapOps.scala @@ -0,0 +1,48 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +/** + * Trait that overrides map operations to take advantage of strict builders. + * + * @tparam K Type of keys + * @tparam V Type of values + * @tparam CC Collection type constructor + * @tparam C Collection type + */ +trait StrictOptimizedMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] + extends MapOps[K, V, CC, C] + with StrictOptimizedIterableOps[(K, V), Iterable, C] { + + override def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = + strictOptimizedMap(mapFactory.newBuilder, f) + + override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = + strictOptimizedFlatMap(mapFactory.newBuilder, f) + + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): CC[K, V2] = + strictOptimizedConcat(suffix, mapFactory.newBuilder) + + override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): CC[K2, V2] = + strictOptimizedCollect(mapFactory.newBuilder, pf) + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = { + val b = mapFactory.newBuilder[K, V1] + b ++= this + b += elem1 + b += elem2 + if (elems.nonEmpty) b ++= elems + b.result() + } +} diff --git a/library/src/scala/collection/StrictOptimizedSeqOps.scala b/library/src/scala/collection/StrictOptimizedSeqOps.scala new file mode 100644 index 000000000000..a131498d8b28 --- /dev/null +++ b/library/src/scala/collection/StrictOptimizedSeqOps.scala @@ -0,0 +1,108 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +/** + * Trait that overrides operations on sequences in order + * to take advantage of strict builders. + */ +trait StrictOptimizedSeqOps [+A, +CC[_], +C] + extends Any + with SeqOps[A, CC, C] + with StrictOptimizedIterableOps[A, CC, C] { + + override def distinctBy[B](f: A => B): C = { + val builder = newSpecificBuilder + val seen = mutable.HashSet.empty[B] + val it = this.iterator + while (it.hasNext) { + val next = it.next() + if (seen.add(f(next))) builder += next + } + builder.result() + } + + override def prepended[B >: A](elem: B): CC[B] = { + val b = iterableFactory.newBuilder[B] + b.sizeHint(this, delta = 1) + b += elem + b ++= this + b.result() + } + + override def appended[B >: A](elem: B): CC[B] = { + val b = iterableFactory.newBuilder[B] + b.sizeHint(this, delta = 1) + b ++= this + b += elem + b.result() + } + + override def appendedAll[B >: A](suffix: IterableOnce[B]): CC[B] = + strictOptimizedConcat(suffix, iterableFactory.newBuilder) + + override def prependedAll[B >: A](prefix: IterableOnce[B]): CC[B] = { + val b = iterableFactory.newBuilder[B] + b ++= prefix + b ++= this + b.result() + } + + override def padTo[B >: A](len: Int, elem: B): CC[B] = { + val b = iterableFactory.newBuilder[B] + val L = size + b.sizeHint(math.max(L, len)) + var diff = len - L + b ++= this + while (diff > 0) { + b += elem + diff -= 1 + } + b.result() + } + + override def diff[B >: A](that: Seq[B]): C = + if (isEmpty || that.isEmpty) coll + else { + val occ = occCounts(that) + val b = newSpecificBuilder + for (x <- this) { + occ.updateWith(x) { + case None => { + b.addOne(x) + None + } + case Some(1) => None + case Some(n) => Some(n - 1) + } + } + b.result() + } + + override def intersect[B >: A](that: Seq[B]): C = + if (isEmpty || that.isEmpty) empty + else { + val occ = occCounts(that) + val b = newSpecificBuilder + for (x <- this) { + occ.updateWith(x) { + case None => None + case Some(n) => { + b.addOne(x) + if (n == 1) None else Some(n - 1) + } + } + } + b.result() + } +} diff --git a/library/src/scala/collection/StrictOptimizedSetOps.scala b/library/src/scala/collection/StrictOptimizedSetOps.scala new file mode 100644 index 000000000000..39e585324f45 --- /dev/null +++ b/library/src/scala/collection/StrictOptimizedSetOps.scala @@ -0,0 +1,29 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +/** + * Trait that overrides set operations to take advantage of strict builders. + * + * @tparam A Elements type + * @tparam CC Collection type constructor + * @tparam C Collection type + */ +trait StrictOptimizedSetOps[A, +CC[_], +C <: SetOps[A, CC, C]] + extends SetOps[A, CC, C] + with StrictOptimizedIterableOps[A, CC, C] { + + override def concat(that: IterableOnce[A]): C = + strictOptimizedConcat(that, newSpecificBuilder) + +} diff --git a/library/src/scala/collection/StrictOptimizedSortedMapOps.scala b/library/src/scala/collection/StrictOptimizedSortedMapOps.scala new file mode 100644 index 000000000000..8317913c6d1b --- /dev/null +++ b/library/src/scala/collection/StrictOptimizedSortedMapOps.scala @@ -0,0 +1,46 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.annotation.implicitNotFound + +/** + * Trait that overrides sorted map operations to take advantage of strict builders. + * + * @tparam K Type of keys + * @tparam V Type of values + * @tparam CC Collection type constructor + * @tparam C Collection type + */ +trait StrictOptimizedSortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]] + extends SortedMapOps[K, V, CC, C] + with StrictOptimizedMapOps[K, V, Map, C] { + + override def map[K2, V2](f: ((K, V)) => (K2, V2))(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + strictOptimizedMap(sortedMapFactory.newBuilder, f) + + override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + strictOptimizedFlatMap(sortedMapFactory.newBuilder, f) + + override def concat[V2 >: V](xs: IterableOnce[(K, V2)]): CC[K, V2] = + strictOptimizedConcat(xs, sortedMapFactory.newBuilder(using ordering)) + + override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + strictOptimizedCollect(sortedMapFactory.newBuilder, pf) + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = { + val m = ((this + elem1).asInstanceOf[Map[K, V]] + elem2).asInstanceOf[CC[K, V1]] + if(elems.isEmpty) m else m.concat(elems).asInstanceOf[CC[K, V1]] + } +} diff --git a/library/src/scala/collection/StrictOptimizedSortedSetOps.scala b/library/src/scala/collection/StrictOptimizedSortedSetOps.scala new file mode 100644 index 000000000000..c01b0d8466f3 --- /dev/null +++ b/library/src/scala/collection/StrictOptimizedSortedSetOps.scala @@ -0,0 +1,42 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.implicitNotFound +import scala.annotation.unchecked.uncheckedVariance + +/** + * Trait that overrides sorted set operations to take advantage of strict builders. + * + * @tparam A Elements type + * @tparam CC Collection type constructor + * @tparam C Collection type + */ +trait StrictOptimizedSortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]] + extends SortedSetOps[A, CC, C] + with StrictOptimizedSetOps[A, Set, C] { + + override def map[B](f: A => B)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + strictOptimizedMap(sortedIterableFactory.newBuilder, f) + + override def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + strictOptimizedFlatMap(sortedIterableFactory.newBuilder, f) + + override def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = + strictOptimizedZip(that, sortedIterableFactory.newBuilder[(A, B)]) + + override def collect[B](pf: PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + strictOptimizedCollect(sortedIterableFactory.newBuilder, pf) + +} diff --git a/library/src/scala/collection/StringOps.scala b/library/src/scala/collection/StringOps.scala new file mode 100644 index 000000000000..169a646d12fa --- /dev/null +++ b/library/src/scala/collection/StringOps.scala @@ -0,0 +1,1653 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import java.lang.{StringBuilder => JStringBuilder} + +import scala.collection.Stepper.EfficientSplit +import scala.collection.convert.impl.{CharStringStepper, CodePointStringStepper} +import scala.collection.immutable.{ArraySeq, WrappedString} +import scala.collection.mutable.StringBuilder +import scala.math.{ScalaNumber, max, min} +import scala.reflect.ClassTag +import scala.util.matching.Regex + +object StringOps { + // just statics for companion class. + private final val LF = 0x0A + private final val FF = 0x0C + private final val CR = 0x0D + private final val SU = 0x1A + + private class StringIterator(private[this] val s: String) extends AbstractIterator[Char] { + private[this] var pos = 0 + def hasNext: Boolean = pos < s.length + def next(): Char = { + if (pos >= s.length) Iterator.empty.next() + val r = s.charAt(pos) + pos += 1 + r + } + } + + private class ReverseIterator(private[this] val s: String) extends AbstractIterator[Char] { + private[this] var pos = s.length-1 + def hasNext: Boolean = pos >= 0 + def next(): Char = { + if (pos < 0) Iterator.empty.next() + val r = s.charAt(pos) + pos -= 1 + r + } + } + + private class GroupedIterator(s: String, groupSize: Int) extends AbstractIterator[String] { + private[this] var pos = 0 + def hasNext: Boolean = pos < s.length + def next(): String = { + if(pos >= s.length) Iterator.empty.next() + val r = s.slice(pos, pos+groupSize) + pos += groupSize + r + } + } + + /** A lazy filtered string. No filtering is applied until one of `foreach`, `map` or `flatMap` is called. */ + class WithFilter(p: Char => Boolean, s: String) { + + /** Apply `f` to each element for its side effects. + * Note: [U] parameter needed to help scalac's type inference. + */ + def foreach[U](f: Char => U): Unit = { + val len = s.length + var i = 0 + while(i < len) { + val x = s.charAt(i) + if(p(x)) f(x) + i += 1 + } + } + + /** Builds a new collection by applying a function to all chars of this filtered string. + * + * @param f the function to apply to each char. + * @return a new collection resulting from applying the given function + * `f` to each char of this string and collecting the results. + */ + def map[B](f: Char => B): immutable.IndexedSeq[B] = { + val len = s.length + val b = immutable.IndexedSeq.newBuilder[B] + b.sizeHint(len) + var i = 0 + while (i < len) { + val x = s.charAt(i) + if(p(x)) b.addOne(f(x)) + i += 1 + } + b.result() + } + + /** Builds a new string by applying a function to all chars of this filtered string. + * + * @param f the function to apply to each char. + * @return a new string resulting from applying the given function + * `f` to each char of this string and collecting the results. + */ + def map(f: Char => Char): String = { + val len = s.length + val sb = new JStringBuilder(len) + var i = 0 + while (i < len) { + val x = s.charAt(i) + if(p(x)) sb.append(f(x)) + i += 1 + } + sb.toString + } + + /** Builds a new collection by applying a function to all chars of this filtered string + * and using the elements of the resulting collections. + * + * @param f the function to apply to each char. + * @return a new collection resulting from applying the given collection-valued function + * `f` to each char of this string and concatenating the results. + */ + def flatMap[B](f: Char => IterableOnce[B]): immutable.IndexedSeq[B] = { + val len = s.length + val b = immutable.IndexedSeq.newBuilder[B] + var i = 0 + while (i < len) { + val x = s.charAt(i) + if(p(x)) b.addAll(f(x)) + i += 1 + } + b.result() + } + + /** Builds a new string by applying a function to all chars of this filtered string + * and using the elements of the resulting Strings. + * + * @param f the function to apply to each char. + * @return a new string resulting from applying the given string-valued function + * `f` to each char of this string and concatenating the results. + */ + def flatMap(f: Char => String): String = { + val len = s.length + val sb = new JStringBuilder + var i = 0 + while (i < len) { + val x = s.charAt(i) + if(p(x)) sb.append(f(x)) + i += 1 + } + sb.toString + } + + /** Creates a new non-strict filter which combines this filter with the given predicate. */ + def withFilter(q: Char => Boolean): WithFilter = new WithFilter(a => p(a) && q(a), s) + } + + /** Avoid an allocation in [[collect]]. */ + private val fallback: Any => Any = _ => fallback +} + +/** Provides extension methods for strings. + * + * Some of these methods treat strings as a plain collection of [[Char]]s + * without any regard for Unicode handling. Unless the user takes Unicode + * handling in to account or makes sure the strings don't require such handling, + * these methods may result in unpaired or invalidly paired surrogate code + * units. + * + * @define unicodeunaware This method treats a string as a plain sequence of + * Char code units and makes no attempt to keep + * surrogate pairs or codepoint sequences together. + * The user is responsible for making sure such cases + * are handled correctly. Failing to do so may result in + * an invalid Unicode string. + */ +final class StringOps(private val s: String) extends AnyVal { + import StringOps._ + + @`inline` def view: StringView = new StringView(s) + + @`inline` def size: Int = s.length + + @`inline` def knownSize: Int = s.length + + /** Get the char at the specified index. */ + @`inline` def apply(i: Int): Char = s.charAt(i) + + def sizeCompare(otherSize: Int): Int = Integer.compare(s.length, otherSize) + + def lengthCompare(len: Int): Int = Integer.compare(s.length, len) + + def sizeIs: Int = s.length + + def lengthIs: Int = s.length + + /** Builds a new collection by applying a function to all chars of this string. + * + * @param f the function to apply to each char. + * @return a new collection resulting from applying the given function + * `f` to each char of this string and collecting the results. + */ + def map[B](f: Char => B): immutable.IndexedSeq[B] = { + val len = s.length + val dst = new Array[AnyRef](len) + var i = 0 + while (i < len) { + dst(i) = f(s charAt i).asInstanceOf[AnyRef] + i += 1 + } + new ArraySeq.ofRef(dst).asInstanceOf[immutable.IndexedSeq[B]] + } + + /** Builds a new string by applying a function to all chars of this string. + * + * @param f the function to apply to each char. + * @return a new string resulting from applying the given function + * `f` to each char of this string and collecting the results. + */ + def map(f: Char => Char): String = { + val len = s.length + val dst = new Array[Char](len) + var i = 0 + while (i < len) { + dst(i) = f(s charAt i) + i += 1 + } + new String(dst) + } + + /** Builds a new collection by applying a function to all chars of this string + * and using the elements of the resulting collections. + * + * @param f the function to apply to each char. + * @return a new collection resulting from applying the given collection-valued function + * `f` to each char of this string and concatenating the results. + */ + def flatMap[B](f: Char => IterableOnce[B]): immutable.IndexedSeq[B] = { + val len = s.length + val b = immutable.IndexedSeq.newBuilder[B] + var i = 0 + while (i < len) { + b.addAll(f(s.charAt(i))) + i += 1 + } + b.result() + } + + /** Builds a new string by applying a function to all chars of this string + * and using the elements of the resulting strings. + * + * @param f the function to apply to each char. + * @return a new string resulting from applying the given string-valued function + * `f` to each char of this string and concatenating the results. + */ + def flatMap(f: Char => String): String = { + val len = s.length + val sb = new JStringBuilder + var i = 0 + while (i < len) { + sb append f(s.charAt(i)) + i += 1 + } + sb.toString + } + + /** Builds a new String by applying a partial function to all chars of this String + * on which the function is defined. + * + * @param pf the partial function which filters and maps the String. + * @return a new String resulting from applying the given partial function + * `pf` to each char on which it is defined and collecting the results. + */ + def collect(pf: PartialFunction[Char, Char]): String = { + val fallback: Any => Any = StringOps.fallback + var i = 0 + val b = new StringBuilder + while (i < s.length) { + val v = pf.applyOrElse(s.charAt(i), fallback) + if (v.asInstanceOf[AnyRef] ne fallback) b.addOne(v.asInstanceOf[Char]) + i += 1 + } + b.result() + } + + /** Builds a new collection by applying a partial function to all chars of this String + * on which the function is defined. + * + * @param pf the partial function which filters and maps the String. + * @tparam B the element type of the returned collection. + * @return a new collection resulting from applying the given partial function + * `pf` to each char on which it is defined and collecting the results. + */ + def collect[B](pf: PartialFunction[Char, B]): immutable.IndexedSeq[B] = { + val fallback: Any => Any = StringOps.fallback + var i = 0 + val b = immutable.IndexedSeq.newBuilder[B] + while (i < s.length) { + val v = pf.applyOrElse(s.charAt(i), fallback) + if (v.asInstanceOf[AnyRef] ne fallback) b.addOne(v.asInstanceOf[B]) + i += 1 + } + b.result() + } + + /** Returns a new collection containing the chars from this string followed by the elements from the + * right hand operand. + * + * @param suffix the collection to append. + * @return a new collection which contains all chars + * of this string followed by all elements of `suffix`. + */ + def concat[B >: Char](suffix: IterableOnce[B]): immutable.IndexedSeq[B] = { + val b = immutable.IndexedSeq.newBuilder[B] + val k = suffix.knownSize + b.sizeHint(s.length + (if(k >= 0) k else 16)) + b.addAll(new WrappedString(s)) + b.addAll(suffix) + b.result() + } + + /** Returns a new string containing the chars from this string followed by the chars from the + * right hand operand. + * + * @param suffix the collection to append. + * @return a new string which contains all chars + * of this string followed by all chars of `suffix`. + */ + def concat(suffix: IterableOnce[Char]): String = { + val k = suffix.knownSize + val sb = new JStringBuilder(s.length + (if(k >= 0) k else 16)) + sb.append(s) + for (ch <- suffix.iterator) sb.append(ch) + sb.toString + } + + /** Returns a new string containing the chars from this string followed by the chars from the + * right hand operand. + * + * @param suffix the string to append. + * @return a new string which contains all chars + * of this string followed by all chars of `suffix`. + */ + @`inline` def concat(suffix: String): String = s + suffix + + /** Alias for `concat` */ + @`inline` def ++[B >: Char](suffix: Iterable[B]): immutable.IndexedSeq[B] = concat(suffix) + + /** Alias for `concat` */ + @`inline` def ++(suffix: IterableOnce[Char]): String = concat(suffix) + + /** Alias for `concat` */ + def ++(xs: String): String = concat(xs) + + /** Returns a collection with an element appended until a given target length is reached. + * + * @param len the target length + * @param elem the padding value + * @return a collection consisting of + * this string followed by the minimal number of occurrences of `elem` so + * that the resulting collection has a length of at least `len`. + */ + def padTo[B >: Char](len: Int, elem: B): immutable.IndexedSeq[B] = { + val sLen = s.length + if (sLen >= len) new WrappedString(s) else { + val b = immutable.IndexedSeq.newBuilder[B] + b.sizeHint(len) + b.addAll(new WrappedString(s)) + var i = sLen + while (i < len) { + b.addOne(elem) + i += 1 + } + b.result() + } + } + + /** Returns a string with a char appended until a given target length is reached. + * + * @param len the target length + * @param elem the padding value + * @return a string consisting of + * this string followed by the minimal number of occurrences of `elem` so + * that the resulting string has a length of at least `len`. + */ + def padTo(len: Int, elem: Char): String = { + val sLen = s.length + if (sLen >= len) s else { + val sb = new JStringBuilder(len) + sb.append(s) + // With JDK 11, this can written as: + // sb.append(String.valueOf(elem).repeat(len - sLen)) + var i = sLen + while (i < len) { + sb.append(elem) + i += 1 + } + sb.toString + } + } + + /** A copy of the string with an element prepended */ + def prepended[B >: Char](elem: B): immutable.IndexedSeq[B] = { + val b = immutable.IndexedSeq.newBuilder[B] + b.sizeHint(s.length + 1) + b.addOne(elem) + b.addAll(new WrappedString(s)) + b.result() + } + + /** Alias for `prepended` */ + @`inline` def +: [B >: Char] (elem: B): immutable.IndexedSeq[B] = prepended(elem) + + /** A copy of the string with an char prepended */ + def prepended(c: Char): String = + new JStringBuilder(s.length + 1).append(c).append(s).toString + + /** Alias for `prepended` */ + @`inline` def +: (c: Char): String = prepended(c) + + /** A copy of the string with all elements from a collection prepended */ + def prependedAll[B >: Char](prefix: IterableOnce[B]): immutable.IndexedSeq[B] = { + val b = immutable.IndexedSeq.newBuilder[B] + val k = prefix.knownSize + b.sizeHint(s.length + (if(k >= 0) k else 16)) + b.addAll(prefix) + b.addAll(new WrappedString(s)) + b.result() + } + + /** Alias for `prependedAll` */ + @`inline` def ++: [B >: Char] (prefix: IterableOnce[B]): immutable.IndexedSeq[B] = prependedAll(prefix) + + /** A copy of the string with another string prepended */ + def prependedAll(prefix: String): String = prefix + s + + /** Alias for `prependedAll` */ + @`inline` def ++: (prefix: String): String = prependedAll(prefix) + + /** A copy of the string with an element appended */ + def appended[B >: Char](elem: B): immutable.IndexedSeq[B] = { + val b = immutable.IndexedSeq.newBuilder[B] + b.sizeHint(s.length + 1) + b.addAll(new WrappedString(s)) + b.addOne(elem) + b.result() + } + + /** Alias for `appended` */ + @`inline` def :+ [B >: Char](elem: B): immutable.IndexedSeq[B] = appended(elem) + + /** A copy of the string with an element appended */ + def appended(c: Char): String = + new JStringBuilder(s.length + 1).append(s).append(c).toString + + /** Alias for `appended` */ + @`inline` def :+ (c: Char): String = appended(c) + + /** A copy of the string with all elements from a collection appended */ + @`inline` def appendedAll[B >: Char](suffix: IterableOnce[B]): immutable.IndexedSeq[B] = + concat(suffix) + + /** Alias for `appendedAll` */ + @`inline` def :++ [B >: Char](suffix: IterableOnce[B]): immutable.IndexedSeq[B] = + concat(suffix) + + /** A copy of the string with another string appended */ + @`inline` def appendedAll(suffix: String): String = s + suffix + + /** Alias for `appendedAll` */ + @`inline` def :++ (suffix: String): String = s + suffix + + /** Produces a new collection where a slice of characters in this string is replaced by another collection. + * + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original string appends the patch to the end. + * If more values are replaced than actually exist, the excess is ignored. + * + * @param from the index of the first replaced char + * @param other the replacement collection + * @param replaced the number of chars to drop in the original string + * @return a new collection consisting of all chars of this string + * except that `replaced` chars starting from `from` are replaced + * by `other`. + */ + def patch[B >: Char](from: Int, other: IterableOnce[B], replaced: Int): immutable.IndexedSeq[B] = { + val len = s.length + @`inline` def slc(off: Int, length: Int): WrappedString = + new WrappedString(s.substring(off, off+length)) + val b = immutable.IndexedSeq.newBuilder[B] + val k = other.knownSize + if(k >= 0) b.sizeHint(len + k - replaced) + val chunk1 = if(from > 0) min(from, len) else 0 + if(chunk1 > 0) b.addAll(slc(0, chunk1)) + b ++= other + val remaining = len - chunk1 - replaced + if(remaining > 0) b.addAll(slc(len - remaining, remaining)) + b.result() + } + + /** Produces a new collection where a slice of characters in this string is replaced by another collection. + * + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original string appends the patch to the end. + * If more values are replaced than actually exist, the excess is ignored. + * + * @param from the index of the first replaced char + * @param other the replacement string + * @param replaced the number of chars to drop in the original string + * @return a new string consisting of all chars of this string + * except that `replaced` chars starting from `from` are replaced + * by `other`. + * @note $unicodeunaware + */ + def patch(from: Int, other: IterableOnce[Char], replaced: Int): String = + patch(from, other.iterator.mkString, replaced) + + /** Produces a new string where a slice of characters in this string is replaced by another string. + * + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original string appends the patch to the end. + * If more values are replaced than actually exist, the excess is ignored. + * + * @param from the index of the first replaced char + * @param other the replacement string + * @param replaced the number of chars to drop in the original string + * @return a new string consisting of all chars of this string + * except that `replaced` chars starting from `from` are replaced + * by `other`. + * @note $unicodeunaware + */ + def patch(from: Int, other: String, replaced: Int): String = { + val len = s.length + val sb = new JStringBuilder(len + other.size - replaced) + val chunk1 = if(from > 0) min(from, len) else 0 + if(chunk1 > 0) sb.append(s, 0, chunk1) + sb.append(other) + val remaining = len - chunk1 - replaced + if(remaining > 0) sb.append(s, len - remaining, len) + sb.toString + } + + /** A copy of this string with one single replaced element. + * @param index the position of the replacement + * @param elem the replacing element + * @return a new string which is a copy of this string with the element at position `index` replaced by `elem`. + * @throws IndexOutOfBoundsException if `index` does not satisfy `0 <= index < length`. + * @note $unicodeunaware + */ + def updated(index: Int, elem: Char): String = { + val sb = new JStringBuilder(s.length).append(s) + sb.setCharAt(index, elem) + sb.toString + } + + /** Tests whether this string contains the given character. + * + * @param elem the character to test. + * @return `true` if this string has an element that is equal (as + * determined by `==`) to `elem`, `false` otherwise. + */ + def contains(elem: Char): Boolean = s.indexOf(elem) >= 0 + + /** Displays all elements of this string in a string using start, end, and + * separator strings. + * + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return The resulting string + * begins with the string `start` and ends with the string + * `end`. Inside, the string chars of this string are separated by + * the string `sep`. + * @note $unicodeunaware + */ + final def mkString(start: String, sep: String, end: String): String = + addString(new StringBuilder(), start, sep, end).toString + + /** Displays all elements of this string in a string using a separator string. + * + * @param sep the separator string. + * @return In the resulting string + * the chars of this string are separated by the string `sep`. + * @note $unicodeunaware + */ + @inline final def mkString(sep: String): String = + if (sep.isEmpty || s.length < 2) s + else mkString("", sep, "") + + /** Returns this string */ + @inline final def mkString: String = s + + /** Appends this string to a string builder. */ + @inline final def addString(b: StringBuilder): b.type = b.append(s) + + /** Appends this string to a string builder using a separator string. */ + @inline final def addString(b: StringBuilder, sep: String): b.type = + addString(b, "", sep, "") + + /** Appends this string to a string builder using start, end and separator strings. */ + final def addString(b: StringBuilder, start: String, sep: String, end: String): b.type = { + val jsb = b.underlying + if (start.length != 0) jsb.append(start) + val len = s.length + if (len != 0) { + if (sep.isEmpty) jsb.append(s) + else { + jsb.ensureCapacity(jsb.length + len + end.length + (len - 1) * sep.length) + jsb.append(s.charAt(0)) + var i = 1 + while (i < len) { + jsb.append(sep) + jsb.append(s.charAt(i)) + i += 1 + } + } + } + if (end.length != 0) jsb.append(end) + b + } + + /** Selects an interval of elements. The returned string is made up + * of all elements `x` which satisfy the invariant: + * {{{ + * from <= indexOf(x) < until + * }}} + * + * @param from the lowest index to include from this string. + * @param until the lowest index to EXCLUDE from this string. + * @return a string containing the elements greater than or equal to + * index `from` extending up to (but not including) index `until` + * of this string. + * @note $unicodeunaware + */ + def slice(from: Int, until: Int): String = { + val start = from max 0 + val end = until min s.length + + if (start >= end) "" + else s.substring(start, end) + } + + // Note: String.repeat is added in JDK 11. + /** Return the current string concatenated `n` times. + */ + def *(n: Int): String = { + if (n <= 0) { + "" + } else { + val sb = new JStringBuilder(s.length * n) + var i = 0 + while (i < n) { + sb.append(s) + i += 1 + } + sb.toString + } + } + + @`inline` private[this] def isLineBreak(c: Char) = c == CR || c == LF + @`inline` private[this] def isLineBreak2(c0: Char, c: Char) = c0 == CR && c == LF + + /** Strip the trailing line separator from this string if there is one. + * The line separator is taken as `"\n"`, `"\r"`, or `"\r\n"`. + */ + def stripLineEnd: String = + if (s.isEmpty) s + else { + var i = s.length - 1 + val last = apply(i) + if (!isLineBreak(last)) s + else { + if (i > 0 && isLineBreak2(apply(i - 1), last)) i -= 1 + s.substring(0, i) + } + } + + /** Return an iterator of all lines embedded in this string, + * including trailing line separator characters. + * + * The empty string yields an empty iterator. + */ + def linesWithSeparators: Iterator[String] = linesSeparated(stripped = false) + + /** Lines in this string, where a line is terminated by + * `"\n"`, `"\r"`, `"\r\n"`, or the end of the string. + * A line may be empty. Line terminators are removed. + */ + def linesIterator: Iterator[String] = linesSeparated(stripped = true) + + // if `stripped`, exclude the line separators + private def linesSeparated(stripped: Boolean): Iterator[String] = new AbstractIterator[String] { + def hasNext: Boolean = !done + def next(): String = if (done) Iterator.empty.next() else advance() + + private[this] val len = s.length + private[this] var index = 0 + @`inline` private def done = index >= len + private def advance(): String = { + val start = index + while (!done && !isLineBreak(apply(index))) index += 1 + var end = index + if (!done) { + val c = apply(index) + index += 1 + if (!done && isLineBreak2(c, apply(index))) index += 1 + if (!stripped) end = index + } + s.substring(start, end) + } + } + + /** Return all lines in this string in an iterator, excluding trailing line + * end characters; i.e., apply `.stripLineEnd` to all lines + * returned by `linesWithSeparators`. + */ + @deprecated("Use `linesIterator`, because JDK 11 adds a `lines` method on String", "2.13.0") + def lines: Iterator[String] = linesIterator + + /** Returns this string with first character converted to upper case. + * If the first character of the string is capitalized, it is returned unchanged. + * This method does not convert characters outside the Basic Multilingual Plane (BMP). + */ + def capitalize: String = + if (s == null || s.length == 0 || !s.charAt(0).isLower) s + else updated(0, s.charAt(0).toUpper) + + /** Returns this string with the given `prefix` stripped. If this string does not + * start with `prefix`, it is returned unchanged. + */ + def stripPrefix(prefix: String) = + if (s startsWith prefix) s.substring(prefix.length) + else s + + /** Returns this string with the given `suffix` stripped. If this string does not + * end with `suffix`, it is returned unchanged. + */ + def stripSuffix(suffix: String) = + if (s endsWith suffix) s.substring(0, s.length - suffix.length) + else s + + /** Replace all literal occurrences of `literal` with the literal string `replacement`. + * This method is equivalent to [[java.lang.String#replace(CharSequence,CharSequence)]]. + * + * @param literal the string which should be replaced everywhere it occurs + * @param replacement the replacement string + * @return the resulting string + */ + @deprecated("Use `s.replace` as an exact replacement", "2.13.2") + def replaceAllLiterally(literal: String, replacement: String): String = s.replace(literal, replacement) + + /** For every line in this string: + * + * Strip a leading prefix consisting of blanks or control characters + * followed by `marginChar` from the line. + */ + def stripMargin(marginChar: Char): String = { + val sb = new JStringBuilder(s.length) + for (line <- linesWithSeparators) { + val len = line.length + var index = 0 + while (index < len && line.charAt(index) <= ' ') index += 1 + val stripped = + if (index < len && line.charAt(index) == marginChar) line.substring(index + 1) + else line + sb.append(stripped) + } + sb.toString + } + + /** For every line in this string: + * + * Strip a leading prefix consisting of blanks or control characters + * followed by `|` from the line. + */ + def stripMargin: String = stripMargin('|') + + private[this] def escape(ch: Char): String = if ( + (ch >= 'a') && (ch <= 'z') || + (ch >= 'A') && (ch <= 'Z') || + (ch >= '0' && ch <= '9')) ch.toString + else "\\" + ch + + /** Split this string around the separator character + * + * If this string is the empty string, returns an array of strings + * that contains a single empty string. + * + * If this string is not the empty string, returns an array containing + * the substrings terminated by the start of the string, the end of the + * string or the separator character, excluding empty trailing substrings + * + * If the separator character is a surrogate character, only split on + * matching surrogate characters if they are not part of a surrogate pair + * + * The behaviour follows, and is implemented in terms of String.split(re: String) + * + * + * @example {{{ + * "a.b".split('.') //returns Array("a", "b") + * + * //splitting the empty string always returns the array with a single + * //empty string + * "".split('.') //returns Array("") + * + * //only trailing empty substrings are removed + * "a.".split('.') //returns Array("a") + * ".a.".split('.') //returns Array("", "a") + * "..a..".split('.') //returns Array("", "", "a") + * + * //all parts are empty and trailing + * ".".split('.') //returns Array() + * "..".split('.') //returns Array() + * + * //surrogate pairs + * val high = 0xD852.toChar + * val low = 0xDF62.toChar + * val highstring = high.toString + * val lowstring = low.toString + * + * //well-formed surrogate pairs are not split + * val highlow = highstring + lowstring + * highlow.split(high) //returns Array(highlow) + * + * //bare surrogate characters are split + * val bare = "_" + highstring + "_" + * bare.split(high) //returns Array("_", "_") + * + * }}} + * + * @param separator the character used as a delimiter + */ + def split(separator: Char): Array[String] = s.split(escape(separator)) + + @throws(classOf[java.util.regex.PatternSyntaxException]) + def split(separators: Array[Char]): Array[String] = { + val re = separators.foldLeft("[")(_+escape(_)) + "]" + s.split(re) + } + + /** You can follow a string with `.r`, turning it into a `Regex`. E.g. + * + * `"""A\w*""".r` is the regular expression for ASCII-only identifiers starting with `A`. + * + * `"""(?\d\d)-(?\d\d)-(?\d\d\d\d)""".r` matches dates + * and provides its subcomponents through groups named "month", "day" and + * "year". + */ + def r: Regex = new Regex(s) + + /** You can follow a string with `.r(g1, ... , gn)`, turning it into a `Regex`, + * with group names g1 through gn. + * + * `"""(\d\d)-(\d\d)-(\d\d\d\d)""".r("month", "day", "year")` matches dates + * and provides its subcomponents through groups named "month", "day" and + * "year". + * + * @param groupNames The names of the groups in the pattern, in the order they appear. + */ + @deprecated("use inline group names like (?X) instead", "2.13.7") + def r(groupNames: String*): Regex = new Regex(s, groupNames: _*) + + /** + * @throws java.lang.IllegalArgumentException If the string does not contain a parsable `Boolean`. + */ + def toBoolean: Boolean = toBooleanImpl(s) + + /** + * Try to parse as a `Boolean` + * @return `Some(true)` if the string is "true" case insensitive, + * `Some(false)` if the string is "false" case insensitive, + * and `None` if the string is anything else + * @throws java.lang.NullPointerException if the string is `null` + */ + def toBooleanOption: Option[Boolean] = StringParsers.parseBool(s) + + /** + * Parse as a `Byte` (string must contain only decimal digits and optional leading `-` or `+`). + * @throws java.lang.NumberFormatException If the string does not contain a parsable `Byte`. + */ + def toByte: Byte = java.lang.Byte.parseByte(s) + + /** + * Try to parse as a `Byte` + * @return `Some(value)` if the string contains a valid byte value, otherwise `None` + * @throws java.lang.NullPointerException if the string is `null` + */ + def toByteOption: Option[Byte] = StringParsers.parseByte(s) + + /** + * Parse as a `Short` (string must contain only decimal digits and optional leading `-` or `+`). + * @throws java.lang.NumberFormatException If the string does not contain a parsable `Short`. + */ + def toShort: Short = java.lang.Short.parseShort(s) + + /** + * Try to parse as a `Short` + * @return `Some(value)` if the string contains a valid short value, otherwise `None` + * @throws java.lang.NullPointerException if the string is `null` + */ + def toShortOption: Option[Short] = StringParsers.parseShort(s) + + /** + * Parse as an `Int` (string must contain only decimal digits and optional leading `-` or `+`). + * @throws java.lang.NumberFormatException If the string does not contain a parsable `Int`. + */ + def toInt: Int = java.lang.Integer.parseInt(s) + + /** + * Try to parse as an `Int` + * @return `Some(value)` if the string contains a valid Int value, otherwise `None` + * @throws java.lang.NullPointerException if the string is `null` + */ + def toIntOption: Option[Int] = StringParsers.parseInt(s) + + /** + * Parse as a `Long` (string must contain only decimal digits and optional leading `-` or `+`). + * @throws java.lang.NumberFormatException If the string does not contain a parsable `Long`. + */ + def toLong: Long = java.lang.Long.parseLong(s) + + /** + * Try to parse as a `Long` + * @return `Some(value)` if the string contains a valid long value, otherwise `None` + * @throws java.lang.NullPointerException if the string is `null` + */ + def toLongOption: Option[Long] = StringParsers.parseLong(s) + + /** + * Parse as a `Float` (surrounding whitespace is removed with a `trim`). + * @throws java.lang.NumberFormatException If the string does not contain a parsable `Float`. + * @throws java.lang.NullPointerException If the string is null. + */ + def toFloat: Float = java.lang.Float.parseFloat(s) + + /** + * Try to parse as a `Float` + * @return `Some(value)` if the string is a parsable `Float`, `None` otherwise + * @throws java.lang.NullPointerException If the string is null + */ + def toFloatOption: Option[Float] = StringParsers.parseFloat(s) + + /** + * Parse as a `Double` (surrounding whitespace is removed with a `trim`). + * @throws java.lang.NumberFormatException If the string does not contain a parsable `Double`. + * @throws java.lang.NullPointerException If the string is null. + */ + def toDouble: Double = java.lang.Double.parseDouble(s) + + /** + * Try to parse as a `Double` + * @return `Some(value)` if the string is a parsable `Double`, `None` otherwise + * @throws java.lang.NullPointerException If the string is null + */ + def toDoubleOption: Option[Double] = StringParsers.parseDouble(s) + + private[this] def toBooleanImpl(s: String): Boolean = + if (s == null) throw new IllegalArgumentException("For input string: \"null\"") + else if (s.equalsIgnoreCase("true")) true + else if (s.equalsIgnoreCase("false")) false + else throw new IllegalArgumentException("For input string: \""+s+"\"") + + def toArray[B >: Char](implicit tag: ClassTag[B]): Array[B] = + if (tag == ClassTag.Char) s.toCharArray.asInstanceOf[Array[B]] + else new WrappedString(s).toArray[B] + + private[this] def unwrapArg(arg: Any): AnyRef = arg match { + case x: ScalaNumber => x.underlying + case x => x.asInstanceOf[AnyRef] + } + + /** Uses the underlying string as a pattern (in a fashion similar to + * printf in C), and uses the supplied arguments to fill in the + * holes. + * + * The interpretation of the formatting patterns is described in + * [[java.util.Formatter]], with the addition that + * classes deriving from `ScalaNumber` (such as [[scala.BigInt]] and + * [[scala.BigDecimal]]) are unwrapped to pass a type which `Formatter` understands. + * + * See [[scala.StringContext#f]] for a formatting interpolator that + * checks the format string at compilation. + * + * @param args the arguments used to instantiating the pattern. + * @throws java.util.IllegalFormatException if the format contains syntax or conversion errors + */ + def format(args: Any*): String = + java.lang.String.format(s, args.map(unwrapArg): _*) + + /** Like `format(args*)` but takes an initial `Locale` parameter + * which influences formatting as in `java.lang.String`'s format. + * + * The interpretation of the formatting patterns is described in + * [[java.util.Formatter]], with the addition that + * classes deriving from `ScalaNumber` (such as `scala.BigInt` and + * `scala.BigDecimal`) are unwrapped to pass a type which `Formatter` + * understands. + * + * @param l an instance of `java.util.Locale` + * @param args the arguments used to instantiating the pattern. + * @throws java.util.IllegalFormatException if the format contains syntax or conversion errors + */ + def formatLocal(l: java.util.Locale, args: Any*): String = + java.lang.String.format(l, s, args.map(unwrapArg): _*) + + def compare(that: String): Int = s.compareTo(that) + + /** Returns true if `this` is less than `that` */ + def < (that: String): Boolean = compare(that) < 0 + + /** Returns true if `this` is greater than `that`. */ + def > (that: String): Boolean = compare(that) > 0 + + /** Returns true if `this` is less than or equal to `that`. */ + def <= (that: String): Boolean = compare(that) <= 0 + + /** Returns true if `this` is greater than or equal to `that`. */ + def >= (that: String): Boolean = compare(that) >= 0 + + /** Counts the number of chars in this string which satisfy a predicate */ + def count(p: (Char) => Boolean): Int = { + var i, res = 0 + val len = s.length + while(i < len) { + if(p(s.charAt(i))) res += 1 + i += 1 + } + res + } + + /** Apply `f` to each element for its side effects. + * Note: [U] parameter needed to help scalac's type inference. + */ + def foreach[U](f: Char => U): Unit = { + val len = s.length + var i = 0 + while(i < len) { + f(s.charAt(i)) + i += 1 + } + } + + /** Tests whether a predicate holds for all chars of this string. + * + * @param p the predicate used to test elements. + * @return `true` if this string is empty or the given predicate `p` + * holds for all chars of this string, otherwise `false`. + */ + def forall(@deprecatedName("f", "2.13.3") p: Char => Boolean): Boolean = { + var i = 0 + val len = s.length + while(i < len) { + if(!p(s.charAt(i))) return false + i += 1 + } + true + } + + /** Applies the given binary operator `op` to the given initial value `z` and all chars + * in this string, going left to right. Returns the initial value if this string is + * empty. + * + * If `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the chars in this string, the + * result is `op( op( ... op( op(z, x,,1,,), x,,2,,) ... ), x,,n,,)`. + * + * @param z An initial value. + * @param op A binary operator. + * @tparam B The result type of the binary operator. + * @return The result of applying `op` to `z` and all chars in this string, + * going left to right. Returns `z` if this string is empty. + */ + def foldLeft[B](z: B)(op: (B, Char) => B): B = { + var v = z + var i = 0 + val len = s.length + while(i < len) { + v = op(v, s.charAt(i)) + i += 1 + } + v + } + + /** Applies the given binary operator `op` to all chars in this string and the given + * initial value `z`, going right to left. Returns the initial value if this string is + * empty. + * + * If `x,,1,,`, `x,,2,,`, ..., `x,,n,,` are the chars in this string, the + * result is `op(x,,1,,, op(x,,2,,, op( ... op(x,,n,,, z) ... )))`. + * + * @param z An initial value. + * @param op A binary operator. + * @tparam B The result type of the binary operator. + * @return The result of applying `op` to all chars in this string + * and `z`, going right to left. Returns `z` if this string + * is empty. + */ + def foldRight[B](z: B)(op: (Char, B) => B): B = { + var v = z + var i = s.length - 1 + while(i >= 0) { + v = op(s.charAt(i), v) + i -= 1 + } + v + } + + /** Alias for [[foldLeft]]. + * + * The type parameter is more restrictive than for `foldLeft` to be + * consistent with [[IterableOnceOps.fold]]. + * + * @tparam A1 The type parameter for the binary operator, a supertype of `Char`. + * @param z An initial value. + * @param op A binary operator. + * @return The result of applying `op` to `z` and all chars in this string, + * going left to right. Returns `z` if this string is empty. + */ + @`inline` def fold[A1 >: Char](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op) + + /** Selects the first char of this string. + * @return the first char of this string. + * @throws NoSuchElementException if the string is empty. + */ + def head: Char = if(s.isEmpty) throw new NoSuchElementException("head of empty String") else s.charAt(0) + + /** Optionally selects the first char. + * @return the first char of this string if it is nonempty, + * `None` if it is empty. + */ + def headOption: Option[Char] = + if(s.isEmpty) None else Some(s.charAt(0)) + + /** Selects the last char of this string. + * @return the last char of this string. + * @throws NoSuchElementException if the string is empty. + */ + def last: Char = if(s.isEmpty) throw new NoSuchElementException("last of empty String") else s.charAt(s.length-1) + + /** Optionally selects the last char. + * @return the last char of this string if it is nonempty, + * `None` if it is empty. + */ + def lastOption: Option[Char] = + if(s.isEmpty) None else Some(s.charAt(s.length-1)) + + /** Produces the range of all indices of this string. + * + * @return a `Range` value from `0` to one less than the length of this string. + */ + def indices: Range = Range(0, s.length) + + /** Iterator can be used only once */ + def iterator: Iterator[Char] = new StringIterator(s) + + /** Stepper can be used with Java 8 Streams. This method is equivalent to a call to + * [[charStepper]]. See also [[codePointStepper]]. + */ + @`inline` def stepper: IntStepper with EfficientSplit = charStepper + + /** Steps over characters in this string. Values are packed in `Int` for efficiency + * and compatibility with Java 8 Streams which have an efficient specialization for `Int`. + */ + @`inline` def charStepper: IntStepper with EfficientSplit = new CharStringStepper(s, 0, s.length) + + /** Steps over code points in this string. + */ + @`inline` def codePointStepper: IntStepper with EfficientSplit = new CodePointStringStepper(s, 0, s.length) + + /** Tests whether the string is not empty. */ + @`inline` def nonEmpty: Boolean = !s.isEmpty + + /** Returns new sequence with elements in reversed order. + * @note $unicodeunaware + */ + def reverse: String = new JStringBuilder(s).reverse().toString + + /** An iterator yielding chars in reversed order. + * + * Note: `xs.reverseIterator` is the same as `xs.reverse.iterator` but implemented more efficiently. + * + * @return an iterator yielding the chars of this string in reversed order + */ + def reverseIterator: Iterator[Char] = new ReverseIterator(s) + + /** Creates a non-strict filter of this string. + * + * @note the difference between `c filter p` and `c withFilter p` is that + * the former creates a new string, whereas the latter only + * restricts the domain of subsequent `map`, `flatMap`, `foreach`, + * and `withFilter` operations. + * + * @param p the predicate used to test elements. + * @return an object of class `stringOps.WithFilter`, which supports + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * All these operations apply to those chars of this string + * which satisfy the predicate `p`. + */ + def withFilter(p: Char => Boolean): StringOps.WithFilter = new StringOps.WithFilter(p, s) + + /** The rest of the string without its first char. + * @throws UnsupportedOperationException if the string is empty. + * @note $unicodeunaware + */ + def tail: String = if(s.isEmpty) throw new UnsupportedOperationException("tail of empty String") else slice(1, s.length) + + /** The initial part of the string without its last char. + * @throws UnsupportedOperationException if the string is empty. + * @note $unicodeunaware + */ + def init: String = if(s.isEmpty) throw new UnsupportedOperationException("init of empty String") else slice(0, s.length-1) + + /** A string containing the first `n` chars of this string. + * @note $unicodeunaware + */ + def take(n: Int): String = slice(0, min(n, s.length)) + + /** The rest of the string without its `n` first chars. + * @note $unicodeunaware + */ + def drop(n: Int): String = slice(min(n, s.length), s.length) + + /** A string containing the last `n` chars of this string. + * @note $unicodeunaware + */ + def takeRight(n: Int): String = drop(s.length - max(n, 0)) + + /** The rest of the string without its `n` last chars. + * @note $unicodeunaware + */ + def dropRight(n: Int): String = take(s.length - max(n, 0)) + + /** Iterates over the tails of this string. The first value will be this + * string and the final one will be an empty string, with the intervening + * values the results of successive applications of `tail`. + * + * @return an iterator over all the tails of this string + * @note $unicodeunaware + */ + def tails: Iterator[String] = iterateUntilEmpty(_.tail) + + /** Iterates over the inits of this string. The first value will be this + * string and the final one will be an empty string, with the intervening + * values the results of successive applications of `init`. + * + * @return an iterator over all the inits of this string + * @note $unicodeunaware + */ + def inits: Iterator[String] = iterateUntilEmpty(_.init) + + // A helper for tails and inits. + private[this] def iterateUntilEmpty(f: String => String): Iterator[String] = + Iterator.iterate(s)(f).takeWhile(x => !x.isEmpty) ++ Iterator.single("") + + /** Selects all chars of this string which satisfy a predicate. */ + def filter(pred: Char => Boolean): String = { + val len = s.length + val sb = new JStringBuilder(len) + var i = 0 + while (i < len) { + val x = s.charAt(i) + if(pred(x)) sb.append(x) + i += 1 + } + if(len == sb.length()) s else sb.toString + } + + /** Selects all chars of this string which do not satisfy a predicate. */ + @`inline` def filterNot(pred: Char => Boolean): String = filter(c => !pred(c)) + + /** Copy chars of this string to an array. + * Fills the given array `xs` starting at index 0. + * Copying will stop once either the entire string has been copied + * or the end of the array is reached + * + * @param xs the array to fill. + */ + @`inline` def copyToArray(xs: Array[Char]): Int = + copyToArray(xs, 0, Int.MaxValue) + + /** Copy chars of this string to an array. + * Fills the given array `xs` starting at index `start`. + * Copying will stop once either the entire string has been copied + * or the end of the array is reached + * + * @param xs the array to fill. + * @param start the starting index. + */ + @`inline` def copyToArray(xs: Array[Char], start: Int): Int = + copyToArray(xs, start, Int.MaxValue) + + /** Copy chars of this string to an array. + * Fills the given array `xs` starting at index `start` with at most `len` chars. + * Copying will stop once either the entire string has been copied, + * or the end of the array is reached or `len` chars have been copied. + * + * @param xs the array to fill. + * @param start the starting index. + * @param len the maximal number of elements to copy. + */ + def copyToArray(xs: Array[Char], start: Int, len: Int): Int = { + val copied = IterableOnce.elemsToCopyToArray(s.length, xs.length, start, len) + if (copied > 0) { + s.getChars(0, copied, xs, start) + } + copied + } + + /** Finds index of the first char satisfying some predicate after or at some start index. + * + * @param p the predicate used to test elements. + * @param from the start index + * @return the index `>= from` of the first element of this string that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + def indexWhere(p: Char => Boolean, from: Int = 0): Int = { + val len = s.length + var i = from + while(i < len) { + if(p(s.charAt(i))) return i + i += 1 + } + -1 + } + + /** Finds index of the last char satisfying some predicate before or at some end index. + * + * @param p the predicate used to test elements. + * @param end the end index + * @return the index `<= end` of the last element of this string that satisfies the predicate `p`, + * or `-1`, if none exists. + */ + def lastIndexWhere(p: Char => Boolean, end: Int = Int.MaxValue): Int = { + val len = s.length + var i = min(end, len-1) + while(i >= 0) { + if(p(s.charAt(i))) return i + i -= 1 + } + -1 + } + + /** Tests whether a predicate holds for at least one char of this string. */ + def exists(p: Char => Boolean): Boolean = indexWhere(p) != -1 + + /** Finds the first char of the string satisfying a predicate, if any. + * + * @param p the predicate used to test elements. + * @return an option value containing the first element in the string + * that satisfies `p`, or `None` if none exists. + */ + def find(p: Char => Boolean): Option[Char] = indexWhere(p) match { + case -1 => None + case i => Some(s.charAt(i)) + } + + /** Drops longest prefix of chars that satisfy a predicate. + * + * @param p The predicate used to test elements. + * @return the longest suffix of this string whose first element + * does not satisfy the predicate `p`. + */ + def dropWhile(p: Char => Boolean): String = indexWhere(c => !p(c)) match { + case -1 => "" + case i => s.substring(i) + } + + /** Takes longest prefix of chars that satisfy a predicate. */ + def takeWhile(p: Char => Boolean): String = indexWhere(c => !p(c)) match { + case -1 => s + case i => s.substring(0, i) + } + + /** Splits this string into two at a given position. + * Note: `c splitAt n` is equivalent to `(c take n, c drop n)`. + * + * @param n the position at which to split. + * @return a pair of strings consisting of the first `n` + * chars of this string, and the other chars. + * @note $unicodeunaware + */ + def splitAt(n: Int): (String, String) = (take(n), drop(n)) + + /** Splits this string into a prefix/suffix pair according to a predicate. + * + * Note: `c span p` is equivalent to (but more efficient than) + * `(c takeWhile p, c dropWhile p)`, provided the evaluation of the + * predicate `p` does not cause any side-effects. + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of this string whose + * chars all satisfy `p`, and the rest of this string. + */ + def span(p: Char => Boolean): (String, String) = indexWhere(c => !p(c)) match { + case -1 => (s, "") + case i => (s.substring(0, i), s.substring(i)) + } + + /** Partitions elements in fixed size strings. + * @see [[scala.collection.Iterator]], method `grouped` + * + * @param size the number of elements per group + * @return An iterator producing strings of size `size`, except the + * last will be less than size `size` if the elements don't divide evenly. + * @note $unicodeunaware + */ + def grouped(size: Int): Iterator[String] = new StringOps.GroupedIterator(s, size) + + /** A pair of, first, all chars that satisfy predicate `p` and, second, all chars that do not. */ + def partition(p: Char => Boolean): (String, String) = { + val res1, res2 = new JStringBuilder + var i = 0 + val len = s.length + while(i < len) { + val x = s.charAt(i) + (if(p(x)) res1 else res2).append(x) + i += 1 + } + (res1.toString, res2.toString) + } + + /** Applies a function `f` to each character of the string and returns a pair of strings: the first one + * made of those characters returned by `f` that were wrapped in [[scala.util.Left]], and the second + * one made of those wrapped in [[scala.util.Right]]. + * + * Example: + * {{{ + * val xs = "1one2two3three" partitionMap { c => + * if (c > 'a') Left(c) else Right(c) + * } + * // xs == ("onetwothree", "123") + * }}} + * + * @param f the 'split function' mapping the elements of this string to an [[scala.util.Either]] + * + * @return a pair of strings: the first one made of those characters returned by `f` that were wrapped in [[scala.util.Left]], + * and the second one made of those wrapped in [[scala.util.Right]]. + */ + def partitionMap(f: Char => Either[Char,Char]): (String, String) = { + val res1, res2 = new JStringBuilder + var i = 0 + val len = s.length + while(i < len) { + f(s.charAt(i)) match { + case Left(c) => res1.append(c) + case Right(c) => res2.append(c) + } + i += 1 + } + (res1.toString, res2.toString) + } + + /** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is + * invoked on the returned `LazyZip2` decorator. + * + * Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of + * constructing and deconstructing intermediary tuples. + * + * {{{ + * val xs = List(1, 2, 3) + * val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d) + * // res == List(4, 8, 12) + * }}} + * + * @param that the iterable providing the second element of each eventual pair + * @tparam B the type of the second element in each eventual pair + * @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs + * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. + */ + def lazyZip[B](that: Iterable[B]): LazyZip2[Char, B, String] = new LazyZip2(s, new WrappedString(s), that) + + + /* ************************************************************************************************************ + The remaining methods are provided for completeness but they delegate to WrappedString implementations which + may not provide the best possible performance. We need them in `StringOps` because their return type + mentions `C` (which is `String` in `StringOps` and `WrappedString` in `WrappedString`). + ************************************************************************************************************ */ + + + /** Computes the multiset difference between this string and another sequence. + * + * @param that the sequence of chars to remove + * @return a new string which contains all chars of this string + * except some of occurrences of elements that also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form + * part of the result, but any following occurrences will. + * @note $unicodeunaware + */ + def diff[B >: Char](that: Seq[B]): String = new WrappedString(s).diff(that).unwrap + + /** Computes the multiset intersection between this string and another sequence. + * + * @param that the sequence of chars to intersect with. + * @return a new string which contains all chars of this string + * which also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained + * in the result, but any following occurrences will be omitted. + * @note $unicodeunaware + */ + def intersect[B >: Char](that: Seq[B]): String = new WrappedString(s).intersect(that).unwrap + + /** Selects all distinct chars of this string ignoring the duplicates. + * + * @note $unicodeunaware + */ + def distinct: String = new WrappedString(s).distinct.unwrap + + /** Selects all distinct chars of this string ignoring the duplicates as determined by `==` after applying + * the transforming function `f`. + * + * @param f The transforming function whose result is used to determine the uniqueness of each element + * @tparam B the type of the elements after being transformed by `f` + * @return a new string consisting of all the chars of this string without duplicates. + * @note $unicodeunaware + */ + def distinctBy[B](f: Char => B): String = new WrappedString(s).distinctBy(f).unwrap + + /** Sorts the characters of this string according to an Ordering. + * + * The sort is stable. That is, elements that are equal (as determined by + * `ord.compare`) appear in the same order in the sorted sequence as in the original. + * + * @see [[scala.math.Ordering]] + * + * @param ord the ordering to be used to compare elements. + * @return a string consisting of the chars of this string + * sorted according to the ordering `ord`. + * @note $unicodeunaware + */ + def sorted[B >: Char](implicit ord: Ordering[B]): String = new WrappedString(s).sorted(ord).unwrap + + /** Sorts this string according to a comparison function. + * + * The sort is stable. That is, elements that are equal (as determined by + * `lt`) appear in the same order in the sorted sequence as in the original. + * + * @param lt the comparison function which tests whether + * its first argument precedes its second argument in + * the desired ordering. + * @return a string consisting of the elements of this string + * sorted according to the comparison function `lt`. + * @note $unicodeunaware + */ + def sortWith(lt: (Char, Char) => Boolean): String = new WrappedString(s).sortWith(lt).unwrap + + /** Sorts this string according to the Ordering which results from transforming + * an implicitly given Ordering with a transformation function. + * + * The sort is stable. That is, elements that are equal (as determined by + * `ord.compare`) appear in the same order in the sorted sequence as in the original. + * + * @see [[scala.math.Ordering]] + * @param f the transformation function mapping elements + * to some other domain `B`. + * @param ord the ordering assumed on domain `B`. + * @tparam B the target type of the transformation `f`, and the type where + * the ordering `ord` is defined. + * @return a string consisting of the chars of this string + * sorted according to the ordering where `x < y` if + * `ord.lt(f(x), f(y))`. + * @note $unicodeunaware + */ + def sortBy[B](f: Char => B)(implicit ord: Ordering[B]): String = new WrappedString(s).sortBy(f)(ord).unwrap + + /** Partitions this string into a map of strings according to some discriminator function. + * + * @param f the discriminator function. + * @tparam K the type of keys returned by the discriminator function. + * @return A map from keys to strings such that the following invariant holds: + * {{{ + * (xs groupBy f)(k) = xs filter (x => f(x) == k) + * }}} + * That is, every key `k` is bound to a string of those elements `x` + * for which `f(x)` equals `k`. + * @note $unicodeunaware + */ + def groupBy[K](f: Char => K): immutable.Map[K, String] = new WrappedString(s).groupBy(f).view.mapValues(_.unwrap).toMap + + /** Groups chars in fixed size blocks by passing a "sliding window" + * over them (as opposed to partitioning them, as is done in grouped.) + * @see [[scala.collection.Iterator]], method `sliding` + * + * @param size the number of chars per group + * @param step the distance between the first chars of successive groups + * @return An iterator producing strings of size `size`, except the + * last element (which may be the only element) will be truncated + * if there are fewer than `size` chars remaining to be grouped. + * @note $unicodeunaware + */ + def sliding(size: Int, step: Int = 1): Iterator[String] = new WrappedString(s).sliding(size, step).map(_.unwrap) + + /** Iterates over combinations of elements. + * + * A '''combination''' of length `n` is a sequence of `n` elements selected in order of their first index in this sequence. + * + * For example, `"xyx"` has two combinations of length 2. The `x` is selected first: `"xx"`, `"xy"`. + * The sequence `"yx"` is not returned as a combination because it is subsumed by `"xy"`. + * + * If there is more than one way to generate the same combination, only one will be returned. + * + * For example, the result `"xy"` arbitrarily selected one of the `x` elements. + * + * As a further illustration, `"xyxx"` has three different ways to generate `"xy"` because there are three elements `x` + * to choose from. Moreover, there are three unordered pairs `"xx"` but only one is returned. + * + * It is not specified which of these equal combinations is returned. It is an implementation detail + * that should not be relied on. For example, the combination `"xx"` does not necessarily contain + * the first `x` in this sequence. This behavior is observable if the elements compare equal + * but are not identical. + * + * As a consequence, `"xyx".combinations(3).next()` is `"xxy"`: the combination does not reflect the order + * of the original sequence, but the order in which elements were selected, by "first index"; + * the order of each `x` element is also arbitrary. + * + * @return An Iterator which traverses the n-element combinations of this string. + * @example {{{ + * "abbbc".combinations(2).foreach(println) + * // ab + * // ac + * // bb + * // bc + * "bab".combinations(2).foreach(println) + * // bb + * // ba + * }}} + * @note $unicodeunaware + */ + def combinations(n: Int): Iterator[String] = new WrappedString(s).combinations(n).map(_.unwrap) + + /** Iterates over distinct permutations of elements. + * + * @return An Iterator which traverses the distinct permutations of this string. + * @example {{{ + * "abb".permutations.foreach(println) + * // abb + * // bab + * // bba + * }}} + * @note $unicodeunaware + */ + def permutations: Iterator[String] = new WrappedString(s).permutations.map(_.unwrap) +} + +final case class StringView(s: String) extends AbstractIndexedSeqView[Char] { + def length = s.length + @throws[StringIndexOutOfBoundsException] + def apply(n: Int) = s.charAt(n) + override def toString: String = s"StringView($s)" +} diff --git a/library/src/scala/collection/StringParsers.scala b/library/src/scala/collection/StringParsers.scala new file mode 100644 index 000000000000..36108dc539da --- /dev/null +++ b/library/src/scala/collection/StringParsers.scala @@ -0,0 +1,321 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection + +import scala.annotation.tailrec + +/** A module containing the implementations of parsers from strings to numeric types, and boolean + */ +private[scala] object StringParsers { + + //compile-time constant helpers + + //Int.MinValue == -2147483648 + private final val intOverflowBoundary = -214748364 + private final val intOverflowDigit = 9 + //Long.MinValue == -9223372036854775808L + private final val longOverflowBoundary = -922337203685477580L + private final val longOverflowDigit = 9 + + private final val POS = true + + @inline + private[this] final def decValue(ch: Char): Int = java.lang.Character.digit(ch, 10) + + @inline + private[this] final def stepToOverflow(from: String, len: Int, agg: Int, isPositive: Boolean, min: Int): Option[Int] = { + @tailrec + def rec(i: Int, agg: Int): Option[Int] = + if (agg < min) None + else if (i == len) { + if (!isPositive) Some(agg) + else if (agg == min) None + else Some(-agg) + } + else { + val digit = decValue(from.charAt(i)) + if (digit == -1) None + else rec(i + 1, agg * 10 - digit) + } + rec(1, agg) + } + + @inline + private[this] final def isDigit(c: Char): Boolean = c >= '0' && c <= '9' + + //bool + @inline + final def parseBool(from: String): Option[Boolean] = + if (from.equalsIgnoreCase("true")) Some(true) + else if (from.equalsIgnoreCase("false")) Some(false) + else None + + //integral types + final def parseByte(from: String): Option[Byte] = { + val len = from.length() + //empty strings parse to None + if (len == 0) None + else { + val first = from.charAt(0) + val v = decValue(first) + if (len == 1) { + //"+" and "-" parse to None + if (v > -1) Some(v.toByte) + else None + } + else if (v > -1) stepToOverflow(from, len, -v, POS, Byte.MinValue).map(_.toByte) + else if (first == '+') stepToOverflow(from, len, 0, POS, Byte.MinValue).map(_.toByte) + else if (first == '-') stepToOverflow(from, len, 0, !POS, Byte.MinValue).map(_.toByte) + else None + } + } + + final def parseShort(from: String): Option[Short] = { + val len = from.length() + //empty strings parse to None + if (len == 0) None + else { + val first = from.charAt(0) + val v = decValue(first) + if (len == 1) { + //"+" and "-" parse to None + if (v > -1) Some(v.toShort) + else None + } + else if (v > -1) stepToOverflow(from, len, -v, POS, Short.MinValue).map(_.toShort) + else if (first == '+') stepToOverflow(from, len, 0, POS, Short.MinValue).map(_.toShort) + else if (first == '-') stepToOverflow(from, len, 0, !POS, Short.MinValue).map(_.toShort) + else None + } + } + + final def parseInt(from: String): Option[Int] = { + val len = from.length() + + @tailrec + def step(i: Int, agg: Int, isPositive: Boolean): Option[Int] = { + if (i == len) { + if (!isPositive) Some(agg) + else if (agg == Int.MinValue) None + else Some(-agg) + } + else if (agg < intOverflowBoundary) None + else { + val digit = decValue(from.charAt(i)) + if (digit == -1 || (agg == intOverflowBoundary && digit == intOverflowDigit)) None + else step(i + 1, (agg * 10) - digit, isPositive) + } + } + //empty strings parse to None + if (len == 0) None + else { + val first = from.charAt(0) + val v = decValue(first) + if (len == 1) { + //"+" and "-" parse to None + if (v > -1) Some(v) + else None + } + else if (v > -1) step(1, -v, POS) + else if (first == '+') step(1, 0, POS) + else if (first == '-') step(1, 0, !POS) + else None + } + } + + final def parseLong(from: String): Option[Long] = { + //like parseInt, but Longer + val len = from.length() + + @tailrec + def step(i: Int, agg: Long, isPositive: Boolean): Option[Long] = { + if (i == len) { + if (isPositive && agg == Long.MinValue) None + else if (isPositive) Some(-agg) + else Some(agg) + } + else if (agg < longOverflowBoundary) None + else { + val digit = decValue(from.charAt(i)) + if (digit == -1 || (agg == longOverflowBoundary && digit == longOverflowDigit)) None + else step(i + 1, agg * 10 - digit, isPositive) + } + } + //empty strings parse to None + if (len == 0) None + else { + val first = from.charAt(0) + val v = decValue(first).toLong + if (len == 1) { + //"+" and "-" parse to None + if (v > -1) Some(v) + else None + } + else if (v > -1) step(1, -v, POS) + else if (first == '+') step(1, 0, POS) + else if (first == '-') step(1, 0, !POS) + else None + } + } + + //floating point + final def checkFloatFormat(format: String): Boolean = { + //indices are tracked with a start index which points *at* the first index + //and an end index which points *after* the last index + //so that slice length === end - start + //thus start == end <=> empty slice + //and format.substring(start, end) is equivalent to the slice + + //some utilities for working with index bounds into the original string + @inline + def forAllBetween(start: Int, end: Int, pred: Char => Boolean): Boolean = { + @tailrec + def rec(i: Int): Boolean = i >= end || pred(format.charAt(i)) && rec(i + 1) + rec(start) + } + + //one after last index for the predicate to hold, or `from` if none hold + //may point after the end of the string + @inline + def skipIndexWhile(predicate: Char => Boolean, from: Int, until: Int): Int = { + @tailrec @inline + def rec(i: Int): Int = if ((i < until) && predicate(format.charAt(i))) rec(i + 1) + else i + rec(from) + } + + + def isHexFloatLiteral(startIndex: Int, endIndex: Int): Boolean = { + def isHexDigit(ch: Char) = ((ch >= '0' && ch <= '9') || + (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F')) + + def prefixOK(startIndex: Int, endIndex: Int): Boolean = { + val len = endIndex - startIndex + (len > 0) && { + //the prefix part is + //hexDigits + //hexDigits. + //hexDigits.hexDigits + //.hexDigits + //but not . + if (format.charAt(startIndex) == '.') { + (len > 1) && forAllBetween(startIndex + 1, endIndex, isHexDigit) + } else { + val noLeading = skipIndexWhile(isHexDigit, startIndex, endIndex) + (noLeading >= endIndex) || + ((format.charAt(noLeading) == '.') && forAllBetween(noLeading + 1, endIndex, isHexDigit)) + } + } + } + + def postfixOK(startIndex: Int, endIndex: Int): Boolean = + (startIndex < endIndex) && { + (forAllBetween(startIndex, endIndex, isDigit)) || { + val startchar = format.charAt(startIndex) + (startchar == '+' || startchar == '-') && + (endIndex - startIndex > 1) && + forAllBetween(startIndex + 1, endIndex, isDigit) + } + } + // prefix [pP] postfix + val pIndex = format.indexWhere(ch => ch == 'p' || ch == 'P', startIndex) + (pIndex <= endIndex) && prefixOK(startIndex, pIndex) && postfixOK(pIndex + 1, endIndex) + } + + def isDecFloatLiteral(startIndex: Int, endIndex: Int): Boolean = { + //invariant: endIndex > startIndex + + def isExp(c: Char): Boolean = c == 'e' || c == 'E' + + def expOK(startIndex: Int, endIndex: Int): Boolean = + (startIndex < endIndex) && { + val startChar = format.charAt(startIndex) + if (startChar == '+' || startChar == '-') + (endIndex > (startIndex + 1)) && + skipIndexWhile(isDigit, startIndex + 1, endIndex) == endIndex + else skipIndexWhile(isDigit, startIndex, endIndex) == endIndex + } + + //significant can be one of + //* digits.digits + //* .digits + //* digits. + //but not just . + val startChar = format.charAt(startIndex) + if (startChar == '.') { + val noSignificant = skipIndexWhile(isDigit, startIndex + 1, endIndex) + // a digit is required followed by optional exp + (noSignificant > startIndex + 1) && (noSignificant >= endIndex || + isExp(format.charAt(noSignificant)) && expOK(noSignificant + 1, endIndex) + ) + } + else if (isDigit(startChar)) { + // one set of digits, then optionally a period, then optionally another set of digits, then optionally an exponent + val noInt = skipIndexWhile(isDigit, startIndex, endIndex) + // just the digits + (noInt == endIndex) || { + if (format.charAt(noInt) == '.') { + val noSignificant = skipIndexWhile(isDigit, noInt + 1, endIndex) + (noSignificant >= endIndex) || //no exponent + isExp(format.charAt(noSignificant)) && expOK(noSignificant + 1, endIndex) + } else + isExp(format.charAt(noInt)) && expOK(noInt + 1, endIndex) + } + } + else false + } + + //count 0x00 to 0x20 as "whitespace", and nothing else + val unspacedStart = format.indexWhere(ch => ch.toInt > 0x20) + val unspacedEnd = format.lastIndexWhere(ch => ch.toInt > 0x20) + 1 + + if (unspacedStart == -1 || unspacedStart >= unspacedEnd || unspacedEnd <= 0) false + else { + //all formats can have a sign + val unsigned = { + val startchar = format.charAt(unspacedStart) + if (startchar == '-' || startchar == '+') unspacedStart + 1 else unspacedStart + } + if (unsigned >= unspacedEnd) false + //that's it for NaN and Infinity + else if (format.charAt(unsigned) == 'N') format.substring(unsigned, unspacedEnd) == "NaN" + else if (format.charAt(unsigned) == 'I') format.substring(unsigned, unspacedEnd) == "Infinity" + else { + //all other formats can have a format suffix + val desuffixed = { + val endchar = format.charAt(unspacedEnd - 1) + if (endchar == 'f' || endchar == 'F' || endchar == 'd' || endchar == 'D') unspacedEnd - 1 + else unspacedEnd + } + val len = desuffixed - unsigned + if (len <= 0) false + else if (len >= 2 && (format.charAt(unsigned + 1) == 'x' || format.charAt(unsigned + 1) == 'X')) + format.charAt(unsigned) == '0' && isHexFloatLiteral(unsigned + 2, desuffixed) + else isDecFloatLiteral(unsigned, desuffixed) + } + } + } + + @inline + def parseFloat(from: String): Option[Float] = + if (checkFloatFormat(from)) Some(java.lang.Float.parseFloat(from)) + else None + + @inline + def parseDouble(from: String): Option[Double] = + if (checkFloatFormat(from)) Some(java.lang.Double.parseDouble(from)) + else None + +} diff --git a/library/src/scala/collection/View.scala b/library/src/scala/collection/View.scala new file mode 100644 index 000000000000..f304b8931f14 --- /dev/null +++ b/library/src/scala/collection/View.scala @@ -0,0 +1,535 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +import scala.annotation.{nowarn, tailrec} +import scala.collection.mutable.{ArrayBuffer, Builder} +import scala.collection.immutable.LazyList + +/** Views are collections whose transformation operations are non strict: the resulting elements + * are evaluated only when the view is effectively traversed (e.g. using `foreach` or `foldLeft`), + * or when the view is converted to a strict collection type (using the `to` operation). + * @define coll view + * @define Coll `View` + */ +trait View[+A] extends Iterable[A] with IterableOps[A, View, View[A]] with IterableFactoryDefaults[A, View] with Serializable { + + override def view: View[A] = this + + override def iterableFactory: IterableFactory[View] = View + + override def empty: scala.collection.View[A] = iterableFactory.empty + + override def toString: String = className + "()" + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix: String = "View" + + @deprecated("Views no longer know about their underlying collection type; .force always returns an IndexedSeq", "2.13.0") + @`inline` def force: IndexedSeq[A] = toIndexedSeq +} + +/** This object reifies operations on views as case classes + * + * @define Coll View + * @define coll view + */ +@SerialVersionUID(3L) +object View extends IterableFactory[View] { + + /** + * @return A `View[A]` whose underlying iterator is provided by the `it` parameter-less function. + * + * @param it Function creating the iterator to be used by the view. This function must always return + * a fresh `Iterator`, otherwise the resulting view will be effectively iterable only once. + * + * @tparam A View element type + */ + def fromIteratorProvider[A](it: () => Iterator[A]): View[A] = new AbstractView[A] { + def iterator = it() + } + + /** + * @return A view iterating over the given `Iterable` + * + * @param it The `IterableOnce` to view. A proper `Iterable` is used directly. If it is really only + * `IterableOnce` it gets memoized on the first traversal. + * + * @tparam E View element type + */ + def from[E](it: IterableOnce[E]): View[E] = it match { + case it: View[E] => it + case it: Iterable[E] => View.fromIteratorProvider(() => it.iterator) + case _ => LazyList.from(it).view + } + + def empty[A]: View[A] = Empty + + def newBuilder[A]: Builder[A, View[A]] = ArrayBuffer.newBuilder[A].mapResult(from) + + override def apply[A](xs: A*): View[A] = new Elems(xs: _*) + + /** The empty view */ + @SerialVersionUID(3L) + case object Empty extends AbstractView[Nothing] { + def iterator = Iterator.empty + override def knownSize = 0 + override def isEmpty: Boolean = true + } + + /** A view with exactly one element */ + @SerialVersionUID(3L) + class Single[A](a: A) extends AbstractView[A] { + def iterator: Iterator[A] = Iterator.single(a) + override def knownSize: Int = 1 + override def isEmpty: Boolean = false + } + + /** A view with given elements */ + @SerialVersionUID(3L) + class Elems[A](xs: A*) extends AbstractView[A] { + def iterator = xs.iterator + override def knownSize = xs.knownSize + override def isEmpty: Boolean = xs.isEmpty + } + + /** A view containing the results of some element computation a number of times. */ + @SerialVersionUID(3L) + class Fill[A](n: Int)(elem: => A) extends AbstractView[A] { + def iterator = Iterator.fill(n)(elem) + override def knownSize: Int = 0 max n + override def isEmpty: Boolean = n <= 0 + } + + /** A view containing values of a given function over a range of integer values starting from 0. */ + @SerialVersionUID(3L) + class Tabulate[A](n: Int)(f: Int => A) extends AbstractView[A] { + def iterator: Iterator[A] = Iterator.tabulate(n)(f) + override def knownSize: Int = 0 max n + override def isEmpty: Boolean = n <= 0 + } + + /** A view containing repeated applications of a function to a start value */ + @SerialVersionUID(3L) + class Iterate[A](start: A, len: Int)(f: A => A) extends AbstractView[A] { + def iterator: Iterator[A] = Iterator.iterate(start)(f).take(len) + override def knownSize: Int = 0 max len + override def isEmpty: Boolean = len <= 0 + } + + /** A view that uses a function `f` to produce elements of type `A` and update + * an internal state `S`. + */ + @SerialVersionUID(3L) + class Unfold[A, S](initial: S)(f: S => Option[(A, S)]) extends AbstractView[A] { + def iterator: Iterator[A] = Iterator.unfold(initial)(f) + } + + /** An `IterableOps` whose collection type and collection type constructor are unknown */ + type SomeIterableOps[A] = IterableOps[A, AnyConstr, _] + + /** A view that filters an underlying collection. */ + @SerialVersionUID(3L) + class Filter[A](val underlying: SomeIterableOps[A], val p: A => Boolean, val isFlipped: Boolean) extends AbstractView[A] { + def iterator = underlying.iterator.filterImpl(p, isFlipped) + override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + } + + object Filter { + def apply[A](underlying: Iterable[A], p: A => Boolean, isFlipped: Boolean): Filter[A] = + underlying match { + case filter: Filter[A] if filter.isFlipped == isFlipped => new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) + case _ => new Filter(underlying, p, isFlipped) + } + } + + /** A view that removes the duplicated elements as determined by the transformation function `f` */ + @SerialVersionUID(3L) + class DistinctBy[A, B](underlying: SomeIterableOps[A], f: A => B) extends AbstractView[A] { + def iterator: Iterator[A] = underlying.iterator.distinctBy(f) + override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + @SerialVersionUID(3L) + class LeftPartitionMapped[A, A1, A2](underlying: SomeIterableOps[A], f: A => Either[A1, A2]) extends AbstractView[A1] { + def iterator: AbstractIterator[A1] = new AbstractIterator[A1] { + private[this] val self = underlying.iterator + private[this] var hd: A1 = _ + private[this] var hdDefined: Boolean = false + def hasNext = hdDefined || { + @tailrec + def findNext(): Boolean = + if (self.hasNext) { + f(self.next()) match { + case Left(a1) => hd = a1; hdDefined = true; true + case Right(_) => findNext() + } + } else false + findNext() + } + def next() = + if (hasNext) { + hdDefined = false + hd + } else Iterator.empty.next() + } + } + + @SerialVersionUID(3L) + class RightPartitionMapped[A, A1, A2](underlying: SomeIterableOps[A], f: A => Either[A1, A2]) extends AbstractView[A2] { + def iterator: AbstractIterator[A2] = new AbstractIterator[A2] { + private[this] val self = underlying.iterator + private[this] var hd: A2 = _ + private[this] var hdDefined: Boolean = false + def hasNext = hdDefined || { + @tailrec + def findNext(): Boolean = + if (self.hasNext) { + f(self.next()) match { + case Left(_) => findNext() + case Right(a2) => hd = a2; hdDefined = true; true + } + } else false + findNext() + } + def next() = + if (hasNext) { + hdDefined = false + hd + } else Iterator.empty.next() + } + } + + /** A view that drops leading elements of the underlying collection. */ + @SerialVersionUID(3L) + class Drop[A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + def iterator = underlying.iterator.drop(n) + protected val normN = n max 0 + override def knownSize = { + val size = underlying.knownSize + if (size >= 0) (size - normN) max 0 else -1 + } + override def isEmpty: Boolean = iterator.isEmpty + } + + /** A view that drops trailing elements of the underlying collection. */ + @SerialVersionUID(3L) + class DropRight[A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + def iterator = dropRightIterator(underlying.iterator, n) + protected val normN = n max 0 + override def knownSize = { + val size = underlying.knownSize + if (size >= 0) (size - normN) max 0 else -1 + } + override def isEmpty: Boolean = + if(knownSize >= 0) knownSize == 0 + else iterator.isEmpty + } + + @SerialVersionUID(3L) + class DropWhile[A](underlying: SomeIterableOps[A], p: A => Boolean) extends AbstractView[A] { + def iterator = underlying.iterator.dropWhile(p) + override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + } + + /** A view that takes leading elements of the underlying collection. */ + @SerialVersionUID(3L) + class Take[+A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + def iterator = underlying.iterator.take(n) + protected val normN = n max 0 + override def knownSize = { + val size = underlying.knownSize + if (size >= 0) size min normN else -1 + } + override def isEmpty: Boolean = iterator.isEmpty + } + + /** A view that takes trailing elements of the underlying collection. */ + @SerialVersionUID(3L) + class TakeRight[+A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + def iterator = takeRightIterator(underlying.iterator, n) + protected val normN = n max 0 + override def knownSize = { + val size = underlying.knownSize + if (size >= 0) size min normN else -1 + } + override def isEmpty: Boolean = + if(knownSize >= 0) knownSize == 0 + else iterator.isEmpty + } + + @SerialVersionUID(3L) + class TakeWhile[A](underlying: SomeIterableOps[A], p: A => Boolean) extends AbstractView[A] { + def iterator: Iterator[A] = underlying.iterator.takeWhile(p) + override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + } + + @SerialVersionUID(3L) + class ScanLeft[+A, +B](underlying: SomeIterableOps[A], z: B, op: (B, A) => B) extends AbstractView[B] { + def iterator: Iterator[B] = underlying.iterator.scanLeft(z)(op) + override def knownSize: Int = { + val size = underlying.knownSize + if (size >= 0) size + 1 else -1 + } + override def isEmpty: Boolean = iterator.isEmpty + } + + /** A view that maps elements of the underlying collection. */ + @SerialVersionUID(3L) + class Map[+A, +B](underlying: SomeIterableOps[A], f: A => B) extends AbstractView[B] { + def iterator = underlying.iterator.map(f) + override def knownSize = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + /** A view that flatmaps elements of the underlying collection. */ + @SerialVersionUID(3L) + class FlatMap[A, B](underlying: SomeIterableOps[A], f: A => IterableOnce[B]) extends AbstractView[B] { + def iterator = underlying.iterator.flatMap(f) + override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = iterator.isEmpty + } + + /** A view that collects elements of the underlying collection. */ + @SerialVersionUID(3L) + class Collect[+A, B](underlying: SomeIterableOps[A], pf: PartialFunction[A, B]) extends AbstractView[B] { + def iterator = underlying.iterator.collect(pf) + } + + /** A view that concatenates elements of the prefix collection or iterator with the elements + * of the suffix collection or iterator. + */ + @SerialVersionUID(3L) + class Concat[A](prefix: SomeIterableOps[A], suffix: SomeIterableOps[A]) extends AbstractView[A] { + def iterator = prefix.iterator ++ suffix.iterator + override def knownSize = { + val prefixSize = prefix.knownSize + if (prefixSize >= 0) { + val suffixSize = suffix.knownSize + if (suffixSize >= 0) prefixSize + suffixSize + else -1 + } + else -1 + } + override def isEmpty: Boolean = prefix.isEmpty && suffix.isEmpty + } + + /** A view that zips elements of the underlying collection with the elements + * of another collection. + */ + @SerialVersionUID(3L) + class Zip[A, B](underlying: SomeIterableOps[A], other: Iterable[B]) extends AbstractView[(A, B)] { + def iterator = underlying.iterator.zip(other) + override def knownSize = { + val s1 = underlying.knownSize + if (s1 == 0) 0 else { + val s2 = other.knownSize + if (s2 == 0) 0 else s1 min s2 + } + } + override def isEmpty: Boolean = underlying.isEmpty || other.isEmpty + } + + /** A view that zips elements of the underlying collection with the elements + * of another collection. If one of the two collections is shorter than the other, + * placeholder elements are used to extend the shorter collection to the length of the longer. + */ + @SerialVersionUID(3L) + class ZipAll[A, B](underlying: SomeIterableOps[A], other: Iterable[B], thisElem: A, thatElem: B) extends AbstractView[(A, B)] { + def iterator = underlying.iterator.zipAll(other, thisElem, thatElem) + override def knownSize = { + val s1 = underlying.knownSize + if(s1 == -1) -1 else { + val s2 = other.knownSize + if(s2 == -1) -1 else s1 max s2 + } + } + override def isEmpty: Boolean = underlying.isEmpty && other.isEmpty + } + + /** A view that appends an element to its elements */ + @SerialVersionUID(3L) + class Appended[+A](underlying: SomeIterableOps[A], elem: A) extends AbstractView[A] { + def iterator: Iterator[A] = new Concat(underlying, new View.Single(elem)).iterator + override def knownSize: Int = { + val size = underlying.knownSize + if (size >= 0) size + 1 else -1 + } + override def isEmpty: Boolean = false + } + + /** A view that prepends an element to its elements */ + @SerialVersionUID(3L) + class Prepended[+A](elem: A, underlying: SomeIterableOps[A]) extends AbstractView[A] { + def iterator: Iterator[A] = new Concat(new View.Single(elem), underlying).iterator + override def knownSize: Int = { + val size = underlying.knownSize + if (size >= 0) size + 1 else -1 + } + override def isEmpty: Boolean = false + } + + @SerialVersionUID(3L) + class Updated[A](underlying: SomeIterableOps[A], index: Int, elem: A) extends AbstractView[A] { + def iterator: Iterator[A] = new AbstractIterator[A] { + private[this] val it = underlying.iterator + private[this] var i = 0 + def next(): A = { + val value = if (i == index) { it.next(); elem } else it.next() + i += 1 + value + } + def hasNext: Boolean = + if(it.hasNext) true + else if(index >= i) throw new IndexOutOfBoundsException(index.toString) + else false + } + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = iterator.isEmpty + } + + @SerialVersionUID(3L) + private[collection] class Patched[A](underlying: SomeIterableOps[A], from: Int, other: IterableOnce[A], replaced: Int) extends AbstractView[A] { + // we may be unable to traverse `other` more than once, so we need to cache it if that's the case + private val _other: Iterable[A] = other match { + case other: Iterable[A] => other + case other => LazyList.from(other) + } + + def iterator: Iterator[A] = underlying.iterator.patch(from, _other.iterator, replaced) + override def knownSize: Int = if (underlying.knownSize == 0 && _other.knownSize == 0) 0 else super.knownSize + override def isEmpty: Boolean = if (knownSize == 0) true else iterator.isEmpty + } + + @SerialVersionUID(3L) + class ZipWithIndex[A](underlying: SomeIterableOps[A]) extends AbstractView[(A, Int)] { + def iterator: Iterator[(A, Int)] = underlying.iterator.zipWithIndex + override def knownSize: Int = underlying.knownSize + override def isEmpty: Boolean = underlying.isEmpty + } + + @SerialVersionUID(3L) + class PadTo[A](underlying: SomeIterableOps[A], len: Int, elem: A) extends AbstractView[A] { + def iterator: Iterator[A] = underlying.iterator.padTo(len, elem) + + override def knownSize: Int = { + val size = underlying.knownSize + if (size >= 0) size max len else -1 + } + override def isEmpty: Boolean = underlying.isEmpty && len <= 0 + } + + private[collection] def takeRightIterator[A](it: Iterator[A], n: Int): Iterator[A] = { + val k = it.knownSize + if(k == 0 || n <= 0) Iterator.empty + else if(n == Int.MaxValue) it + else if(k > 0) it.drop((k-n) max 0) + else new TakeRightIterator[A](it, n) + } + + private final class TakeRightIterator[A](private[this] var underlying: Iterator[A], maxlen: Int) extends AbstractIterator[A] { + private[this] var len: Int = -1 + private[this] var pos: Int = 0 + private[this] var buf: ArrayBuffer[AnyRef] = _ + def init(): Unit = if(buf eq null) { + buf = new ArrayBuffer[AnyRef](maxlen min 256) + len = 0 + while(underlying.hasNext) { + val n = underlying.next().asInstanceOf[AnyRef] + if(pos >= buf.length) buf.addOne(n) + else buf(pos) = n + pos += 1 + if(pos == maxlen) pos = 0 + len += 1 + } + underlying = null + if(len > maxlen) len = maxlen + pos = pos - len + if(pos < 0) pos += maxlen + } + override def knownSize = len + def hasNext: Boolean = { + init() + len > 0 + } + def next(): A = { + init() + if(len == 0) Iterator.empty.next() + else { + val x = buf(pos).asInstanceOf[A] + pos += 1 + if(pos == maxlen) pos = 0 + len -= 1 + x + } + } + override def drop(n: Int): Iterator[A] = { + init() + if (n > 0) { + len = (len - n) max 0 + pos = (pos + n) % maxlen + } + this + } + } + + private[collection] def dropRightIterator[A](it: Iterator[A], n: Int): Iterator[A] = { + if(n <= 0) it + else { + val k = it.knownSize + if(k >= 0) it.take(k - n) + else new DropRightIterator[A](it, n) + } + } + + private final class DropRightIterator[A](private[this] var underlying: Iterator[A], maxlen: Int) extends AbstractIterator[A] { + private[this] var len: Int = -1 // known size or -1 if the end of `underlying` has not been seen yet + private[this] var pos: Int = 0 + private[this] var buf: ArrayBuffer[AnyRef] = _ + def init(): Unit = if(buf eq null) { + buf = new ArrayBuffer[AnyRef](maxlen min 256) + while(pos < maxlen && underlying.hasNext) { + buf.addOne(underlying.next().asInstanceOf[AnyRef]) + pos += 1 + } + if(!underlying.hasNext) len = 0 + pos = 0 + } + override def knownSize = len + def hasNext: Boolean = { + init() + len != 0 + } + def next(): A = { + if(!hasNext) Iterator.empty.next() + else { + val x = buf(pos).asInstanceOf[A] + if(len == -1) { + buf(pos) = underlying.next().asInstanceOf[AnyRef] + if(!underlying.hasNext) len = 0 + } else len -= 1 + pos += 1 + if(pos == maxlen) pos = 0 + x + } + } + } +} + +/** Explicit instantiation of the `View` trait to reduce class file size in subclasses. */ +@SerialVersionUID(3L) +abstract class AbstractView[+A] extends scala.collection.AbstractIterable[A] with View[A] diff --git a/library/src/scala/collection/WithFilter.scala b/library/src/scala/collection/WithFilter.scala new file mode 100644 index 000000000000..7a68275336ff --- /dev/null +++ b/library/src/scala/collection/WithFilter.scala @@ -0,0 +1,70 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + +/** A template trait that contains just the `map`, `flatMap`, `foreach` and `withFilter` methods + * of trait `Iterable`. + * + * @tparam A Element type (e.g. `Int`) + * @tparam CC Collection type constructor (e.g. `List`) + * + * @define coll collection + */ +@SerialVersionUID(3L) +abstract class WithFilter[+A, +CC[_]] extends Serializable { + + /** Builds a new collection by applying a function to all elements of the + * `filtered` outer $coll. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying + * the given function `f` to each element of the filtered outer $coll + * and collecting the results. + */ + def map[B](f: A => B): CC[B] + + /** Builds a new collection by applying a function to all elements of the + * `filtered` outer $coll containing this `WithFilter` instance that satisfy + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying + * the given collection-valued function `f` to each element + * of the filtered outer $coll and + * concatenating the results. + */ + def flatMap[B](f: A => IterableOnce[B]): CC[B] + + /** Applies a function `f` to all elements of the `filtered` outer $coll. + * + * @param f the function that is applied for its side-effect to every element. + * The result of function `f` is discarded. + * + * @tparam U the type parameter describing the result of function `f`. + * This result will always be ignored. Typically `U` is `Unit`, + * but this is not necessary. + */ + def foreach[U](f: A => U): Unit + + /** Further refines the filter for this `filtered` $coll. + * + * @param q the predicate used to test elements. + * @return an object of class `WithFilter`, which supports + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * All these operations apply to those elements of this $coll which + * also satisfy both `p` and `q` predicates. + */ + def withFilter(q: A => Boolean): WithFilter[A, CC] + +} diff --git a/library/src/scala/collection/concurrent/BasicNode.java b/library/src/scala/collection/concurrent/BasicNode.java new file mode 100644 index 000000000000..b6a628d1295e --- /dev/null +++ b/library/src/scala/collection/concurrent/BasicNode.java @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.concurrent; + +public abstract class BasicNode { + + public abstract String string(int lev); + +} diff --git a/library/src/scala/collection/concurrent/CNodeBase.java b/library/src/scala/collection/concurrent/CNodeBase.java new file mode 100644 index 000000000000..4033c12af449 --- /dev/null +++ b/library/src/scala/collection/concurrent/CNodeBase.java @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.concurrent; + +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +abstract class CNodeBase extends MainNode { + + @SuppressWarnings("unchecked") + public static final AtomicIntegerFieldUpdater> updater = + AtomicIntegerFieldUpdater.newUpdater((Class>) (Class) CNodeBase.class, "csize"); + + public volatile int csize = -1; + + public boolean CAS_SIZE(int oldval, int nval) { + return updater.compareAndSet(this, oldval, nval); + } + + public void WRITE_SIZE(int nval) { + updater.set(this, nval); + } + + public int READ_SIZE() { + return updater.get(this); + } + +} diff --git a/library/src/scala/collection/concurrent/Gen.java b/library/src/scala/collection/concurrent/Gen.java new file mode 100644 index 000000000000..548c1892321f --- /dev/null +++ b/library/src/scala/collection/concurrent/Gen.java @@ -0,0 +1,15 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.concurrent; + +final class Gen {} diff --git a/library/src/scala/collection/concurrent/INodeBase.java b/library/src/scala/collection/concurrent/INodeBase.java new file mode 100644 index 000000000000..b16265c68ea3 --- /dev/null +++ b/library/src/scala/collection/concurrent/INodeBase.java @@ -0,0 +1,39 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.concurrent; + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +abstract class INodeBase extends BasicNode { + + @SuppressWarnings("unchecked") + public static final AtomicReferenceFieldUpdater, MainNode> updater = + AtomicReferenceFieldUpdater.newUpdater((Class>) (Class) INodeBase.class, (Class>) (Class) MainNode.class, "mainnode"); + + static final Object RESTART = new Object(); + + static final Object NO_SUCH_ELEMENT_SENTINEL = new Object(); + + public volatile MainNode mainnode = null; + + public final Gen gen; + + public INodeBase(Gen generation) { + gen = generation; + } + + public BasicNode prev() { + return null; + } + +} diff --git a/library/src/scala/collection/concurrent/MainNode.java b/library/src/scala/collection/concurrent/MainNode.java new file mode 100644 index 000000000000..1bfc11594ec9 --- /dev/null +++ b/library/src/scala/collection/concurrent/MainNode.java @@ -0,0 +1,46 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.concurrent; + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +abstract class MainNode extends BasicNode { + + @SuppressWarnings("unchecked") + public static final AtomicReferenceFieldUpdater, MainNode> updater = + AtomicReferenceFieldUpdater.newUpdater((Class>) (Class) MainNode.class, (Class>) (Class) MainNode.class, "prev"); + + public volatile MainNode prev = null; + + public abstract int cachedSize(Object ct); + + // standard contract + public abstract int knownSize(); + + public boolean CAS_PREV(MainNode oldval, MainNode nval) { + return updater.compareAndSet(this, oldval, nval); + } + + public void WRITE_PREV(MainNode nval) { + updater.set(this, nval); + } + + // do we need this? unclear in the javadocs... + // apparently not - volatile reads are supposed to be safe + // regardless of whether there are concurrent ARFU updates + @Deprecated @SuppressWarnings("unchecked") + public MainNode READ_PREV() { + return (MainNode) updater.get(this); + } + +} diff --git a/library/src/scala/collection/concurrent/Map.scala b/library/src/scala/collection/concurrent/Map.scala new file mode 100644 index 000000000000..291f85513b58 --- /dev/null +++ b/library/src/scala/collection/concurrent/Map.scala @@ -0,0 +1,190 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.concurrent + +import scala.annotation.tailrec + +/** A template trait for mutable maps that allow concurrent access. + * + * $concurrentmapinfo + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#concurrent_maps "Scala's Collection Library overview"]] + * section on `Concurrent Maps` for more information. + * + * @tparam K the key type of the map + * @tparam V the value type of the map + * + * @define Coll `concurrent.Map` + * @define coll concurrent map + * @define concurrentmapinfo + * This is a base trait for all Scala concurrent map implementations. It + * provides all of the methods a `Map` does, with the difference that all the + * changes are atomic. It also describes methods specific to concurrent maps. + * + * @define atomicop + * This is an atomic operation. + */ +trait Map[K, V] extends scala.collection.mutable.Map[K, V] { + + /** + * Associates the given key with a given value, unless the key was already + * associated with some other value. + * + * $atomicop + * + * @param k key with which the specified value is to be associated with + * @param v value to be associated with the specified key + * @return `Some(oldvalue)` if there was a value `oldvalue` previously + * associated with the specified key, or `None` if there was no + * mapping for the specified key + */ + def putIfAbsent(k: K, v: V): Option[V] + + /** + * Removes the entry for the specified key if it's currently mapped to the + * specified value. + * + * $atomicop + * + * @param k key for which the entry should be removed + * @param v value expected to be associated with the specified key if + * the removal is to take place + * @return `true` if the removal took place, `false` otherwise + */ + def remove(k: K, v: V): Boolean + + /** + * Replaces the entry for the given key only if it was previously mapped to + * a given value. + * + * $atomicop + * + * @param k key for which the entry should be replaced + * @param oldvalue value expected to be associated with the specified key + * if replacing is to happen + * @param newvalue value to be associated with the specified key + * @return `true` if the entry was replaced, `false` otherwise + */ + def replace(k: K, oldvalue: V, newvalue: V): Boolean + + /** + * Replaces the entry for the given key only if it was previously mapped + * to some value. + * + * $atomicop + * + * @param k key for which the entry should be replaced + * @param v value to be associated with the specified key + * @return `Some(v)` if the given key was previously mapped to some value `v`, or `None` otherwise + */ + def replace(k: K, v: V): Option[V] + + override def getOrElseUpdate(key: K, @deprecatedName("op", since="2.13.13") defaultValue: => V): V = get(key) match { + case Some(v) => v + case None => + val v = defaultValue + putIfAbsent(key, v) match { + case Some(ov) => ov + case None => v + } + } + + /** + * Removes the entry for the specified key if it's currently mapped to the + * specified value. Comparison to the specified value is done using reference + * equality. + * + * Not all map implementations can support removal based on reference + * equality, and for those implementations, object equality is used instead. + * + * $atomicop + * + * @param k key for which the entry should be removed + * @param v value expected to be associated with the specified key if + * the removal is to take place + * @return `true` if the removal took place, `false` otherwise + */ + // TODO: make part of the API in a future version + private[collection] def removeRefEq(k: K, v: V): Boolean = remove(k, v) + + /** + * Replaces the entry for the given key only if it was previously mapped to + * a given value. Comparison to the specified value is done using reference + * equality. + * + * Not all map implementations can support replacement based on reference + * equality, and for those implementations, object equality is used instead. + * + * $atomicop + * + * @param k key for which the entry should be replaced + * @param oldValue value expected to be associated with the specified key + * if replacing is to happen + * @param newValue value to be associated with the specified key + * @return `true` if the entry was replaced, `false` otherwise + */ + // TODO: make part of the API in a future version + private[collection] def replaceRefEq(k: K, oldValue: V, newValue: V): Boolean = replace(k, oldValue, newValue) + + /** + * Update a mapping for the specified key and its current optionally mapped value + * (`Some` if there is current mapping, `None` if not). + * + * If the remapping function returns `Some(v)`, the mapping is updated with the new value `v`. + * If the remapping function returns `None`, the mapping is removed (or remains absent if initially absent). + * If the function itself throws an exception, the exception is rethrown, and the current mapping is left unchanged. + * + * If the map is updated by another concurrent access, the remapping function will be retried until successfully updated. + * + * @param key the key value + * @param remappingFunction a function that receives current optionally mapped value and return a new mapping + * @return the new value associated with the specified key + */ + override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = updateWithAux(key)(remappingFunction) + + @tailrec + private def updateWithAux(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { + val previousValue = get(key) + val nextValue = remappingFunction(previousValue) + previousValue match { + case Some(prev) => nextValue match { + case Some(next) => if (replaceRefEq(key, prev, next)) return nextValue + case _ => if (removeRefEq(key, prev)) return None + } + case _ => nextValue match { + case Some(next) => if (putIfAbsent(key, next).isEmpty) return nextValue + case _ => return None + } + } + updateWithAux(key)(remappingFunction) + } + + private[collection] def filterInPlaceImpl(p: (K, V) => Boolean): this.type = { + val it = iterator + while (it.hasNext) { + val (k, v) = it.next() + if (!p(k, v)) removeRefEq(k, v) + } + this + } + + private[collection] def mapValuesInPlaceImpl(f: (K, V) => V): this.type = { + val it = iterator + while (it.hasNext) { + val (k, v) = it.next() + replaceRefEq(k, v, f(k, v)) + } + this + } +} diff --git a/library/src/scala/collection/concurrent/TrieMap.scala b/library/src/scala/collection/concurrent/TrieMap.scala new file mode 100644 index 000000000000..ddc5379f1f25 --- /dev/null +++ b/library/src/scala/collection/concurrent/TrieMap.scala @@ -0,0 +1,1200 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package concurrent + +import java.util.concurrent.atomic._ +import scala.{unchecked => uc} +import scala.annotation.tailrec +import scala.collection.concurrent.TrieMap.RemovalPolicy +import scala.collection.generic.DefaultSerializable +import scala.collection.immutable.{List, Nil} +import scala.collection.mutable.GrowableBuilder +import scala.util.Try +import scala.util.hashing.Hashing + +private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: Equiv[K]) extends INodeBase[K, V](g) { + import INodeBase._ + + WRITE(bn) + + def this(g: Gen, equiv: Equiv[K]) = this(null, g, equiv) + + def WRITE(nval: MainNode[K, V]) = INodeBase.updater.set(this, nval) + + def CAS(old: MainNode[K, V], n: MainNode[K, V]) = INodeBase.updater.compareAndSet(this, old, n) + + def gcasRead(ct: TrieMap[K, V]): MainNode[K, V] = GCAS_READ(ct) + + def GCAS_READ(ct: TrieMap[K, V]): MainNode[K, V] = { + val m = /*READ*/mainnode + val prevval = /*READ*/m.prev + if (prevval eq null) m + else GCAS_Complete(m, ct) + } + + @tailrec private def GCAS_Complete(m: MainNode[K, V], ct: TrieMap[K, V]): MainNode[K, V] = if (m eq null) null else { + // complete the GCAS + val prev = /*READ*/m.prev + val ctr = ct.readRoot(abort = true) + + prev match { + case null => + m + case fn: FailedNode[_, _] => // try to commit to previous value + if (CAS(m, fn.prev)) fn.prev + else GCAS_Complete(/*READ*/mainnode, ct) + case vn: MainNode[_, _] => + // Assume that you've read the root from the generation G. + // Assume that the snapshot algorithm is correct. + // ==> you can only reach nodes in generations <= G. + // ==> `gen` is <= G. + // We know that `ctr.gen` is >= G. + // ==> if `ctr.gen` = `gen` then they are both equal to G. + // ==> otherwise, we know that either `ctr.gen` > G, `gen` < G, + // or both + if ((ctr.gen eq gen) && ct.nonReadOnly) { + // try to commit + if (m.CAS_PREV(prev, null)) m + else GCAS_Complete(m, ct) + } else { + // try to abort + m.CAS_PREV(prev, new FailedNode(prev)) + GCAS_Complete(/*READ*/mainnode, ct) + } + } + } + + def GCAS(old: MainNode[K, V], n: MainNode[K, V], ct: TrieMap[K, V]): Boolean = { + n.WRITE_PREV(old) + if (CAS(old, n)) { + GCAS_Complete(n, ct) + /*READ*/n.prev eq null + } else false + } + + private def equal(k1: K, k2: K, ct: TrieMap[K, V]) = ct.equality.equiv(k1, k2) + + private def inode(cn: MainNode[K, V]) = { + val nin = new INode[K, V](gen, equiv) + nin.WRITE(cn) + nin + } + + def copyToGen(ngen: Gen, ct: TrieMap[K, V]) = { + val nin = new INode[K, V](ngen, equiv) + val main = GCAS_READ(ct) + nin.WRITE(main) + nin + } + + /** Inserts a key value pair, overwriting the old pair if the keys match. + * + * @return true if successful, false otherwise + */ + @tailrec def rec_insert(k: K, v: V, hc: Int, lev: Int, parent: INode[K, V], startgen: Gen, ct: TrieMap[K, V]): Boolean = { + val m = GCAS_READ(ct) // use -Yinline! + + m match { + case cn: CNode[K, V] => // 1) a multiway node + val idx = (hc >>> lev) & 0x1f + val flag = 1 << idx + val bmp = cn.bitmap + val mask = flag - 1 + val pos = Integer.bitCount(bmp & mask) + if ((bmp & flag) != 0) { + // 1a) insert below + cn.array(pos) match { + case in: INode[K, V] @uc => + if (startgen eq in.gen) in.rec_insert(k, v, hc, lev + 5, this, startgen, ct) + else { + if (GCAS(cn, cn.renewed(startgen, ct), ct)) rec_insert(k, v, hc, lev, parent, startgen, ct) + else false + } + case sn: SNode[K, V] @uc => + if (sn.hc == hc && equal(sn.k, k, ct)) GCAS(cn, cn.updatedAt(pos, new SNode(sn.k, v, hc), gen), ct) + else { + val rn = if (cn.gen eq gen) cn else cn.renewed(gen, ct) + val nn = rn.updatedAt(pos, inode(CNode.dual(sn, sn.hc, new SNode(k, v, hc), hc, lev + 5, gen, equiv)), gen) + GCAS(cn, nn, ct) + } + case basicNode => throw new MatchError(basicNode) + } + } else { + val rn = if (cn.gen eq gen) cn else cn.renewed(gen, ct) + val ncnode = rn.insertedAt(pos, flag, k, v, hc, gen) + GCAS(cn, ncnode, ct) + } + case tn: TNode[K, V] => + clean(parent, ct, lev - 5) + false + case ln: LNode[K, V] => // 3) an l-node + val nn = ln.inserted(k, v) + GCAS(ln, nn, ct) + case mainNode => throw new MatchError(mainNode) + } + } + + + + /** Inserts a new key value pair, given that a specific condition is met. + * + * @param cond KEY_PRESENT_OR_ABSENT - don't care if the key was there, insert or overwrite + * KEY_ABSENT - key wasn't there, insert only, do not overwrite + * KEY_PRESENT - key was there, overwrite only, do not insert + * other value `v` - only overwrite if the current value is this + * @param fullEquals whether to use reference or full equals when comparing `v` to the current value + * @param hc the hashcode of `k` + * + * @return null if unsuccessful, Option[V] otherwise (indicating previous value bound to the key) + */ + @tailrec def rec_insertif(k: K, v: V, hc: Int, cond: AnyRef, fullEquals: Boolean, lev: Int, parent: INode[K, V], startgen: Gen, ct: TrieMap[K, V]): Option[V] = { + val m = GCAS_READ(ct) // use -Yinline! + + m match { + case cn: CNode[K, V] => // 1) a multiway node + val idx = (hc >>> lev) & 0x1f + val flag = 1 << idx + val bmp = cn.bitmap + val mask = flag - 1 + val pos = Integer.bitCount(bmp & mask) + if ((bmp & flag) != 0) { + // 1a) insert below + cn.array(pos) match { + case in: INode[K, V] @uc => + if (startgen eq in.gen) in.rec_insertif(k, v, hc, cond, fullEquals, lev + 5, this, startgen, ct) + else { + if (GCAS(cn, cn.renewed(startgen, ct), ct)) rec_insertif(k, v, hc, cond, fullEquals, lev, parent, startgen, ct) + else null + } + case sn: SNode[K, V] @uc => cond match { + case INode.KEY_PRESENT_OR_ABSENT => + if (sn.hc == hc && equal(sn.k, k, ct)) { + if (GCAS(cn, cn.updatedAt(pos, new SNode(sn.k, v, hc), gen), ct)) Some(sn.v) else null + } else { + val rn = if (cn.gen eq gen) cn else cn.renewed(gen, ct) + val nn = rn.updatedAt(pos, inode(CNode.dual(sn, sn.hc, new SNode(k, v, hc), hc, lev + 5, gen, equiv)), gen) + if (GCAS(cn, nn, ct)) None + else null + } + case INode.KEY_ABSENT => + if (sn.hc == hc && equal(sn.k, k, ct)) Some(sn.v) + else { + val rn = if (cn.gen eq gen) cn else cn.renewed(gen, ct) + val nn = rn.updatedAt(pos, inode(CNode.dual(sn, sn.hc, new SNode(k, v, hc), hc, lev + 5, gen, equiv)), gen) + if (GCAS(cn, nn, ct)) None + else null + } + case INode.KEY_PRESENT => + if (sn.hc == hc && equal(sn.k, k, ct)) { + if (GCAS(cn, cn.updatedAt(pos, new SNode(k, v, hc), gen), ct)) Some(sn.v) else null + } else None + case otherv => + if (sn.hc == hc && equal(sn.k, k, ct) && (if (fullEquals) sn.v == otherv else sn.v.asInstanceOf[AnyRef] eq otherv)) { + if (GCAS(cn, cn.updatedAt(pos, new SNode(k, v, hc), gen), ct)) Some(sn.v) else null + } else None + } + case basicNode => throw new MatchError(basicNode) + } + } else cond match { + case INode.KEY_PRESENT_OR_ABSENT | INode.KEY_ABSENT => + val rn = if (cn.gen eq gen) cn else cn.renewed(gen, ct) + val ncnode = rn.insertedAt(pos, flag, k, v, hc, gen) + if (GCAS(cn, ncnode, ct)) None else null + case INode.KEY_PRESENT => None + case otherv => None + } + case sn: TNode[K, V] => + clean(parent, ct, lev - 5) + null + case ln: LNode[K, V] => // 3) an l-node + def insertln() = { + val nn = ln.inserted(k, v) + GCAS(ln, nn, ct) + } + cond match { + case INode.KEY_PRESENT_OR_ABSENT => + val optv = ln.get(k) + if (insertln()) optv else null + case INode.KEY_ABSENT => + ln.get(k) match { + case None => if (insertln()) None else null + case optv => optv + } + case INode.KEY_PRESENT => + ln.get(k) match { + case Some(v0) => if (insertln()) Some(v0) else null + case None => None + } + case otherv => + ln.get(k) match { + case Some(v0) if (if (fullEquals) v0 == otherv else v0.asInstanceOf[AnyRef] eq otherv) => + if (insertln()) Some(otherv.asInstanceOf[V]) else null + case _ => None + } + } + case mainNode => throw new MatchError(mainNode) + } + } + + /** Looks up the value associated with the key. + * + * @param hc the hashcode of `k` + * + * @return NO_SUCH_ELEMENT_SENTINEL if no value has been found, RESTART if the operation wasn't successful, + * or any other value otherwise + */ + @tailrec def rec_lookup(k: K, hc: Int, lev: Int, parent: INode[K, V], startgen: Gen, ct: TrieMap[K, V]): AnyRef = { + val m = GCAS_READ(ct) // use -Yinline! + + m match { + case cn: CNode[K, V] => // 1) a multinode + val idx = (hc >>> lev) & 0x1f + val flag = 1 << idx + val bmp = cn.bitmap + if ((bmp & flag) == 0) NO_SUCH_ELEMENT_SENTINEL // 1a) bitmap shows no binding + else { // 1b) bitmap contains a value - descend + val pos = if (bmp == 0xffffffff) idx else Integer.bitCount(bmp & (flag - 1)) + val sub = cn.array(pos) + sub match { + case in: INode[K, V] @uc => + if (ct.isReadOnly || (startgen eq in.gen)) in.rec_lookup(k, hc, lev + 5, this, startgen, ct) + else { + if (GCAS(cn, cn.renewed(startgen, ct), ct)) rec_lookup(k, hc, lev, parent, startgen, ct) + else RESTART + } + case sn: SNode[K, V] @uc => // 2) singleton node + if (sn.hc == hc && equal(sn.k, k, ct)) sn.v.asInstanceOf[AnyRef] + else NO_SUCH_ELEMENT_SENTINEL + case basicNode => throw new MatchError(basicNode) + } + } + case tn: TNode[_, _] => // 3) non-live node + def cleanReadOnly(tn: TNode[K, V]) = if (ct.nonReadOnly) { + clean(parent, ct, lev - 5) + RESTART + } else { + if (tn.hc == hc && tn.k == k) tn.v.asInstanceOf[AnyRef] + else NO_SUCH_ELEMENT_SENTINEL + } + cleanReadOnly(tn) + case ln: LNode[K, V] => // 5) an l-node + ln.get(k).asInstanceOf[Option[AnyRef]].getOrElse(NO_SUCH_ELEMENT_SENTINEL) + case mainNode => throw new MatchError(mainNode) + } + } + + /** Removes the key associated with the given value. + * + * @param hc the hashcode of `k` + * + * @param removalPolicy policy deciding whether to remove `k` based on `v` and the + * current value associated with `k` (Always, FullEquals, or ReferenceEq) + * + * @return null if not successful, an Option[V] indicating the previous value otherwise + */ + def rec_remove( + k: K, + v: V, + removalPolicy: Int, + hc: Int, + lev: Int, + parent: INode[K, V], + startgen: Gen, + ct: TrieMap[K, V]): Option[V] = { + + GCAS_READ(ct) match { + case cn: CNode[K, V] => + val idx = (hc >>> lev) & 0x1f + val bmp = cn.bitmap + val flag = 1 << idx + if ((bmp & flag) == 0) None + else { + val pos = Integer.bitCount(bmp & (flag - 1)) + val sub = cn.array(pos) + val res = sub match { + case in: INode[K, V] @uc => + if (startgen eq in.gen) in.rec_remove(k, v, removalPolicy, hc, lev + 5, this, startgen, ct) + else { + if (GCAS(cn, cn.renewed(startgen, ct), ct)) rec_remove(k, v, removalPolicy, hc, lev, parent, startgen, ct) + else null + } + case sn: SNode[K, V] @uc => + if (sn.hc == hc && equal(sn.k, k, ct) && RemovalPolicy.shouldRemove(removalPolicy)(sn.v, v)) { + val ncn = cn.removedAt(pos, flag, gen).toContracted(lev) + if (GCAS(cn, ncn, ct)) Some(sn.v) else null + } else None + case basicNode => throw new MatchError(basicNode) + } + + if (res == None || (res eq null)) res + else { + @tailrec def cleanParent(nonlive: AnyRef): Unit = { + val cn = parent.GCAS_READ(ct) + cn match { + case cn: CNode[K, V] => + val idx = (hc >>> (lev - 5)) & 0x1f + val bmp = cn.bitmap + val flag = 1 << idx + if ((bmp & flag) == 0) {} // somebody already removed this i-node, we're done + else { + val pos = Integer.bitCount(bmp & (flag - 1)) + val sub = cn.array(pos) + if (sub eq this) (nonlive: @uc) match { + case tn: TNode[K, V] @uc => + val ncn = cn.updatedAt(pos, tn.copyUntombed, gen).toContracted(lev - 5) + if (!parent.GCAS(cn, ncn, ct)) + if (ct.readRoot().gen == startgen) cleanParent(nonlive) + } + } + case _ => // parent is no longer a cnode, we're done + } + } + + if (parent ne null) { // never tomb at root + val n = GCAS_READ(ct) + if (n.isInstanceOf[TNode[_, _]]) + cleanParent(n) + } + + res + } + } + case tn: TNode[K, V] => + clean(parent, ct, lev - 5) + null + case ln: LNode[K, V] => + if (removalPolicy == RemovalPolicy.Always) { + val optv = ln.get(k) + val nn = ln.removed(k, ct) + if (GCAS(ln, nn, ct)) optv else null + } else ln.get(k) match { + case optv @ Some(v0) if RemovalPolicy.shouldRemove(removalPolicy)(v, v0) => + val nn = ln.removed(k, ct) + if (GCAS(ln, nn, ct)) optv else null + case _ => None + } + case mainNode => throw new MatchError(mainNode) + } + } + + private def clean(nd: INode[K, V], ct: TrieMap[K, V], lev: Int): Unit = { + val m = nd.GCAS_READ(ct) + m match { + case cn: CNode[K, V] => nd.GCAS(cn, cn.toCompressed(ct, lev, gen), ct) + case _ => + } + } + + def isNullInode(ct: TrieMap[K, V]) = GCAS_READ(ct) eq null + + def cachedSize(ct: TrieMap[K, V]): Int = + GCAS_READ(ct).cachedSize(ct) + + def knownSize(ct: TrieMap[K, V]): Int = + GCAS_READ(ct).knownSize() + + /* this is a quiescent method! */ + def string(lev: Int) = "%sINode -> %s".format(" " * lev, mainnode match { + case null => "" + case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v, tn.hc) + case cn: CNode[_, _] => cn.string(lev) + case ln: LNode[_, _] => ln.string(lev) + case x => "".format(x) + }) + +} + + +private[concurrent] object INode { + //////////////////////////////////////////////////////////////////////////////////////////////////// + // Arguments for `cond` argument in TrieMap#rec_insertif + //////////////////////////////////////////////////////////////////////////////////////////////////// + final val KEY_PRESENT = new AnyRef + final val KEY_ABSENT = new AnyRef + final val KEY_PRESENT_OR_ABSENT = new AnyRef + + def newRootNode[K, V](equiv: Equiv[K]) = { + val gen = new Gen + val cn = new CNode[K, V](0, new Array(0), gen) + new INode[K, V](cn, gen, equiv) + } +} + + +private[concurrent] final class FailedNode[K, V](p: MainNode[K, V]) extends MainNode[K, V] { + WRITE_PREV(p) + + def string(lev: Int): Nothing = throw new UnsupportedOperationException + + def cachedSize(ct: AnyRef): Int = throw new UnsupportedOperationException + + def knownSize: Int = throw new UnsupportedOperationException + + override def toString = "FailedNode(%s)".format(p) +} + + +private[concurrent] trait KVNode[K, V] { + def kvPair: (K, V) +} + + +private[collection] final class SNode[K, V](final val k: K, final val v: V, final val hc: Int) + extends BasicNode with KVNode[K, V] { + def copy = new SNode(k, v, hc) + def copyTombed = new TNode(k, v, hc) + def copyUntombed = new SNode(k, v, hc) + def kvPair = (k, v) + def string(lev: Int) = (" " * lev) + "SNode(%s, %s, %x)".format(k, v, hc) +} + +// Tomb Node, used to ensure proper ordering during removals +private[collection] final class TNode[K, V](final val k: K, final val v: V, final val hc: Int) + extends MainNode[K, V] with KVNode[K, V] { + def copy = new TNode(k, v, hc) + def copyTombed = new TNode(k, v, hc) + def copyUntombed = new SNode(k, v, hc) + def kvPair = (k, v) + def cachedSize(ct: AnyRef): Int = 1 + def knownSize: Int = 1 + def string(lev: Int) = (" " * lev) + "TNode(%s, %s, %x, !)".format(k, v, hc) +} + +// List Node, leaf node that handles hash collisions +private[collection] final class LNode[K, V](val entries: List[(K, V)], equiv: Equiv[K]) + extends MainNode[K, V] { + + def this(k: K, v: V, equiv: Equiv[K]) = this((k -> v) :: Nil, equiv) + + def this(k1: K, v1: V, k2: K, v2: V, equiv: Equiv[K]) = + this(if (equiv.equiv(k1, k2)) (k2 -> v2) :: Nil else (k1 -> v1) :: (k2 -> v2) :: Nil, equiv) + + def inserted(k: K, v: V) = { + var k0: K = k + @tailrec + def remove(elems: List[(K, V)], acc: List[(K, V)]): List[(K, V)] = { + if (elems.isEmpty) acc + else if (equiv.equiv(elems.head._1, k)) { + k0 = elems.head._1 + acc ::: elems.tail + } else remove(elems.tail, elems.head :: acc) + } + val e = remove(entries, Nil) + new LNode((k0 -> v) :: e, equiv) + } + + def removed(k: K, ct: TrieMap[K, V]): MainNode[K, V] = { + val updmap = entries.filterNot(entry => equiv.equiv(entry._1, k)) + if (updmap.sizeIs > 1) new LNode(updmap, equiv) + else { + val (k, v) = updmap.iterator.next() + new TNode(k, v, ct.computeHash(k)) // create it tombed so that it gets compressed on subsequent accesses + } + } + + def get(k: K): Option[V] = entries.find(entry => equiv.equiv(entry._1, k)).map(_._2) + + def cachedSize(ct: AnyRef): Int = entries.size + + def knownSize: Int = -1 // shouldn't ever be empty, and the size of a list is not known + + def string(lev: Int) = (" " * lev) + "LNode(%s)".format(entries.mkString(", ")) + +} + +// Ctrie Node, contains bitmap and array of references to branch nodes +private[collection] final class CNode[K, V](val bitmap: Int, val array: Array[BasicNode], val gen: Gen) extends CNodeBase[K, V] { + // this should only be called from within read-only snapshots + def cachedSize(ct: AnyRef): Int = { + val currsz = READ_SIZE() + if (currsz != -1) currsz + else { + val sz = computeSize(ct.asInstanceOf[TrieMap[K, V]]) + while (READ_SIZE() == -1) CAS_SIZE(-1, sz) + READ_SIZE() + } + } + + def knownSize: Int = READ_SIZE() // this should only ever return -1 if unknown + + // lends itself towards being parallelizable by choosing + // a random starting offset in the array + // => if there are concurrent size computations, they start + // at different positions, so they are more likely to + // to be independent + private def computeSize(ct: TrieMap[K, V]): Int = { + var i = 0 + var sz = 0 + val offset = + if (array.length > 0) + //util.Random.nextInt(array.length) /* <-- benchmarks show that this causes observable contention */ + java.util.concurrent.ThreadLocalRandom.current.nextInt(0, array.length) + else 0 + while (i < array.length) { + val pos = (i + offset) % array.length + array(pos) match { + case sn: SNode[_, _] => sz += 1 + case in: INode[K, V] @uc => sz += in.cachedSize(ct) + case basicNode => throw new MatchError(basicNode) + } + i += 1 + } + sz + } + + def updatedAt(pos: Int, nn: BasicNode, gen: Gen) = { + val len = array.length + val narr = new Array[BasicNode](len) + Array.copy(array, 0, narr, 0, len) + narr(pos) = nn + new CNode[K, V](bitmap, narr, gen) + } + + def removedAt(pos: Int, flag: Int, gen: Gen) = { + val arr = array + val len = arr.length + val narr = new Array[BasicNode](len - 1) + Array.copy(arr, 0, narr, 0, pos) + Array.copy(arr, pos + 1, narr, pos, len - pos - 1) + new CNode[K, V](bitmap ^ flag, narr, gen) + } + + def insertedAt(pos: Int, flag: Int, k: K, v: V, hc: Int, gen: Gen) = { + val len = array.length + val bmp = bitmap + val narr = new Array[BasicNode](len + 1) + Array.copy(array, 0, narr, 0, pos) + narr(pos) = new SNode(k, v, hc) + Array.copy(array, pos, narr, pos + 1, len - pos) + new CNode[K, V](bmp | flag, narr, gen) + } + + /** Returns a copy of this cnode such that all the i-nodes below it are copied + * to the specified generation `ngen`. + */ + def renewed(ngen: Gen, ct: TrieMap[K, V]) = { + var i = 0 + val arr = array + val len = arr.length + val narr = new Array[BasicNode](len) + while (i < len) { + arr(i) match { + case in: INode[K, V] @uc => narr(i) = in.copyToGen(ngen, ct) + case bn: BasicNode => narr(i) = bn + } + i += 1 + } + new CNode[K, V](bitmap, narr, ngen) + } + + private def resurrect(inode: INode[K, V], inodemain: AnyRef): BasicNode = inodemain match { + case tn: TNode[_, _] => tn.copyUntombed + case _ => inode + } + + def toContracted(lev: Int): MainNode[K, V] = if (array.length == 1 && lev > 0) array(0) match { + case sn: SNode[K, V] @uc => sn.copyTombed + case _ => this + } else this + + // - if the branching factor is 1 for this CNode, and the child + // is a tombed SNode, returns its tombed version + // - otherwise, if there is at least one non-null node below, + // returns the version of this node with at least some null-inodes + // removed (those existing when the op began) + // - if there are only null-i-nodes below, returns null + def toCompressed(ct: TrieMap[K, V], lev: Int, gen: Gen) = { + val bmp = bitmap + var i = 0 + val arr = array + val tmparray = new Array[BasicNode](arr.length) + while (i < arr.length) { // construct new bitmap + val sub = arr(i) + sub match { + case in: INode[K, V] @uc => + val inodemain = in.gcasRead(ct) + assert(inodemain ne null) + tmparray(i) = resurrect(in, inodemain) + case sn: SNode[K, V] @uc => + tmparray(i) = sn + case basicNode => throw new MatchError(basicNode) + } + i += 1 + } + + new CNode[K, V](bmp, tmparray, gen).toContracted(lev) + } + + def string(lev: Int): String = "CNode %x\n%s".format(bitmap, array.map(_.string(lev + 1)).mkString("\n")) + + override def toString = { + def elems: Seq[String] = array.flatMap { + case sn: SNode[K, V] @uc => Iterable.single(sn.kvPair._2.toString) + case in: INode[K, V] @uc => Iterable.single(augmentString(in.toString).drop(14) + "(" + in.gen + ")") + case basicNode => throw new MatchError(basicNode) + } + f"CNode(sz: ${elems.size}%d; ${elems.sorted.mkString(", ")})" + } +} + +private[concurrent] object CNode { + + def dual[K, V](x: SNode[K, V], xhc: Int, y: SNode[K, V], yhc: Int, lev: Int, gen: Gen, equiv: Equiv[K]): MainNode[K, V] = if (lev < 35) { + val xidx = (xhc >>> lev) & 0x1f + val yidx = (yhc >>> lev) & 0x1f + val bmp = (1 << xidx) | (1 << yidx) + if (xidx == yidx) { + val subinode = new INode[K, V](gen, equiv)//(TrieMap.inodeupdater) + subinode.mainnode = dual(x, xhc, y, yhc, lev + 5, gen, equiv) + new CNode(bmp, Array(subinode), gen) + } else { + if (xidx < yidx) new CNode(bmp, Array(x, y), gen) + else new CNode(bmp, Array(y, x), gen) + } + } else { + new LNode(x.k, x.v, y.k, y.v, equiv) + } + +} + + +private[concurrent] case class RDCSS_Descriptor[K, V](old: INode[K, V], expectedmain: MainNode[K, V], nv: INode[K, V]) { + @volatile var committed = false +} + + +/** A concurrent hash-trie or TrieMap is a concurrent thread-safe lock-free + * implementation of a hash array mapped trie. It is used to implement the + * concurrent map abstraction. It has particularly scalable concurrent insert + * and remove operations and is memory-efficient. It supports O(1), atomic, + * lock-free snapshots which are used to implement linearizable lock-free size, + * iterator and clear operations. The cost of evaluating the (lazy) snapshot is + * distributed across subsequent updates, thus making snapshot evaluation horizontally scalable. + * + * For details, see: [[http://lampwww.epfl.ch/~prokopec/ctries-snapshot.pdf]] + */ +@SerialVersionUID(-5212455458703321708L) +final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater[TrieMap[K, V], AnyRef], hashf: Hashing[K], ef: Equiv[K]) + extends scala.collection.mutable.AbstractMap[K, V] + with scala.collection.concurrent.Map[K, V] + with scala.collection.mutable.MapOps[K, V, TrieMap, TrieMap[K, V]] + with scala.collection.MapFactoryDefaults[K, V, TrieMap, mutable.Iterable] + with DefaultSerializable { + + private[this] var hashingobj = if (hashf.isInstanceOf[Hashing.Default[_]]) new TrieMap.MangledHashing[K] else hashf + private[this] var equalityobj = ef + @transient + private[this] var rootupdater = rtupd + def hashing = hashingobj + def equality = equalityobj + @volatile private var root = r + + def this(hashf: Hashing[K], ef: Equiv[K]) = this( + INode.newRootNode(ef), + AtomicReferenceFieldUpdater.newUpdater(classOf[TrieMap[K, V]], classOf[AnyRef], "root"), + hashf, + ef + ) + + def this() = this(Hashing.default, Equiv.universal) + + override def mapFactory: MapFactory[TrieMap] = TrieMap + + /* internal methods */ + + private def writeObject(out: java.io.ObjectOutputStream): Unit = { + out.writeObject(hashingobj) + out.writeObject(equalityobj) + + val it = iterator + while (it.hasNext) { + val (k, v) = it.next() + out.writeObject(k) + out.writeObject(v) + } + out.writeObject(TrieMapSerializationEnd) + } + + private def readObject(in: java.io.ObjectInputStream): Unit = { + root = INode.newRootNode(equality) + rootupdater = AtomicReferenceFieldUpdater.newUpdater(classOf[TrieMap[K, V]], classOf[AnyRef], "root") + + hashingobj = in.readObject().asInstanceOf[Hashing[K]] + equalityobj = in.readObject().asInstanceOf[Equiv[K]] + + var obj: AnyRef = in.readObject() + + while (obj != TrieMapSerializationEnd) { + obj = in.readObject() + if (obj != TrieMapSerializationEnd) { + val k = obj.asInstanceOf[K] + val v = in.readObject().asInstanceOf[V] + update(k, v) + } + } + } + + private def CAS_ROOT(ov: AnyRef, nv: AnyRef) = rootupdater.compareAndSet(this, ov, nv) + + private[collection] def readRoot(abort: Boolean = false): INode[K, V] = RDCSS_READ_ROOT(abort) + + private[concurrent] def RDCSS_READ_ROOT(abort: Boolean = false): INode[K, V] = { + val r = /*READ*/root + r match { + case in: INode[K, V] @uc => in + case desc: RDCSS_Descriptor[K, V] @uc => RDCSS_Complete(abort) + case x => throw new MatchError(x) + } + } + + @tailrec private def RDCSS_Complete(abort: Boolean): INode[K, V] = { + val v = /*READ*/root + v match { + case in: INode[K, V] @uc => in + case desc: RDCSS_Descriptor[K, V] @uc => + val RDCSS_Descriptor(ov, exp, nv) = desc + if (abort) { + if (CAS_ROOT(desc, ov)) ov + else RDCSS_Complete(abort) + } else { + val oldmain = ov.gcasRead(this) + if (oldmain eq exp) { + if (CAS_ROOT(desc, nv)) { + desc.committed = true + nv + } else RDCSS_Complete(abort) + } else { + if (CAS_ROOT(desc, ov)) ov + else RDCSS_Complete(abort) + } + } + case x => throw new MatchError(x) + } + } + + private def RDCSS_ROOT(ov: INode[K, V], expectedmain: MainNode[K, V], nv: INode[K, V]): Boolean = { + val desc = RDCSS_Descriptor(ov, expectedmain, nv) + if (CAS_ROOT(ov, desc)) { + RDCSS_Complete(abort = false) + /*READ*/desc.committed + } else false + } + + @tailrec private def inserthc(k: K, hc: Int, v: V): Unit = { + val r = RDCSS_READ_ROOT() + if (!r.rec_insert(k, v, hc, 0, null, r.gen, this)) inserthc(k, hc, v) + } + + @tailrec private def insertifhc(k: K, hc: Int, v: V, cond: AnyRef, fullEquals: Boolean): Option[V] = { + val r = RDCSS_READ_ROOT() + + val ret = r.rec_insertif(k, v, hc, cond, fullEquals, 0, null, r.gen, this) + if (ret eq null) insertifhc(k, hc, v, cond, fullEquals) + else ret + } + + /** Finds the value associated with this key + * + * @param k the key to look up + * @param hc the hashcode of `k` + * + * @return the value: V associated with `k`, if it exists. Otherwise, INodeBase.NO_SUCH_ELEMENT_SENTINEL + */ + @tailrec private def lookuphc(k: K, hc: Int): AnyRef = { + val r = RDCSS_READ_ROOT() + val res = r.rec_lookup(k, hc, 0, null, r.gen, this) + if (res eq INodeBase.RESTART) lookuphc(k, hc) + else res + } + + /** Removes a key-value pair from the map + * + * @param k the key to remove + * @param v the value compare with the value found associated with the key + * @param removalPolicy policy deciding whether to remove `k` based on `v` and the + * current value associated with `k` (Always, FullEquals, or ReferenceEq) + * @return an Option[V] indicating the previous value + */ + @tailrec private def removehc(k: K, v: V, removalPolicy: Int, hc: Int): Option[V] = { + val r = RDCSS_READ_ROOT() + val res = r.rec_remove(k, v, removalPolicy, hc, 0, null, r.gen, this) + if (res ne null) res + else removehc(k, v, removalPolicy, hc) + } + + + def string = RDCSS_READ_ROOT().string(0) + + /* public methods */ + + def isReadOnly = rootupdater eq null + + def nonReadOnly = rootupdater ne null + + /** Returns a snapshot of this TrieMap. + * This operation is lock-free and linearizable. + * + * The snapshot is lazily updated - the first time some branch + * in the snapshot or this TrieMap are accessed, they are rewritten. + * This means that the work of rebuilding both the snapshot and this + * TrieMap is distributed across all the threads doing updates or accesses + * subsequent to the snapshot creation. + */ + @tailrec def snapshot(): TrieMap[K, V] = { + val r = RDCSS_READ_ROOT() + val expmain = r.gcasRead(this) + if (RDCSS_ROOT(r, expmain, r.copyToGen(new Gen, this))) new TrieMap(r.copyToGen(new Gen, this), rootupdater, hashing, equality) + else snapshot() + } + + /** Returns a read-only snapshot of this TrieMap. + * This operation is lock-free and linearizable. + * + * The snapshot is lazily updated - the first time some branch + * of this TrieMap are accessed, it is rewritten. The work of creating + * the snapshot is thus distributed across subsequent updates + * and accesses on this TrieMap by all threads. + * Note that the snapshot itself is never rewritten unlike when calling + * the `snapshot` method, but the obtained snapshot cannot be modified. + * + * This method is used by other methods such as `size` and `iterator`. + */ + @tailrec def readOnlySnapshot(): scala.collection.Map[K, V] = { + val r = RDCSS_READ_ROOT() + val expmain = r.gcasRead(this) + if (RDCSS_ROOT(r, expmain, r.copyToGen(new Gen, this))) new TrieMap(r, null, hashing, equality) + else readOnlySnapshot() + } + + @tailrec override def clear(): Unit = { + val r = RDCSS_READ_ROOT() + if (!RDCSS_ROOT(r, r.gcasRead(this), INode.newRootNode[K, V](equality))) clear() + } + + def computeHash(k: K) = hashingobj.hash(k) + + @deprecated("Use getOrElse(k, null) instead.", "2.13.0") + def lookup(k: K): V = { + val hc = computeHash(k) + val lookupRes = lookuphc(k, hc) + val res = if (lookupRes == INodeBase.NO_SUCH_ELEMENT_SENTINEL) null else lookupRes + res.asInstanceOf[V] + } + + override def apply(k: K): V = { + val hc = computeHash(k) + val res = lookuphc(k, hc) + if (res eq INodeBase.NO_SUCH_ELEMENT_SENTINEL) throw new NoSuchElementException + else res.asInstanceOf[V] + } + + def get(k: K): Option[V] = { + val hc = computeHash(k) + val res = lookuphc(k, hc) + if (res eq INodeBase.NO_SUCH_ELEMENT_SENTINEL) None else Some(res).asInstanceOf[Option[V]] + } + + override def put(key: K, value: V): Option[V] = { + val hc = computeHash(key) + insertifhc(key, hc, value, INode.KEY_PRESENT_OR_ABSENT, fullEquals = false /* unused */) + } + + override def update(k: K, v: V): Unit = { + val hc = computeHash(k) + inserthc(k, hc, v) + } + + def addOne(kv: (K, V)) = { + update(kv._1, kv._2) + this + } + + override def remove(k: K): Option[V] = { + val hc = computeHash(k) + removehc(k = k, v = null.asInstanceOf[V], RemovalPolicy.Always, hc = hc) + } + + def subtractOne(k: K) = { + remove(k) + this + } + + def putIfAbsent(k: K, v: V): Option[V] = { + val hc = computeHash(k) + insertifhc(k, hc, v, INode.KEY_ABSENT, fullEquals = false /* unused */) + } + + // TODO once computeIfAbsent is added to concurrent.Map, + // move the comment there and tweak the 'at most once' part + /** If the specified key is not already in the map, computes its value using + * the given thunk `defaultValue` and enters it into the map. + * + * If the specified mapping function throws an exception, + * that exception is rethrown. + * + * Note: This method will invoke `defaultValue` at most once. + * However, `defaultValue` may be invoked without the result being added to the map if + * a concurrent process is also trying to add a value corresponding to the + * same key `k`. + * + * @param k the key to modify + * @param defaultValue the expression that computes the value + * @return the newly added value + */ + override def getOrElseUpdate(k: K, @deprecatedName("op", since="2.13.13") defaultValue: => V): V = { + val hc = computeHash(k) + lookuphc(k, hc) match { + case INodeBase.NO_SUCH_ELEMENT_SENTINEL => + val v = defaultValue + insertifhc(k, hc, v, INode.KEY_ABSENT, fullEquals = false /* unused */) match { + case Some(oldValue) => oldValue + case None => v + } + case oldValue => oldValue.asInstanceOf[V] + } + } + + def remove(k: K, v: V): Boolean = { + val hc = computeHash(k) + removehc(k, v, RemovalPolicy.FullEquals, hc).nonEmpty + } + + override private[collection] def removeRefEq(k: K, v: V): Boolean = { + val hc = computeHash(k) + removehc(k, v, RemovalPolicy.ReferenceEq, hc).nonEmpty + } + + def replace(k: K, oldvalue: V, newvalue: V): Boolean = { + val hc = computeHash(k) + insertifhc(k, hc, newvalue, oldvalue.asInstanceOf[AnyRef], fullEquals = true).nonEmpty + } + + override private[collection] def replaceRefEq(k: K, oldValue: V, newValue: V): Boolean = { + val hc = computeHash(k) + insertifhc(k, hc, newValue, oldValue.asInstanceOf[AnyRef], fullEquals = false).nonEmpty + } + + def replace(k: K, v: V): Option[V] = { + val hc = computeHash(k) + insertifhc(k, hc, v, INode.KEY_PRESENT, fullEquals = false /* unused */) + } + + def iterator: Iterator[(K, V)] = { + if (nonReadOnly) readOnlySnapshot().iterator + else new TrieMapIterator(0, this) + } + + //////////////////////////////////////////////////////////////////////////// + // + // scala/bug#10177 These methods need overrides as the inherited implementations + // call `.iterator` more than once, which doesn't guarantee a coherent + // view of the data if there is a concurrent writer + // Note that the we don't need overrides for keysIterator or valuesIterator + // TrieMapTest validates the behaviour. + override def values: Iterable[V] = { + if (nonReadOnly) readOnlySnapshot().values + else super.values + } + override def keySet: Set[K] = { + if (nonReadOnly) readOnlySnapshot().keySet + else super.keySet + } + + override def view: MapView[K, V] = if (nonReadOnly) readOnlySnapshot().view else super.view + + @deprecated("Use .view.filterKeys(f). A future version will include a strict version of this method (for now, .view.filterKeys(p).toMap).", "2.13.0") + override def filterKeys(p: K => Boolean): collection.MapView[K, V] = view.filterKeys(p) + + @deprecated("Use .view.mapValues(f). A future version will include a strict version of this method (for now, .view.mapValues(f).toMap).", "2.13.0") + override def mapValues[W](f: V => W): collection.MapView[K, W] = view.mapValues(f) + // END extra overrides + /////////////////////////////////////////////////////////////////// + + override def size: Int = + if (nonReadOnly) readOnlySnapshot().size + else RDCSS_READ_ROOT().cachedSize(this) + override def knownSize: Int = + if (nonReadOnly) -1 + else RDCSS_READ_ROOT().knownSize(this) + override def isEmpty: Boolean = + (if (nonReadOnly) readOnlySnapshot() else this).sizeIs == 0 // sizeIs checks knownSize + override protected[this] def className = "TrieMap" + + override def lastOption: Option[(K, V)] = if (isEmpty) None else Try(last).toOption +} + + +@SerialVersionUID(3L) +object TrieMap extends MapFactory[TrieMap] { + + def empty[K, V]: TrieMap[K, V] = new TrieMap[K, V] + + def from[K, V](it: IterableOnce[(K, V)]): TrieMap[K, V] = new TrieMap[K, V]() ++= it + + def newBuilder[K, V]: mutable.GrowableBuilder[(K, V), TrieMap[K, V]] = new GrowableBuilder(empty[K, V]) + + @transient + val inodeupdater: AtomicReferenceFieldUpdater[INodeBase[_, _], MainNode[_, _]] = AtomicReferenceFieldUpdater.newUpdater(classOf[INodeBase[_, _]], classOf[MainNode[_, _]], "mainnode") + + class MangledHashing[K] extends Hashing[K] { + def hash(k: K): Int = scala.util.hashing.byteswap32(k.##) + } + + private[concurrent] object RemovalPolicy { + final val Always = 0 + final val FullEquals = 1 + final val ReferenceEq = 2 + + def shouldRemove[V](removalPolicy: Int)(a: V, b: V): Boolean = + removalPolicy match { + case Always => true + case FullEquals => a == b + case ReferenceEq => a.asInstanceOf[AnyRef] eq b.asInstanceOf[AnyRef] + } + } +} + +// non-final as an extension point for parallel collections +private[collection] class TrieMapIterator[K, V](var level: Int, private var ct: TrieMap[K, V], mustInit: Boolean = true) extends AbstractIterator[(K, V)] { + private val stack = new Array[Array[BasicNode]](7) + private val stackpos = new Array[Int](7) + private var depth = -1 + private var subiter: Iterator[(K, V)] = null + private var current: KVNode[K, V] = null + + if (mustInit) initialize() + + def hasNext = (current ne null) || (subiter ne null) + + def next() = if (hasNext) { + var r: (K, V) = null + if (subiter ne null) { + r = subiter.next() + checkSubiter() + } else { + r = current.kvPair + advance() + } + r + } else Iterator.empty.next() + + private def readin(in: INode[K, V]) = in.gcasRead(ct) match { + case cn: CNode[K, V] => + depth += 1 + stack(depth) = cn.array + stackpos(depth) = -1 + advance() + case tn: TNode[K, V] => + current = tn + case ln: LNode[K, V] => + subiter = ln.entries.iterator + checkSubiter() + case null => + current = null + case mainNode => throw new MatchError(mainNode) + } + + private def checkSubiter() = if (!subiter.hasNext) { + subiter = null + advance() + } + + private def initialize(): Unit = { + assert(ct.isReadOnly) + + val r = ct.RDCSS_READ_ROOT() + readin(r) + } + + @tailrec + final def advance(): Unit = if (depth >= 0) { + val npos = stackpos(depth) + 1 + if (npos < stack(depth).length) { + stackpos(depth) = npos + stack(depth)(npos) match { + case sn: SNode[K, V] @uc => current = sn + case in: INode[K, V] @uc => readin(in) + case basicNode => throw new MatchError(basicNode) + } + } else { + depth -= 1 + advance() + } + } else current = null + + protected def newIterator(_lev: Int, _ct: TrieMap[K, V], _mustInit: Boolean): TrieMapIterator[K, V] = new TrieMapIterator[K, V](_lev, _ct, _mustInit) + + protected def dupTo(it: TrieMapIterator[K, V]): Unit = { + it.level = this.level + it.ct = this.ct + it.depth = this.depth + it.current = this.current + + // these need a deep copy + Array.copy(this.stack, 0, it.stack, 0, 7) + Array.copy(this.stackpos, 0, it.stackpos, 0, 7) + + // this one needs to be evaluated + if (this.subiter == null) it.subiter = null + else { + val lst = this.subiter.to(immutable.List) + this.subiter = lst.iterator + it.subiter = lst.iterator + } + } + + /** Returns a sequence of iterators over subsets of this iterator. + * It's used to ease the implementation of splitters for a parallel version of the TrieMap. + */ + protected def subdivide(): Seq[Iterator[(K, V)]] = if (subiter ne null) { + // the case where an LNode is being iterated + val it = newIterator(level + 1, ct, _mustInit = false) + it.depth = -1 + it.subiter = this.subiter + it.current = null + this.subiter = null + advance() + this.level += 1 + Seq(it, this) + } else if (depth == -1) { + this.level += 1 + Seq(this) + } else { + var d = 0 + while (d <= depth) { + val rem = stack(d).length - 1 - stackpos(d) + if (rem > 0) { + val (arr1, arr2) = stack(d).drop(stackpos(d) + 1).splitAt(rem / 2) + stack(d) = arr1 + stackpos(d) = -1 + val it = newIterator(level + 1, ct, _mustInit = false) + it.stack(0) = arr2 + it.stackpos(0) = -1 + it.depth = 0 + it.advance() // <-- fix it + this.level += 1 + return Seq(this, it) + } + d += 1 + } + this.level += 1 + Seq(this) + } + +} + +/** Only used for ctrie serialization. */ +@SerialVersionUID(3L) +private[concurrent] case object TrieMapSerializationEnd diff --git a/library/src/scala/collection/convert/AsJavaConverters.scala b/library/src/scala/collection/convert/AsJavaConverters.scala new file mode 100644 index 000000000000..2fc73da64fe7 --- /dev/null +++ b/library/src/scala/collection/convert/AsJavaConverters.scala @@ -0,0 +1,260 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package convert + +import java.util.{concurrent => juc} +import java.{lang => jl, util => ju} + +import scala.{unchecked => uc} + +/** Defines converter methods from Scala to Java collections. + * These methods are available through the [[scala.jdk.javaapi.CollectionConverters]] object. + */ +trait AsJavaConverters { + import JavaCollectionWrappers._ + + /** + * Converts a Scala `Iterator` to a Java `Iterator`. + * + * The returned Java `Iterator` is backed by the provided Scala `Iterator` and any side-effects of + * using it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Iterator` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Iterator` will be returned. + * + * @param i The Scala `Iterator` to be converted. + * @return A Java `Iterator` view of the argument. + */ + def asJava[A](i: Iterator[A]): ju.Iterator[A] = i match { + case null => null + case wrapper: JIteratorWrapper[A @uc] => wrapper.underlying + case _ => new IteratorWrapper(i) + } + + /** + * Converts a Scala `Iterator` to a Java `Enumeration`. + * + * The returned Java `Enumeration` is backed by the provided Scala `Iterator` and any side-effects + * of using it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Iterator` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Enumeration` will be returned. + * + * @param i The Scala `Iterator` to be converted. + * @return A Java `Enumeration` view of the argument. + */ + def asJavaEnumeration[A](i: Iterator[A]): ju.Enumeration[A] = i match { + case null => null + case wrapper: JEnumerationWrapper[A @uc] => wrapper.underlying + case _ => new IteratorWrapper(i) + } + + /** + * Converts a Scala `Iterable` to a Java `Iterable`. + * + * The returned Java `Iterable` is backed by the provided Scala `Iterable` and any side-effects of + * using it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Iterable` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Iterable` will be returned. + * + * @param i The Scala `Iterable` to be converted. + * @return A Java `Iterable` view of the argument. + */ + def asJava[A](i: Iterable[A]): jl.Iterable[A] = i match { + case null => null + case wrapper: JIterableWrapper[A @uc] => wrapper.underlying + case _ => new IterableWrapper(i) + } + + /** + * Converts a Scala `Iterable` to an immutable Java `Collection`. + * + * If the Scala `Iterable` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Collection` will be returned. + * + * @param i The Scala `Iterable` to be converted. + * @return A Java `Collection` view of the argument. + */ + def asJavaCollection[A](i: Iterable[A]): ju.Collection[A] = i match { + case null => null + case wrapper: JCollectionWrapper[A @uc] => wrapper.underlying + case _ => new IterableWrapper(i) + } + + /** + * Converts a Scala mutable `Buffer` to a Java List. + * + * The returned Java List is backed by the provided Scala `Buffer` and any side-effects of using + * it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Buffer` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `List` will be returned. + * + * @param b The Scala `Buffer` to be converted. + * @return A Java `List` view of the argument. + */ + def asJava[A](b: mutable.Buffer[A]): ju.List[A] = b match { + case null => null + case wrapper: JListWrapper[A @uc] => wrapper.underlying + case _ => new MutableBufferWrapper(b) + } + + /** + * Converts a Scala mutable `Seq` to a Java `List`. + * + * The returned Java `List` is backed by the provided Scala `Seq` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Seq` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `List` will be returned. + * + * @param s The Scala `Seq` to be converted. + * @return A Java `List` view of the argument. + */ + def asJava[A](s: mutable.Seq[A]): ju.List[A] = s match { + case null => null + case wrapper: JListWrapper[A @uc] => wrapper.underlying + case _ => new MutableSeqWrapper(s) + } + + /** + * Converts a Scala `Seq` to a Java `List`. + * + * The returned Java `List` is backed by the provided Scala `Seq` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Seq` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `List` will be returned. + * + * @param s The Scala `Seq` to be converted. + * @return A Java `List` view of the argument. + */ + def asJava[A](s: Seq[A]): ju.List[A] = s match { + case null => null + case wrapper: JListWrapper[A @uc] => wrapper.underlying + case _ => new SeqWrapper(s) + } + + /** + * Converts a Scala mutable `Set` to a Java `Set`. + * + * The returned Java `Set` is backed by the provided Scala `Set` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Set` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Set` will be returned. + * + * @param s The Scala mutable `Set` to be converted. + * @return A Java `Set` view of the argument. + */ + def asJava[A](s: mutable.Set[A]): ju.Set[A] = s match { + case null => null + case wrapper: JSetWrapper[A @uc] => wrapper.underlying + case _ => new MutableSetWrapper(s) + } + + /** + * Converts a Scala `Set` to a Java `Set`. + * + * The returned Java `Set` is backed by the provided Scala `Set` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Set` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Set` will be returned. + * + * @param s The Scala `Set` to be converted. + * @return A Java `Set` view of the argument. + */ + def asJava[A](s: Set[A]): ju.Set[A] = s match { + case null => null + case wrapper: JSetWrapper[A @uc] => wrapper.underlying + case _ => new SetWrapper(s) + } + + /** + * Converts a Scala mutable `Map` to a Java `Map`. + * + * The returned Java `Map` is backed by the provided Scala `Map` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Map` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Map` will be returned. + * + * @param m The Scala mutable `Map` to be converted. + * @return A Java `Map` view of the argument. + */ + def asJava[K, V](m: mutable.Map[K, V]): ju.Map[K, V] = m match { + case null => null + case wrapper: JMapWrapper[K @uc, V @uc] => wrapper.underlying + case _ => new MutableMapWrapper(m) + } + + /** + * Converts a Scala mutable `Map` to a Java `Dictionary`. + * + * The returned Java `Dictionary` is backed by the provided Scala `Dictionary` and any + * side-effects of using it via the Java interface will be visible via the Scala interface and + * vice versa. + * + * If the Scala `Map` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Dictionary` will be returned. + * + * @param m The Scala `Map` to be converted. + * @return A Java `Dictionary` view of the argument. + */ + def asJavaDictionary[K, V](m: mutable.Map[K, V]): ju.Dictionary[K, V] = m match { + case null => null + case wrapper: JDictionaryWrapper[K @uc, V @uc] => wrapper.underlying + case _ => new DictionaryWrapper(m) + } + + /** + * Converts a Scala `Map` to a Java `Map`. + * + * The returned Java `Map` is backed by the provided Scala `Map` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Map` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `Map` will be returned. + * + * @param m The Scala `Map` to be converted. + * @return A Java `Map` view of the argument. + */ + def asJava[K, V](m: Map[K, V]): ju.Map[K, V] = m match { + case null => null + case wrapper: JMapWrapper[K @uc, V @uc] => wrapper.underlying + case _ => new MapWrapper(m) + } + + /** + * Converts a Scala mutable `concurrent.Map` to a Java `ConcurrentMap`. + * + * The returned Java `ConcurrentMap` is backed by the provided Scala `concurrent.Map` and any + * side-effects of using it via the Java interface will be visible via the Scala interface and + * vice versa. + * + * If the Scala `concurrent.Map` was previously obtained from an implicit or explicit call of + * `asScala` then the original Java `ConcurrentMap` will be returned. + * + * @param m The Scala `concurrent.Map` to be converted. + * @return A Java `ConcurrentMap` view of the argument. + */ + def asJava[K, V](m: concurrent.Map[K, V]): juc.ConcurrentMap[K, V] = m match { + case null => null + case wrapper: JConcurrentMapWrapper[K @uc, V @uc] => wrapper.underlying + case _ => new ConcurrentMapWrapper(m) + } +} diff --git a/library/src/scala/collection/convert/AsJavaExtensions.scala b/library/src/scala/collection/convert/AsJavaExtensions.scala new file mode 100644 index 000000000000..d356a419325d --- /dev/null +++ b/library/src/scala/collection/convert/AsJavaExtensions.scala @@ -0,0 +1,108 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package convert + +import java.util.{concurrent => juc} +import java.{lang => jl, util => ju} + +/** Defines `asJava` extension methods, available through [[scala.jdk.CollectionConverters]]. */ +trait AsJavaExtensions { + import scala.jdk.javaapi.{CollectionConverters => conv} + + implicit class IteratorHasAsJava[A](i: Iterator[A]) { + /** Converts a Scala `Iterator` to a Java `Iterator`, see + * [[AsJavaConverters.asJava[A](i:Iterator[A])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.Iterator[A] = conv.asJava(i) + + /** Converts a Scala `Iterator` to a Java `Enumeration`, see + * [[AsJavaConverters.asJavaEnumeration `scala.jdk.javaapi.CollectionConverters.asJavaEnumeration`]]. + */ + def asJavaEnumeration: ju.Enumeration[A] = conv.asJavaEnumeration(i) + } + + implicit class IterableHasAsJava[A](i: Iterable[A]) { + /** Converts a Scala `Iterable` to a Java `Iterable`, see + * [[AsJavaConverters.asJava[A](i:Iterable[A])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: jl.Iterable[A] = conv.asJava(i) + + /** Converts a Scala `Iterator` to a Java `Collection`, see + * [[AsJavaConverters.asJavaCollection `scala.jdk.javaapi.CollectionConverters.asJavaCollection`]]. + */ + def asJavaCollection: ju.Collection[A] = conv.asJavaCollection(i) + } + + implicit class BufferHasAsJava[A](b: mutable.Buffer[A]) { + /** Converts a Scala `Buffer` to a Java `List`, see + * [[AsJavaConverters.asJava[A](b:scala\.collection\.mutable\.Buffer[A])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.List[A] = conv.asJava(b) + } + + implicit class MutableSeqHasAsJava[A](s: mutable.Seq[A]) { + /** Converts a Scala `Seq` to a Java `List`, see + * [[AsJavaConverters.asJava[A](s:scala\.collection\.mutable\.Seq[A])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.List[A] = conv.asJava(s) + } + + implicit class SeqHasAsJava[A](s: Seq[A]) { + /** Converts a Scala `Seq` to a Java `List`, see + * [[AsJavaConverters.asJava[A](s:scala\.collection\.Seq[A])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.List[A] = conv.asJava(s) + } + + implicit class MutableSetHasAsJava[A](s: mutable.Set[A]) { + /** Converts a Scala `mutable.Set` to a Java `Set`, see + * [[AsJavaConverters.asJava[A](s:scala\.collection\.mutable\.Set[A])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.Set[A] = conv.asJava(s) + } + + implicit class SetHasAsJava[A](s: Set[A]) { + /** Converts a Scala `Set` to a Java `Set`, see + * [[AsJavaConverters.asJava[A](s:scala\.collection\.Set[A])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.Set[A] = conv.asJava(s) + } + + implicit class MutableMapHasAsJava[K, V](m: mutable.Map[K, V]) { + /** Converts a Scala `mutable.Map` to a Java `Map`, see + * [[AsJavaConverters.asJava[K,V](m:scala\.collection\.mutable\.Map[K,V])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.Map[K, V] = conv.asJava(m) + + /** Converts a Scala `mutable.Map` to a Java `Map`, see + * [[AsJavaConverters.asJavaDictionary `scala.jdk.javaapi.CollectionConverters.asJavaDictionary`]]. + */ + def asJavaDictionary: ju.Dictionary[K, V] = conv.asJavaDictionary(m) + } + + implicit class MapHasAsJava[K, V](m: Map[K, V]) { + /** Converts a Scala `Map` to a Java `Map`, see + * [[AsJavaConverters.asJava[K,V](m:scala\.collection\.Map[K,V])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: ju.Map[K, V] = conv.asJava(m) + } + + implicit class ConcurrentMapHasAsJava[K, V](m: concurrent.Map[K, V]) { + /** Converts a Scala `concurrent.Map` to a Java `ConcurrentMap`, see + * [[AsJavaConverters.asJava[K,V](m:scala\.collection\.concurrent\.Map[K,V])* `scala.jdk.javaapi.CollectionConverters.asJava`]]. + */ + def asJava: juc.ConcurrentMap[K, V] = conv.asJava(m) + } +} diff --git a/library/src/scala/collection/convert/AsScalaConverters.scala b/library/src/scala/collection/convert/AsScalaConverters.scala new file mode 100644 index 000000000000..e1055c60b36e --- /dev/null +++ b/library/src/scala/collection/convert/AsScalaConverters.scala @@ -0,0 +1,207 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package convert + +import java.util.{concurrent => juc} +import java.{lang => jl, util => ju} + +import scala.{unchecked => uc} + +/** Defines converter methods from Java to Scala collections. + * These methods are available through the [[scala.jdk.javaapi.CollectionConverters]] object. + */ +trait AsScalaConverters { + import JavaCollectionWrappers._ + + /** + * Converts a Java `Iterator` to a Scala `Iterator`. + * + * The returned Scala `Iterator` is backed by the provided Java `Iterator` and any side-effects of + * using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Iterator` was previously obtained from an implicit or explicit call of + * `asJava` then the original Scala `Iterator` will be returned. + * + * @param i The Java `Iterator` to be converted. + * @return A Scala `Iterator` view of the argument. + */ + def asScala[A](i: ju.Iterator[A]): Iterator[A] = i match { + case null => null + case wrapper: IteratorWrapper[A @uc] => wrapper.underlying + case _ => new JIteratorWrapper(i) + } + + /** + * Converts a Java `Enumeration` to a Scala `Iterator`. + * + * The returned Scala `Iterator` is backed by the provided Java `Enumeration` and any side-effects + * of using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Enumeration` was previously obtained from an implicit or explicit call of + * `asJavaEnumeration` then the original Scala `Iterator` will be returned. + * + * @param e The Java `Enumeration` to be converted. + * @return A Scala `Iterator` view of the argument. + */ + def asScala[A](e: ju.Enumeration[A]): Iterator[A] = e match { + case null => null + case wrapper: IteratorWrapper[A @uc] => wrapper.underlying + case _ => new JEnumerationWrapper(e) + } + + /** + * Converts a Java `Iterable` to a Scala `Iterable`. + * + * The returned Scala `Iterable` is backed by the provided Java `Iterable` and any side-effects of + * using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Iterable` was previously obtained from an implicit or explicit call of + * `asJava` then the original Scala `Iterable` will be returned. + * + * @param i The Java `Iterable` to be converted. + * @return A Scala `Iterable` view of the argument. + */ + def asScala[A](i: jl.Iterable[A]): Iterable[A] = i match { + case null => null + case wrapper: IterableWrapper[A @uc] => wrapper.underlying + case _ => new JIterableWrapper(i) + } + + /** + * Converts a Java `Collection` to a Scala `Iterable`. + * + * If the Java `Collection` was previously obtained from an implicit or explicit call of + * `asJavaCollection` then the original Scala `Iterable` will be returned. + * + * @param c The Java `Collection` to be converted. + * @return A Scala `Iterable` view of the argument. + */ + def asScala[A](c: ju.Collection[A]): Iterable[A] = c match { + case null => null + case wrapper: IterableWrapper[A @uc] => wrapper.underlying + case _ => new JCollectionWrapper(c) + } + + /** + * Converts a Java `List` to a Scala mutable `Buffer`. + * + * The returned Scala `Buffer` is backed by the provided Java `List` and any side-effects of using + * it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `List` was previously obtained from an implicit or explicit call of + * `asJava` then the original Scala `Buffer` will be returned. + * + * @param l The Java `List` to be converted. + * @return A Scala mutable `Buffer` view of the argument. + */ + def asScala[A](l: ju.List[A]): mutable.Buffer[A] = l match { + case null => null + case wrapper: MutableBufferWrapper[A @uc] => wrapper.underlying + case _ => new JListWrapper(l) + } + + /** + * Converts a Java `Set` to a Scala mutable `Set`. + * + * The returned Scala `Set` is backed by the provided Java `Set` and any side-effects of using it + * via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Set` was previously obtained from an implicit or explicit call of + * `asJava` then the original Scala `Set` will be returned. + * + * @param s The Java `Set` to be converted. + * @return A Scala mutable `Set` view of the argument. + */ + def asScala[A](s: ju.Set[A]): mutable.Set[A] = s match { + case null => null + case wrapper: MutableSetWrapper[A @uc] => wrapper.underlying + case _ => new JSetWrapper(s) + } + + /** + * Converts a Java `Map` to a Scala mutable `Map`. + * + * The returned Scala `Map` is backed by the provided Java `Map` and any side-effects of using it + * via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Map` was previously obtained from an implicit or explicit call of + * `asJava` then the original Scala `Map` will be returned. + * + * If the wrapped map is synchronized (e.g. from `java.util.Collections.synchronizedMap`), it is + * your responsibility to wrap all non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an atomic `get` when `null` + * values may be present. + * + * @param m The Java `Map` to be converted. + * @return A Scala mutable `Map` view of the argument. + */ + def asScala[K, V](m: ju.Map[K, V]): mutable.Map[K, V] = m match { + case null => null + case wrapper: MutableMapWrapper[K @uc, V @uc] => wrapper.underlying + case _ => new JMapWrapper(m) + } + + /** + * Converts a Java `ConcurrentMap` to a Scala mutable `ConcurrentMap`. + * + * The returned Scala `ConcurrentMap` is backed by the provided Java `ConcurrentMap` and any + * side-effects of using it via the Scala interface will be visible via the Java interface and + * vice versa. + * + * If the Java `ConcurrentMap` was previously obtained from an implicit or explicit call of + * `asJava` then the original Scala `ConcurrentMap` will be returned. + * + * @param m The Java `ConcurrentMap` to be converted. + * @return A Scala mutable `ConcurrentMap` view of the argument. + */ + def asScala[K, V](m: juc.ConcurrentMap[K, V]): concurrent.Map[K, V] = m match { + case null => null + case wrapper: ConcurrentMapWrapper[K @uc, V @uc] => wrapper.underlyingConcurrentMap + case _ => new JConcurrentMapWrapper(m) + } + + /** + * Converts a Java `Dictionary` to a Scala mutable `Map`. + * + * The returned Scala `Map` is backed by the provided Java `Dictionary` and any side-effects of + * using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Dictionary` was previously obtained from an implicit or explicit call of + * `asJavaDictionary` then the original Scala `Map` will be returned. + * + * @param d The Java `Dictionary` to be converted. + * @return A Scala mutable `Map` view of the argument. + */ + def asScala[K, V](d: ju.Dictionary[K, V]): mutable.Map[K, V] = d match { + case null => null + case wrapper: DictionaryWrapper[K @uc, V @uc] => wrapper.underlying + case _ => new JDictionaryWrapper(d) + } + + /** + * Converts a Java `Properties` to a Scala mutable `Map[String, String]`. + * + * The returned Scala `Map[String, String]` is backed by the provided Java `Properties` and any + * side-effects of using it via the Scala interface will be visible via the Java interface and + * vice versa. + * + * @param p The Java `Properties` to be converted. + * @return A Scala mutable `Map[String, String]` view of the argument. + */ + def asScala(p: ju.Properties): mutable.Map[String, String] = p match { + case null => null + case _ => new JPropertiesWrapper(p) + } +} diff --git a/library/src/scala/collection/convert/AsScalaExtensions.scala b/library/src/scala/collection/convert/AsScalaExtensions.scala new file mode 100644 index 000000000000..ef08f4505fe1 --- /dev/null +++ b/library/src/scala/collection/convert/AsScalaExtensions.scala @@ -0,0 +1,93 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package convert + +import java.util.{concurrent => juc} +import java.{lang => jl, util => ju} + +/** Defines `asScala` extension methods, available through [[scala.jdk.CollectionConverters]]. */ +trait AsScalaExtensions { + import scala.jdk.javaapi.{CollectionConverters => conv} + + implicit class IteratorHasAsScala[A](i: ju.Iterator[A]) { + /** Converts a Java `Iterator` to a Scala `Iterator`, see + * [[AsScalaConverters.asScala[A](i:java\.util\.Iterator[A])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: Iterator[A] = conv.asScala(i) + } + + implicit class EnumerationHasAsScala[A](e: ju.Enumeration[A]) { + /** Converts a Java `Enumeration` to a Scala `Iterator`, see + * [[AsScalaConverters.asScala[A](e:java\.util\.Enumeration[A])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: Iterator[A] = conv.asScala(e) + } + + implicit class IterableHasAsScala[A](i: jl.Iterable[A]) { + /** Converts a Java `Iterable` to a Scala `Iterable`, see + * [[AsScalaConverters.asScala[A](i:Iterable[A])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: Iterable[A] = conv.asScala(i) + } + + implicit class CollectionHasAsScala[A](c: ju.Collection[A]) { + /** Converts a Java `Collection` to a Scala `Iterable`, see + * [[AsScalaConverters.asScala[A](c:java\.util\.Collection[A])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: Iterable[A] = conv.asScala(c) + } + + implicit class ListHasAsScala[A](l: ju.List[A]) { + /** Converts a Java `List` to a Scala `Buffer`, see + * [[AsScalaConverters.asScala[A](l:java\.util\.List[A])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: mutable.Buffer[A] = conv.asScala(l) + } + + implicit class SetHasAsScala[A](s: ju.Set[A]) { + /** Converts a Java `Set` to a Scala `Set`, see + * [[AsScalaConverters.asScala[A](s:java\.util\.Set[A])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: mutable.Set[A] = conv.asScala(s) + } + + implicit class MapHasAsScala[K, V](m: ju.Map[K, V]) { + /** Converts a Java `Map` to a Scala `Map`, see + * [[AsScalaConverters.asScala[A,B](m:java\.util\.Map[A,B])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: mutable.Map[K, V] = conv.asScala(m) + } + + implicit class ConcurrentMapHasAsScala[K, V](m: juc.ConcurrentMap[K, V]) { + /** Converts a Java `ConcurrentMap` to a Scala `concurrent.Map`, see + * [[AsScalaConverters.asScala[A,B](m:java\.util\.concurrent\.ConcurrentMap[A,B])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: concurrent.Map[K, V] = conv.asScala(m) + } + + implicit class DictionaryHasAsScala[K, V](d: ju.Dictionary[K, V]) { + /** Converts a Java `Dictionary` to a Scala `Map`, see + * [[AsScalaConverters.asScala[A,B](d:java\.util\.Dictionary[A,B])* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: mutable.Map[K, V] = conv.asScala(d) + } + + implicit class PropertiesHasAsScala(i: ju.Properties) { + /** Converts a Java `Properties` to a Scala `Map`, see + * [[AsScalaConverters.asScala(p:java\.util\.Properties)* `scala.jdk.javaapi.CollectionConverters.asScala`]]. + */ + def asScala: mutable.Map[String, String] = conv.asScala(i) + } +} diff --git a/library/src/scala/collection/convert/ImplicitConversions.scala b/library/src/scala/collection/convert/ImplicitConversions.scala new file mode 100644 index 000000000000..6492c60d6d9e --- /dev/null +++ b/library/src/scala/collection/convert/ImplicitConversions.scala @@ -0,0 +1,181 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package convert + +import java.util.{concurrent => juc} +import java.{lang => jl, util => ju} + +import scala.collection.JavaConverters._ +import scala.language.implicitConversions + +/** Defines implicit converter methods from Java to Scala collections. */ +@deprecated("Use `scala.jdk.CollectionConverters` instead", "2.13.0") +trait ToScalaImplicits { + /** Implicitly converts a Java `Iterator` to a Scala `Iterator`. + * @see [[JavaConverters.asScalaIterator]] + */ + implicit def `iterator asScala`[A](it: ju.Iterator[A]): Iterator[A] = asScalaIterator(it) + + /** Implicitly converts a Java `Enumeration` to a Scala `Iterator`. + * @see [[JavaConverters.enumerationAsScalaIterator]] + */ + implicit def `enumeration AsScalaIterator`[A](i: ju.Enumeration[A]): Iterator[A] = enumerationAsScalaIterator(i) + + /** Implicitly converts a Java `Iterable` to a Scala `Iterable`. + * @see [[JavaConverters.iterableAsScalaIterable]] + */ + implicit def `iterable AsScalaIterable`[A](i: jl.Iterable[A]): Iterable[A] = iterableAsScalaIterable(i) + + /** Implicitly converts a Java `Collection` to an Scala `Iterable`. + * @see [[JavaConverters.collectionAsScalaIterable]] + */ + implicit def `collection AsScalaIterable`[A](i: ju.Collection[A]): Iterable[A] = collectionAsScalaIterable(i) + + /** Implicitly converts a Java `List` to a Scala mutable `Buffer`. + * @see [[JavaConverters.asScalaBuffer]] + */ + implicit def `list asScalaBuffer`[A](l: ju.List[A]): mutable.Buffer[A] = asScalaBuffer(l) + + /** Implicitly converts a Java `Set` to a Scala mutable `Set`. + * @see [[JavaConverters.asScalaSet]] + */ + implicit def `set asScala`[A](s: ju.Set[A]): mutable.Set[A] = asScalaSet(s) + + /** Implicitly converts a Java `Map` to a Scala mutable `Map`. + * @see [[JavaConverters.mapAsScalaMap]] + */ + implicit def `map AsScala`[K, V](m: ju.Map[K, V]): mutable.Map[K, V] = mapAsScalaMap(m) + + /** Implicitly converts a Java `ConcurrentMap` to a Scala mutable `ConcurrentMap`. + * @see [[JavaConverters.mapAsScalaConcurrentMap]] + */ + implicit def `map AsScalaConcurrentMap`[K, V](m: juc.ConcurrentMap[K, V]): concurrent.Map[K, V] = mapAsScalaConcurrentMap(m) + + /** Implicitly converts a Java `Dictionary` to a Scala mutable `Map`. + * @see [[JavaConverters.dictionaryAsScalaMap]] + */ + implicit def `dictionary AsScalaMap`[K, V](p: ju.Dictionary[K, V]): mutable.Map[K, V] = dictionaryAsScalaMap(p) + + /** Implicitly converts a Java `Properties` to a Scala `mutable Map[String, String]`. + * @see [[JavaConverters.propertiesAsScalaMap]] + */ + implicit def `properties AsScalaMap`(p: ju.Properties): mutable.Map[String, String] = propertiesAsScalaMap(p) +} + +/** Defines implicit conversions from Scala to Java collections. */ +@deprecated("Use `scala.jdk.CollectionConverters` instead", "2.13.0") +trait ToJavaImplicits { + /** Implicitly converts a Scala `Iterator` to a Java `Iterator`. + * @see [[JavaConverters.asJavaIterator]] + */ + implicit def `iterator asJava`[A](it: Iterator[A]): ju.Iterator[A] = asJavaIterator(it) + + /** Implicitly converts a Scala `Iterator` to a Java `Enumeration`. + * @see [[JavaConverters.asJavaEnumeration]] + */ + implicit def `enumeration asJava`[A](it: Iterator[A]): ju.Enumeration[A] = asJavaEnumeration(it) + + /** Implicitly converts a Scala `Iterable` to a Java `Iterable`. + * @see [[JavaConverters.asJavaIterable]] + */ + implicit def `iterable asJava`[A](i: Iterable[A]): jl.Iterable[A] = asJavaIterable(i) + + /** Implicitly converts a Scala `Iterable` to an immutable Java `Collection`. + * @see [[JavaConverters.asJavaCollection]] + */ + implicit def `collection asJava`[A](it: Iterable[A]): ju.Collection[A] = asJavaCollection(it) + + /** Implicitly converts a Scala mutable `Buffer` to a Java `List`. + * @see [[JavaConverters.bufferAsJavaList]] + */ + implicit def `buffer AsJavaList`[A](b: mutable.Buffer[A]): ju.List[A] = bufferAsJavaList(b) + + /** Implicitly converts a Scala mutable `Seq` to a Java `List`. + * @see [[JavaConverters.mutableSeqAsJavaList]] + */ + implicit def `mutableSeq AsJavaList`[A](seq: mutable.Seq[A]): ju.List[A] = mutableSeqAsJavaList(seq) + + /** Implicitly converts a Scala `Seq` to a Java `List`. + * @see [[JavaConverters.seqAsJavaList]] + */ + implicit def `seq AsJavaList`[A](seq: Seq[A]): ju.List[A] = seqAsJavaList(seq) + + /** Implicitly converts a Scala mutable `Set` to a Java `Set`. + * @see [[JavaConverters.mutableSetAsJavaSet]] + */ + implicit def `mutableSet AsJavaSet`[A](s: mutable.Set[A]): ju.Set[A] = mutableSetAsJavaSet(s) + + /** Implicitly converts a Scala `Set` to a Java `Set`. + * @see [[JavaConverters.setAsJavaSet]] + */ + implicit def `set AsJavaSet`[A](s: Set[A]): ju.Set[A] = setAsJavaSet(s) + + /** Implicitly converts a Scala mutable `Map` to a Java `Map`. + * @see [[JavaConverters.mutableMapAsJavaMap]] + */ + implicit def `mutableMap AsJavaMap`[K, V](m: mutable.Map[K, V]): ju.Map[K, V] = mutableMapAsJavaMap(m) + + /** Implicitly converts a Scala mutable `Map` to a Java `Dictionary`. + * @see [[JavaConverters.asJavaDictionary]] + */ + implicit def `dictionary asJava`[K, V](m: mutable.Map[K, V]): ju.Dictionary[K, V] = asJavaDictionary(m) + + /** Implicitly converts a Scala `Map` to a Java `Map`. + * @see [[JavaConverters.mapAsJavaMap]] + */ + implicit def `map AsJavaMap`[K, V](m: Map[K, V]): ju.Map[K, V] = mapAsJavaMap(m) + + /** Implicitly converts a Scala mutable `concurrent.Map` to a Java `ConcurrentMap`. + * @see [[JavaConverters.mapAsJavaConcurrentMap]] + */ + implicit def `map AsJavaConcurrentMap`[K, V](m: concurrent.Map[K, V]): juc.ConcurrentMap[K, V] = mapAsJavaConcurrentMap(m) +} + +/** + * Convenience for miscellaneous implicit conversions from Scala to Java collections API. + * + * It is recommended to use explicit conversions provided by [[collection.JavaConverters]] instead. + * Implicit conversions may cause unexpected issues, see [[ImplicitConversions]]. + */ +@deprecated("Use `scala.jdk.CollectionConverters` instead", "2.13.0") +object ImplicitConversionsToJava extends ToJavaImplicits + +/** + * Convenience for miscellaneous implicit conversions from Java to Scala collections API. + * + * It is recommended to use explicit conversions provided by [[collection.JavaConverters]] instead. + * Implicit conversions may cause unexpected issues, see [[ImplicitConversions]]. + */ +@deprecated("Use `scala.jdk.CollectionConverters` instead", "2.13.0") +object ImplicitConversionsToScala extends ToScalaImplicits + +/** + * Convenience for miscellaneous implicit conversions between Java and Scala collections API. + * + * It is recommended to use explicit conversions provided by [[collection.JavaConverters]] instead. + * Implicit conversions may cause unexpected issues. Example: + * + * {{{ + * import collection.convert.ImplicitConversions._ + * case class StringBox(s: String) + * val m = Map(StringBox("one") -> "uno") + * m.get("one") + * }}} + * + * The above example returns `null` instead of producing a type error at compile-time. The map is + * implicitly converted to a `java.util.Map` which provides a method `get(x: AnyRef)`. + */ +@deprecated("Use `scala.jdk.CollectionConverters` instead", "2.13.0") +object ImplicitConversions extends ToScalaImplicits with ToJavaImplicits diff --git a/library/src/scala/collection/convert/JavaCollectionWrappers.scala b/library/src/scala/collection/convert/JavaCollectionWrappers.scala new file mode 100644 index 000000000000..f79adff98e23 --- /dev/null +++ b/library/src/scala/collection/convert/JavaCollectionWrappers.scala @@ -0,0 +1,635 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package convert + +import java.util.{concurrent => juc} +import java.util.{NavigableMap} +import java.{lang => jl, util => ju} + +import scala.jdk.CollectionConverters._ +import scala.util.Try +import scala.util.chaining._ +import scala.util.control.ControlThrowable + +/** Wrappers for exposing Scala collections as Java collections and vice-versa */ +@SerialVersionUID(3L) +// not private[convert] because `WeakHashMap` uses JMapWrapper +private[collection] object JavaCollectionWrappers extends Serializable { + @SerialVersionUID(3L) + class IteratorWrapper[A](val underlying: Iterator[A]) extends ju.Iterator[A] with ju.Enumeration[A] with Serializable { + def hasNext = underlying.hasNext + def next() = underlying.next() + def hasMoreElements = underlying.hasNext + def nextElement() = underlying.next() + override def remove(): Nothing = throw new UnsupportedOperationException + override def equals(other: Any): Boolean = other match { + case that: IteratorWrapper[_] => this.underlying == that.underlying + case _ => false + } + override def hashCode: Int = underlying.hashCode() + } + + @SerialVersionUID(3L) + class JIteratorWrapper[A](val underlying: ju.Iterator[A]) extends AbstractIterator[A] with Serializable { + def hasNext = underlying.hasNext + def next() = underlying.next + override def equals(other: Any): Boolean = other match { + case that: JIteratorWrapper[_] => this.underlying == that.underlying + case _ => false + } + override def hashCode: Int = underlying.hashCode() + } + + @SerialVersionUID(3L) + class JEnumerationWrapper[A](val underlying: ju.Enumeration[A]) extends AbstractIterator[A] with Serializable { + def hasNext = underlying.hasMoreElements + def next() = underlying.nextElement + override def equals(other: Any): Boolean = other match { + case that: JEnumerationWrapper[_] => this.underlying == that.underlying + case _ => false + } + override def hashCode: Int = underlying.hashCode() + } + + trait IterableWrapperTrait[A] extends ju.AbstractCollection[A] { + val underlying: Iterable[A] + def size = underlying.size + override def iterator: IteratorWrapper[A] = new IteratorWrapper(underlying.iterator) + override def isEmpty = underlying.isEmpty + } + + @SerialVersionUID(3L) + class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable { + override def equals(other: Any): Boolean = other match { + case that: IterableWrapper[_] => this.underlying == that.underlying + case _ => false + } + override def hashCode: Int = underlying.hashCode() + } + + @SerialVersionUID(3L) + class JIterableWrapper[A](val underlying: jl.Iterable[A]) + extends AbstractIterable[A] + with StrictOptimizedIterableOps[A, Iterable, Iterable[A]] + with Serializable { + def iterator = underlying.iterator.asScala + override def iterableFactory: mutable.ArrayBuffer.type = mutable.ArrayBuffer + override def isEmpty: Boolean = !underlying.iterator().hasNext + override def equals(other: Any): Boolean = other match { + case that: JIterableWrapper[_] => this.underlying == that.underlying + case _ => false + } + override def hashCode: Int = underlying.hashCode() + } + + @SerialVersionUID(3L) + class JCollectionWrapper[A](val underlying: ju.Collection[A]) + extends AbstractIterable[A] + with StrictOptimizedIterableOps[A, Iterable, Iterable[A]] + with Serializable { + def iterator = underlying.iterator.asScala + override def size = underlying.size + override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize + override def isEmpty = underlying.isEmpty + override def iterableFactory: mutable.ArrayBuffer.type = mutable.ArrayBuffer + override def equals(other: Any): Boolean = other match { + case that: JCollectionWrapper[_] => this.underlying == that.underlying + case _ => false + } + override def hashCode: Int = underlying.hashCode() + } + + @SerialVersionUID(3L) + class SeqWrapper[A](val underlying: Seq[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] with Serializable { + def get(i: Int) = underlying(i) + } + + @SerialVersionUID(3L) + class MutableSeqWrapper[A](val underlying: mutable.Seq[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] with Serializable { + def get(i: Int) = underlying(i) + override def set(i: Int, elem: A) = { + val p = underlying(i) + underlying(i) = elem + p + } + } + + @SerialVersionUID(3L) + class MutableBufferWrapper[A](val underlying: mutable.Buffer[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] with Serializable { + def get(i: Int) = underlying(i) + override def set(i: Int, elem: A) = { val p = underlying(i); underlying(i) = elem; p } + override def add(elem: A) = { underlying += elem; true } + override def remove(i: Int) = underlying remove i + } + + @SerialVersionUID(3L) + class JListWrapper[A](val underlying: ju.List[A]) + extends mutable.AbstractBuffer[A] + with SeqOps[A, mutable.Buffer, mutable.Buffer[A]] + with StrictOptimizedSeqOps[A, mutable.Buffer, mutable.Buffer[A]] + with IterableFactoryDefaults[A, mutable.Buffer] + with Serializable { + def length = underlying.size + override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize + override def isEmpty = underlying.isEmpty + override def iterator: Iterator[A] = underlying.iterator.asScala + def apply(i: Int) = underlying.get(i) + def update(i: Int, elem: A) = underlying.set(i, elem) + def prepend(elem: A) = { underlying.subList(0, 0) add elem; this } + def addOne(elem: A): this.type = { underlying add elem; this } + def insert(idx: Int,elem: A): Unit = underlying.subList(0, idx).add(elem) + def insertAll(i: Int, elems: IterableOnce[A]) = { + val ins = underlying.subList(0, i) + elems.iterator.foreach(ins.add(_)) + } + def remove(i: Int) = underlying.remove(i) + def clear() = underlying.clear() + // Note: Clone cannot just call underlying.clone because in Java, only specific collections + // expose clone methods. Generically, they're protected. + override def clone(): JListWrapper[A] = new JListWrapper(new ju.ArrayList[A](underlying)) + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type = { + remove(from, replaced) + insertAll(from, patch) + this + } + def remove(from: Int, n: Int): Unit = underlying.subList(from, from+n).clear() + override def iterableFactory: mutable.ArrayBuffer.type = mutable.ArrayBuffer + override def subtractOne(elem: A): this.type = { underlying.remove(elem.asInstanceOf[AnyRef]); this } + } + + @SerialVersionUID(3L) + class SetWrapper[A](underlying: Set[A]) extends ju.AbstractSet[A] with Serializable { self => + // Note various overrides to avoid performance gotchas. + override def contains(o: Object): Boolean = { + try { underlying.contains(o.asInstanceOf[A]) } + catch { case cce: ClassCastException => false } + } + override def isEmpty = underlying.isEmpty + def size = underlying.size + def iterator: ju.Iterator[A] = new ju.Iterator[A] { + val ui = underlying.iterator + var prev: Option[A] = None + def hasNext = ui.hasNext + def next = { val e = ui.next(); prev = Some(e); e } + override def remove() = prev match { + case Some(e) => + underlying match { + case ms: mutable.Set[a] => + ms remove e + prev = None + case _ => + throw new UnsupportedOperationException("remove") + } + case _ => + throw new IllegalStateException("next must be called at least once before remove") + } + } + } + + @SerialVersionUID(3L) + class MutableSetWrapper[A](val underlying: mutable.Set[A]) extends SetWrapper[A](underlying) with Serializable { + override def add(elem: A) = { + val sz = underlying.size + underlying += elem + sz < underlying.size + } + override def remove(elem: AnyRef) = + try underlying.remove(elem.asInstanceOf[A]) + catch { case ex: ClassCastException => false } + override def clear() = underlying.clear() + } + + @SerialVersionUID(3L) + class JSetWrapper[A](val underlying: ju.Set[A]) + extends mutable.AbstractSet[A] + with mutable.SetOps[A, mutable.Set, mutable.Set[A]] + with StrictOptimizedSetOps[A, mutable.Set, mutable.Set[A]] + with Serializable { + + override def size: Int = underlying.size + override def isEmpty: Boolean = underlying.isEmpty + override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize + def iterator: Iterator[A] = underlying.iterator.asScala + + def contains(elem: A): Boolean = underlying.contains(elem) + + def addOne(elem: A): this.type = { underlying add elem; this } + def subtractOne(elem: A): this.type = { underlying remove elem; this } + + override def remove(elem: A): Boolean = underlying remove elem + + override def clear(): Unit = { + underlying.clear() + } + + override def empty: mutable.Set[A] = new JSetWrapper(new ju.HashSet[A]) + + // Note: Clone cannot just call underlying.clone because in Java, only specific collections + // expose clone methods. Generically, they're protected. + override def clone(): mutable.Set[A] = new JSetWrapper[A](new ju.LinkedHashSet[A](underlying)) + + override def iterableFactory: IterableFactory[mutable.Set] = mutable.HashSet + + override def filterInPlace(p: A => Boolean): this.type = { + if (underlying.size() > 0) underlying.removeIf(!p(_)) + this + } + } + + @SerialVersionUID(3L) + class MapWrapper[K, V](underlying: Map[K, V]) extends ju.AbstractMap[K, V] with Serializable { self => + override def size = underlying.size + + override def get(key: AnyRef): V = try { + underlying get key.asInstanceOf[K] match { + case None => null.asInstanceOf[V] + case Some(v) => v + } + } catch { + case ex: ClassCastException => null.asInstanceOf[V] + } + + override def entrySet: ju.Set[ju.Map.Entry[K, V]] = new ju.AbstractSet[ju.Map.Entry[K, V]] { + def size = self.size + + def iterator: ju.Iterator[ju.Map.Entry[K, V]] = new ju.Iterator[ju.Map.Entry[K, V]] { + val ui = underlying.iterator + var prev : Option[K] = None + + def hasNext = ui.hasNext + + def next(): ju.Map.Entry[K, V] = { + val (k, v) = ui.next() + prev = Some(k) + new ju.Map.Entry[K, V] { + def getKey = k + def getValue = v + def setValue(v1 : V) = self.put(k, v1) + + // It's important that this implementation conform to the contract + // specified in the javadocs of java.util.Map.Entry.hashCode + // + // See https://github.com/scala/bug/issues/10663 + override def hashCode = { + (if (k == null) 0 else k.hashCode()) ^ + (if (v == null) 0 else v.hashCode()) + } + + override def equals(other: Any) = other match { + case e: ju.Map.Entry[_, _] => k == e.getKey && v == e.getValue + case _ => false + } + } + } + + override def remove(): Unit = { + prev match { + case Some(k) => + underlying match { + case mm: mutable.Map[a, _] => + mm -= k + prev = None + case _ => + throw new UnsupportedOperationException("remove") + } + case _ => + throw new IllegalStateException("next must be called at least once before remove") + } + } + } + } + + override def containsKey(key: AnyRef): Boolean = try { + // Note: Subclass of collection.Map with specific key type may redirect generic + // contains to specific contains, which will throw a ClassCastException if the + // wrong type is passed. This is why we need a type cast to A inside a try/catch. + underlying.contains(key.asInstanceOf[K]) + } catch { + case ex: ClassCastException => false + } + } + + @SerialVersionUID(3L) + class MutableMapWrapper[K, V](val underlying: mutable.Map[K, V]) extends MapWrapper[K, V](underlying) { + override def put(k: K, v: V) = underlying.put(k, v) match { + case Some(v1) => v1 + case None => null.asInstanceOf[V] + } + + override def remove(k: AnyRef): V = try { + underlying remove k.asInstanceOf[K] match { + case None => null.asInstanceOf[V] + case Some(v) => v + } + } catch { + case ex: ClassCastException => null.asInstanceOf[V] + } + + override def clear() = underlying.clear() + } + + @SerialVersionUID(3L) + abstract class AbstractJMapWrapper[K, V] + extends mutable.AbstractMap[K, V] + with JMapWrapperLike[K, V, mutable.Map, mutable.Map[K, V]] with Serializable + + trait JMapWrapperLike[K, V, +CC[X, Y] <: mutable.MapOps[X, Y, CC, _], +C <: mutable.MapOps[K, V, CC, C]] + extends mutable.MapOps[K, V, CC, C] + with StrictOptimizedMapOps[K, V, CC, C] + with StrictOptimizedIterableOps[(K, V), mutable.Iterable, C] { + + def underlying: ju.Map[K, V] + + override def size = underlying.size + + // support Some(null) if currently bound to null + def get(k: K) = { + val v = underlying.get(k) + if (v != null) + Some(v) + else if (underlying.containsKey(k)) + Some(null.asInstanceOf[V]) + else + None + } + + override def getOrElseUpdate(key: K, op: => V): V = + underlying.computeIfAbsent(key, _ => op) match { + case null => update(key, null.asInstanceOf[V]); null.asInstanceOf[V] + case v => v + } + + def addOne(kv: (K, V)): this.type = { underlying.put(kv._1, kv._2); this } + def subtractOne(key: K): this.type = { underlying remove key; this } + + // support Some(null) if currently bound to null + override def put(k: K, v: V): Option[V] = + if (v == null) { + val present = underlying.containsKey(k) + val result = underlying.put(k, v) + if (present) Some(result) else None + } else { + var result: Option[V] = None + def recompute(k0: K, v0: V): V = v.tap(_ => + if (v0 != null) result = Some(v0) + else if (underlying.containsKey(k0)) result = Some(null.asInstanceOf[V]) + ) + underlying.compute(k, recompute) + result + } + + override def update(k: K, v: V): Unit = underlying.put(k, v) + + override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { + def remap(k: K, v: V): V = + remappingFunction(Option(v)) match { + case Some(null) => throw PutNull + case Some(x) => x + case None => null.asInstanceOf[V] + } + try Option(underlying.compute(key, remap)) + catch { + case PutNull => update(key, null.asInstanceOf[V]); Some(null.asInstanceOf[V]) + } + } + + // support Some(null) if currently bound to null + override def remove(k: K): Option[V] = { + var result: Option[V] = None + def recompute(k0: K, v0: V): V = { + if (v0 != null) result = Some(v0) + else if (underlying.containsKey(k0)) result = Some(null.asInstanceOf[V]) + null.asInstanceOf[V] + } + underlying.compute(k, recompute) + result + } + + def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] { + val ui = underlying.entrySet.iterator + def hasNext = ui.hasNext + def next() = { val e = ui.next(); (e.getKey, e.getValue) } + } + + override def foreachEntry[U](f: (K, V) => U): Unit = { + val i = underlying.entrySet().iterator() + while (i.hasNext) { + val entry = i.next() + f(entry.getKey, entry.getValue) + } + } + + override def clear() = underlying.clear() + + } + + /** Wraps a Java map as a Scala one. If the map is to support concurrent access, + * use [[JConcurrentMapWrapper]] instead. If the wrapped map is synchronized + * (e.g. from `java.util.Collections.synchronizedMap`), it is your responsibility + * to wrap all non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an + * atomic `get` when `null` values may be present. + */ + @SerialVersionUID(3L) + class JMapWrapper[K, V](val underlying : ju.Map[K, V]) + extends AbstractJMapWrapper[K, V] with Serializable { + + override def isEmpty: Boolean = underlying.isEmpty + override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize + override def empty: JMapWrapper[K, V] = new JMapWrapper(new ju.HashMap[K, V]) + } + + @SerialVersionUID(3L) + class ConcurrentMapWrapper[K, V](underlying: concurrent.Map[K, V]) extends MutableMapWrapper[K, V](underlying) with juc.ConcurrentMap[K, V] { + + def underlyingConcurrentMap: concurrent.Map[K, V] = underlying + + override def putIfAbsent(k: K, v: V) = underlying.putIfAbsent(k, v).getOrElse(null.asInstanceOf[V]) + + override def remove(k: AnyRef, v: AnyRef) = + try underlying.remove(k.asInstanceOf[K], v.asInstanceOf[V]) + catch { case ex: ClassCastException => false } + + override def replace(k: K, v: V): V = underlying.replace(k, v).getOrElse(null.asInstanceOf[V]) + + override def replace(k: K, oldval: V, newval: V) = underlying.replace(k, oldval, newval) + } + + /** Wraps a concurrent Java map as a Scala one. Single-element concurrent + * access is supported; multi-element operations such as maps and filters + * are not guaranteed to be atomic. + */ + @SerialVersionUID(3L) + class JConcurrentMapWrapper[K, V](val underlying: juc.ConcurrentMap[K, V]) + extends AbstractJMapWrapper[K, V] + with concurrent.Map[K, V] { + + override def get(k: K) = Option(underlying get k) + + override def getOrElseUpdate(key: K, op: => V): V = + underlying.computeIfAbsent(key, _ => op) match { + case null => super/*[concurrent.Map]*/.getOrElseUpdate(key, op) + case v => v + } + + override def isEmpty: Boolean = underlying.isEmpty + override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize + override def empty: JConcurrentMapWrapper[K, V] = new JConcurrentMapWrapper(new juc.ConcurrentHashMap[K, V]) + + def putIfAbsent(k: K, v: V): Option[V] = Option(underlying.putIfAbsent(k, v)) + + def remove(k: K, v: V): Boolean = underlying.remove(k, v) + + def replace(k: K, v: V): Option[V] = Option(underlying.replace(k, v)) + + def replace(k: K, oldvalue: V, newvalue: V): Boolean = underlying.replace(k, oldvalue, newvalue) + + override def lastOption: Option[(K, V)] = + underlying match { + case nav: NavigableMap[K @unchecked, V @unchecked] => Option(nav.lastEntry).map(e => (e.getKey, e.getValue)) + case _ if isEmpty => None + case _ => Try(last).toOption + } + + override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { + def remap(k: K, v: V): V = + remappingFunction(Option(v)) match { + case Some(null) => throw PutNull // see scala/scala#10129 + case Some(x) => x + case None => null.asInstanceOf[V] + } + try Option(underlying.compute(key, remap)) + catch { + case PutNull => super/*[concurrent.Map]*/.updateWith(key)(remappingFunction) + } + } + } + + @SerialVersionUID(3L) + class DictionaryWrapper[K, V](val underlying: mutable.Map[K, V]) extends ju.Dictionary[K, V] with Serializable { + def size: Int = underlying.size + def isEmpty: Boolean = underlying.isEmpty + def keys: ju.Enumeration[K] = underlying.keysIterator.asJavaEnumeration + def elements: ju.Enumeration[V] = underlying.valuesIterator.asJavaEnumeration + def get(key: AnyRef) = try { + underlying get key.asInstanceOf[K] match { + case None => null.asInstanceOf[V] + case Some(v) => v + } + } catch { + case ex: ClassCastException => null.asInstanceOf[V] + } + def put(key: K, value: V): V = underlying.put(key, value) match { + case Some(v) => v + case None => null.asInstanceOf[V] + } + override def remove(key: AnyRef) = try { + underlying remove key.asInstanceOf[K] match { + case None => null.asInstanceOf[V] + case Some(v) => v + } + } catch { + case ex: ClassCastException => null.asInstanceOf[V] + } + + override def equals(other: Any): Boolean = other match { + case that: DictionaryWrapper[_, _] => this.underlying == that.underlying + case _ => false + } + + override def hashCode: Int = underlying.hashCode() + } + + @SerialVersionUID(3L) + class JDictionaryWrapper[K, V](val underlying: ju.Dictionary[K, V]) extends mutable.AbstractMap[K, V] with Serializable { + override def size: Int = underlying.size + override def isEmpty: Boolean = underlying.isEmpty + override def knownSize: Int = if (underlying.isEmpty) 0 else super.knownSize + + def get(k: K) = Option(underlying get k) + + def addOne(kv: (K, V)): this.type = { underlying.put(kv._1, kv._2); this } + def subtractOne(key: K): this.type = { underlying remove key; this } + + override def put(k: K, v: V): Option[V] = Option(underlying.put(k, v)) + + override def update(k: K, v: V): Unit = { underlying.put(k, v) } + + override def remove(k: K): Option[V] = Option(underlying remove k) + def iterator = underlying.keys.asScala map (k => (k, underlying get k)) + + override def clear() = iterator.foreach(entry => underlying.remove(entry._1)) + + override def mapFactory: mutable.HashMap.type = mutable.HashMap + } + + @SerialVersionUID(3L) + class JPropertiesWrapper(underlying: ju.Properties) + extends mutable.AbstractMap[String, String] + with mutable.MapOps[String, String, mutable.Map, mutable.Map[String, String]] + with StrictOptimizedMapOps[String, String, mutable.Map, mutable.Map[String, String]] + with StrictOptimizedIterableOps[(String, String), mutable.Iterable, mutable.Map[String, String]] + with Serializable { + + override def size = underlying.size + override def isEmpty: Boolean = underlying.isEmpty + override def knownSize: Int = size + def get(k: String) = { + val v = underlying get k + if (v != null) Some(v.asInstanceOf[String]) else None + } + + def addOne(kv: (String, String)): this.type = { underlying.put(kv._1, kv._2); this } + def subtractOne(key: String): this.type = { underlying remove key; this } + + override def put(k: String, v: String): Option[String] = { + val r = underlying.put(k, v) + if (r != null) Some(r.asInstanceOf[String]) else None + } + + override def update(k: String, v: String): Unit = { underlying.put(k, v) } + + override def remove(k: String): Option[String] = { + val r = underlying remove k + if (r != null) Some(r.asInstanceOf[String]) else None + } + + def iterator: Iterator[(String, String)] = new AbstractIterator[(String, String)] { + val ui = underlying.entrySet.iterator + def hasNext = ui.hasNext + def next() = { + val e = ui.next() + (e.getKey.asInstanceOf[String], e.getValue.asInstanceOf[String]) + } + } + + override def clear() = underlying.clear() + + override def empty: JPropertiesWrapper = new JPropertiesWrapper(new ju.Properties) + + def getProperty(key: String) = underlying.getProperty(key) + + def getProperty(key: String, defaultValue: String) = + underlying.getProperty(key, defaultValue) + + def setProperty(key: String, value: String) = + underlying.setProperty(key, value) + + override def mapFactory: mutable.HashMap.type = mutable.HashMap + } + + /** Thrown when certain Map operations attempt to put a null value. */ + private val PutNull = new ControlThrowable {} +} diff --git a/library/src/scala/collection/convert/StreamExtensions.scala b/library/src/scala/collection/convert/StreamExtensions.scala new file mode 100644 index 000000000000..90b8bcb9031d --- /dev/null +++ b/library/src/scala/collection/convert/StreamExtensions.scala @@ -0,0 +1,480 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert + +import java.util.Spliterator +import java.util.stream._ +import java.{lang => jl} + +import scala.annotation.implicitNotFound +import scala.collection.Stepper.EfficientSplit +import scala.collection._ +import scala.collection.convert.StreamExtensions.{AccumulatorFactoryInfo, StreamShape, StreamUnboxer} +import scala.jdk.CollectionConverters._ +import scala.jdk._ + +/** Defines extension methods to create Java Streams for Scala collections, available through + * [[scala.jdk.javaapi.StreamConverters]]. + */ +trait StreamExtensions { + // collections + + implicit class IterableHasSeqStream[A](cc: IterableOnce[A]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for this collection. If the + * collection contains primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaSeqStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[A, S, St], st: StepperShape[A, St]): S = + s.fromStepper(cc.stepper, par = false) + } + + // Not `CC[X] <: IterableOnce[X]`, but `C` with an extra constraint, to support non-parametric classes like IntAccumulator + implicit class IterableNonGenericHasParStream[A, C <: IterableOnce[_]](c: C)(implicit ev: C <:< IterableOnce[A]) { + private type IterableOnceWithEfficientStepper = IterableOnce[A] { + def stepper[S <: Stepper[_]](implicit shape : StepperShape[A, S]) : S with EfficientSplit + } + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for this collection. If the + * collection contains primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaParStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[A, S, St], + st: StepperShape[A, St], + @implicitNotFound("`parStream` can only be called on collections where `stepper` returns a `Stepper with EfficientSplit`") + isEfficient: C <:< IterableOnceWithEfficientStepper): S = + s.fromStepper(ev(c).stepper, par = true) + } + + // maps + + implicit class MapHasSeqKeyValueStream[K, V, CC[X, Y] <: collection.MapOps[X, Y, collection.Map, _]](cc: CC[K, V]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the keys of this map. If + * the keys are primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaSeqKeyStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[K, S, St], st: StepperShape[K, St]): S = + s.fromStepper(cc.keyStepper, par = false) + + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the values of this map. If + * the values are primitives, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaSeqValueStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[V, S, St], st: StepperShape[V, St]): S = + s.fromStepper(cc.valueStepper, par = false) + + // The asJavaSeqStream extension method for IterableOnce doesn't apply because its `CC` takes a single type parameter, whereas the one here takes two + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the `(key, value)` pairs of + * this map. + */ + def asJavaSeqStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[(K, V), S, St], st: StepperShape[(K, V), St]): S = + s.fromStepper(cc.stepper, par = false) + } + + + implicit class MapHasParKeyValueStream[K, V, CC[X, Y] <: collection.MapOps[X, Y, collection.Map, _]](cc: CC[K, V]) { + private type MapOpsWithEfficientKeyStepper = collection.MapOps[K, V, collection.Map, _] { def keyStepper[S <: Stepper[_]](implicit shape : StepperShape[K, S]) : S with EfficientSplit } + private type MapOpsWithEfficientValueStepper = collection.MapOps[K, V, collection.Map, _] { def valueStepper[S <: Stepper[_]](implicit shape : StepperShape[V, S]) : S with EfficientSplit } + private type MapOpsWithEfficientStepper = collection.MapOps[K, V, collection.Map, _] { def stepper[S <: Stepper[_]](implicit shape : StepperShape[(K, V), S]) : S with EfficientSplit } + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the keys of this map. If + * the keys are primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaParKeyStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[K, S, St], + st: StepperShape[K, St], + @implicitNotFound("parKeyStream can only be called on maps where `keyStepper` returns a `Stepper with EfficientSplit`") + isEfficient: CC[K, V] <:< MapOpsWithEfficientKeyStepper): S = + s.fromStepper(cc.keyStepper, par = true) + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the values of this map. If + * the values are primitives, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaParValueStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[V, S, St], + st: StepperShape[V, St], + @implicitNotFound("parValueStream can only be called on maps where `valueStepper` returns a `Stepper with EfficientSplit`") + isEfficient: CC[K, V] <:< MapOpsWithEfficientValueStepper): S = + s.fromStepper(cc.valueStepper, par = true) + + // The asJavaParStream extension method for IterableOnce doesn't apply because its `CC` takes a single type parameter, whereas the one here takes two + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the `(key, value)` pairs of + * this map. + */ + def asJavaParStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit + s: StreamShape[(K, V), S, St], + st: StepperShape[(K, V), St], + @implicitNotFound("parStream can only be called on maps where `stepper` returns a `Stepper with EfficientSplit`") + isEfficient: CC[K, V] <:< MapOpsWithEfficientStepper): S = + s.fromStepper(cc.stepper, par = true) + } + + // steppers + + implicit class StepperHasSeqStream[A](stepper: Stepper[A]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for this stepper. If the + * stepper yields primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaSeqStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[A, S, St], st: StepperShape[A, St]): S = { + val sStepper = stepper match { + case as: AnyStepper[A] => st.seqUnbox(as) + case _ => stepper.asInstanceOf[St] + } + s.fromStepper(sStepper, par = false) + } + } + + implicit class StepperHasParStream[A](stepper: Stepper[A] with EfficientSplit) { + /** Create a parallel [[java.util.stream.Stream Java Stream]] for this stepper. If the + * stepper yields primitive values, a corresponding specialized Stream is returned (e.g., + * [[java.util.stream.IntStream `IntStream`]]). + */ + def asJavaParStream[S <: BaseStream[_, _], St <: Stepper[_]](implicit s: StreamShape[A, S, St], st: StepperShape[A, St]): S = { + val sStepper = stepper match { + case as: AnyStepper[A] with EfficientSplit => st.parUnbox(as) + case _ => stepper.asInstanceOf[St] + } + s.fromStepper(sStepper, par = true) + } + } + + // arrays + // uses the JDK array spliterators (`DoubleArraySpliterator`). users can also call + // `array.stepper.seqStream`, which then uses the Scala steppers (`DoubleArrayStepper`). the + // steppers are also available on byte/short/char/float arrays (`WidenedByteArrayStepper`), + // JDK spliterators only for double/int/long/reference. + + implicit class DoubleArrayHasSeqParStream(a: Array[Double]) { + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def asJavaSeqStream: DoubleStream = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def asJavaParStream: DoubleStream = asJavaSeqStream.parallel + } + + implicit class IntArrayHasSeqParStream(a: Array[Int]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaSeqStream: IntStream = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaParStream: IntStream = asJavaSeqStream.parallel + } + + implicit class LongArrayHasSeqParStream(a: Array[Long]) { + /** Create a sequential [[java.util.stream.LongStream Java LongStream]] for this array. */ + def asJavaSeqStream: LongStream = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.LongStream Java LongStream]] for this array. */ + def asJavaParStream: LongStream = asJavaSeqStream.parallel + } + + implicit class AnyArrayHasSeqParStream[A <: AnyRef](a: Array[A]) { + /** Create a sequential [[java.util.stream.Stream Java Stream]] for this array. */ + def asJavaSeqStream: Stream[A] = java.util.Arrays.stream(a) + /** Create a parallel [[java.util.stream.Stream Java Stream]] for this array. */ + def asJavaParStream: Stream[A] = asJavaSeqStream.parallel + } + + implicit class ByteArrayHasSeqParStream(a: Array[Byte]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaSeqStream: IntStream = a.stepper.asJavaSeqStream + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaParStream: IntStream = a.stepper.asJavaParStream + } + + implicit class ShortArrayHasSeqParStream(a: Array[Short]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaSeqStream: IntStream = a.stepper.asJavaSeqStream + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaParStream: IntStream = a.stepper.asJavaParStream + } + + implicit class CharArrayHasSeqParStream(a: Array[Char]) { + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaSeqStream: IntStream = a.stepper.asJavaSeqStream + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for this array. */ + def asJavaParStream: IntStream = a.stepper.asJavaParStream + } + + implicit class FloatArrayHasSeqParStream(a: Array[Float]) { + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def asJavaSeqStream: DoubleStream = a.stepper.asJavaSeqStream + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for this array. */ + def asJavaParStream: DoubleStream = a.stepper.asJavaParStream + } + + + + // strings + + implicit class StringHasSeqParStream(s: String) { + /** + * A sequential stream on the characters of a string, same as [[asJavaSeqCharStream]]. See also + * [[asJavaSeqCodePointStream]]. + */ + def asJavaSeqStream: IntStream = StreamSupport.intStream(s.stepper.spliterator, /* par = */ false) + /** + * A parallel stream on the characters of a string, same as [[asJavaParCharStream]]. See also + * [[asJavaParCodePointStream]]. + */ + def asJavaParStream: IntStream = StreamSupport.intStream(s.stepper.spliterator, /* par = */ true) + + /** A sequential stream on the characters of a string. See also [[asJavaSeqCodePointStream]]. */ + def asJavaSeqCharStream: IntStream = StreamSupport.intStream(s.charStepper.spliterator, /* par = */ false) + /** A parallel stream on the characters of a string. See also [[asJavaParCodePointStream]]. */ + def asJavaParCharStream: IntStream = StreamSupport.intStream(s.charStepper.spliterator, /* par = */ true) + + /** A sequential stream on the code points of a string. See also [[asJavaSeqCharStream]]. */ + def asJavaSeqCodePointStream: IntStream = StreamSupport.intStream(s.codePointStepper.spliterator, /* par = */ false) + /** A parallel stream on the code points of a string. See also [[asJavaParCharStream]]. */ + def asJavaParCodePointStream: IntStream = StreamSupport.intStream(s.codePointStepper.spliterator, /* par = */ true) + } + + // toScala for streams + + implicit class StreamHasToScala[A](stream: Stream[A]) { + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScala(Accumulator)` + * builds the result in parallel. + * + * A `toScala(Accumulator)` call automatically converts streams of boxed integers, longs or + * doubles are converted to the primitive accumulators ([[scala.jdk.IntAccumulator]], etc.). + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[C1](factory: collection.Factory[A, C1])(implicit info: AccumulatorFactoryInfo[A, C1]): C1 = { + + def anyAcc = stream.collect(AnyAccumulator.supplier[A], AnyAccumulator.adder[A], AnyAccumulator.merger[A]) + if (info.companion == AnyAccumulator) anyAcc.asInstanceOf[C1] + else if (info.companion == IntAccumulator) stream.asInstanceOf[Stream[Int]].collect(IntAccumulator.supplier, IntAccumulator.boxedAdder, IntAccumulator.merger).asInstanceOf[C1] + else if (info.companion == LongAccumulator) stream.asInstanceOf[Stream[Long]].collect(LongAccumulator.supplier, LongAccumulator.boxedAdder, LongAccumulator.merger).asInstanceOf[C1] + else if (info.companion == DoubleAccumulator) stream.asInstanceOf[Stream[Double]].collect(DoubleAccumulator.supplier, DoubleAccumulator.boxedAdder, DoubleAccumulator.merger).asInstanceOf[C1] + else if (stream.isParallel) anyAcc.to(factory) + else factory.fromSpecific(stream.iterator.asScala) + } + + /** Convert a generic Java Stream wrapping a primitive type to a corresponding primitive + * Stream. + */ + def asJavaPrimitiveStream[S](implicit unboxer: StreamUnboxer[A, S]): S = unboxer(stream) + } + + implicit class IntStreamHasToScala(stream: IntStream) { + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScala(Accumulator)` + * builds the result in parallel. + * + * A `toScala(Accumulator)` call automatically converts the `IntStream` to a primitive + * [[scala.jdk.IntAccumulator]]. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[C1](factory: collection.Factory[Int, C1])(implicit info: AccumulatorFactoryInfo[Int, C1]): C1 = { + def intAcc = stream.collect(IntAccumulator.supplier, IntAccumulator.adder, IntAccumulator.merger) + if (info.companion == AnyAccumulator) stream.collect(AnyAccumulator.supplier[Int], AnyAccumulator.unboxedIntAdder, AnyAccumulator.merger[Int]).asInstanceOf[C1] + else if (info.companion == IntAccumulator) intAcc.asInstanceOf[C1] + else if (stream.isParallel) intAcc.to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Int]].asScala) + } + } + + implicit class LongStreamHasToScala(stream: LongStream) { + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScala(Accumulator)` + * builds the result in parallel. + * + * A `toScala(Accumulator)` call automatically converts the `LongStream` to a primitive + * [[scala.jdk.LongAccumulator]]. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[C1](factory: collection.Factory[Long, C1])(implicit info: AccumulatorFactoryInfo[Long, C1]): C1 = { + def longAcc = stream.collect(LongAccumulator.supplier, LongAccumulator.adder, LongAccumulator.merger) + if (info.companion == AnyAccumulator) stream.collect(AnyAccumulator.supplier[Long], AnyAccumulator.unboxedLongAdder, AnyAccumulator.merger[Long]).asInstanceOf[C1] + else if (info.companion == LongAccumulator) longAcc.asInstanceOf[C1] + else if (stream.isParallel) longAcc.to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Long]].asScala) + } + } + + implicit class DoubleStreamHasToScala(stream: DoubleStream) { + /** + * Copy the elements of this stream into a Scala collection. + * + * Converting a parallel streams to an [[scala.jdk.Accumulator]] using `stream.toScala(Accumulator)` + * builds the result in parallel. + * + * A `toScala(Accumulator)` call automatically converts the `DoubleStream` to a primitive + * [[scala.jdk.DoubleAccumulator]]. + * + * When converting a parallel stream to a different Scala collection, the stream is first + * converted into an [[scala.jdk.Accumulator]], which supports parallel building. The accumulator is + * then converted to the target collection. Note that the stream is processed eagerly while + * building the accumulator, even if the target collection is lazy. + * + * Sequential streams are directly converted to the target collection. If the target collection + * is lazy, the conversion is lazy as well. + */ + def toScala[C1](factory: collection.Factory[Double, C1])(implicit info: AccumulatorFactoryInfo[Double, C1]): C1 = { + def doubleAcc = stream.collect(DoubleAccumulator.supplier, DoubleAccumulator.adder, DoubleAccumulator.merger) + if (info.companion == AnyAccumulator) stream.collect(AnyAccumulator.supplier[Double], AnyAccumulator.unboxedDoubleAdder, AnyAccumulator.merger[Double]).asInstanceOf[C1] + else if (info.companion == DoubleAccumulator) doubleAcc.asInstanceOf[C1] + else if (stream.isParallel) doubleAcc.to(factory) + else factory.fromSpecific(stream.iterator.asInstanceOf[java.util.Iterator[Double]].asScala) + } + } +} + +object StreamExtensions { + /** An implicit StreamShape instance connects element types with the corresponding specialized + * Stream and Stepper types. This is used in `asJavaStream` extension methods to create + * generic or primitive streams according to the element type. + */ + sealed trait StreamShape[T, S <: BaseStream[_, _], St <: Stepper[_]] { + final def fromStepper(st: St, par: Boolean): S = mkStream(st, par) + protected def mkStream(st: St, par: Boolean): S + } + + object StreamShape extends StreamShapeLowPriority1 { + // primitive + implicit val intStreamShape : StreamShape[Int , IntStream , IntStepper] = mkIntStreamShape[Int] + implicit val longStreamShape : StreamShape[Long , LongStream , LongStepper] = mkLongStreamShape[Long] + implicit val doubleStreamShape: StreamShape[Double, DoubleStream, DoubleStepper] = mkDoubleStreamShape[Double] + + // widening + implicit val byteStreamShape : StreamShape[Byte , IntStream , IntStepper] = mkIntStreamShape[Byte] + implicit val shortStreamShape: StreamShape[Short, IntStream , IntStepper] = mkIntStreamShape[Short] + implicit val charStreamShape : StreamShape[Char , IntStream , IntStepper] = mkIntStreamShape[Char] + implicit val floatStreamShape: StreamShape[Float, DoubleStream, DoubleStepper] = mkDoubleStreamShape[Float] + + // boxed java primitives + + implicit val jIntegerStreamShape : StreamShape[jl.Integer , IntStream , IntStepper ] = mkIntStreamShape[jl.Integer] + implicit val jLongStreamShape : StreamShape[jl.Long , LongStream , LongStepper ] = mkLongStreamShape[jl.Long] + implicit val jDoubleStreamShape : StreamShape[jl.Double , DoubleStream, DoubleStepper] = mkDoubleStreamShape[jl.Double] + implicit val jByteStreamShape : StreamShape[jl.Byte , IntStream , IntStepper ] = mkIntStreamShape[jl.Byte] + implicit val jShortStreamShape : StreamShape[jl.Short , IntStream , IntStepper ] = mkIntStreamShape[jl.Short] + implicit val jCharacterStreamShape : StreamShape[jl.Character, IntStream , IntStepper ] = mkIntStreamShape[jl.Character] + implicit val jFloatStreamShape : StreamShape[jl.Float , DoubleStream, DoubleStepper] = mkDoubleStreamShape[jl.Float] + + private def mkIntStreamShape[T]: StreamShape[T, IntStream, IntStepper] = new StreamShape[T, IntStream, IntStepper] { + protected def mkStream(st: IntStepper, par: Boolean): IntStream = StreamSupport.intStream(st.spliterator, par) + } + + private def mkLongStreamShape[T]: StreamShape[T, LongStream, LongStepper] = new StreamShape[T, LongStream, LongStepper] { + protected def mkStream(st: LongStepper, par: Boolean): LongStream = StreamSupport.longStream(st.spliterator, par) + } + + private def mkDoubleStreamShape[T]: StreamShape[T, DoubleStream, DoubleStepper] = new StreamShape[T, DoubleStream, DoubleStepper] { + protected def mkStream(st: DoubleStepper, par: Boolean): DoubleStream = StreamSupport.doubleStream(st.spliterator, par) + } + } + + trait StreamShapeLowPriority1 { + // reference + implicit def anyStreamShape[T]: StreamShape[T, Stream[T], Stepper[T]] = anyStreamShapePrototype.asInstanceOf[StreamShape[T, Stream[T], Stepper[T]]] + + private[this] val anyStreamShapePrototype: StreamShape[AnyRef, Stream[AnyRef], Stepper[AnyRef]] = new StreamShape[AnyRef, Stream[AnyRef], Stepper[AnyRef]] { + def mkStream(s: Stepper[AnyRef], par: Boolean): Stream[AnyRef] = StreamSupport.stream(s.spliterator.asInstanceOf[Spliterator[AnyRef]], par) + } + } + + /** Connects a stream element type `A` to the corresponding, potentially specialized, Stream type. + * Used in the `stream.asJavaPrimitiveStream` extension method. + */ + sealed trait StreamUnboxer[A, S] { + def apply(s: Stream[A]): S + } + object StreamUnboxer { + implicit val intStreamUnboxer: StreamUnboxer[Int, IntStream] = new StreamUnboxer[Int, IntStream] { + def apply(s: Stream[Int]): IntStream = s.mapToInt(x => x) + } + implicit val javaIntegerStreamUnboxer: StreamUnboxer[jl.Integer, IntStream] = intStreamUnboxer.asInstanceOf[StreamUnboxer[jl.Integer, IntStream]] + + implicit val longStreamUnboxer: StreamUnboxer[Long, LongStream] = new StreamUnboxer[Long, LongStream] { + def apply(s: Stream[Long]): LongStream = s.mapToLong(x => x) + } + implicit val javaLongStreamUnboxer: StreamUnboxer[jl.Long, LongStream] = longStreamUnboxer.asInstanceOf[StreamUnboxer[jl.Long, LongStream]] + + implicit val doubleStreamUnboxer: StreamUnboxer[Double, DoubleStream] = new StreamUnboxer[Double, DoubleStream] { + def apply(s: Stream[Double]): DoubleStream = s.mapToDouble(x => x) + } + implicit val javaDoubleStreamUnboxer: StreamUnboxer[jl.Double, DoubleStream] = doubleStreamUnboxer.asInstanceOf[StreamUnboxer[jl.Double, DoubleStream]] + } + + + + /** An implicit `AccumulatorFactoryInfo` connects primitive element types to the corresponding + * specialized [[scala.jdk.Accumulator]] factory. This is used in the `stream.toScala` extension methods + * to ensure collecting a primitive stream into a primitive accumulator does not box. + * + * When converting to a collection other than `Accumulator`, the generic + * `noAccumulatorFactoryInfo` is passed. + */ + trait AccumulatorFactoryInfo[A, C] { + val companion: AnyRef + } + trait LowPriorityAccumulatorFactoryInfo { + implicit def noAccumulatorFactoryInfo[A, C]: AccumulatorFactoryInfo[A, C] = noAccumulatorFactoryInfoPrototype.asInstanceOf[AccumulatorFactoryInfo[A, C]] + private val noAccumulatorFactoryInfoPrototype: AccumulatorFactoryInfo[AnyRef, AnyRef] = new AccumulatorFactoryInfo[AnyRef, AnyRef] { + val companion: AnyRef = null + } + } + object AccumulatorFactoryInfo extends LowPriorityAccumulatorFactoryInfo { + implicit def anyAccumulatorFactoryInfo[A]: AccumulatorFactoryInfo[A, AnyAccumulator[A]] = anyAccumulatorFactoryInfoPrototype.asInstanceOf[AccumulatorFactoryInfo[A, AnyAccumulator[A]]] + + private object anyAccumulatorFactoryInfoPrototype extends AccumulatorFactoryInfo[AnyRef, AnyAccumulator[AnyRef]] { + val companion: AnyRef = AnyAccumulator + } + + implicit val intAccumulatorFactoryInfo: AccumulatorFactoryInfo[Int, IntAccumulator] = new AccumulatorFactoryInfo[Int, IntAccumulator] { + val companion: AnyRef = IntAccumulator + } + + implicit val longAccumulatorFactoryInfo: AccumulatorFactoryInfo[Long, LongAccumulator] = new AccumulatorFactoryInfo[Long, LongAccumulator] { + val companion: AnyRef = LongAccumulator + } + + implicit val doubleAccumulatorFactoryInfo: AccumulatorFactoryInfo[Double, DoubleAccumulator] = new AccumulatorFactoryInfo[Double, DoubleAccumulator] { + val companion: AnyRef = DoubleAccumulator + } + + implicit val jIntegerAccumulatorFactoryInfo: AccumulatorFactoryInfo[jl.Integer, IntAccumulator] = intAccumulatorFactoryInfo.asInstanceOf[AccumulatorFactoryInfo[jl.Integer, IntAccumulator]] + implicit val jLongAccumulatorFactoryInfo: AccumulatorFactoryInfo[jl.Long, IntAccumulator] = longAccumulatorFactoryInfo.asInstanceOf[AccumulatorFactoryInfo[jl.Long, IntAccumulator]] + implicit val jDoubleAccumulatorFactoryInfo: AccumulatorFactoryInfo[jl.Double, IntAccumulator] = doubleAccumulatorFactoryInfo.asInstanceOf[AccumulatorFactoryInfo[jl.Double, IntAccumulator]] + } +} diff --git a/library/src/scala/collection/convert/impl/ArrayStepper.scala b/library/src/scala/collection/convert/impl/ArrayStepper.scala new file mode 100644 index 000000000000..4e8408bca99a --- /dev/null +++ b/library/src/scala/collection/convert/impl/ArrayStepper.scala @@ -0,0 +1,79 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection._ + +private[collection] class ObjectArrayStepper[A <: Object](underlying: Array[A], _i0: Int, _iN: Int) + extends IndexedStepperBase[AnyStepper[A], ObjectArrayStepper[A]](_i0, _iN) + with AnyStepper[A] { + def nextStep(): A = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): ObjectArrayStepper[A] = new ObjectArrayStepper[A](underlying, i0, half) +} + +private[collection] class BoxedBooleanArrayStepper(underlying: Array[Boolean], _i0: Int, _iN: Int) + extends IndexedStepperBase[AnyStepper[Boolean], BoxedBooleanArrayStepper](_i0, _iN) + with AnyStepper[Boolean] { + def nextStep(): Boolean = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): BoxedBooleanArrayStepper = new BoxedBooleanArrayStepper(underlying, i0, half) +} + +private[collection] class WidenedByteArrayStepper(underlying: Array[Byte], _i0: Int, _iN: Int) + extends IndexedStepperBase[IntStepper, WidenedByteArrayStepper](_i0, _iN) + with IntStepper { + def nextStep(): Int = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): WidenedByteArrayStepper = new WidenedByteArrayStepper(underlying, i0, half) +} + +private[collection] class WidenedCharArrayStepper(underlying: Array[Char], _i0: Int, _iN: Int) + extends IndexedStepperBase[IntStepper, WidenedCharArrayStepper](_i0, _iN) + with IntStepper { + def nextStep(): Int = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): WidenedCharArrayStepper = new WidenedCharArrayStepper(underlying, i0, half) +} + +private[collection] class WidenedShortArrayStepper(underlying: Array[Short], _i0: Int, _iN: Int) + extends IndexedStepperBase[IntStepper, WidenedShortArrayStepper](_i0, _iN) + with IntStepper { + def nextStep(): Int = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): WidenedShortArrayStepper = new WidenedShortArrayStepper(underlying, i0, half) +} + +private[collection] class WidenedFloatArrayStepper(underlying: Array[Float], _i0: Int, _iN: Int) + extends IndexedStepperBase[DoubleStepper, WidenedFloatArrayStepper](_i0, _iN) + with DoubleStepper { + def nextStep(): Double = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): WidenedFloatArrayStepper = new WidenedFloatArrayStepper(underlying, i0, half) +} + +private[collection] class DoubleArrayStepper(underlying: Array[Double], _i0: Int, _iN: Int) + extends IndexedStepperBase[DoubleStepper, DoubleArrayStepper](_i0, _iN) + with DoubleStepper { + def nextStep(): Double = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): DoubleArrayStepper = new DoubleArrayStepper(underlying, i0, half) +} + +private[collection] class IntArrayStepper(underlying: Array[Int], _i0: Int, _iN: Int) + extends IndexedStepperBase[IntStepper, IntArrayStepper](_i0, _iN) + with IntStepper { + def nextStep(): Int = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): IntArrayStepper = new IntArrayStepper(underlying, i0, half) +} + +private[collection] class LongArrayStepper(underlying: Array[Long], _i0: Int, _iN: Int) + extends IndexedStepperBase[LongStepper, LongArrayStepper](_i0, _iN) + with LongStepper { + def nextStep(): Long = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): LongArrayStepper = new LongArrayStepper(underlying, i0, half) +} diff --git a/library/src/scala/collection/convert/impl/BinaryTreeStepper.scala b/library/src/scala/collection/convert/impl/BinaryTreeStepper.scala new file mode 100644 index 000000000000..d15977eced17 --- /dev/null +++ b/library/src/scala/collection/convert/impl/BinaryTreeStepper.scala @@ -0,0 +1,248 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import java.util.Spliterator + +import annotation.tailrec +import scala.collection.Stepper.EfficientSplit +import scala.collection._ + + +private[collection] object BinaryTreeStepper { + val emptyStack = new Array[AnyRef](0) +} + + +/** A generic stepper that can traverse ordered binary trees. + * The tree is assumed to have all the stuff on the left first, then the root, then everything on the right. + * + * Splits occur at the root of whatever has not yet been traversed (the substepper steps up to but + * does not include the root). + * + * The stepper maintains an internal stack, not relying on the tree traversal to be reversible. Trees with + * nodes that maintain a parent pointer may be traversed slightly faster without a stack, but splitting is + * more awkward. + * + * Algorithmically, this class implements a simple state machine that unrolls the left-leaning links in + * a binary tree onto a stack. At all times, the machine should be in one of these states: + * 1. Empty: `myCurrent` is `null` and `index` is `-1`. `stack` should also be `Array.empty` then. + * 2. Ready: `myCurrent` is not `null` and contains the next `A` to be extracted + * 3. Pending: `myCurrent` is `null` and `stack(index)` contains the next node to visit + * + * Subclasses should allow this class to do all the work of maintaining state; `next` should simply + * reduce `maxLength` by one, and consume `myCurrent` and set it to `null` if `hasNext` is true. + */ +private[collection] abstract class BinaryTreeStepperBase[A, T >: Null <: AnyRef, Sub >: Null, Semi <: Sub with BinaryTreeStepperBase[A, T, _, _]]( + protected var maxLength: Int, protected var myCurrent: T, protected var stack: Array[AnyRef], protected var index: Int, + protected val left: T => T, protected val right: T => T +) +extends EfficientSplit { + /** Unrolls a subtree onto the stack starting from a particular node, returning + * the last node found. This final node is _not_ placed on the stack, and + * may have things to its right. + */ + @tailrec protected final def unroll(from: T): T = { + val l = left(from) + if (l eq null) from + else { + if (index+1 >= stack.length) stack = java.util.Arrays.copyOf(stack, 4 + stack.length*2) + index += 1 + stack(index) = from + unroll(l) + } + } + + /** Takes a subtree whose left side, if any, has already been visited, and unrolls + * the right side of the tree onto the stack, thereby detaching that node of + * the subtree from the stack entirely (so it is ready to use). It returns + * the node that is being detached. Note that the node must _not_ already be + * on the stack. + */ + protected final def detach(node: T): node.type = { + val r = right(node) + if (r ne null) { + val last = unroll(r) + if (index+1 >= stack.length) stack = java.util.Arrays.copyOf(stack, 4 + stack.length*2) + index += 1 + stack(index) = last + } + node + } + + /** Given an empty state and the root of a new tree, initialize the tree properly + * to be in an (appropriate) ready state. Will do all sorts of wrong stuff if the + * tree is not already empty. + * + * Right now overwrites everything so could allow reuse, but isn't used for it. + */ + private[impl] final def initialize(root: T, size: Int): Unit = + if (root eq null) { + maxLength = 0 + myCurrent = null + stack = BinaryTreeStepper.emptyStack + index = -1 + } + else { + maxLength = size + index = -1 + myCurrent = detach(unroll(root)) + } + + protected def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): Semi + + def characteristics: Int = Spliterator.ORDERED + + def estimateSize: Long = if (hasStep) maxLength else 0 + + def hasStep: Boolean = (myCurrent ne null) || (maxLength > 0 && { + if (index < 0) { maxLength = 0; stack = BinaryTreeStepper.emptyStack; false } + else { + val ans = stack(index).asInstanceOf[T] + index -= 1 + myCurrent = detach(ans) + true + } + }) + + /** Splits the tree at the root by giving everything unrolled on the stack to a new stepper, + * detaching the root, and leaving the right-hand side of the root unrolled. + * + * If the tree is empty or only has one element left, it returns `null` instead of splitting. + */ + def trySplit(): Sub = + if (!hasStep || index < 0) null + else { + val root = stack(0).asInstanceOf[T] + val leftStack = + if (index > 0) java.util.Arrays.copyOfRange(stack, 1, index+1) + else BinaryTreeStepper.emptyStack + val leftIndex = index - 1 + val leftCurrent = myCurrent + var leftMax = maxLength + index = -1 + detach(root) + myCurrent = root + leftMax -= 2+index + maxLength -= 2+leftIndex + semiclone(leftMax, leftCurrent, leftStack, leftIndex) + } +} + + +private[collection] final class AnyBinaryTreeStepper[A, T >: Null <: AnyRef]( + _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => A +) +extends BinaryTreeStepperBase[A, T, AnyStepper[A], AnyBinaryTreeStepper[A, T]](_maxLength, _myCurrent, _stack, _index, _left, _right) +with AnyStepper[A] { + def nextStep(): A = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = null + maxLength -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): AnyBinaryTreeStepper[A, T] = + new AnyBinaryTreeStepper[A, T](maxL, myC, stk, ix, left, right, extract) +} +private[collection] object AnyBinaryTreeStepper { + def from[A, T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => A): AnyBinaryTreeStepper[A, T] = { + val ans = new AnyBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) + ans.initialize(root, maxLength) + ans + } +} + + +private[collection] final class DoubleBinaryTreeStepper[T >: Null <: AnyRef]( + _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => Double +) +extends BinaryTreeStepperBase[Double, T, DoubleStepper, DoubleBinaryTreeStepper[T]](_maxLength, _myCurrent, _stack, _index, _left, _right) +with DoubleStepper { + def nextStep(): Double = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = null + maxLength -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): DoubleBinaryTreeStepper[T] = + new DoubleBinaryTreeStepper[T](maxL, myC, stk, ix, left, right, extract) +} +private [collection] object DoubleBinaryTreeStepper { + def from[T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => Double): DoubleBinaryTreeStepper[T] = { + val ans = new DoubleBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) + ans.initialize(root, maxLength) + ans + } +} + + +private[collection] final class IntBinaryTreeStepper[T >: Null <: AnyRef]( + _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => Int +) +extends BinaryTreeStepperBase[Int, T, IntStepper, IntBinaryTreeStepper[T]](_maxLength, _myCurrent, _stack, _index, _left, _right) +with IntStepper { + def nextStep(): Int = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = null + maxLength -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): IntBinaryTreeStepper[T] = + new IntBinaryTreeStepper[T](maxL, myC, stk, ix, left, right, extract) +} +private [collection] object IntBinaryTreeStepper { + def from[T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => Int): IntBinaryTreeStepper[T] = { + val ans = new IntBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) + ans.initialize(root, maxLength) + ans + } +} + + + +private[collection] final class LongBinaryTreeStepper[T >: Null <: AnyRef]( + _maxLength: Int, _myCurrent: T, _stack: Array[AnyRef], _index: Int, _left: T => T, _right: T => T, protected val extract: T => Long +) +extends BinaryTreeStepperBase[Long, T, LongStepper, LongBinaryTreeStepper[T]](_maxLength, _myCurrent, _stack, _index, _left, _right) +with LongStepper { + def nextStep(): Long = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = null + maxLength -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(maxL: Int, myC: T, stk: Array[AnyRef], ix: Int): LongBinaryTreeStepper[T] = + new LongBinaryTreeStepper[T](maxL, myC, stk, ix, left, right, extract) +} +private [collection] object LongBinaryTreeStepper { + def from[T >: Null <: AnyRef](maxLength: Int, root: T, left: T => T, right: T => T, extract: T => Long): LongBinaryTreeStepper[T] = { + val ans = new LongBinaryTreeStepper(0, null, BinaryTreeStepper.emptyStack, -1, left, right, extract) + ans.initialize(root, maxLength) + ans + } +} + + diff --git a/library/src/scala/collection/convert/impl/BitSetStepper.scala b/library/src/scala/collection/convert/impl/BitSetStepper.scala new file mode 100644 index 000000000000..905afaaf4a0d --- /dev/null +++ b/library/src/scala/collection/convert/impl/BitSetStepper.scala @@ -0,0 +1,118 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{BitSetOps, IntStepper, Stepper} + + +private[collection] final class BitSetStepper( + private var underlying: BitSetOps[_], + private var cache0: Long, private var cache1: Long, + _i0: Int, _iN: Int, + private var cacheIndex: Int +) +extends InOrderStepperBase[IntStepper, BitSetStepper](_i0, _iN) +with IntStepper { + import BitSetOps.{WordLength, LogWL} + + // When `found` is set, `i0` is an element that exists + protected var found: Boolean = false + + @annotation.tailrec + protected def findNext(): Boolean = + if (i0 >= iN) false + else { + val ix = i0 >> LogWL + if (ix == cacheIndex || ix == cacheIndex+1) { + val i = scanLong(if (ix == cacheIndex) cache0 else cache1, i0 & (WordLength - 1)) + if (i >= 0) { + i0 = (i0 & ~(WordLength - 1)) | i + found = (i0 < iN) + found + } + else { + i0 = (i0 & ~(WordLength - 1)) + WordLength + findNext() + } + } + else if (underlying eq null) { + i0 = iN + found = false + found + } + else { + cacheIndex = ix + cache0 = underlying.word(cacheIndex) + cache1 = if ((iN - 1) >> LogWL == ix) -1L else underlying.word(cacheIndex+1) + findNext() + } + } + + def semiclone(half: Int): BitSetStepper = + if (underlying == null) { + val ans = new BitSetStepper(null, cache0, cache1, i0, half, cacheIndex) + ans.found = found + i0 = half + found = false + ans + } + else { + // Set up new stepper + val ixNewN = (half - 1) >> LogWL + val ans = + new BitSetStepper(if (ixNewN <= cacheIndex + 1) null else underlying, cache0, cache1, i0, half, cacheIndex) + if (found) ans.found = true + + // Advance old stepper to breakpoint + val ixOld0 = half >> LogWL + if (ixOld0 > cacheIndex + 1) { + cache0 = underlying.word(ixOld0) + cache1 = if (((iN - 1) >> LogWL) == ixOld0) -1L else underlying.word(ixOld0+1) + cacheIndex = ixOld0 + i0 = half + found = false + } + + // Return new stepper + ans + } + + @annotation.tailrec + private[this] def scanLong(bits: Long, from: Int): Int = + if (from >= WordLength) -1 + else if ((bits & (1L << from)) != 0) from + else scanLong(bits, from + 1) + + def nextStep(): Int = + if (found || findNext()) { + found = false + val ans = i0 + i0 += 1 + ans + } + else Stepper.throwNSEE() +} + +private[collection] object BitSetStepper { + def from(bs: scala.collection.BitSetOps[_]): IntStepper with EfficientSplit = + new BitSetStepper( + if (bs.nwords <= 2) null else bs, + if (bs.nwords <= 0) -1L else bs.word(0), + if (bs.nwords <= 1) -1L else bs.word(1), + 0, + bs.nwords * BitSetOps.WordLength, + 0 + ) +} diff --git a/library/src/scala/collection/convert/impl/ChampStepper.scala b/library/src/scala/collection/convert/impl/ChampStepper.scala new file mode 100644 index 000000000000..ddf7c34dc65a --- /dev/null +++ b/library/src/scala/collection/convert/impl/ChampStepper.scala @@ -0,0 +1,245 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection.Stepper.EfficientSplit +import scala.collection._ +import scala.collection.immutable.Node + +/** A stepper that is a slightly elaborated version of the ChampBaseIterator; + * the main difference is that it knows when it should stop instead of running + * to the end of all trees. + */ +private[collection] abstract class ChampStepperBase[ + A, T <: Node[T], Sub >: Null, Semi <: Sub with ChampStepperBase[A, T, _, _] +](protected var maxSize: Int) +extends EfficientSplit { + import Node.MaxDepth + + // Much of this code is identical to ChampBaseIterator. If you change that, look here too! + + protected var currentValueCursor: Int = 0 + protected var currentValueLength: Int = 0 + protected var currentValueNode: T = _ + + private var currentStackLevel: Int = -1 + private var nodeCursorsAndLengths: Array[Int] = _ + private var nodes: Array[T] = _ + + private def initNodes(): Unit = { + if (nodeCursorsAndLengths eq null) { + nodeCursorsAndLengths = new Array[Int](MaxDepth * 2) + nodes = new Array[Node[T]](MaxDepth).asInstanceOf[Array[T]] + } + } + def initRoot(rootNode: T): Unit = { + if (rootNode.hasNodes) pushNode(rootNode) + if (rootNode.hasPayload) setupPayloadNode(rootNode) + } + + private final def setupPayloadNode(node: T): Unit = { + currentValueNode = node + currentValueCursor = 0 + currentValueLength = node.payloadArity + } + + private final def pushNode(node: T): Unit = { + initNodes() + currentStackLevel = currentStackLevel + 1 + + val cursorIndex = currentStackLevel * 2 + val lengthIndex = currentStackLevel * 2 + 1 + + nodes(currentStackLevel) = node + nodeCursorsAndLengths(cursorIndex) = 0 + nodeCursorsAndLengths(lengthIndex) = node.nodeArity + } + + private final def popNode(): Unit = { + currentStackLevel = currentStackLevel - 1 + } + + /** + * Searches for next node that contains payload values, + * and pushes encountered sub-nodes on a stack for depth-first traversal. + */ + private final def searchNextValueNode(): Boolean = { + while (currentStackLevel >= 0) { + val cursorIndex = currentStackLevel * 2 + val lengthIndex = currentStackLevel * 2 + 1 + + val nodeCursor = nodeCursorsAndLengths(cursorIndex) + val nodeLength = nodeCursorsAndLengths(lengthIndex) + + if (nodeCursor < nodeLength) { + nodeCursorsAndLengths(cursorIndex) += 1 + + val nextNode = nodes(currentStackLevel).getNode(nodeCursor) + + if (nextNode.hasNodes) { pushNode(nextNode) } + if (nextNode.hasPayload) { setupPayloadNode(nextNode) ; return true } + } else { + popNode() + } + } + false + } + + def characteristics: Int = 0 + + def estimateSize: Long = if (hasStep) maxSize else 0L + + def semiclone(): Semi + + final def hasStep: Boolean = maxSize > 0 && { + val ans = (currentValueCursor < currentValueLength) || searchNextValueNode() + if (!ans) maxSize = 0 + ans + } + + final def trySplit(): Sub = + if (!hasStep) null + else { + var fork = 0 + while (fork <= currentStackLevel && nodeCursorsAndLengths(2*fork) >= nodeCursorsAndLengths(2*fork + 1)) fork += 1 + if (fork > currentStackLevel && currentValueCursor > currentValueLength -2) null + else { + val semi = semiclone() + semi.maxSize = maxSize + semi.currentValueCursor = currentValueCursor + semi.currentValueNode = currentValueNode + if (fork > currentStackLevel) { + // Just need to finish the current node + semi.currentStackLevel = -1 + val i = (currentValueCursor + currentValueLength) >>> 1 + semi.currentValueLength = i + currentValueCursor = i + } + else { + // Need (at least some of) the full stack, so make an identical copy + semi.nodeCursorsAndLengths = java.util.Arrays.copyOf(nodeCursorsAndLengths, nodeCursorsAndLengths.length) + semi.nodes = java.util.Arrays.copyOf(nodes.asInstanceOf[Array[Node[T]]], nodes.length).asInstanceOf[Array[T]] + semi.currentStackLevel = currentStackLevel + semi.currentValueLength = currentValueLength + + // Split the top level of the stack where there's still something to split + // Could make this more efficient by duplicating code from searchNextValueNode + // instead of setting up for it to run normally. But splits tend to be rare, + // so it's not critically important. + // + // Note that this split can be kind of uneven; if we knew how many child nodes there + // were we could do better. + val i = (nodeCursorsAndLengths(2*fork) + nodeCursorsAndLengths(2*fork + 1)) >>> 1 + semi.nodeCursorsAndLengths(2*fork + 1) = i + var j = currentStackLevel + while (j > fork) { + nodeCursorsAndLengths(2*j) = nodeCursorsAndLengths(2*j + 1) + j -= 1 + } + nodeCursorsAndLengths(2*fork) = i + searchNextValueNode() + } + semi + } + } +} + + +private[collection] final class AnyChampStepper[A, T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => A) +extends ChampStepperBase[A, T, AnyStepper[A], AnyChampStepper[A, T]](_maxSize) +with AnyStepper[A] { + def nextStep(): A = + if (hasStep) { + val ans = extract(currentValueNode, currentValueCursor) + currentValueCursor += 1 + maxSize -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(): AnyChampStepper[A, T] = new AnyChampStepper[A, T](0, extract) +} +private[collection] object AnyChampStepper { + def from[A, T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => A): AnyChampStepper[A, T] = { + val ans = new AnyChampStepper[A, T](maxSize, extract) + ans.initRoot(root) + ans + } +} + +private[collection] final class DoubleChampStepper[T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Double) +extends ChampStepperBase[Double, T, DoubleStepper, DoubleChampStepper[T]](_maxSize) +with DoubleStepper { + def nextStep(): Double = + if (hasStep) { + val ans = extract(currentValueNode, currentValueCursor) + currentValueCursor += 1 + maxSize -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(): DoubleChampStepper[T] = new DoubleChampStepper[T](0, extract) +} +private[collection] object DoubleChampStepper { + def from[T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Double): DoubleChampStepper[T] = { + val ans = new DoubleChampStepper[T](maxSize, extract) + ans.initRoot(root) + ans + } +} + +private[collection] final class IntChampStepper[T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Int) +extends ChampStepperBase[Int, T, IntStepper, IntChampStepper[T]](_maxSize) +with IntStepper { + def nextStep(): Int = + if (hasStep) { + val ans = extract(currentValueNode, currentValueCursor) + currentValueCursor += 1 + maxSize -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(): IntChampStepper[T] = new IntChampStepper[T](0, extract) +} +private[collection] object IntChampStepper { + def from[T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Int): IntChampStepper[T] = { + val ans = new IntChampStepper[T](maxSize, extract) + ans.initRoot(root) + ans + } +} + +private[collection] final class LongChampStepper[T >: Null <: Node[T]](_maxSize: Int, protected val extract: (T, Int) => Long) +extends ChampStepperBase[Long, T, LongStepper, LongChampStepper[T]](_maxSize) +with LongStepper { + def nextStep(): Long = + if (hasStep) { + val ans = extract(currentValueNode, currentValueCursor) + currentValueCursor += 1 + maxSize -= 1 + ans + } + else Stepper.throwNSEE() + + def semiclone(): LongChampStepper[T] = new LongChampStepper[T](0, extract) +} +private[collection] object LongChampStepper { + def from[T >: Null <: Node[T]](maxSize: Int, root: T, extract: (T, Int) => Long): LongChampStepper[T] = { + val ans = new LongChampStepper[T](maxSize, extract) + ans.initRoot(root) + ans + } +} diff --git a/library/src/scala/collection/convert/impl/InOrderStepperBase.scala b/library/src/scala/collection/convert/impl/InOrderStepperBase.scala new file mode 100644 index 000000000000..476b5c882177 --- /dev/null +++ b/library/src/scala/collection/convert/impl/InOrderStepperBase.scala @@ -0,0 +1,53 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import java.util.Spliterator + +import scala.collection.Stepper.EfficientSplit + +/** Abstracts all the generic operations of stepping over a collection + * that has an indexable ordering but may have gaps. + * + * For collections that are guaranteed to not have gaps, use `IndexedStepperBase` instead. + */ +private[convert] abstract class InOrderStepperBase[Sub >: Null, Semi <: Sub](protected var i0: Int, protected var iN: Int) +extends EfficientSplit { + /** Set `true` if the element at `i0` is known to be there. `false` if either not known or is a gap. + */ + protected def found: Boolean + + /** Advance `i0` over any gaps, updating internal state so `found` is correct at the new position. + * Returns the new value of `found`. + */ + protected def findNext(): Boolean + + protected def semiclone(half: Int): Semi + + final def hasStep: Boolean = found || findNext() + + def characteristics: Int = Spliterator.ORDERED + + def estimateSize: Long = iN - i0 + + def trySplit(): Sub = { + if (iN-1 > i0) { + val half = (i0 + iN) >>> 1 + val ans = semiclone(half) + i0 = half + ans + } + else null + } +} diff --git a/library/src/scala/collection/convert/impl/IndexedSeqStepper.scala b/library/src/scala/collection/convert/impl/IndexedSeqStepper.scala new file mode 100644 index 000000000000..aa8fbe307278 --- /dev/null +++ b/library/src/scala/collection/convert/impl/IndexedSeqStepper.scala @@ -0,0 +1,44 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection._ + +private[collection] class AnyIndexedSeqStepper[A](underlying: collection.IndexedSeqOps[A, AnyConstr, _], _i0: Int, _iN: Int) + extends IndexedStepperBase[AnyStepper[A], AnyIndexedSeqStepper[A]](_i0, _iN) + with AnyStepper[A] { + def nextStep(): A = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): AnyIndexedSeqStepper[A] = new AnyIndexedSeqStepper[A](underlying, i0, half) +} + +private[collection] class DoubleIndexedSeqStepper[CC <: collection.IndexedSeqOps[Double, AnyConstr, _]](underlying: CC, _i0: Int, _iN: Int) + extends IndexedStepperBase[DoubleStepper, DoubleIndexedSeqStepper[CC]](_i0, _iN) + with DoubleStepper { + def nextStep(): Double = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): DoubleIndexedSeqStepper[CC] = new DoubleIndexedSeqStepper[CC](underlying, i0, half) +} + +private[collection] class IntIndexedSeqStepper[CC <: collection.IndexedSeqOps[Int, AnyConstr, _]](underlying: CC, _i0: Int, _iN: Int) + extends IndexedStepperBase[IntStepper, IntIndexedSeqStepper[CC]](_i0, _iN) + with IntStepper { + def nextStep(): Int = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): IntIndexedSeqStepper[CC] = new IntIndexedSeqStepper[CC](underlying, i0, half) +} + +private[collection] class LongIndexedSeqStepper[CC <: collection.IndexedSeqOps[Long, AnyConstr, _]](underlying: CC, _i0: Int, _iN: Int) + extends IndexedStepperBase[LongStepper, LongIndexedSeqStepper[CC]](_i0, _iN) + with LongStepper { + def nextStep(): Long = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + protected def semiclone(half: Int): LongIndexedSeqStepper[CC] = new LongIndexedSeqStepper[CC](underlying, i0, half) +} diff --git a/library/src/scala/collection/convert/impl/IndexedStepperBase.scala b/library/src/scala/collection/convert/impl/IndexedStepperBase.scala new file mode 100644 index 000000000000..d2094dd30da6 --- /dev/null +++ b/library/src/scala/collection/convert/impl/IndexedStepperBase.scala @@ -0,0 +1,40 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import java.util.Spliterator + +import scala.collection.Stepper.EfficientSplit + +/** Abstracts all the generic operations of stepping over an indexable collection */ +private[convert] abstract class IndexedStepperBase[Sub >: Null, Semi <: Sub](protected var i0: Int, protected var iN: Int) + extends EfficientSplit { + protected def semiclone(half: Int): Semi + + def hasStep: Boolean = i0 < iN + + def characteristics: Int = Spliterator.ORDERED + Spliterator.SIZED + Spliterator.SUBSIZED + + def estimateSize: Long = iN - i0 + + def trySplit(): Sub = { + if (iN-1 > i0) { + val half = (i0+iN) >>> 1 + val ans = semiclone(half) + i0 = half + ans + } + else null + } +} diff --git a/library/src/scala/collection/convert/impl/IteratorStepper.scala b/library/src/scala/collection/convert/impl/IteratorStepper.scala new file mode 100644 index 000000000000..8fac29cf96ae --- /dev/null +++ b/library/src/scala/collection/convert/impl/IteratorStepper.scala @@ -0,0 +1,129 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import java.util.Spliterator + +import scala.collection.{AnyStepper, DoubleStepper, IntStepper, LongStepper, Stepper} +import scala.jdk.{AnyAccumulator, DoubleAccumulator, IntAccumulator, LongAccumulator} + +private[collection] class AnyIteratorStepper[A](_underlying: Iterator[A]) + extends IteratorStepperBase[A, AnyStepper[A], AnyIteratorStepper[A]](_underlying) + with AnyStepper[A] { + protected def semiclone(): AnyIteratorStepper[A] = new AnyIteratorStepper(null) + + def nextStep(): A = if (proxied ne null) proxied.nextStep() else underlying.next() + + def trySplit(): AnyStepper[A] = if (proxied ne null) proxied.trySplit() else { + val acc = new AnyAccumulator[A] + var i = 0 + val n = nextChunkSize & 0xFFFFFFFC + while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } + if (i < n || !underlying.hasNext) { + proxied = acc.stepper + proxied.trySplit() + } + else { + val ans = semiclone() + ans.proxied = acc.stepper + nextChunkSize = if ((nextChunkSize&3) == 3) { if (n < 0x40000000) n*2 else n } else nextChunkSize + 1 + ans + } + } +} + +private[collection] class DoubleIteratorStepper(_underlying: Iterator[Double]) + extends IteratorStepperBase[Double, DoubleStepper, DoubleIteratorStepper](_underlying) + with DoubleStepper { + protected def semiclone(): DoubleIteratorStepper = new DoubleIteratorStepper(null) + + def nextStep(): Double = if (proxied ne null) proxied.nextStep() else underlying.next() + + def trySplit(): DoubleStepper = if (proxied ne null) proxied.trySplit() else { + val acc = new DoubleAccumulator + var i = 0 + val n = nextChunkSize & 0xFFFFFFFC + while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } + if (i < n || !underlying.hasNext) { + proxied = acc.stepper + proxied.trySplit() + } + else { + val ans = semiclone() + ans.proxied = acc.stepper + nextChunkSize = if ((nextChunkSize&3) == 3) { if (n < 0x40000000) n*2 else n } else nextChunkSize + 1 + ans + } + } +} + +private[collection] class IntIteratorStepper(_underlying: Iterator[Int]) + extends IteratorStepperBase[Int, IntStepper, IntIteratorStepper](_underlying) + with IntStepper { + protected def semiclone(): IntIteratorStepper = new IntIteratorStepper(null) + + def nextStep(): Int = if (proxied ne null) proxied.nextStep() else underlying.next() + + def trySplit(): IntStepper = if (proxied ne null) proxied.trySplit() else { + val acc = new IntAccumulator + var i = 0 + val n = nextChunkSize & 0xFFFFFFFC + while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } + if (i < n || !underlying.hasNext) { + proxied = acc.stepper + proxied.trySplit() + } + else { + val ans = semiclone() + ans.proxied = acc.stepper + nextChunkSize = if ((nextChunkSize&3) == 3) { if (n < 0x40000000) n*2 else n } else nextChunkSize + 1 + ans + } + } +} + +private[collection] class LongIteratorStepper(_underlying: Iterator[Long]) + extends IteratorStepperBase[Long, LongStepper, LongIteratorStepper](_underlying) + with LongStepper { + protected def semiclone(): LongIteratorStepper = new LongIteratorStepper(null) + + def nextStep(): Long = if (proxied ne null) proxied.nextStep() else underlying.next() + + def trySplit(): LongStepper = if (proxied ne null) proxied.trySplit() else { + val acc = new LongAccumulator + var i = 0 + val n = nextChunkSize & 0xFFFFFFFC + while (i < n && underlying.hasNext) { acc += underlying.next(); i += 1 } + if (i < n || !underlying.hasNext) { + proxied = acc.stepper + proxied.trySplit() + } + else { + val ans = semiclone() + ans.proxied = acc.stepper + nextChunkSize = if ((nextChunkSize&3) == 3) { if (n < 0x40000000) n*2 else n } else nextChunkSize + 1 + ans + } + } +} + +/** Common functionality for Steppers that step through an Iterator, caching the results as needed when a split is requested. */ +private[convert] abstract class IteratorStepperBase[A, SP >: Null <: Stepper[A], Semi <: SP](final protected val underlying: Iterator[A]) { + final protected var nextChunkSize = 16 + final protected var proxied: SP = null + protected def semiclone(): Semi // Must initialize with null iterator! + def characteristics: Int = if (proxied ne null) Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED else Spliterator.ORDERED + def estimateSize: Long = if (proxied ne null) proxied.estimateSize else Long.MaxValue + def hasStep: Boolean = if (proxied ne null) proxied.hasStep else underlying.hasNext +} diff --git a/library/src/scala/collection/convert/impl/NumericRangeStepper.scala b/library/src/scala/collection/convert/impl/NumericRangeStepper.scala new file mode 100644 index 000000000000..3a03f8fabf63 --- /dev/null +++ b/library/src/scala/collection/convert/impl/NumericRangeStepper.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection.{AnyStepper, IntStepper, LongStepper, Stepper} +import scala.collection.immutable.NumericRange + +private[collection] class AnyNumericRangeStepper[A](underlying: NumericRange[A], _i0: Int, _iN: Int) +extends IndexedStepperBase[AnyStepper[A], AnyNumericRangeStepper[A]](_i0, _iN) +with AnyStepper[A] { + def nextStep(): A = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + def semiclone(half: Int) = new AnyNumericRangeStepper[A](underlying, i0, half) +} + +private[collection] class IntNumericRangeStepper(underlying: NumericRange[Int], _i0: Int, _iN: Int) +extends IndexedStepperBase[IntStepper, IntNumericRangeStepper](_i0, _iN) +with IntStepper { + def nextStep(): Int = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + def semiclone(half: Int) = new IntNumericRangeStepper(underlying, i0, half) +} + +private[collection] class LongNumericRangeStepper(underlying: NumericRange[Long], _i0: Int, _iN: Int) +extends IndexedStepperBase[LongStepper, LongNumericRangeStepper](_i0, _iN) +with LongStepper { + def nextStep(): Long = if (hasStep) { val j = i0; i0 += 1; underlying(j) } else Stepper.throwNSEE() + def semiclone(half: Int) = new LongNumericRangeStepper(underlying, i0, half) +} diff --git a/library/src/scala/collection/convert/impl/RangeStepper.scala b/library/src/scala/collection/convert/impl/RangeStepper.scala new file mode 100644 index 000000000000..46f803151704 --- /dev/null +++ b/library/src/scala/collection/convert/impl/RangeStepper.scala @@ -0,0 +1,40 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection.{IntStepper, Stepper} + +/** Implements Stepper on an integer Range. You don't actually need the Range to do this, + * so only the relevant parts are included. Because the arguments are protected, they are + * not error-checked; `Range` is required to provide valid arguments. + */ +private[collection] final class RangeStepper(protected var myNext: Int, myStep: Int, _i0: Int, _iN: Int) +extends IndexedStepperBase[IntStepper, RangeStepper](_i0, _iN) +with IntStepper { + def nextStep(): Int = + if (hasStep) { + val ans = myNext + myNext += myStep + i0 += 1 + ans + } + else Stepper.throwNSEE() + protected def semiclone(half: Int): RangeStepper = new RangeStepper(myNext, myStep, i0, half) + override def trySplit(): IntStepper = { + val old_i0 = i0 + val ans = super.trySplit() + myNext += (i0 - old_i0) * myStep + ans + } +} diff --git a/library/src/scala/collection/convert/impl/StringStepper.scala b/library/src/scala/collection/convert/impl/StringStepper.scala new file mode 100644 index 000000000000..e8c4d7073c43 --- /dev/null +++ b/library/src/scala/collection/convert/impl/StringStepper.scala @@ -0,0 +1,58 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import java.lang.Character.{charCount, isLowSurrogate} +import java.util.Spliterator + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{IntStepper, Stepper} + +/** Implements `Stepper` on a `String` where you step through chars packed into `Int`. + */ +private[collection] final class CharStringStepper(underlying: String, _i0: Int, _iN: Int) +extends IndexedStepperBase[IntStepper, CharStringStepper](_i0, _iN) +with IntStepper { + def nextStep(): Int = + if (hasStep) { val j = i0; i0 += 1; underlying.charAt(j) } + else Stepper.throwNSEE() + + def semiclone(half: Int): CharStringStepper = new CharStringStepper(underlying, i0, half) +} + +/** Implements `Stepper` on a `String` where you step through code points. + */ +private[collection] final class CodePointStringStepper(underlying: String, private var i0: Int, private var iN: Int) +extends IntStepper with EfficientSplit { + def characteristics: Int = Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED + def estimateSize: Long = iN - i0 + def hasStep: Boolean = i0 < iN + def nextStep(): Int = { + if (hasStep) { + val cp = underlying.codePointAt(i0) + i0 += charCount(cp) + cp + } + else Stepper.throwNSEE() + } + def trySplit(): CodePointStringStepper = + if (iN - 3 > i0) { + var half = (i0 + iN) >>> 1 + if (isLowSurrogate(underlying.charAt(half))) half -= 1 + val ans = new CodePointStringStepper(underlying, i0, half) + i0 = half + ans + } + else null +} diff --git a/library/src/scala/collection/convert/impl/TableStepper.scala b/library/src/scala/collection/convert/impl/TableStepper.scala new file mode 100644 index 000000000000..2c144e4fae8f --- /dev/null +++ b/library/src/scala/collection/convert/impl/TableStepper.scala @@ -0,0 +1,138 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection.Stepper.EfficientSplit +import scala.collection._ + +private[collection] abstract class TableStepperBase[A, I >: Null <: AnyRef, Sub >: Null, Semi <: Sub with TableStepperBase[A, I, _, _]]( + protected var maxLength: Int, protected val table: Array[I], protected var i0: Int, protected val iN: Int +) +extends EfficientSplit { + // Always holds table(i0); if `null` it is time to switch to the next element + protected var myCurrent: I = if (i0 < iN) table(i0) else null + + // Only call this when `myCurrent` is null (meaning we need to advance) + @annotation.tailrec + protected final def findNextCurrent(): Boolean = + if (i0 < iN) { + i0 += 1 + if (i0 >= iN) false + else { + myCurrent = table(i0) + if (myCurrent eq null) findNextCurrent() + else true + } + } + else false + + protected def semiclone(half: Int): Semi + + def characteristics: Int = 0 + + def estimateSize: Long = if (!hasStep) { maxLength = 0; 0 } else maxLength + + def hasStep: Boolean = (myCurrent ne null) || findNextCurrent() + + def trySplit(): Sub = { + if (iN-1 > i0 && maxLength > 0) { + val half = (i0 + iN) >>> 1 + val ans = semiclone(half) + ans.myCurrent = myCurrent + myCurrent = table(half) + var inLeft = if (ans.myCurrent ne null) 1 else 0 + var inRight = if (myCurrent ne null) 1 else 0 + if (iN - i0 < 32) { + var i = i0+1 + while (i < half && (table(i) ne null)) { i += 1; inLeft += 1 } + i = half+1 + while (i < iN && (table(i) ne null)) { i += 1; inRight += 1 } + } + maxLength -= inLeft + ans.maxLength -= inRight + i0 = half + ans + } + else null + } +} + + +private[collection] final class AnyTableStepper[A, I >: Null <: AnyRef]( + _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => A, _i0: Int, _iN: Int +) +extends TableStepperBase[A, I, AnyStepper[A], AnyTableStepper[A, I]](_maxLength, _table, _i0, _iN) +with AnyStepper[A] { + def nextStep(): A = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = iterate(myCurrent) + ans + } + else Stepper.throwNSEE() + + def semiclone(half: Int): AnyTableStepper[A, I] = new AnyTableStepper[A, I](maxLength, table, iterate, extract, i0, half) +} + + +private[collection] final class DoubleTableStepper[I >: Null <: AnyRef]( + _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => Double, _i0: Int, _iN: Int +) +extends TableStepperBase[Double, I, DoubleStepper, DoubleTableStepper[I]](_maxLength, _table, _i0, _iN) +with DoubleStepper { + def nextStep(): Double = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = iterate(myCurrent) + ans + } + else Stepper.throwNSEE() + + def semiclone(half: Int): DoubleTableStepper[I] = new DoubleTableStepper[I](maxLength, table, iterate, extract, i0, half) +} + + +private[collection] final class IntTableStepper[I >: Null <: AnyRef]( + _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => Int, _i0: Int, _iN: Int +) +extends TableStepperBase[Int, I, IntStepper, IntTableStepper[I]](_maxLength, _table, _i0, _iN) +with IntStepper { + def nextStep(): Int = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = iterate(myCurrent) + ans + } + else Stepper.throwNSEE() + + def semiclone(half: Int): IntTableStepper[I] = new IntTableStepper[I](maxLength, table, iterate, extract, i0, half) +} + + +private[collection] final class LongTableStepper[I >: Null <: AnyRef]( + _maxLength: Int, _table: Array[I], iterate: I => I, extract: I => Long, _i0: Int, _iN: Int +) +extends TableStepperBase[Long, I, LongStepper, LongTableStepper[I]](_maxLength, _table, _i0, _iN) +with LongStepper { + def nextStep(): Long = + if (hasStep) { + val ans = extract(myCurrent) + myCurrent = iterate(myCurrent) + ans + } + else Stepper.throwNSEE() + + def semiclone(half: Int): LongTableStepper[I] = new LongTableStepper[I](maxLength, table, iterate, extract, i0, half) +} + diff --git a/library/src/scala/collection/convert/impl/VectorStepper.scala b/library/src/scala/collection/convert/impl/VectorStepper.scala new file mode 100644 index 000000000000..ca0d45330a70 --- /dev/null +++ b/library/src/scala/collection/convert/impl/VectorStepper.scala @@ -0,0 +1,131 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.convert +package impl + +import scala.collection._ + +private[convert] abstract class VectorStepperBase[Sub >: Null, Semi <: Sub]( + _i0: Int, + _iN: Int, + protected val displayN: Int, + protected val trunk: Array[AnyRef] +) +extends IndexedStepperBase[Sub, Semi](_i0, _iN) { + protected var index: Int = 32 // Force an advanceData on the first element + protected var leaves: Array[AnyRef] = null + protected var index1: Int = 32 // Force advanceData to defer to initTo on the first element + protected var twigs: Array[AnyRef] = null + + protected final def advanceData(iX: Int): Unit = { + index1 += 1 + if (index1 >= 32) initTo(iX) + else { + leaves = twigs(index1).asInstanceOf[Array[AnyRef]] + index = 0 + } + } + protected final def initTo(iX: Int): Unit = displayN match { + case 0 => + leaves = trunk + index = iX + case 1 => + twigs = trunk + index1 = iX >>> 5 + leaves = twigs(index1).asInstanceOf[Array[AnyRef]] + index = iX & 0x1F + case _ => + var n = displayN + var dataN = trunk + while (n > 2) { + dataN = dataN((iX >> (5*n)) & 0x1F).asInstanceOf[Array[AnyRef]] + n -= 1 + } + twigs = dataN((iX >>> 10) & 0x1F).asInstanceOf[Array[AnyRef]] + index1 = (iX >> 5) & 0x1F + leaves = twigs(index1).asInstanceOf[Array[AnyRef]] + index = iX & 0x1F + } +} + +private[collection] class AnyVectorStepper[A](_i0: Int, _iN: Int, _displayN: Int, _trunk: Array[AnyRef]) +extends VectorStepperBase[AnyStepper[A], AnyVectorStepper[A]](_i0, _iN, _displayN, _trunk) +with AnyStepper[A] { + def nextStep(): A = if (hasStep) { + index += 1 + if (index >= 32) advanceData(i0) + i0 += 1 + leaves(index).asInstanceOf[A] + } else Stepper.throwNSEE() + def semiclone(half: Int): AnyVectorStepper[A] = { + val ans = new AnyVectorStepper[A](i0, half, displayN, trunk) + index = 32 + index1 = 32 + i0 = half + ans + } +} + +private[collection] class DoubleVectorStepper(_i0: Int, _iN: Int, _displayN: Int, _trunk: Array[AnyRef]) +extends VectorStepperBase[DoubleStepper, DoubleVectorStepper](_i0, _iN, _displayN, _trunk) +with DoubleStepper { + def nextStep(): Double = if (hasStep) { + index += 1 + if (index >= 32) advanceData(i0) + i0 += 1 + leaves(index).asInstanceOf[Double] + } else Stepper.throwNSEE() + def semiclone(half: Int): DoubleVectorStepper = { + val ans = new DoubleVectorStepper(i0, half, displayN, trunk) + index = 32 + index1 = 32 + i0 = half + ans + } +} + +private[collection] class IntVectorStepper(_i0: Int, _iN: Int, _displayN: Int, _trunk: Array[AnyRef]) +extends VectorStepperBase[IntStepper, IntVectorStepper](_i0, _iN, _displayN, _trunk) +with IntStepper { + def nextStep(): Int = if (hasStep) { + index += 1 + if (index >= 32) advanceData(i0) + i0 += 1 + leaves(index).asInstanceOf[Int] + } else Stepper.throwNSEE() + def semiclone(half: Int): IntVectorStepper = { + val ans = new IntVectorStepper(i0, half, displayN, trunk) + index = 32 + index1 = 32 + i0 = half + ans + } +} + +private[collection] class LongVectorStepper(_i0: Int, _iN: Int, _displayN: Int, _trunk: Array[AnyRef]) +extends VectorStepperBase[LongStepper, LongVectorStepper](_i0, _iN, _displayN, _trunk) +with LongStepper { + def nextStep(): Long = if (hasStep) { + index += 1 + if (index >= 32) advanceData(i0) + i0 += 1 + leaves(index).asInstanceOf[Long] + } else Stepper.throwNSEE() + def semiclone(half: Int): LongVectorStepper = { + val ans = new LongVectorStepper(i0, half, displayN, trunk) + index = 32 + index1 = 32 + i0 = half + ans + } +} diff --git a/library/src/scala/collection/generic/BitOperations.scala b/library/src/scala/collection/generic/BitOperations.scala new file mode 100644 index 000000000000..4464b4935d07 --- /dev/null +++ b/library/src/scala/collection/generic/BitOperations.scala @@ -0,0 +1,50 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package generic + + +/** Some bit operations. + * + * See [[https://www.drmaciver.com/2008/08/unsigned-comparison-in-javascala/]] for + * an explanation of unsignedCompare. + */ +private[collection] object BitOperations { + trait Int { + type Int = scala.Int + def zero(i: Int, mask: Int) = (i & mask) == 0 + def mask(i: Int, mask: Int) = i & (complement(mask - 1) ^ mask) + def hasMatch(key: Int, prefix: Int, m: Int) = mask(key, m) == prefix + def unsignedCompare(i: Int, j: Int) = (i < j) ^ (i < 0) ^ (j < 0) + def shorter(m1: Int, m2: Int) = unsignedCompare(m2, m1) + def complement(i: Int) = (-1) ^ i + def bits(num: Int) = 31 to 0 by -1 map (i => (num >>> i & 1) != 0) + def bitString(num: Int, sep: String = "") = bits(num) map (b => if (b) "1" else "0") mkString sep + def highestOneBit(j: Int) = java.lang.Integer.highestOneBit(j) + } + object Int extends Int + + trait Long { + type Long = scala.Long + def zero(i: Long, mask: Long) = (i & mask) == 0L + def mask(i: Long, mask: Long) = i & (complement(mask - 1) ^ mask) + def hasMatch(key: Long, prefix: Long, m: Long) = mask(key, m) == prefix + def unsignedCompare(i: Long, j: Long) = (i < j) ^ (i < 0L) ^ (j < 0L) + def shorter(m1: Long, m2: Long) = unsignedCompare(m2, m1) + def complement(i: Long) = (-1L) ^ i + def bits(num: Long) = 63L to 0L by -1L map (i => (num >>> i & 1L) != 0L) + def bitString(num: Long, sep: String = "") = bits(num) map (b => if (b) "1" else "0") mkString sep + def highestOneBit(j: Long) = java.lang.Long.highestOneBit(j) + } + object Long extends Long +} diff --git a/library/src/scala/collection/generic/CommonErrors.scala b/library/src/scala/collection/generic/CommonErrors.scala new file mode 100644 index 000000000000..e9f863643d27 --- /dev/null +++ b/library/src/scala/collection/generic/CommonErrors.scala @@ -0,0 +1,29 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package generic + + +/** Some precomputed common errors to reduce the generated code size. + */ +private[collection] object CommonErrors { + /** IndexOutOfBounds exception with a known max index */ + @noinline + def indexOutOfBounds(index: Int, max: Int): IndexOutOfBoundsException = + new IndexOutOfBoundsException(s"$index is out of bounds (min 0, max ${max})") + + /** IndexOutOfBounds exception with an unknown max index. */ + @noinline + def indexOutOfBounds(index: Int): IndexOutOfBoundsException = + new IndexOutOfBoundsException(s"$index is out of bounds (min 0, max unknown)") +} diff --git a/library/src/scala/collection/generic/DefaultSerializationProxy.scala b/library/src/scala/collection/generic/DefaultSerializationProxy.scala new file mode 100644 index 000000000000..2e584eaa427d --- /dev/null +++ b/library/src/scala/collection/generic/DefaultSerializationProxy.scala @@ -0,0 +1,87 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.generic + +import java.io.{ObjectInputStream, ObjectOutputStream} + +import scala.collection.{Factory, Iterable} +import scala.collection.mutable.Builder + +/** The default serialization proxy for collection implementations. + * + * This class is `final` and requires an extra `Factory` object rather than leaving the details of creating a `Builder` + * to an abstract method that could be implemented by a subclass. This is necessary because the factory is needed + * for deserializing this class's private state, which happens before any subclass fields would be deserialized. Any + * additional state required to create the proper `Builder` needs to be captured by the `factory`. + */ +@SerialVersionUID(3L) +final class DefaultSerializationProxy[A](factory: Factory[A, Any], @transient private[this] val coll: Iterable[A]) extends Serializable { + + @transient protected var builder: Builder[A, Any] = _ + + private[this] def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + val k = coll.knownSize + out.writeInt(k) + var count = 0 + coll.foreach { x => + out.writeObject(x) + count += 1 + } + if(k >= 0) { + if(count != k) throw new IllegalStateException(s"Illegal size $count of collection, expected $k") + } else out.writeObject(SerializeEnd) + } + + private[this] def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + builder = factory.newBuilder + val k = in.readInt() + if(k >= 0) { + builder.sizeHint(k) + var count = 0 + while(count < k) { + builder += in.readObject().asInstanceOf[A] + count += 1 + } + } else { + while (true) in.readObject match { + case SerializeEnd => return + case a => builder += a.asInstanceOf[A] + } + } + } + + protected[this] def readResolve(): Any = builder.result() +} + +@SerialVersionUID(3L) +private[collection] case object SerializeEnd + +/** Mix-in trait to enable DefaultSerializationProxy for the standard collection types. Depending on the type + * it is mixed into, it will dynamically choose `iterableFactory`, `mapFactory`, `sortedIterableFactory` or + * `sortedMapFactory` for deserialization into the respective `CC` type. Override `writeReplace` or implement + * it directly without using this trait if you need a non-standard factory or if you want to use a different + * serialization scheme. + */ +trait DefaultSerializable extends Serializable { this: scala.collection.Iterable[_] => + protected[this] def writeReplace(): AnyRef = { + val f: Factory[Any, Any] = this match { + case it: scala.collection.SortedMap[_, _] => it.sortedMapFactory.sortedMapFactory[Any, Any](using it.ordering.asInstanceOf[Ordering[Any]]).asInstanceOf[Factory[Any, Any]] + case it: scala.collection.Map[_, _] => it.mapFactory.mapFactory[Any, Any].asInstanceOf[Factory[Any, Any]] + case it: scala.collection.SortedSet[_] => it.sortedIterableFactory.evidenceIterableFactory[Any](using it.ordering.asInstanceOf[Ordering[Any]]) + case it => it.iterableFactory.iterableFactory + } + new DefaultSerializationProxy(f, this) + } +} diff --git a/library/src/scala/collection/generic/IsIterable.scala b/library/src/scala/collection/generic/IsIterable.scala new file mode 100644 index 000000000000..2260f0f2aacb --- /dev/null +++ b/library/src/scala/collection/generic/IsIterable.scala @@ -0,0 +1,164 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package generic + +/** A trait which can be used to avoid code duplication when defining extension + * methods that should be applicable both to existing Scala collections (i.e., + * types extending `Iterable`) as well as other (potentially user-defined) + * types that could be converted to a Scala collection type. This trait + * makes it possible to treat Scala collections and types that can be implicitly + * converted to a collection type uniformly. For example, one can provide + * extension methods that work both on collection types and on `String`s (`String`s + * do not extend `Iterable`, but can be converted to `Iterable`) + * + * `IsIterable` provides three members: + * + * 1. type member `A`, which represents the element type of the target `Iterable[A]` + * 1. type member `C`, which represents the type returned by transformation operations that preserve the collection’s elements type + * 1. method `apply`, which provides a way to convert between the type we wish to add extension methods to, `Repr`, and `IterableOps[A, Iterable, C]`. + * + * ===Usage=== + * + * One must provide `IsIterable` as an implicit parameter type of an implicit + * conversion. Its usage is shown below. Our objective in the following example + * is to provide a generic extension method `mapReduce` to any type that extends + * or can be converted to `Iterable`. In our example, this includes + * `String`. + * + * {{{ + * import scala.collection.{Iterable, IterableOps} + * import scala.collection.generic.IsIterable + * + * class ExtensionMethods[Repr, I <: IsIterable[Repr]](coll: Repr, it: I) { + * def mapReduce[B](mapper: it.A => B)(reducer: (B, B) => B): B = { + * val iter = it(coll).iterator + * var res = mapper(iter.next()) + * while (iter.hasNext) + * res = reducer(res, mapper(iter.next())) + * res + * } + * } + * + * implicit def withExtensions[Repr](coll: Repr)(implicit it: IsIterable[Repr]): ExtensionMethods[Repr, it.type] = + * new ExtensionMethods(coll, it) + * + * // See it in action! + * List(1, 2, 3).mapReduce(_ * 2)(_ + _) // res0: Int = 12 + * "Yeah, well, you know, that's just, like, your opinion, man.".mapReduce(x => 1)(_ + _) // res1: Int = 59 + *}}} + * + * Here, we begin by creating a class `ExtensionMethods` which contains our + * `mapReduce` extension method. + * + * Note that `ExtensionMethods` takes a constructor argument `coll` of type `Repr`, where + * `Repr` represents (typically) the collection type, and an argument `it` of a subtype of `IsIterable[Repr]`. + * The body of the method starts by converting the `coll` argument to an `IterableOps` in order to + * call the `iterator` method on it. + * The remaining of the implementation is straightforward. + * + * The `withExtensions` implicit conversion makes the `mapReduce` operation available + * on any type `Repr` for which it exists an implicit `IsIterable[Repr]` instance. + * Note how we keep track of the precise type of the implicit `it` argument by using the + * `it.type` singleton type, rather than the wider `IsIterable[Repr]` type. We do that + * so that the information carried by the type members `A` and `C` of the `it` argument + * is not lost. + * + * When the `mapReduce` method is called on some type of which it is not + * a member, implicit search is triggered. Because implicit conversion + * `withExtensions` is generic, it will be applied as long as an implicit + * value of type `IsIterable[Repr]` can be found. Given that the + * `IsIterable` companion object contains implicit members that return values of type + * `IsIterable`, this requirement is typically satisfied, and the chain + * of interactions described in the previous paragraph is set into action. + * (See the `IsIterable` companion object, which contains a precise + * specification of the available implicits.) + * + * ''Note'': Currently, it's not possible to combine the implicit conversion and + * the class with the extension methods into an implicit class due to + * limitations of type inference. + * + * ===Implementing `IsIterable` for New Types=== + * + * One must simply provide an implicit value of type `IsIterable` + * specific to the new type, or an implicit conversion which returns an + * instance of `IsIterable` specific to the new type. + * + * Below is an example of an implementation of the `IsIterable` trait + * where the `Repr` type is `Range`. + * + *{{{ + * implicit val rangeRepr: IsIterable[Range] { type A = Int; type C = IndexedSeq[Int] } = + * new IsIterable[Range] { + * type A = Int + * type C = IndexedSeq[Int] + * def apply(coll: Range): IterableOps[Int, IndexedSeq, IndexedSeq[Int]] = coll + * } + *}}} + * + * (Note that in practice the `IsIterable[Range]` instance is already provided by + * the standard library, and it is defined as an `IsSeq[Range]` instance) + */ +trait IsIterable[Repr] extends IsIterableOnce[Repr] { + + /** The type returned by transformation operations that preserve the same elements + * type (e.g. `filter`, `take`). + * + * In practice, this type is often `Repr` itself, excepted in the case + * of `SeqView[A]` (and other `View[A]` subclasses), where it is “only” `View[A]`. + */ + type C + + @deprecated("'conversion' is now a method named 'apply'", "2.13.0") + override val conversion: Repr => IterableOps[A, Iterable, C] = apply(_) + + /** A conversion from the type `Repr` to `IterableOps[A, Iterable, C]` */ + def apply(coll: Repr): IterableOps[A, Iterable, C] + +} + +object IsIterable extends IsIterableLowPriority { + + // Straightforward case: IterableOps subclasses + implicit def iterableOpsIsIterable[A0, CC0[X] <: IterableOps[X, Iterable, CC0[X]]]: IsIterable[CC0[A0]] { type A = A0; type C = CC0[A0] } = + new IsIterable[CC0[A0]] { + type A = A0 + type C = CC0[A0] + def apply(coll: CC0[A]): IterableOps[A, Iterable, C] = coll + } + + // The `BitSet` type can not be unified with the `CC0` parameter of + // the above definition because it does not take a type parameter. + // Hence the need for a separate case: + implicit def bitSetOpsIsIterable[C0 <: BitSet with BitSetOps[C0]]: IsIterable[C0] { type A = Int; type C = C0 } = + new IsIterable[C0] { + type A = Int + type C = C0 + def apply(coll: C0): IterableOps[Int, Iterable, C0] = coll + } + +} + +trait IsIterableLowPriority { + + // Makes `IsSeq` instances visible in `IsIterable` companion + implicit def isSeqLikeIsIterable[Repr](implicit + isSeqLike: IsSeq[Repr] + ): IsIterable[Repr] { type A = isSeqLike.A; type C = isSeqLike.C } = isSeqLike + + // Makes `IsMap` instances visible in `IsIterable` companion + implicit def isMapLikeIsIterable[Repr](implicit + isMapLike: IsMap[Repr] + ): IsIterable[Repr] { type A = isMapLike.A; type C = isMapLike.C } = isMapLike + +} diff --git a/library/src/scala/collection/generic/IsIterableOnce.scala b/library/src/scala/collection/generic/IsIterableOnce.scala new file mode 100644 index 000000000000..82f0ec8b7332 --- /dev/null +++ b/library/src/scala/collection/generic/IsIterableOnce.scala @@ -0,0 +1,71 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package generic + +/** Type class witnessing that a collection representation type `Repr` has + * elements of type `A` and has a conversion to `IterableOnce[A]`. + * + * This type enables simple enrichment of `IterableOnce`s with extension + * methods which can make full use of the mechanics of the Scala collections + * framework in their implementation. + * + * Example usage, + * {{{ + * class FilterMapImpl[Repr, I <: IsIterableOnce[Repr]](coll: Repr, it: I) { + * final def filterMap[B, That](f: it.A => Option[B])(implicit bf: BuildFrom[Repr, B, That]): That = { + * val b = bf.newBuilder(coll) + * for(e <- it(coll).iterator) f(e) foreach (b +=) + * b.result() + * } + * } + * implicit def filterMap[Repr](coll: Repr)(implicit it: IsIterableOnce[Repr]): FilterMapImpl[Repr, it.type] = + * new FilterMapImpl(coll, it) + * + * List(1, 2, 3, 4, 5) filterMap (i => if(i % 2 == 0) Some(i) else None) + * // == List(2, 4) + * }}} + */ +trait IsIterableOnce[Repr] { + + /** The type of elements we can traverse over (e.g. `Int`). */ + type A + + @deprecated("'conversion' is now a method named 'apply'", "2.13.0") + val conversion: Repr => IterableOnce[A] = apply(_) + + /** A conversion from the representation type `Repr` to a `IterableOnce[A]`. */ + def apply(coll: Repr): IterableOnce[A] + +} + +object IsIterableOnce extends IsIterableOnceLowPriority { + + // Straightforward case: IterableOnce subclasses + implicit def iterableOnceIsIterableOnce[CC0[A] <: IterableOnce[A], A0]: IsIterableOnce[CC0[A0]] { type A = A0 } = + new IsIterableOnce[CC0[A0]] { + type A = A0 + def apply(coll: CC0[A0]): IterableOnce[A0] = coll + } + +} + +trait IsIterableOnceLowPriority { + + // Makes `IsIterable` instance visible in `IsIterableOnce` companion + implicit def isIterableLikeIsIterableOnce[Repr](implicit + isIterableLike: IsIterable[Repr] + ): IsIterableOnce[Repr] { type A = isIterableLike.A } = isIterableLike + +} diff --git a/library/src/scala/collection/generic/IsMap.scala b/library/src/scala/collection/generic/IsMap.scala new file mode 100644 index 000000000000..6178b2f2b7ca --- /dev/null +++ b/library/src/scala/collection/generic/IsMap.scala @@ -0,0 +1,115 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package generic + +import IsMap.Tupled +import scala.collection.immutable.{IntMap, LongMap} + +/** + * Type class witnessing that a collection type `Repr` + * has keys of type `K`, values of type `V` and has a conversion to + * `MapOps[K, V, Iterable, C]`, for some types `K`, `V` and `C`. + * + * This type enables simple enrichment of `Map`s with extension methods. + * + * @see [[scala.collection.generic.IsIterable]] + * @tparam Repr Collection type (e.g. `Map[Int, String]`) + */ +trait IsMap[Repr] extends IsIterable[Repr] { + + /** The type of keys */ + type K + + /** The type of values */ + type V + + type A = (K, V) + + /** A conversion from the type `Repr` to `MapOps[K, V, Iterable, C]` + * + * @note The third type parameter of the returned `MapOps` value is + * still `Iterable` (and not `Map`) because `MapView[K, V]` only + * extends `MapOps[K, V, View, View[A]]`. + */ + override def apply(c: Repr): MapOps[K, V, Tupled[Iterable]#Ap, C] + +} + +object IsMap { + + /** Convenient type level function that takes a unary type constructor `F[_]` + * and returns a binary type constructor that tuples its parameters and passes + * them to `F`. + * + * `Tupled[F]#Ap` is equivalent to `({ type Ap[X, +Y] = F[(X, Y)] })#Ap`. + */ + type Tupled[F[+_]] = { type Ap[X, Y] = F[(X, Y)] } + + // Map collections + implicit def mapOpsIsMap[CC0[X, Y] <: MapOps[X, Y, Tupled[Iterable]#Ap, CC0[X, Y]], K0, V0]: IsMap[CC0[K0, V0]] { type K = K0; type V = V0; type C = CC0[K, V] } = + new IsMap[CC0[K0, V0]] { + type K = K0 + type V = V0 + type C = CC0[K0, V0] + def apply(c: CC0[K0, V0]): MapOps[K0, V0, Tupled[Iterable]#Ap, C] = c + } + + // MapView + implicit def mapViewIsMap[CC0[X, Y] <: MapView[X, Y], K0, V0]: IsMap[CC0[K0, V0]] { type K = K0; type V = V0; type C = View[(K0, V0)] } = + new IsMap[CC0[K0, V0]] { + type K = K0 + type V = V0 + type C = View[(K, V)] + def apply(c: CC0[K0, V0]): MapOps[K0, V0, Tupled[Iterable]#Ap, View[(K0, V0)]] = c + } + + // AnyRefMap has stricter bounds than the ones used by the mapOpsIsMap definition + @deprecated("AnyRefMap is deprecated", "2.13.16") + implicit def anyRefMapIsMap[K0 <: AnyRef, V0]: IsMap[mutable.AnyRefMap[K0, V0]] { type K = K0; type V = V0; type C = mutable.AnyRefMap[K0, V0] } = + new IsMap[mutable.AnyRefMap[K0, V0]] { + type K = K0 + type V = V0 + type C = mutable.AnyRefMap[K0, V0] + def apply(c: mutable.AnyRefMap[K0, V0]): MapOps[K0, V0, Tupled[Iterable]#Ap, mutable.AnyRefMap[K0, V0]] = c + } + + // IntMap takes one type parameter only whereas mapOpsIsMap uses a parameter CC0 with two type parameters + implicit def intMapIsMap[V0]: IsMap[IntMap[V0]] { type K = Int; type V = V0; type C = IntMap[V0] } = + new IsMap[IntMap[V0]] { + type K = Int + type V = V0 + type C = IntMap[V0] + def apply(c: IntMap[V0]): MapOps[Int, V0, Tupled[Iterable]#Ap, IntMap[V0]] = c + } + + // LongMap is in a similar situation as IntMap + implicit def longMapIsMap[V0]: IsMap[LongMap[V0]] { type K = Long; type V = V0; type C = LongMap[V0] } = + new IsMap[LongMap[V0]] { + type K = Long + type V = V0 + type C = LongMap[V0] + def apply(c: LongMap[V0]): MapOps[Long, V0, Tupled[Iterable]#Ap, LongMap[V0]] = c + } + + // mutable.LongMap is in a similar situation as LongMap and IntMap + implicit def mutableLongMapIsMap[V0]: IsMap[mutable.LongMap[V0]] { type K = Long; type V = V0; type C = mutable.LongMap[V0] } = + new IsMap[mutable.LongMap[V0]] { + type K = Long + type V = V0 + type C = mutable.LongMap[V0] + def apply(c: mutable.LongMap[V0]): MapOps[Long, V0, Tupled[Iterable]#Ap, mutable.LongMap[V0]] = c + } + + +} diff --git a/library/src/scala/collection/generic/IsSeq.scala b/library/src/scala/collection/generic/IsSeq.scala new file mode 100644 index 000000000000..73d0cc9762d6 --- /dev/null +++ b/library/src/scala/collection/generic/IsSeq.scala @@ -0,0 +1,114 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package generic + +import scala.reflect.ClassTag + +/** Type class witnessing that a collection representation type `Repr` has + * elements of type `A` and has a conversion to `SeqOps[A, Iterable, C]`, for + * some types `A` and `C`. + * + * This type enables simple enrichment of `Seq`s with extension methods which + * can make full use of the mechanics of the Scala collections framework in + * their implementation. + * + * @see [[scala.collection.generic.IsIterable]] + */ +trait IsSeq[Repr] extends IsIterable[Repr] { + + @deprecated("'conversion' is now a method named 'apply'", "2.13.0") + override val conversion: Repr => SeqOps[A, Iterable, C] = apply(_) + + /** A conversion from the type `Repr` to `SeqOps[A, Iterable, C]` + * + * @note The second type parameter of the returned `SeqOps` value is + * still `Iterable` (and not `Seq`) because `SeqView[A]` only + * extends `SeqOps[A, View, View[A]]`. + */ + def apply(coll: Repr): SeqOps[A, Iterable, C] +} + +object IsSeq { + + private val seqOpsIsSeqVal: IsSeq[Seq[Any]] = + new IsSeq[Seq[Any]] { + type A = Any + type C = Any + def apply(coll: Seq[Any]): SeqOps[Any, Iterable, Any] = coll + } + + implicit def seqOpsIsSeq[CC0[X] <: SeqOps[X, Iterable, CC0[X]], A0]: IsSeq[CC0[A0]] { type A = A0; type C = CC0[A0] } = + seqOpsIsSeqVal.asInstanceOf[IsSeq[CC0[A0]] { type A = A0; type C = CC0[A0] }] + + implicit def seqViewIsSeq[CC0[X] <: SeqView[X], A0]: IsSeq[CC0[A0]] { type A = A0; type C = View[A0] } = + new IsSeq[CC0[A0]] { + type A = A0 + type C = View[A] + def apply(coll: CC0[A0]): SeqOps[A0, View, View[A0]] = coll + } + + implicit val stringIsSeq: IsSeq[String] { type A = Char; type C = String } = + new IsSeq[String] { + type A = Char + type C = String + def apply(s: String): SeqOps[Char, immutable.IndexedSeq, String] = + new SeqOps[Char, immutable.ArraySeq, String] { + def length: Int = s.length + def apply(i: Int): Char = s.charAt(i) + def toIterable: Iterable[Char] = new immutable.WrappedString(s) + protected[this] def coll: String = s + protected[this] def fromSpecific(coll: IterableOnce[Char]): String = coll.iterator.mkString + def iterableFactory: IterableFactory[immutable.ArraySeq] = immutable.ArraySeq.untagged + override def empty: String = "" + protected[this] def newSpecificBuilder: mutable.Builder[Char, String] = new StringBuilder + def iterator: Iterator[Char] = s.iterator + } + } + + implicit val stringViewIsSeq: IsSeq[StringView] { type A = Char; type C = View[Char] } = + new IsSeq[StringView] { + type A = Char + type C = View[Char] + def apply(coll: StringView): SeqOps[Char, View, View[Char]] = coll + } + + implicit def arrayIsSeq[A0 : ClassTag]: IsSeq[Array[A0]] { type A = A0; type C = Array[A0] } = + new IsSeq[Array[A0]] { + type A = A0 + type C = Array[A0] + def apply(a: Array[A0]): SeqOps[A0, Seq, Array[A0]] = + new SeqOps[A, mutable.ArraySeq, Array[A]] { + def apply(i: Int): A = a(i) + def length: Int = a.length + def toIterable: Iterable[A] = mutable.ArraySeq.make(a) + protected def coll: Array[A] = a + protected def fromSpecific(coll: IterableOnce[A]): Array[A] = Array.from(coll) + def iterableFactory: IterableFactory[mutable.ArraySeq] = mutable.ArraySeq.untagged + override def empty: Array[A] = Array.empty[A] + protected def newSpecificBuilder: mutable.Builder[A, Array[A]] = Array.newBuilder + def iterator: Iterator[A] = a.iterator + } + } + + // `Range` can not be unified with the `CC0` parameter of the + // `seqOpsIsSeq` definition because it does not take a type parameter. + // Hence the need for a separate case: + implicit def rangeIsSeq[C0 <: Range]: IsSeq[C0] { type A = Int; type C = immutable.IndexedSeq[Int] } = + new IsSeq[C0] { + type A = Int + type C = immutable.IndexedSeq[Int] + def apply(coll: C0): SeqOps[Int, Seq, immutable.IndexedSeq[Int]] = coll + } + +} diff --git a/library/src/scala/collection/generic/Subtractable.scala b/library/src/scala/collection/generic/Subtractable.scala new file mode 100644 index 000000000000..f8af03581aad --- /dev/null +++ b/library/src/scala/collection/generic/Subtractable.scala @@ -0,0 +1,62 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package generic + +/** This trait represents collection-like objects that can be reduced + * using a '+' operator. It defines variants of `-` and `--` + * as convenience methods in terms of single-element removal `-`. + * + * @tparam A the type of the elements of the $coll. + * @tparam Repr the type of the $coll itself + * @define coll collection + * @define Coll Subtractable + */ +@deprecated("Subtractable is deprecated. This is now implemented as part of SetOps, MapOps, etc.", "2.13.0") +trait Subtractable[A, +Repr <: Subtractable[A, Repr]] { self => + + /** The representation object of type `Repr` which contains the collection's elements + */ + protected def repr: Repr + + /** Creates a new $coll from this $coll with an element removed. + * @param elem the element to remove + * @return a new collection that contains all elements of the current $coll + * except one less occurrence of `elem`. + */ + def -(elem: A): Repr + + /** Creates a new $coll from this $coll with some elements removed. + * + * This method takes two or more elements to be removed. Another overloaded + * variant of this method handles the case where a single element is + * removed. + * @param elem1 the first element to remove. + * @param elem2 the second element to remove. + * @param elems the remaining elements to remove. + * @return a new $coll that contains all elements of the current $coll + * except one less occurrence of each of the given elements. + */ + def -(elem1: A, elem2: A, elems: A*): Repr = + this - elem1 - elem2 -- elems + + /** Creates a new $coll from this $coll by removing all elements of another + * collection. + * + * @param xs the collection containing the removed elements. + * @return a new $coll that contains all elements of the current $coll + * except one less occurrence of each of the elements of `elems`. + */ + def --(xs: IterableOnce[A]): Repr = (repr /: xs.iterator) (_ - _) +} diff --git a/library/src/scala/collection/generic/package.scala b/library/src/scala/collection/generic/package.scala new file mode 100644 index 000000000000..5aaf90547384 --- /dev/null +++ b/library/src/scala/collection/generic/package.scala @@ -0,0 +1,34 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + + +package object generic { + @deprecated("Clearable was moved from collection.generic to collection.mutable", "2.13.0") + type Clearable = scala.collection.mutable.Clearable + + @deprecated("Use scala.collection.BuildFrom instead", "2.13.0") + type CanBuildFrom[-From, -A, +C] = scala.collection.BuildFrom[From, A, C] + + @deprecated("Growable was moved from collection.generic to collection.mutable", "2.13.0") + type Growable[-A] = scala.collection.mutable.Growable[A] + + @deprecated("Shrinkable was moved from collection.generic to collection.mutable", "2.13.0") + type Shrinkable[-A] = scala.collection.mutable.Shrinkable[A] + + @deprecated("Use IsIterable instead", "2.13.0") + type IsTraversableLike[Repr] = IsIterable[Repr] + + @deprecated("Use IsIterableOnce instead", "2.13.0") + type IsTraversableOnce[Repr] = IsIterableOnce[Repr] +} diff --git a/library/src/scala/collection/immutable/ArraySeq.scala b/library/src/scala/collection/immutable/ArraySeq.scala new file mode 100644 index 000000000000..eafe9baa719f --- /dev/null +++ b/library/src/scala/collection/immutable/ArraySeq.scala @@ -0,0 +1,694 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package immutable + +import java.util.Arrays + +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.Stepper.EfficientSplit +import scala.collection.mutable.{ArrayBuffer, ArrayBuilder, Builder, ArraySeq => MutableArraySeq} +import scala.collection.convert.impl._ +import scala.reflect.ClassTag +import scala.runtime.ScalaRunTime +import scala.util.Sorting +import scala.util.hashing.MurmurHash3 + +/** + * An immutable array. + * + * Supports efficient indexed access and has a small memory footprint. + * + * @define coll immutable array + * @define Coll `ArraySeq` + */ +sealed abstract class ArraySeq[+A] + extends AbstractSeq[A] + with IndexedSeq[A] + with IndexedSeqOps[A, ArraySeq, ArraySeq[A]] + with StrictOptimizedSeqOps[A, ArraySeq, ArraySeq[A]] + with EvidenceIterableFactoryDefaults[A, ArraySeq, ClassTag] + with Serializable { + + /** The tag of the element type. This does not have to be equal to the element type of this ArraySeq. A primitive + * ArraySeq can be backed by an array of boxed values and a reference ArraySeq can be backed by an array of a supertype + * or subtype of the element type. */ + protected def elemTag: ClassTag[_] + + override def iterableFactory: SeqFactory[ArraySeq] = ArraySeq.untagged + + /** The wrapped mutable `Array` that backs this `ArraySeq`. Any changes to this array will break + * the expected immutability. Its element type does not have to be equal to the element type of this ArraySeq. + * A primitive ArraySeq can be backed by an array of boxed values and a reference ArraySeq can be backed by an + * array of a supertype or subtype of the element type. */ + def unsafeArray: Array[_] + + protected def evidenceIterableFactory: ArraySeq.type = ArraySeq + protected def iterableEvidence: ClassTag[A @uncheckedVariance] = elemTag.asInstanceOf[ClassTag[A]] + + def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit + + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): A + + override def updated[B >: A](index: Int, elem: B): ArraySeq[B] = { + val dest = new Array[Any](length) + Array.copy(unsafeArray, 0, dest, 0, length) + dest(index) = elem + ArraySeq.unsafeWrapArray(dest).asInstanceOf[ArraySeq[B]] + } + + override def map[B](f: A => B): ArraySeq[B] = { + val a = new Array[Any](size) + var i = 0 + while (i < a.length){ + a(i) = f(apply(i)) + i += 1 + } + ArraySeq.unsafeWrapArray(a).asInstanceOf[ArraySeq[B]] + } + + override def prepended[B >: A](elem: B): ArraySeq[B] = + ArraySeq.unsafeWrapArray(unsafeArray.prepended[Any](elem)).asInstanceOf[ArraySeq[B]] + + override def appended[B >: A](elem: B): ArraySeq[B] = + ArraySeq.unsafeWrapArray(unsafeArray.appended[Any](elem)).asInstanceOf[ArraySeq[B]] + + /** Fast concatenation of two [[ArraySeq]]s. + * + * @return null if optimisation not possible. + */ + private def appendedAllArraySeq[B >: A](that: ArraySeq[B]): ArraySeq[B] = { + // Optimise concatenation of two ArraySeqs + // For ArraySeqs with sizes of [100, 1000, 10000] this is [3.5, 4.1, 5.2]x as fast + if (isEmpty) + that + else if (that.isEmpty) + this + else { + val thisIsObj = this.unsafeArray.isInstanceOf[Array[AnyRef]] + val thatIsObj = that.unsafeArray.isInstanceOf[Array[AnyRef]] + val mismatch = thisIsObj != thatIsObj + if (mismatch) + // Combining primatives and objects: abort + null + else if (thisIsObj) { + // A and B are objects + val ax = this.unsafeArray.asInstanceOf[Array[A]] + val ay = that.unsafeArray.asInstanceOf[Array[B]] + val len = ax.length + ay.length + val a = new Array[AnyRef](len) + System.arraycopy(ax, 0, a, 0, ax.length) + System.arraycopy(ay, 0, a, ax.length, ay.length) + ArraySeq.unsafeWrapArray(a).asInstanceOf[ArraySeq[B]] + } else { + // A is a primative and B = A. Use this instance's protected ClassTag. + val ax = this.unsafeArray.asInstanceOf[Array[A]] + val ay = that.unsafeArray.asInstanceOf[Array[A]] + val len = ax.length + ay.length + val a = iterableEvidence.newArray(len) + System.arraycopy(ax, 0, a, 0, ax.length) + System.arraycopy(ay, 0, a, ax.length, ay.length) + ArraySeq.unsafeWrapArray(a).asInstanceOf[ArraySeq[B]] + } + } + } + + override def appendedAll[B >: A](suffix: collection.IterableOnce[B]): ArraySeq[B] = { + def genericResult = { + val k = suffix.knownSize + if (k == 0) this + else { + val b = ArrayBuilder.make[Any] + if(k >= 0) b.sizeHint(k + unsafeArray.length) + b.addAll(unsafeArray) + b.addAll(suffix) + ArraySeq.unsafeWrapArray(b.result()).asInstanceOf[ArraySeq[B]] + } + } + + suffix match { + case that: ArraySeq[_] => + val result = appendedAllArraySeq(that.asInstanceOf[ArraySeq[B]]) + if (result == null) genericResult + else result + case _ => + genericResult + } + } + + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): ArraySeq[B] = { + def genericResult = { + val k = prefix.knownSize + if (k == 0) this + else { + val b = ArrayBuilder.make[Any] + if(k >= 0) b.sizeHint(k + unsafeArray.length) + b.addAll(prefix) + if(k < 0) b.sizeHint(b.length + unsafeArray.length) + b.addAll(unsafeArray) + ArraySeq.unsafeWrapArray(b.result()).asInstanceOf[ArraySeq[B]] + } + } + + prefix match { + case that: ArraySeq[_] => + val result = that.asInstanceOf[ArraySeq[B]].appendedAllArraySeq(this) + if (result == null) genericResult + else result + case _ => + genericResult + } + } + + override def zip[B](that: collection.IterableOnce[B]): ArraySeq[(A, B)] = + that match { + case bs: ArraySeq[B] => + ArraySeq.tabulate(length min bs.length) { i => + (apply(i), bs(i)) + } + case _ => + strictOptimizedZip[B, ArraySeq[(A, B)]](that, iterableFactory.newBuilder) + } + + override def take(n: Int): ArraySeq[A] = + if (unsafeArray.length <= n) + this + else + ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).take(n)).asInstanceOf[ArraySeq[A]] + + override def takeRight(n: Int): ArraySeq[A] = + if (unsafeArray.length <= n) + this + else + ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).takeRight(n)).asInstanceOf[ArraySeq[A]] + + override def drop(n: Int): ArraySeq[A] = + if (n <= 0) + this + else + ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).drop(n)).asInstanceOf[ArraySeq[A]] + + override def dropRight(n: Int): ArraySeq[A] = + if (n <= 0) + this + else + ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).dropRight(n)).asInstanceOf[ArraySeq[A]] + + override def slice(from: Int, until: Int): ArraySeq[A] = + if (from <= 0 && unsafeArray.length <= until) + this + else + ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).slice(from, until)).asInstanceOf[ArraySeq[A]] + + override def foldLeft[B](z: B)(f: (B, A) => B): B = { + // For ArraySeqs with sizes of [100, 1000, 10000] this is [1.3, 1.8, 1.8]x as fast + // as the same while-loop over this instead of unsafeArray. + val array = unsafeArray + var b = z + var i = 0 + while (i < array.length) { + val a = array(i).asInstanceOf[A] + b = f(b, a) + i += 1 + } + b + } + + override def foldRight[B](z: B)(f: (A, B) => B): B = { + // For ArraySeqs with sizes of [100, 1000, 10000] this is [1.6, 1.8, 2.7]x as fast + // as the same while-loop over this instead of unsafeArray. + val array = unsafeArray + var b = z + var i = array.length + while (i > 0) { + i -= 1 + val a = array(i).asInstanceOf[A] + b = f(a, b) + } + b + } + + override def tail: ArraySeq[A] = ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).tail).asInstanceOf[ArraySeq[A]] + + override def reverse: ArraySeq[A] = ArraySeq.unsafeWrapArray(new ArrayOps(unsafeArray).reverse).asInstanceOf[ArraySeq[A]] + + override protected[this] def className = "ArraySeq" + + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { + val copied = IterableOnce.elemsToCopyToArray(length, xs.length, start, len) + if(copied > 0) { + Array.copy(unsafeArray, 0, xs, start, copied) + } + copied + } + + override protected final def applyPreferredMaxLength: Int = Int.MaxValue + + override def sorted[B >: A](implicit ord: Ordering[B]): ArraySeq[A] = + if(unsafeArray.length <= 1) this + else { + val a = Array.copyAs[AnyRef](unsafeArray, length)(ClassTag.AnyRef) + Arrays.sort(a, ord.asInstanceOf[Ordering[AnyRef]]) + new ArraySeq.ofRef[AnyRef](a).asInstanceOf[ArraySeq[A]] + } +} + +/** + * $factoryInfo + * @define coll immutable array + * @define Coll `ArraySeq` + */ +@SerialVersionUID(3L) +object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self => + val untagged: SeqFactory[ArraySeq] = new ClassTagSeqFactory.AnySeqDelegate(self) + + private[this] lazy val emptyImpl = new ArraySeq.ofRef[Nothing](new Array[Nothing](0)) + + def empty[A : ClassTag]: ArraySeq[A] = emptyImpl + + def from[A](it: scala.collection.IterableOnce[A])(implicit tag: ClassTag[A]): ArraySeq[A] = it match { + case as: ArraySeq[A] => as + case _ => unsafeWrapArray(Array.from[A](it)) + } + + def newBuilder[A : ClassTag]: Builder[A, ArraySeq[A]] = + ArrayBuffer.newBuilder[A].mapResult(b => unsafeWrapArray[A](b.toArray)) + + override def fill[A : ClassTag](n: Int)(elem: => A): ArraySeq[A] = tabulate(n)(_ => elem) + + override def tabulate[A : ClassTag](n: Int)(f: Int => A): ArraySeq[A] = { + val elements = Array.ofDim[A](scala.math.max(n, 0)) + var i = 0 + while (i < n) { + ScalaRunTime.array_update(elements, i, f(i)) + i = i + 1 + } + ArraySeq.unsafeWrapArray(elements) + } + + /** + * Wrap an existing `Array` into an `ArraySeq` of the proper primitive specialization type + * without copying. Any changes to wrapped array will break the expected immutability. + * + * Note that an array containing boxed primitives can be wrapped in an `ArraySeq` without + * copying. For example, `val a: Array[Any] = Array(1)` is an array of `Object` at runtime, + * containing `Integer`s. An `ArraySeq[Int]` can be obtained with a cast: + * `ArraySeq.unsafeWrapArray(a).asInstanceOf[ArraySeq[Int]]`. The values are still + * boxed, the resulting instance is an [[ArraySeq.ofRef]]. Writing + * `ArraySeq.unsafeWrapArray(a.asInstanceOf[Array[Int]])` does not work, it throws a + * `ClassCastException` at runtime. + */ + def unsafeWrapArray[T](x: Array[T]): ArraySeq[T] = ((x: @unchecked) match { + case null => null + case x: Array[AnyRef] => new ofRef[AnyRef](x) + case x: Array[Int] => new ofInt(x) + case x: Array[Double] => new ofDouble(x) + case x: Array[Long] => new ofLong(x) + case x: Array[Float] => new ofFloat(x) + case x: Array[Char] => new ofChar(x) + case x: Array[Byte] => new ofByte(x) + case x: Array[Short] => new ofShort(x) + case x: Array[Boolean] => new ofBoolean(x) + case x: Array[Unit] => new ofUnit(x) + }).asInstanceOf[ArraySeq[T]] + + @SerialVersionUID(3L) + final class ofRef[T <: AnyRef](val unsafeArray: Array[T]) extends ArraySeq[T] { + def elemTag: ClassTag[T] = ClassTag[T](unsafeArray.getClass.getComponentType) + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): T = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any): Boolean = that match { + case that: ofRef[_] => + Array.equals( + this.unsafeArray.asInstanceOf[Array[AnyRef]], + that.unsafeArray.asInstanceOf[Array[AnyRef]]) + case _ => super.equals(that) + } + override def sorted[B >: T](implicit ord: Ordering[B]): ArraySeq.ofRef[T] = { + if(unsafeArray.length <= 1) this + else { + val a = unsafeArray.clone() + Arrays.sort(a, ord.asInstanceOf[Ordering[T]]) + new ArraySeq.ofRef(a) + } + } + override def iterator: Iterator[T] = new ArrayOps.ArrayIterator[T](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[T, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + new ObjectArrayStepper(unsafeArray, 0, unsafeArray.length) + else shape.parUnbox(new ObjectArrayStepper(unsafeArray, 0, unsafeArray.length).asInstanceOf[AnyStepper[T] with EfficientSplit]) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofByte(val unsafeArray: Array[Byte]) extends ArraySeq[Byte] { + // Type erases to `ManifestFactory.ByteManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Byte.type = ClassTag.Byte + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Byte = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofByte => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def sorted[B >: Byte](implicit ord: Ordering[B]): ArraySeq[Byte] = + if(length <= 1) this + else if(ord eq Ordering.Byte) { + val a = unsafeArray.clone() + Arrays.sort(a) + new ArraySeq.ofByte(a) + } else super.sorted[B] + override def iterator: Iterator[Byte] = new ArrayOps.ArrayIterator[Byte](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Byte, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new WidenedByteArrayStepper(unsafeArray, 0, unsafeArray.length)) + else new WidenedByteArrayStepper(unsafeArray, 0, unsafeArray.length) + ).asInstanceOf[S with EfficientSplit] + override def updated[B >: Byte](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Byte => new ArraySeq.ofByte(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Byte](elem: B): ArraySeq[B] = + elem match { + case b: Byte => new ArraySeq.ofByte(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Byte](elem: B): ArraySeq[B] = + elem match { + case b: Byte => new ArraySeq.ofByte(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + } + + @SerialVersionUID(3L) + final class ofShort(val unsafeArray: Array[Short]) extends ArraySeq[Short] { + // Type erases to `ManifestFactory.ShortManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Short.type = ClassTag.Short + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Short = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofShort => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def sorted[B >: Short](implicit ord: Ordering[B]): ArraySeq[Short] = + if(length <= 1) this + else if(ord eq Ordering.Short) { + val a = unsafeArray.clone() + Arrays.sort(a) + new ArraySeq.ofShort(a) + } else super.sorted[B] + override def iterator: Iterator[Short] = new ArrayOps.ArrayIterator[Short](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Short, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new WidenedShortArrayStepper(unsafeArray, 0, unsafeArray.length)) + else new WidenedShortArrayStepper(unsafeArray, 0, unsafeArray.length) + ).asInstanceOf[S with EfficientSplit] + override def updated[B >: Short](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Short => new ArraySeq.ofShort(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Short](elem: B): ArraySeq[B] = + elem match { + case b: Short => new ArraySeq.ofShort(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Short](elem: B): ArraySeq[B] = + elem match { + case b: Short => new ArraySeq.ofShort(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + } + + @SerialVersionUID(3L) + final class ofChar(val unsafeArray: Array[Char]) extends ArraySeq[Char] { + // Type erases to `ManifestFactory.CharManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Char.type = ClassTag.Char + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Char = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofChar => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def sorted[B >: Char](implicit ord: Ordering[B]): ArraySeq[Char] = + if(length <= 1) this + else if(ord eq Ordering.Char) { + val a = unsafeArray.clone() + Arrays.sort(a) + new ArraySeq.ofChar(a) + } else super.sorted[B] + override def iterator: Iterator[Char] = new ArrayOps.ArrayIterator[Char](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Char, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new WidenedCharArrayStepper(unsafeArray, 0, unsafeArray.length)) + else new WidenedCharArrayStepper(unsafeArray, 0, unsafeArray.length) + ).asInstanceOf[S with EfficientSplit] + override def updated[B >: Char](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Char => new ArraySeq.ofChar(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Char](elem: B): ArraySeq[B] = + elem match { + case b: Char => new ArraySeq.ofChar(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Char](elem: B): ArraySeq[B] = + elem match { + case b: Char => new ArraySeq.ofChar(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + + override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = + (new MutableArraySeq.ofChar(unsafeArray)).addString(sb, start, sep, end) + } + + @SerialVersionUID(3L) + final class ofInt(val unsafeArray: Array[Int]) extends ArraySeq[Int] { + // Type erases to `ManifestFactory.IntManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Int.type = ClassTag.Int + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Int = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofInt => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def sorted[B >: Int](implicit ord: Ordering[B]): ArraySeq[Int] = + if(length <= 1) this + else if(ord eq Ordering.Int) { + val a = unsafeArray.clone() + Arrays.sort(a) + new ArraySeq.ofInt(a) + } else super.sorted[B] + override def iterator: Iterator[Int] = new ArrayOps.ArrayIterator[Int](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Int, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new IntArrayStepper(unsafeArray, 0, unsafeArray.length)) + else new IntArrayStepper(unsafeArray, 0, unsafeArray.length) + ).asInstanceOf[S with EfficientSplit] + override def updated[B >: Int](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Int => new ArraySeq.ofInt(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Int](elem: B): ArraySeq[B] = + elem match { + case b: Int => new ArraySeq.ofInt(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Int](elem: B): ArraySeq[B] = + elem match { + case b: Int => new ArraySeq.ofInt(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + } + + @SerialVersionUID(3L) + final class ofLong(val unsafeArray: Array[Long]) extends ArraySeq[Long] { + // Type erases to `ManifestFactory.LongManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Long.type = ClassTag.Long + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Long = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofLong => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def sorted[B >: Long](implicit ord: Ordering[B]): ArraySeq[Long] = + if(length <= 1) this + else if(ord eq Ordering.Long) { + val a = unsafeArray.clone() + Arrays.sort(a) + new ArraySeq.ofLong(a) + } else super.sorted[B] + override def iterator: Iterator[Long] = new ArrayOps.ArrayIterator[Long](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Long, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParLongStepper(new LongArrayStepper(unsafeArray, 0, unsafeArray.length)) + else new LongArrayStepper(unsafeArray, 0, unsafeArray.length) + ).asInstanceOf[S with EfficientSplit] + override def updated[B >: Long](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Long => new ArraySeq.ofLong(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Long](elem: B): ArraySeq[B] = + elem match { + case b: Long => new ArraySeq.ofLong(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Long](elem: B): ArraySeq[B] = + elem match { + case b: Long => new ArraySeq.ofLong(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + } + + @SerialVersionUID(3L) + final class ofFloat(val unsafeArray: Array[Float]) extends ArraySeq[Float] { + // Type erases to `ManifestFactory.FloatManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Float.type = ClassTag.Float + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Float = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofFloat => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def iterator: Iterator[Float] = new ArrayOps.ArrayIterator[Float](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Float, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParDoubleStepper(new WidenedFloatArrayStepper(unsafeArray, 0, unsafeArray.length)) + else new WidenedFloatArrayStepper(unsafeArray, 0, unsafeArray.length) + ).asInstanceOf[S with EfficientSplit] + override def updated[B >: Float](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Float => new ArraySeq.ofFloat(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Float](elem: B): ArraySeq[B] = + elem match { + case b: Float => new ArraySeq.ofFloat(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Float](elem: B): ArraySeq[B] = + elem match { + case b: Float => new ArraySeq.ofFloat(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + } + + @SerialVersionUID(3L) + final class ofDouble(val unsafeArray: Array[Double]) extends ArraySeq[Double] { + // Type erases to `ManifestFactory.DoubleManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Double.type = ClassTag.Double + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Double = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofDouble => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def iterator: Iterator[Double] = new ArrayOps.ArrayIterator[Double](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Double, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParDoubleStepper(new DoubleArrayStepper(unsafeArray, 0, unsafeArray.length)) + else new DoubleArrayStepper(unsafeArray, 0, unsafeArray.length) + ).asInstanceOf[S with EfficientSplit] + override def updated[B >: Double](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Double => new ArraySeq.ofDouble(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Double](elem: B): ArraySeq[B] = + elem match { + case b: Double => new ArraySeq.ofDouble(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Double](elem: B): ArraySeq[B] = + elem match { + case b: Double => new ArraySeq.ofDouble(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + } + + @SerialVersionUID(3L) + final class ofBoolean(val unsafeArray: Array[Boolean]) extends ArraySeq[Boolean] { + // Type erases to `ManifestFactory.BooleanManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Boolean.type = ClassTag.Boolean + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Boolean = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofBoolean => Arrays.equals(unsafeArray, that.unsafeArray) + case _ => super.equals(that) + } + override def sorted[B >: Boolean](implicit ord: Ordering[B]): ArraySeq[Boolean] = + if(length <= 1) this + else if(ord eq Ordering.Boolean) { + val a = unsafeArray.clone() + Sorting.stableSort(a) + new ArraySeq.ofBoolean(a) + } else super.sorted[B] + override def iterator: Iterator[Boolean] = new ArrayOps.ArrayIterator[Boolean](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Boolean, S]): S with EfficientSplit = + new BoxedBooleanArrayStepper(unsafeArray, 0, unsafeArray.length).asInstanceOf[S with EfficientSplit] + override def updated[B >: Boolean](index: Int, elem: B): ArraySeq[B] = + elem match { + case b: Boolean => new ArraySeq.ofBoolean(unsafeArray.updated(index, b)) + case _ => super.updated(index, elem) + } + override def appended[B >: Boolean](elem: B): ArraySeq[B] = + elem match { + case b: Boolean => new ArraySeq.ofBoolean(unsafeArray.appended(b)) + case _ => super.appended(elem) + } + override def prepended[B >: Boolean](elem: B): ArraySeq[B] = + elem match { + case b: Boolean => new ArraySeq.ofBoolean(unsafeArray.prepended(b)) + case _ => super.prepended(elem) + } + } + + @SerialVersionUID(3L) + final class ofUnit(val unsafeArray: Array[Unit]) extends ArraySeq[Unit] { + // Type erases to `ManifestFactory.UnitManifest`, but can't annotate that because it's not accessible + protected def elemTag: ClassTag.Unit.type = ClassTag.Unit + def length: Int = unsafeArray.length + @throws[ArrayIndexOutOfBoundsException] + def apply(i: Int): Unit = unsafeArray(i) + override def hashCode = MurmurHash3.arraySeqHash(unsafeArray) + override def equals(that: Any) = that match { + case that: ofUnit => unsafeArray.length == that.unsafeArray.length + case _ => super.equals(that) + } + override def iterator: Iterator[Unit] = new ArrayOps.ArrayIterator[Unit](unsafeArray) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Unit, S]): S with EfficientSplit = + new ObjectArrayStepper[AnyRef](unsafeArray.asInstanceOf[Array[AnyRef]], 0, unsafeArray.length).asInstanceOf[S with EfficientSplit] + } +} diff --git a/library/src/scala/collection/immutable/BitSet.scala b/library/src/scala/collection/immutable/BitSet.scala new file mode 100644 index 000000000000..a9b5837ff566 --- /dev/null +++ b/library/src/scala/collection/immutable/BitSet.scala @@ -0,0 +1,375 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import BitSetOps.{LogWL, updateArray} +import mutable.Builder +import scala.annotation.{implicitNotFound, nowarn} + +/** A class for immutable bitsets. + * $bitsetinfo + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#immutable-bitsets "Scala's Collection Library overview"]] + * section on `Immutable BitSets` for more information. + * + * @define Coll `immutable.BitSet` + * @define coll immutable bitset + */ +sealed abstract class BitSet + extends AbstractSet[Int] + with SortedSet[Int] + with SortedSetOps[Int, SortedSet, BitSet] + with StrictOptimizedSortedSetOps[Int, SortedSet, BitSet] + with collection.BitSet + with collection.BitSetOps[BitSet] + with Serializable { + + override def unsorted: Set[Int] = this + + override protected def fromSpecific(coll: IterableOnce[Int]): BitSet = bitSetFactory.fromSpecific(coll) + override protected def newSpecificBuilder: Builder[Int, BitSet] = bitSetFactory.newBuilder + override def empty: BitSet = bitSetFactory.empty + + def bitSetFactory: BitSet.type = BitSet + + protected[collection] def fromBitMaskNoCopy(elems: Array[Long]): BitSet = BitSet.fromBitMaskNoCopy(elems) + + def incl(elem: Int): BitSet = { + require(elem >= 0, "bitset element must be >= 0") + if (contains(elem)) this + else { + val idx = elem >> LogWL + updateWord(idx, word(idx) | (1L << elem)) + } + } + + def excl(elem: Int): BitSet = { + require(elem >= 0, "bitset element must be >= 0") + if (contains(elem)) { + val idx = elem >> LogWL + updateWord(idx, word(idx) & ~(1L << elem)) + } else this + } + + /** Update word at index `idx`; enlarge set if `idx` outside range of set. + */ + protected def updateWord(idx: Int, w: Long): BitSet + + override def map(f: Int => Int): BitSet = strictOptimizedMap(newSpecificBuilder, f) + override def map[B](f: Int => B)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + super[StrictOptimizedSortedSetOps].map(f) + + override def flatMap(f: Int => IterableOnce[Int]): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f) + override def flatMap[B](f: Int => IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + super[StrictOptimizedSortedSetOps].flatMap(f) + + override def collect(pf: PartialFunction[Int, Int]): BitSet = strictOptimizedCollect(newSpecificBuilder, pf) + override def collect[B](pf: scala.PartialFunction[Int, B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + super[StrictOptimizedSortedSetOps].collect(pf) + + // necessary for disambiguation + override def zip[B](that: scala.IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] = + super.zip(that) + + protected[this] def writeReplace(): AnyRef = new BitSet.SerializationProxy(this) +} + +/** + * $factoryInfo + * @define Coll `immutable.BitSet` + * @define coll immutable bitset + */ +@nowarn("cat=deprecation&msg=Implementation classes of BitSet should not be accessed directly") +@SerialVersionUID(3L) +object BitSet extends SpecificIterableFactory[Int, BitSet] { + + def fromSpecific(it: scala.collection.IterableOnce[Int]): BitSet = + it match { + case bs: BitSet => bs + case _ => (newBuilder ++= it).result() + } + + final val empty: BitSet = new BitSet1(0L) + + def newBuilder: Builder[Int, BitSet] = + mutable.BitSet.newBuilder.mapResult(bs => fromBitMaskNoCopy(bs.elems)) + + private def createSmall(a: Long, b: Long): BitSet = if (b == 0L) new BitSet1(a) else new BitSet2(a, b) + + /** A bitset containing all the bits in an array */ + def fromBitMask(elems: Array[Long]): BitSet = { + val len = elems.length + if (len == 0) empty + else if (len == 1) new BitSet1(elems(0)) + else if (len == 2) createSmall(elems(0), elems(1)) + else { + val a = java.util.Arrays.copyOf(elems, len) + new BitSetN(a) + } + } + + /** A bitset containing all the bits in an array, wrapping the existing + * array without copying. + */ + def fromBitMaskNoCopy(elems: Array[Long]): BitSet = { + val len = elems.length + if (len == 0) empty + else if (len == 1) new BitSet1(elems(0)) + else if (len == 2) createSmall(elems(0), elems(1)) + else new BitSetN(elems) + } + + @deprecated("Implementation classes of BitSet should not be accessed directly", "2.13.0") + class BitSet1(val elems: Long) extends BitSet { + protected[collection] def nwords = 1 + protected[collection] def word(idx: Int) = if (idx == 0) elems else 0L + protected[collection] def updateWord(idx: Int, w: Long): BitSet = + if (idx == 0) new BitSet1(w) + else if (idx == 1) createSmall(elems, w) + else this.fromBitMaskNoCopy(updateArray(Array(elems), idx, w)) + + + override def diff(other: collection.Set[Int]): BitSet = other match { + case bs: collection.BitSet => bs.nwords match { + case 0 => this + case _ => + val newElems = elems & ~bs.word(0) + if (newElems == 0L) this.empty else new BitSet1(newElems) + } + case _ => super.diff(other) + } + + override def filterImpl(pred: Int => Boolean, isFlipped: Boolean): BitSet = { + val _elems = BitSetOps.computeWordForFilter(pred, isFlipped, elems, 0) + if (_elems == 0L) this.empty else new BitSet1(_elems) + } + } + + @deprecated("Implementation classes of BitSet should not be accessed directly", "2.13.0") + class BitSet2(val elems0: Long, val elems1: Long) extends BitSet { + protected[collection] def nwords = 2 + protected[collection] def word(idx: Int) = if (idx == 0) elems0 else if (idx == 1) elems1 else 0L + protected[collection] def updateWord(idx: Int, w: Long): BitSet = + if (idx == 0) new BitSet2(w, elems1) + else if (idx == 1) createSmall(elems0, w) + else this.fromBitMaskNoCopy(updateArray(Array(elems0, elems1), idx, w)) + + + override def diff(other: collection.Set[Int]): BitSet = other match { + case bs: collection.BitSet => bs.nwords match { + case 0 => this + case 1 => + new BitSet2(elems0 & ~bs.word(0), elems1) + case _ => + val _elems0 = elems0 & ~bs.word(0) + val _elems1 = elems1 & ~bs.word(1) + + if (_elems1 == 0L) { + if (_elems0 == 0L) { + this.empty + } else { + new BitSet1(_elems0) + } + } else { + new BitSet2(_elems0, _elems1) + } + } + case _ => super.diff(other) + } + + override def filterImpl(pred: Int => Boolean, isFlipped: Boolean): BitSet = { + val _elems0 = BitSetOps.computeWordForFilter(pred, isFlipped, elems0, 0) + val _elems1 = BitSetOps.computeWordForFilter(pred, isFlipped, elems1, 1) + + if (_elems1 == 0L) { + if (_elems0 == 0L) { + this.empty + } + else new BitSet1(_elems0) + } + else new BitSet2(_elems0, _elems1) + } + } + + @deprecated("Implementation classes of BitSet should not be accessed directly", "2.13.0") + class BitSetN(val elems: Array[Long]) extends BitSet { + protected[collection] def nwords = elems.length + + protected[collection] def word(idx: Int) = if (idx < nwords) elems(idx) else 0L + + protected[collection] def updateWord(idx: Int, w: Long): BitSet = this.fromBitMaskNoCopy(updateArray(elems, idx, w)) + + override def diff(that: collection.Set[Int]): BitSet = that match { + case bs: collection.BitSet => + /* + * Algorithm: + * + * We iterate, word-by-word, backwards from the shortest of the two bitsets (this, or bs) i.e. the one with + * the fewer words. Two extra concerns for optimization are described below. + * + * Array Shrinking: + * If `this` is not longer than `bs`, then since we must iterate through the full array of words, + * we can track the new highest index word which is non-zero, at little additional cost. At the end, the new + * Array[Long] allocated for the returned BitSet will only be of size `maxNonZeroIndex + 1` + * + * Tracking Changes: + * If the two sets are disjoint, then we can return `this`. Therefor, until at least one change is detected, + * we check each word for if it has changed from its corresponding word in `this`. Once a single change is + * detected, we stop checking because the cost of the new Array must be paid anyways. + */ + + val bsnwords = bs.nwords + val thisnwords = nwords + if (bsnwords >= thisnwords) { + // here, we may have opportunity to shrink the size of the array + // so, track the highest index which is non-zero. That ( + 1 ) will be our new array length + var i = thisnwords - 1 + var currentWord = 0L + // if there are never any changes, we can return `this` at the end + var anyChanges = false + while (i >= 0 && currentWord == 0L) { + val oldWord = word(i) + currentWord = oldWord & ~bs.word(i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + i match { + case -1 => + if (anyChanges) { + if (currentWord == 0) { + this.empty + } else { + new BitSet1(currentWord) + } + } else { + this + } + case 0 => + val oldFirstWord = word(0) + val firstWord = oldFirstWord & ~bs.word(0) + anyChanges ||= firstWord != oldFirstWord + if (anyChanges) { + new BitSet2(firstWord, currentWord) + } else { + this + } + case _ => + val minimumNonZeroIndex: Int = i + 1 + while (!anyChanges && i >= 0) { + val oldWord = word(i) + currentWord = oldWord & ~bs.word(i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + if (anyChanges) { + val newArray = elems.take(minimumNonZeroIndex + 1) + newArray(i + 1) = currentWord + while (i >= 0) { + newArray(i) = word(i) & ~bs.word(i) + i -= 1 + } + new BitSetN(newArray) + } else { + this + } + } + } else { + var i = bsnwords - 1 + var anyChanges = false + var currentWord = 0L + while (i >= 0 && !anyChanges) { + val oldWord = word(i) + currentWord = oldWord & ~bs.word(i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + if (anyChanges) { + val newElems = elems.clone() + newElems(i + 1) = currentWord + while (i >= 0) { + newElems(i) = word(i) & ~bs.word(i) + i -= 1 + } + this.fromBitMaskNoCopy(newElems) + } else { + this + } + } + case _ => super.diff(that) + } + + + override def filterImpl(pred: Int => Boolean, isFlipped: Boolean): BitSet = { + // here, we may have opportunity to shrink the size of the array + // so, track the highest index which is non-zero. That ( + 1 ) will be our new array length + var i = nwords - 1 + var currentWord = 0L + // if there are never any changes, we can return `this` at the end + var anyChanges = false + while (i >= 0 && currentWord == 0L) { + val oldWord = word(i) + currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + i match { + case -1 => + if (anyChanges) { + if (currentWord == 0) { + this.empty + } else { + new BitSet1(currentWord) + } + } else { + this + } + case 0 => + val oldFirstWord = word(0) + val firstWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldFirstWord, 0) + anyChanges ||= firstWord != oldFirstWord + if (anyChanges) { + new BitSet2(firstWord, currentWord) + } else { + this + } + case _ => + val minimumNonZeroIndex: Int = i + 1 + while (!anyChanges && i >= 0) { + val oldWord = word(i) + currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + if (anyChanges) { + val newArray = elems.take(minimumNonZeroIndex + 1) + newArray(i + 1) = currentWord + while (i >= 0) { + newArray(i) = BitSetOps.computeWordForFilter(pred, isFlipped, word(i), i) + i -= 1 + } + new BitSetN(newArray) + } else { + this + } + } + } + + override def toBitMask: Array[Long] = elems.clone() + } + + @SerialVersionUID(3L) + private final class SerializationProxy(coll: BitSet) extends scala.collection.BitSet.SerializationProxy(coll) { + protected[this] def readResolve(): Any = BitSet.fromBitMaskNoCopy(elems) + } +} diff --git a/library/src/scala/collection/immutable/ChampCommon.scala b/library/src/scala/collection/immutable/ChampCommon.scala new file mode 100644 index 000000000000..7b3e3949a126 --- /dev/null +++ b/library/src/scala/collection/immutable/ChampCommon.scala @@ -0,0 +1,252 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.immutable + +import scala.collection.AbstractIterator +import java.lang.Integer.bitCount +import java.lang.Math.ceil +import java.lang.System.arraycopy + +private[collection] object Node { + final val HashCodeLength = 32 + + final val BitPartitionSize = 5 + + final val BitPartitionMask = (1 << BitPartitionSize) - 1 + + final val MaxDepth = ceil(HashCodeLength.toDouble / BitPartitionSize).toInt + + final val BranchingFactor = 1 << BitPartitionSize + + final def maskFrom(hash: Int, shift: Int): Int = (hash >>> shift) & BitPartitionMask + + final def bitposFrom(mask: Int): Int = 1 << mask + + final def indexFrom(bitmap: Int, bitpos: Int): Int = bitCount(bitmap & (bitpos - 1)) + + final def indexFrom(bitmap: Int, mask: Int, bitpos: Int): Int = if (bitmap == -1) mask else indexFrom(bitmap, bitpos) + +} + +private[collection] abstract class Node[T <: Node[T]] { + + def hasNodes: Boolean + + def nodeArity: Int + + def getNode(index: Int): T + + def hasPayload: Boolean + + def payloadArity: Int + + def getPayload(index: Int): Any + + def getHash(index: Int): Int + + def cachedJavaKeySetHashCode: Int + + private final def arrayIndexOutOfBounds(as: Array[_], ix:Int): ArrayIndexOutOfBoundsException = + new ArrayIndexOutOfBoundsException(s"$ix is out of bounds (min 0, max ${as.length-1}") + + protected final def removeElement(as: Array[Int], ix: Int): Array[Int] = { + if (ix < 0) throw arrayIndexOutOfBounds(as, ix) + if (ix > as.length - 1) throw arrayIndexOutOfBounds(as, ix) + val result = new Array[Int](as.length - 1) + arraycopy(as, 0, result, 0, ix) + arraycopy(as, ix + 1, result, ix, as.length - ix - 1) + result + } + + protected final def removeAnyElement(as: Array[Any], ix: Int): Array[Any] = { + if (ix < 0) throw arrayIndexOutOfBounds(as, ix) + if (ix > as.length - 1) throw arrayIndexOutOfBounds(as, ix) + val result = new Array[Any](as.length - 1) + arraycopy(as, 0, result, 0, ix) + arraycopy(as, ix + 1, result, ix, as.length - ix - 1) + result + } + + protected final def insertElement(as: Array[Int], ix: Int, elem: Int): Array[Int] = { + if (ix < 0) throw arrayIndexOutOfBounds(as, ix) + if (ix > as.length) throw arrayIndexOutOfBounds(as, ix) + val result = new Array[Int](as.length + 1) + arraycopy(as, 0, result, 0, ix) + result(ix) = elem + arraycopy(as, ix, result, ix + 1, as.length - ix) + result + } + protected final def insertAnyElement(as: Array[Any], ix: Int, elem: Int): Array[Any] = { + if (ix < 0) throw arrayIndexOutOfBounds(as, ix) + if (ix > as.length) throw arrayIndexOutOfBounds(as, ix) + val result = new Array[Any](as.length + 1) + arraycopy(as, 0, result, 0, ix) + result(ix) = elem + arraycopy(as, ix, result, ix + 1, as.length - ix) + result + } +} + +/** + * Base class for fixed-stack iterators that traverse a hash-trie. The iterator performs a + * depth-first pre-order traversal, which yields first all payload elements of the current + * node before traversing sub-nodes (left to right). + * + * @tparam T the trie node type we are iterating over + */ +private[immutable] abstract class ChampBaseIterator[A, T <: Node[T]] extends AbstractIterator[A] { + + import Node.MaxDepth + + // Note--this code is duplicated to a large extent both in + // ChampBaseReverseIterator and in convert.impl.ChampStepperBase. + // If you change this code, check those also in case they also + // need to be modified. + + protected var currentValueCursor: Int = 0 + protected var currentValueLength: Int = 0 + protected var currentValueNode: T = _ + + private[this] var currentStackLevel: Int = -1 + private[this] var nodeCursorsAndLengths: Array[Int] = _ + private[this] var nodes: Array[T] = _ + private def initNodes(): Unit = { + if (nodeCursorsAndLengths eq null) { + nodeCursorsAndLengths = new Array[Int](MaxDepth * 2) + nodes = new Array[Node[T]](MaxDepth).asInstanceOf[Array[T]] + } + } + + def this(rootNode: T) = { + this() + if (rootNode.hasNodes) pushNode(rootNode) + if (rootNode.hasPayload) setupPayloadNode(rootNode) + } + + private final def setupPayloadNode(node: T): Unit = { + currentValueNode = node + currentValueCursor = 0 + currentValueLength = node.payloadArity + } + + private final def pushNode(node: T): Unit = { + initNodes() + currentStackLevel = currentStackLevel + 1 + + val cursorIndex = currentStackLevel * 2 + val lengthIndex = currentStackLevel * 2 + 1 + + nodes(currentStackLevel) = node + nodeCursorsAndLengths(cursorIndex) = 0 + nodeCursorsAndLengths(lengthIndex) = node.nodeArity + } + + private final def popNode(): Unit = { + currentStackLevel = currentStackLevel - 1 + } + + /** + * Searches for next node that contains payload values, + * and pushes encountered sub-nodes on a stack for depth-first traversal. + */ + private final def searchNextValueNode(): Boolean = { + while (currentStackLevel >= 0) { + val cursorIndex = currentStackLevel * 2 + val lengthIndex = currentStackLevel * 2 + 1 + + val nodeCursor = nodeCursorsAndLengths(cursorIndex) + val nodeLength = nodeCursorsAndLengths(lengthIndex) + + if (nodeCursor < nodeLength) { + nodeCursorsAndLengths(cursorIndex) += 1 + + val nextNode = nodes(currentStackLevel).getNode(nodeCursor) + + if (nextNode.hasNodes) { pushNode(nextNode) } + if (nextNode.hasPayload) { setupPayloadNode(nextNode) ; return true } + } else { + popNode() + } + } + + return false + } + + final def hasNext = (currentValueCursor < currentValueLength) || searchNextValueNode() + +} + +/** + * Base class for fixed-stack iterators that traverse a hash-trie in reverse order. The base + * iterator performs a depth-first post-order traversal, traversing sub-nodes (right to left). + * + * @tparam T the trie node type we are iterating over + */ +private[immutable] abstract class ChampBaseReverseIterator[A, T <: Node[T]] extends AbstractIterator[A] { + + import Node.MaxDepth + + protected var currentValueCursor: Int = -1 + protected var currentValueNode: T = _ + + private[this] var currentStackLevel: Int = -1 + private[this] val nodeIndex: Array[Int] = new Array[Int](MaxDepth + 1) + private[this] val nodeStack: Array[T] = new Array[Node[T]](MaxDepth + 1).asInstanceOf[Array[T]] + + def this(rootNode: T) = { + this() + pushNode(rootNode) + searchNextValueNode() + } + + private final def setupPayloadNode(node: T): Unit = { + currentValueNode = node + currentValueCursor = node.payloadArity - 1 + } + + private final def pushNode(node: T): Unit = { + currentStackLevel = currentStackLevel + 1 + + nodeStack(currentStackLevel) = node + nodeIndex(currentStackLevel) = node.nodeArity - 1 + } + + private final def popNode(): Unit = { + currentStackLevel = currentStackLevel - 1 + } + + /** + * Searches for rightmost node that contains payload values, + * and pushes encountered sub-nodes on a stack for depth-first traversal. + */ + private final def searchNextValueNode(): Boolean = { + while (currentStackLevel >= 0) { + val nodeCursor = nodeIndex(currentStackLevel) ; nodeIndex(currentStackLevel) = nodeCursor - 1 + + if (nodeCursor >= 0) { + val nextNode = nodeStack(currentStackLevel).getNode(nodeCursor) + pushNode(nextNode) + } else { + val currNode = nodeStack(currentStackLevel) + popNode() + + if (currNode.hasPayload) { setupPayloadNode(currNode) ; return true } + } + } + + return false + } + + final def hasNext = (currentValueCursor >= 0) || searchNextValueNode() + +} diff --git a/library/src/scala/collection/immutable/HashMap.scala b/library/src/scala/collection/immutable/HashMap.scala new file mode 100644 index 000000000000..e9257f1948fc --- /dev/null +++ b/library/src/scala/collection/immutable/HashMap.scala @@ -0,0 +1,2422 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.immutable + +import java.lang.Integer.bitCount +import java.lang.System.arraycopy + +import scala.annotation.unchecked.{uncheckedVariance => uV} +import scala.collection.Hashing.improve +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializable +import scala.collection.mutable, mutable.ReusableBuilder +import scala.collection.{Iterator, MapFactory, MapFactoryDefaults, Stepper, StepperShape, mutable} +import scala.runtime.AbstractFunction2 +import scala.runtime.Statics.releaseFence +import scala.util.hashing.MurmurHash3 + +/** This class implements immutable maps using a Compressed Hash-Array Mapped Prefix-tree. + * See paper https://michael.steindorfer.name/publications/oopsla15.pdf for more details. + * + * @tparam K the type of the keys contained in this hash set. + * @tparam V the type of the values associated with the keys in this hash map. + * + * @define Coll `immutable.HashMap` + * @define coll immutable champ hash map + */ + +final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: BitmapIndexedMapNode[K, V]) + extends AbstractMap[K, V] + with StrictOptimizedMapOps[K, V, HashMap, HashMap[K, V]] + with MapFactoryDefaults[K, V, HashMap, Iterable] + with DefaultSerializable { + + def this() = this(MapNode.empty) + + // This release fence is present because rootNode may have previously been mutated during construction. + releaseFence() + + override def mapFactory: MapFactory[HashMap] = HashMap + + override def knownSize: Int = rootNode.size + + override def size: Int = rootNode.size + + override def isEmpty: Boolean = rootNode.size == 0 + + override def keySet: Set[K] = if (size == 0) Set.empty else new HashKeySet + + private[immutable] final class HashKeySet extends ImmutableKeySet { + + private[this] def newKeySetOrThis(newHashMap: HashMap[K, _]): Set[K] = + if (newHashMap eq HashMap.this) this else newHashMap.keySet + private[this] def newKeySetOrThis(newRootNode: BitmapIndexedMapNode[K, _]): Set[K] = + if (newRootNode eq rootNode) this else new HashMap(newRootNode).keySet + + override def incl(elem: K): Set[K] = { + val originalHash = elem.## + val improvedHash = improve(originalHash) + val newNode = rootNode.updated(elem, null.asInstanceOf[V], originalHash, improvedHash, 0, replaceValue = false) + newKeySetOrThis(newNode) + } + override def excl(elem: K): Set[K] = newKeySetOrThis(HashMap.this - elem) + override def filter(pred: K => Boolean): Set[K] = newKeySetOrThis(HashMap.this.filter(kv => pred(kv._1))) + override def filterNot(pred: K => Boolean): Set[K] = newKeySetOrThis(HashMap.this.filterNot(kv => pred(kv._1))) + } + + def iterator: Iterator[(K, V)] = { + if (isEmpty) Iterator.empty + else new MapKeyValueTupleIterator[K, V](rootNode) + } + + override def keysIterator: Iterator[K] = { + if (isEmpty) Iterator.empty + else new MapKeyIterator[K, V](rootNode) + } + override def valuesIterator: Iterator[V] = { + if (isEmpty) Iterator.empty + else new MapValueIterator[K, V](rootNode) + } + + protected[immutable] def reverseIterator: Iterator[(K, V)] = { + if (isEmpty) Iterator.empty + else new MapKeyValueTupleReverseIterator[K, V](rootNode) + } + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[(K, V), S]): S with EfficientSplit = + shape. + parUnbox(collection.convert.impl.AnyChampStepper.from[(K, V), MapNode[K, V]](size, rootNode, (node, i) => node.getPayload(i))) + + override def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S with EfficientSplit = { + import collection.convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => IntChampStepper.from[ MapNode[K, V]](size, rootNode, (node, i) => node.getKey(i).asInstanceOf[Int]) + case StepperShape.LongShape => LongChampStepper.from[ MapNode[K, V]](size, rootNode, (node, i) => node.getKey(i).asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleChampStepper.from[MapNode[K, V]](size, rootNode, (node, i) => node.getKey(i).asInstanceOf[Double]) + case _ => shape.parUnbox(AnyChampStepper.from[K, MapNode[K, V]](size, rootNode, (node, i) => node.getKey(i))) + } + s.asInstanceOf[S with EfficientSplit] + } + + override def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S with EfficientSplit = { + import collection.convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => IntChampStepper.from[ MapNode[K, V]](size, rootNode, (node, i) => node.getValue(i).asInstanceOf[Int]) + case StepperShape.LongShape => LongChampStepper.from[ MapNode[K, V]](size, rootNode, (node, i) => node.getValue(i).asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleChampStepper.from[MapNode[K, V]](size, rootNode, (node, i) => node.getValue(i).asInstanceOf[Double]) + case _ => shape.parUnbox(AnyChampStepper.from[V, MapNode[K, V]](size, rootNode, (node, i) => node.getValue(i))) + } + s.asInstanceOf[S with EfficientSplit] + } + + override final def contains(key: K): Boolean = { + val keyUnimprovedHash = key.## + val keyHash = improve(keyUnimprovedHash) + rootNode.containsKey(key, keyUnimprovedHash, keyHash, 0) + } + + override def apply(key: K): V = { + val keyUnimprovedHash = key.## + val keyHash = improve(keyUnimprovedHash) + rootNode.apply(key, keyUnimprovedHash, keyHash, 0) + } + + def get(key: K): Option[V] = { + val keyUnimprovedHash = key.## + val keyHash = improve(keyUnimprovedHash) + rootNode.get(key, keyUnimprovedHash, keyHash, 0) + } + + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + val keyUnimprovedHash = key.## + val keyHash = improve(keyUnimprovedHash) + rootNode.getOrElse(key, keyUnimprovedHash, keyHash, 0, default) + } + + @inline private[this] def newHashMapOrThis[V1 >: V](newRootNode: BitmapIndexedMapNode[K, V1]): HashMap[K, V1] = + if (newRootNode eq rootNode) this else new HashMap(newRootNode) + + def updated[V1 >: V](key: K, value: V1): HashMap[K, V1] = { + val keyUnimprovedHash = key.## + newHashMapOrThis(rootNode.updated(key, value, keyUnimprovedHash, improve(keyUnimprovedHash), 0, replaceValue = true)) + } + + // preemptively overridden in anticipation of performance optimizations + override def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): HashMap[K, V1] = + super.updatedWith[V1](key)(remappingFunction) + + def removed(key: K): HashMap[K, V] = { + val keyUnimprovedHash = key.## + newHashMapOrThis(rootNode.removed(key, keyUnimprovedHash, improve(keyUnimprovedHash), 0)) + } + + override def concat[V1 >: V](that: scala.IterableOnce[(K, V1)]): HashMap[K, V1] = that match { + case hm: HashMap[K, V1] => + if (isEmpty) hm + else { + val newNode = rootNode.concat(hm.rootNode, 0) + if (newNode eq hm.rootNode) hm + else newHashMapOrThis(newNode) + } + case hm: mutable.HashMap[K @unchecked, V @unchecked] => + val iter = hm.nodeIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = hm.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, next.value, originalHash, improved, 0, replaceValue = true) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + + while (iter.hasNext) { + val next = iter.next() + val originalHash = hm.unimproveHash(next.hash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, next.value, originalHash, improve(originalHash), 0, shallowlyMutableNodeMap) + } + return new HashMap(current) + } + } + this + case lhm: mutable.LinkedHashMap[K @unchecked, V @unchecked] => + val iter = lhm.entryIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, next.value, originalHash, improved, 0, replaceValue = true) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, next.value, originalHash, improve(originalHash), 0, shallowlyMutableNodeMap) + } + return new HashMap(current) + } + } + this + case _ => + class accum extends AbstractFunction2[K, V1, Unit] with Function1[(K, V1), Unit] { + var changed = false + var shallowlyMutableNodeMap: Int = 0 + var current: BitmapIndexedMapNode[K, V1] = rootNode + def apply(kv: (K, V1)) = apply(kv._1, kv._2) + def apply(key: K, value: V1): Unit = { + val originalHash = key.## + val improved = improve(originalHash) + if (!changed) { + current = current.updated(key, value, originalHash, improved, 0, replaceValue = true) + if (current ne rootNode) { + // Note: We could have started with shallowlyMutableNodeMap = 0, however this way, in the case that + // the first changed key ended up in a subnode beneath root, we mark that root right away as being + // shallowly mutable. + // + // since key->value has just been inserted, and certainly caused a new root node to be created, we can say with + // certainty that it either caused a new subnode to be created underneath `current`, in which case we should + // carry on mutating that subnode, or it ended up as a child data pair of the root, in which case, no harm is + // done by including its bit position in the shallowlyMutableNodeMap anyways. + changed = true + shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + } + } else { + shallowlyMutableNodeMap = current.updateWithShallowMutations(key, value, originalHash, improved, 0, shallowlyMutableNodeMap) + } + } + } + that match { + case thatMap: Map[K, V1] => + if (thatMap.isEmpty) this + else { + val accum = new accum + thatMap.foreachEntry(accum) + newHashMapOrThis(accum.current) + } + case _ => + val it = that.iterator + if (it.isEmpty) this + else { + val accum = new accum + it.foreach(accum) + newHashMapOrThis(accum.current) + } + } + } + + override def tail: HashMap[K, V] = this - head._1 + + override def init: HashMap[K, V] = this - last._1 + + override def head: (K, V) = iterator.next() + + override def last: (K, V) = reverseIterator.next() + + override def foreach[U](f: ((K, V)) => U): Unit = rootNode.foreach(f) + + override def foreachEntry[U](f: (K, V) => U): Unit = rootNode.foreachEntry(f) + + /** Applies a function to each key, value, and **original** hash value in this Map */ + @inline private[collection] def foreachWithHash(f: (K, V, Int) => Unit): Unit = rootNode.foreachWithHash(f) + + override def equals(that: Any): Boolean = + that match { + case map: HashMap[_, _] => (this eq map) || (this.rootNode == map.rootNode) + case _ => super.equals(that) + } + + override def hashCode(): Int = { + if (isEmpty) MurmurHash3.emptyMapHash + else { + // Optimized to avoid recomputation of key hashcodes as these are cached in the nodes and can be assumed to be + // immutable. + val hashIterator = new MapKeyValueTupleHashIterator(rootNode) + val hash = MurmurHash3.unorderedHash(hashIterator, MurmurHash3.mapSeed) + // assert(hash == super.hashCode()) + hash + } + } + + override protected[this] def className = "HashMap" + + /** Merges this HashMap with an other HashMap by combining all key-value pairs of both maps, and delegating to a merge + * function to resolve any key collisions between the two HashMaps. + * + * @example {{{ + * val left = HashMap(1 -> 1, 2 -> 1) + * val right = HashMap(2 -> 2, 3 -> 2) + * + * val merged = left.merged(right){ case ((k0, v0), (k1, v1)) => (k0 + k1) -> (v0 + v1) } + * // HashMap(1 -> 1, 3 -> 2, 4 -> 3) + * + * }}} + * + * @param that the HashMap to merge this HashMap with + * @param mergef the merge function which resolves collisions between the two HashMaps. If `mergef` is null, then + * keys from `this` will overwrite keys from `that`, making the behaviour equivalent to + * `that.concat(this)` + * + * @note In cases where `mergef` returns keys which themselves collide with other keys returned by `merge`, or + * found in `this` or `that`, it is not defined which value will be chosen. For example: + * + * Colliding multiple results of merging: + * {{{ + * // key `3` collides between a result of merging keys `1` and `2` + * val left = HashMap(1 -> 1, 2 -> 2) + * val right = HashMap(1 -> 1, 2 -> 2) + * + * val merged = left.merged(right){ case (_, (_, v1)) => 3 -> v1 } + * // HashMap(3 -> 2) is returned, but it could also have returned HashMap(3 -> 1) + * }}} + * Colliding results of merging with other keys: + * {{{ + * // key `2` collides between a result of merging `1`, and existing key `2` + * val left = HashMap(1 -> 1, 2 -> 1) + * val right = HashMap(1 -> 2) + * + * val merged = left.merged(right)((_,_) => 2 -> 3) + * // HashMap(2 -> 1) is returned, but it could also have returned HashMap(2 -> 3) + * }}} + * + */ + def merged[V1 >: V](that: HashMap[K, V1])(mergef: ((K, V), (K, V1)) => (K, V1)): HashMap[K, V1] = + if (mergef == null) { + that ++ this + } else { + if (isEmpty) that + else if (that.isEmpty) this + else if (size == 1) { + val payload@(k, v) = rootNode.getPayload(0) + val originalHash = rootNode.getHash(0) + val improved = improve(originalHash) + + if (that.rootNode.containsKey(k, originalHash, improved, 0)) { + val thatPayload = that.rootNode.getTuple(k, originalHash, improved, 0) + val (mergedK, mergedV) = mergef(payload, thatPayload) + val mergedOriginalHash = mergedK.## + val mergedImprovedHash = improve(mergedOriginalHash) + new HashMap(that.rootNode.removed(thatPayload._1, originalHash, improved, 0).updated(mergedK, mergedV, mergedOriginalHash, mergedImprovedHash, 0, replaceValue = true)) + } else { + new HashMap(that.rootNode.updated(k, v, originalHash, improved, 0, replaceValue = true)) + } + } else if (that.size == 0) { + val thatPayload@(k, v) = rootNode.getPayload(0) + val thatOriginalHash = rootNode.getHash(0) + val thatImproved = improve(thatOriginalHash) + + if (rootNode.containsKey(k, thatOriginalHash, thatImproved, 0)) { + val payload = rootNode.getTuple(k, thatOriginalHash, thatImproved, 0) + val (mergedK, mergedV) = mergef(payload, thatPayload) + val mergedOriginalHash = mergedK.## + val mergedImprovedHash = improve(mergedOriginalHash) + new HashMap(rootNode.updated(mergedK, mergedV, mergedOriginalHash, mergedImprovedHash, 0, replaceValue = true)) + } else { + new HashMap(rootNode.updated(k, v, thatOriginalHash, thatImproved, 0, replaceValue = true)) + } + } else { + val builder = new HashMapBuilder[K, V1] + rootNode.mergeInto(that.rootNode, builder, 0)(mergef) + builder.result() + } + } + + override def transform[W](f: (K, V) => W): HashMap[K, W] = + newHashMapOrThis(rootNode.transform[Any](f)).asInstanceOf[HashMap[K, W]] + + override protected[collection] def filterImpl(pred: ((K, V)) => Boolean, isFlipped: Boolean): HashMap[K, V] = { + val newRootNode = rootNode.filterImpl(pred, isFlipped) + if (newRootNode eq rootNode) this + else if (newRootNode.size == 0) HashMap.empty + else new HashMap(newRootNode) + } + + override def removedAll(keys: IterableOnce[K]): HashMap[K, V] = { + if (isEmpty) { + this + } else { + keys match { + case hashSet: HashSet[K] => + if (hashSet.isEmpty) { + this + } else { + // TODO: Remove all keys from the hashSet in a sub-linear fashion by only visiting the nodes in the tree + // This can be a direct port of the implementation of `SetNode[A]#diff(SetNode[A])` + val newRootNode = new MapNodeRemoveAllSetNodeIterator(hashSet.rootNode).removeAll(rootNode) + if (newRootNode eq rootNode) this + else if (newRootNode.size <= 0) HashMap.empty + else new HashMap(newRootNode) + } + case hashSet: collection.mutable.HashSet[K] => + if (hashSet.isEmpty) { + this + } else { + val iter = hashSet.nodeIterator + var curr = rootNode + + while (iter.hasNext) { + val next = iter.next() + val originalHash = hashSet.unimproveHash(next.hash) + val improved = improve(originalHash) + curr = curr.removed(next.key, originalHash, improved, 0) + if (curr.size == 0) { + return HashMap.empty + } + } + newHashMapOrThis(curr) + } + case lhashSet: collection.mutable.LinkedHashSet[K] => + if (lhashSet.isEmpty) { + this + } else { + val iter = lhashSet.entryIterator + var curr = rootNode + + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhashSet.unimproveHash(next.hash) + val improved = improve(originalHash) + curr = curr.removed(next.key, originalHash, improved, 0) + if (curr.size == 0) { + return HashMap.empty + } + } + newHashMapOrThis(curr) + } + case _ => + val iter = keys.iterator + var curr = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = next.## + val improved = improve(originalHash) + curr = curr.removed(next, originalHash, improved, 0) + if (curr.size == 0) { + return HashMap.empty + } + } + newHashMapOrThis(curr) + } + } + } + + override def partition(p: ((K, V)) => Boolean): (HashMap[K, V], HashMap[K, V]) = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `partition` could be optimized to traverse the trie node-by-node, splitting each node into two, + // based on the result of applying `p` to its elements and subnodes. + super.partition(p) + } + + override def take(n: Int): HashMap[K, V] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `take` could be optimized to construct a new trie structure by visiting each node, and including + // those nodes in the resulting trie, until `n` total elements have been included. + super.take(n) + } + + override def takeRight(n: Int): HashMap[K, V] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `take` could be optimized to construct a new trie structure by visiting each node in reverse, and + // and including those nodes in the resulting trie, until `n` total elements have been included. + super.takeRight(n) + } + + override def takeWhile(p: ((K, V)) => Boolean): HashMap[K, V] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `takeWhile` could be optimized to construct a new trie structure by visiting each node, and + // including those nodes in the resulting trie, until `p` returns `false` + super.takeWhile(p) + } + + override def dropWhile(p: ((K, V)) => Boolean): HashMap[K, V] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `dropWhile` could be optimized to construct a new trie structure by visiting each node, and + // dropping those nodes in the resulting trie, until `p` returns `true` + super.dropWhile(p) + } + + override def dropRight(n: Int): HashMap[K, V] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `dropRight` could be optimized to construct a new trie structure by visiting each node, in reverse + // order, and dropping all nodes until `n` elements have been dropped + super.dropRight(n) + } + + override def drop(n: Int): HashMap[K, V] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `dropRight` could be optimized to construct a new trie structure by visiting each node, and + // dropping all nodes until `n` elements have been dropped + super.drop(n) + } + + override def span(p: ((K, V)) => Boolean): (HashMap[K, V], HashMap[K, V]) = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + // + // In particular, `scan` could be optimized to construct a new trie structure by visiting each node, and + // keeping each node and element until `p` returns false, then including the remaining nodes in the second result. + // This would avoid having to rebuild most of the trie, and would eliminate the need to perform hashing and equality + // checks. + super.span(p) + } + +} + +private[immutable] object MapNode { + + private final val EmptyMapNode = new BitmapIndexedMapNode(0, 0, Array.empty, Array.empty, 0, 0) + + def empty[K, V]: BitmapIndexedMapNode[K, V] = EmptyMapNode.asInstanceOf[BitmapIndexedMapNode[K, V]] + + final val TupleLength = 2 + +} + + +private[immutable] sealed abstract class MapNode[K, +V] extends Node[MapNode[K, V @uV]] { + def apply(key: K, originalHash: Int, hash: Int, shift: Int): V + + def get(key: K, originalHash: Int, hash: Int, shift: Int): Option[V] + + def getOrElse[V1 >: V](key: K, originalHash: Int, hash: Int, shift: Int, f: => V1): V1 + + def containsKey(key: K, originalHash: Int, hash: Int, shift: Int): Boolean + + /** Returns a MapNode with the passed key-value assignment added + * + * @param key the key to add to the MapNode + * @param value the value to associate with `key` + * @param originalHash the original hash of `key` + * @param hash the improved hash of `key` + * @param shift the shift of the node (distanceFromRoot * BitPartitionSize) + * @param replaceValue if true, then the value currently associated to `key` will be replaced with the passed value + * argument. + * if false, then the key will be inserted if not already present, however if the key is present + * then the passed value will not replace the current value. That is, if `false`, then this + * method has `update if not exists` semantics. + */ + def updated[V1 >: V](key: K, value: V1, originalHash: Int, hash: Int, shift: Int, replaceValue: Boolean): MapNode[K, V1] + + def removed[V1 >: V](key: K, originalHash: Int, hash: Int, shift: Int): MapNode[K, V1] + + def hasNodes: Boolean + + def nodeArity: Int + + def getNode(index: Int): MapNode[K, V] + + def hasPayload: Boolean + + def payloadArity: Int + + def getKey(index: Int): K + + def getValue(index: Int): V + + def getPayload(index: Int): (K, V) + + def size: Int + + def foreach[U](f: ((K, V)) => U): Unit + + def foreachEntry[U](f: (K, V) => U): Unit + + def foreachWithHash(f: (K, V, Int) => Unit): Unit + + def transform[W](f: (K, V) => W): MapNode[K, W] + + def copy(): MapNode[K, V] + + def concat[V1 >: V](that: MapNode[K, V1], shift: Int): MapNode[K, V1] + + def filterImpl(pred: ((K, V)) => Boolean, isFlipped: Boolean): MapNode[K, V] + + /** Merges this node with that node, adding each resulting tuple to `builder` + * + * `this` should be a node from `left` hashmap in `left.merged(right)(mergef)` + * + * @param that node from the "right" HashMap. Must also be at the same "path" or "position" within the right tree, + * as `this` is, within the left tree + */ + def mergeInto[V1 >: V](that: MapNode[K, V1], builder: HashMapBuilder[K, V1], shift: Int)(mergef: ((K, V), (K, V1)) => (K, V1)): Unit + + /** Returns the exact (equal by reference) key, and value, associated to a given key. + * If the key is not bound to a value, then an exception is thrown + */ + def getTuple(key: K, originalHash: Int, hash: Int, shift: Int): (K, V) + + /** Adds all key-value pairs to a builder */ + def buildTo[V1 >: V](builder: HashMapBuilder[K, V1]): Unit +} + +private final class BitmapIndexedMapNode[K, +V]( + var dataMap: Int, + var nodeMap: Int, + var content: Array[Any], + var originalHashes: Array[Int], + var size: Int, + var cachedJavaKeySetHashCode: Int) extends MapNode[K, V] { + + releaseFence() + + import MapNode._ + import Node._ + + /* + assert(checkInvariantContentIsWellTyped()) + assert(checkInvariantSubNodesAreCompacted()) + + private final def checkInvariantSubNodesAreCompacted(): Boolean = + new MapKeyValueTupleIterator[K, V](this).size - payloadArity >= 2 * nodeArity + + private final def checkInvariantContentIsWellTyped(): Boolean = { + val predicate1 = TupleLength * payloadArity + nodeArity == content.length + + val predicate2 = Range(0, TupleLength * payloadArity) + .forall(i => content(i).isInstanceOf[MapNode[_, _]] == false) + + val predicate3 = Range(TupleLength * payloadArity, content.length) + .forall(i => content(i).isInstanceOf[MapNode[_, _]] == true) + + predicate1 && predicate2 && predicate3 + } + */ + + def getKey(index: Int): K = content(TupleLength * index).asInstanceOf[K] + def getValue(index: Int): V = content(TupleLength * index + 1).asInstanceOf[V] + + def getPayload(index: Int) = Tuple2( + content(TupleLength * index).asInstanceOf[K], + content(TupleLength * index + 1).asInstanceOf[V]) + + override def getHash(index: Int): Int = originalHashes(index) + + def getNode(index: Int): MapNode[K, V] = + content(content.length - 1 - index).asInstanceOf[MapNode[K, V]] + + def apply(key: K, originalHash: Int, keyHash: Int, shift: Int): V = { + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + if (key == getKey(index)) getValue(index) else throw new NoSuchElementException(s"key not found: $key") + } else if ((nodeMap & bitpos) != 0) { + getNode(indexFrom(nodeMap, mask, bitpos)).apply(key, originalHash, keyHash, shift + BitPartitionSize) + } else { + throw new NoSuchElementException(s"key not found: $key") + } + } + + def get(key: K, originalHash: Int, keyHash: Int, shift: Int): Option[V] = { + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val key0 = this.getKey(index) + if (key == key0) Some(this.getValue(index)) else None + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + this.getNode(index).get(key, originalHash, keyHash, shift + BitPartitionSize) + } else { + None + } + } + + override def getTuple(key: K, originalHash: Int, hash: Int, shift: Int): (K, V) = { + val mask = maskFrom(hash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val payload = getPayload(index) + if (key == payload._1) payload else Iterator.empty.next() + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + getNode(index).getTuple(key, originalHash, hash, shift + BitPartitionSize) + } else { + Iterator.empty.next() + } + } + + def getOrElse[V1 >: V](key: K, originalHash: Int, keyHash: Int, shift: Int, f: => V1): V1 = { + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val key0 = this.getKey(index) + if (key == key0) getValue(index) else f + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + this.getNode(index).getOrElse(key, originalHash, keyHash, shift + BitPartitionSize, f) + } else { + f + } + } + + override def containsKey(key: K, originalHash: Int, keyHash: Int, shift: Int): Boolean = { + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + // assert(hashes(index) == computeHash(this.getKey(index)), (hashes.toSeq, content.toSeq, index, key, keyHash, shift)) + (originalHashes(index) == originalHash) && key == getKey(index) + } else if ((nodeMap & bitpos) != 0) { + getNode(indexFrom(nodeMap, mask, bitpos)).containsKey(key, originalHash, keyHash, shift + BitPartitionSize) + } else { + false + } + } + + + def updated[V1 >: V](key: K, value: V1, originalHash: Int, keyHash: Int, shift: Int, replaceValue: Boolean): BitmapIndexedMapNode[K, V1] = { + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val key0 = getKey(index) + val key0UnimprovedHash = getHash(index) + if (key0UnimprovedHash == originalHash && key0 == key) { + if (replaceValue) { + val value0 = this.getValue(index) + if ((key0.asInstanceOf[AnyRef] eq key.asInstanceOf[AnyRef]) && (value0.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef])) + this + else copyAndSetValue(bitpos, key, value) + } else this + } else { + val value0 = this.getValue(index) + val key0Hash = improve(key0UnimprovedHash) + val subNodeNew = mergeTwoKeyValPairs(key0, value0, key0UnimprovedHash, key0Hash, key, value, originalHash, keyHash, shift + BitPartitionSize) + + copyAndMigrateFromInlineToNode(bitpos, key0Hash, subNodeNew) + } + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + val subNode = this.getNode(index) + val subNodeNew = subNode.updated(key, value, originalHash, keyHash, shift + BitPartitionSize, replaceValue) + + if (subNodeNew eq subNode) this else copyAndSetNode(bitpos, subNode, subNodeNew) + } else copyAndInsertValue(bitpos, key, originalHash, keyHash, value) + } + + /** A variant of `updated` which performs shallow mutations on the root (`this`), and if possible, on immediately + * descendant child nodes (only one level beneath `this`) + * + * The caller should pass a bitmap of child nodes of this node, which this method may mutate. + * If this method may mutate a child node, then if the updated key-value belongs in that child node, it will + * be shallowly mutated (its children will not be mutated). + * + * If instead this method may not mutate the child node in which the to-be-updated key-value pair belongs, then + * that child will be updated immutably, but the result will be mutably re-inserted as a child of this node. + * + * @param key the key to update + * @param value the value to set `key` to + * @param originalHash key.## + * @param keyHash the improved hash + * @param shallowlyMutableNodeMap bitmap of child nodes of this node, which can be shallowly mutated + * during the call to this method + * + * @return Int which is the bitwise OR of shallowlyMutableNodeMap and any freshly created nodes, which will be + * available for mutations in subsequent calls. + */ + def updateWithShallowMutations[V1 >: V](key: K, value: V1, originalHash: Int, keyHash: Int, shift: Int, shallowlyMutableNodeMap: Int): Int = { + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val key0 = getKey(index) + val key0UnimprovedHash = getHash(index) + if (key0UnimprovedHash == originalHash && key0 == key) { + val value0 = this.getValue(index) + if (!((key0.asInstanceOf[AnyRef] eq key.asInstanceOf[AnyRef]) && (value0.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]))) { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + content(idx + 1) = value + } + shallowlyMutableNodeMap + } else { + val value0 = this.getValue(index) + val key0Hash = improve(key0UnimprovedHash) + + val subNodeNew = mergeTwoKeyValPairs(key0, value0, key0UnimprovedHash, key0Hash, key, value, originalHash, keyHash, shift + BitPartitionSize) + migrateFromInlineToNodeInPlace(bitpos, key0Hash, subNodeNew) + shallowlyMutableNodeMap | bitpos + } + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + val subNode = this.getNode(index) + val subNodeSize = subNode.size + val subNodeHashCode = subNode.cachedJavaKeySetHashCode + + var returnMutableNodeMap = shallowlyMutableNodeMap + + val subNodeNew: MapNode[K, V1] = subNode match { + case subNodeBm: BitmapIndexedMapNode[K, V] if (bitpos & shallowlyMutableNodeMap) != 0 => + subNodeBm.updateWithShallowMutations(key, value, originalHash, keyHash, shift + BitPartitionSize, 0) + subNodeBm + case _ => + val result = subNode.updated(key, value, originalHash, keyHash, shift + BitPartitionSize, replaceValue = true) + if (result ne subNode) { + returnMutableNodeMap |= bitpos + } + result + } + + this.content(this.content.length - 1 - this.nodeIndex(bitpos)) = subNodeNew + this.size = this.size - subNodeSize + subNodeNew.size + this.cachedJavaKeySetHashCode = this.cachedJavaKeySetHashCode - subNodeHashCode + subNodeNew.cachedJavaKeySetHashCode + returnMutableNodeMap + } else { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length + TupleLength) + + // copy 'src' and insert 2 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + dst(idx) = key + dst(idx + 1) = value + arraycopy(src, idx, dst, idx + TupleLength, src.length - idx) + + this.dataMap |= bitpos + this.content = dst + this.originalHashes = insertElement(originalHashes, dataIx, originalHash) + this.size += 1 + this.cachedJavaKeySetHashCode += keyHash + shallowlyMutableNodeMap + } + } + + def removed[V1 >: V](key: K, originalHash: Int, keyHash: Int, shift: Int): BitmapIndexedMapNode[K, V1] = { + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val key0 = this.getKey(index) + + if (key0 == key) { + if (this.payloadArity == 2 && this.nodeArity == 0) { + /* + * Create new node with remaining pair. The new node will a) either become the new root + * returned, or b) unwrapped and inlined during returning. + */ + val newDataMap = if (shift == 0) (dataMap ^ bitpos) else bitposFrom(maskFrom(keyHash, 0)) + if (index == 0) + new BitmapIndexedMapNode[K, V1](newDataMap, 0, Array(getKey(1), getValue(1)), Array(originalHashes(1)), 1, improve(getHash(1))) + else + new BitmapIndexedMapNode[K, V1](newDataMap, 0, Array(getKey(0), getValue(0)), Array(originalHashes(0)), 1, improve(getHash(0))) + } else copyAndRemoveValue(bitpos, keyHash) + } else this + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + val subNode = this.getNode(index) + + val subNodeNew = subNode.removed(key, originalHash, keyHash, shift + BitPartitionSize) + // assert(subNodeNew.size != 0, "Sub-node must have at least one element.") + + if (subNodeNew eq subNode) return this + + // cache just in case subNodeNew is a hashCollision node, in which in which case a little arithmetic is avoided + // in Vector#length + val subNodeNewSize = subNodeNew.size + + if (subNodeNewSize == 1) { + if (this.size == subNode.size) { + // subNode is the only child (no other data or node children of `this` exist) + // escalate (singleton or empty) result + subNodeNew.asInstanceOf[BitmapIndexedMapNode[K, V]] + } else { + // inline value (move to front) + copyAndMigrateFromNodeToInline(bitpos, subNode, subNodeNew) + } + } else if (subNodeNewSize > 1) { + // modify current node (set replacement node) + copyAndSetNode(bitpos, subNode, subNodeNew) + } else this + } else this + } + + def mergeTwoKeyValPairs[V1 >: V](key0: K, value0: V1, originalHash0: Int, keyHash0: Int, key1: K, value1: V1, originalHash1: Int, keyHash1: Int, shift: Int): MapNode[K, V1] = { + // assert(key0 != key1) + + if (shift >= HashCodeLength) { + new HashCollisionMapNode[K, V1](originalHash0, keyHash0, Vector((key0, value0), (key1, value1))) + } else { + val mask0 = maskFrom(keyHash0, shift) + val mask1 = maskFrom(keyHash1, shift) + val newCachedHash = keyHash0 + keyHash1 + + if (mask0 != mask1) { + // unique prefixes, payload fits on same level + val dataMap = bitposFrom(mask0) | bitposFrom(mask1) + + if (mask0 < mask1) { + new BitmapIndexedMapNode[K, V1](dataMap, 0, Array(key0, value0, key1, value1), Array(originalHash0, originalHash1), 2, newCachedHash) + } else { + new BitmapIndexedMapNode[K, V1](dataMap, 0, Array(key1, value1, key0, value0), Array(originalHash1, originalHash0), 2, newCachedHash) + } + } else { + // identical prefixes, payload must be disambiguated deeper in the trie + val nodeMap = bitposFrom(mask0) + val node = mergeTwoKeyValPairs(key0, value0, originalHash0, keyHash0, key1, value1, originalHash1, keyHash1, shift + BitPartitionSize) + new BitmapIndexedMapNode[K, V1](0, nodeMap, Array(node), Array.emptyIntArray, node.size, node.cachedJavaKeySetHashCode) + } + } + } + + def hasNodes: Boolean = nodeMap != 0 + + def nodeArity: Int = bitCount(nodeMap) + + def hasPayload: Boolean = dataMap != 0 + + def payloadArity: Int = bitCount(dataMap) + + def dataIndex(bitpos: Int) = bitCount(dataMap & (bitpos - 1)) + + def nodeIndex(bitpos: Int) = bitCount(nodeMap & (bitpos - 1)) + + def copyAndSetValue[V1 >: V](bitpos: Int, newKey: K, newValue: V1): BitmapIndexedMapNode[K, V1] = { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length) + + // copy 'src' and set 1 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, src.length) + //dst(idx) = newKey + dst(idx + 1) = newValue + new BitmapIndexedMapNode[K, V1](dataMap, nodeMap, dst, originalHashes, size, cachedJavaKeySetHashCode) + } + + def copyAndSetNode[V1 >: V](bitpos: Int, oldNode: MapNode[K, V1], newNode: MapNode[K, V1]): BitmapIndexedMapNode[K, V1] = { + val idx = this.content.length - 1 - this.nodeIndex(bitpos) + + val src = this.content + val dst = new Array[Any](src.length) + + // copy 'src' and set 1 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, src.length) + dst(idx) = newNode + new BitmapIndexedMapNode[K, V1]( + dataMap, + nodeMap, + dst, + originalHashes, + size - oldNode.size + newNode.size, + cachedJavaKeySetHashCode - oldNode.cachedJavaKeySetHashCode + newNode.cachedJavaKeySetHashCode + ) + } + + def copyAndInsertValue[V1 >: V](bitpos: Int, key: K, originalHash: Int, keyHash: Int, value: V1): BitmapIndexedMapNode[K, V1] = { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length + TupleLength) + + // copy 'src' and insert 2 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + dst(idx) = key + dst(idx + 1) = value + arraycopy(src, idx, dst, idx + TupleLength, src.length - idx) + + val dstHashes = insertElement(originalHashes, dataIx, originalHash) + + new BitmapIndexedMapNode[K, V1](dataMap | bitpos, nodeMap, dst, dstHashes, size + 1, cachedJavaKeySetHashCode + keyHash) + } + + def copyAndRemoveValue(bitpos: Int, keyHash: Int): BitmapIndexedMapNode[K, V] = { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length - TupleLength) + + // copy 'src' and remove 2 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + arraycopy(src, idx + TupleLength, dst, idx, src.length - idx - TupleLength) + + val dstHashes = removeElement(originalHashes, dataIx) + + new BitmapIndexedMapNode[K, V](dataMap ^ bitpos, nodeMap, dst, dstHashes, size - 1, cachedJavaKeySetHashCode - keyHash) + } + + /** Variant of `copyAndMigrateFromInlineToNode` which mutates `this` rather than returning a new node. + * + * @param bitpos the bit position of the data to migrate to node + * @param keyHash the improved hash of the key currently at `bitpos` + * @param node the node to place at `bitpos` beneath `this` + */ + def migrateFromInlineToNodeInPlace[V1 >: V](bitpos: Int, keyHash: Int, node: MapNode[K, V1]): this.type = { + val dataIx = dataIndex(bitpos) + val idxOld = TupleLength * dataIx + val idxNew = this.content.length - TupleLength - nodeIndex(bitpos) + + val src = this.content + val dst = new Array[Any](src.length - TupleLength + 1) + + // copy 'src' and remove 2 element(s) at position 'idxOld' and + // insert 1 element(s) at position 'idxNew' + // assert(idxOld <= idxNew) + arraycopy(src, 0, dst, 0, idxOld) + arraycopy(src, idxOld + TupleLength, dst, idxOld, idxNew - idxOld) + dst(idxNew) = node + arraycopy(src, idxNew + TupleLength, dst, idxNew + 1, src.length - idxNew - TupleLength) + + val dstHashes = removeElement(originalHashes, dataIx) + + this.dataMap = dataMap ^ bitpos + this.nodeMap = nodeMap | bitpos + this.content = dst + this.originalHashes = dstHashes + this.size = size - 1 + node.size + this.cachedJavaKeySetHashCode = cachedJavaKeySetHashCode - keyHash + node.cachedJavaKeySetHashCode + this + } + + def copyAndMigrateFromInlineToNode[V1 >: V](bitpos: Int, keyHash: Int, node: MapNode[K, V1]): BitmapIndexedMapNode[K, V1] = { + val dataIx = dataIndex(bitpos) + val idxOld = TupleLength * dataIx + val idxNew = this.content.length - TupleLength - nodeIndex(bitpos) + + val src = this.content + val dst = new Array[Any](src.length - TupleLength + 1) + + // copy 'src' and remove 2 element(s) at position 'idxOld' and + // insert 1 element(s) at position 'idxNew' + // assert(idxOld <= idxNew) + arraycopy(src, 0, dst, 0, idxOld) + arraycopy(src, idxOld + TupleLength, dst, idxOld, idxNew - idxOld) + dst(idxNew) = node + arraycopy(src, idxNew + TupleLength, dst, idxNew + 1, src.length - idxNew - TupleLength) + + val dstHashes = removeElement(originalHashes, dataIx) + + new BitmapIndexedMapNode[K, V1]( + dataMap = dataMap ^ bitpos, + nodeMap = nodeMap | bitpos, + content = dst, + originalHashes = dstHashes, + size = size - 1 + node.size, + cachedJavaKeySetHashCode = cachedJavaKeySetHashCode - keyHash + node.cachedJavaKeySetHashCode + ) + } + + def copyAndMigrateFromNodeToInline[V1 >: V](bitpos: Int, oldNode: MapNode[K, V1], node: MapNode[K, V1]): BitmapIndexedMapNode[K, V1] = { + val idxOld = this.content.length - 1 - nodeIndex(bitpos) + val dataIxNew = dataIndex(bitpos) + val idxNew = TupleLength * dataIxNew + + val key = node.getKey(0) + val value = node.getValue(0) + val src = this.content + val dst = new Array[Any](src.length - 1 + TupleLength) + + // copy 'src' and remove 1 element(s) at position 'idxOld' and + // insert 2 element(s) at position 'idxNew' + // assert(idxOld >= idxNew) + arraycopy(src, 0, dst, 0, idxNew) + dst(idxNew) = key + dst(idxNew + 1) = value + arraycopy(src, idxNew, dst, idxNew + TupleLength, idxOld - idxNew) + arraycopy(src, idxOld + 1, dst, idxOld + TupleLength, src.length - idxOld - 1) + val hash = node.getHash(0) + val dstHashes = insertElement(originalHashes, dataIxNew, hash) + new BitmapIndexedMapNode[K, V1]( + dataMap = dataMap | bitpos, + nodeMap = nodeMap ^ bitpos, + content = dst, + originalHashes = dstHashes, + size = size - oldNode.size + 1, + cachedJavaKeySetHashCode = cachedJavaKeySetHashCode - oldNode.cachedJavaKeySetHashCode + node.cachedJavaKeySetHashCode + ) + } + + override def foreach[U](f: ((K, V)) => U): Unit = { + val iN = payloadArity // arity doesn't change during this operation + var i = 0 + while (i < iN) { + f(getPayload(i)) + i += 1 + } + + val jN = nodeArity // arity doesn't change during this operation + var j = 0 + while (j < jN) { + getNode(j).foreach(f) + j += 1 + } + } + + override def foreachEntry[U](f: (K, V) => U): Unit = { + val iN = payloadArity // arity doesn't change during this operation + var i = 0 + while (i < iN) { + f(getKey(i), getValue(i)) + i += 1 + } + + val jN = nodeArity // arity doesn't change during this operation + var j = 0 + while (j < jN) { + getNode(j).foreachEntry(f) + j += 1 + } + } + + override def foreachWithHash(f: (K, V, Int) => Unit): Unit = { + var i = 0 + val iN = payloadArity // arity doesn't change during this operation + while (i < iN) { + f(getKey(i), getValue(i), getHash(i)) + i += 1 + } + + val jN = nodeArity // arity doesn't change during this operation + var j = 0 + while (j < jN) { + getNode(j).foreachWithHash(f) + j += 1 + } + } + override def buildTo[V1 >: V](builder: HashMapBuilder[K, V1]): Unit = { + var i = 0 + val iN = payloadArity + val jN = nodeArity + while (i < iN) { + builder.addOne(getKey(i), getValue(i), getHash(i)) + i += 1 + } + + var j = 0 + while (j < jN) { + getNode(j).buildTo(builder) + j += 1 + } + } + + override def transform[W](f: (K, V) => W): BitmapIndexedMapNode[K, W] = { + var newContent: Array[Any] = null + val iN = payloadArity // arity doesn't change during this operation + val jN = nodeArity // arity doesn't change during this operation + val newContentLength = content.length + var i = 0 + while (i < iN) { + val key = getKey(i) + val value = getValue(i) + val newValue = f(key, value) + if (newContent eq null) { + if (newValue.asInstanceOf[AnyRef] ne value.asInstanceOf[AnyRef]) { + newContent = content.clone() + newContent(TupleLength * i + 1) = newValue + } + } else { + newContent(TupleLength * i + 1) = newValue + } + i += 1 + } + + var j = 0 + while (j < jN) { + val node = getNode(j) + val newNode = node.transform(f) + if (newContent eq null) { + if (newNode ne node) { + newContent = content.clone() + newContent(newContentLength - j - 1) = newNode + } + } else + newContent(newContentLength - j - 1) = newNode + j += 1 + } + if (newContent eq null) this.asInstanceOf[BitmapIndexedMapNode[K, W]] + else new BitmapIndexedMapNode[K, W](dataMap, nodeMap, newContent, originalHashes, size, cachedJavaKeySetHashCode) + } + + override def mergeInto[V1 >: V](that: MapNode[K, V1], builder: HashMapBuilder[K, V1], shift: Int)(mergef: ((K, V), (K, V1)) => (K, V1)): Unit = that match { + case bm: BitmapIndexedMapNode[K, V] @unchecked => + if (size == 0) { + that.buildTo(builder) + return + } else if (bm.size == 0) { + buildTo(builder) + return + } + + val allMap = dataMap | bm.dataMap | nodeMap | bm.nodeMap + + val minIndex: Int = Integer.numberOfTrailingZeros(allMap) + val maxIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(allMap) + + { + var index = minIndex + var leftIdx = 0 + var rightIdx = 0 + + while (index < maxIndex) { + val bitpos = bitposFrom(index) + + if ((bitpos & dataMap) != 0) { + val leftKey = getKey(leftIdx) + val leftValue = getValue(leftIdx) + val leftOriginalHash = getHash(leftIdx) + if ((bitpos & bm.dataMap) != 0) { + // left data and right data + val rightKey = bm.getKey(rightIdx) + val rightValue = bm.getValue(rightIdx) + val rightOriginalHash = bm.getHash(rightIdx) + if (leftOriginalHash == rightOriginalHash && leftKey == rightKey) { + builder.addOne(mergef((leftKey, leftValue), (rightKey, rightValue))) + } else { + builder.addOne(leftKey, leftValue, leftOriginalHash) + builder.addOne(rightKey, rightValue, rightOriginalHash) + } + rightIdx += 1 + } else if ((bitpos & bm.nodeMap) != 0) { + // left data and right node + val subNode = bm.getNode(bm.nodeIndex(bitpos)) + val leftImprovedHash = improve(leftOriginalHash) + val removed = subNode.removed(leftKey, leftOriginalHash, leftImprovedHash, shift + BitPartitionSize) + if (removed eq subNode) { + // no overlap in leftData and rightNode, just build both children to builder + subNode.buildTo(builder) + builder.addOne(leftKey, leftValue, leftOriginalHash, leftImprovedHash) + } else { + // there is collision, so special treatment for that key + removed.buildTo(builder) + builder.addOne(mergef((leftKey, leftValue), subNode.getTuple(leftKey, leftOriginalHash, leftImprovedHash, shift + BitPartitionSize))) + } + } else { + // left data and nothing on right + builder.addOne(leftKey, leftValue, leftOriginalHash) + } + leftIdx += 1 + } else if ((bitpos & nodeMap) != 0) { + if ((bitpos & bm.dataMap) != 0) { + // left node and right data + val rightKey = bm.getKey(rightIdx) + val rightValue = bm.getValue(rightIdx) + val rightOriginalHash = bm.getHash(rightIdx) + val rightImprovedHash = improve(rightOriginalHash) + + val subNode = getNode(nodeIndex(bitpos)) + val removed = subNode.removed(rightKey, rightOriginalHash, rightImprovedHash, shift + BitPartitionSize) + if (removed eq subNode) { + // no overlap in leftNode and rightData, just build both children to builder + subNode.buildTo(builder) + builder.addOne(rightKey, rightValue, rightOriginalHash, rightImprovedHash) + } else { + // there is collision, so special treatment for that key + removed.buildTo(builder) + builder.addOne(mergef(subNode.getTuple(rightKey, rightOriginalHash, rightImprovedHash, shift + BitPartitionSize), (rightKey, rightValue))) + } + rightIdx += 1 + + } else if ((bitpos & bm.nodeMap) != 0) { + // left node and right node + getNode(nodeIndex(bitpos)).mergeInto(bm.getNode(bm.nodeIndex(bitpos)), builder, shift + BitPartitionSize)(mergef) + } else { + // left node and nothing on right + getNode(nodeIndex(bitpos)).buildTo(builder) + } + } else if ((bitpos & bm.dataMap) != 0) { + // nothing on left, right data + val dataIndex = bm.dataIndex(bitpos) + builder.addOne(bm.getKey(dataIndex),bm.getValue(dataIndex), bm.getHash(dataIndex)) + rightIdx += 1 + + } else if ((bitpos & bm.nodeMap) != 0) { + // nothing on left, right node + bm.getNode(bm.nodeIndex(bitpos)).buildTo(builder) + } + + index += 1 + } + } + case _: HashCollisionMapNode[_, _] => + throw new RuntimeException("Cannot merge BitmapIndexedMapNode with HashCollisionMapNode") + } + + override def equals(that: Any): Boolean = + that match { + case node: BitmapIndexedMapNode[_, _] => + (this eq node) || + (this.cachedJavaKeySetHashCode == node.cachedJavaKeySetHashCode) && + (this.nodeMap == node.nodeMap) && + (this.dataMap == node.dataMap) && + (this.size == node.size) && + java.util.Arrays.equals(this.originalHashes, node.originalHashes) && + deepContentEquality(this.content, node.content, content.length) + case _ => false + } + + @inline private def deepContentEquality(a1: Array[Any], a2: Array[Any], length: Int): Boolean = { + if (a1 eq a2) + true + else { + var isEqual = true + var i = 0 + + while (isEqual && i < length) { + isEqual = a1(i) == a2(i) + i += 1 + } + + isEqual + } + } + + override def hashCode(): Int = + throw new UnsupportedOperationException("Trie nodes do not support hashing.") + + override def concat[V1 >: V](that: MapNode[K, V1], shift: Int): BitmapIndexedMapNode[K, V1] = that match { + case bm: BitmapIndexedMapNode[K, V] @unchecked => + if (size == 0) return bm + else if (bm.size == 0 || (bm eq this)) return this + else if (bm.size == 1) { + val originalHash = bm.getHash(0) + return this.updated(bm.getKey(0), bm.getValue(0), originalHash, improve(originalHash), shift, replaceValue = true) + } + // if we go through the merge and the result does not differ from `bm`, we can just return `bm`, to improve sharing + // So, `anyChangesMadeSoFar` will be set to `true` as soon as we encounter a difference between the + // currently-being-computed result, and `bm` + var anyChangesMadeSoFar = false + + val allMap = dataMap | bm.dataMap | nodeMap | bm.nodeMap + + // minimumIndex is inclusive -- it is the first index for which there is data or nodes + val minimumBitPos: Int = Node.bitposFrom(Integer.numberOfTrailingZeros(allMap)) + // maximumIndex is inclusive -- it is the last index for which there is data or nodes + // it could not be exclusive, because then upper bound in worst case (Node.BranchingFactor) would be out-of-bound + // of int bitposition representation + val maximumBitPos: Int = Node.bitposFrom(Node.BranchingFactor - Integer.numberOfLeadingZeros(allMap) - 1) + + var leftNodeRightNode = 0 + var leftDataRightNode = 0 + var leftNodeRightData = 0 + var leftDataOnly = 0 + var rightDataOnly = 0 + var leftNodeOnly = 0 + var rightNodeOnly = 0 + var leftDataRightDataMigrateToNode = 0 + var leftDataRightDataRightOverwrites = 0 + + var dataToNodeMigrationTargets = 0 + + { + var bitpos = minimumBitPos + var leftIdx = 0 + var rightIdx = 0 + var finished = false + + while (!finished) { + + if ((bitpos & dataMap) != 0) { + if ((bitpos & bm.dataMap) != 0) { + val leftOriginalHash = getHash(leftIdx) + if (leftOriginalHash == bm.getHash(rightIdx) && getKey(leftIdx) == bm.getKey(rightIdx)) { + leftDataRightDataRightOverwrites |= bitpos + } else { + leftDataRightDataMigrateToNode |= bitpos + dataToNodeMigrationTargets |= Node.bitposFrom(Node.maskFrom(improve(leftOriginalHash), shift)) + } + rightIdx += 1 + } else if ((bitpos & bm.nodeMap) != 0) { + leftDataRightNode |= bitpos + } else { + leftDataOnly |= bitpos + } + leftIdx += 1 + } else if ((bitpos & nodeMap) != 0) { + if ((bitpos & bm.dataMap) != 0) { + leftNodeRightData |= bitpos + rightIdx += 1 + } else if ((bitpos & bm.nodeMap) != 0) { + leftNodeRightNode |= bitpos + } else { + leftNodeOnly |= bitpos + } + } else if ((bitpos & bm.dataMap) != 0) { + rightDataOnly |= bitpos + rightIdx += 1 + } else if ((bitpos & bm.nodeMap) != 0) { + rightNodeOnly |= bitpos + } + + if (bitpos == maximumBitPos) { + finished = true + } else { + bitpos = bitpos << 1 + } + } + } + + + val newDataMap = leftDataOnly | rightDataOnly | leftDataRightDataRightOverwrites + + val newNodeMap = + leftNodeRightNode | + leftDataRightNode | + leftNodeRightData | + leftNodeOnly | + rightNodeOnly | + dataToNodeMigrationTargets + + + if ((newDataMap == (rightDataOnly | leftDataRightDataRightOverwrites)) && (newNodeMap == rightNodeOnly)) { + // nothing from `this` will make it into the result -- return early + return bm + } + + val newDataSize = bitCount(newDataMap) + val newContentSize = (MapNode.TupleLength * newDataSize) + bitCount(newNodeMap) + + val newContent = new Array[Any](newContentSize) + val newOriginalHashes = new Array[Int](newDataSize) + var newSize = 0 + var newCachedHashCode = 0 + + { + var leftDataIdx = 0 + var rightDataIdx = 0 + var leftNodeIdx = 0 + var rightNodeIdx = 0 + + val nextShift = shift + Node.BitPartitionSize + + var compressedDataIdx = 0 + var compressedNodeIdx = 0 + + var bitpos = minimumBitPos + var finished = false + + while (!finished) { + + if ((bitpos & leftNodeRightNode) != 0) { + val rightNode = bm.getNode(rightNodeIdx) + val newNode = getNode(leftNodeIdx).concat(rightNode, nextShift) + if (rightNode ne newNode) { + anyChangesMadeSoFar = true + } + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + rightNodeIdx += 1 + leftNodeIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + + } else if ((bitpos & leftDataRightNode) != 0) { + val newNode = { + val n = bm.getNode(rightNodeIdx) + val leftKey = getKey(leftDataIdx) + val leftValue = getValue(leftDataIdx) + val leftOriginalHash = getHash(leftDataIdx) + val leftImproved = improve(leftOriginalHash) + + val updated = n.updated(leftKey, leftValue, leftOriginalHash, leftImproved, nextShift, replaceValue = false) + + if (updated ne n) { + anyChangesMadeSoFar = true + } + + updated + } + + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + rightNodeIdx += 1 + leftDataIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } + else if ((bitpos & leftNodeRightData) != 0) { + anyChangesMadeSoFar = true + val newNode = { + val rightOriginalHash = bm.getHash(rightDataIdx) + getNode(leftNodeIdx).updated( + key = bm.getKey(rightDataIdx), + value = bm.getValue(rightDataIdx), + originalHash = bm.getHash(rightDataIdx), + hash = improve(rightOriginalHash), + shift = nextShift, + replaceValue = true + ) + } + + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + leftNodeIdx += 1 + rightDataIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + + } else if ((bitpos & leftDataOnly) != 0) { + anyChangesMadeSoFar = true + val originalHash = originalHashes(leftDataIdx) + newContent(MapNode.TupleLength * compressedDataIdx) = getKey(leftDataIdx).asInstanceOf[AnyRef] + newContent(MapNode.TupleLength * compressedDataIdx + 1) = getValue(leftDataIdx).asInstanceOf[AnyRef] + newOriginalHashes(compressedDataIdx) = originalHash + + compressedDataIdx += 1 + leftDataIdx += 1 + newSize += 1 + newCachedHashCode += improve(originalHash) + } else if ((bitpos & rightDataOnly) != 0) { + val originalHash = bm.originalHashes(rightDataIdx) + newContent(MapNode.TupleLength * compressedDataIdx) = bm.getKey(rightDataIdx).asInstanceOf[AnyRef] + newContent(MapNode.TupleLength * compressedDataIdx + 1) = bm.getValue(rightDataIdx).asInstanceOf[AnyRef] + newOriginalHashes(compressedDataIdx) = originalHash + + compressedDataIdx += 1 + rightDataIdx += 1 + newSize += 1 + newCachedHashCode += improve(originalHash) + } else if ((bitpos & leftNodeOnly) != 0) { + anyChangesMadeSoFar = true + val newNode = getNode(leftNodeIdx) + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + leftNodeIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } else if ((bitpos & rightNodeOnly) != 0) { + val newNode = bm.getNode(rightNodeIdx) + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + rightNodeIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } else if ((bitpos & leftDataRightDataMigrateToNode) != 0) { + anyChangesMadeSoFar = true + val newNode = { + val leftOriginalHash = getHash(leftDataIdx) + val rightOriginalHash = bm.getHash(rightDataIdx) + + bm.mergeTwoKeyValPairs( + getKey(leftDataIdx), getValue(leftDataIdx), leftOriginalHash, improve(leftOriginalHash), + bm.getKey(rightDataIdx), bm.getValue(rightDataIdx), rightOriginalHash, improve(rightOriginalHash), + nextShift + ) + } + + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + leftDataIdx += 1 + rightDataIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } else if ((bitpos & leftDataRightDataRightOverwrites) != 0) { + val originalHash = bm.originalHashes(rightDataIdx) + newContent(MapNode.TupleLength * compressedDataIdx) = bm.getKey(rightDataIdx).asInstanceOf[AnyRef] + newContent(MapNode.TupleLength * compressedDataIdx + 1) = bm.getValue(rightDataIdx).asInstanceOf[AnyRef] + newOriginalHashes(compressedDataIdx) = originalHash + + compressedDataIdx += 1 + rightDataIdx += 1 + newSize += 1 + newCachedHashCode += improve(originalHash) + leftDataIdx += 1 + } + + if (bitpos == maximumBitPos) { + finished = true + } else { + bitpos = bitpos << 1 + } + } + } + + if (anyChangesMadeSoFar) + new BitmapIndexedMapNode( + dataMap = newDataMap, + nodeMap = newNodeMap, + content = newContent, + originalHashes = newOriginalHashes, + size = newSize, + cachedJavaKeySetHashCode = newCachedHashCode + ) + else bm + + case _ => + // should never happen -- hash collisions are never at the same level as bitmapIndexedMapNodes + throw new UnsupportedOperationException("Cannot concatenate a HashCollisionMapNode with a BitmapIndexedMapNode") + } + + override def copy(): BitmapIndexedMapNode[K, V] = { + val contentClone = content.clone() + val contentLength = contentClone.length + var i = bitCount(dataMap) * TupleLength + while (i < contentLength) { + contentClone(i) = contentClone(i).asInstanceOf[MapNode[K, V]].copy() + i += 1 + } + new BitmapIndexedMapNode[K, V](dataMap, nodeMap, contentClone, originalHashes.clone(), size, cachedJavaKeySetHashCode) + } + + override def filterImpl(pred: ((K, V)) => Boolean, flipped: Boolean): BitmapIndexedMapNode[K, V] = { + if (size == 0) this + else if (size == 1) { + if (pred(getPayload(0)) != flipped) this else MapNode.empty + } else if (nodeMap == 0) { + // Performance optimization for nodes of depth 1: + // + // this node has no "node" children, all children are inlined data elems, therefor logic is significantly simpler + // approach: + // * traverse the content array, accumulating in `newDataMap: Int` any bit positions of keys which pass the filter + // * (bitCount(newDataMap) * TupleLength) tells us the new content array and originalHashes array size, so now perform allocations + // * traverse the content array once more, placing each passing element (according to `newDatamap`) in the new content and originalHashes arrays + // + // note: + // * this optimization significantly improves performance of not only small trees, but also larger trees, since + // even non-root nodes are affected by this improvement, and large trees will consist of many nodes as + // descendants + // + val minimumIndex: Int = Integer.numberOfTrailingZeros(dataMap) + val maximumIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(dataMap) + + var newDataMap = 0 + var newCachedHashCode = 0 + var dataIndex = 0 + + var i = minimumIndex + + while(i < maximumIndex) { + val bitpos = bitposFrom(i) + + if ((bitpos & dataMap) != 0) { + val payload = getPayload(dataIndex) + val passed = pred(payload) != flipped + + if (passed) { + newDataMap |= bitpos + newCachedHashCode += improve(getHash(dataIndex)) + } + + dataIndex += 1 + } + + i += 1 + } + + if (newDataMap == 0) { + MapNode.empty + } else if (newDataMap == dataMap) { + this + } else { + val newSize = Integer.bitCount(newDataMap) + val newContent = new Array[Any](newSize * TupleLength) + val newOriginalHashCodes = new Array[Int](newSize) + val newMaximumIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(newDataMap) + + var j = Integer.numberOfTrailingZeros(newDataMap) + + var newDataIndex = 0 + + + while (j < newMaximumIndex) { + val bitpos = bitposFrom(j) + if ((bitpos & newDataMap) != 0) { + val oldIndex = indexFrom(dataMap, bitpos) + newContent(newDataIndex * TupleLength) = content(oldIndex * TupleLength) + newContent(newDataIndex * TupleLength + 1) = content(oldIndex * TupleLength + 1) + newOriginalHashCodes(newDataIndex) = originalHashes(oldIndex) + newDataIndex += 1 + } + j += 1 + } + + new BitmapIndexedMapNode(newDataMap, 0, newContent, newOriginalHashCodes, newSize, newCachedHashCode) + } + + + } else { + val allMap = dataMap | nodeMap + val minimumIndex: Int = Integer.numberOfTrailingZeros(allMap) + val maximumIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(allMap) + + var oldDataPassThrough = 0 + + // bitmap of nodes which, when filtered, returned a single-element node. These must be migrated to data + var nodeMigrateToDataTargetMap = 0 + // the queue of single-element, post-filter nodes + var nodesToMigrateToData: mutable.Queue[MapNode[K, V]] = null + + // bitmap of all nodes which, when filtered, returned themselves. They are passed forward to the returned node + var nodesToPassThroughMap = 0 + + // bitmap of any nodes which, after being filtered, returned a node that is not empty, but also not `eq` itself + // These are stored for later inclusion into the final `content` array + // not named `newNodesMap` (plural) to avoid confusion with `newNodeMap` (singular) + var mapOfNewNodes = 0 + // each bit in `mapOfNewNodes` corresponds to one element in this queue + var newNodes: mutable.Queue[MapNode[K, V]] = null + + var newDataMap = 0 + var newNodeMap = 0 + var newSize = 0 + var newCachedHashCode = 0 + + var dataIndex = 0 + var nodeIndex = 0 + + var i = minimumIndex + while (i < maximumIndex) { + val bitpos = bitposFrom(i) + + if ((bitpos & dataMap) != 0) { + val payload = getPayload(dataIndex) + val passed = pred(payload) != flipped + + if (passed) { + newDataMap |= bitpos + oldDataPassThrough |= bitpos + newSize += 1 + newCachedHashCode += improve(getHash(dataIndex)) + } + + dataIndex += 1 + } else if ((bitpos & nodeMap) != 0) { + val oldSubNode = getNode(nodeIndex) + val newSubNode = oldSubNode.filterImpl(pred, flipped) + + newSize += newSubNode.size + newCachedHashCode += newSubNode.cachedJavaKeySetHashCode + + // if (newSubNode.size == 0) do nothing (drop it) + if (newSubNode.size > 1) { + newNodeMap |= bitpos + if (oldSubNode eq newSubNode) { + nodesToPassThroughMap |= bitpos + } else { + mapOfNewNodes |= bitpos + if (newNodes eq null) { + newNodes = mutable.Queue.empty + } + newNodes += newSubNode + } + } else if (newSubNode.size == 1) { + newDataMap |= bitpos + nodeMigrateToDataTargetMap |= bitpos + if (nodesToMigrateToData eq null) { + nodesToMigrateToData = mutable.Queue() + } + nodesToMigrateToData += newSubNode + } + + nodeIndex += 1 + } + + i += 1 + } + + if (newSize == 0) { + MapNode.empty + } else if (newSize == size) { + this + } else { + val newDataSize = bitCount(newDataMap) + val newContentSize = (MapNode.TupleLength * newDataSize) + bitCount(newNodeMap) + val newContent = new Array[Any](newContentSize) + val newOriginalHashes = new Array[Int](newDataSize) + + val newAllMap = newDataMap | newNodeMap + val maxIndex = Node.BranchingFactor - Integer.numberOfLeadingZeros(newAllMap) + + // note: We MUST start from the minimum index in the old (`this`) node, otherwise `old{Node,Data}Index` will + // not be incremented properly. Otherwise we could have started at Integer.numberOfTrailingZeroes(newAllMap) + var i = minimumIndex + + var oldDataIndex = 0 + var oldNodeIndex = 0 + + var newDataIndex = 0 + var newNodeIndex = 0 + + while (i < maxIndex) { + val bitpos = bitposFrom(i) + + if ((bitpos & oldDataPassThrough) != 0) { + newContent(newDataIndex * TupleLength) = getKey(oldDataIndex) + newContent(newDataIndex * TupleLength + 1) = getValue(oldDataIndex) + newOriginalHashes(newDataIndex) = getHash(oldDataIndex) + newDataIndex += 1 + oldDataIndex += 1 + } else if ((bitpos & nodesToPassThroughMap) != 0) { + newContent(newContentSize - newNodeIndex - 1) = getNode(oldNodeIndex) + newNodeIndex += 1 + oldNodeIndex += 1 + } else if ((bitpos & nodeMigrateToDataTargetMap) != 0) { + // we need not check for null here. If nodeMigrateToDataTargetMap != 0, then nodesMigrateToData must not be null + val node = nodesToMigrateToData.dequeue() + newContent(TupleLength * newDataIndex) = node.getKey(0) + newContent(TupleLength * newDataIndex + 1) = node.getValue(0) + newOriginalHashes(newDataIndex) = node.getHash(0) + newDataIndex += 1 + oldNodeIndex += 1 + } else if ((bitpos & mapOfNewNodes) != 0) { + newContent(newContentSize - newNodeIndex - 1) = newNodes.dequeue() + newNodeIndex += 1 + oldNodeIndex += 1 + } else if ((bitpos & dataMap) != 0) { + oldDataIndex += 1 + } else if ((bitpos & nodeMap) != 0) { + oldNodeIndex += 1 + } + + i += 1 + } + + new BitmapIndexedMapNode[K, V](newDataMap, newNodeMap, newContent, newOriginalHashes, newSize, newCachedHashCode) + } + } + } +} + +private final class HashCollisionMapNode[K, +V ]( + val originalHash: Int, + val hash: Int, + var content: Vector[(K, V @uV)] + ) extends MapNode[K, V] { + + import Node._ + + require(content.length >= 2) + + releaseFence() + + private[immutable] def indexOf(key: Any): Int = { + val iter = content.iterator + var i = 0 + while (iter.hasNext) { + if (iter.next()._1 == key) return i + i += 1 + } + -1 + } + + def size: Int = content.length + + def apply(key: K, originalHash: Int, hash: Int, shift: Int): V = get(key, originalHash, hash, shift).getOrElse(Iterator.empty.next()) + + def get(key: K, originalHash: Int, hash: Int, shift: Int): Option[V] = + if (this.hash == hash) { + val index = indexOf(key) + if (index >= 0) Some(content(index)._2) else None + } else None + + override def getTuple(key: K, originalHash: Int, hash: Int, shift: Int): (K, V) = { + val index = indexOf(key) + if (index >= 0) content(index) else Iterator.empty.next() + } + + def getOrElse[V1 >: V](key: K, originalHash: Int, hash: Int, shift: Int, f: => V1): V1 = { + if (this.hash == hash) { + indexOf(key) match { + case -1 => f + case other => content(other)._2 + } + } else f + } + + override def containsKey(key: K, originalHash: Int, hash: Int, shift: Int): Boolean = + this.hash == hash && indexOf(key) >= 0 + + def contains[V1 >: V](key: K, value: V1, hash: Int, shift: Int): Boolean = + this.hash == hash && { + val index = indexOf(key) + index >= 0 && (content(index)._2.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) + } + + def updated[V1 >: V](key: K, value: V1, originalHash: Int, hash: Int, shift: Int, replaceValue: Boolean): MapNode[K, V1] = { + val index = indexOf(key) + if (index >= 0) { + if (replaceValue) { + if (content(index)._2.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) { + this + } else { + new HashCollisionMapNode[K, V1](originalHash, hash, content.updated[(K, V1)](index, (key, value))) + } + } else { + this + } + } else { + new HashCollisionMapNode[K, V1](originalHash, hash, content.appended[(K, V1)]((key, value))) + } + } + + def removed[V1 >: V](key: K, originalHash: Int, hash: Int, shift: Int): MapNode[K, V1] = { + if (!this.containsKey(key, originalHash, hash, shift)) { + this + } else { + val updatedContent = content.filterNot(keyValuePair => keyValuePair._1 == key) + // assert(updatedContent.size == content.size - 1) + + updatedContent.size match { + case 1 => + val (k, v) = updatedContent(0) + new BitmapIndexedMapNode[K, V1](bitposFrom(maskFrom(hash, 0)), 0, Array(k, v), Array(originalHash), 1, hash) + case _ => new HashCollisionMapNode[K, V1](originalHash, hash, updatedContent) + } + } + } + + def hasNodes: Boolean = false + + def nodeArity: Int = 0 + + def getNode(index: Int): MapNode[K, V] = + throw new IndexOutOfBoundsException("No sub-nodes present in hash-collision leaf node.") + + def hasPayload: Boolean = true + + def payloadArity: Int = content.length + + def getKey(index: Int): K = getPayload(index)._1 + def getValue(index: Int): V = getPayload(index)._2 + + def getPayload(index: Int): (K, V) = content(index) + + override def getHash(index: Int): Int = originalHash + + def foreach[U](f: ((K, V)) => U): Unit = content.foreach(f) + + def foreachEntry[U](f: (K, V) => U): Unit = content.foreach { case (k, v) => f(k, v)} + + override def foreachWithHash(f: (K, V, Int) => Unit): Unit = { + val iter = content.iterator + while (iter.hasNext) { + val next = iter.next() + f(next._1, next._2, originalHash) + } + } + + override def transform[W](f: (K, V) => W): HashCollisionMapNode[K, W] = { + val newContent = Vector.newBuilder[(K, W)] + val contentIter = content.iterator + // true if any values have been transformed to a different value via `f` + var anyChanges = false + while(contentIter.hasNext) { + val (k, v) = contentIter.next() + val newValue = f(k, v) + newContent.addOne((k, newValue)) + anyChanges ||= (v.asInstanceOf[AnyRef] ne newValue.asInstanceOf[AnyRef]) + } + if (anyChanges) new HashCollisionMapNode(originalHash, hash, newContent.result()) + else this.asInstanceOf[HashCollisionMapNode[K, W]] + } + + override def equals(that: Any): Boolean = + that match { + case node: HashCollisionMapNode[_, _] => + (this eq node) || + (this.hash == node.hash) && + (this.content.length == node.content.length) && { + val iter = content.iterator + while (iter.hasNext) { + val (key, value) = iter.next() + val index = node.indexOf(key) + if (index < 0 || value != node.content(index)._2) { + return false + } + } + true + } + case _ => false + } + + override def concat[V1 >: V](that: MapNode[K, V1], shift: Int): HashCollisionMapNode[K, V1] = that match { + case hc: HashCollisionMapNode[K, V1] => + if (hc eq this) { + this + } else { + var newContent: VectorBuilder[(K, V1)] = null + val iter = content.iterator + while (iter.hasNext) { + val nextPayload = iter.next() + if (hc.indexOf(nextPayload._1) < 0) { + if (newContent eq null) { + newContent = new VectorBuilder[(K, V1)]() + newContent.addAll(hc.content) + } + newContent.addOne(nextPayload) + } + } + if (newContent eq null) hc else new HashCollisionMapNode(originalHash, hash, newContent.result()) + } + case _: BitmapIndexedMapNode[K, V1] => + // should never happen -- hash collisions are never at the same level as bitmapIndexedMapNodes + throw new UnsupportedOperationException("Cannot concatenate a HashCollisionMapNode with a BitmapIndexedMapNode") + } + + + override def mergeInto[V1 >: V](that: MapNode[K, V1], builder: HashMapBuilder[K, V1], shift: Int)(mergef: ((K, V), (K, V1)) => (K, V1)): Unit = that match { + case hc: HashCollisionMapNode[K, V1] => + val iter = content.iterator + val rightArray = hc.content.toArray[AnyRef] // really Array[(K, V1)] + + def rightIndexOf(key: K): Int = { + var i = 0 + while (i < rightArray.length) { + val elem = rightArray(i) + if ((elem ne null) && (elem.asInstanceOf[(K, V1)])._1 == key) return i + i += 1 + } + -1 + } + + while (iter.hasNext) { + val nextPayload = iter.next() + val index = rightIndexOf(nextPayload._1) + + if (index == -1) { + builder.addOne(nextPayload) + } else { + val rightPayload = rightArray(index).asInstanceOf[(K, V1)] + rightArray(index) = null + + builder.addOne(mergef(nextPayload, rightPayload)) + } + } + + var i = 0 + while (i < rightArray.length) { + val elem = rightArray(i) + if (elem ne null) builder.addOne(elem.asInstanceOf[(K, V1)]) + i += 1 + } + case _: BitmapIndexedMapNode[K, V1] => + throw new RuntimeException("Cannot merge HashCollisionMapNode with BitmapIndexedMapNode") + + } + + override def buildTo[V1 >: V](builder: HashMapBuilder[K, V1]): Unit = { + val iter = content.iterator + while (iter.hasNext) { + val (k, v) = iter.next() + builder.addOne(k, v, originalHash, hash) + } + } + + override def filterImpl(pred: ((K, V)) => Boolean, flipped: Boolean): MapNode[K, V] = { + val newContent = content.filterImpl(pred, flipped) + val newContentLength = newContent.length + if (newContentLength == 0) { + MapNode.empty + } else if (newContentLength == 1) { + val (k, v) = newContent.head + new BitmapIndexedMapNode[K, V](bitposFrom(maskFrom(hash, 0)), 0, Array(k, v), Array(originalHash), 1, hash) + } else if (newContentLength == content.length) this + else new HashCollisionMapNode(originalHash, hash, newContent) + } + + override def copy(): HashCollisionMapNode[K, V] = new HashCollisionMapNode[K, V](originalHash, hash, content) + + override def hashCode(): Int = + throw new UnsupportedOperationException("Trie nodes do not support hashing.") + + override def cachedJavaKeySetHashCode: Int = size * hash + +} + +private final class MapKeyIterator[K, V](rootNode: MapNode[K, V]) + extends ChampBaseIterator[K, MapNode[K, V]](rootNode) { + + def next() = { + if (!hasNext) Iterator.empty.next() + + val key = currentValueNode.getKey(currentValueCursor) + currentValueCursor += 1 + + key + } + +} + +private final class MapValueIterator[K, V](rootNode: MapNode[K, V]) + extends ChampBaseIterator[V, MapNode[K, V]](rootNode) { + + def next() = { + if (!hasNext) Iterator.empty.next() + + val value = currentValueNode.getValue(currentValueCursor) + currentValueCursor += 1 + + value + } +} + +private final class MapKeyValueTupleIterator[K, V](rootNode: MapNode[K, V]) + extends ChampBaseIterator[(K, V), MapNode[K, V]](rootNode) { + + def next() = { + if (!hasNext) Iterator.empty.next() + + val payload = currentValueNode.getPayload(currentValueCursor) + currentValueCursor += 1 + + payload + } + +} + +private final class MapKeyValueTupleReverseIterator[K, V](rootNode: MapNode[K, V]) + extends ChampBaseReverseIterator[(K, V), MapNode[K, V]](rootNode) { + + def next() = { + if (!hasNext) Iterator.empty.next() + + val payload = currentValueNode.getPayload(currentValueCursor) + currentValueCursor -= 1 + + payload + } +} + +private final class MapKeyValueTupleHashIterator[K, V](rootNode: MapNode[K, V]) + extends ChampBaseReverseIterator[Any, MapNode[K, V]](rootNode) { + private[this] var hash = 0 + private[this] var value: V = _ + override def hashCode(): Int = MurmurHash3.tuple2Hash(hash, value.##, MurmurHash3.productSeed) + def next(): MapKeyValueTupleHashIterator[K, V] = { + if (!hasNext) Iterator.empty.next() + + hash = currentValueNode.getHash(currentValueCursor) + value = currentValueNode.getValue(currentValueCursor) + currentValueCursor -= 1 + this + } +} + +/** Used in HashMap[K, V]#removeAll(HashSet[K]) */ +private final class MapNodeRemoveAllSetNodeIterator[K](rootSetNode: SetNode[K]) extends ChampBaseIterator[K, SetNode[K]](rootSetNode) { + /** Returns the result of immutably removing all keys in `rootSetNode` from `rootMapNode` */ + def removeAll[V](rootMapNode: BitmapIndexedMapNode[K, V]): BitmapIndexedMapNode[K, V] = { + var curr = rootMapNode + while (curr.size > 0 && hasNext) { + val originalHash = currentValueNode.getHash(currentValueCursor) + curr = curr.removed( + key = currentValueNode.getPayload(currentValueCursor), + keyHash = improve(originalHash), + originalHash = originalHash, + shift = 0 + ) + currentValueCursor += 1 + } + curr + } + + override def next(): K = Iterator.empty.next() +} + +/** + * $factoryInfo + * + * @define Coll `immutable.HashMap` + * @define coll immutable champ hash map + */ +@SerialVersionUID(3L) +object HashMap extends MapFactory[HashMap] { + + @transient + private final val EmptyMap = new HashMap(MapNode.empty) + + def empty[K, V]: HashMap[K, V] = + EmptyMap.asInstanceOf[HashMap[K, V]] + + def from[K, V](source: collection.IterableOnce[(K, V)]): HashMap[K, V] = + source match { + case hs: HashMap[K, V] => hs + case _ => (newBuilder[K, V] ++= source).result() + } + + /** Create a new Builder which can be reused after calling `result()` without an + * intermediate call to `clear()` in order to build multiple related results. + */ + def newBuilder[K, V]: ReusableBuilder[(K, V), HashMap[K, V]] = new HashMapBuilder[K, V] +} + + +/** A Builder for a HashMap. + * $multipleResults + */ +private[immutable] final class HashMapBuilder[K, V] extends ReusableBuilder[(K, V), HashMap[K, V]] { + import MapNode._ + import Node._ + + private def newEmptyRootNode = new BitmapIndexedMapNode[K, V](0, 0, Array.emptyObjectArray.asInstanceOf[Array[Any]], Array.emptyIntArray, 0, 0) + + /** The last given out HashMap as a return value of `result()`, if any, otherwise null. + * Indicates that on next add, the elements should be copied to an identical structure, before continuing + * mutations. */ + private var aliased: HashMap[K, V] = _ + + private def isAliased: Boolean = aliased != null + + /** The root node of the partially built hashmap. */ + private var rootNode: BitmapIndexedMapNode[K, V] = newEmptyRootNode + + private[immutable] def getOrElse[V0 >: V](key: K, value: V0): V0 = + if (rootNode.size == 0) value + else { + val originalHash = key.## + rootNode.getOrElse(key, originalHash, improve(originalHash), 0, value) + } + + /** Inserts element `elem` into array `as` at index `ix`, shifting right the trailing elems */ + private[this] def insertElement(as: Array[Int], ix: Int, elem: Int): Array[Int] = { + if (ix < 0) throw new ArrayIndexOutOfBoundsException + if (ix > as.length) throw new ArrayIndexOutOfBoundsException + val result = new Array[Int](as.length + 1) + arraycopy(as, 0, result, 0, ix) + result(ix) = elem + arraycopy(as, ix, result, ix + 1, as.length - ix) + result + } + + /** Inserts key-value into the bitmapIndexMapNode. Requires that this is a new key-value pair */ + private[this] def insertValue[V1 >: V](bm: BitmapIndexedMapNode[K, V],bitpos: Int, key: K, originalHash: Int, keyHash: Int, value: V1): Unit = { + val dataIx = bm.dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = bm.content + val dst = new Array[Any](src.length + TupleLength) + + // copy 'src' and insert 2 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + dst(idx) = key + dst(idx + 1) = value + arraycopy(src, idx, dst, idx + TupleLength, src.length - idx) + + val dstHashes = insertElement(bm.originalHashes, dataIx, originalHash) + + bm.dataMap |= bitpos + bm.content = dst + bm.originalHashes = dstHashes + bm.size += 1 + bm.cachedJavaKeySetHashCode += keyHash + } + + /** Upserts a key/value pair into mapNode, mutably */ + private[immutable] def update(mapNode: MapNode[K, V], key: K, value: V, originalHash: Int, keyHash: Int, shift: Int): Unit = { + mapNode match { + case bm: BitmapIndexedMapNode[K, V] => + val mask = maskFrom(keyHash, shift) + val bitpos = bitposFrom(mask) + if ((bm.dataMap & bitpos) != 0) { + val index = indexFrom(bm.dataMap, mask, bitpos) + val key0 = bm.getKey(index) + val key0UnimprovedHash = bm.getHash(index) + + if (key0UnimprovedHash == originalHash && key0 == key) { + bm.content(TupleLength * index + 1) = value + } else { + val value0 = bm.getValue(index) + val key0Hash = improve(key0UnimprovedHash) + + val subNodeNew: MapNode[K, V] = + bm.mergeTwoKeyValPairs(key0, value0, key0UnimprovedHash, key0Hash, key, value, originalHash, keyHash, shift + BitPartitionSize) + + bm.migrateFromInlineToNodeInPlace(bitpos, key0Hash, subNodeNew) + } + + } else if ((bm.nodeMap & bitpos) != 0) { + val index = indexFrom(bm.nodeMap, mask, bitpos) + val subNode = bm.getNode(index) + val beforeSize = subNode.size + val beforeHash = subNode.cachedJavaKeySetHashCode + update(subNode, key, value, originalHash, keyHash, shift + BitPartitionSize) + bm.size += subNode.size - beforeSize + bm.cachedJavaKeySetHashCode += subNode.cachedJavaKeySetHashCode - beforeHash + } else { + insertValue(bm, bitpos, key, originalHash, keyHash, value) + } + case hc: HashCollisionMapNode[K, V] => + val index = hc.indexOf(key) + if (index < 0) { + hc.content = hc.content.appended((key, value)) + } else { + hc.content = hc.content.updated(index, (key, value)) + } + } + } + + /** If currently referencing aliased structure, copy elements to new mutable structure */ + private[this] def ensureUnaliased() = { + if (isAliased) copyElems() + aliased = null + } + + /** Copy elements to new mutable structure */ + private[this] def copyElems(): Unit = { + rootNode = rootNode.copy() + } + + override def result(): HashMap[K, V] = + if (rootNode.size == 0) { + HashMap.empty + } else if (aliased != null) { + aliased + } else { + aliased = new HashMap(rootNode) + releaseFence() + aliased + } + + override def addOne(elem: (K, V)): this.type = { + ensureUnaliased() + val h = elem._1.## + val im = improve(h) + update(rootNode, elem._1, elem._2, h, im, 0) + this + } + + def addOne(key: K, value: V): this.type = { + ensureUnaliased() + val originalHash = key.## + update(rootNode, key, value, originalHash, improve(originalHash), 0) + this + } + def addOne(key: K, value: V, originalHash: Int): this.type = { + ensureUnaliased() + update(rootNode, key, value, originalHash, improve(originalHash), 0) + this + } + def addOne(key: K, value: V, originalHash: Int, hash: Int): this.type = { + ensureUnaliased() + update(rootNode, key, value, originalHash, hash, 0) + this + } + + override def addAll(xs: IterableOnce[(K, V)]): this.type = { + ensureUnaliased() + xs match { + case hm: HashMap[K, V] => + new ChampBaseIterator[(K, V), MapNode[K, V]](hm.rootNode) { + while(hasNext) { + val originalHash = currentValueNode.getHash(currentValueCursor) + update( + mapNode = rootNode, + key = currentValueNode.getKey(currentValueCursor), + value = currentValueNode.getValue(currentValueCursor), + originalHash = originalHash, + keyHash = improve(originalHash), + shift = 0 + ) + currentValueCursor += 1 + } + + override def next() = Iterator.empty.next() + } + case hm: collection.mutable.HashMap[K, V] => + val iter = hm.nodeIterator + while (iter.hasNext) { + val next = iter.next() + val originalHash = hm.unimproveHash(next.hash) + val hash = improve(originalHash) + update(rootNode, next.key, next.value, originalHash, hash, 0) + } + case lhm: collection.mutable.LinkedHashMap[K, V] => + val iter = lhm.entryIterator + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + val hash = improve(originalHash) + update(rootNode, next.key, next.value, originalHash, hash, 0) + } + case thatMap: Map[K, V] => + thatMap.foreachEntry((key, value) => addOne(key, value)) + case other => + val it = other.iterator + while(it.hasNext) addOne(it.next()) + } + + this + } + + override def clear(): Unit = { + aliased = null + if (rootNode.size > 0) { + rootNode = newEmptyRootNode + } + } + + private[collection] def size: Int = rootNode.size + + override def knownSize: Int = rootNode.size +} diff --git a/library/src/scala/collection/immutable/HashSet.scala b/library/src/scala/collection/immutable/HashSet.scala new file mode 100644 index 000000000000..3c72236a5395 --- /dev/null +++ b/library/src/scala/collection/immutable/HashSet.scala @@ -0,0 +1,2116 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import java.lang.Integer.{bitCount, numberOfTrailingZeros} +import java.lang.System.arraycopy + +import scala.collection.Hashing.improve +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializable +import scala.collection.mutable.ReusableBuilder +import scala.runtime.Statics.releaseFence +import scala.util.hashing.MurmurHash3 + +/** This class implements immutable sets using a Compressed Hash-Array Mapped Prefix-tree. + * See paper https://michael.steindorfer.name/publications/oopsla15.pdf for more details. + * + * @tparam A the type of the elements contained in this hash set. + * @define Coll `immutable.HashSet` + * @define coll immutable champ hash set + */ +final class HashSet[A] private[immutable](private[immutable] val rootNode: BitmapIndexedSetNode[A]) + extends AbstractSet[A] + with StrictOptimizedSetOps[A, HashSet, HashSet[A]] + with IterableFactoryDefaults[A, HashSet] + with DefaultSerializable { + + def this() = this(SetNode.empty) + + // This release fence is present because rootNode may have previously been mutated during construction. + releaseFence() + + private[this] def newHashSetOrThis(newRootNode: BitmapIndexedSetNode[A]): HashSet[A] = + if (rootNode eq newRootNode) this else new HashSet(newRootNode) + + override def iterableFactory: IterableFactory[HashSet] = HashSet + + override def knownSize: Int = rootNode.size + + override def size: Int = rootNode.size + + override def isEmpty: Boolean = rootNode.size == 0 + + def iterator: Iterator[A] = { + if (isEmpty) Iterator.empty + else new SetIterator[A](rootNode) + } + + protected[immutable] def reverseIterator: Iterator[A] = new SetReverseIterator[A](rootNode) + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => IntChampStepper.from[ SetNode[A]](size, rootNode, (node, i) => node.getPayload(i).asInstanceOf[Int]) + case StepperShape.LongShape => LongChampStepper.from[ SetNode[A]](size, rootNode, (node, i) => node.getPayload(i).asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleChampStepper.from[SetNode[A]](size, rootNode, (node, i) => node.getPayload(i).asInstanceOf[Double]) + case _ => shape.parUnbox(AnyChampStepper.from[A, SetNode[A]](size, rootNode, (node, i) => node.getPayload(i))) + } + s.asInstanceOf[S with EfficientSplit] + } + + def contains(element: A): Boolean = { + val elementUnimprovedHash = element.## + val elementHash = improve(elementUnimprovedHash) + rootNode.contains(element, elementUnimprovedHash, elementHash, 0) + } + + def incl(element: A): HashSet[A] = { + val elementUnimprovedHash = element.## + val elementHash = improve(elementUnimprovedHash) + val newRootNode = rootNode.updated(element, elementUnimprovedHash, elementHash, 0) + newHashSetOrThis(newRootNode) + } + + def excl(element: A): HashSet[A] = { + val elementUnimprovedHash = element.## + val elementHash = improve(elementUnimprovedHash) + val newRootNode = rootNode.removed(element, elementUnimprovedHash, elementHash, 0) + newHashSetOrThis(newRootNode) + } + + override def concat(that: IterableOnce[A]): HashSet[A] = + that match { + case hs: HashSet[A] => + if (isEmpty) hs + else { + val newNode = rootNode.concat(hs.rootNode, 0) + if (newNode eq hs.rootNode) hs + else newHashSetOrThis(newNode) + } + case hs: collection.mutable.HashSet[A] => + val iter = hs.nodeIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = hs.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, originalHash, improved, 0) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + while (iter.hasNext) { + val next = iter.next() + val originalHash = hs.unimproveHash(next.hash) + val improved = improve(originalHash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, originalHash, improved, 0, shallowlyMutableNodeMap) + } + return new HashSet(current) + } + } + this + case lhs: collection.mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhs.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, originalHash, improved, 0) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhs.unimproveHash(next.hash) + val improved = improve(originalHash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, originalHash, improved, 0, shallowlyMutableNodeMap) + } + return new HashSet(current) + } + } + this + case _ => + val iter = that.iterator + var current = rootNode + while (iter.hasNext) { + val element = iter.next() + val originalHash = element.## + val improved = improve(originalHash) + current = current.updated(element, originalHash, improved, 0) + + if (current ne rootNode) { + // Note: We could have started with shallowlyMutableNodeMap = 0, however this way, in the case that + // the first changed key ended up in a subnode beneath root, we mark that root right away as being + // shallowly mutable. + // + // since `element` has just been inserted, and certainly caused a new root node to be created, we can say with + // certainty that it either caused a new subnode to be created underneath `current`, in which case we should + // carry on mutating that subnode, or it ended up as a child data pair of the root, in which case, no harm is + // done by including its bit position in the shallowlyMutableNodeMap anyways. + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + while (iter.hasNext) { + val element = iter.next() + val originalHash = element.## + val improved = improve(originalHash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(element, originalHash, improved, 0, shallowlyMutableNodeMap) + } + return new HashSet(current) + } + } + this + } + + override def tail: HashSet[A] = this - head + + override def init: HashSet[A] = this - last + + override def head: A = iterator.next() + + override def last: A = reverseIterator.next() + + override def foreach[U](f: A => U): Unit = rootNode.foreach(f) + + /** Applies a function f to each element, and its corresponding **original** hash, in this Set */ + @`inline` private[collection] def foreachWithHash(f: (A, Int) => Unit): Unit = rootNode.foreachWithHash(f) + + /** Applies a function f to each element, and its corresponding **original** hash, in this Set + * Stops iterating the first time that f returns `false`.*/ + @`inline` private[collection] def foreachWithHashWhile(f: (A, Int) => Boolean): Unit = rootNode.foreachWithHashWhile(f) + + // For binary compatibility, the method used to have this signature by mistake. + // protected is public in bytecode. + protected def subsetOf(that: Set[A]): Boolean = subsetOf(that: collection.Set[A]) + + override def subsetOf(that: collection.Set[A]): Boolean = isEmpty || !that.isEmpty && (that match { + case set: HashSet[A] => rootNode.subsetOf(set.rootNode, 0) + case _ => super.subsetOf(that) + }) + + override def equals(that: Any): Boolean = + that match { + case set: HashSet[_] => (this eq set) || (this.rootNode == set.rootNode) + case _ => super.equals(that) + } + + override protected[this] def className = "HashSet" + + override def hashCode(): Int = { + val it = new SetHashIterator(rootNode) + val hash = MurmurHash3.unorderedHash(it, MurmurHash3.setSeed) + //assert(hash == super.hashCode()) + hash + } + + override def diff(that: collection.Set[A]): HashSet[A] = { + if (isEmpty) { + this + } else { + that match { + case hashSet: HashSet[A] => + if (hashSet.isEmpty) this else { + val newRootNode = rootNode.diff(hashSet.rootNode, 0) + if (newRootNode.size == 0) HashSet.empty else newHashSetOrThis(rootNode.diff(hashSet.rootNode, 0)) + } + case hashSet: collection.mutable.HashSet[A] => + val iter = hashSet.nodeIterator + var curr = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = hashSet.unimproveHash(next.hash) + val improved = improve(originalHash) + curr = curr.removed(next.key, originalHash, improved, 0) + if (curr ne rootNode) { + if (curr.size == 0) { + return HashSet.empty + } + while (iter.hasNext) { + val next = iter.next() + val originalHash = hashSet.unimproveHash(next.hash) + val improved = improve(originalHash) + + curr.removeWithShallowMutations(next.key, originalHash, improved) + + if (curr.size == 0) { + return HashSet.empty + } + } + return new HashSet(curr) + } + } + this + + case other => + val thatKnownSize = other.knownSize + + if (thatKnownSize == 0) { + this + } else if (thatKnownSize <= size) { + /* this branch intentionally includes the case of thatKnownSize == -1. We know that HashSets are quite fast at look-up, so + we're likely to be the faster of the two at that. */ + removedAllWithShallowMutations(other) + } else { + // TODO: Develop more sophisticated heuristic for which branch to take + filterNot(other.contains) + } + } + + } + } + + /** Immutably removes all elements of `that` from this HashSet + * + * Mutation is used internally, but only on root SetNodes which this method itself creates. + * + * That is, this method is safe to call on published sets because it does not mutate `this` + */ + private[this] def removedAllWithShallowMutations(that: IterableOnce[A]): HashSet[A] = { + val iter = that.iterator + var curr = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = next.## + val improved = improve(originalHash) + curr = curr.removed(next, originalHash, improved, 0) + if (curr ne rootNode) { + if (curr.size == 0) { + return HashSet.empty + } + while (iter.hasNext) { + val next = iter.next() + val originalHash = next.## + val improved = improve(originalHash) + + curr.removeWithShallowMutations(next, originalHash, improved) + + if (curr.size == 0) { + return HashSet.empty + } + } + return new HashSet(curr) + } + } + this + } + + override def removedAll(that: IterableOnce[A]): HashSet[A] = that match { + case set: scala.collection.Set[A] => diff(set) + case range: Range if range.length > size => + filter { + case i: Int => !range.contains(i) + case _ => true + } + + case _ => + removedAllWithShallowMutations(that) + } + + override def partition(p: A => Boolean): (HashSet[A], HashSet[A]) = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.partition(p) + } + + override def span(p: A => Boolean): (HashSet[A], HashSet[A]) = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.span(p) + } + + override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): HashSet[A] = { + val newRootNode = rootNode.filterImpl(pred, isFlipped) + if (newRootNode eq rootNode) this + else if (newRootNode.size == 0) HashSet.empty + else new HashSet(newRootNode) + } + + override def intersect(that: collection.Set[A]): HashSet[A] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.intersect(that) + } + + override def take(n: Int): HashSet[A] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.take(n) + } + + override def takeRight(n: Int): HashSet[A] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.takeRight(n) + } + + override def takeWhile(p: A => Boolean): HashSet[A] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.takeWhile(p) + } + + override def drop(n: Int): HashSet[A] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.drop(n) + } + + override def dropRight(n: Int): HashSet[A] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.dropRight(n) + } + + override def dropWhile(p: A => Boolean): HashSet[A] = { + // This method has been preemptively overridden in order to ensure that an optimizing implementation may be included + // in a minor release without breaking binary compatibility. + super.dropWhile(p) + } +} + +private[immutable] object SetNode { + + private final val EmptySetNode = new BitmapIndexedSetNode(0, 0, Array.empty, Array.empty, 0, 0) + + def empty[A]: BitmapIndexedSetNode[A] = EmptySetNode.asInstanceOf[BitmapIndexedSetNode[A]] + + final val TupleLength = 1 + +} + +private[immutable] sealed abstract class SetNode[A] extends Node[SetNode[A]] { + + def contains(element: A, originalHash: Int, hash: Int, shift: Int): Boolean + + def updated(element: A, originalHash: Int, hash: Int, shift: Int): SetNode[A] + + def removed(element: A, originalHash: Int, hash: Int, shift: Int): SetNode[A] + + def hasNodes: Boolean + + def nodeArity: Int + + def getNode(index: Int): SetNode[A] + + def hasPayload: Boolean + + def payloadArity: Int + + def getPayload(index: Int): A + + def size: Int + + def foreach[U](f: A => U): Unit + + def subsetOf(that: SetNode[A], shift: Int): Boolean + + def copy(): SetNode[A] + + def filterImpl(pred: A => Boolean, flipped: Boolean): SetNode[A] + + def diff(that: SetNode[A], shift: Int): SetNode[A] + + def concat(that: SetNode[A], shift: Int): SetNode[A] + + def foreachWithHash(f: (A, Int) => Unit): Unit + + def foreachWithHashWhile(f: (A, Int) => Boolean): Boolean +} + +private final class BitmapIndexedSetNode[A]( + var dataMap: Int, + var nodeMap: Int, + var content: Array[Any], + var originalHashes: Array[Int], + var size: Int, + var cachedJavaKeySetHashCode: Int) extends SetNode[A] { + + import Node._ + import SetNode._ + + /* + assert(checkInvariantContentIsWellTyped()) + assert(checkInvariantSubNodesAreCompacted()) + + private final def checkInvariantSubNodesAreCompacted(): Boolean = + new SetIterator[A](this).size - payloadArity >= 2 * nodeArity + + private final def checkInvariantContentIsWellTyped(): Boolean = { + val predicate1 = TupleLength * payloadArity + nodeArity == content.length + + val predicate2 = Range(0, TupleLength * payloadArity) + .forall(i => content(i).isInstanceOf[SetNode[_]] == false) + + val predicate3 = Range(TupleLength * payloadArity, content.length) + .forall(i => content(i).isInstanceOf[SetNode[_]] == true) + + predicate1 && predicate2 && predicate3 + } + */ + + def getPayload(index: Int): A = content(index).asInstanceOf[A] + + override def getHash(index: Int): Int = originalHashes(index) + + def getNode(index: Int): SetNode[A] = content(content.length - 1 - index).asInstanceOf[SetNode[A]] + + def contains(element: A, originalHash: Int, elementHash: Int, shift: Int): Boolean = { + val mask = maskFrom(elementHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + return originalHashes(index) == originalHash && element == this.getPayload(index) + } + + if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + return this.getNode(index).contains(element, originalHash, elementHash, shift + BitPartitionSize) + } + + false + } + + def updated(element: A, originalHash: Int, elementHash: Int, shift: Int): BitmapIndexedSetNode[A] = { + val mask = maskFrom(elementHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val element0 = this.getPayload(index) + + if (element0.asInstanceOf[AnyRef] eq element.asInstanceOf[AnyRef]) { + return this + } else { + val element0UnimprovedHash = getHash(index) + val element0Hash = improve(element0UnimprovedHash) + if (originalHash == element0UnimprovedHash && element0 == element) { + return this + } else { + val subNodeNew = mergeTwoKeyValPairs(element0, element0UnimprovedHash, element0Hash, element, originalHash, elementHash, shift + BitPartitionSize) + return copyAndMigrateFromInlineToNode(bitpos, element0Hash, subNodeNew) + } + } + } + if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + val subNode = this.getNode(index) + + val subNodeNew = subNode.updated(element, originalHash, elementHash, shift + BitPartitionSize) + if (subNode eq subNodeNew) { + return this + } else { + return copyAndSetNode(bitpos, subNode, subNodeNew) + } + } + + copyAndInsertValue(bitpos, element, originalHash, elementHash) + } + /** A variant of `updated` which performs shallow mutations on the root (`this`), and if possible, on immediately + * descendant child nodes (only one level beneath `this`) + * + * The caller should pass a bitmap of child nodes of this node, which this method may mutate. + * If this method may mutate a child node, then if the updated value is located in that child node, it will + * be shallowly mutated (its children will not be mutated). + * + * If instead this method may not mutate the child node in which the to-be-updated value is located, then + * that child will be updated immutably, but the result will be mutably re-inserted as a child of this node. + * + * @param key the key to update + * @param originalHash key.## + * @param keyHash the improved hash + * @param shallowlyMutableNodeMap bitmap of child nodes of this node, which can be shallowly mutated + * during the call to this method + * + * @return Int which is the bitwise OR of shallowlyMutableNodeMap and any freshly created nodes, which will be + * available for mutations in subsequent calls. + */ + def updateWithShallowMutations(element: A, originalHash: Int, elementHash: Int, shift: Int, shallowlyMutableNodeMap: Int): Int = { + val mask = maskFrom(elementHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val element0 = getPayload(index) + val element0UnimprovedHash = getHash(index) + if (element0UnimprovedHash == originalHash && element0 == element) { + shallowlyMutableNodeMap + } else { + val element0Hash = improve(element0UnimprovedHash) + val subNodeNew = mergeTwoKeyValPairs(element0, element0UnimprovedHash, element0Hash, element, originalHash, elementHash, shift + BitPartitionSize) + migrateFromInlineToNodeInPlace(bitpos, element0Hash, subNodeNew) + shallowlyMutableNodeMap | bitpos + } + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + val subNode = this.getNode(index) + val subNodeSize = subNode.size + val subNodeCachedJavaKeySetHashCode = subNode.cachedJavaKeySetHashCode + + var returnNodeMap = shallowlyMutableNodeMap + + val subNodeNew: SetNode[A] = subNode match { + case subNodeBm: BitmapIndexedSetNode[A] if (bitpos & shallowlyMutableNodeMap) != 0 => + subNodeBm.updateWithShallowMutations(element, originalHash, elementHash, shift + BitPartitionSize, 0) + subNodeBm + case _ => + val subNodeNew = subNode.updated(element, originalHash, elementHash, shift + BitPartitionSize) + if (subNodeNew ne subNode) { + returnNodeMap |= bitpos + } + subNodeNew + } + + this.content(this.content.length - 1 - this.nodeIndex(bitpos)) = subNodeNew + this.size = this.size - subNodeSize + subNodeNew.size + this.cachedJavaKeySetHashCode = this.cachedJavaKeySetHashCode - subNodeCachedJavaKeySetHashCode + subNodeNew.cachedJavaKeySetHashCode + returnNodeMap + } else { + val dataIx = dataIndex(bitpos) + val idx = dataIx + + val src = this.content + val dst = new Array[Any](src.length + TupleLength) + + // copy 'src' and insert 2 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + dst(idx) = element + arraycopy(src, idx, dst, idx + TupleLength, src.length - idx) + + val dstHashes = insertElement(originalHashes, dataIx, originalHash) + + this.dataMap |= bitpos + this.content = dst + this.originalHashes = dstHashes + this.size += 1 + this.cachedJavaKeySetHashCode += elementHash + shallowlyMutableNodeMap + } + } + + + def removed(element: A, originalHash: Int, elementHash: Int, shift: Int): BitmapIndexedSetNode[A] = { + val mask = maskFrom(elementHash, shift) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val element0 = this.getPayload(index) + + if (element0 == element) { + if (this.payloadArity == 2 && this.nodeArity == 0) { + // Create new node with remaining pair. The new node will a) either become the new root + // returned, or b) unwrapped and inlined during returning. + val newDataMap = if (shift == 0) (dataMap ^ bitpos) else bitposFrom(maskFrom(elementHash, 0)) + if (index == 0) new BitmapIndexedSetNode[A](newDataMap, 0, Array(getPayload(1)), Array(originalHashes(1)), size - 1, improve(originalHashes(1))) + else new BitmapIndexedSetNode[A](newDataMap, 0, Array(getPayload(0)), Array(originalHashes(0)), size - 1, improve(originalHashes(0))) + } + else copyAndRemoveValue(bitpos, elementHash) + } + else this + } + else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + val subNode = this.getNode(index) + val subNodeNew = subNode.removed(element, originalHash, elementHash, shift + BitPartitionSize) + + if (subNodeNew eq subNode) this + // if subNodeNew is a hashCollision node, size has cost in Vector#length + else subNodeNew.size match { + case 1 => + // subNode is the only child (no other data or node children of `this` exist) + // escalate (singleton or empty) result + if (this.size == subNode.size) subNodeNew.asInstanceOf[BitmapIndexedSetNode[A]] + // inline value (move to front) + else copyAndMigrateFromNodeToInline(bitpos, elementHash, subNode, subNodeNew) + case subNodeNewSize if subNodeNewSize > 1 => + // modify current node (set replacement node) + copyAndSetNode(bitpos, subNode, subNodeNew) + case _ => this + } + } + else this + } + /** Variant of `removed` which will perform mutation on only the top-level node (`this`), rather than return a new + * node + * + * Should only be called on root nodes, because shift is assumed to be 0 + * + * @param element the element to remove + * @param originalHash the original hash of `element` + * @param elementHash the improved hash of `element` + */ + def removeWithShallowMutations(element: A, originalHash: Int, elementHash: Int): this.type = { + val mask = maskFrom(elementHash, 0) + val bitpos = bitposFrom(mask) + + if ((dataMap & bitpos) != 0) { + val index = indexFrom(dataMap, mask, bitpos) + val element0 = this.getPayload(index) + + if (element0 == element) { + if (this.payloadArity == 2 && this.nodeArity == 0) { + val newDataMap = dataMap ^ bitpos + if (index == 0) { + val newContent = Array[Any](getPayload(1)) + val newOriginalHashes = Array(originalHashes(1)) + val newCachedJavaKeySetHashCode = improve(getHash(1)) + this.content = newContent + this.originalHashes = newOriginalHashes + this.cachedJavaKeySetHashCode = newCachedJavaKeySetHashCode + } else { + val newContent = Array[Any](getPayload(0)) + val newOriginalHashes = Array(originalHashes(0)) + val newCachedJavaKeySetHashCode = improve(getHash(0)) + this.content = newContent + this.originalHashes = newOriginalHashes + this.cachedJavaKeySetHashCode = newCachedJavaKeySetHashCode + } + this.dataMap = newDataMap + this.nodeMap = 0 + this.size = 1 + this + } + else { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length - TupleLength) + + arraycopy(src, 0, dst, 0, idx) + arraycopy(src, idx + TupleLength, dst, idx, src.length - idx - TupleLength) + + val dstHashes = removeElement(originalHashes, dataIx) + + this.dataMap = this.dataMap ^ bitpos + this.content = dst + this.originalHashes = dstHashes + this.size -= 1 + this.cachedJavaKeySetHashCode -= elementHash + this + } + } else this + } else if ((nodeMap & bitpos) != 0) { + val index = indexFrom(nodeMap, mask, bitpos) + val subNode = this.getNode(index) + + val subNodeNew = subNode.removed(element, originalHash, elementHash, BitPartitionSize).asInstanceOf[BitmapIndexedSetNode[A]] + + if (subNodeNew eq subNode) return this + + if (subNodeNew.size == 1) { + if (this.payloadArity == 0 && this.nodeArity == 1) { + this.dataMap = subNodeNew.dataMap + this.nodeMap = subNodeNew.nodeMap + this.content = subNodeNew.content + this.originalHashes = subNodeNew.originalHashes + this.size = subNodeNew.size + this.cachedJavaKeySetHashCode = subNodeNew.cachedJavaKeySetHashCode + this + } else { + migrateFromNodeToInlineInPlace(bitpos, originalHash, elementHash, subNode, subNodeNew) + this + } + } else { + // size must be > 1 + this.content(this.content.length - 1 - this.nodeIndex(bitpos)) = subNodeNew + this.size -= 1 + this.cachedJavaKeySetHashCode = this.cachedJavaKeySetHashCode - subNode.cachedJavaKeySetHashCode + subNodeNew.cachedJavaKeySetHashCode + this + } + } else this + } + + def mergeTwoKeyValPairs(key0: A, originalKeyHash0: Int, keyHash0: Int, key1: A, originalKeyHash1: Int, keyHash1: Int, shift: Int): SetNode[A] = { + // assert(key0 != key1) + + if (shift >= HashCodeLength) { + new HashCollisionSetNode[A](originalKeyHash0, keyHash0, Vector(key0, key1)) + } else { + val mask0 = maskFrom(keyHash0, shift) + val mask1 = maskFrom(keyHash1, shift) + + if (mask0 != mask1) { + // unique prefixes, payload fits on same level + val dataMap = bitposFrom(mask0) | bitposFrom(mask1) + val newCachedHashCode = keyHash0 + keyHash1 + + if (mask0 < mask1) { + new BitmapIndexedSetNode[A](dataMap, 0, Array(key0, key1), Array(originalKeyHash0, originalKeyHash1), 2, newCachedHashCode) + } else { + new BitmapIndexedSetNode[A](dataMap, 0, Array(key1, key0), Array(originalKeyHash1, originalKeyHash0), 2, newCachedHashCode) + } + } else { + // identical prefixes, payload must be disambiguated deeper in the trie + val nodeMap = bitposFrom(mask0) + val node = mergeTwoKeyValPairs(key0, originalKeyHash0, keyHash0, key1, originalKeyHash1, keyHash1, shift + BitPartitionSize) + + new BitmapIndexedSetNode[A](0, nodeMap, Array(node), Array.emptyIntArray, node.size, node.cachedJavaKeySetHashCode) + } + } + } + + def hasPayload: Boolean = dataMap != 0 + + def payloadArity: Int = bitCount(dataMap) + + def hasNodes: Boolean = nodeMap != 0 + + def nodeArity: Int = bitCount(nodeMap) + + def dataIndex(bitpos: Int) = bitCount(dataMap & (bitpos - 1)) + + def nodeIndex(bitpos: Int) = bitCount(nodeMap & (bitpos - 1)) + + def copyAndSetNode(bitpos: Int, oldNode: SetNode[A], newNode: SetNode[A]) = { + val idx = this.content.length - 1 - this.nodeIndex(bitpos) + + val src = this.content + val dst = new Array[Any](src.length) + + // copy 'src' and set 1 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, src.length) + dst(idx) = newNode + new BitmapIndexedSetNode[A]( + dataMap = dataMap, + nodeMap = nodeMap, + content = dst, + originalHashes = originalHashes, + size = size - oldNode.size + newNode.size, + cachedJavaKeySetHashCode = cachedJavaKeySetHashCode - oldNode.cachedJavaKeySetHashCode + newNode.cachedJavaKeySetHashCode + ) + } + + def copyAndInsertValue(bitpos: Int, key: A, originalHash: Int, elementHash: Int) = { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length + 1) + + // copy 'src' and insert 1 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + dst(idx) = key + arraycopy(src, idx, dst, idx + 1, src.length - idx) + val dstHashes = insertElement(originalHashes, dataIx, originalHash) + + new BitmapIndexedSetNode[A](dataMap | bitpos, nodeMap, dst, dstHashes, size + 1, cachedJavaKeySetHashCode + elementHash) + } + + def copyAndSetValue(bitpos: Int, key: A, originalHash: Int, elementHash: Int) = { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length) + + // copy 'src' and set 1 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, src.length) + dst(idx) = key + + new BitmapIndexedSetNode[A](dataMap | bitpos, nodeMap, dst, originalHashes, size, cachedJavaKeySetHashCode) + } + + def copyAndRemoveValue(bitpos: Int, elementHash: Int) = { + val dataIx = dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = this.content + val dst = new Array[Any](src.length - 1) + + // copy 'src' and remove 1 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + arraycopy(src, idx + 1, dst, idx, src.length - idx - 1) + val dstHashes = removeElement(originalHashes, dataIx) + new BitmapIndexedSetNode[A](dataMap ^ bitpos, nodeMap, dst, dstHashes, size - 1, cachedJavaKeySetHashCode - elementHash) + } + + def copyAndMigrateFromInlineToNode(bitpos: Int, elementHash: Int, node: SetNode[A]) = { + val dataIx = dataIndex(bitpos) + val idxOld = TupleLength * dataIx + val idxNew = this.content.length - TupleLength - nodeIndex(bitpos) + + val src = this.content + val dst = new Array[Any](src.length - 1 + 1) + + // copy 'src' and remove 1 element(s) at position 'idxOld' and + // insert 1 element(s) at position 'idxNew' + // assert(idxOld <= idxNew) + arraycopy(src, 0, dst, 0, idxOld) + arraycopy(src, idxOld + 1, dst, idxOld, idxNew - idxOld) + dst(idxNew) = node + arraycopy(src, idxNew + 1, dst, idxNew + 1, src.length - idxNew - 1) + val dstHashes = removeElement(originalHashes, dataIx) + new BitmapIndexedSetNode[A]( + dataMap = dataMap ^ bitpos, + nodeMap = nodeMap | bitpos, + content = dst, originalHashes = dstHashes, + size = size - 1 + node.size, + cachedJavaKeySetHashCode = cachedJavaKeySetHashCode - elementHash + node.cachedJavaKeySetHashCode + ) + } + /** Variant of `copyAndMigrateFromInlineToNode` which mutates `this` rather than returning a new node. + * + * Note: This method will mutate `this`, and will mutate `this.content` + * + * Mutation of `this.content` will occur as an optimization not possible in maps. Since TupleLength == 1 for sets, + * content array size does not change during inline <-> node migrations. Therefor, since we are updating in-place, + * we reuse this.content by shifting data/nodes around, rather than allocating a new array. + * + * @param bitpos the bit position of the data to migrate to node + * @param keyHash the improved hash of the element currently at `bitpos` + * @param node the node to place at `bitpos` + */ + def migrateFromInlineToNodeInPlace(bitpos: Int, keyHash: Int, node: SetNode[A]): this.type = { + val dataIx = dataIndex(bitpos) + val idxOld = TupleLength * dataIx + val idxNew = this.content.length - TupleLength - nodeIndex(bitpos) + + arraycopy(content, idxOld + TupleLength, content, idxOld, idxNew - idxOld) + content(idxNew) = node + + this.dataMap = this.dataMap ^ bitpos + this.nodeMap = this.nodeMap | bitpos + this.originalHashes = removeElement(originalHashes, dataIx) + this.size = this.size - 1 + node.size + this.cachedJavaKeySetHashCode = this.cachedJavaKeySetHashCode - keyHash + node.cachedJavaKeySetHashCode + this + } + + def copyAndMigrateFromNodeToInline(bitpos: Int, elementHash: Int, oldNode: SetNode[A], node: SetNode[A]) = { + val idxOld = this.content.length - 1 - nodeIndex(bitpos) + val dataIxNew = dataIndex(bitpos) + val idxNew = TupleLength * dataIxNew + + val src = this.content + val dst = new Array[Any](src.length - 1 + 1) + + // copy 'src' and remove 1 element(s) at position 'idxOld' and + // insert 1 element(s) at position 'idxNew' + // assert(idxOld >= idxNew) + arraycopy(src, 0, dst, 0, idxNew) + dst(idxNew) = node.getPayload(0) + arraycopy(src, idxNew, dst, idxNew + 1, idxOld - idxNew) + arraycopy(src, idxOld + 1, dst, idxOld + 1, src.length - idxOld - 1) + val hash = node.getHash(0) + val dstHashes = insertElement(originalHashes, dataIxNew, hash) + new BitmapIndexedSetNode[A]( + dataMap = dataMap | bitpos, + nodeMap = nodeMap ^ bitpos, + content = dst, + originalHashes = dstHashes, + size = size - oldNode.size + 1, + cachedJavaKeySetHashCode = cachedJavaKeySetHashCode - oldNode.cachedJavaKeySetHashCode + node.cachedJavaKeySetHashCode + ) + } + + /** Variant of `copyAndMigrateFromNodeToInline` which mutates `this` rather than returning a new node. + * + * Note: This method will mutate `this`, and will mutate `this.content` + * + * Mutation of `this.content` will occur as an optimization not possible in maps. Since TupleLength == 1 for sets, + * content array size does not change during inline <-> node migrations. Therefor, since we are updating in-place, + * we reuse this.content by shifting data/nodes around, rather than allocating a new array. + * + * @param bitpos the bit position of the node to migrate inline + * @param oldNode the node currently stored at position `bitpos` + * @param node the node containing the single element to migrate inline + */ + def migrateFromNodeToInlineInPlace(bitpos: Int, originalHash: Int, elementHash: Int, oldNode: SetNode[A], node: SetNode[A]): Unit = { + val idxOld = this.content.length - 1 - nodeIndex(bitpos) + val dataIxNew = dataIndex(bitpos) + val element = node.getPayload(0) + arraycopy(content, dataIxNew, content, dataIxNew + 1, idxOld - dataIxNew) + content(dataIxNew) = element + val hash = node.getHash(0) + val dstHashes = insertElement(originalHashes, dataIxNew, hash) + + this.dataMap = this.dataMap | bitpos + this.nodeMap = this.nodeMap ^ bitpos + this.originalHashes = dstHashes + this.size = this.size - oldNode.size + 1 + this.cachedJavaKeySetHashCode = this.cachedJavaKeySetHashCode - oldNode.cachedJavaKeySetHashCode + node.cachedJavaKeySetHashCode + } + + def foreach[U](f: A => U): Unit = { + val thisPayloadArity = payloadArity + var i = 0 + while (i < thisPayloadArity) { + f(getPayload(i)) + i += 1 + } + + val thisNodeArity = nodeArity + var j = 0 + while (j < thisNodeArity) { + getNode(j).foreach(f) + j += 1 + } + } + + def subsetOf(that: SetNode[A], shift: Int): Boolean = if (this eq that) true else that match { + case _: HashCollisionSetNode[A] => false + case node: BitmapIndexedSetNode[A] => + val thisBitmap = this.dataMap | this.nodeMap + val nodeBitmap = node.dataMap | node.nodeMap + + if ((thisBitmap | nodeBitmap) != nodeBitmap) + return false + + var bitmap = thisBitmap & nodeBitmap + var bitsToSkip = numberOfTrailingZeros(bitmap) + + var isValidSubset = true + while (isValidSubset && bitsToSkip < HashCodeLength) { + val bitpos = bitposFrom(bitsToSkip) + + isValidSubset = + if ((this.dataMap & bitpos) != 0) { + if ((node.dataMap & bitpos) != 0) { + // Data x Data + val payload0 = this.getPayload(indexFrom(this.dataMap, bitpos)) + val payload1 = node.getPayload(indexFrom(node.dataMap, bitpos)) + payload0 == payload1 + } else { + // Data x Node + val thisDataIndex = indexFrom(this.dataMap, bitpos) + val payload = this.getPayload(thisDataIndex) + val subNode = that.getNode(indexFrom(node.nodeMap, bitpos)) + val elementUnimprovedHash = getHash(thisDataIndex) + val elementHash = improve(elementUnimprovedHash) + subNode.contains(payload, elementUnimprovedHash, elementHash, shift + BitPartitionSize) + } + } else ((node.dataMap & bitpos) == 0) && { + // Node x Node + val subNode0 = this.getNode(indexFrom(this.nodeMap, bitpos)) + val subNode1 = node.getNode(indexFrom(node.nodeMap, bitpos)) + subNode0.subsetOf(subNode1, shift + BitPartitionSize) + } + + val newBitmap = bitmap ^ bitpos + bitmap = newBitmap + bitsToSkip = numberOfTrailingZeros(newBitmap) + } + isValidSubset + } + + override def filterImpl(pred: A => Boolean, flipped: Boolean): BitmapIndexedSetNode[A] = { + if (size == 0) this + else if (size == 1) { + if (pred(getPayload(0)) != flipped) this else SetNode.empty + } else if (nodeMap == 0) { + // Performance optimization for nodes of depth 1: + // + // this node has no "node" children, all children are inlined data elems, therefor logic is significantly simpler + // approach: + // * traverse the content array, accumulating in `newDataMap: Int` any bit positions of keys which pass the filter + // * (bitCount(newDataMap) * TupleLength) tells us the new content array and originalHashes array size, so now perform allocations + // * traverse the content array once more, placing each passing element (according to `newDatamap`) in the new content and originalHashes arrays + // + // note: + // * this optimization significantly improves performance of not only small trees, but also larger trees, since + // even non-root nodes are affected by this improvement, and large trees will consist of many nodes as + // descendants + // + val minimumIndex: Int = Integer.numberOfTrailingZeros(dataMap) + val maximumIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(dataMap) + + var newDataMap = 0 + var newCachedHashCode = 0 + var dataIndex = 0 + + var i = minimumIndex + + while(i < maximumIndex) { + val bitpos = bitposFrom(i) + + if ((bitpos & dataMap) != 0) { + val payload = getPayload(dataIndex) + val passed = pred(payload) != flipped + + if (passed) { + newDataMap |= bitpos + newCachedHashCode += improve(getHash(dataIndex)) + } + + dataIndex += 1 + } + + i += 1 + } + + if (newDataMap == 0) { + SetNode.empty + } else if (newDataMap == dataMap) { + this + } else { + val newSize = Integer.bitCount(newDataMap) + val newContent = new Array[Any](newSize) + val newOriginalHashCodes = new Array[Int](newSize) + val newMaximumIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(newDataMap) + + var j = Integer.numberOfTrailingZeros(newDataMap) + + var newDataIndex = 0 + + while (j < newMaximumIndex) { + val bitpos = bitposFrom(j) + if ((bitpos & newDataMap) != 0) { + val oldIndex = indexFrom(dataMap, bitpos) + newContent(newDataIndex) = content(oldIndex) + newOriginalHashCodes(newDataIndex) = originalHashes(oldIndex) + newDataIndex += 1 + } + j += 1 + } + + new BitmapIndexedSetNode(newDataMap, 0, newContent, newOriginalHashCodes, newSize, newCachedHashCode) + } + } else { + val allMap = dataMap | nodeMap + val minimumIndex: Int = Integer.numberOfTrailingZeros(allMap) + val maximumIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(allMap) + + var oldDataPassThrough = 0 + + // bitmap of nodes which, when filtered, returned a single-element node. These must be migrated to data + var nodeMigrateToDataTargetMap = 0 + + // TODO: When filtering results in a single-elem node, simply `(A, originalHash, improvedHash)` could be returned, + // rather than a singleton node (to avoid pointlessly allocating arrays, nodes, which would just be inlined in + // the parent anyways). This would probably involve changing the return type of filterImpl to `AnyRef` which may + // return at runtime a SetNode[A], or a tuple of (A, Int, Int) + + // the queue of single-element, post-filter nodes + var nodesToMigrateToData: mutable.Queue[SetNode[A]] = null + + // bitmap of all nodes which, when filtered, returned themselves. They are passed forward to the returned node + var nodesToPassThroughMap = 0 + + // bitmap of any nodes which, after being filtered, returned a node that is not empty, but also not `eq` itself + // These are stored for later inclusion into the final `content` array + // not named `newNodesMap` (plural) to avoid confusion with `newNodeMap` (singular) + var mapOfNewNodes = 0 + // each bit in `mapOfNewNodes` corresponds to one element in this queue + var newNodes: mutable.Queue[SetNode[A]] = null + + var newDataMap = 0 + var newNodeMap = 0 + var newSize = 0 + var newCachedHashCode = 0 + + var dataIndex = 0 + var nodeIndex = 0 + + var i = minimumIndex + while (i < maximumIndex) { + val bitpos = bitposFrom(i) + + if ((bitpos & dataMap) != 0) { + val payload = getPayload(dataIndex) + val passed = pred(payload) != flipped + + if (passed) { + newDataMap |= bitpos + oldDataPassThrough |= bitpos + newSize += 1 + newCachedHashCode += improve(getHash(dataIndex)) + } + + dataIndex += 1 + } else if ((bitpos & nodeMap) != 0) { + val oldSubNode = getNode(nodeIndex) + val newSubNode = oldSubNode.filterImpl(pred, flipped) + + newSize += newSubNode.size + newCachedHashCode += newSubNode.cachedJavaKeySetHashCode + + // if (newSubNode.size == 0) do nothing (drop it) + if (newSubNode.size > 1) { + newNodeMap |= bitpos + if (oldSubNode eq newSubNode) { + nodesToPassThroughMap |= bitpos + } else { + mapOfNewNodes |= bitpos + if (newNodes eq null) { + newNodes = mutable.Queue.empty + } + newNodes += newSubNode + } + } else if (newSubNode.size == 1) { + newDataMap |= bitpos + nodeMigrateToDataTargetMap |= bitpos + if (nodesToMigrateToData eq null) { + nodesToMigrateToData = mutable.Queue.empty + } + nodesToMigrateToData += newSubNode + } + + nodeIndex += 1 + } + + i += 1 + } + + this.newNodeFrom( + newSize = newSize, + newDataMap = newDataMap, + newNodeMap = newNodeMap, + minimumIndex = minimumIndex, + oldDataPassThrough = oldDataPassThrough, + nodesToPassThroughMap = nodesToPassThroughMap, + nodeMigrateToDataTargetMap = nodeMigrateToDataTargetMap, + nodesToMigrateToData = nodesToMigrateToData, + mapOfNewNodes = mapOfNewNodes, + newNodes = newNodes, + newCachedHashCode = newCachedHashCode + ) + } + } + + override def diff(that: SetNode[A], shift: Int): BitmapIndexedSetNode[A] = that match { + case bm: BitmapIndexedSetNode[A] => + if (size == 0) this + else if (size == 1) { + val h = getHash(0) + if (that.contains(getPayload(0), h, improve(h), shift)) SetNode.empty else this + } else { + val allMap = dataMap | nodeMap + val minimumIndex: Int = Integer.numberOfTrailingZeros(allMap) + val maximumIndex: Int = Node.BranchingFactor - Integer.numberOfLeadingZeros(allMap) + + var oldDataPassThrough = 0 + + // bitmap of nodes which, when filtered, returned a single-element node. These must be migrated to data + var nodeMigrateToDataTargetMap = 0 + // the queue of single-element, post-filter nodes + var nodesToMigrateToData: mutable.Queue[SetNode[A]] = null + + // bitmap of all nodes which, when filtered, returned themselves. They are passed forward to the returned node + var nodesToPassThroughMap = 0 + + // bitmap of any nodes which, after being filtered, returned a node that is not empty, but also not `eq` itself + // These are stored for later inclusion into the final `content` array + // not named `newNodesMap` (plural) to avoid confusion with `newNodeMap` (singular) + var mapOfNewNodes = 0 + // each bit in `mapOfNewNodes` corresponds to one element in this queue + var newNodes: mutable.Queue[SetNode[A]] = null + + var newDataMap = 0 + var newNodeMap = 0 + var newSize = 0 + var newCachedHashCode = 0 + + var dataIndex = 0 + var nodeIndex = 0 + + var i = minimumIndex + while (i < maximumIndex) { + val bitpos = bitposFrom(i) + + if ((bitpos & dataMap) != 0) { + val payload = getPayload(dataIndex) + val originalHash = getHash(dataIndex) + val hash = improve(originalHash) + + if (!bm.contains(payload, originalHash, hash, shift)) { + newDataMap |= bitpos + oldDataPassThrough |= bitpos + newSize += 1 + newCachedHashCode += hash + } + + dataIndex += 1 + } else if ((bitpos & nodeMap) != 0) { + val oldSubNode = getNode(nodeIndex) + + val newSubNode: SetNode[A] = + if ((bitpos & bm.dataMap) != 0) { + val thatDataIndex = indexFrom(bm.dataMap, bitpos) + val thatPayload = bm.getPayload(thatDataIndex) + val thatOriginalHash = bm.getHash(thatDataIndex) + val thatHash = improve(thatOriginalHash) + oldSubNode.removed(thatPayload, thatOriginalHash, thatHash, shift + BitPartitionSize) + } else if ((bitpos & bm.nodeMap) != 0) { + oldSubNode.diff(bm.getNode(indexFrom(bm.nodeMap, bitpos)), shift + BitPartitionSize) + } else { + oldSubNode + } + + newSize += newSubNode.size + newCachedHashCode += newSubNode.cachedJavaKeySetHashCode + + // if (newSubNode.size == 0) do nothing (drop it) + if (newSubNode.size > 1) { + newNodeMap |= bitpos + if (oldSubNode eq newSubNode) { + nodesToPassThroughMap |= bitpos + } else { + mapOfNewNodes |= bitpos + if (newNodes eq null) { + newNodes = mutable.Queue.empty + } + newNodes += newSubNode + } + } else if (newSubNode.size == 1) { + newDataMap |= bitpos + nodeMigrateToDataTargetMap |= bitpos + if (nodesToMigrateToData eq null) { + nodesToMigrateToData = mutable.Queue.empty + } + nodesToMigrateToData += newSubNode + } + + nodeIndex += 1 + } + + i += 1 + } + this.newNodeFrom( + newSize = newSize, + newDataMap = newDataMap, + newNodeMap = newNodeMap, + minimumIndex = minimumIndex, + oldDataPassThrough = oldDataPassThrough, + nodesToPassThroughMap = nodesToPassThroughMap, + nodeMigrateToDataTargetMap = nodeMigrateToDataTargetMap, + nodesToMigrateToData = nodesToMigrateToData, + mapOfNewNodes = mapOfNewNodes, + newNodes = newNodes, + newCachedHashCode = newCachedHashCode + ) + } + case _: HashCollisionSetNode[A] => + // this branch should never happen, because HashCollisionSetNodes and BitMapIndexedSetNodes do not occur at the + // same depth + throw new RuntimeException("BitmapIndexedSetNode diff HashCollisionSetNode") + } + + /** Utility method only for use in `diff` and `filterImpl` + * + * @param newSize the size of the new SetNode + * @param newDataMap the dataMap of the new SetNode + * @param newNodeMap the nodeMap of the new SetNode + * @param minimumIndex the minimum index (in range of [0, 31]) for which there are sub-nodes or data beneath the new + * SetNode + * @param oldDataPassThrough bitmap representing all the data that are just passed from `this` to the new + * SetNode + * @param nodesToPassThroughMap bitmap representing all nodes that are just passed from `this` to the new SetNode + * @param nodeMigrateToDataTargetMap bitmap representing all positions which will now be data in the new SetNode, + * but which were nodes in `this` + * @param nodesToMigrateToData a queue (in order of child position) of single-element nodes, which will be migrated + * to data, in positions in the `nodeMigrateToDataTargetMap` + * @param mapOfNewNodes bitmap of positions of new nodes to include in the new SetNode + * @param newNodes queue in order of child position, of all new nodes to include in the new SetNode + * @param newCachedHashCode the cached java keyset hashcode of the new SetNode + */ + private[this] def newNodeFrom( + newSize: Int, + newDataMap: Int, + newNodeMap: Int, + minimumIndex: Int, + oldDataPassThrough: Int, + nodesToPassThroughMap: Int, + nodeMigrateToDataTargetMap: Int, + nodesToMigrateToData: mutable.Queue[SetNode[A]], + mapOfNewNodes: Int, + newNodes: mutable.Queue[SetNode[A]], + newCachedHashCode: Int): BitmapIndexedSetNode[A] = { + if (newSize == 0) { + SetNode.empty + } else if (newSize == size) { + this + } else { + val newDataSize = bitCount(newDataMap) + val newContentSize = newDataSize + bitCount(newNodeMap) + val newContent = new Array[Any](newContentSize) + val newOriginalHashes = new Array[Int](newDataSize) + + val newAllMap = newDataMap | newNodeMap + val maxIndex = Node.BranchingFactor - Integer.numberOfLeadingZeros(newAllMap) + + // note: We MUST start from the minimum index in the old (`this`) node, otherwise `old{Node,Data}Index` will + // not be incremented properly. Otherwise we could have started at Integer.numberOfTrailingZeroes(newAllMap) + var i = minimumIndex + + var oldDataIndex = 0 + var oldNodeIndex = 0 + + var newDataIndex = 0 + var newNodeIndex = 0 + + while (i < maxIndex) { + val bitpos = bitposFrom(i) + + if ((bitpos & oldDataPassThrough) != 0) { + newContent(newDataIndex) = getPayload(oldDataIndex) + newOriginalHashes(newDataIndex) = getHash(oldDataIndex) + newDataIndex += 1 + oldDataIndex += 1 + } else if ((bitpos & nodesToPassThroughMap) != 0) { + newContent(newContentSize - newNodeIndex - 1) = getNode(oldNodeIndex) + newNodeIndex += 1 + oldNodeIndex += 1 + } else if ((bitpos & nodeMigrateToDataTargetMap) != 0) { + // we need not check for null here. If nodeMigrateToDataTargetMap != 0, then nodesMigrateToData must not be null + val node = nodesToMigrateToData.dequeue() + newContent(newDataIndex) = node.getPayload(0) + newOriginalHashes(newDataIndex) = node.getHash(0) + newDataIndex += 1 + oldNodeIndex += 1 + } else if ((bitpos & mapOfNewNodes) != 0) { + // we need not check for null here. If mapOfNewNodes != 0, then newNodes must not be null + newContent(newContentSize - newNodeIndex - 1) = newNodes.dequeue() + newNodeIndex += 1 + oldNodeIndex += 1 + } else if ((bitpos & dataMap) != 0) { + oldDataIndex += 1 + } else if ((bitpos & nodeMap) != 0) { + oldNodeIndex += 1 + } + + i += 1 + } + + new BitmapIndexedSetNode[A](newDataMap, newNodeMap, newContent, newOriginalHashes, newSize, newCachedHashCode) + } + } + + + override def equals(that: Any): Boolean = + that match { + case node: BitmapIndexedSetNode[_] => + (this eq node) || + (this.cachedJavaKeySetHashCode == node.cachedJavaKeySetHashCode) && + (this.nodeMap == node.nodeMap) && + (this.dataMap == node.dataMap) && + (this.size == node.size) && + java.util.Arrays.equals(this.originalHashes, node.originalHashes) && + deepContentEquality(this.content, node.content, content.length) + case _ => false + } + + @`inline` private def deepContentEquality(a1: Array[Any], a2: Array[Any], length: Int): Boolean = { + if (a1 eq a2) + true + else { + var isEqual = true + var i = 0 + + while (isEqual && i < length) { + isEqual = a1(i) == a2(i) + i += 1 + } + + isEqual + } + } + + override def hashCode(): Int = + throw new UnsupportedOperationException("Trie nodes do not support hashing.") + + override def toString: String = f"BitmapIndexedSetNode(size=$size, dataMap=$dataMap%x, nodeMap=$nodeMap%x)" // content=${scala.runtime.ScalaRunTime.stringOf(content)} + + override def copy(): BitmapIndexedSetNode[A] = { + val contentClone = content.clone() + val contentLength = contentClone.length + var i = bitCount(dataMap) + while (i < contentLength) { + contentClone(i) = contentClone(i).asInstanceOf[SetNode[A]].copy() + i += 1 + } + new BitmapIndexedSetNode[A](dataMap, nodeMap, contentClone, originalHashes.clone(), size, cachedJavaKeySetHashCode) + } + + override def concat(that: SetNode[A], shift: Int): BitmapIndexedSetNode[A] = that match { + case bm: BitmapIndexedSetNode[A] => + if (size == 0) return bm + else if (bm.size == 0 || (bm eq this)) return this + else if (bm.size == 1) { + val originalHash = bm.getHash(0) + return this.updated(bm.getPayload(0), originalHash, improve(originalHash), shift) + } + + // if we go through the merge and the result does not differ from `this`, we can just return `this`, to improve sharing + // So, `anyChangesMadeSoFar` will be set to `true` as soon as we encounter a difference between the + // currently-being-computed result, and `this` + var anyChangesMadeSoFar = false + + // bitmap containing `1` in any position that has any descendant in either left or right, either data or node + val allMap = dataMap | bm.dataMap | nodeMap | bm.nodeMap + + // minimumIndex is inclusive -- it is the first index for which there is data or nodes + val minimumBitPos: Int = Node.bitposFrom(Integer.numberOfTrailingZeros(allMap)) + // maximumIndex is inclusive -- it is the last index for which there is data or nodes + // it could not be exclusive, because then upper bound in worst case (Node.BranchingFactor) would be out-of-bound + // of int bitposition representation + val maximumBitPos: Int = Node.bitposFrom(Node.BranchingFactor - Integer.numberOfLeadingZeros(allMap) - 1) + + var leftNodeRightNode = 0 + var leftDataRightNode = 0 + var leftNodeRightData = 0 + var leftDataOnly = 0 + var rightDataOnly = 0 + var leftNodeOnly = 0 + var rightNodeOnly = 0 + var leftDataRightDataMigrateToNode = 0 + var leftDataRightDataLeftOverwrites = 0 + + var dataToNodeMigrationTargets = 0 + + { + var bitpos = minimumBitPos + var leftIdx = 0 + var rightIdx = 0 + var finished = false + + while (!finished) { + + if ((bitpos & dataMap) != 0) { + if ((bitpos & bm.dataMap) != 0) { + if (getHash(leftIdx) == bm.getHash(rightIdx) && getPayload(leftIdx) == bm.getPayload(rightIdx)) { + leftDataRightDataLeftOverwrites |= bitpos + } else { + leftDataRightDataMigrateToNode |= bitpos + dataToNodeMigrationTargets |= Node.bitposFrom(Node.maskFrom(improve(getHash(leftIdx)), shift)) + } + rightIdx += 1 + } else if ((bitpos & bm.nodeMap) != 0) { + leftDataRightNode |= bitpos + } else { + leftDataOnly |= bitpos + } + leftIdx += 1 + } else if ((bitpos & nodeMap) != 0) { + if ((bitpos & bm.dataMap) != 0) { + leftNodeRightData |= bitpos + rightIdx += 1 + } else if ((bitpos & bm.nodeMap) != 0) { + leftNodeRightNode |= bitpos + } else { + leftNodeOnly |= bitpos + } + } else if ((bitpos & bm.dataMap) != 0) { + rightDataOnly |= bitpos + rightIdx += 1 + } else if ((bitpos & bm.nodeMap) != 0) { + rightNodeOnly |= bitpos + } + + if (bitpos == maximumBitPos) { + finished = true + } else { + bitpos = bitpos << 1 + } + } + } + + + val newDataMap = leftDataOnly | rightDataOnly | leftDataRightDataLeftOverwrites + + val newNodeMap = + leftNodeRightNode | + leftDataRightNode | + leftNodeRightData | + leftNodeOnly | + rightNodeOnly | + dataToNodeMigrationTargets + + + if ((newDataMap == (leftDataOnly | leftDataRightDataLeftOverwrites)) && (newNodeMap == leftNodeOnly)) { + // nothing from `bm` will make it into the result -- return early + return this + } + + val newDataSize = bitCount(newDataMap) + val newContentSize = newDataSize + bitCount(newNodeMap) + + val newContent = new Array[Any](newContentSize) + val newOriginalHashes = new Array[Int](newDataSize) + var newSize = 0 + var newCachedHashCode = 0 + + { + var leftDataIdx = 0 + var rightDataIdx = 0 + var leftNodeIdx = 0 + var rightNodeIdx = 0 + + val nextShift = shift + Node.BitPartitionSize + + var compressedDataIdx = 0 + var compressedNodeIdx = 0 + + var bitpos = minimumBitPos + var finished = false + + while (!finished) { + + if ((bitpos & leftNodeRightNode) != 0) { + val leftNode = getNode(leftNodeIdx) + val newNode = leftNode.concat(bm.getNode(rightNodeIdx), nextShift) + if (leftNode ne newNode) { + anyChangesMadeSoFar = true + } + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + rightNodeIdx += 1 + leftNodeIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + + } else if ((bitpos & leftDataRightNode) != 0) { + anyChangesMadeSoFar = true + val newNode = { + val n = bm.getNode(rightNodeIdx) + val leftPayload = getPayload(leftDataIdx) + val leftOriginalHash = getHash(leftDataIdx) + val leftImproved = improve(leftOriginalHash) + n.updated(leftPayload, leftOriginalHash, leftImproved, nextShift) + } + + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + rightNodeIdx += 1 + leftDataIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } + else if ((bitpos & leftNodeRightData) != 0) { + val newNode = { + val rightOriginalHash = bm.getHash(rightDataIdx) + val leftNode = getNode(leftNodeIdx) + val updated = leftNode.updated( + element = bm.getPayload(rightDataIdx), + originalHash = bm.getHash(rightDataIdx), + hash = improve(rightOriginalHash), + shift = nextShift + ) + if (updated ne leftNode) { + anyChangesMadeSoFar = true + } + updated + } + + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + leftNodeIdx += 1 + rightDataIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + + } else if ((bitpos & leftDataOnly) != 0) { + val originalHash = originalHashes(leftDataIdx) + newContent(compressedDataIdx) = getPayload(leftDataIdx).asInstanceOf[AnyRef] + newOriginalHashes(compressedDataIdx) = originalHash + + compressedDataIdx += 1 + leftDataIdx += 1 + newSize += 1 + newCachedHashCode += improve(originalHash) + } else if ((bitpos & rightDataOnly) != 0) { + anyChangesMadeSoFar = true + val originalHash = bm.originalHashes(rightDataIdx) + newContent(compressedDataIdx) = bm.getPayload(rightDataIdx).asInstanceOf[AnyRef] + newOriginalHashes(compressedDataIdx) = originalHash + + compressedDataIdx += 1 + rightDataIdx += 1 + newSize += 1 + newCachedHashCode += improve(originalHash) + } else if ((bitpos & leftNodeOnly) != 0) { + val newNode = getNode(leftNodeIdx) + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + leftNodeIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } else if ((bitpos & rightNodeOnly) != 0) { + anyChangesMadeSoFar = true + val newNode = bm.getNode(rightNodeIdx) + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + rightNodeIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } else if ((bitpos & leftDataRightDataMigrateToNode) != 0) { + anyChangesMadeSoFar = true + val newNode = { + val leftOriginalHash = getHash(leftDataIdx) + val rightOriginalHash = bm.getHash(rightDataIdx) + + bm.mergeTwoKeyValPairs( + getPayload(leftDataIdx), leftOriginalHash, improve(leftOriginalHash), + bm.getPayload(rightDataIdx), rightOriginalHash, improve(rightOriginalHash), + nextShift + ) + } + + newContent(newContentSize - compressedNodeIdx - 1) = newNode + compressedNodeIdx += 1 + leftDataIdx += 1 + rightDataIdx += 1 + newSize += newNode.size + newCachedHashCode += newNode.cachedJavaKeySetHashCode + } else if ((bitpos & leftDataRightDataLeftOverwrites) != 0) { + val originalHash = bm.originalHashes(rightDataIdx) + newContent(compressedDataIdx) = bm.getPayload(rightDataIdx).asInstanceOf[AnyRef] + newOriginalHashes(compressedDataIdx) = originalHash + + compressedDataIdx += 1 + rightDataIdx += 1 + newSize += 1 + newCachedHashCode += improve(originalHash) + leftDataIdx += 1 + } + + if (bitpos == maximumBitPos) { + finished = true + } else { + bitpos = bitpos << 1 + } + } + } + + if (anyChangesMadeSoFar) + new BitmapIndexedSetNode( + dataMap = newDataMap, + nodeMap = newNodeMap, + content = newContent, + originalHashes = newOriginalHashes, + size = newSize, + cachedJavaKeySetHashCode = newCachedHashCode + ) + else this + + case _ => + // should never happen -- hash collisions are never at the same level as bitmapIndexedSetNodes + throw new UnsupportedOperationException("Cannot concatenate a HashCollisionSetNode with a BitmapIndexedSetNode") + } + + override def foreachWithHash(f: (A, Int) => Unit): Unit = { + val iN = payloadArity // arity doesn't change during this operation + var i = 0 + while (i < iN) { + f(getPayload(i), getHash(i)) + i += 1 + } + + val jN = nodeArity // arity doesn't change during this operation + var j = 0 + while (j < jN) { + getNode(j).foreachWithHash(f) + j += 1 + } + } + + override def foreachWithHashWhile(f: (A, Int) => Boolean): Boolean = { + val thisPayloadArity = payloadArity + var pass = true + var i = 0 + while (i < thisPayloadArity && pass) { + pass &&= f(getPayload(i), getHash(i)) + i += 1 + } + + val thisNodeArity = nodeArity + var j = 0 + while (j < thisNodeArity && pass) { + pass &&= getNode(j).foreachWithHashWhile(f) + j += 1 + } + pass + } +} + +private final class HashCollisionSetNode[A](val originalHash: Int, val hash: Int, var content: Vector[A]) extends SetNode[A] { + + import Node._ + + require(content.length >= 2) + + def contains(element: A, originalHash: Int, hash: Int, shift: Int): Boolean = + this.hash == hash && content.contains(element) + + def updated(element: A, originalHash: Int, hash: Int, shift: Int): SetNode[A] = + if (this.contains(element, originalHash, hash, shift)) { + this + } else { + new HashCollisionSetNode[A](originalHash, hash, content.appended(element)) + } + + /** + * Remove an element from the hash collision node. + * + * When after deletion only one element remains, we return a bit-mapped indexed node with a + * singleton element and a hash-prefix for trie level 0. This node will be then a) either become + * the new root, or b) unwrapped and inlined deeper in the trie. + */ + def removed(element: A, originalHash: Int, hash: Int, shift: Int): SetNode[A] = + if (!this.contains(element, originalHash, hash, shift)) { + this + } else { + val updatedContent = content.filterNot(element0 => element0 == element) + // assert(updatedContent.size == content.size - 1) + + updatedContent.size match { + case 1 => new BitmapIndexedSetNode[A](bitposFrom(maskFrom(hash, 0)), 0, Array(updatedContent(0)), Array(originalHash), 1, hash) + case _ => new HashCollisionSetNode[A](originalHash, hash, updatedContent) + } + } + + def hasNodes: Boolean = false + + def nodeArity: Int = 0 + + def getNode(index: Int): SetNode[A] = + throw new IndexOutOfBoundsException("No sub-nodes present in hash-collision leaf node.") + + def hasPayload: Boolean = true + + def payloadArity: Int = content.length + + def getPayload(index: Int): A = content(index) + + override def getHash(index: Int): Int = originalHash + + def size: Int = content.length + + def foreach[U](f: A => U): Unit = { + val iter = content.iterator + while (iter.hasNext) { + f(iter.next()) + } + } + + + override def cachedJavaKeySetHashCode: Int = size * hash + + def subsetOf(that: SetNode[A], shift: Int): Boolean = if (this eq that) true else that match { + case node: HashCollisionSetNode[A] => + this.payloadArity <= node.payloadArity && this.content.forall(node.content.contains) + case _ => + false + } + + override def filterImpl(pred: A => Boolean, flipped: Boolean): SetNode[A] = { + val newContent = content.filterImpl(pred, flipped) + val newContentLength = newContent.length + if (newContentLength == 0) { + SetNode.empty + } else if (newContentLength == 1) { + new BitmapIndexedSetNode[A](bitposFrom(maskFrom(hash, 0)), 0, Array(newContent.head), Array(originalHash), 1, hash) + } else if (newContent.length == content.length) this + else new HashCollisionSetNode(originalHash, hash, newContent) + } + + override def diff(that: SetNode[A], shift: Int): SetNode[A] = + filterImpl(that.contains(_, originalHash, hash, shift), flipped = true) + + override def equals(that: Any): Boolean = + that match { + case node: HashCollisionSetNode[_] => + (this eq node) || + (this.hash == node.hash) && + (this.content.size == node.content.size) && + this.content.forall(node.content.contains) + case _ => false + } + + override def hashCode(): Int = + throw new UnsupportedOperationException("Trie nodes do not support hashing.") + + override def copy(): HashCollisionSetNode[A] = new HashCollisionSetNode[A](originalHash, hash, content) + + override def concat(that: SetNode[A], shift: Int): SetNode[A] = that match { + case hc: HashCollisionSetNode[A] => + if (hc eq this) { + this + } else { + var newContent: VectorBuilder[A] = null + val iter = hc.content.iterator + while (iter.hasNext) { + val nextPayload = iter.next() + if (!content.contains(nextPayload)) { + if (newContent eq null) { + newContent = new VectorBuilder() + newContent.addAll(this.content) + } + newContent.addOne(nextPayload) + } + } + if (newContent eq null) this else new HashCollisionSetNode(originalHash, hash, newContent.result()) + } + case _: BitmapIndexedSetNode[A] => + // should never happen -- hash collisions are never at the same level as bitmapIndexedSetNodes + throw new UnsupportedOperationException("Cannot concatenate a HashCollisionSetNode with a BitmapIndexedSetNode") + } + + override def foreachWithHash(f: (A, Int) => Unit): Unit = { + val iter = content.iterator + while (iter.hasNext) { + val next = iter.next() + f(next.asInstanceOf[A], originalHash) + } + } + + override def foreachWithHashWhile(f: (A, Int) => Boolean): Boolean = { + var stillGoing = true + val iter = content.iterator + while (iter.hasNext && stillGoing) { + val next = iter.next() + stillGoing &&= f(next.asInstanceOf[A], originalHash) + } + stillGoing + } +} + +private final class SetIterator[A](rootNode: SetNode[A]) + extends ChampBaseIterator[A, SetNode[A]](rootNode) { + + def next() = { + if (!hasNext) Iterator.empty.next() + + val payload = currentValueNode.getPayload(currentValueCursor) + currentValueCursor += 1 + + payload + } + +} + +private final class SetReverseIterator[A](rootNode: SetNode[A]) + extends ChampBaseReverseIterator[A, SetNode[A]](rootNode) { + + def next(): A = { + if (!hasNext) Iterator.empty.next() + + val payload = currentValueNode.getPayload(currentValueCursor) + currentValueCursor -= 1 + + payload + } + +} + +private final class SetHashIterator[A](rootNode: SetNode[A]) + extends ChampBaseIterator[AnyRef, SetNode[A]](rootNode) { + private[this] var hash = 0 + override def hashCode(): Int = hash + + def next(): AnyRef = { + if (!hasNext) Iterator.empty.next() + + hash = currentValueNode.getHash(currentValueCursor) + currentValueCursor += 1 + this + } + +} + + +/** + * $factoryInfo + * + * @define Coll `immutable.HashSet` + * @define coll immutable champ hash set + */ +@SerialVersionUID(3L) +object HashSet extends IterableFactory[HashSet] { + + @transient + private final val EmptySet = new HashSet(SetNode.empty) + + def empty[A]: HashSet[A] = + EmptySet.asInstanceOf[HashSet[A]] + + def from[A](source: collection.IterableOnce[A]): HashSet[A] = + source match { + case hs: HashSet[A] => hs + case _ if source.knownSize == 0 => empty[A] + case _ => (newBuilder[A] ++= source).result() + } + + /** Create a new Builder which can be reused after calling `result()` without an + * intermediate call to `clear()` in order to build multiple related results. + */ + def newBuilder[A]: ReusableBuilder[A, HashSet[A]] = new HashSetBuilder +} + +/** Builder for HashSet. + * $multipleResults + */ +private[collection] final class HashSetBuilder[A] extends ReusableBuilder[A, HashSet[A]] { + import Node._ + import SetNode._ + + private def newEmptyRootNode = new BitmapIndexedSetNode[A](0, 0, Array.emptyObjectArray.asInstanceOf[Array[Any]], Array.emptyIntArray, 0, 0) + + /** The last given out HashSet as a return value of `result()`, if any, otherwise null. + * Indicates that on next add, the elements should be copied to an identical structure, before continuing + * mutations. */ + private var aliased: HashSet[A] = _ + + private def isAliased: Boolean = aliased != null + + /** The root node of the partially built hashmap. */ + private var rootNode: BitmapIndexedSetNode[A] = newEmptyRootNode + + /** Inserts element `elem` into array `as` at index `ix`, shifting right the trailing elems */ + private def insertElement(as: Array[Int], ix: Int, elem: Int): Array[Int] = { + if (ix < 0) throw new ArrayIndexOutOfBoundsException + if (ix > as.length) throw new ArrayIndexOutOfBoundsException + val result = new Array[Int](as.length + 1) + arraycopy(as, 0, result, 0, ix) + result(ix) = elem + arraycopy(as, ix, result, ix + 1, as.length - ix) + result + } + + /** Inserts key-value into the bitmapIndexMapNode. Requires that this is a new key-value pair */ + private def insertValue[A1 >: A](bm: BitmapIndexedSetNode[A], bitpos: Int, key: A, originalHash: Int, keyHash: Int): Unit = { + val dataIx = bm.dataIndex(bitpos) + val idx = TupleLength * dataIx + + val src = bm.content + val dst = new Array[Any](src.length + TupleLength) + + // copy 'src' and insert 2 element(s) at position 'idx' + arraycopy(src, 0, dst, 0, idx) + dst(idx) = key + arraycopy(src, idx, dst, idx + TupleLength, src.length - idx) + + val dstHashes = insertElement(bm.originalHashes, dataIx, originalHash) + + bm.dataMap = bm.dataMap | bitpos + bm.content = dst + bm.originalHashes = dstHashes + bm.size += 1 + bm.cachedJavaKeySetHashCode += keyHash + } + + /** Mutates `bm` to replace inline data at bit position `bitpos` with updated key/value */ + private def setValue[A1 >: A](bm: BitmapIndexedSetNode[A], bitpos: Int, elem: A): Unit = { + val dataIx = bm.dataIndex(bitpos) + val idx = TupleLength * dataIx + bm.content(idx) = elem + } + + def update(setNode: SetNode[A], element: A, originalHash: Int, elementHash: Int, shift: Int): Unit = + setNode match { + case bm: BitmapIndexedSetNode[A] => + val mask = maskFrom(elementHash, shift) + val bitpos = bitposFrom(mask) + + if ((bm.dataMap & bitpos) != 0) { + val index = indexFrom(bm.dataMap, mask, bitpos) + val element0 = bm.getPayload(index) + val element0UnimprovedHash = bm.getHash(index) + + if (element0UnimprovedHash == originalHash && element0 == element) { + setValue(bm, bitpos, element0) + } else { + val element0Hash = improve(element0UnimprovedHash) + val subNodeNew = bm.mergeTwoKeyValPairs(element0, element0UnimprovedHash, element0Hash, element, originalHash, elementHash, shift + BitPartitionSize) + bm.migrateFromInlineToNodeInPlace(bitpos, element0Hash, subNodeNew) + } + } else if ((bm.nodeMap & bitpos) != 0) { + val index = indexFrom(bm.nodeMap, mask, bitpos) + val subNode = bm.getNode(index) + val beforeSize = subNode.size + val beforeHashCode = subNode.cachedJavaKeySetHashCode + update(subNode, element, originalHash, elementHash, shift + BitPartitionSize) + bm.size += subNode.size - beforeSize + bm.cachedJavaKeySetHashCode += subNode.cachedJavaKeySetHashCode - beforeHashCode + } else { + insertValue(bm, bitpos, element, originalHash, elementHash) + } + case hc: HashCollisionSetNode[A] => + val index = hc.content.indexOf(element) + if (index < 0) { + hc.content = hc.content.appended(element) + } else { + hc.content = hc.content.updated(index, element) + } + } + + /** If currently referencing aliased structure, copy elements to new mutable structure */ + private def ensureUnaliased():Unit = { + if (isAliased) copyElems() + aliased = null + } + + /** Copy elements to new mutable structure */ + private def copyElems(): Unit = { + rootNode = rootNode.copy() + } + + override def result(): HashSet[A] = + if (rootNode.size == 0) { + HashSet.empty + } else if (aliased != null) { + aliased + } else { + aliased = new HashSet(rootNode) + releaseFence() + aliased + } + + override def addOne(elem: A): this.type = { + ensureUnaliased() + val h = elem.## + val im = improve(h) + update(rootNode, elem, originalHash = h, elementHash = im, shift = 0) + this + } + + override def addAll(xs: IterableOnce[A]) = { + ensureUnaliased() + xs match { + case hm: HashSet[A] => + new ChampBaseIterator[A, SetNode[A]](hm.rootNode) { + while(hasNext) { + val originalHash = currentValueNode.getHash(currentValueCursor) + update( + setNode = rootNode, + element = currentValueNode.getPayload(currentValueCursor), + originalHash = originalHash, + elementHash = improve(originalHash), + shift = 0 + ) + currentValueCursor += 1 + } + override def next() = Iterator.empty.next() + } + case other => + val it = other.iterator + while(it.hasNext) addOne(it.next()) + } + + this + } + + override def clear(): Unit = { + aliased = null + if (rootNode.size > 0) { + // if rootNode is empty, we will not have given it away anyways, we instead give out the reused Set.empty + rootNode = newEmptyRootNode + } + } + + private[collection] def size: Int = rootNode.size + + override def knownSize: Int = rootNode.size +} diff --git a/library/src/scala/collection/immutable/IntMap.scala b/library/src/scala/collection/immutable/IntMap.scala new file mode 100644 index 000000000000..1aa1a6108d0c --- /dev/null +++ b/library/src/scala/collection/immutable/IntMap.scala @@ -0,0 +1,502 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package immutable + +import scala.collection.generic.{BitOperations, DefaultSerializationProxy} +import scala.collection.mutable.{Builder, ImmutableBuilder} +import scala.annotation.tailrec +import scala.annotation.unchecked.uncheckedVariance +import scala.language.implicitConversions + +/** Utility class for integer maps. + */ +private[immutable] object IntMapUtils extends BitOperations.Int { + def branchMask(i: Int, j: Int) = highestOneBit(i ^ j) + + def join[T](p1: Int, t1: IntMap[T], p2: Int, t2: IntMap[T]): IntMap[T] = { + val m = branchMask(p1, p2) + val p = mask(p1, m) + if (zero(p1, m)) IntMap.Bin(p, m, t1, t2) + else IntMap.Bin(p, m, t2, t1) + } + + def bin[T](prefix: Int, mask: Int, left: IntMap[T], right: IntMap[T]): IntMap[T] = (left, right) match { + case (left, IntMap.Nil) => left + case (IntMap.Nil, right) => right + case (left, right) => IntMap.Bin(prefix, mask, left, right) + } +} + +import IntMapUtils.{Int => _, _} + +/** A companion object for integer maps. + * + * @define Coll `IntMap` + */ +object IntMap { + def empty[T] : IntMap[T] = IntMap.Nil + + def singleton[T](key: Int, value: T): IntMap[T] = IntMap.Tip(key, value) + + def apply[T](elems: (Int, T)*): IntMap[T] = + elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)) + + def from[V](coll: IterableOnce[(Int, V)]): IntMap[V] = + newBuilder[V].addAll(coll).result() + + private[immutable] case object Nil extends IntMap[Nothing] { + // Important! Without this equals method in place, an infinite + // loop from Map.equals => size => pattern-match-on-Nil => equals + // develops. Case objects and custom equality don't mix without + // careful handling. + override def equals(that : Any) = that match { + case _: this.type => true + case _: IntMap[_] => false // The only empty IntMaps are eq Nil + case _ => super.equals(that) + } + } + + private[immutable] case class Tip[+T](key: Int, value: T) extends IntMap[T]{ + def withValue[S](s: S) = + if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[IntMap.Tip[S]] + else IntMap.Tip(key, s) + } + + private[immutable] case class Bin[+T](prefix: Int, mask: Int, left: IntMap[T], right: IntMap[T]) extends IntMap[T] { + def bin[S](left: IntMap[S], right: IntMap[S]): IntMap[S] = { + if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[IntMap.Bin[S]] + else IntMap.Bin[S](prefix, mask, left, right) + } + } + + def newBuilder[V]: Builder[(Int, V), IntMap[V]] = + new ImmutableBuilder[(Int, V), IntMap[V]](empty) { + def addOne(elem: (Int, V)): this.type = { elems = elems + elem; this } + } + + implicit def toFactory[V](dummy: IntMap.type): Factory[(Int, V), IntMap[V]] = ToFactory.asInstanceOf[Factory[(Int, V), IntMap[V]]] + + @SerialVersionUID(3L) + private[this] object ToFactory extends Factory[(Int, AnyRef), IntMap[AnyRef]] with Serializable { + def fromSpecific(it: IterableOnce[(Int, AnyRef)]): IntMap[AnyRef] = IntMap.from[AnyRef](it) + def newBuilder: Builder[(Int, AnyRef), IntMap[AnyRef]] = IntMap.newBuilder[AnyRef] + } + + implicit def toBuildFrom[V](factory: IntMap.type): BuildFrom[Any, (Int, V), IntMap[V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (Int, V), IntMap[V]]] + private[this] object ToBuildFrom extends BuildFrom[Any, (Int, AnyRef), IntMap[AnyRef]] { + def fromSpecific(from: Any)(it: IterableOnce[(Int, AnyRef)]) = IntMap.from(it) + def newBuilder(from: Any) = IntMap.newBuilder[AnyRef] + } + + implicit def iterableFactory[V]: Factory[(Int, V), IntMap[V]] = toFactory(this) + implicit def buildFromIntMap[V]: BuildFrom[IntMap[_], (Int, V), IntMap[V]] = toBuildFrom(this) +} + +// Iterator over a non-empty IntMap. +private[immutable] abstract class IntMapIterator[V, T](it: IntMap[V]) extends AbstractIterator[T] { + + // Basically this uses a simple stack to emulate conversion over the tree. However + // because we know that Ints are at least 32 bits we can have at most 32 IntMap.Bins and + // one IntMap.Tip sitting on the tree at any point. Therefore we know the maximum stack + // depth is 33 and + var index = 0 + var buffer = new Array[AnyRef](33) + + def pop = { + index -= 1 + buffer(index).asInstanceOf[IntMap[V]] + } + + def push(x: IntMap[V]): Unit = { + buffer(index) = x.asInstanceOf[AnyRef] + index += 1 + } + push(it) + + /** + * What value do we assign to a tip? + */ + def valueOf(tip: IntMap.Tip[V]): T + + def hasNext = index != 0 + @tailrec + final def next(): T = + pop match { + case IntMap.Bin(_,_, t@IntMap.Tip(_, _), right) => { + push(right) + valueOf(t) + } + case IntMap.Bin(_, _, left, right) => { + push(right) + push(left) + next() + } + case t@IntMap.Tip(_, _) => valueOf(t) + // This should never happen. We don't allow IntMap.Nil in subtrees of the IntMap + // and don't return an IntMapIterator for IntMap.Nil. + case IntMap.Nil => throw new IllegalStateException("Empty maps not allowed as subtrees") + } +} + +private[immutable] class IntMapEntryIterator[V](it: IntMap[V]) extends IntMapIterator[V, (Int, V)](it) { + def valueOf(tip: IntMap.Tip[V]) = (tip.key, tip.value) +} + +private[immutable] class IntMapValueIterator[V](it: IntMap[V]) extends IntMapIterator[V, V](it) { + def valueOf(tip: IntMap.Tip[V]) = tip.value +} + +private[immutable] class IntMapKeyIterator[V](it: IntMap[V]) extends IntMapIterator[V, Int](it) { + def valueOf(tip: IntMap.Tip[V]) = tip.key +} + +import IntMap._ + +/** Specialised immutable map structure for integer keys, based on + * [[https://ittc.ku.edu/~andygill/papers/IntMap98.pdf Fast Mergeable Integer Maps]] + * by Okasaki and Gill. Essentially a trie based on binary digits of the integers. + * + * '''Note:''' This class is as of 2.8 largely superseded by HashMap. + * + * @tparam T type of the values associated with integer keys. + * + * @define Coll `immutable.IntMap` + * @define coll immutable integer map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +sealed abstract class IntMap[+T] extends AbstractMap[Int, T] + with StrictOptimizedMapOps[Int, T, Map, IntMap[T]] + with Serializable { + + override protected def fromSpecific(coll: scala.collection.IterableOnce[(Int, T) @uncheckedVariance]): IntMap[T] = + intMapFrom[T](coll) + protected def intMapFrom[V2](coll: scala.collection.IterableOnce[(Int, V2)]): IntMap[V2] = { + val b = IntMap.newBuilder[V2] + b.sizeHint(coll) + b.addAll(coll) + b.result() + } + override protected def newSpecificBuilder: Builder[(Int, T), IntMap[T]] @uncheckedVariance = + new ImmutableBuilder[(Int, T), IntMap[T]](empty) { + def addOne(elem: (Int, T)): this.type = { elems = elems + elem; this } + } + + override def empty: IntMap[T] = IntMap.Nil + + override def toList = { + val buffer = new scala.collection.mutable.ListBuffer[(Int, T)] + foreach(buffer += _) + buffer.toList + } + + /** + * Iterator over key, value pairs of the map in unsigned order of the keys. + * + * @return an iterator over pairs of integer keys and corresponding values. + */ + def iterator: Iterator[(Int, T)] = this match { + case IntMap.Nil => Iterator.empty + case _ => new IntMapEntryIterator(this) + } + + /** + * Loops over the key, value pairs of the map in unsigned order of the keys. + */ + override final def foreach[U](f: ((Int, T)) => U): Unit = this match { + case IntMap.Bin(_, _, left, right) => { left.foreach(f); right.foreach(f) } + case IntMap.Tip(key, value) => f((key, value)) + case IntMap.Nil => + } + + override def foreachEntry[U](f: (Int, T) => U): Unit = this match { + case IntMap.Bin(_, _, left, right) => { left.foreachEntry(f); right.foreachEntry(f) } + case IntMap.Tip(key, value) => f(key, value) + case IntMap.Nil => + } + + override def keysIterator: Iterator[Int] = this match { + case IntMap.Nil => Iterator.empty + case _ => new IntMapKeyIterator(this) + } + + /** + * Loop over the keys of the map. The same as `keys.foreach(f)`, but may + * be more efficient. + * + * @param f The loop body + */ + final def foreachKey[U](f: Int => U): Unit = this match { + case IntMap.Bin(_, _, left, right) => { left.foreachKey(f); right.foreachKey(f) } + case IntMap.Tip(key, _) => f(key) + case IntMap.Nil => + } + + override def valuesIterator: Iterator[T] = this match { + case IntMap.Nil => Iterator.empty + case _ => new IntMapValueIterator(this) + } + + /** + * Loop over the values of the map. The same as `values.foreach(f)`, but may + * be more efficient. + * + * @param f The loop body + */ + final def foreachValue[U](f: T => U): Unit = this match { + case IntMap.Bin(_, _, left, right) => { left.foreachValue(f); right.foreachValue(f) } + case IntMap.Tip(_, value) => f(value) + case IntMap.Nil => + } + + override protected[this] def className = "IntMap" + + override def isEmpty = this eq IntMap.Nil + override def knownSize: Int = if (isEmpty) 0 else super.knownSize + override def filter(f: ((Int, T)) => Boolean): IntMap[T] = this match { + case IntMap.Bin(prefix, mask, left, right) => { + val (newleft, newright) = (left.filter(f), right.filter(f)) + if ((left eq newleft) && (right eq newright)) this + else bin(prefix, mask, newleft, newright) + } + case IntMap.Tip(key, value) => + if (f((key, value))) this + else IntMap.Nil + case IntMap.Nil => IntMap.Nil + } + + override def transform[S](f: (Int, T) => S): IntMap[S] = this match { + case b@IntMap.Bin(prefix, mask, left, right) => b.bin(left.transform(f), right.transform(f)) + case t@IntMap.Tip(key, value) => t.withValue(f(key, value)) + case IntMap.Nil => IntMap.Nil + } + + final override def size: Int = this match { + case IntMap.Nil => 0 + case IntMap.Tip(_, _) => 1 + case IntMap.Bin(_, _, left, right) => left.size + right.size + } + + @tailrec + final def get(key: Int): Option[T] = this match { + case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.get(key) else right.get(key) + case IntMap.Tip(key2, value) => if (key == key2) Some(value) else None + case IntMap.Nil => None + } + + @tailrec + final override def getOrElse[S >: T](key: Int, default: => S): S = this match { + case IntMap.Nil => default + case IntMap.Tip(key2, value) => if (key == key2) value else default + case IntMap.Bin(prefix, mask, left, right) => + if (zero(key, mask)) left.getOrElse(key, default) else right.getOrElse(key, default) + } + + @tailrec + final override def apply(key: Int): T = this match { + case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key) + case IntMap.Tip(key2, value) => if (key == key2) value else throw new IllegalArgumentException("Key not found") + case IntMap.Nil => throw new IllegalArgumentException("key not found") + } + + override def + [S >: T] (kv: (Int, S)): IntMap[S] = updated(kv._1, kv._2) + + override def updated[S >: T](key: Int, value: S): IntMap[S] = this match { + case IntMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, IntMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) IntMap.Bin(prefix, mask, left.updated(key, value), right) + else IntMap.Bin(prefix, mask, left, right.updated(key, value)) + case IntMap.Tip(key2, value2) => + if (key == key2) IntMap.Tip(key, value) + else join(key, IntMap.Tip(key, value), key2, this) + case IntMap.Nil => IntMap.Tip(key, value) + } + + def map[V2](f: ((Int, T)) => (Int, V2)): IntMap[V2] = intMapFrom(new View.Map(this, f)) + + def flatMap[V2](f: ((Int, T)) => IterableOnce[(Int, V2)]): IntMap[V2] = intMapFrom(new View.FlatMap(this, f)) + + override def concat[V1 >: T](that: collection.IterableOnce[(Int, V1)]): IntMap[V1] = + super.concat(that).asInstanceOf[IntMap[V1]] // Already has correct type but not declared as such + + override def ++ [V1 >: T](that: collection.IterableOnce[(Int, V1)]): IntMap[V1] = concat(that) + + def collect[V2](pf: PartialFunction[(Int, T), (Int, V2)]): IntMap[V2] = + strictOptimizedCollect(IntMap.newBuilder[V2], pf) + + /** + * Updates the map, using the provided function to resolve conflicts if the key is already present. + * + * Equivalent to: + * {{{ + * this.get(key) match { + * case None => this.update(key, value) + * case Some(oldvalue) => this.update(key, f(oldvalue, value) + * } + * }}} + * + * @tparam S The supertype of values in this `LongMap`. + * @param key The key to update + * @param value The value to use if there is no conflict + * @param f The function used to resolve conflicts. + * @return The updated map. + */ + def updateWith[S >: T](key: Int, value: S, f: (T, S) => S): IntMap[S] = this match { + case IntMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, IntMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) IntMap.Bin(prefix, mask, left.updateWith(key, value, f), right) + else IntMap.Bin(prefix, mask, left, right.updateWith(key, value, f)) + case IntMap.Tip(key2, value2) => + if (key == key2) IntMap.Tip(key, f(value2, value)) + else join(key, IntMap.Tip(key, value), key2, this) + case IntMap.Nil => IntMap.Tip(key, value) + } + + def removed (key: Int): IntMap[T] = this match { + case IntMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) this + else if (zero(key, mask)) bin(prefix, mask, left - key, right) + else bin(prefix, mask, left, right - key) + case IntMap.Tip(key2, _) => + if (key == key2) IntMap.Nil + else this + case IntMap.Nil => IntMap.Nil + } + + /** + * A combined transform and filter function. Returns an `IntMap` such that + * for each `(key, value)` mapping in this map, if `f(key, value) == None` + * the map contains no mapping for key, and if `f(key, value)`. + * + * @tparam S The type of the values in the resulting `LongMap`. + * @param f The transforming function. + * @return The modified map. + */ + def modifyOrRemove[S](f: (Int, T) => Option[S]): IntMap[S] = this match { + case IntMap.Bin(prefix, mask, left, right) => + val newleft = left.modifyOrRemove(f) + val newright = right.modifyOrRemove(f) + if ((left eq newleft) && (right eq newright)) this.asInstanceOf[IntMap[S]] + else bin(prefix, mask, newleft, newright) + case IntMap.Tip(key, value) => f(key, value) match { + case None => + IntMap.Nil + case Some(value2) => + //hack to preserve sharing + if (value.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef]) this.asInstanceOf[IntMap[S]] + else IntMap.Tip(key, value2) + } + case IntMap.Nil => + IntMap.Nil + } + + /** + * Forms a union map with that map, using the combining function to resolve conflicts. + * + * @tparam S The type of values in `that`, a supertype of values in `this`. + * @param that The map to form a union with. + * @param f The function used to resolve conflicts between two mappings. + * @return Union of `this` and `that`, with identical key conflicts resolved using the function `f`. + */ + def unionWith[S >: T](that: IntMap[S], f: (Int, S, S) => S): IntMap[S] = (this, that) match{ + case (IntMap.Bin(p1, m1, l1, r1), that@(IntMap.Bin(p2, m2, l2, r2))) => + if (shorter(m1, m2)) { + if (!hasMatch(p2, p1, m1)) join(p1, this, p2, that) + else if (zero(p2, m1)) IntMap.Bin(p1, m1, l1.unionWith(that, f), r1) + else IntMap.Bin(p1, m1, l1, r1.unionWith(that, f)) + } else if (shorter(m2, m1)){ + if (!hasMatch(p1, p2, m2)) join(p1, this, p2, that) + else if (zero(p1, m2)) IntMap.Bin(p2, m2, this.unionWith(l2, f), r2) + else IntMap.Bin(p2, m2, l2, this.unionWith(r2, f)) + } + else { + if (p1 == p2) IntMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)) + else join(p1, this, p2, that) + } + case (IntMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x)) + case (x, IntMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)) + case (IntMap.Nil, x) => x + case (x, IntMap.Nil) => x + } + + /** + * Forms the intersection of these two maps with a combining function. The + * resulting map is a map that has only keys present in both maps and has + * values produced from the original mappings by combining them with `f`. + * + * @tparam S The type of values in `that`. + * @tparam R The type of values in the resulting `LongMap`. + * @param that The map to intersect with. + * @param f The combining function. + * @return Intersection of `this` and `that`, with values for identical keys produced by function `f`. + */ + def intersectionWith[S, R](that: IntMap[S], f: (Int, T, S) => R): IntMap[R] = (this, that) match { + case (IntMap.Bin(p1, m1, l1, r1), that@IntMap.Bin(p2, m2, l2, r2)) => + if (shorter(m1, m2)) { + if (!hasMatch(p2, p1, m1)) IntMap.Nil + else if (zero(p2, m1)) l1.intersectionWith(that, f) + else r1.intersectionWith(that, f) + } else if (m1 == m2) bin(p1, m1, l1.intersectionWith(l2, f), r1.intersectionWith(r2, f)) + else { + if (!hasMatch(p1, p2, m2)) IntMap.Nil + else if (zero(p1, m2)) this.intersectionWith(l2, f) + else this.intersectionWith(r2, f) + } + case (IntMap.Tip(key, value), that) => that.get(key) match { + case None => IntMap.Nil + case Some(value2) => IntMap.Tip(key, f(key, value, value2)) + } + case (_, IntMap.Tip(key, value)) => this.get(key) match { + case None => IntMap.Nil + case Some(value2) => IntMap.Tip(key, f(key, value2, value)) + } + case (_, _) => IntMap.Nil + } + + /** + * Left biased intersection. Returns the map that has all the same mappings + * as this but only for keys which are present in the other map. + * + * @tparam R The type of values in `that`. + * @param that The map to intersect with. + * @return A map with all the keys both in `this` and `that`, mapped to corresponding values from `this`. + */ + def intersection[R](that: IntMap[R]): IntMap[T] = + this.intersectionWith(that, (key: Int, value: T, value2: R) => value) + + def ++[S >: T](that: IntMap[S]) = + this.unionWith[S](that, (key, x, y) => y) + + /** + * The entry with the lowest key value considered in unsigned order. + */ + @tailrec + final def firstKey: Int = this match { + case Bin(_, _, l, r) => l.firstKey + case Tip(k, v) => k + case IntMap.Nil => throw new IllegalStateException("Empty set") + } + + /** + * The entry with the highest key value considered in unsigned order. + */ + @tailrec + final def lastKey: Int = this match { + case Bin(_, _, l, r) => r.lastKey + case Tip(k, v) => k + case IntMap.Nil => throw new IllegalStateException("Empty set") + } + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(IntMap.toFactory[T](IntMap), this) +} diff --git a/library/src/scala/collection/immutable/Iterable.scala b/library/src/scala/collection/immutable/Iterable.scala new file mode 100644 index 000000000000..a38c60ed8cc2 --- /dev/null +++ b/library/src/scala/collection/immutable/Iterable.scala @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.immutable + +import scala.collection.{IterableFactory, IterableFactoryDefaults} + +/** A trait for collections that are guaranteed immutable. + * + * @tparam A the element type of the collection + * + * @define coll immutable collection + * @define Coll `immutable.Iterable` + */ +trait Iterable[+A] extends collection.Iterable[A] + with collection.IterableOps[A, Iterable, Iterable[A]] + with IterableFactoryDefaults[A, Iterable] { + + override def iterableFactory: IterableFactory[Iterable] = Iterable +} + +@SerialVersionUID(3L) +object Iterable extends IterableFactory.Delegate[Iterable](List) { + override def from[E](it: IterableOnce[E]): Iterable[E] = it match { + case iterable: Iterable[E] => iterable + case _ => super.from(it) + } +} diff --git a/library/src/scala/collection/immutable/LazyList.scala b/library/src/scala/collection/immutable/LazyList.scala new file mode 100644 index 000000000000..54591032e2af --- /dev/null +++ b/library/src/scala/collection/immutable/LazyList.scala @@ -0,0 +1,1405 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import java.io.{ObjectInputStream, ObjectOutputStream} +import java.lang.{StringBuilder => JStringBuilder} + +import scala.annotation.tailrec +import scala.collection.generic.SerializeEnd +import scala.collection.mutable.{Builder, ReusableBuilder, StringBuilder} +import scala.language.implicitConversions +import scala.runtime.Statics + +/** This class implements an immutable linked list. We call it "lazy" + * because it computes its elements only when they are needed. + * + * Elements are memoized; that is, the value of each element is computed at most once. + * + * Elements are computed in-order and are never skipped. In other words, + * accessing the tail causes the head to be computed first. + * + * How lazy is a `LazyList`? When you have a value of type `LazyList`, you + * don't know yet whether the list is empty or not. If you learn that it is non-empty, + * then you also know that the head has been computed. But the tail is itself + * a `LazyList`, whose emptiness-or-not might remain undetermined. + * + * A `LazyList` may be infinite. For example, `LazyList.from(0)` contains + * all of the natural numbers 0, 1, 2, and so on. For infinite sequences, + * some methods (such as `count`, `sum`, `max` or `min`) will not terminate. + * + * Here is an example: + * + * {{{ + * import scala.math.BigInt + * object Main extends App { + * val fibs: LazyList[BigInt] = + * BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map{ n => n._1 + n._2 } + * fibs.take(5).foreach(println) + * } + * + * // prints + * // + * // 0 + * // 1 + * // 1 + * // 2 + * // 3 + * }}} + * + * To illustrate, let's add some output to the definition `fibs`, so we + * see what's going on. + * + * {{{ + * import scala.math.BigInt + * object Main extends App { + * val fibs: LazyList[BigInt] = + * BigInt(0) #:: BigInt(1) #:: + * fibs.zip(fibs.tail).map{ n => + * println(s"Adding \${n._1} and \${n._2}") + * n._1 + n._2 + * } + * fibs.take(5).foreach(println) + * fibs.take(6).foreach(println) + * } + * + * // prints + * // + * // 0 + * // 1 + * // Adding 0 and 1 + * // 1 + * // Adding 1 and 1 + * // 2 + * // Adding 1 and 2 + * // 3 + * + * // And then prints + * // + * // 0 + * // 1 + * // 1 + * // 2 + * // 3 + * // Adding 2 and 3 + * // 5 + * }}} + * + * Note that the definition of `fibs` uses `val` not `def`. The memoization of the + * `LazyList` requires us to have somewhere to store the information and a `val` + * allows us to do that. + * + * Further remarks about the semantics of `LazyList`: + * + * - Though the `LazyList` changes as it is accessed, this does not + * contradict its immutability. Once the values are memoized they do + * not change. Values that have yet to be memoized still "exist", they + * simply haven't been computed yet. + * + * - One must be cautious of memoization; it can eat up memory if you're not + * careful. That's because memoization of the `LazyList` creates a structure much like + * [[scala.collection.immutable.List]]. As long as something is holding on to + * the head, the head holds on to the tail, and so on recursively. + * If, on the other hand, there is nothing holding on to the head (e.g. if we used + * `def` to define the `LazyList`) then once it is no longer being used directly, + * it disappears. + * + * - Note that some operations, including [[drop]], [[dropWhile]], + * [[flatMap]] or [[collect]] may process a large number of intermediate + * elements before returning. + * + * Here's another example. Let's start with the natural numbers and iterate + * over them. + * + * {{{ + * // We'll start with a silly iteration + * def loop(s: String, i: Int, iter: Iterator[Int]): Unit = { + * // Stop after 200,000 + * if (i < 200001) { + * if (i % 50000 == 0) println(s + i) + * loop(s, iter.next(), iter) + * } + * } + * + * // Our first LazyList definition will be a val definition + * val lazylist1: LazyList[Int] = { + * def loop(v: Int): LazyList[Int] = v #:: loop(v + 1) + * loop(0) + * } + * + * // Because lazylist1 is a val, everything that the iterator produces is held + * // by virtue of the fact that the head of the LazyList is held in lazylist1 + * val it1 = lazylist1.iterator + * loop("Iterator1: ", it1.next(), it1) + * + * // We can redefine this LazyList such that all we have is the Iterator left + * // and allow the LazyList to be garbage collected as required. Using a def + * // to provide the LazyList ensures that no val is holding onto the head as + * // is the case with lazylist1 + * def lazylist2: LazyList[Int] = { + * def loop(v: Int): LazyList[Int] = v #:: loop(v + 1) + * loop(0) + * } + * val it2 = lazylist2.iterator + * loop("Iterator2: ", it2.next(), it2) + * + * // And, of course, we don't actually need a LazyList at all for such a simple + * // problem. There's no reason to use a LazyList if you don't actually need + * // one. + * val it3 = new Iterator[Int] { + * var i = -1 + * def hasNext = true + * def next(): Int = { i += 1; i } + * } + * loop("Iterator3: ", it3.next(), it3) + * }}} + * + * In the `fibs` example earlier, the fact that `tail` works at all is of interest. + * `fibs` has an initial `(0, 1, LazyList(...))`, so `tail` is deterministic. + * If we defined `fibs` such that only `0` were concretely known, then the act + * of determining `tail` would require the evaluation of `tail`, so the + * computation would be unable to progress, as in this code: + * {{{ + * // The first time we try to access the tail we're going to need more + * // information which will require us to recurse, which will require us to + * // recurse, which... + * lazy val sov: LazyList[Vector[Int]] = Vector(0) #:: sov.zip(sov.tail).map { n => n._1 ++ n._2 } + * }}} + * + * The definition of `fibs` above creates a larger number of objects than + * necessary depending on how you might want to implement it. The following + * implementation provides a more "cost effective" implementation due to the + * fact that it has a more direct route to the numbers themselves: + * + * {{{ + * lazy val fib: LazyList[Int] = { + * def loop(h: Int, n: Int): LazyList[Int] = h #:: loop(n, h + n) + * loop(1, 1) + * } + * }}} + * + * The head, the tail and whether the list is empty or not can be initially unknown. + * Once any of those are evaluated, they are all known, though if the tail is + * built with `#::` or `#:::`, it's content still isn't evaluated. Instead, evaluating + * the tails content is deferred until the tails empty status, head or tail is + * evaluated. + * + * Delaying the evaluation of whether a LazyList is empty or not until it's needed + * allows LazyList to not eagerly evaluate any elements on a call to `filter`. + * + * Only when it's further evaluated (which may be never!) any of the elements gets + * forced. + * + * for example: + * + * {{{ + * def tailWithSideEffect: LazyList[Nothing] = { + * println("getting empty LazyList") + * LazyList.empty + * } + * + * val emptyTail = tailWithSideEffect // prints "getting empty LazyList" + * + * val suspended = 1 #:: tailWithSideEffect // doesn't print anything + * val tail = suspended.tail // although the tail is evaluated, *still* nothing is yet printed + * val filtered = tail.filter(_ => false) // still nothing is printed + * filtered.isEmpty // prints "getting empty LazyList" + * }}} + * + * ---- + * + * You may sometimes encounter an exception like the following: + * + * {{{ + * java.lang.RuntimeException: "LazyList evaluation depends on its own result (self-reference); see docs for more info + * }}} + * + * This exception occurs when a `LazyList` is attempting to derive its next element + * from itself, and is attempting to read the element currently being evaluated. A + * trivial example of such might be + * + * {{{ + * lazy val a: LazyList[Int] = 1 #:: 2 #:: a.filter(_ > 2) + * }}} + * + * When attempting to evaluate the third element of `a`, it will skip the first two + * elements and read the third, but that element is already being evaluated. This is + * often caused by a subtle logic error; in this case, using `>=` in the `filter` + * would fix the error. + * + * @tparam A the type of the elements contained in this lazy list. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview"]] + * section on `LazyLists` for more information. + * @define Coll `LazyList` + * @define coll lazy list + * @define orderDependent + * @define orderDependentFold + * @define appendStackSafety Note: Repeated chaining of calls to append methods (`appended`, + * `appendedAll`, `lazyAppendedAll`) without forcing any of the + * intermediate resulting lazy lists may overflow the stack when + * the final result is forced. + * @define preservesLaziness This method preserves laziness; elements are only evaluated + * individually as needed. + * @define initiallyLazy This method does not evaluate anything until an operation is performed + * on the result (e.g. calling `head` or `tail`, or checking if it is empty). + * @define evaluatesAllElements This method evaluates all elements of the collection. + */ +@SerialVersionUID(3L) +final class LazyList[+A] private(private[this] var lazyState: () => LazyList.State[A]) + extends AbstractSeq[A] + with LinearSeq[A] + with LinearSeqOps[A, LazyList, LazyList[A]] + with IterableFactoryDefaults[A, LazyList] + with Serializable { + import LazyList._ + + @volatile private[this] var stateEvaluated: Boolean = false + @inline private def stateDefined: Boolean = stateEvaluated + private[this] var midEvaluation = false + + private lazy val state: State[A] = { + // if it's already mid-evaluation, we're stuck in an infinite + // self-referential loop (also it's empty) + if (midEvaluation) { + throw new RuntimeException( + "LazyList evaluation depends on its own result (self-reference); see docs for more info" + ) + } + midEvaluation = true + val res = try lazyState() finally midEvaluation = false + // if we set it to `true` before evaluating, we may infinite loop + // if something expects `state` to already be evaluated + stateEvaluated = true + lazyState = null // allow GC + res + } + + override def iterableFactory: SeqFactory[LazyList] = LazyList + + override def isEmpty: Boolean = state eq State.Empty + + /** @inheritdoc + * + * $preservesLaziness + */ + override def knownSize: Int = if (knownIsEmpty) 0 else -1 + + override def head: A = state.head + + override def tail: LazyList[A] = state.tail + + @inline private[this] def knownIsEmpty: Boolean = stateEvaluated && (isEmpty: @inline) + @inline private def knownNonEmpty: Boolean = stateEvaluated && !(isEmpty: @inline) + + /** Evaluates all undefined elements of the lazy list. + * + * This method detects cycles in lazy lists, and terminates after all + * elements of the cycle are evaluated. For example: + * + * {{{ + * val ring: LazyList[Int] = 1 #:: 2 #:: 3 #:: ring + * ring.force + * ring.toString + * + * // prints + * // + * // LazyList(1, 2, 3, ...) + * }}} + * + * This method will *not* terminate for non-cyclic infinite-sized collections. + * + * @return this + */ + def force: this.type = { + // Use standard 2x 1x iterator trick for cycle detection ("those" is slow one) + var these, those: LazyList[A] = this + if (!these.isEmpty) { + these = these.tail + } + while (those ne these) { + if (these.isEmpty) return this + these = these.tail + if (these.isEmpty) return this + these = these.tail + if (these eq those) return this + those = those.tail + } + this + } + + /** @inheritdoc + * + * The iterator returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + override def iterator: Iterator[A] = + if (knownIsEmpty) Iterator.empty + else new LazyIterator(this) + + /** Apply the given function `f` to each element of this linear sequence + * (while respecting the order of the elements). + * + * @param f The treatment to apply to each element. + * @note Overridden here as final to trigger tail-call optimization, which + * replaces 'this' with 'tail' at each iteration. This is absolutely + * necessary for allowing the GC to collect the underlying LazyList as elements + * are consumed. + * @note This function will force the realization of the entire LazyList + * unless the `f` throws an exception. + */ + @tailrec + override def foreach[U](f: A => U): Unit = { + if (!isEmpty) { + f(head) + tail.foreach(f) + } + } + + /** LazyList specialization of foldLeft which allows GC to collect along the + * way. + * + * @tparam B The type of value being accumulated. + * @param z The initial value seeded into the function `op`. + * @param op The operation to perform on successive elements of the `LazyList`. + * @return The accumulated value from successive applications of `op`. + */ + @tailrec + override def foldLeft[B](z: B)(op: (B, A) => B): B = + if (isEmpty) z + else tail.foldLeft(op(z, head))(op) + + // State.Empty doesn't use the SerializationProxy + protected[this] def writeReplace(): AnyRef = + if (knownNonEmpty) new LazyList.SerializationProxy[A](this) else this + + override protected[this] def className = "LazyList" + + /** The lazy list resulting from the concatenation of this lazy list with the argument lazy list. + * + * $preservesLaziness + * + * $appendStackSafety + * + * @param suffix The collection that gets appended to this lazy list + * @return The lazy list containing elements of this lazy list and the iterable object. + */ + def lazyAppendedAll[B >: A](suffix: => collection.IterableOnce[B]): LazyList[B] = + newLL { + if (isEmpty) suffix match { + case lazyList: LazyList[B] => lazyList.state // don't recompute the LazyList + case coll if coll.knownSize == 0 => State.Empty + case coll => stateFromIterator(coll.iterator) + } + else sCons(head, tail lazyAppendedAll suffix) + } + + /** @inheritdoc + * + * $preservesLaziness + * + * $appendStackSafety + */ + override def appendedAll[B >: A](suffix: IterableOnce[B]): LazyList[B] = + if (knownIsEmpty) LazyList.from(suffix) + else lazyAppendedAll(suffix) + + /** @inheritdoc + * + * $preservesLaziness + * + * $appendStackSafety + */ + override def appended[B >: A](elem: B): LazyList[B] = + if (knownIsEmpty) newLL(sCons(elem, LazyList.empty)) + else lazyAppendedAll(Iterator.single(elem)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def scanLeft[B](z: B)(op: (B, A) => B): LazyList[B] = + if (knownIsEmpty) newLL(sCons(z, LazyList.empty)) + else newLL(scanLeftState(z)(op)) + + private def scanLeftState[B](z: B)(op: (B, A) => B): State[B] = + sCons( + z, + newLL { + if (isEmpty) State.Empty + else tail.scanLeftState(op(z, head))(op) + } + ) + + /** LazyList specialization of reduceLeft which allows GC to collect + * along the way. + * + * @tparam B The type of value being accumulated. + * @param f The operation to perform on successive elements of the `LazyList`. + * @return The accumulated value from successive applications of `f`. + */ + override def reduceLeft[B >: A](f: (B, A) => B): B = { + if (this.isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") + else { + var reducedRes: B = this.head + var left: LazyList[A] = this.tail + while (!left.isEmpty) { + reducedRes = f(reducedRes, left.head) + left = left.tail + } + reducedRes + } + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def partition(p: A => Boolean): (LazyList[A], LazyList[A]) = (filter(p), filterNot(p)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def partitionMap[A1, A2](f: A => Either[A1, A2]): (LazyList[A1], LazyList[A2]) = { + val (left, right) = map(f).partition(_.isLeft) + (left.map(_.asInstanceOf[Left[A1, _]].value), right.map(_.asInstanceOf[Right[_, A2]].value)) + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def filter(pred: A => Boolean): LazyList[A] = + if (knownIsEmpty) LazyList.empty + else LazyList.filterImpl(this, pred, isFlipped = false) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def filterNot(pred: A => Boolean): LazyList[A] = + if (knownIsEmpty) LazyList.empty + else LazyList.filterImpl(this, pred, isFlipped = true) + + /** A `collection.WithFilter` which allows GC of the head of lazy list during processing. + * + * This method is not particularly useful for a lazy list, as [[filter]] already preserves + * laziness. + * + * The `collection.WithFilter` returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + override def withFilter(p: A => Boolean): collection.WithFilter[A, LazyList] = + new LazyList.WithFilter(coll, p) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def prepended[B >: A](elem: B): LazyList[B] = newLL(sCons(elem, this)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): LazyList[B] = + if (knownIsEmpty) LazyList.from(prefix) + else if (prefix.knownSize == 0) this + else newLL(stateFromIteratorConcatSuffix(prefix.iterator)(state)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def map[B](f: A => B): LazyList[B] = + if (knownIsEmpty) LazyList.empty + else (mapImpl(f): @inline) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def tapEach[U](f: A => U): LazyList[A] = map { a => f(a); a } + + private def mapImpl[B](f: A => B): LazyList[B] = + newLL { + if (isEmpty) State.Empty + else sCons(f(head), tail.mapImpl(f)) + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def collect[B](pf: PartialFunction[A, B]): LazyList[B] = + if (knownIsEmpty) LazyList.empty + else LazyList.collectImpl(this, pf) + + /** @inheritdoc + * + * This method does not evaluate any elements further than + * the first element for which the partial function is defined. + */ + @tailrec + override def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = + if (isEmpty) None + else { + val res = pf.applyOrElse(head, LazyList.anyToMarker.asInstanceOf[A => B]) + if (res.asInstanceOf[AnyRef] eq Statics.pfMarker) tail.collectFirst(pf) + else Some(res) + } + + /** @inheritdoc + * + * This method does not evaluate any elements further than + * the first element matching the predicate. + */ + @tailrec + override def find(p: A => Boolean): Option[A] = + if (isEmpty) None + else { + val elem = head + if (p(elem)) Some(elem) + else tail.find(p) + } + + /** @inheritdoc + * + * $preservesLaziness + */ + // optimisations are not for speed, but for functionality + // see tickets #153, #498, #2147, and corresponding tests in run/ (as well as run/stream_flatmap_odds.scala) + override def flatMap[B](f: A => IterableOnce[B]): LazyList[B] = + if (knownIsEmpty) LazyList.empty + else LazyList.flatMapImpl(this, f) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def flatten[B](implicit asIterable: A => IterableOnce[B]): LazyList[B] = flatMap(asIterable) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def zip[B](that: collection.IterableOnce[B]): LazyList[(A, B)] = + if (this.knownIsEmpty || that.knownSize == 0) LazyList.empty + else newLL(zipState(that.iterator)) + + private def zipState[B](it: Iterator[B]): State[(A, B)] = + if (this.isEmpty || !it.hasNext) State.Empty + else sCons((head, it.next()), newLL { tail zipState it }) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def zipWithIndex: LazyList[(A, Int)] = this zip LazyList.from(0) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def zipAll[A1 >: A, B](that: collection.Iterable[B], thisElem: A1, thatElem: B): LazyList[(A1, B)] = { + if (this.knownIsEmpty) { + if (that.knownSize == 0) LazyList.empty + else LazyList.continually(thisElem) zip that + } else { + if (that.knownSize == 0) zip(LazyList.continually(thatElem)) + else newLL(zipAllState(that.iterator, thisElem, thatElem)) + } + } + + private def zipAllState[A1 >: A, B](it: Iterator[B], thisElem: A1, thatElem: B): State[(A1, B)] = { + if (it.hasNext) { + if (this.isEmpty) sCons((thisElem, it.next()), newLL { LazyList.continually(thisElem) zipState it }) + else sCons((this.head, it.next()), newLL { this.tail.zipAllState(it, thisElem, thatElem) }) + } else { + if (this.isEmpty) State.Empty + else sCons((this.head, thatElem), this.tail zip LazyList.continually(thatElem)) + } + } + + /** @inheritdoc + * + * This method is not particularly useful for a lazy list, as [[zip]] already preserves + * laziness. + * + * The `collection.LazyZip2` returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + // just in case it can be meaningfully overridden at some point + override def lazyZip[B](that: collection.Iterable[B]): LazyZip2[A, B, LazyList.this.type] = + super.lazyZip(that) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def unzip[A1, A2](implicit asPair: A => (A1, A2)): (LazyList[A1], LazyList[A2]) = + (map(asPair(_)._1), map(asPair(_)._2)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (LazyList[A1], LazyList[A2], LazyList[A3]) = + (map(asTriple(_)._1), map(asTriple(_)._2), map(asTriple(_)._3)) + + /** @inheritdoc + * + * $initiallyLazy + * Additionally, it preserves laziness for all except the first `n` elements. + */ + override def drop(n: Int): LazyList[A] = + if (n <= 0) this + else if (knownIsEmpty) LazyList.empty + else LazyList.dropImpl(this, n) + + /** @inheritdoc + * + * $initiallyLazy + * Additionally, it preserves laziness for all elements after the predicate returns `false`. + */ + override def dropWhile(p: A => Boolean): LazyList[A] = + if (knownIsEmpty) LazyList.empty + else LazyList.dropWhileImpl(this, p) + + /** @inheritdoc + * + * $initiallyLazy + */ + override def dropRight(n: Int): LazyList[A] = { + if (n <= 0) this + else if (knownIsEmpty) LazyList.empty + else newLL { + var scout = this + var remaining = n + // advance scout n elements ahead (or until empty) + while (remaining > 0 && !scout.isEmpty) { + remaining -= 1 + scout = scout.tail + } + dropRightState(scout) + } + } + + private def dropRightState(scout: LazyList[_]): State[A] = + if (scout.isEmpty) State.Empty + else sCons(head, newLL(tail.dropRightState(scout.tail))) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def take(n: Int): LazyList[A] = + if (knownIsEmpty) LazyList.empty + else (takeImpl(n): @inline) + + private def takeImpl(n: Int): LazyList[A] = { + if (n <= 0) LazyList.empty + else newLL { + if (isEmpty) State.Empty + else sCons(head, tail.takeImpl(n - 1)) + } + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def takeWhile(p: A => Boolean): LazyList[A] = + if (knownIsEmpty) LazyList.empty + else (takeWhileImpl(p): @inline) + + private def takeWhileImpl(p: A => Boolean): LazyList[A] = + newLL { + if (isEmpty || !p(head)) State.Empty + else sCons(head, tail.takeWhileImpl(p)) + } + + /** @inheritdoc + * + * $initiallyLazy + */ + override def takeRight(n: Int): LazyList[A] = + if (n <= 0 || knownIsEmpty) LazyList.empty + else LazyList.takeRightImpl(this, n) + + /** @inheritdoc + * + * $initiallyLazy + * Additionally, it preserves laziness for all but the first `from` elements. + */ + override def slice(from: Int, until: Int): LazyList[A] = take(until).drop(from) + + /** @inheritdoc + * + * $evaluatesAllElements + */ + override def reverse: LazyList[A] = reverseOnto(LazyList.empty) + + // need contravariant type B to make the compiler happy - still returns LazyList[A] + @tailrec + private def reverseOnto[B >: A](tl: LazyList[B]): LazyList[B] = + if (isEmpty) tl + else tail.reverseOnto(newLL(sCons(head, tl))) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def diff[B >: A](that: collection.Seq[B]): LazyList[A] = + if (knownIsEmpty) LazyList.empty + else super.diff(that) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def intersect[B >: A](that: collection.Seq[B]): LazyList[A] = + if (knownIsEmpty) LazyList.empty + else super.intersect(that) + + @tailrec + private def lengthGt(len: Int): Boolean = + if (len < 0) true + else if (isEmpty) false + else tail.lengthGt(len - 1) + + /** @inheritdoc + * + * The iterator returned by this method mostly preserves laziness; + * a single element ahead of the iterator is evaluated. + */ + override def grouped(size: Int): Iterator[LazyList[A]] = { + require(size > 0, "size must be positive, but was " + size) + slidingImpl(size = size, step = size) + } + + /** @inheritdoc + * + * The iterator returned by this method mostly preserves laziness; + * `size - step max 1` elements ahead of the iterator are evaluated. + */ + override def sliding(size: Int, step: Int): Iterator[LazyList[A]] = { + require(size > 0 && step > 0, s"size=$size and step=$step, but both must be positive") + slidingImpl(size = size, step = step) + } + + @inline private def slidingImpl(size: Int, step: Int): Iterator[LazyList[A]] = + if (knownIsEmpty) Iterator.empty + else new SlidingIterator[A](this, size = size, step = step) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def padTo[B >: A](len: Int, elem: B): LazyList[B] = { + if (len <= 0) this + else newLL { + if (isEmpty) LazyList.fill(len)(elem).state + else sCons(head, tail.padTo(len - 1, elem)) + } + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def patch[B >: A](from: Int, other: IterableOnce[B], replaced: Int): LazyList[B] = + if (knownIsEmpty) LazyList from other + else patchImpl(from, other, replaced) + + private def patchImpl[B >: A](from: Int, other: IterableOnce[B], replaced: Int): LazyList[B] = + newLL { + if (from <= 0) stateFromIteratorConcatSuffix(other.iterator)(LazyList.dropImpl(this, replaced).state) + else if (isEmpty) stateFromIterator(other.iterator) + else sCons(head, tail.patchImpl(from - 1, other, replaced)) + } + + /** @inheritdoc + * + * $evaluatesAllElements + */ + // overridden just in case a lazy implementation is developed at some point + override def transpose[B](implicit asIterable: A => collection.Iterable[B]): LazyList[LazyList[B]] = super.transpose + + /** @inheritdoc + * + * $preservesLaziness + */ + override def updated[B >: A](index: Int, elem: B): LazyList[B] = + if (index < 0) throw new IndexOutOfBoundsException(s"$index") + else updatedImpl(index, elem, index) + + private def updatedImpl[B >: A](index: Int, elem: B, startIndex: Int): LazyList[B] = { + newLL { + if (index <= 0) sCons(elem, tail) + else if (tail.isEmpty) throw new IndexOutOfBoundsException(startIndex.toString) + else sCons(head, tail.updatedImpl(index - 1, elem, startIndex)) + } + } + + /** Appends all elements of this $coll to a string builder using start, end, and separator strings. + * The written text begins with the string `start` and ends with the string `end`. + * Inside, the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * An undefined state is represented with `"<not computed>"` and cycles are represented with `"<cycle>"`. + * + * $evaluatesAllElements + * + * @param sb the string builder to which elements are appended. + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return the string builder `b` to which elements were appended. + */ + override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = { + force + addStringNoForce(sb.underlying, start, sep, end) + sb + } + + private[this] def addStringNoForce(b: JStringBuilder, start: String, sep: String, end: String): b.type = { + b.append(start) + if (!stateDefined) b.append("") + else if (!isEmpty) { + b.append(head) + var cursor = this + @inline def appendCursorElement(): Unit = b.append(sep).append(cursor.head) + var scout = tail + @inline def scoutNonEmpty: Boolean = scout.stateDefined && !scout.isEmpty + if ((cursor ne scout) && (!scout.stateDefined || (cursor.state ne scout.state))) { + cursor = scout + if (scoutNonEmpty) { + scout = scout.tail + // Use 2x 1x iterator trick for cycle detection; slow iterator can add strings + while ((cursor ne scout) && scoutNonEmpty && (cursor.state ne scout.state)) { + appendCursorElement() + cursor = cursor.tail + scout = scout.tail + if (scoutNonEmpty) scout = scout.tail + } + } + } + if (!scoutNonEmpty) { // Not a cycle, scout hit an end + while (cursor ne scout) { + appendCursorElement() + cursor = cursor.tail + } + // if cursor (eq scout) has state defined, it is empty; else unknown state + if (!cursor.stateDefined) b.append(sep).append("") + } else { + @inline def same(a: LazyList[A], b: LazyList[A]): Boolean = (a eq b) || (a.state eq b.state) + // Cycle. + // If we have a prefix of length P followed by a cycle of length C, + // the scout will be at position (P%C) in the cycle when the cursor + // enters it at P. They'll then collide when the scout advances another + // C - (P%C) ahead of the cursor. + // If we run the scout P farther, then it will be at the start of + // the cycle: (C - (P%C) + (P%C)) == C == 0. So if another runner + // starts at the beginning of the prefix, they'll collide exactly at + // the start of the loop. + var runner = this + var k = 0 + while (!same(runner, scout)) { + runner = runner.tail + scout = scout.tail + k += 1 + } + // Now runner and scout are at the beginning of the cycle. Advance + // cursor, adding to string, until it hits; then we'll have covered + // everything once. If cursor is already at beginning, we'd better + // advance one first unless runner didn't go anywhere (in which case + // we've already looped once). + if (same(cursor, scout) && (k > 0)) { + appendCursorElement() + cursor = cursor.tail + } + while (!same(cursor, scout)) { + appendCursorElement() + cursor = cursor.tail + } + b.append(sep).append("") + } + } + b.append(end) + b + } + + /** $preservesLaziness + * + * @return a string representation of this collection. An undefined state is + * represented with `"<not computed>"` and cycles are represented with `"<cycle>"` + * + * Examples: + * + * - `"LazyList(4, <not computed>)"`, a non-empty lazy list ; + * - `"LazyList(1, 2, 3, <not computed>)"`, a lazy list with at least three elements ; + * - `"LazyList(1, 2, 3, <cycle>)"`, an infinite lazy list that contains + * a cycle at the fourth element. + */ + override def toString(): String = addStringNoForce(new JStringBuilder(className), "(", ", ", ")").toString + + /** @inheritdoc + * + * $preservesLaziness + */ + @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0") + override def hasDefiniteSize: Boolean = { + if (!stateDefined) false + else if (isEmpty) true + else { + // Two-iterator trick (2x & 1x speed) for cycle detection. + var those = this + var these = tail + while (those ne these) { + if (!these.stateDefined) return false + else if (these.isEmpty) return true + these = these.tail + if (!these.stateDefined) return false + else if (these.isEmpty) return true + these = these.tail + if (those eq these) return false + those = those.tail + } + false // Cycle detected + } + } +} + +/** + * $factoryInfo + * @define coll lazy list + * @define Coll `LazyList` + */ +@SerialVersionUID(3L) +object LazyList extends SeqFactory[LazyList] { + // Eagerly evaluate cached empty instance + private[this] val _empty = newLL(State.Empty).force + + private sealed trait State[+A] extends Serializable { + def head: A + def tail: LazyList[A] + } + + private object State { + @SerialVersionUID(3L) + object Empty extends State[Nothing] { + def head: Nothing = throw new NoSuchElementException("head of empty lazy list") + def tail: LazyList[Nothing] = throw new UnsupportedOperationException("tail of empty lazy list") + } + + @SerialVersionUID(3L) + final class Cons[A](val head: A, val tail: LazyList[A]) extends State[A] + } + + /** Creates a new LazyList. */ + @inline private def newLL[A](state: => State[A]): LazyList[A] = new LazyList[A](() => state) + + /** Creates a new State.Cons. */ + @inline private def sCons[A](hd: A, tl: LazyList[A]): State[A] = new State.Cons[A](hd, tl) + + private val anyToMarker: Any => Any = _ => Statics.pfMarker + + /* All of the following `Impl` methods are carefully written so as not to + * leak the beginning of the `LazyList`. They copy the initial `LazyList` (`ll`) into + * `var rest`, which gets closed over as a `scala.runtime.ObjectRef`, thus not permanently + * leaking the head of the `LazyList`. Additionally, the methods are written so that, should + * an exception be thrown by the evaluation of the `LazyList` or any supplied function, they + * can continue their execution where they left off. + */ + + private def filterImpl[A](ll: LazyList[A], p: A => Boolean, isFlipped: Boolean): LazyList[A] = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef = ll // val restRef = new ObjectRef(ll) + newLL { + var elem: A = null.asInstanceOf[A] + var found = false + var rest = restRef // var rest = restRef.elem + while (!found && !rest.isEmpty) { + elem = rest.head + found = p(elem) != isFlipped + rest = rest.tail + restRef = rest // restRef.elem = rest + } + if (found) sCons(elem, filterImpl(rest, p, isFlipped)) else State.Empty + } + } + + private def collectImpl[A, B](ll: LazyList[A], pf: PartialFunction[A, B]): LazyList[B] = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef = ll // val restRef = new ObjectRef(ll) + newLL { + val marker = Statics.pfMarker + val toMarker = anyToMarker.asInstanceOf[A => B] // safe because Function1 is erased + + var res: B = marker.asInstanceOf[B] // safe because B is unbounded + var rest = restRef // var rest = restRef.elem + while((res.asInstanceOf[AnyRef] eq marker) && !rest.isEmpty) { + res = pf.applyOrElse(rest.head, toMarker) + rest = rest.tail + restRef = rest // restRef.elem = rest + } + if (res.asInstanceOf[AnyRef] eq marker) State.Empty + else sCons(res, collectImpl(rest, pf)) + } + } + + private def flatMapImpl[A, B](ll: LazyList[A], f: A => IterableOnce[B]): LazyList[B] = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef = ll // val restRef = new ObjectRef(ll) + newLL { + var it: Iterator[B] = null + var itHasNext = false + var rest = restRef // var rest = restRef.elem + while (!itHasNext && !rest.isEmpty) { + it = f(rest.head).iterator + itHasNext = it.hasNext + if (!itHasNext) { // wait to advance `rest` because `it.next()` can throw + rest = rest.tail + restRef = rest // restRef.elem = rest + } + } + if (itHasNext) { + val head = it.next() + rest = rest.tail + restRef = rest // restRef.elem = rest + sCons(head, newLL(stateFromIteratorConcatSuffix(it)(flatMapImpl(rest, f).state))) + } else State.Empty + } + } + + private def dropImpl[A](ll: LazyList[A], n: Int): LazyList[A] = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef = ll // val restRef = new ObjectRef(ll) + var iRef = n // val iRef = new IntRef(n) + newLL { + var rest = restRef // var rest = restRef.elem + var i = iRef // var i = iRef.elem + while (i > 0 && !rest.isEmpty) { + rest = rest.tail + restRef = rest // restRef.elem = rest + i -= 1 + iRef = i // iRef.elem = i + } + rest.state + } + } + + private def dropWhileImpl[A](ll: LazyList[A], p: A => Boolean): LazyList[A] = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef = ll // val restRef = new ObjectRef(ll) + newLL { + var rest = restRef // var rest = restRef.elem + while (!rest.isEmpty && p(rest.head)) { + rest = rest.tail + restRef = rest // restRef.elem = rest + } + rest.state + } + } + + private def takeRightImpl[A](ll: LazyList[A], n: Int): LazyList[A] = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef = ll // val restRef = new ObjectRef(ll) + var scoutRef = ll // val scoutRef = new ObjectRef(ll) + var remainingRef = n // val remainingRef = new IntRef(n) + newLL { + var scout = scoutRef // var scout = scoutRef.elem + var remaining = remainingRef // var remaining = remainingRef.elem + // advance `scout` `n` elements ahead (or until empty) + while (remaining > 0 && !scout.isEmpty) { + scout = scout.tail + scoutRef = scout // scoutRef.elem = scout + remaining -= 1 + remainingRef = remaining // remainingRef.elem = remaining + } + var rest = restRef // var rest = restRef.elem + // advance `rest` and `scout` in tandem until `scout` reaches the end + while(!scout.isEmpty) { + scout = scout.tail + scoutRef = scout // scoutRef.elem = scout + rest = rest.tail // can't throw an exception as `scout` has already evaluated its tail + restRef = rest // restRef.elem = rest + } + // `rest` is the last `n` elements (or all of them) + rest.state + } + } + + /** An alternative way of building and matching lazy lists using LazyList.cons(hd, tl). + */ + object cons { + /** A lazy list consisting of a given first element and remaining elements + * @param hd The first element of the result lazy list + * @param tl The remaining elements of the result lazy list + */ + def apply[A](hd: => A, tl: => LazyList[A]): LazyList[A] = newLL(sCons(hd, newLL(tl.state))) + + /** Maps a lazy list to its head and tail */ + def unapply[A](xs: LazyList[A]): Option[(A, LazyList[A])] = #::.unapply(xs) + } + + implicit def toDeferrer[A](l: => LazyList[A]): Deferrer[A] = new Deferrer[A](() => l) + + final class Deferrer[A] private[LazyList] (private val l: () => LazyList[A]) extends AnyVal { + /** Construct a LazyList consisting of a given first element followed by elements + * from another LazyList. + */ + def #:: [B >: A](elem: => B): LazyList[B] = newLL(sCons(elem, newLL(l().state))) + /** Construct a LazyList consisting of the concatenation of the given LazyList and + * another LazyList. + */ + def #:::[B >: A](prefix: LazyList[B]): LazyList[B] = prefix lazyAppendedAll l() + } + + object #:: { + def unapply[A](s: LazyList[A]): Option[(A, LazyList[A])] = + if (!s.isEmpty) Some((s.head, s.tail)) else None + } + + def from[A](coll: collection.IterableOnce[A]): LazyList[A] = coll match { + case lazyList: LazyList[A] => lazyList + case _ if coll.knownSize == 0 => empty[A] + case _ => newLL(stateFromIterator(coll.iterator)) + } + + def empty[A]: LazyList[A] = _empty + + /** Creates a State from an Iterator, with another State appended after the Iterator + * is empty. + */ + private def stateFromIteratorConcatSuffix[A](it: Iterator[A])(suffix: => State[A]): State[A] = + if (it.hasNext) sCons(it.next(), newLL(stateFromIteratorConcatSuffix(it)(suffix))) + else suffix + + /** Creates a State from an IterableOnce. */ + private def stateFromIterator[A](it: Iterator[A]): State[A] = + if (it.hasNext) sCons(it.next(), newLL(stateFromIterator(it))) + else State.Empty + + override def concat[A](xss: collection.Iterable[A]*): LazyList[A] = + if (xss.knownSize == 0) empty + else newLL(concatIterator(xss.iterator)) + + private def concatIterator[A](it: Iterator[collection.Iterable[A]]): State[A] = + if (!it.hasNext) State.Empty + else stateFromIteratorConcatSuffix(it.next().iterator)(concatIterator(it)) + + /** An infinite LazyList that repeatedly applies a given function to a start value. + * + * @param start the start value of the LazyList + * @param f the function that's repeatedly applied + * @return the LazyList returning the infinite sequence of values `start, f(start), f(f(start)), ...` + */ + def iterate[A](start: => A)(f: A => A): LazyList[A] = + newLL { + val head = start + sCons(head, iterate(f(head))(f)) + } + + /** + * Create an infinite LazyList starting at `start` and incrementing by + * step `step`. + * + * @param start the start value of the LazyList + * @param step the increment value of the LazyList + * @return the LazyList starting at value `start`. + */ + def from(start: Int, step: Int): LazyList[Int] = + newLL(sCons(start, from(start + step, step))) + + /** + * Create an infinite LazyList starting at `start` and incrementing by `1`. + * + * @param start the start value of the LazyList + * @return the LazyList starting at value `start`. + */ + def from(start: Int): LazyList[Int] = from(start, 1) + + /** + * Create an infinite LazyList containing the given element expression (which + * is computed for each occurrence). + * + * @param elem the element composing the resulting LazyList + * @return the LazyList containing an infinite number of elem + */ + def continually[A](elem: => A): LazyList[A] = newLL(sCons(elem, continually(elem))) + + override def fill[A](n: Int)(elem: => A): LazyList[A] = + if (n > 0) newLL(sCons(elem, fill(n - 1)(elem))) else empty + + override def tabulate[A](n: Int)(f: Int => A): LazyList[A] = { + def at(index: Int): LazyList[A] = + if (index < n) newLL(sCons(f(index), at(index + 1))) else empty + + at(0) + } + + // significantly simpler than the iterator returned by Iterator.unfold + override def unfold[A, S](init: S)(f: S => Option[(A, S)]): LazyList[A] = + newLL { + f(init) match { + case Some((elem, state)) => sCons(elem, unfold(state)(f)) + case None => State.Empty + } + } + + /** The builder returned by this method only evaluates elements + * of collections added to it as needed. + * + * @tparam A the type of the ${coll}’s elements + * @return A builder for $Coll objects. + */ + def newBuilder[A]: Builder[A, LazyList[A]] = new LazyBuilder[A] + + private class LazyIterator[+A](private[this] var lazyList: LazyList[A]) extends AbstractIterator[A] { + override def hasNext: Boolean = !lazyList.isEmpty + + override def next(): A = + if (lazyList.isEmpty) Iterator.empty.next() + else { + val res = lazyList.head + lazyList = lazyList.tail + res + } + } + + private class SlidingIterator[A](private[this] var lazyList: LazyList[A], size: Int, step: Int) + extends AbstractIterator[LazyList[A]] { + private val minLen = size - step max 0 + private var first = true + + def hasNext: Boolean = + if (first) !lazyList.isEmpty + else lazyList.lengthGt(minLen) + + def next(): LazyList[A] = { + if (!hasNext) Iterator.empty.next() + else { + first = false + val list = lazyList + lazyList = list.drop(step) + list.take(size) + } + } + } + + private final class WithFilter[A] private[LazyList](lazyList: LazyList[A], p: A => Boolean) + extends collection.WithFilter[A, LazyList] { + private[this] val filtered = lazyList.filter(p) + def map[B](f: A => B): LazyList[B] = filtered.map(f) + def flatMap[B](f: A => IterableOnce[B]): LazyList[B] = filtered.flatMap(f) + def foreach[U](f: A => U): Unit = filtered.foreach(f) + def withFilter(q: A => Boolean): collection.WithFilter[A, LazyList] = new WithFilter(filtered, q) + } + + private final class LazyBuilder[A] extends ReusableBuilder[A, LazyList[A]] { + import LazyBuilder._ + + private[this] var next: DeferredState[A] = _ + private[this] var list: LazyList[A] = _ + + clear() + + override def clear(): Unit = { + val deferred = new DeferredState[A] + list = newLL(deferred.eval()) + next = deferred + } + + override def result(): LazyList[A] = { + next init State.Empty + list + } + + override def addOne(elem: A): this.type = { + val deferred = new DeferredState[A] + next init sCons(elem, newLL(deferred.eval())) + next = deferred + this + } + + // lazy implementation which doesn't evaluate the collection being added + override def addAll(xs: IterableOnce[A]): this.type = { + if (xs.knownSize != 0) { + val deferred = new DeferredState[A] + next init stateFromIteratorConcatSuffix(xs.iterator)(deferred.eval()) + next = deferred + } + this + } + } + + private object LazyBuilder { + final class DeferredState[A] { + private[this] var _state: () => State[A] = _ + + def eval(): State[A] = { + val state = _state + if (state == null) throw new IllegalStateException("uninitialized") + state() + } + + // racy + def init(state: => State[A]): Unit = { + if (_state != null) throw new IllegalStateException("already initialized") + _state = () => state + } + } + } + + /** This serialization proxy is used for LazyLists which start with a sequence of evaluated cons cells. + * The forced sequence is serialized in a compact, sequential format, followed by the unevaluated tail, which uses + * standard Java serialization to store the complete structure of unevaluated thunks. This allows the serialization + * of long evaluated lazy lists without exhausting the stack through recursive serialization of cons cells. + */ + @SerialVersionUID(3L) + final class SerializationProxy[A](@transient protected var coll: LazyList[A]) extends Serializable { + + private[this] def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + var these = coll + while (these.knownNonEmpty) { + out.writeObject(these.head) + these = these.tail + } + out.writeObject(SerializeEnd) + out.writeObject(these) + } + + private[this] def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val init = new mutable.ListBuffer[A] + var initRead = false + while (!initRead) in.readObject match { + case SerializeEnd => initRead = true + case a => init += a.asInstanceOf[A] + } + val tail = in.readObject().asInstanceOf[LazyList[A]] + // scala/scala#10118: caution that no code path can evaluate `tail.state` + // before the resulting LazyList is returned + val it = init.toList.iterator + coll = newLL(stateFromIteratorConcatSuffix(it)(tail.state)) + } + + private[this] def readResolve(): Any = coll + } +} diff --git a/library/src/scala/collection/immutable/List.scala b/library/src/scala/collection/immutable/List.scala new file mode 100644 index 000000000000..66cb8a487cde --- /dev/null +++ b/library/src/scala/collection/immutable/List.scala @@ -0,0 +1,693 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.unchecked.uncheckedVariance +import scala.annotation.tailrec +import mutable.{Builder, ListBuffer} +import scala.collection.generic.{CommonErrors, DefaultSerializable} +import scala.runtime.Statics.releaseFence + +/** A class for immutable linked lists representing ordered collections + * of elements of type `A`. + * + * This class comes with two implementing case classes `scala.Nil` + * and `scala.::` that implement the abstract members `isEmpty`, + * `head` and `tail`. + * + * This class is optimal for last-in-first-out (LIFO), stack-like access patterns. If you need another access + * pattern, for example, random access or FIFO, consider using a collection more suited to this than `List`. + * + * ==Performance== + * '''Time:''' `List` has `O(1)` prepend and head/tail access. Most other operations are `O(n)` on the number of elements in the list. + * This includes the index-based lookup of elements, `length`, `append` and `reverse`. + * + * '''Space:''' `List` implements '''structural sharing''' of the tail list. This means that many operations are either + * zero- or constant-memory cost. + * {{{ + * val mainList = List(3, 2, 1) + * val with4 = 4 :: mainList // re-uses mainList, costs one :: instance + * val with42 = 42 :: mainList // also re-uses mainList, cost one :: instance + * val shorter = mainList.tail // costs nothing as it uses the same 2::1::Nil instances as mainList + * }}} + * + * @example {{{ + * // Make a list via the companion object factory + * val days = List("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") + * + * // Make a list element-by-element + * val when = "AM" :: "PM" :: Nil + * + * // Pattern match + * days match { + * case firstDay :: otherDays => + * println("The first day of the week is: " + firstDay) + * case Nil => + * println("There don't seem to be any week days.") + * } + * }}} + * + * @note The functional list is characterized by persistence and structural sharing, thus offering considerable + * performance and space consumption benefits in some scenarios if used correctly. + * However, note that objects having multiple references into the same functional list (that is, + * objects that rely on structural sharing), will be serialized and deserialized with multiple lists, one for + * each reference to it. I.e. structural sharing is lost after serialization/deserialization. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lists "Scala's Collection Library overview"]] + * section on `Lists` for more information. + * + * @define coll list + * @define Coll `List` + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(3L) +sealed abstract class List[+A] + extends AbstractSeq[A] + with LinearSeq[A] + with LinearSeqOps[A, List, List[A]] + with StrictOptimizedLinearSeqOps[A, List, List[A]] + with StrictOptimizedSeqOps[A, List, List[A]] + with IterableFactoryDefaults[A, List] + with DefaultSerializable { + + override def iterableFactory: SeqFactory[List] = List + + /** Adds an element at the beginning of this list. + * @param elem the element to prepend. + * @return a list which contains `x` as first element and + * which continues with this list. + * Example: + * {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}} + */ + def :: [B >: A](elem: B): List[B] = new ::(elem, this) + + /** Adds the elements of a given list in front of this list. + * + * Example: + * {{{List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)}}} + * + * @param prefix The list elements to prepend. + * @return a list resulting from the concatenation of the given + * list `prefix` and this list. + */ + def ::: [B >: A](prefix: List[B]): List[B] = + if (isEmpty) prefix + else if (prefix.isEmpty) this + else { + val result = new ::[B](prefix.head, this) + var curr = result + var that = prefix.tail + while (!that.isEmpty) { + val temp = new ::[B](that.head, this) + curr.next = temp + curr = temp + that = that.tail + } + releaseFence() + result + } + + /** Adds the elements of a given list in reverse order in front of this list. + * `xs reverse_::: ys` is equivalent to + * `xs.reverse ::: ys` but is more efficient. + * + * @param prefix the prefix to reverse and then prepend + * @return the concatenation of the reversed prefix and the current list. + */ + def reverse_:::[B >: A](prefix: List[B]): List[B] = { + var these: List[B] = this + var pres = prefix + while (!pres.isEmpty) { + these = pres.head :: these + pres = pres.tail + } + these + } + + override final def isEmpty: Boolean = this eq Nil + + override def prepended[B >: A](elem: B): List[B] = elem :: this + + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): List[B] = prefix match { + case xs: List[B] => xs ::: this + case _ if prefix.knownSize == 0 => this + case b: ListBuffer[B] if this.isEmpty => b.toList + case _ => + val iter = prefix.iterator + if (iter.hasNext) { + val result = new ::[B](iter.next(), this) + var curr = result + while (iter.hasNext) { + val temp = new ::[B](iter.next(), this) + curr.next = temp + curr = temp + } + releaseFence() + result + } else { + this + } + } + + // When calling appendAll with another list `suffix`, avoid copying `suffix` + override def appendedAll[B >: A](suffix: collection.IterableOnce[B]): List[B] = suffix match { + case xs: List[B] => this ::: xs + case _ => super.appendedAll(suffix) + } + + override def take(n: Int): List[A] = if (isEmpty || n <= 0) Nil else { + val h = new ::(head, Nil) + var t = h + var rest = tail + var i = 1 + while ({if (rest.isEmpty) return this; i < n}) { + i += 1 + val nx = new ::(rest.head, Nil) + t.next = nx + t = nx + rest = rest.tail + } + releaseFence() + h + } + + /** @inheritdoc + * + * @example {{{ + * // Given a list + * val letters = List('a','b','c','d','e') + * + * // `slice` returns all elements beginning at index `from` and afterwards, + * // up until index `until` (excluding index `until`.) + * letters.slice(1,3) // Returns List('b','c') + * }}} + */ + override def slice(from: Int, until: Int): List[A] = { + val lo = scala.math.max(from, 0) + if (until <= lo || isEmpty) Nil + else this drop lo take (until - lo) + } + + override def takeRight(n: Int): List[A] = { + @tailrec + def loop(lead: List[A], lag: List[A]): List[A] = lead match { + case Nil => lag + case _ :: tail => loop(tail, lag.tail) + } + loop(drop(n), this) + } + + // dropRight is inherited from LinearSeq + + override def splitAt(n: Int): (List[A], List[A]) = { + val b = new ListBuffer[A] + var i = 0 + var these = this + while (!these.isEmpty && i < n) { + i += 1 + b += these.head + these = these.tail + } + (b.toList, these) + } + + override def updated[B >: A](index: Int, elem: B): List[B] = { + var i = 0 + var current = this + val prefix = ListBuffer.empty[B] + while (i < index && current.nonEmpty) { + i += 1 + prefix += current.head + current = current.tail + } + if (i == index && current.nonEmpty) { + prefix.prependToList(elem :: current.tail) + } else { + throw CommonErrors.indexOutOfBounds(index = index, max = length - 1) + } + } + + final override def map[B](f: A => B): List[B] = { + if (this eq Nil) Nil else { + val h = new ::[B](f(head), Nil) + var t: ::[B] = h + var rest = tail + while (rest ne Nil) { + val nx = new ::(f(rest.head), Nil) + t.next = nx + t = nx + rest = rest.tail + } + releaseFence() + h + } + } + + final override def collect[B](pf: PartialFunction[A, B]): List[B] = { + if (this eq Nil) Nil else { + var rest = this + var h: ::[B] = null + var x: Any = null + // Special case for first element + while (h eq null) { + x = pf.applyOrElse(rest.head, List.partialNotApplied) + if (x.asInstanceOf[AnyRef] ne List.partialNotApplied) h = new ::(x.asInstanceOf[B], Nil) + rest = rest.tail + if (rest eq Nil) return if (h eq null) Nil else h + } + var t = h + // Remaining elements + while (rest ne Nil) { + x = pf.applyOrElse(rest.head, List.partialNotApplied) + if (x.asInstanceOf[AnyRef] ne List.partialNotApplied) { + val nx = new ::(x.asInstanceOf[B], Nil) + t.next = nx + t = nx + } + rest = rest.tail + } + releaseFence() + h + } + } + + final override def flatMap[B](f: A => IterableOnce[B]): List[B] = { + var rest = this + var h: ::[B] = null + var t: ::[B] = null + while (rest ne Nil) { + val it = f(rest.head).iterator + while (it.hasNext) { + val nx = new ::(it.next(), Nil) + if (t eq null) { + h = nx + } else { + t.next = nx + } + t = nx + } + rest = rest.tail + } + if (h eq null) Nil else {releaseFence(); h} + } + + @inline final override def takeWhile(p: A => Boolean): List[A] = { + val b = new ListBuffer[A] + var these = this + while (!these.isEmpty && p(these.head)) { + b += these.head + these = these.tail + } + b.toList + } + + @inline final override def span(p: A => Boolean): (List[A], List[A]) = { + val b = new ListBuffer[A] + var these = this + while (!these.isEmpty && p(these.head)) { + b += these.head + these = these.tail + } + (b.toList, these) + } + + // Overridden with an implementation identical to the inherited one (at this time) + // solely so it can be finalized and thus inlinable. + @inline final override def foreach[U](f: A => U): Unit = { + var these = this + while (!these.isEmpty) { + f(these.head) + these = these.tail + } + } + + final override def reverse: List[A] = { + var result: List[A] = Nil + var these = this + while (!these.isEmpty) { + result = these.head :: result + these = these.tail + } + result + } + + final override def foldRight[B](z: B)(op: (A, B) => B): B = { + var acc = z + var these: List[A] = reverse + while (!these.isEmpty) { + acc = op(these.head, acc) + these = these.tail + } + acc + } + + // Copy/Paste overrides to avoid interface calls inside loops. + + override final def length: Int = { + var these = this + var len = 0 + while (!these.isEmpty) { + len += 1 + these = these.tail + } + len + } + + override final def lengthCompare(len: Int): Int = { + @tailrec def loop(i: Int, xs: List[A]): Int = { + if (i == len) + if (xs.isEmpty) 0 else 1 + else if (xs.isEmpty) + -1 + else + loop(i + 1, xs.tail) + } + if (len < 0) 1 + else loop(0, coll) + } + + override final def forall(p: A => Boolean): Boolean = { + var these: List[A] = this + while (!these.isEmpty) { + if (!p(these.head)) return false + these = these.tail + } + true + } + + override final def exists(p: A => Boolean): Boolean = { + var these: List[A] = this + while (!these.isEmpty) { + if (p(these.head)) return true + these = these.tail + } + false + } + + override final def contains[A1 >: A](elem: A1): Boolean = { + var these: List[A] = this + while (!these.isEmpty) { + if (these.head == elem) return true + these = these.tail + } + false + } + + override final def find(p: A => Boolean): Option[A] = { + var these: List[A] = this + while (!these.isEmpty) { + if (p(these.head)) return Some(these.head) + these = these.tail + } + None + } + + override def last: A = { + if (isEmpty) throw new NoSuchElementException("List.last") + else { + var these = this + var scout = tail + while (!scout.isEmpty) { + these = scout + scout = scout.tail + } + these.head + } + } + + override def corresponds[B](that: collection.Seq[B])(p: (A, B) => Boolean): Boolean = that match { + case that: LinearSeq[B] => + var i = this + var j = that + while (!(i.isEmpty || j.isEmpty)) { + if (!p(i.head, j.head)) + return false + i = i.tail + j = j.tail + } + i.isEmpty && j.isEmpty + case _ => + super.corresponds(that)(p) + } + + override protected[this] def className = "List" + + /** Builds a new list by applying a function to all elements of this list. + * Like `xs map f`, but returns `xs` unchanged if function + * `f` maps all elements to themselves (as determined by `eq`). + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a list resulting from applying the given function + * `f` to each element of this list and collecting the results. + */ + @`inline` final def mapConserve[B >: A <: AnyRef](f: A => B): List[B] = { + // Note to developers: there exists a duplication between this function and `reflect.internal.util.Collections#map2Conserve`. + // If any successful optimization attempts or other changes are made, please rehash them there too. + @tailrec + def loop(mappedHead: List[B], mappedLast: ::[B], unchanged: List[A], pending: List[A]): List[B] = { + if (pending.isEmpty) { + if (mappedHead eq null) unchanged + else { + mappedLast.next = (unchanged: List[B]) + mappedHead + } + } + else { + val head0 = pending.head + val head1 = f(head0) + + if (head1 eq head0.asInstanceOf[AnyRef]) + loop(mappedHead, mappedLast, unchanged, pending.tail) + else { + var xc = unchanged + var mappedHead1: List[B] = mappedHead + var mappedLast1: ::[B] = mappedLast + while (xc ne pending) { + val next = new ::[B](xc.head, Nil) + if (mappedHead1 eq null) mappedHead1 = next + if (mappedLast1 ne null) mappedLast1.next = next + mappedLast1 = next + xc = xc.tail + } + val next = new ::(head1, Nil) + if (mappedHead1 eq null) mappedHead1 = next + if (mappedLast1 ne null) mappedLast1.next = next + mappedLast1 = next + val tail0 = pending.tail + loop(mappedHead1, mappedLast1, tail0, tail0) + + } + } + } + val result = loop(null, null, this, this) + releaseFence() + result + } + + override def filter(p: A => Boolean): List[A] = filterCommon(p, isFlipped = false) + + override def filterNot(p: A => Boolean): List[A] = filterCommon(p, isFlipped = true) + + private[this] def filterCommon(p: A => Boolean, isFlipped: Boolean): List[A] = { + + // everything seen so far so far is not included + @tailrec def noneIn(l: List[A]): List[A] = { + if (l.isEmpty) + Nil + else { + val h = l.head + val t = l.tail + if (p(h) != isFlipped) + allIn(l, t) + else + noneIn(t) + } + } + + // everything from 'start' is included, if everything from this point is in we can return the origin + // start otherwise if we discover an element that is out we must create a new partial list. + @tailrec def allIn(start: List[A], remaining: List[A]): List[A] = { + if (remaining.isEmpty) + start + else { + val x = remaining.head + if (p(x) != isFlipped) + allIn(start, remaining.tail) + else + partialFill(start, remaining) + } + } + + // we have seen elements that should be included then one that should be excluded, start building + def partialFill(origStart: List[A], firstMiss: List[A]): List[A] = { + val newHead = new ::(origStart.head, Nil) + var toProcess = origStart.tail + var currentLast = newHead + + // we know that all elements are :: until at least firstMiss.tail + while (!(toProcess eq firstMiss)) { + val newElem = new ::(toProcess.head, Nil) + currentLast.next = newElem + currentLast = newElem + toProcess = toProcess.tail + } + + // at this point newHead points to a list which is a duplicate of all the 'in' elements up to the first miss. + // currentLast is the last element in that list. + + // now we are going to try and share as much of the tail as we can, only moving elements across when we have to. + var next = firstMiss.tail + var nextToCopy = next // the next element we would need to copy to our list if we cant share. + while (!next.isEmpty) { + // generally recommended is next.isNonEmpty but this incurs an extra method call. + val head: A = next.head + if (p(head) != isFlipped) { + next = next.tail + } else { + // its not a match - do we have outstanding elements? + while (!(nextToCopy eq next)) { + val newElem = new ::(nextToCopy.head, Nil) + currentLast.next = newElem + currentLast = newElem + nextToCopy = nextToCopy.tail + } + nextToCopy = next.tail + next = next.tail + } + } + + // we have remaining elements - they are unchanged attach them to the end + if (!nextToCopy.isEmpty) + currentLast.next = nextToCopy + + newHead + } + + val result = noneIn(this) + releaseFence() + result + } + + override def partition(p: A => Boolean): (List[A], List[A]) = { + if (isEmpty) List.TupleOfNil + else super.partition(p) match { + case (Nil, xs) => (Nil, this) + case (xs, Nil) => (this, Nil) + case pair => pair + } + } + + final override def toList: List[A] = this + + // Override for performance + override def equals(o: scala.Any): Boolean = { + @tailrec def listEq(a: List[_], b: List[_]): Boolean = + (a eq b) || { + val aEmpty = a.isEmpty + val bEmpty = b.isEmpty + if (!(aEmpty || bEmpty) && a.head == b.head) { + listEq(a.tail, b.tail) + } + else { + aEmpty && bEmpty + } + } + + o match { + case that: List[_] => listEq(this, that) + case _ => super.equals(o) + } + } + + // TODO: uncomment once bincompat allows (reference: scala/scala#9365) + /* + // Override for performance: traverse only as much as needed + // and share tail when nothing needs to be filtered out anymore + override def diff[B >: A](that: collection.Seq[B]): AnyRef = { + if (that.isEmpty || this.isEmpty) this + else if (tail.isEmpty) if (that.contains(head)) Nil else this + else { + val occ = occCounts(that) + val b = new ListBuffer[A]() + @tailrec + def rec(remainder: List[A]): List[A] = { + if(occ.isEmpty) b.prependToList(remainder) + else remainder match { + case Nil => b.result() + case head :: next => { + occ.updateWith(head){ + case None => { + b.append(head) + None + } + case Some(1) => None + case Some(n) => Some(n - 1) + } + rec(next) + } + } + } + rec(this) + } + } + */ + +} + +// Internal code that mutates `next` _must_ call `Statics.releaseFence()` if either immediately, or +// before a newly-allocated, thread-local :: instance is aliased (e.g. in ListBuffer.toList) +final case class :: [+A](override val head: A, private[scala] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally + extends List[A] { + releaseFence() + override def headOption: Some[A] = Some(head) + override def tail: List[A] = next +} + +case object Nil extends List[Nothing] { + override def head: Nothing = throw new NoSuchElementException("head of empty list") + override def headOption: None.type = None + override def tail: Nothing = throw new UnsupportedOperationException("tail of empty list") + override def last: Nothing = throw new NoSuchElementException("last of empty list") + override def init: Nothing = throw new UnsupportedOperationException("init of empty list") + override def knownSize: Int = 0 + override def iterator: Iterator[Nothing] = Iterator.empty + override def unzip[A1, A2](implicit asPair: Nothing => (A1, A2)): (List[A1], List[A2]) = EmptyUnzip + + @transient + private[this] val EmptyUnzip = (Nil, Nil) +} + +/** + * $factoryInfo + * @define coll list + * @define Coll `List` + */ +@SerialVersionUID(3L) +object List extends StrictOptimizedSeqFactory[List] { + private val TupleOfNil = (Nil, Nil) + + def from[B](coll: collection.IterableOnce[B]): List[B] = Nil.prependedAll(coll) + + def newBuilder[A]: Builder[A, List[A]] = new ListBuffer() + + def empty[A]: List[A] = Nil + + @transient + private[collection] val partialNotApplied = new Function1[Any, Any] { def apply(x: Any): Any = this } +} diff --git a/library/src/scala/collection/immutable/ListMap.scala b/library/src/scala/collection/immutable/ListMap.scala new file mode 100644 index 000000000000..74d1697cac7f --- /dev/null +++ b/library/src/scala/collection/immutable/ListMap.scala @@ -0,0 +1,372 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.{nowarn, tailrec} +import scala.collection.mutable.ReusableBuilder +import scala.collection.generic.DefaultSerializable +import scala.runtime.Statics.releaseFence +import scala.util.hashing.MurmurHash3 + +/** + * This class implements immutable maps using a list-based data structure. List map iterators and + * traversal methods visit key-value pairs in the order they were first inserted. + * + * Entries are stored internally in reversed insertion order, which means the newest key is at the + * head of the list. As such, methods such as `head` and `tail` are O(n), while `last` and `init` + * are O(1). Other operations, such as inserting or removing entries, are also O(n), which makes + * this collection suitable only for a small number of elements. + * + * Instances of `ListMap` represent empty maps; they can be either created by calling the + * constructor directly, or by applying the function `ListMap.empty`. + * + * @tparam K the type of the keys contained in this list map + * @tparam V the type of the values associated with the keys + * + * @define Coll ListMap + * @define coll list map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +sealed class ListMap[K, +V] + extends AbstractMap[K, V] + with SeqMap[K, V] + with StrictOptimizedMapOps[K, V, ListMap, ListMap[K, V]] + with MapFactoryDefaults[K, V, ListMap, Iterable] + with DefaultSerializable { + + override def mapFactory: MapFactory[ListMap] = ListMap + + override def size: Int = 0 + + override def isEmpty: Boolean = true + + override def knownSize: Int = 0 + def get(key: K): Option[V] = None + + def updated[V1 >: V](key: K, value: V1): ListMap[K, V1] = new ListMap.Node[K, V1](key, value, this) + + def removed(key: K): ListMap[K, V] = this + + def iterator: Iterator[(K, V)] = { + var curr: ListMap[K, V] = this + var res: List[(K, V)] = Nil + while (curr.nonEmpty) { + res = (curr.key, curr.value) :: res + curr = curr.next + } + res.iterator + } + + @nowarn("msg=overriding method keys") + override def keys: Iterable[K] = { + var curr: ListMap[K, V] = this + var res: List[K] = Nil + while (curr.nonEmpty) { + res = curr.key :: res + curr = curr.next + } + res + } + + override def hashCode(): Int = { + if (isEmpty) MurmurHash3.emptyMapHash + else { + // Can't efficiently override foreachEntry directly in ListMap because it would need to preserve iteration + // order be reversing the list first. But mapHash is symmetric so the reversed order is fine here. + val _reversed = new immutable.AbstractMap[K, V] { + override def isEmpty: Boolean = ListMap.this.isEmpty + override def removed(key: K): Map[K, V] = ListMap.this.removed(key) + override def updated[V1 >: V](key: K, value: V1): Map[K, V1] = ListMap.this.updated(key, value) + override def get(key: K): Option[V] = ListMap.this.get(key) + override def iterator: Iterator[(K, V)] = ListMap.this.iterator + override def foreachEntry[U](f: (K, V) => U): Unit = { + var curr: ListMap[K, V] = ListMap.this + while (curr.nonEmpty) { + f(curr.key, curr.value) + curr = curr.next + } + } + } + MurmurHash3.mapHash(_reversed) + } + } + + private[immutable] def key: K = throw new NoSuchElementException("key of empty map") + private[immutable] def value: V = throw new NoSuchElementException("value of empty map") + private[immutable] def next: ListMap[K, V] = throw new NoSuchElementException("next of empty map") + + override def foldRight[Z](z: Z)(op: ((K, V), Z) => Z): Z = ListMap.foldRightInternal(this, z, op) + override protected[this] def className = "ListMap" + +} + +/** + * $factoryInfo + * + * Note that each element insertion takes O(n) time, which means that creating a list map with + * n elements will take O(n^2^) time. This makes the builder suitable only for a small number of + * elements. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#list-maps "Scala's Collection Library overview"]] + * section on `List Maps` for more information. + * @define Coll ListMap + * @define coll list map + */ +@SerialVersionUID(3L) +object ListMap extends MapFactory[ListMap] { + /** + * Represents an entry in the `ListMap`. + */ + private[immutable] final class Node[K, V]( + override private[immutable] val key: K, + private[immutable] var _value: V, + private[immutable] var _init: ListMap[K, V] + ) extends ListMap[K, V] { + releaseFence() + + override private[immutable] def value: V = _value + + override def size: Int = sizeInternal(this, 0) + + @tailrec private[this] def sizeInternal(cur: ListMap[K, V], acc: Int): Int = + if (cur.isEmpty) acc + else sizeInternal(cur.next, acc + 1) + + override def isEmpty: Boolean = false + + override def knownSize: Int = -1 + + @throws[NoSuchElementException] + override def apply(k: K): V = applyInternal(this, k) + + @tailrec private[this] def applyInternal(cur: ListMap[K, V], k: K): V = + if (cur.isEmpty) throw new NoSuchElementException("key not found: " + k) + else if (k == cur.key) cur.value + else applyInternal(cur.next, k) + + override def get(k: K): Option[V] = getInternal(this, k) + + @tailrec private[this] def getInternal(cur: ListMap[K, V], k: K): Option[V] = + if (cur.isEmpty) None + else if (k == cur.key) Some(cur.value) + else getInternal(cur.next, k) + + override def contains(k: K): Boolean = containsInternal(this, k) + + @tailrec private[this] def containsInternal(cur: ListMap[K, V], k: K): Boolean = + if (cur.isEmpty) false + else if (k == cur.key) true + else containsInternal(cur.next, k) + + override def updated[V1 >: V](k: K, v: V1): ListMap[K, V1] = { + + var index = -1 // the index (in reverse) where the key to update exists, if it is found + var found = false // true if the key is found int he map + var isDifferent = false // true if the key was found and the values are different + + { + var curr: ListMap[K, V] = this + + while (curr.nonEmpty && !found) { + if (k == curr.key) { + found = true + isDifferent = v.asInstanceOf[AnyRef] ne curr.value.asInstanceOf[AnyRef] + } + index += 1 + curr = curr.init + } + } + + if (found) { + if (isDifferent) { + var newHead: ListMap.Node[K, V1] = null + var prev: ListMap.Node[K, V1] = null + var curr: ListMap[K, V1] = this + var i = 0 + while (i < index) { + val temp = new ListMap.Node(curr.key, curr.value, null) + if (prev ne null) { + prev._init = temp + } + prev = temp + curr = curr.init + if (newHead eq null) { + newHead = prev + } + i += 1 + } + val newNode = new ListMap.Node(curr.key, v, curr.init) + if (prev ne null) { + prev._init = newNode + } + releaseFence() + if (newHead eq null) newNode else newHead + } else { + this + } + } else { + new ListMap.Node(k, v, this) + } + } + + @tailrec private[this] def removeInternal(k: K, cur: ListMap[K, V], acc: List[ListMap[K, V]]): ListMap[K, V] = + if (cur.isEmpty) acc.last + else if (k == cur.key) acc.foldLeft(cur.next) { (t, h) => new Node(h.key, h.value, t) } + else removeInternal(k, cur.next, cur :: acc) + + override def removed(k: K): ListMap[K, V] = removeInternal(k, this, Nil) + + override private[immutable] def next: ListMap[K, V] = _init + + override def last: (K, V) = (key, value) + override def init: ListMap[K, V] = next + + } + + def empty[K, V]: ListMap[K, V] = EmptyListMap.asInstanceOf[ListMap[K, V]] + + private object EmptyListMap extends ListMap[Any, Nothing] + + def from[K, V](it: collection.IterableOnce[(K, V)]): ListMap[K, V] = + it match { + case lm: ListMap[K, V] => lm + case lhm: collection.mutable.LinkedHashMap[K, V] => + // by directly iterating through LinkedHashMap entries, we save creating intermediate tuples for each + // key-value pair + var current: ListMap[K, V] = empty[K, V] + var firstEntry = lhm._firstEntry + while (firstEntry ne null) { + current = new Node(firstEntry.key, firstEntry.value, current) + firstEntry = firstEntry.later + } + current + case _: collection.Map[K, V] | _: collection.MapView[K, V] => + // when creating from a map, we need not handle duplicate keys, so we can just append each key-value to the end + var current: ListMap[K, V] = empty[K, V] + val iter = it.iterator + while (iter.hasNext) { + val (k, v) = iter.next() + current = new Node(k, v, current) + } + current + + case _ => (newBuilder[K, V] ++= it).result() + } + + /** Returns a new ListMap builder + * + * The implementation safely handles additions after `result()` without calling `clear()` + * + * @tparam K the map key type + * @tparam V the map value type + */ + def newBuilder[K, V]: ReusableBuilder[(K, V), ListMap[K, V]] = new ListMapBuilder[K, V] + + @tailrec private def foldRightInternal[K, V, Z](map: ListMap[K, V], prevValue: Z, op: ((K, V), Z) => Z): Z = { + if (map.isEmpty) prevValue + else foldRightInternal(map.init, op(map.last, prevValue), op) + } +} + +/** Builder for ListMap. + * $multipleResults + */ +private[immutable] final class ListMapBuilder[K, V] extends mutable.ReusableBuilder[(K, V), ListMap[K, V]] { + private[this] var isAliased: Boolean = false + private[this] var underlying: ListMap[K, V] = ListMap.empty + + override def clear(): Unit = { + underlying = ListMap.empty + isAliased = false + } + + override def result(): ListMap[K, V] = { + isAliased = true + releaseFence() + underlying + } + + override def addOne(elem: (K, V)): this.type = addOne(elem._1, elem._2) + + @tailrec + private[this] def insertValueAtKeyReturnFound(m: ListMap[K, V], key: K, value: V): Boolean = m match { + case n: ListMap.Node[K, V] => + if (n.key == key) { + n._value = value + true + } else { + insertValueAtKeyReturnFound(n.init, key, value) + } + case _ => false + } + + def addOne(key: K, value: V): this.type = { + if (isAliased) { + underlying = underlying.updated(key, value) + } else { + if (!insertValueAtKeyReturnFound(underlying, key, value)) { + underlying = new ListMap.Node(key, value, underlying) + } + } + this + } + override def addAll(xs: IterableOnce[(K, V)]): this.type = { + if (isAliased) { + super.addAll(xs) + } else if (underlying.nonEmpty) { + xs match { + case m: collection.Map[K, V] => + // if it is a map, then its keys will not collide with themselves. + // therefor we only need to check the already-existing elements for collisions. + // No need to check the entire list + + val iter = m.iterator + var newUnderlying = underlying + while (iter.hasNext) { + val next = iter.next() + if (!insertValueAtKeyReturnFound(underlying, next._1, next._2)) { + newUnderlying = new ListMap.Node[K, V](next._1, next._2, newUnderlying) + } + } + underlying = newUnderlying + this + + case _ => + super.addAll(xs) + } + } else xs match { + case lhm: collection.mutable.LinkedHashMap[K, V] => + // special-casing LinkedHashMap avoids creating of Iterator and tuples for each key-value + var firstEntry = lhm._firstEntry + while (firstEntry ne null) { + underlying = new ListMap.Node(firstEntry.key, firstEntry.value, underlying) + firstEntry = firstEntry.later + } + this + + case _: collection.Map[K, V] | _: collection.MapView[K, V] => + val iter = xs.iterator + while (iter.hasNext) { + val (k, v) = iter.next() + underlying = new ListMap.Node(k, v, underlying) + } + + this + case _ => + super.addAll(xs) + } + } +} diff --git a/library/src/scala/collection/immutable/ListSet.scala b/library/src/scala/collection/immutable/ListSet.scala new file mode 100644 index 000000000000..2e2758cb2747 --- /dev/null +++ b/library/src/scala/collection/immutable/ListSet.scala @@ -0,0 +1,138 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import mutable.{Builder, ImmutableBuilder} +import scala.annotation.tailrec +import scala.collection.generic.DefaultSerializable + +/** + * This class implements immutable sets using a list-based data structure. List set iterators and + * traversal methods visit elements in the order they were first inserted. + * + * Elements are stored internally in reversed insertion order, which means the newest element is at + * the head of the list. As such, methods such as `head` and `tail` are O(n), while `last` and + * `init` are O(1). Other operations, such as inserting or removing entries, are also O(n), which + * makes this collection suitable only for a small number of elements. + * + * Instances of `ListSet` represent empty sets; they can be either created by calling the + * constructor directly, or by applying the function `ListSet.empty`. + * + * @tparam A the type of the elements contained in this list set + * + * @define Coll ListSet + * @define coll list set + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +sealed class ListSet[A] + extends AbstractSet[A] + with StrictOptimizedSetOps[A, ListSet, ListSet[A]] + with IterableFactoryDefaults[A, ListSet] + with DefaultSerializable { + + override protected[this] def className: String = "ListSet" + + override def size: Int = 0 + override def knownSize: Int = 0 + override def isEmpty: Boolean = true + + def contains(elem: A): Boolean = false + + def incl(elem: A): ListSet[A] = new Node(elem) + def excl(elem: A): ListSet[A] = this + + def iterator: scala.collection.Iterator[A] = { + var curr: ListSet[A] = this + var res: List[A] = Nil + while (!curr.isEmpty) { + res = curr.elem :: res + curr = curr.next + } + res.iterator + } + + protected def elem: A = throw new NoSuchElementException("elem of empty set") + protected def next: ListSet[A] = throw new NoSuchElementException("next of empty set") + + override def iterableFactory: IterableFactory[ListSet] = ListSet + + /** + * Represents an entry in the `ListSet`. + */ + protected class Node(override protected val elem: A) extends ListSet[A] { + + override def size = sizeInternal(this, 0) + override def knownSize: Int = -1 + @tailrec private[this] def sizeInternal(n: ListSet[A], acc: Int): Int = + if (n.isEmpty) acc + else sizeInternal(n.next, acc + 1) + + override def isEmpty: Boolean = false + + override def contains(e: A): Boolean = containsInternal(this, e) + + @tailrec private[this] def containsInternal(n: ListSet[A], e: A): Boolean = + !n.isEmpty && (n.elem == e || containsInternal(n.next, e)) + + override def incl(e: A): ListSet[A] = if (contains(e)) this else new Node(e) + + override def excl(e: A): ListSet[A] = removeInternal(e, this, Nil) + + @tailrec private[this] def removeInternal(k: A, cur: ListSet[A], acc: List[ListSet[A]]): ListSet[A] = + if (cur.isEmpty) acc.last + else if (k == cur.elem) acc.foldLeft(cur.next)((t, h) => new t.Node(h.elem)) + else removeInternal(k, cur.next, cur :: acc) + + override protected def next: ListSet[A] = ListSet.this + + override def last: A = elem + + override def init: ListSet[A] = next + } +} + +/** + * $factoryInfo + * + * Note that each element insertion takes O(n) time, which means that creating a list set with + * n elements will take O(n^2^) time. This makes the builder suitable only for a small number of + * elements. + * + * @define Coll ListSet + * @define coll list set + */ +@SerialVersionUID(3L) +object ListSet extends IterableFactory[ListSet] { + + def from[E](it: scala.collection.IterableOnce[E]): ListSet[E] = + it match { + case ls: ListSet[E] => ls + case _ if it.knownSize == 0 => empty[E] + case _ => (newBuilder[E] ++= it).result() + } + + private object EmptyListSet extends ListSet[Any] { + override def knownSize: Int = 0 + } + private[collection] def emptyInstance: ListSet[Any] = EmptyListSet + + def empty[A]: ListSet[A] = EmptyListSet.asInstanceOf[ListSet[A]] + + def newBuilder[A]: Builder[A, ListSet[A]] = + new ImmutableBuilder[A, ListSet[A]](empty) { + def addOne(elem: A): this.type = { elems = elems + elem; this } + } +} diff --git a/library/src/scala/collection/immutable/LongMap.scala b/library/src/scala/collection/immutable/LongMap.scala new file mode 100644 index 000000000000..8ff968f58305 --- /dev/null +++ b/library/src/scala/collection/immutable/LongMap.scala @@ -0,0 +1,490 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package immutable + +import java.lang.IllegalStateException + +import scala.collection.generic.{BitOperations, DefaultSerializationProxy} +import scala.collection.mutable.{Builder, ImmutableBuilder, ListBuffer} +import scala.annotation.tailrec +import scala.annotation.unchecked.uncheckedVariance +import scala.language.implicitConversions + +/** Utility class for long maps. + */ +private[immutable] object LongMapUtils extends BitOperations.Long { + def branchMask(i: Long, j: Long) = highestOneBit(i ^ j) + + def join[T](p1: Long, t1: LongMap[T], p2: Long, t2: LongMap[T]): LongMap[T] = { + val m = branchMask(p1, p2) + val p = mask(p1, m) + if (zero(p1, m)) LongMap.Bin(p, m, t1, t2) + else LongMap.Bin(p, m, t2, t1) + } + + def bin[T](prefix: Long, mask: Long, left: LongMap[T], right: LongMap[T]): LongMap[T] = (left, right) match { + case (left, LongMap.Nil) => left + case (LongMap.Nil, right) => right + case (left, right) => LongMap.Bin(prefix, mask, left, right) + } +} + +import LongMapUtils.{Long => _, _} + +/** A companion object for long maps. + * + * @define Coll `LongMap` + */ +object LongMap { + def empty[T]: LongMap[T] = LongMap.Nil + def singleton[T](key: Long, value: T): LongMap[T] = LongMap.Tip(key, value) + def apply[T](elems: (Long, T)*): LongMap[T] = + elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)) + + def from[V](coll: IterableOnce[(Long, V)]): LongMap[V] = + newBuilder[V].addAll(coll).result() + + def newBuilder[V]: Builder[(Long, V), LongMap[V]] = + new ImmutableBuilder[(Long, V), LongMap[V]](empty) { + def addOne(elem: (Long, V)): this.type = { elems = elems + elem; this } + } + + private[immutable] case object Nil extends LongMap[Nothing] { + // Important, don't remove this! See IntMap for explanation. + override def equals(that : Any) = that match { + case _: this.type => true + case _: LongMap[_] => false // The only empty LongMaps are eq Nil + case _ => super.equals(that) + } + } + + private[immutable] case class Tip[+T](key: Long, value: T) extends LongMap[T] { + def withValue[S](s: S) = + if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[LongMap.Tip[S]] + else LongMap.Tip(key, s) + } + + private[immutable] case class Bin[+T](prefix: Long, mask: Long, left: LongMap[T], right: LongMap[T]) extends LongMap[T] { + def bin[S](left: LongMap[S], right: LongMap[S]): LongMap[S] = { + if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[LongMap.Bin[S]] + else LongMap.Bin[S](prefix, mask, left, right) + } + } + + implicit def toFactory[V](dummy: LongMap.type): Factory[(Long, V), LongMap[V]] = ToFactory.asInstanceOf[Factory[(Long, V), LongMap[V]]] + + @SerialVersionUID(3L) + private[this] object ToFactory extends Factory[(Long, AnyRef), LongMap[AnyRef]] with Serializable { + def fromSpecific(it: IterableOnce[(Long, AnyRef)]): LongMap[AnyRef] = LongMap.from[AnyRef](it) + def newBuilder: Builder[(Long, AnyRef), LongMap[AnyRef]] = LongMap.newBuilder[AnyRef] + } + + implicit def toBuildFrom[V](factory: LongMap.type): BuildFrom[Any, (Long, V), LongMap[V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (Long, V), LongMap[V]]] + private[this] object ToBuildFrom extends BuildFrom[Any, (Long, AnyRef), LongMap[AnyRef]] { + def fromSpecific(from: Any)(it: IterableOnce[(Long, AnyRef)]) = LongMap.from(it) + def newBuilder(from: Any) = LongMap.newBuilder[AnyRef] + } + + implicit def iterableFactory[V]: Factory[(Long, V), LongMap[V]] = toFactory(this) + implicit def buildFromLongMap[V]: BuildFrom[LongMap[_], (Long, V), LongMap[V]] = toBuildFrom(this) +} + +// Iterator over a non-empty LongMap. +private[immutable] abstract class LongMapIterator[V, T](it: LongMap[V]) extends AbstractIterator[T] { + + // Basically this uses a simple stack to emulate conversion over the tree. However + // because we know that Longs are only 64 bits we can have at most 64 LongMap.Bins and + // one LongMap.Tip sitting on the tree at any point. Therefore we know the maximum stack + // depth is 65 + var index = 0 + var buffer = new Array[AnyRef](65) + + def pop() = { + index -= 1 + buffer(index).asInstanceOf[LongMap[V]] + } + + def push(x: LongMap[V]): Unit = { + buffer(index) = x.asInstanceOf[AnyRef] + index += 1 + } + push(it) + + /** + * What value do we assign to a tip? + */ + def valueOf(tip: LongMap.Tip[V]): T + + def hasNext = index != 0 + @tailrec + final def next(): T = + pop() match { + case LongMap.Bin(_,_, t@LongMap.Tip(_, _), right) => { + push(right) + valueOf(t) + } + case LongMap.Bin(_, _, left, right) => { + push(right) + push(left) + next() + } + case t@LongMap.Tip(_, _) => valueOf(t) + // This should never happen. We don't allow LongMap.Nil in subtrees of the LongMap + // and don't return an LongMapIterator for LongMap.Nil. + case LongMap.Nil => throw new IllegalStateException("Empty maps not allowed as subtrees") + } +} + +private[immutable] class LongMapEntryIterator[V](it: LongMap[V]) extends LongMapIterator[V, (Long, V)](it){ + def valueOf(tip: LongMap.Tip[V]) = (tip.key, tip.value) +} + +private[immutable] class LongMapValueIterator[V](it: LongMap[V]) extends LongMapIterator[V, V](it){ + def valueOf(tip: LongMap.Tip[V]) = tip.value +} + +private[immutable] class LongMapKeyIterator[V](it: LongMap[V]) extends LongMapIterator[V, Long](it){ + def valueOf(tip: LongMap.Tip[V]) = tip.key +} + +/** + * Specialised immutable map structure for long keys, based on + * [[https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.37.5452 Fast Mergeable Long Maps]] + * by Okasaki and Gill. Essentially a trie based on binary digits of the integers. + * + * Note: This class is as of 2.8 largely superseded by HashMap. + * + * @tparam T type of the values associated with the long keys. + * + * @define Coll `immutable.LongMap` + * @define coll immutable long integer map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +sealed abstract class LongMap[+T] extends AbstractMap[Long, T] + with StrictOptimizedMapOps[Long, T, Map, LongMap[T]] + with Serializable { + + override protected def fromSpecific(coll: scala.collection.IterableOnce[(Long, T)] @uncheckedVariance): LongMap[T] = { + //TODO should this be the default implementation of this method in StrictOptimizedIterableOps? + val b = newSpecificBuilder + b.sizeHint(coll) + b.addAll(coll) + b.result() + } + override protected def newSpecificBuilder: Builder[(Long, T), LongMap[T]] @uncheckedVariance = + new ImmutableBuilder[(Long, T), LongMap[T]](empty) { + def addOne(elem: (Long, T)): this.type = { elems = elems + elem; this } + } + + override def empty: LongMap[T] = LongMap.Nil + + override def toList = { + val buffer = new ListBuffer[(Long, T)] + foreach(buffer += _) + buffer.toList + } + + /** + * Iterator over key, value pairs of the map in unsigned order of the keys. + * + * @return an iterator over pairs of long keys and corresponding values. + */ + def iterator: Iterator[(Long, T)] = this match { + case LongMap.Nil => Iterator.empty + case _ => new LongMapEntryIterator(this) + } + + /** + * Loops over the key, value pairs of the map in unsigned order of the keys. + */ + override final def foreach[U](f: ((Long, T)) => U): Unit = this match { + case LongMap.Bin(_, _, left, right) => { left.foreach(f); right.foreach(f) } + case LongMap.Tip(key, value) => f((key, value)) + case LongMap.Nil => + } + + override final def foreachEntry[U](f: (Long, T) => U): Unit = this match { + case LongMap.Bin(_, _, left, right) => { left.foreachEntry(f); right.foreachEntry(f) } + case LongMap.Tip(key, value) => f(key, value) + case LongMap.Nil => + } + + override def keysIterator: Iterator[Long] = this match { + case LongMap.Nil => Iterator.empty + case _ => new LongMapKeyIterator(this) + } + + /** + * Loop over the keys of the map. The same as keys.foreach(f), but may + * be more efficient. + * + * @param f The loop body + */ + final def foreachKey[U](f: Long => U): Unit = this match { + case LongMap.Bin(_, _, left, right) => { left.foreachKey(f); right.foreachKey(f) } + case LongMap.Tip(key, _) => f(key) + case LongMap.Nil => + } + + override def valuesIterator: Iterator[T] = this match { + case LongMap.Nil => Iterator.empty + case _ => new LongMapValueIterator(this) + } + + /** + * Loop over the values of the map. The same as values.foreach(f), but may + * be more efficient. + * + * @param f The loop body + */ + final def foreachValue[U](f: T => U): Unit = this match { + case LongMap.Bin(_, _, left, right) => { left.foreachValue(f); right.foreachValue(f) } + case LongMap.Tip(_, value) => f(value) + case LongMap.Nil => + } + + override protected[this] def className = "LongMap" + + override def isEmpty = this eq LongMap.Nil + override def knownSize: Int = if (isEmpty) 0 else super.knownSize + override def filter(f: ((Long, T)) => Boolean): LongMap[T] = this match { + case LongMap.Bin(prefix, mask, left, right) => { + val (newleft, newright) = (left.filter(f), right.filter(f)) + if ((left eq newleft) && (right eq newright)) this + else bin(prefix, mask, newleft, newright) + } + case LongMap.Tip(key, value) => + if (f((key, value))) this + else LongMap.Nil + case LongMap.Nil => LongMap.Nil + } + + override def transform[S](f: (Long, T) => S): LongMap[S] = this match { + case b@LongMap.Bin(prefix, mask, left, right) => b.bin(left.transform(f), right.transform(f)) + case t@LongMap.Tip(key, value) => t.withValue(f(key, value)) + case LongMap.Nil => LongMap.Nil + } + + final override def size: Int = this match { + case LongMap.Nil => 0 + case LongMap.Tip(_, _) => 1 + case LongMap.Bin(_, _, left, right) => left.size + right.size + } + + @tailrec + final def get(key: Long): Option[T] = this match { + case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.get(key) else right.get(key) + case LongMap.Tip(key2, value) => if (key == key2) Some(value) else None + case LongMap.Nil => None + } + + @tailrec + final override def getOrElse[S >: T](key: Long, default: => S): S = this match { + case LongMap.Nil => default + case LongMap.Tip(key2, value) => if (key == key2) value else default + case LongMap.Bin(prefix, mask, left, right) => + if (zero(key, mask)) left.getOrElse(key, default) else right.getOrElse(key, default) + } + + @tailrec + final override def apply(key: Long): T = this match { + case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key) + case LongMap.Tip(key2, value) => if (key == key2) value else throw new IllegalArgumentException("Key not found") + case LongMap.Nil => throw new IllegalArgumentException("key not found") + } + + override def + [S >: T] (kv: (Long, S)): LongMap[S] = updated(kv._1, kv._2) + + override def updated[S >: T](key: Long, value: S): LongMap[S] = this match { + case LongMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, LongMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) LongMap.Bin(prefix, mask, left.updated(key, value), right) + else LongMap.Bin(prefix, mask, left, right.updated(key, value)) + case LongMap.Tip(key2, value2) => + if (key == key2) LongMap.Tip(key, value) + else join(key, LongMap.Tip(key, value), key2, this) + case LongMap.Nil => LongMap.Tip(key, value) + } + + /** + * Updates the map, using the provided function to resolve conflicts if the key is already present. + * + * Equivalent to + * {{{ + * this.get(key) match { + * case None => this.update(key, value) + * case Some(oldvalue) => this.update(key, f(oldvalue, value) + * } + * }}} + * + * @tparam S The supertype of values in this `LongMap`. + * @param key The key to update. + * @param value The value to use if there is no conflict. + * @param f The function used to resolve conflicts. + * @return The updated map. + */ + def updateWith[S >: T](key: Long, value: S, f: (T, S) => S): LongMap[S] = this match { + case LongMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, LongMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) LongMap.Bin(prefix, mask, left.updateWith(key, value, f), right) + else LongMap.Bin(prefix, mask, left, right.updateWith(key, value, f)) + case LongMap.Tip(key2, value2) => + if (key == key2) LongMap.Tip(key, f(value2, value)) + else join(key, LongMap.Tip(key, value), key2, this) + case LongMap.Nil => LongMap.Tip(key, value) + } + + def removed(key: Long): LongMap[T] = this match { + case LongMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) this + else if (zero(key, mask)) bin(prefix, mask, left - key, right) + else bin(prefix, mask, left, right - key) + case LongMap.Tip(key2, _) => + if (key == key2) LongMap.Nil + else this + case LongMap.Nil => LongMap.Nil + } + + /** + * A combined transform and filter function. Returns an `LongMap` such that + * for each `(key, value)` mapping in this map, if `f(key, value) == None` + * the map contains no mapping for key, and if `f(key, value)`. + * + * @tparam S The type of the values in the resulting `LongMap`. + * @param f The transforming function. + * @return The modified map. + */ + def modifyOrRemove[S](f: (Long, T) => Option[S]): LongMap[S] = this match { + case LongMap.Bin(prefix, mask, left, right) => { + val newleft = left.modifyOrRemove(f) + val newright = right.modifyOrRemove(f) + if ((left eq newleft) && (right eq newright)) this.asInstanceOf[LongMap[S]] + else bin(prefix, mask, newleft, newright) + } + case LongMap.Tip(key, value) => f(key, value) match { + case None => LongMap.Nil + case Some(value2) => + //hack to preserve sharing + if (value.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef]) this.asInstanceOf[LongMap[S]] + else LongMap.Tip(key, value2) + } + case LongMap.Nil => LongMap.Nil + } + + /** + * Forms a union map with that map, using the combining function to resolve conflicts. + * + * @tparam S The type of values in `that`, a supertype of values in `this`. + * @param that The map to form a union with. + * @param f The function used to resolve conflicts between two mappings. + * @return Union of `this` and `that`, with identical key conflicts resolved using the function `f`. + */ + def unionWith[S >: T](that: LongMap[S], f: (Long, S, S) => S): LongMap[S] = (this, that) match{ + case (LongMap.Bin(p1, m1, l1, r1), that@(LongMap.Bin(p2, m2, l2, r2))) => + if (shorter(m1, m2)) { + if (!hasMatch(p2, p1, m1)) join(p1, this, p2, that) + else if (zero(p2, m1)) LongMap.Bin(p1, m1, l1.unionWith(that, f), r1) + else LongMap.Bin(p1, m1, l1, r1.unionWith(that, f)) + } else if (shorter(m2, m1)){ + if (!hasMatch(p1, p2, m2)) join(p1, this, p2, that) + else if (zero(p1, m2)) LongMap.Bin(p2, m2, this.unionWith(l2, f), r2) + else LongMap.Bin(p2, m2, l2, this.unionWith(r2, f)) + } + else { + if (p1 == p2) LongMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)) + else join(p1, this, p2, that) + } + case (LongMap.Tip(key, value), x) => x.updateWith(key, value, (x, y) => f(key, y, x)) + case (x, LongMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)) + case (LongMap.Nil, x) => x + case (x, LongMap.Nil) => x + } + + /** + * Forms the intersection of these two maps with a combining function. The + * resulting map is a map that has only keys present in both maps and has + * values produced from the original mappings by combining them with `f`. + * + * @tparam S The type of values in `that`. + * @tparam R The type of values in the resulting `LongMap`. + * @param that The map to intersect with. + * @param f The combining function. + * @return Intersection of `this` and `that`, with values for identical keys produced by function `f`. + */ + def intersectionWith[S, R](that: LongMap[S], f: (Long, T, S) => R): LongMap[R] = (this, that) match { + case (LongMap.Bin(p1, m1, l1, r1), that@LongMap.Bin(p2, m2, l2, r2)) => + if (shorter(m1, m2)) { + if (!hasMatch(p2, p1, m1)) LongMap.Nil + else if (zero(p2, m1)) l1.intersectionWith(that, f) + else r1.intersectionWith(that, f) + } else if (m1 == m2) bin(p1, m1, l1.intersectionWith(l2, f), r1.intersectionWith(r2, f)) + else { + if (!hasMatch(p1, p2, m2)) LongMap.Nil + else if (zero(p1, m2)) this.intersectionWith(l2, f) + else this.intersectionWith(r2, f) + } + case (LongMap.Tip(key, value), that) => that.get(key) match { + case None => LongMap.Nil + case Some(value2) => LongMap.Tip(key, f(key, value, value2)) + } + case (_, LongMap.Tip(key, value)) => this.get(key) match { + case None => LongMap.Nil + case Some(value2) => LongMap.Tip(key, f(key, value2, value)) + } + case (_, _) => LongMap.Nil + } + + /** + * Left biased intersection. Returns the map that has all the same mappings as this but only for keys + * which are present in the other map. + * + * @tparam R The type of values in `that`. + * @param that The map to intersect with. + * @return A map with all the keys both in `this` and `that`, mapped to corresponding values from `this`. + */ + def intersection[R](that: LongMap[R]): LongMap[T] = + this.intersectionWith(that, (key: Long, value: T, value2: R) => value) + + def ++[S >: T](that: LongMap[S]) = + this.unionWith[S](that, (key, x, y) => y) + + @tailrec + final def firstKey: Long = this match { + case LongMap.Bin(_, _, l, r) => l.firstKey + case LongMap.Tip(k, v) => k + case LongMap.Nil => throw new IllegalStateException("Empty set") + } + + @tailrec + final def lastKey: Long = this match { + case LongMap.Bin(_, _, l, r) => r.lastKey + case LongMap.Tip(k , v) => k + case LongMap.Nil => throw new IllegalStateException("Empty set") + } + + def map[V2](f: ((Long, T)) => (Long, V2)): LongMap[V2] = LongMap.from(new View.Map(coll, f)) + + def flatMap[V2](f: ((Long, T)) => IterableOnce[(Long, V2)]): LongMap[V2] = LongMap.from(new View.FlatMap(coll, f)) + + override def concat[V1 >: T](that: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = + super.concat(that).asInstanceOf[LongMap[V1]] // Already has correct type but not declared as such + + override def ++ [V1 >: T](that: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = concat(that) + + def collect[V2](pf: PartialFunction[(Long, T), (Long, V2)]): LongMap[V2] = + strictOptimizedCollect(LongMap.newBuilder[V2], pf) + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(LongMap.toFactory[T](LongMap), this) +} diff --git a/library/src/scala/collection/immutable/Map.scala b/library/src/scala/collection/immutable/Map.scala new file mode 100644 index 000000000000..8f372312512e --- /dev/null +++ b/library/src/scala/collection/immutable/Map.scala @@ -0,0 +1,712 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.generic.DefaultSerializable +import scala.collection.immutable.Map.Map4 +import scala.collection.mutable.{Builder, ReusableBuilder} +import SeqMap.{SeqMap1, SeqMap2, SeqMap3, SeqMap4} + +/** Base type of immutable Maps */ +trait Map[K, +V] + extends Iterable[(K, V)] + with collection.Map[K, V] + with MapOps[K, V, Map, Map[K, V]] + with MapFactoryDefaults[K, V, Map, Iterable] { + + override def mapFactory: scala.collection.MapFactory[Map] = Map + + override final def toMap[K2, V2](implicit ev: (K, V) <:< (K2, V2)): Map[K2, V2] = Map.from(this.asInstanceOf[Map[K2, V2]]) + + /** The same map with a given default function. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefault`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d the function mapping keys to values, used for non-present keys + * @return a wrapper of the map with a default value + */ + def withDefault[V1 >: V](d: K => V1): Map[K, V1] = new Map.WithDefault[K, V1](this, d) + + /** The same map with a given default value. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefaultValue`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d default value used for non-present keys + * @return a wrapper of the map with a default value + */ + def withDefaultValue[V1 >: V](d: V1): Map[K, V1] = new Map.WithDefault[K, V1](this, _ => d) +} + +/** Base trait of immutable Maps implementations + * + * @define coll immutable map + * @define Coll `immutable.Map` + */ +trait MapOps[K, +V, +CC[X, +Y] <: MapOps[X, Y, CC, _], +C <: MapOps[K, V, CC, C]] + extends IterableOps[(K, V), Iterable, C] + with collection.MapOps[K, V, CC, C] { + + protected def coll: C with CC[K, V] + + /** Removes a key from this map, returning a new map. + * + * @param key the key to be removed + * @return a new map without a binding for ''key'' + */ + def removed(key: K): C + + /** Alias for `removed` */ + @`inline` final def - (key: K): C = removed(key) + + @deprecated("Use -- with an explicit collection", "2.13.0") + def - (key1: K, key2: K, keys: K*): C = removed(key1).removed(key2).removedAll(keys) + + /** Creates a new $coll from this $coll by removing all elements of another + * collection. + * + * $willForceEvaluation + * + * @param keys the collection containing the removed elements. + * @return a new $coll that contains all elements of the current $coll + * except one less occurrence of each of the elements of `elems`. + */ + def removedAll(keys: IterableOnce[K]): C = keys.iterator.foldLeft[C](coll)(_ - _) + + /** Alias for `removedAll` */ + @`inline` final override def -- (keys: IterableOnce[K]): C = removedAll(keys) + + /** Creates a new map obtained by updating this map with a given key/value pair. + * @param key the key + * @param value the value + * @tparam V1 the type of the added value + * @return A new map with the new key/value mapping added to this map. + */ + def updated[V1 >: V](key: K, value: V1): CC[K, V1] + + /** + * Update a mapping for the specified key and its current optionally mapped value + * (`Some` if there is current mapping, `None` if not). + * + * If the remapping function returns `Some(v)`, the mapping is updated with the new value `v`. + * If the remapping function returns `None`, the mapping is removed (or remains absent if initially absent). + * If the function itself throws an exception, the exception is rethrown, and the current mapping is left unchanged. + * + * @param key the key value + * @param remappingFunction a function that receives current optionally mapped value and return a new mapping + * @return A new map with the updated mapping with the key + */ + def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): CC[K,V1] = { + val previousValue = this.get(key) + remappingFunction(previousValue) match { + case None => previousValue.fold(coll)(_ => this.removed(key).coll) + case Some(nextValue) => + if (previousValue.exists(_.asInstanceOf[AnyRef] eq nextValue.asInstanceOf[AnyRef])) coll + else coll.updated(key, nextValue) + } + } + + /** + * Alias for `updated` + * + * @param kv the key/value pair. + * @tparam V1 the type of the value in the key/value pair. + * @return A new map with the new binding added to this map. + */ + override def + [V1 >: V](kv: (K, V1)): CC[K, V1] = updated(kv._1, kv._2) + + /** This function transforms all the values of mappings contained + * in this map with function `f`. + * + * @param f A function over keys and values + * @return the updated map + */ + def transform[W](f: (K, V) => W): CC[K, W] = map { case (k, v) => (k, f(k, v)) } + + override def keySet: Set[K] = new ImmutableKeySet + + /** The implementation class of the set returned by `keySet` */ + protected[immutable] class ImmutableKeySet extends AbstractSet[K] with GenKeySet with DefaultSerializable { + def incl(elem: K): Set[K] = if (this(elem)) this else empty ++ this + elem + def excl(elem: K): Set[K] = if (this(elem)) empty ++ this - elem else this + } + +} + +trait StrictOptimizedMapOps[K, +V, +CC[X, +Y] <: MapOps[X, Y, CC, _], +C <: MapOps[K, V, CC, C]] + extends MapOps[K, V, CC, C] + with collection.StrictOptimizedMapOps[K, V, CC, C] + with StrictOptimizedIterableOps[(K, V), Iterable, C] { + + override def concat [V1 >: V](that: collection.IterableOnce[(K, V1)]): CC[K, V1] = { + var result: CC[K, V1] = coll + val it = that.iterator + while (it.hasNext) result = result + it.next() + result + } +} + + +/** + * $factoryInfo + * @define coll immutable map + * @define Coll `immutable.Map` + */ +@SerialVersionUID(3L) +object Map extends MapFactory[Map] { + + @SerialVersionUID(3L) + class WithDefault[K, +V](val underlying: Map[K, V], val defaultValue: K => V) + extends AbstractMap[K, V] + with MapOps[K, V, Map, WithDefault[K, V]] with Serializable { + + def get(key: K): Option[V] = underlying.get(key) + + override def default(key: K): V = defaultValue(key) + + override def iterableFactory: IterableFactory[Iterable] = underlying.iterableFactory + + def iterator: Iterator[(K, V)] = underlying.iterator + + override def isEmpty: Boolean = underlying.isEmpty + + override def mapFactory: MapFactory[Map] = underlying.mapFactory + + override def concat [V2 >: V](xs: collection.IterableOnce[(K, V2)]): WithDefault[K, V2] = + new WithDefault(underlying.concat(xs), defaultValue) + + def removed(key: K): WithDefault[K, V] = new WithDefault[K, V](underlying.removed(key), defaultValue) + + def updated[V1 >: V](key: K, value: V1): WithDefault[K, V1] = + new WithDefault[K, V1](underlying.updated(key, value), defaultValue) + + override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) + + override protected def fromSpecific(coll: collection.IterableOnce[(K, V)] @uncheckedVariance): WithDefault[K, V] = + new WithDefault[K, V](mapFactory.from(coll), defaultValue) + + override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] @uncheckedVariance = + Map.newBuilder.mapResult((p: Map[K, V]) => new WithDefault[K, V](p, defaultValue)) + } + + def empty[K, V]: Map[K, V] = EmptyMap.asInstanceOf[Map[K, V]] + + def from[K, V](it: IterableOnce[(K, V)]): Map[K, V] = + it match { + case it: Iterable[_] if it.isEmpty => empty[K, V] + // Since IterableOnce[(K, V)] launders the variance of K, + // identify only our implementations which can be soundly substituted. + // For example, the ordering used by sorted maps would fail on widened key type. (scala/bug#12745) + // The following type test is not sufficient: case m: Map[K, V] => m + case m: HashMap[K, V] => m + case m: Map1[K, V] => m + case m: Map2[K, V] => m + case m: Map3[K, V] => m + case m: Map4[K, V] => m + //case m: WithDefault[K, V] => m // cf SortedMap.WithDefault + //case m: SeqMap[K, V] => SeqMap.from(it) // inlined here to avoid hard dependency + case m: ListMap[K, V] => m + case m: TreeSeqMap[K, V] => m + case m: VectorMap[K, V] => m + case m: SeqMap1[K, V] => m + case m: SeqMap2[K, V] => m + case m: SeqMap3[K, V] => m + case m: SeqMap4[K, V] => m + + // Maps with a reified key type must be rebuilt, such as `SortedMap` and `IntMap`. + case _ => newBuilder[K, V].addAll(it).result() + } + + def newBuilder[K, V]: Builder[(K, V), Map[K, V]] = new MapBuilderImpl + + @SerialVersionUID(3L) + private object EmptyMap extends AbstractMap[Any, Nothing] with Serializable { + override def size: Int = 0 + override def knownSize: Int = 0 + override def isEmpty: Boolean = true + override def apply(key: Any) = throw new NoSuchElementException("key not found: " + key) + override def contains(key: Any) = false + def get(key: Any): Option[Nothing] = None + override def getOrElse [V1](key: Any, default: => V1): V1 = default + def iterator: Iterator[(Any, Nothing)] = Iterator.empty + override def keysIterator: Iterator[Any] = Iterator.empty + override def valuesIterator: Iterator[Nothing] = Iterator.empty + def updated [V1] (key: Any, value: V1): Map[Any, V1] = new Map1(key, value) + def removed(key: Any): Map[Any, Nothing] = this + override def concat[V2 >: Nothing](suffix: IterableOnce[(Any, V2)]): Map[Any, V2] = suffix match { + case m: immutable.Map[Any, V2] => m + case _ => super.concat(suffix) + } + } + + @SerialVersionUID(3L) + final class Map1[K, +V](key1: K, value1: V) extends AbstractMap[K, V] with StrictOptimizedIterableOps[(K, V), Iterable, Map[K, V]] with Serializable { + override def size: Int = 1 + override def knownSize: Int = 1 + override def isEmpty: Boolean = false + override def apply(key: K): V = if (key == key1) value1 else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K): Boolean = key == key1 + def get(key: K): Option[V] = + if (key == key1) Some(value1) else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 else default + def iterator: Iterator[(K, V)] = Iterator.single((key1, value1)) + override def keysIterator: Iterator[K] = Iterator.single(key1) + override def valuesIterator: Iterator[V] = Iterator.single(value1) + def updated[V1 >: V](key: K, value: V1): Map[K, V1] = + if (key == key1) new Map1(key1, value) + else new Map2(key1, value1, key, value) + def removed(key: K): Map[K, V] = + if (key == key1) Map.empty else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)) + } + override def exists(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) + override def forall(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) + override protected[collection] def filterImpl(pred: ((K, V)) => Boolean, isFlipped: Boolean): Map[K, V] = + if (pred((key1, value1)) != isFlipped) this else Map.empty + override def transform[W](f: (K, V) => W): Map[K, W] = { + val walue1 = f(key1, value1) + if (walue1.asInstanceOf[AnyRef] eq value1.asInstanceOf[AnyRef]) this.asInstanceOf[Map[K, W]] + else new Map1(key1, walue1) + } + override def hashCode(): Int = { + import scala.util.hashing.MurmurHash3 + var a, b = 0 + val N = 1 + var c = 1 + + var h = MurmurHash3.tuple2Hash(key1, value1) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.mapSeed + h = MurmurHash3.mix(h, a) + h = MurmurHash3.mix(h, b) + h = MurmurHash3.mixLast(h, c) + MurmurHash3.finalizeHash(h, N) + } + } + + @SerialVersionUID(3L) + final class Map2[K, +V](key1: K, value1: V, key2: K, value2: V) extends AbstractMap[K, V] with StrictOptimizedIterableOps[(K, V), Iterable, Map[K, V]] with Serializable { + override def size: Int = 2 + override def knownSize: Int = 2 + override def isEmpty: Boolean = false + override def apply(key: K): V = + if (key == key1) value1 + else if (key == key2) value2 + else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K): Boolean = (key == key1) || (key == key2) + def get(key: K): Option[V] = + if (key == key1) Some(value1) + else if (key == key2) Some(value2) + else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 + else if (key == key2) value2 + else default + def iterator: Iterator[(K, V)] = new Map2Iterator[(K, V)] { + override protected def nextResult(k: K, v: V): (K, V) = (k, v) + } + override def keysIterator: Iterator[K] = new Map2Iterator[K] { + override protected def nextResult(k: K, v: V): K = k + } + override def valuesIterator: Iterator[V] = new Map2Iterator[V] { + override protected def nextResult(k: K, v: V): V = v + } + + private abstract class Map2Iterator[A] extends AbstractIterator[A] { + private[this] var i = 0 + override def hasNext: Boolean = i < 2 + override def next(): A = { + val result = i match { + case 0 => nextResult(key1, value1) + case 1 => nextResult(key2, value2) + case _ => Iterator.empty.next() + } + i += 1 + result + } + override def drop(n: Int): Iterator[A] = { i += n; this } + protected def nextResult(k: K, v: V @uncheckedVariance): A + } + def updated[V1 >: V](key: K, value: V1): Map[K, V1] = + if (key == key1) new Map2(key1, value, key2, value2) + else if (key == key2) new Map2(key1, value1, key2, value) + else new Map3(key1, value1, key2, value2, key, value) + def removed(key: K): Map[K, V] = + if (key == key1) new Map1(key2, value2) + else if (key == key2) new Map1(key1, value1) + else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)); f((key2, value2)) + } + override def exists(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) || p((key2, value2)) + override def forall(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) && p((key2, value2)) + override protected[collection] def filterImpl(pred: ((K, V)) => Boolean, isFlipped: Boolean): Map[K, V] = { + var k1 = null.asInstanceOf[K] + var v1 = null.asInstanceOf[V] + var n = 0 + if (pred((key1, value1)) != isFlipped) { {k1 = key1; v1 = value1}; n += 1} + if (pred((key2, value2)) != isFlipped) { if (n == 0) {k1 = key2; v1 = value2}; n += 1} + + n match { + case 0 => Map.empty + case 1 => new Map1(k1, v1) + case 2 => this + } + } + override def transform[W](f: (K, V) => W): Map[K, W] = { + val walue1 = f(key1, value1) + val walue2 = f(key2, value2) + if ((walue1.asInstanceOf[AnyRef] eq value1.asInstanceOf[AnyRef]) && + (walue2.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef])) this.asInstanceOf[Map[K, W]] + else new Map2(key1, walue1, key2, walue2) + } + override def hashCode(): Int = { + import scala.util.hashing.MurmurHash3 + var a, b = 0 + val N = 2 + var c = 1 + + var h = MurmurHash3.tuple2Hash(key1, value1) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.tuple2Hash(key2, value2) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.mapSeed + h = MurmurHash3.mix(h, a) + h = MurmurHash3.mix(h, b) + h = MurmurHash3.mixLast(h, c) + MurmurHash3.finalizeHash(h, N) + } + } + + @SerialVersionUID(3L) + class Map3[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V) extends AbstractMap[K, V] with StrictOptimizedIterableOps[(K, V), Iterable, Map[K, V]] with Serializable { + override def size: Int = 3 + override def knownSize: Int = 3 + override def isEmpty: Boolean = false + override def apply(key: K): V = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K): Boolean = (key == key1) || (key == key2) || (key == key3) + def get(key: K): Option[V] = + if (key == key1) Some(value1) + else if (key == key2) Some(value2) + else if (key == key3) Some(value3) + else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else default + def iterator: Iterator[(K, V)] = new Map3Iterator[(K, V)] { + override protected def nextResult(k: K, v: V): (K, V) = (k, v) + } + override def keysIterator: Iterator[K] = new Map3Iterator[K] { + override protected def nextResult(k: K, v: V): K = k + } + override def valuesIterator: Iterator[V] = new Map3Iterator[V] { + override protected def nextResult(k: K, v: V): V = v + } + + private abstract class Map3Iterator[A] extends AbstractIterator[A] { + private[this] var i = 0 + override def hasNext: Boolean = i < 3 + override def next(): A = { + val result = i match { + case 0 => nextResult(key1, value1) + case 1 => nextResult(key2, value2) + case 2 => nextResult(key3, value3) + case _ => Iterator.empty.next() + } + i += 1 + result + } + override def drop(n: Int): Iterator[A] = { i += n; this } + protected def nextResult(k: K, v: V @uncheckedVariance): A + } + def updated[V1 >: V](key: K, value: V1): Map[K, V1] = + if (key == key1) new Map3(key1, value, key2, value2, key3, value3) + else if (key == key2) new Map3(key1, value1, key2, value, key3, value3) + else if (key == key3) new Map3(key1, value1, key2, value2, key3, value) + else new Map4(key1, value1, key2, value2, key3, value3, key, value) + def removed(key: K): Map[K, V] = + if (key == key1) new Map2(key2, value2, key3, value3) + else if (key == key2) new Map2(key1, value1, key3, value3) + else if (key == key3) new Map2(key1, value1, key2, value2) + else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)); f((key2, value2)); f((key3, value3)) + } + override def exists(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) || p((key2, value2)) || p((key3, value3)) + override def forall(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) && p((key2, value2)) && p((key3, value3)) + override protected[collection] def filterImpl(pred: ((K, V)) => Boolean, isFlipped: Boolean): Map[K, V] = { + var k1, k2 = null.asInstanceOf[K] + var v1, v2 = null.asInstanceOf[V] + var n = 0 + if (pred((key1, value1)) != isFlipped) { { k1 = key1; v1 = value1 }; n += 1} + if (pred((key2, value2)) != isFlipped) { if (n == 0) { k1 = key2; v1 = value2 } else { k2 = key2; v2 = value2 }; n += 1} + if (pred((key3, value3)) != isFlipped) { if (n == 0) { k1 = key3; v1 = value3 } else if (n == 1) { k2 = key3; v2 = value3 }; n += 1} + + n match { + case 0 => Map.empty + case 1 => new Map1(k1, v1) + case 2 => new Map2(k1, v1, k2, v2) + case 3 => this + } + } + override def transform[W](f: (K, V) => W): Map[K, W] = { + val walue1 = f(key1, value1) + val walue2 = f(key2, value2) + val walue3 = f(key3, value3) + if ((walue1.asInstanceOf[AnyRef] eq value1.asInstanceOf[AnyRef]) && + (walue2.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef]) && + (walue3.asInstanceOf[AnyRef] eq value3.asInstanceOf[AnyRef])) this.asInstanceOf[Map[K, W]] + else new Map3(key1, walue1, key2, walue2, key3, walue3) + } + override def hashCode(): Int = { + import scala.util.hashing.MurmurHash3 + var a, b = 0 + val N = 3 + var c = 1 + + var h = MurmurHash3.tuple2Hash(key1, value1) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.tuple2Hash(key2, value2) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.tuple2Hash(key3, value3) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.mapSeed + h = MurmurHash3.mix(h, a) + h = MurmurHash3.mix(h, b) + h = MurmurHash3.mixLast(h, c) + MurmurHash3.finalizeHash(h, N) + } + } + + @SerialVersionUID(3L) + final class Map4[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V, key4: K, value4: V) + extends AbstractMap[K, V] with StrictOptimizedIterableOps[(K, V), Iterable, Map[K, V]] with Serializable { + + override def size: Int = 4 + override def knownSize: Int = 4 + override def isEmpty: Boolean = false + override def apply(key: K): V = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else if (key == key4) value4 + else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K): Boolean = (key == key1) || (key == key2) || (key == key3) || (key == key4) + def get(key: K): Option[V] = + if (key == key1) Some(value1) + else if (key == key2) Some(value2) + else if (key == key3) Some(value3) + else if (key == key4) Some(value4) + else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else if (key == key4) value4 + else default + def iterator: Iterator[(K, V)] = new Map4Iterator[(K, V)] { + override protected def nextResult(k: K, v: V): (K, V) = (k, v) + } + override def keysIterator: Iterator[K] = new Map4Iterator[K] { + override protected def nextResult(k: K, v: V): K = k + } + override def valuesIterator: Iterator[V] = new Map4Iterator[V] { + override protected def nextResult(k: K, v: V): V = v + } + + private abstract class Map4Iterator[A] extends AbstractIterator[A] { + private[this] var i = 0 + override def hasNext: Boolean = i < 4 + override def next(): A = { + val result = i match { + case 0 => nextResult(key1, value1) + case 1 => nextResult(key2, value2) + case 2 => nextResult(key3, value3) + case 3 => nextResult(key4, value4) + case _ => Iterator.empty.next() + } + i += 1 + result + } + override def drop(n: Int): Iterator[A] = { i += n; this } + protected def nextResult(k: K, v: V @uncheckedVariance): A + } + def updated[V1 >: V](key: K, value: V1): Map[K, V1] = + if (key == key1) new Map4(key1, value, key2, value2, key3, value3, key4, value4) + else if (key == key2) new Map4(key1, value1, key2, value, key3, value3, key4, value4) + else if (key == key3) new Map4(key1, value1, key2, value2, key3, value, key4, value4) + else if (key == key4) new Map4(key1, value1, key2, value2, key3, value3, key4, value) + else HashMap.empty[K, V1].updated(key1,value1).updated(key2, value2).updated(key3, value3).updated(key4, value4).updated(key, value) + def removed(key: K): Map[K, V] = + if (key == key1) new Map3(key2, value2, key3, value3, key4, value4) + else if (key == key2) new Map3(key1, value1, key3, value3, key4, value4) + else if (key == key3) new Map3(key1, value1, key2, value2, key4, value4) + else if (key == key4) new Map3(key1, value1, key2, value2, key3, value3) + else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)); f((key2, value2)); f((key3, value3)); f((key4, value4)) + } + override def exists(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) || p((key2, value2)) || p((key3, value3)) || p((key4, value4)) + override def forall(p: ((K, V)) => Boolean): Boolean = p((key1, value1)) && p((key2, value2)) && p((key3, value3)) && p((key4, value4)) + override protected[collection] def filterImpl(pred: ((K, V)) => Boolean, isFlipped: Boolean): Map[K, V] = { + var k1, k2, k3 = null.asInstanceOf[K] + var v1, v2, v3 = null.asInstanceOf[V] + var n = 0 + if (pred((key1, value1)) != isFlipped) { { k1 = key1; v1 = value1 }; n += 1} + if (pred((key2, value2)) != isFlipped) { if (n == 0) { k1 = key2; v1 = value2 } else { k2 = key2; v2 = value2 }; n += 1} + if (pred((key3, value3)) != isFlipped) { if (n == 0) { k1 = key3; v1 = value3 } else if (n == 1) { k2 = key3; v2 = value3 } else { k3 = key3; v3 = value3}; n += 1} + if (pred((key4, value4)) != isFlipped) { if (n == 0) { k1 = key4; v1 = value4 } else if (n == 1) { k2 = key4; v2 = value4 } else if (n == 2) { k3 = key4; v3 = value4 }; n += 1} + + n match { + case 0 => Map.empty + case 1 => new Map1(k1, v1) + case 2 => new Map2(k1, v1, k2, v2) + case 3 => new Map3(k1, v1, k2, v2, k3, v3) + case 4 => this + } + } + override def transform[W](f: (K, V) => W): Map[K, W] = { + val walue1 = f(key1, value1) + val walue2 = f(key2, value2) + val walue3 = f(key3, value3) + val walue4 = f(key4, value4) + if ((walue1.asInstanceOf[AnyRef] eq value1.asInstanceOf[AnyRef]) && + (walue2.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef]) && + (walue3.asInstanceOf[AnyRef] eq value3.asInstanceOf[AnyRef]) && + (walue4.asInstanceOf[AnyRef] eq value4.asInstanceOf[AnyRef])) this.asInstanceOf[Map[K, W]] + else new Map4(key1, walue1, key2, walue2, key3, walue3, key4, walue4) + } + private[immutable] def buildTo[V1 >: V](builder: HashMapBuilder[K, V1]): builder.type = + builder.addOne(key1, value1).addOne(key2, value2).addOne(key3, value3).addOne(key4, value4) + override def hashCode(): Int = { + import scala.util.hashing.MurmurHash3 + var a, b = 0 + val N = 4 + var c = 1 + + var h = MurmurHash3.tuple2Hash(key1, value1) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.tuple2Hash(key2, value2) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.tuple2Hash(key3, value3) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.tuple2Hash(key4, value4) + a += h + b ^= h + c *= h | 1 + + h = MurmurHash3.mapSeed + h = MurmurHash3.mix(h, a) + h = MurmurHash3.mix(h, b) + h = MurmurHash3.mixLast(h, c) + MurmurHash3.finalizeHash(h, N) + } + } +} + +/** Explicit instantiation of the `Map` trait to reduce class file size in subclasses. */ +abstract class AbstractMap[K, +V] extends scala.collection.AbstractMap[K, V] with Map[K, V] + +private[immutable] final class MapBuilderImpl[K, V] extends ReusableBuilder[(K, V), Map[K, V]] { + private[this] var elems: Map[K, V] = Map.empty + private[this] var switchedToHashMapBuilder: Boolean = false + private[this] var hashMapBuilder: HashMapBuilder[K, V] = _ + + private[immutable] def getOrElse[V0 >: V](key: K, value: V0): V0 = + if (hashMapBuilder ne null) hashMapBuilder.getOrElse(key, value) + else elems.getOrElse(key, value) + + override def clear(): Unit = { + elems = Map.empty + if (hashMapBuilder != null) { + hashMapBuilder.clear() + } + switchedToHashMapBuilder = false + } + + override def result(): Map[K, V] = + if (switchedToHashMapBuilder) hashMapBuilder.result() else elems + + def addOne(key: K, value: V): this.type = { + if (switchedToHashMapBuilder) { + hashMapBuilder.addOne(key, value) + } else if (elems.size < 4) { + elems = elems.updated(key, value) + } else { + // assert(elems.size == 4) + if (elems.contains(key)) { + elems = elems.updated(key, value) + } else { + switchedToHashMapBuilder = true + if (hashMapBuilder == null) { + hashMapBuilder = new HashMapBuilder + } + elems.asInstanceOf[Map4[K, V]].buildTo(hashMapBuilder) + hashMapBuilder.addOne(key, value) + } + } + + this + } + + def addOne(elem: (K, V)) = addOne(elem._1, elem._2) + + override def addAll(xs: IterableOnce[(K, V)]): this.type = + if (switchedToHashMapBuilder) { + hashMapBuilder.addAll(xs) + this + } else { + super.addAll(xs) + } +} diff --git a/library/src/scala/collection/immutable/NumericRange.scala b/library/src/scala/collection/immutable/NumericRange.scala new file mode 100644 index 000000000000..78efb2adafca --- /dev/null +++ b/library/src/scala/collection/immutable/NumericRange.scala @@ -0,0 +1,541 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.immutable + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{AbstractIterator, AnyStepper, IterableFactoryDefaults, Iterator, Stepper, StepperShape} +import scala.collection.generic.CommonErrors + +/** `NumericRange` is a more generic version of the + * `Range` class which works with arbitrary types. + * It must be supplied with an `Integral` implementation of the + * range type. + * + * Factories for likely types include `Range.BigInt`, `Range.Long`, + * and `Range.BigDecimal`. `Range.Int` exists for completeness, but + * the `Int`-based `scala.Range` should be more performant. + * + * {{{ + * val r1 = Range(0, 100, 1) + * val veryBig = Int.MaxValue.toLong + 1 + * val r2 = Range.Long(veryBig, veryBig + 100, 1) + * assert(r1 sameElements r2.map(_ - veryBig)) + * }}} + * + * @define Coll `NumericRange` + * @define coll numeric range + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(3L) +sealed class NumericRange[T]( + val start: T, + val end: T, + val step: T, + val isInclusive: Boolean +)(implicit + num: Integral[T] +) + extends AbstractSeq[T] + with IndexedSeq[T] + with IndexedSeqOps[T, IndexedSeq, IndexedSeq[T]] + with StrictOptimizedSeqOps[T, IndexedSeq, IndexedSeq[T]] + with IterableFactoryDefaults[T, IndexedSeq] + with Serializable { self => + + override def iterator: Iterator[T] = new NumericRange.NumericRangeIterator(this, num) + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[T, S]): S with EfficientSplit = { + import scala.collection.convert._ + import impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntNumericRangeStepper (this.asInstanceOf[NumericRange[Int]], 0, length) + case StepperShape.LongShape => new LongNumericRangeStepper (this.asInstanceOf[NumericRange[Long]], 0, length) + case _ => shape.parUnbox(new AnyNumericRangeStepper[T](this, 0, length).asInstanceOf[AnyStepper[T] with EfficientSplit]) + } + s.asInstanceOf[S with EfficientSplit] + } + + + /** Note that NumericRange must be invariant so that constructs + * such as "1L to 10 by 5" do not infer the range type as AnyVal. + */ + import num._ + + // See comment in Range for why this must be lazy. + override lazy val length: Int = NumericRange.count(start, end, step, isInclusive) + override lazy val isEmpty: Boolean = ( + (num.gt(start, end) && num.gt(step, num.zero)) + || (num.lt(start, end) && num.lt(step, num.zero)) + || (num.equiv(start, end) && !isInclusive) + ) + override def last: T = + if (isEmpty) Nil.head + else locationAfterN(length - 1) + override def init: NumericRange[T] = + if (isEmpty) Nil.init + else new NumericRange(start, end - step, step, isInclusive) + + override def head: T = if (isEmpty) Nil.head else start + override def tail: NumericRange[T] = + if (isEmpty) Nil.tail + else if(isInclusive) new NumericRange.Inclusive(start + step, end, step) + else new NumericRange.Exclusive(start + step, end, step) + + /** Create a new range with the start and end values of this range and + * a new `step`. + */ + def by(newStep: T): NumericRange[T] = copy(start, end, newStep) + + + /** Create a copy of this range. + */ + def copy(start: T, end: T, step: T): NumericRange[T] = + new NumericRange(start, end, step, isInclusive) + + @throws[IndexOutOfBoundsException] + def apply(idx: Int): T = { + if (idx < 0 || idx >= length) + throw CommonErrors.indexOutOfBounds(index = idx, max = length - 1) + else locationAfterN(idx) + } + + override def foreach[@specialized(Specializable.Unit) U](f: T => U): Unit = { + var count = 0 + var current = start + while (count < length) { + f(current) + current += step + count += 1 + } + } + + private[this] def indexOfTyped(elem: T, from: Int): Int = + posOf(elem) match { + case pos if pos >= from => pos + case _ => -1 + } + + final override def indexOf[B >: T](elem: B, from: Int): Int = + try indexOfTyped(elem.asInstanceOf[T], from) + catch { case _: ClassCastException => super.indexOf(elem, from) } + + private[this] def lastIndexOfTyped(elem: T, end: Int): Int = + posOf(elem) match { + case pos if pos <= end => pos + case _ => -1 + } + + final override def lastIndexOf[B >: T](elem: B, end: Int = length - 1): Int = + try lastIndexOfTyped(elem.asInstanceOf[T], end) + catch { case _: ClassCastException => super.lastIndexOf(elem, end) } + + private[this] def posOf(i: T): Int = + /* + If i is in this NumericRange, its position can simply be calculated by taking the amount of values up till i. + NumericRange.count does this in an most efficient manner. + Note that the contains() method throws an exception if the range has more than Int.MaxValue elements, but so did + the original indexOf / lastIndexOf functions, so no functionality changed. */ + if (contains(i)) { + /* Because of zero indexing, the count is always one higher than the index. This can be simply solved by setting + isInclusive = false. */ + NumericRange.count(this.start, i, this.step, isInclusive = false) + } else -1 + + // TODO: these private methods are straight copies from Range, duplicated + // to guard against any (most likely illusory) performance drop. They should + // be eliminated one way or another. + + // Tests whether a number is within the endpoints, without testing + // whether it is a member of the sequence (i.e. when step > 1.) + private def isWithinBoundaries(elem: T) = !isEmpty && ( + (step > zero && start <= elem && elem <= last ) || + (step < zero && last <= elem && elem <= start) + ) + // Methods like apply throw exceptions on invalid n, but methods like take/drop + // are forgiving: therefore the checks are with the methods. + private def locationAfterN(n: Int): T = start + (step * fromInt(n)) + + private def crossesTheEndAfterN(n: Int): Boolean = { + // if we're sure that subtraction in the context of T won't overflow, we use this function + // to calculate the length of the range + def unsafeRangeLength(r: NumericRange[T]): T = { + val diff = num.minus(r.end, r.start) + val quotient = num.quot(diff, r.step) + val remainder = num.rem(diff, r.step) + if (!r.isInclusive && num.equiv(remainder, num.zero)) + num.max(quotient, num.zero) + else + num.max(num.plus(quotient, num.one), num.zero) + } + + // detects whether value can survive a bidirectional trip to -and then from- Int. + def fitsInInteger(value: T): Boolean = num.equiv(num.fromInt(num.toInt(value)), value) + + val stepIsInTheSameDirectionAsStartToEndVector = + (num.gt(end, start) && num.gt(step, num.zero)) || (num.lt(end, start) && num.sign(step) == -num.one) + + if (num.equiv(start, end) || n <= 0 || !stepIsInTheSameDirectionAsStartToEndVector) return n >= 1 + + val sameSign = num.equiv(num.sign(start), num.sign(end)) + + if (sameSign) { // subtraction is safe + val len = unsafeRangeLength(this) + if (fitsInInteger(len)) n >= num.toInt(len) else num.gteq(num.fromInt(n), len) + } else { + // split to two ranges, which subtraction is safe in both of them (around zero) + val stepsRemainderToZero = num.rem(start, step) + val walksOnZero = num.equiv(stepsRemainderToZero, num.zero) + val closestToZero = if (walksOnZero) -step else stepsRemainderToZero + + /* + When splitting into two ranges, we should be super-careful about one of the sides hitting MinValue of T, + so we take two steps smaller than zero to ensure unsafeRangeLength won't overflow (taking one step may overflow depending on the step). + Same thing happens for MaxValue from zero, so we take one step further to ensure the safety of unsafeRangeLength. + After performing such operation, there are some elements remaining in between and around zero, + which their length is represented by carry. + */ + val (l: NumericRange[T], r: NumericRange[T], carry: Int) = + if (num.lt(start, num.zero)) { + if (walksOnZero) { + val twoStepsAfterLargestNegativeNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (NumericRange(start, closestToZero, step), copy(twoStepsAfterLargestNegativeNumber, end, step), 2) + } else { + (NumericRange(start, closestToZero, step), copy(num.plus(closestToZero, step), end, step), 1) + } + } else { + if (walksOnZero) { + val twoStepsAfterZero = num.times(step, num.fromInt(2)) + (copy(twoStepsAfterZero, end, step), NumericRange.inclusive(start, -step, step), 2) + } else { + val twoStepsAfterSmallestPositiveNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (copy(twoStepsAfterSmallestPositiveNumber, end, step), NumericRange.inclusive(start, closestToZero, step), 2) + } + } + + val leftLength = unsafeRangeLength(l) + val rightLength = unsafeRangeLength(r) + + // instead of `n >= rightLength + leftLength + curry` which may cause addition overflow, + // this can be used `(n - leftLength - curry) >= rightLength` (Both in Int and T, depends on whether the lengths fit in Int) + if (fitsInInteger(leftLength) && fitsInInteger(rightLength)) + n - num.toInt(leftLength) - carry >= num.toInt(rightLength) + else + num.gteq(num.minus(num.minus(num.fromInt(n), leftLength), num.fromInt(carry)), rightLength) + } + } + + // When one drops everything. Can't ever have unchecked operations + // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } + // will overflow. This creates an exclusive range where start == end + // based on the given value. + private def newEmptyRange(value: T) = NumericRange(value, value, step) + + override def take(n: Int): NumericRange[T] = { + if (n <= 0 || isEmpty) newEmptyRange(start) + else if (crossesTheEndAfterN(n)) this + else new NumericRange.Inclusive(start, locationAfterN(n - 1), step) + } + + override def drop(n: Int): NumericRange[T] = { + if (n <= 0 || isEmpty) this + else if (crossesTheEndAfterN(n)) newEmptyRange(end) + else copy(locationAfterN(n), end, step) + } + + override def splitAt(n: Int): (NumericRange[T], NumericRange[T]) = (take(n), drop(n)) + + override def reverse: NumericRange[T] = + if (isEmpty) this + else { + val newStep = -step + if (num.sign(newStep) == num.sign(step)) { + throw new ArithmeticException("number type is unsigned, and .reverse requires a negative step") + } else new NumericRange.Inclusive(last, start, newStep) + } + + import NumericRange.defaultOrdering + + override def min[T1 >: T](implicit ord: Ordering[T1]): T = + // We can take the fast path: + // - If the Integral of this NumericRange is also the requested Ordering + // (Integral <: Ordering). This can happen for custom Integral types. + // - The Ordering is the default Ordering of a well-known Integral type. + if ((ord eq num) || defaultOrdering.get(num).exists(ord eq _)) { + if (num.sign(step) > zero) head + else last + } else super.min(ord) + + override def max[T1 >: T](implicit ord: Ordering[T1]): T = + // See comment for fast path in min(). + if ((ord eq num) || defaultOrdering.get(num).exists(ord eq _)) { + if (num.sign(step) > zero) last + else head + } else super.max(ord) + + // a well-typed contains method. + def containsTyped(x: T): Boolean = + isWithinBoundaries(x) && (((x - start) % step) == zero) + + override def contains[A1 >: T](x: A1): Boolean = + try containsTyped(x.asInstanceOf[T]) + catch { case _: ClassCastException => false } + + override def sum[B >: T](implicit num: Numeric[B]): B = { + if (isEmpty) num.zero + else if (size == 1) head + else { + // If there is no overflow, use arithmetic series formula + // a + ... (n terms total) ... + b = n*(a+b)/2 + if ((num eq scala.math.Numeric.IntIsIntegral)|| + (num eq scala.math.Numeric.ShortIsIntegral)|| + (num eq scala.math.Numeric.ByteIsIntegral)|| + (num eq scala.math.Numeric.CharIsIntegral)) { + // We can do math with no overflow in a Long--easy + val exact = (size * ((num toLong head) + (num toInt last))) / 2 + num fromInt exact.toInt + } + else if (num eq scala.math.Numeric.LongIsIntegral) { + // Uh-oh, might be overflow, so we have to divide before we overflow. + // Either numRangeElements or (head + last) must be even, so divide the even one before multiplying + val a = head.toLong + val b = last.toLong + val ans = + if ((size & 1) == 0) (size / 2) * (a + b) + else size * { + // Sum is even, but we might overflow it, so divide in pieces and add back remainder + val ha = a/2 + val hb = b/2 + ha + hb + ((a - 2*ha) + (b - 2*hb)) / 2 + } + ans.asInstanceOf[B] + } + else if ((num eq scala.math.Numeric.BigIntIsIntegral) || + (num eq scala.math.Numeric.BigDecimalAsIfIntegral)) { + // No overflow, so we can use arithmetic series formula directly + // (not going to worry about running out of memory) + val numAsIntegral = num.asInstanceOf[Integral[B]] + import numAsIntegral._ + ((num fromInt size) * (head + last)) / (num fromInt 2) + } + else { + // User provided custom Numeric, so we cannot rely on arithmetic series formula (e.g. won't work on something like Z_6) + if (isEmpty) num.zero + else { + var acc = num.zero + var i = head + var idx = 0 + while(idx < length) { + acc = num.plus(acc, i) + i = i + step + idx = idx + 1 + } + acc + } + } + } + } + + override lazy val hashCode: Int = super.hashCode() + override protected final def applyPreferredMaxLength: Int = Int.MaxValue + + override def equals(other: Any): Boolean = other match { + case x: NumericRange[_] => + (x canEqual this) && (length == x.length) && ( + (isEmpty) || // all empty sequences are equal + (start == x.start && last == x.last) // same length and same endpoints implies equality + ) + case _ => + super.equals(other) + } + + override def toString: String = { + val empty = if (isEmpty) "empty " else "" + val preposition = if (isInclusive) "to" else "until" + val stepped = if (step == 1) "" else s" by $step" + s"${empty}NumericRange $start $preposition $end$stepped" + } + + override protected[this] def className = "NumericRange" +} + +/** A companion object for numeric ranges. + * @define Coll `NumericRange` + * @define coll numeric range + */ +object NumericRange { + private def bigDecimalCheckUnderflow[T](start: T, end: T, step: T)(implicit num: Integral[T]): Unit = { + def FAIL(boundary: T, step: T): Unit = { + val msg = boundary match { + case bd: BigDecimal => s"Precision ${bd.mc.getPrecision}" + case _ => "Precision" + } + throw new IllegalArgumentException( + s"$msg inadequate to represent steps of size $step near $boundary" + ) + } + if (num.minus(num.plus(start, step), start) != step) FAIL(start, step) + if (num.minus(end, num.minus(end, step)) != step) FAIL(end, step) + } + + /** Calculates the number of elements in a range given start, end, step, and + * whether or not it is inclusive. Throws an exception if step == 0 or + * the number of elements exceeds the maximum Int. + */ + def count[T](start: T, end: T, step: T, isInclusive: Boolean)(implicit num: Integral[T]): Int = { + val zero = num.zero + val upward = num.lt(start, end) + val posStep = num.gt(step, zero) + + if (step == zero) throw new IllegalArgumentException("step cannot be 0.") + else if (start == end) if (isInclusive) 1 else 0 + else if (upward != posStep) 0 + else { + /* We have to be frightfully paranoid about running out of range. + * We also can't assume that the numbers will fit in a Long. + * We will assume that if a > 0, -a can be represented, and if + * a < 0, -a+1 can be represented. We also assume that if we + * can't fit in Int, we can represent 2*Int.MaxValue+3 (at least). + * And we assume that numbers wrap rather than cap when they overflow. + */ + // Check whether we can short-circuit by deferring to Int range. + val startint = num.toInt(start) + if (start == num.fromInt(startint)) { + val endint = num.toInt(end) + if (end == num.fromInt(endint)) { + val stepint = num.toInt(step) + if (step == num.fromInt(stepint)) { + return { + if (isInclusive) Range.inclusive(startint, endint, stepint).length + else Range (startint, endint, stepint).length + } + } + } + } + // If we reach this point, deferring to Int failed. + // Numbers may be big. + if (num.isInstanceOf[Numeric.BigDecimalAsIfIntegral]) { + bigDecimalCheckUnderflow(start, end, step) // Throw exception if math is inaccurate (including no progress at all) + } + val one = num.one + val limit = num.fromInt(Int.MaxValue) + def check(t: T): T = + if (num.gt(t, limit)) throw new IllegalArgumentException("More than Int.MaxValue elements.") + else t + // If the range crosses zero, it might overflow when subtracted + val startside = num.sign(start) + val endside = num.sign(end) + num.toInt{ + if (num.gteq(num.times(startside, endside), zero)) { + // We're sure we can subtract these numbers. + // Note that we do not use .rem because of different conventions for Long and BigInt + val diff = num.minus(end, start) + val quotient = check(num.quot(diff, step)) + val remainder = num.minus(diff, num.times(quotient, step)) + if (!isInclusive && zero == remainder) quotient else check(num.plus(quotient, one)) + } + else { + // We might not even be able to subtract these numbers. + // Jump in three pieces: + // * start to -1 or 1, whichever is closer (waypointA) + // * one step, which will take us at least to 0 (ends at waypointB) + // * (except with really small numbers) + // * there to the end + val negone = num.fromInt(-1) + val startlim = if (posStep) negone else one + //Use start value if the start value is closer to zero than startlim + // * e.g. .5 is closer to zero than 1 and -.5 is closer to zero than -1 + val startdiff = { + if ((posStep && num.lt(startlim, start)) || (!posStep && num.gt(startlim, start))) + start + else + num.minus(startlim, start) + } + val startq = check(num.quot(startdiff, step)) + val waypointA = if (startq == zero) start else num.plus(start, num.times(startq, step)) + val waypointB = num.plus(waypointA, step) + check { + if (num.lt(waypointB, end) != upward) { + // No last piece + if (isInclusive && waypointB == end) num.plus(startq, num.fromInt(2)) + else num.plus(startq, one) + } + else { + // There is a last piece + val enddiff = num.minus(end,waypointB) + val endq = check(num.quot(enddiff, step)) + val last = if (endq == zero) waypointB else num.plus(waypointB, num.times(endq, step)) + // Now we have to tally up all the pieces + // 1 for the initial value + // startq steps to waypointA + // 1 step to waypointB + // endq steps to the end (one less if !isInclusive and last==end) + num.plus(startq, num.plus(endq, if (!isInclusive && last==end) one else num.fromInt(2))) + } + } + } + } + } + } + + @SerialVersionUID(3L) + class Inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) + extends NumericRange(start, end, step, isInclusive = true) { + override def copy(start: T, end: T, step: T): Inclusive[T] = + NumericRange.inclusive(start, end, step) + + def exclusive: Exclusive[T] = NumericRange(start, end, step) + } + + @SerialVersionUID(3L) + class Exclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) + extends NumericRange(start, end, step, isInclusive = false) { + override def copy(start: T, end: T, step: T): Exclusive[T] = + NumericRange(start, end, step) + + def inclusive: Inclusive[T] = NumericRange.inclusive(start, end, step) + } + + def apply[T](start: T, end: T, step: T)(implicit num: Integral[T]): Exclusive[T] = + new Exclusive(start, end, step) + def inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]): Inclusive[T] = + new Inclusive(start, end, step) + + private[collection] val defaultOrdering = Map[Numeric[_], Ordering[_]]( + Numeric.BigIntIsIntegral -> Ordering.BigInt, + Numeric.IntIsIntegral -> Ordering.Int, + Numeric.ShortIsIntegral -> Ordering.Short, + Numeric.ByteIsIntegral -> Ordering.Byte, + Numeric.CharIsIntegral -> Ordering.Char, + Numeric.LongIsIntegral -> Ordering.Long, + Numeric.BigDecimalAsIfIntegral -> Ordering.BigDecimal + ) + + @SerialVersionUID(3L) + private final class NumericRangeIterator[T](self: NumericRange[T], num: Integral[T]) extends AbstractIterator[T] with Serializable { + import num.mkNumericOps + + private[this] var _hasNext = !self.isEmpty + private[this] var _next: T = self.start + private[this] val lastElement: T = if (_hasNext) self.last else self.start + override def knownSize: Int = if (_hasNext) num.toInt((lastElement - _next) / self.step) + 1 else 0 + def hasNext: Boolean = _hasNext + def next(): T = { + if (!_hasNext) Iterator.empty.next() + val value = _next + _hasNext = value != lastElement + _next = num.plus(value, self.step) + value + } + } +} diff --git a/library/src/scala/collection/immutable/Queue.scala b/library/src/scala/collection/immutable/Queue.scala new file mode 100644 index 000000000000..89def7096aea --- /dev/null +++ b/library/src/scala/collection/immutable/Queue.scala @@ -0,0 +1,217 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package immutable + +import scala.collection.generic.DefaultSerializable +import scala.collection.mutable.{Builder, ListBuffer} + +/** `Queue` objects implement data structures that allow to + * insert and retrieve elements in a first-in-first-out (FIFO) manner. + * + * `Queue` is implemented as a pair of `List`s, one containing the ''in'' elements and the other the ''out'' elements. + * Elements are added to the ''in'' list and removed from the ''out'' list. When the ''out'' list runs dry, the + * queue is pivoted by replacing the ''out'' list by ''in.reverse'', and ''in'' by ''Nil''. + * + * Adding items to the queue always has cost `O(1)`. Removing items has cost `O(1)`, except in the case + * where a pivot is required, in which case, a cost of `O(n)` is incurred, where `n` is the number of elements in the queue. When this happens, + * `n` remove operations with `O(1)` cost are guaranteed. Removing an item is on average `O(1)`. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#immutable-queues "Scala's Collection Library overview"]] + * section on `Immutable Queues` for more information. + * + * @define Coll `immutable.Queue` + * @define coll immutable queue + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ + +sealed class Queue[+A] protected(protected val in: List[A], protected val out: List[A]) + extends AbstractSeq[A] + with LinearSeq[A] + with LinearSeqOps[A, Queue, Queue[A]] + with StrictOptimizedLinearSeqOps[A, Queue, Queue[A]] + with StrictOptimizedSeqOps[A, Queue, Queue[A]] + with IterableFactoryDefaults[A, Queue] + with DefaultSerializable { + + override def iterableFactory: SeqFactory[Queue] = Queue + + /** Returns the `n`-th element of this queue. + * The first element is at position `0`. + * + * @param n index of the element to return + * @return the element at position `n` in this queue. + * @throws NoSuchElementException if the queue is too short. + */ + override def apply(n: Int): A = { + def indexOutOfRange(): Nothing = throw new IndexOutOfBoundsException(n.toString) + + var index = 0 + var curr = out + + while (index < n && curr.nonEmpty) { + index += 1 + curr = curr.tail + } + + if (index == n) { + if (curr.nonEmpty) curr.head + else if (in.nonEmpty) in.last + else indexOutOfRange() + } else { + val indexFromBack = n - index + val inLength = in.length + if (indexFromBack >= inLength) indexOutOfRange() + else in(inLength - indexFromBack - 1) + } + } + + /** Returns the elements in the list as an iterator + */ + override def iterator: Iterator[A] = out.iterator.concat(in.reverse) + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + override def isEmpty: Boolean = in.isEmpty && out.isEmpty + + override def head: A = + if (out.nonEmpty) out.head + else if (in.nonEmpty) in.last + else throw new NoSuchElementException("head on empty queue") + + override def tail: Queue[A] = + if (out.nonEmpty) new Queue(in, out.tail) + else if (in.nonEmpty) new Queue(Nil, in.reverse.tail) + else throw new NoSuchElementException("tail on empty queue") + + override def last: A = + if (in.nonEmpty) in.head + else if (out.nonEmpty) out.last + else throw new NoSuchElementException("last on empty queue") + + /* This is made to avoid inefficient implementation of iterator. */ + override def forall(p: A => Boolean): Boolean = + in.forall(p) && out.forall(p) + + /* This is made to avoid inefficient implementation of iterator. */ + override def exists(p: A => Boolean): Boolean = + in.exists(p) || out.exists(p) + + override protected[this] def className = "Queue" + + /** Returns the length of the queue. */ + override def length: Int = in.length + out.length + + override def prepended[B >: A](elem: B): Queue[B] = new Queue(in, elem :: out) + + override def appended[B >: A](elem: B): Queue[B] = enqueue(elem) + + override def appendedAll[B >: A](that: scala.collection.IterableOnce[B]): Queue[B] = { + val newIn = that match { + case that: Queue[B] => that.in ++ (that.out reverse_::: this.in) + case that: List[B] => that reverse_::: this.in + case _ => + var result: List[B] = this.in + val iter = that.iterator + while (iter.hasNext) { + result = iter.next() :: result + } + result + } + if (newIn eq this.in) this else new Queue[B](newIn, this.out) + } + + /** Creates a new queue with element added at the end + * of the old queue. + * + * @param elem the element to insert + */ + def enqueue[B >: A](elem: B): Queue[B] = new Queue(elem :: in, out) + + /** Creates a new queue with all elements provided by an `Iterable` object + * added at the end of the old queue. + * + * The elements are appended in the order they are given out by the + * iterator. + * + * @param iter an iterable object + */ + @deprecated("Use `enqueueAll` instead of `enqueue` to enqueue a collection of elements", "2.13.0") + @`inline` final def enqueue[B >: A](iter: scala.collection.Iterable[B]) = enqueueAll(iter) + + /** Creates a new queue with all elements provided by an `Iterable` object + * added at the end of the old queue. + * + * The elements are appended in the order they are given out by the + * iterator. + * + * @param iter an iterable object + */ + def enqueueAll[B >: A](iter: scala.collection.Iterable[B]): Queue[B] = appendedAll(iter) + + /** Returns a tuple with the first element in the queue, + * and a new queue with this element removed. + * + * @return the first element of the queue. + * @throws NoSuchElementException if the queue is empty + */ + def dequeue: (A, Queue[A]) = out match { + case Nil if !in.isEmpty => val rev = in.reverse ; (rev.head, new Queue(Nil, rev.tail)) + case x :: xs => (x, new Queue(in, xs)) + case _ => throw new NoSuchElementException("dequeue on empty queue") + } + + /** Optionally retrieves the first element and a queue of the remaining elements. + * + * @return A tuple of the first element of the queue, and a new queue with this element removed. + * If the queue is empty, `None` is returned. + */ + def dequeueOption: Option[(A, Queue[A])] = if(isEmpty) None else Some(dequeue) + + /** Returns the first element in the queue, or throws an error if there + * is no element contained in the queue. + * + * @throws NoSuchElementException if the queue is empty + * @return the first element. + */ + def front: A = head + + /** Returns a string representation of this queue. + */ + override def toString(): String = mkString("Queue(", ", ", ")") +} + +/** $factoryInfo + * @define Coll `immutable.Queue` + * @define coll immutable queue + */ +@SerialVersionUID(3L) +object Queue extends StrictOptimizedSeqFactory[Queue] { + def newBuilder[A]: Builder[A, Queue[A]] = new ListBuffer[A] mapResult (x => new Queue[A](Nil, x)) + + def from[A](source: IterableOnce[A]): Queue[A] = source match { + case q: Queue[A] => q + case _ => + val list = List.from(source) + if (list.isEmpty) empty + else new Queue(Nil, list) + } + + def empty[A]: Queue[A] = EmptyQueue + override def apply[A](xs: A*): Queue[A] = new Queue[A](Nil, xs.toList) + + private object EmptyQueue extends Queue[Nothing](Nil, Nil) { } +} diff --git a/library/src/scala/collection/immutable/Range.scala b/library/src/scala/collection/immutable/Range.scala new file mode 100644 index 000000000000..92df921605f6 --- /dev/null +++ b/library/src/scala/collection/immutable/Range.scala @@ -0,0 +1,674 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.immutable + +import scala.collection.Stepper.EfficientSplit +import scala.collection.convert.impl.RangeStepper +import scala.collection.generic.CommonErrors +import scala.collection.{AbstractIterator, AnyStepper, IterableFactoryDefaults, Iterator, Stepper, StepperShape} +import scala.util.hashing.MurmurHash3 + +/** The `Range` class represents integer values in range + * ''[start;end)'' with non-zero step value `step`. + * It's a special case of an indexed sequence. + * For example: + * + * {{{ + * val r1 = 0 until 10 + * val r2 = r1.start until r1.end by r1.step + 1 + * println(r2.length) // = 5 + * }}} + * + * Ranges that contain more than `Int.MaxValue` elements can be created, but + * these overfull ranges have only limited capabilities. Any method that + * could require a collection of over `Int.MaxValue` length to be created, or + * could be asked to index beyond `Int.MaxValue` elements will throw an + * exception. Overfull ranges can safely be reduced in size by changing + * the step size (e.g. `by 3`) or taking/dropping elements. `contains`, + * `equals`, and access to the ends of the range (`head`, `last`, `tail`, + * `init`) are also permitted on overfull ranges. + * + * @param start the start of this range. + * @param end the end of the range. For exclusive ranges, e.g. + * `Range(0,3)` or `(0 until 3)`, this is one + * step past the last one in the range. For inclusive + * ranges, e.g. `Range.inclusive(0,3)` or `(0 to 3)`, + * it may be in the range if it is not skipped by the step size. + * To find the last element inside a non-empty range, + * use `last` instead. + * @param step the step for the range. + * + * @define coll range + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define doesNotUseBuilders + * '''Note:''' this method does not use builders to construct a new range, + * and its complexity is O(1). + */ +@SerialVersionUID(3L) +sealed abstract class Range( + val start: Int, + val end: Int, + val step: Int +) + extends AbstractSeq[Int] + with IndexedSeq[Int] + with IndexedSeqOps[Int, IndexedSeq, IndexedSeq[Int]] + with StrictOptimizedSeqOps[Int, IndexedSeq, IndexedSeq[Int]] + with IterableFactoryDefaults[Int, IndexedSeq] + with Serializable { range => + + final override def iterator: Iterator[Int] = new RangeIterator(start, step, lastElement, isEmpty) + + override final def stepper[S <: Stepper[_]](implicit shape: StepperShape[Int, S]): S with EfficientSplit = { + val st = new RangeStepper(start, step, 0, length) + val r = + if (shape.shape == StepperShape.IntShape) st + else { + assert(shape.shape == StepperShape.ReferenceShape, s"unexpected StepperShape: $shape") + AnyStepper.ofParIntStepper(st) + } + r.asInstanceOf[S with EfficientSplit] + } + + private[this] def gap = end.toLong - start.toLong + private[this] def isExact = gap % step == 0 + private[this] def hasStub = isInclusive || !isExact + private[this] def longLength = gap / step + ( if (hasStub) 1 else 0 ) + + def isInclusive: Boolean + + final override val isEmpty: Boolean = ( + (start > end && step > 0) + || (start < end && step < 0) + || (start == end && !isInclusive) + ) + + private[this] val numRangeElements: Int = { + if (step == 0) throw new IllegalArgumentException("step cannot be 0.") + else if (isEmpty) 0 + else { + val len = longLength + if (len > scala.Int.MaxValue) -1 + else len.toInt + } + } + + final def length = if (numRangeElements < 0) fail() else numRangeElements + + // This field has a sensible value only for non-empty ranges + private[this] val lastElement = step match { + case 1 => if (isInclusive) end else end-1 + case -1 => if (isInclusive) end else end+1 + case _ => + val remainder = (gap % step).toInt + if (remainder != 0) end - remainder + else if (isInclusive) end + else end - step + } + + /** The last element of this range. This method will return the correct value + * even if there are too many elements to iterate over. + */ + final override def last: Int = + if (isEmpty) throw Range.emptyRangeError("last") else lastElement + final override def head: Int = + if (isEmpty) throw Range.emptyRangeError("head") else start + + /** Creates a new range containing all the elements of this range except the last one. + * + * $doesNotUseBuilders + * + * @return a new range consisting of all the elements of this range except the last one. + */ + final override def init: Range = + if (isEmpty) throw Range.emptyRangeError("init") else dropRight(1) + + /** Creates a new range containing all the elements of this range except the first one. + * + * $doesNotUseBuilders + * + * @return a new range consisting of all the elements of this range except the first one. + */ + final override def tail: Range = { + if (isEmpty) throw Range.emptyRangeError("tail") + if (numRangeElements == 1) newEmptyRange(end) + else if(isInclusive) new Range.Inclusive(start + step, end, step) + else new Range.Exclusive(start + step, end, step) + } + + override def map[B](f: Int => B): IndexedSeq[B] = { + validateMaxLength() + super.map(f) + } + + final protected def copy(start: Int = start, end: Int = end, step: Int = step, isInclusive: Boolean = isInclusive): Range = + if(isInclusive) new Range.Inclusive(start, end, step) else new Range.Exclusive(start, end, step) + + /** Create a new range with the `start` and `end` values of this range and + * a new `step`. + * + * @return a new range with a different step + */ + final def by(step: Int): Range = copy(start, end, step) + + // Check cannot be evaluated eagerly because we have a pattern where + // ranges are constructed like: "x to y by z" The "x to y" piece + // should not trigger an exception. So the calculation is delayed, + // which means it will not fail fast for those cases where failing was + // correct. + private[this] def validateMaxLength(): Unit = { + if (numRangeElements < 0) + fail() + } + private[this] def description = "%d %s %d by %s".format(start, if (isInclusive) "to" else "until", end, step) + private[this] def fail() = throw new IllegalArgumentException(description + ": seqs cannot contain more than Int.MaxValue elements.") + + @throws[IndexOutOfBoundsException] + final def apply(idx: Int): Int = { + validateMaxLength() + if (idx < 0 || idx >= numRangeElements) + throw CommonErrors.indexOutOfBounds(index = idx, max = numRangeElements - 1) + else start + (step * idx) + } + + /*@`inline`*/ final override def foreach[@specialized(Unit) U](f: Int => U): Unit = { + // Implementation chosen on the basis of favorable microbenchmarks + // Note--initialization catches step == 0 so we don't need to here + if (!isEmpty) { + var i = start + while (true) { + f(i) + if (i == lastElement) return + i += step + } + } + } + + override final def indexOf[@specialized(Int) B >: Int](elem: B, from: Int = 0): Int = + elem match { + case i: Int => + val pos = posOf(i) + if (pos >= from) pos else -1 + case _ => super.indexOf(elem, from) + } + + override final def lastIndexOf[@specialized(Int) B >: Int](elem: B, end: Int = length - 1): Int = + elem match { + case i: Int => + val pos = posOf(i) + if (pos <= end) pos else -1 + case _ => super.lastIndexOf(elem, end) + } + + private[this] def posOf(i: Int): Int = + if (contains(i)) (i - start) / step else -1 + + override def sameElements[B >: Int](that: IterableOnce[B]): Boolean = that match { + case other: Range => + (this.length : @annotation.switch) match { + case 0 => other.isEmpty + case 1 => other.length == 1 && this.start == other.start + case n => other.length == n && ( + (this.start == other.start) + && (this.step == other.step) + ) + } + case _ => super.sameElements(that) + } + + /** Creates a new range containing the first `n` elements of this range. + * + * @param n the number of elements to take. + * @return a new range consisting of `n` first elements. + */ + final override def take(n: Int): Range = + if (n <= 0 || isEmpty) newEmptyRange(start) + else if (n >= numRangeElements && numRangeElements >= 0) this + else { + // May have more than Int.MaxValue elements in range (numRangeElements < 0) + // but the logic is the same either way: take the first n + new Range.Inclusive(start, locationAfterN(n - 1), step) + } + + /** Creates a new range containing all the elements of this range except the first `n` elements. + * + * @param n the number of elements to drop. + * @return a new range consisting of all the elements of this range except `n` first elements. + */ + final override def drop(n: Int): Range = + if (n <= 0 || isEmpty) this + else if (n >= numRangeElements && numRangeElements >= 0) newEmptyRange(end) + else { + // May have more than Int.MaxValue elements (numRangeElements < 0) + // but the logic is the same either way: go forwards n steps, keep the rest + copy(locationAfterN(n), end, step) + } + + /** Creates a new range consisting of the last `n` elements of the range. + * + * $doesNotUseBuilders + */ + final override def takeRight(n: Int): Range = { + if (n <= 0) newEmptyRange(start) + else if (numRangeElements >= 0) drop(numRangeElements - n) + else { + // Need to handle over-full range separately + val y = last + val x = y - step.toLong*(n-1) + if ((step > 0 && x < start) || (step < 0 && x > start)) this + else Range.inclusive(x.toInt, y, step) + } + } + + /** Creates a new range consisting of the initial `length - n` elements of the range. + * + * $doesNotUseBuilders + */ + final override def dropRight(n: Int): Range = { + if (n <= 0) this + else if (numRangeElements >= 0) take(numRangeElements - n) + else { + // Need to handle over-full range separately + val y = last - step.toInt*n + if ((step > 0 && y < start) || (step < 0 && y > start)) newEmptyRange(start) + else Range.inclusive(start, y.toInt, step) + } + } + + // Advance from the start while we meet the given test + private[this] def argTakeWhile(p: Int => Boolean): Long = { + if (isEmpty) start + else { + var current = start + val stop = last + while (current != stop && p(current)) current += step + if (current != stop || !p(current)) current + else current.toLong + step + } + } + + final override def takeWhile(p: Int => Boolean): Range = { + val stop = argTakeWhile(p) + if (stop==start) newEmptyRange(start) + else { + val x = (stop - step).toInt + if (x == last) this + else Range.inclusive(start, x, step) + } + } + + final override def dropWhile(p: Int => Boolean): Range = { + val stop = argTakeWhile(p) + if (stop == start) this + else { + val x = (stop - step).toInt + if (x == last) newEmptyRange(last) + else Range.inclusive(x + step, last, step) + } + } + + final override def span(p: Int => Boolean): (Range, Range) = { + val border = argTakeWhile(p) + if (border == start) (newEmptyRange(start), this) + else { + val x = (border - step).toInt + if (x == last) (this, newEmptyRange(last)) + else (Range.inclusive(start, x, step), Range.inclusive(x+step, last, step)) + } + } + + /** Creates a new range containing the elements starting at `from` up to but not including `until`. + * + * $doesNotUseBuilders + * + * @param from the element at which to start + * @param until the element at which to end (not included in the range) + * @return a new range consisting of a contiguous interval of values in the old range + */ + final override def slice(from: Int, until: Int): Range = + if (from <= 0) take(until) + else if (until >= numRangeElements && numRangeElements >= 0) drop(from) + else { + val fromValue = locationAfterN(from) + if (from >= until) newEmptyRange(fromValue) + else Range.inclusive(fromValue, locationAfterN(until-1), step) + } + + // Overridden only to refine the return type + final override def splitAt(n: Int): (Range, Range) = (take(n), drop(n)) + + // Methods like apply throw exceptions on invalid n, but methods like take/drop + // are forgiving: therefore the checks are with the methods. + private[this] def locationAfterN(n: Int) = start + (step * n) + + // When one drops everything. Can't ever have unchecked operations + // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } + // will overflow. This creates an exclusive range where start == end + // based on the given value. + private[this] def newEmptyRange(value: Int) = new Range.Exclusive(value, value, step) + + /** Returns the reverse of this range. + */ + final override def reverse: Range = + if (isEmpty) this + else new Range.Inclusive(last, start, -step) + + /** Make range inclusive. + */ + final def inclusive: Range = + if (isInclusive) this + else new Range.Inclusive(start, end, step) + + final def contains(x: Int): Boolean = { + if (x == end && !isInclusive) false + else if (step > 0) { + if (x < start || x > end) false + else (step == 1) || (Integer.remainderUnsigned(x - start, step) == 0) + } + else { + if (x < end || x > start) false + else (step == -1) || (Integer.remainderUnsigned(start - x, -step) == 0) + } + } + /* Seq#contains has a type parameter so the optimised contains above doesn't override it */ + override final def contains[B >: Int](elem: B): Boolean = elem match { + case i: Int => this.contains(i) + case _ => super.contains(elem) + } + + final override def sum[B >: Int](implicit num: Numeric[B]): Int = { + if (num eq scala.math.Numeric.IntIsIntegral) { + // this is normal integer range with usual addition. arithmetic series formula can be used + if (isEmpty) 0 + else if (size == 1) head + else ((size * (head.toLong + last)) / 2).toInt + } else { + // user provided custom Numeric, we cannot rely on arithmetic series formula + if (isEmpty) num.toInt(num.zero) + else { + var acc = num.zero + var i = head + while (true) { + acc = num.plus(acc, i) + if (i == lastElement) return num.toInt(acc) + i = i + step + } + 0 // Never hit this--just to satisfy compiler since it doesn't know while(true) has type Nothing + } + } + } + + final override def min[A1 >: Int](implicit ord: Ordering[A1]): Int = + if (ord eq Ordering.Int) { + if (step > 0) head + else last + } else if (Ordering.Int isReverseOf ord) { + if (step > 0) last + else head + } else super.min(ord) + + final override def max[A1 >: Int](implicit ord: Ordering[A1]): Int = + if (ord eq Ordering.Int) { + if (step > 0) last + else head + } else if (Ordering.Int isReverseOf ord) { + if (step > 0) head + else last + } else super.max(ord) + + override def tails: Iterator[Range] = + new AbstractIterator[Range] { + private[this] var i = 0 + override def hasNext = i <= Range.this.length + override def next() = { + if (hasNext) { + val res = Range.this.drop(i) + i += 1 + res + } else { + Iterator.empty.next() + } + } + } + + override def inits: Iterator[Range] = + new AbstractIterator[Range] { + private[this] var i = 0 + override def hasNext = i <= Range.this.length + override def next() = { + if (hasNext) { + val res = Range.this.dropRight(i) + i += 1 + res + } else { + Iterator.empty.next() + } + } + } + override protected final def applyPreferredMaxLength: Int = Int.MaxValue + + final override def equals(other: Any): Boolean = other match { + case x: Range => + // Note: this must succeed for overfull ranges (length > Int.MaxValue) + if (isEmpty) x.isEmpty // empty sequences are equal + else // this is non-empty... + x.nonEmpty && start == x.start && { // ...so other must contain something and have same start + val l0 = last + (l0 == x.last && ( // And same end + start == l0 || step == x.step // And either the same step, or not take any steps + )) + } + case _ => + super.equals(other) + } + + final override def hashCode: Int = + if(length >= 2) MurmurHash3.rangeHash(start, step, lastElement) + else super.hashCode + + final override def toString: String = { + val preposition = if (isInclusive) "to" else "until" + val stepped = if (step == 1) "" else s" by $step" + val prefix = if (isEmpty) "empty " else if (!isExact) "inexact " else "" + s"${prefix}Range $start $preposition $end$stepped" + } + + override protected[this] def className = "Range" + + override def distinct: Range = this + + override def grouped(size: Int): Iterator[Range] = { + require(size >= 1, f"size=$size%d, but size must be positive") + if (isEmpty) { + Iterator.empty + } else { + val s = size + new AbstractIterator[Range] { + private[this] var i = 0 + override def hasNext = Range.this.length > i + override def next() = + if (hasNext) { + val x = Range.this.slice(i, i + s) + i += s + x + } else { + Iterator.empty.next() + } + } + } + } + + override def sorted[B >: Int](implicit ord: Ordering[B]): IndexedSeq[Int] = + if (ord eq Ordering.Int) { + if (step > 0) { + this + } else { + reverse + } + } else { + super.sorted(ord) + } +} + +/** + * Companion object for ranges. + * @define Coll `Range` + * @define coll range + */ +object Range { + + /** Counts the number of range elements. + * precondition: step != 0 + * If the size of the range exceeds Int.MaxValue, the + * result will be negative. + */ + def count(start: Int, end: Int, step: Int, isInclusive: Boolean): Int = { + if (step == 0) + throw new IllegalArgumentException("step cannot be 0.") + + val isEmpty = + if (start == end) !isInclusive + else if (start < end) step < 0 + else step > 0 + + if (isEmpty) 0 + else { + // Counts with Longs so we can recognize too-large ranges. + val gap: Long = end.toLong - start.toLong + val jumps: Long = gap / step + // Whether the size of this range is one larger than the + // number of full-sized jumps. + val hasStub = isInclusive || (gap % step != 0) + val result: Long = jumps + ( if (hasStub) 1 else 0 ) + + if (result > scala.Int.MaxValue) -1 + else result.toInt + } + } + def count(start: Int, end: Int, step: Int): Int = + count(start, end, step, isInclusive = false) + + /** Make a range from `start` until `end` (exclusive) with given step value. + * @note step != 0 + */ + def apply(start: Int, end: Int, step: Int): Range.Exclusive = new Range.Exclusive(start, end, step) + + /** Make a range from `start` until `end` (exclusive) with step value 1. + */ + def apply(start: Int, end: Int): Range.Exclusive = new Range.Exclusive(start, end, 1) + + /** Make an inclusive range from `start` to `end` with given step value. + * @note step != 0 + */ + def inclusive(start: Int, end: Int, step: Int): Range.Inclusive = new Range.Inclusive(start, end, step) + + /** Make an inclusive range from `start` to `end` with step value 1. + */ + def inclusive(start: Int, end: Int): Range.Inclusive = new Range.Inclusive(start, end, 1) + + @SerialVersionUID(3L) + final class Inclusive(start: Int, end: Int, step: Int) extends Range(start, end, step) { + def isInclusive: Boolean = true + } + + @SerialVersionUID(3L) + final class Exclusive(start: Int, end: Int, step: Int) extends Range(start, end, step) { + def isInclusive: Boolean = false + } + + // BigInt and Long are straightforward generic ranges. + object BigInt { + def apply(start: BigInt, end: BigInt, step: BigInt): NumericRange.Exclusive[BigInt] = NumericRange(start, end, step) + def inclusive(start: BigInt, end: BigInt, step: BigInt): NumericRange.Inclusive[BigInt] = NumericRange.inclusive(start, end, step) + } + + object Long { + def apply(start: Long, end: Long, step: Long): NumericRange.Exclusive[Long] = NumericRange(start, end, step) + def inclusive(start: Long, end: Long, step: Long): NumericRange.Inclusive[Long] = NumericRange.inclusive(start, end, step) + } + + // BigDecimal uses an alternative implementation of Numeric in which + // it pretends to be Integral[T] instead of Fractional[T]. See Numeric for + // details. The intention is for it to throw an exception anytime + // imprecision or surprises might result from anything, although this may + // not yet be fully implemented. + object BigDecimal { + implicit val bigDecAsIntegral: Numeric.BigDecimalAsIfIntegral = Numeric.BigDecimalAsIfIntegral + + def apply(start: BigDecimal, end: BigDecimal, step: BigDecimal): NumericRange.Exclusive[BigDecimal] = + NumericRange(start, end, step) + def inclusive(start: BigDecimal, end: BigDecimal, step: BigDecimal): NumericRange.Inclusive[BigDecimal] = + NumericRange.inclusive(start, end, step) + } + + // As there is no appealing default step size for not-really-integral ranges, + // we offer a partially constructed object. + class Partial[T, U](private val f: T => U) extends AnyVal { + def by(x: T): U = f(x) + override def toString = "Range requires step" + } + + // Illustrating genericity with Int Range, which should have the same behavior + // as the original Range class. However we leave the original Range + // indefinitely, for performance and because the compiler seems to bootstrap + // off it and won't do so with our parameterized version without modifications. + object Int { + def apply(start: Int, end: Int, step: Int): NumericRange.Exclusive[Int] = NumericRange(start, end, step) + def inclusive(start: Int, end: Int, step: Int): NumericRange.Inclusive[Int] = NumericRange.inclusive(start, end, step) + } + + private def emptyRangeError(what: String): Throwable = + new NoSuchElementException(what + " on empty Range") +} + +/** + * @param lastElement The last element included in the Range + * @param initiallyEmpty Whether the Range was initially empty or not + */ +@SerialVersionUID(3L) +private class RangeIterator( + start: Int, + step: Int, + lastElement: Int, + initiallyEmpty: Boolean +) extends AbstractIterator[Int] with Serializable { + private[this] var _hasNext: Boolean = !initiallyEmpty + private[this] var _next: Int = start + override def knownSize: Int = if (_hasNext) (lastElement - _next) / step + 1 else 0 + def hasNext: Boolean = _hasNext + @throws[NoSuchElementException] + def next(): Int = { + if (!_hasNext) Iterator.empty.next() + val value = _next + _hasNext = value != lastElement + _next = value + step + value + } + + override def drop(n: Int): Iterator[Int] = { + if (n > 0) { + val longPos = _next.toLong + step * n + if (step > 0) { + _next = Math.min(lastElement, longPos).toInt + _hasNext = longPos <= lastElement + } + else if (step < 0) { + _next = Math.max(lastElement, longPos).toInt + _hasNext = longPos >= lastElement + } + } + this + } +} diff --git a/library/src/scala/collection/immutable/RedBlackTree.scala b/library/src/scala/collection/immutable/RedBlackTree.scala new file mode 100644 index 000000000000..d9ac41e1ce5b --- /dev/null +++ b/library/src/scala/collection/immutable/RedBlackTree.scala @@ -0,0 +1,1244 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.meta.{getter, setter} +import scala.annotation.tailrec +import scala.runtime.Statics.releaseFence + +/** An object containing the RedBlack tree implementation used by for `TreeMaps` and `TreeSets`. + * + * Implementation note: since efficiency is important for data structures this implementation + * uses `null` to represent empty trees. This also means pattern matching cannot + * easily be used. The API represented by the RedBlackTree object tries to hide these + * optimizations behind a reasonably clean API. + */ +private[collection] object RedBlackTree { + def validate[A](tree: Tree[A, _])(implicit ordering: Ordering[A]): tree.type = { + def impl(tree: Tree[A, _], keyProp: A => Boolean): Int = { + assert(keyProp(tree.key), s"key check failed: $tree") + if (tree.isRed) { + assert(tree.left == null || tree.left.isBlack, s"red-red left $tree") + assert(tree.right == null || tree.right.isBlack, s"red-red right $tree") + } + val leftBlacks = if (tree.left == null) 0 else impl(tree.left, k => keyProp(k) && ordering.compare(k, tree.key) < 0) + val rightBlacks = if (tree.right == null) 0 else impl(tree.right, k => keyProp(k) && ordering.compare(k, tree.key) > 0) + assert(leftBlacks == rightBlacks, s"not balanced: $tree") + leftBlacks + (if (tree.isBlack) 1 else 0) + } + if (tree != null) impl(tree, _ => true) + tree + } + + def isEmpty(tree: Tree[_, _]): Boolean = tree eq null + + def contains[A: Ordering](tree: Tree[A, _], x: A): Boolean = lookup(tree, x) ne null + def get[A: Ordering, B](tree: Tree[A, B], x: A): Option[B] = lookup(tree, x) match { + case null => None + case found => Some(found.value) + } + + @tailrec + def lookup[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + val cmp = ordering.compare(x, tree.key) + if (cmp < 0) lookup(tree.left, x) + else if (cmp > 0) lookup(tree.right, x) + else tree + } + private[immutable] abstract class Helper[A](implicit val ordering: Ordering[A]) { + def beforePublish[B](tree: Tree[A, B]): Tree[A, B] = { + if (tree eq null) tree + else if (tree.isMutable) { + val res = tree.mutableBlack.makeImmutable + releaseFence() + res + } else tree.black + } + /** Create a new balanced tree where `newLeft` replaces `tree.left`. + * tree and newLeft are never null */ + protected[this] final def mutableBalanceLeft[A1, B, B1 >: B](tree: Tree[A1, B], newLeft: Tree[A1, B1]): Tree[A1, B1] = { + // Parameter trees + // tree | newLeft + // -- KV R | nl.L nl.KV nl.R + // | nl.R.L nl.R.KV nl.R.R + //Note - unlike the immutable trees we can't consider tree.left eq newLeft + //as the balance operations may mutate the same object + //but that check was mostly to avoid the object creation + if (newLeft.isRed) { + val newLeft_left = newLeft.left + val newLeft_right = newLeft.right + if (isRedTree(newLeft_left)) { + // RED + // black(nl.L) nl.KV black + // nl.R KV R + val resultLeft = newLeft_left.mutableBlack + val resultRight = tree.mutableBlackWithLeft(newLeft_right) + + newLeft.mutableWithLeftRight(resultLeft, resultRight) + } else if (isRedTree(newLeft_right)) { + // RED + // black nl.R.KV black + // nl.L nl.KV nl.R.L nl.R.R KV R + + val newLeft_right_right = newLeft_right.right + + val resultLeft = newLeft.mutableBlackWithRight(newLeft_right.left) + val resultRight = tree.mutableBlackWithLeft(newLeft_right_right) + + newLeft_right.mutableWithLeftRight(resultLeft, resultRight) + } else { + // tree + // newLeft KV R + tree.mutableWithLeft(newLeft) + } + } else { + // tree + // newLeft KV R + tree.mutableWithLeft(newLeft) + } + } + /** Create a new balanced tree where `newRight` replaces `tree.right`. + * tree and newRight are never null */ + protected[this] final def mutableBalanceRight[A1, B, B1 >: B](tree: Tree[A1, B], newRight: Tree[A1, B1]): Tree[A1, B1] = { + // Parameter trees + // tree | newRight + // L KV -- | nr.L nr.KV nr.R + // | nr.L.L nr.L.KV nr.L.R + //Note - unlike the immutable trees we can't consider tree.right eq newRight + //as the balance operations may mutate the same object + //but that check was mostly to avoid the object creation + if (newRight.isRed) { + val newRight_left = newRight.left + if (isRedTree(newRight_left)) { + // RED + // black nr.L.KV black + // L KV nr.L.L nr.L.R nr.KV nr.R + + val resultLeft = tree.mutableBlackWithRight(newRight_left.left) + val resultRight = newRight.mutableBlackWithLeft(newRight_left.right) + + newRight_left.mutableWithLeftRight(resultLeft, resultRight) + + } else { + val newRight_right = newRight.right + if (isRedTree(newRight_right)) { + // RED + // black nr.KV black(nr.R) + // L KV nr.L + + val resultLeft = tree.mutableBlackWithRight(newRight_left) + val resultRight = newRight_right.mutableBlack + + newRight.mutableWithLeftRight(resultLeft, resultRight) + } else { + // tree + // L KV newRight + tree.mutableWithRight(newRight) + } + } + } else { + // tree + // L KV newRight + tree.mutableWithRight(newRight) + } + } + } + private[immutable] class SetHelper[A](implicit ordering: Ordering[A]) extends Helper[A] { + protected[this] final def mutableUpd(tree: Tree[A, Any], k: A): Tree[A, Any] = + if (tree eq null) { + mutableRedTree(k, (), null, null) + } else if (k.asInstanceOf[AnyRef] eq tree.key.asInstanceOf[AnyRef]) { + tree + } else { + val cmp = ordering.compare(k, tree.key) + if (cmp < 0) + mutableBalanceLeft(tree, mutableUpd(tree.left, k)) + else if (cmp > 0) + mutableBalanceRight(tree, mutableUpd(tree.right, k)) + else tree + } + } + private[immutable] class MapHelper[A, B](implicit ordering: Ordering[A]) extends Helper[A] { + protected[this] final def mutableUpd[B1 >: B](tree: Tree[A, B], k: A, v: B1): Tree[A, B1] = + if (tree eq null) { + mutableRedTree(k, v, null, null) + } else if (k.asInstanceOf[AnyRef] eq tree.key.asInstanceOf[AnyRef]) { + tree.mutableWithV(v) + } else { + val cmp = ordering.compare(k, tree.key) + if (cmp < 0) + mutableBalanceLeft(tree, mutableUpd(tree.left, k, v)) + else if (cmp > 0) + mutableBalanceRight(tree, mutableUpd(tree.right, k, v)) + else tree.mutableWithV(v) + } + } + + def count(tree: Tree[_, _]) = if (tree eq null) 0 else tree.count + def update[A: Ordering, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean): Tree[A, B1] = blacken(upd(tree, k, v, overwrite)) + def delete[A: Ordering, B](tree: Tree[A, B], k: A): Tree[A, B] = blacken(del(tree, k)) + def rangeImpl[A: Ordering, B](tree: Tree[A, B], from: Option[A], until: Option[A]): Tree[A, B] = (from, until) match { + case (Some(from), Some(until)) => this.range(tree, from, until) + case (Some(from), None) => this.from(tree, from) + case (None, Some(until)) => this.until(tree, until) + case (None, None) => tree + } + def range[A: Ordering, B](tree: Tree[A, B], from: A, until: A): Tree[A, B] = blacken(doRange(tree, from, until)) + def from[A: Ordering, B](tree: Tree[A, B], from: A): Tree[A, B] = blacken(doFrom(tree, from)) + def to[A: Ordering, B](tree: Tree[A, B], to: A): Tree[A, B] = blacken(doTo(tree, to)) + def until[A: Ordering, B](tree: Tree[A, B], key: A): Tree[A, B] = blacken(doUntil(tree, key)) + + def drop[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doDrop(tree, n)) + def take[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = blacken(doTake(tree, n)) + def slice[A: Ordering, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = blacken(doSlice(tree, from, until)) + + def smallest[A, B](tree: Tree[A, B]): Tree[A, B] = { + if (tree eq null) throw new NoSuchElementException("empty tree") + var result = tree + while (result.left ne null) result = result.left + result + } + def greatest[A, B](tree: Tree[A, B]): Tree[A, B] = { + if (tree eq null) throw new NoSuchElementException("empty tree") + var result = tree + while (result.right ne null) result = result.right + result + } + + def tail[A, B](tree: Tree[A, B]): Tree[A, B] = { + def _tail(tree: Tree[A, B]): Tree[A, B] = + if (tree eq null) throw new NoSuchElementException("empty tree") + else { + val tl = tree.left + if (tl eq null) tree.right + else if (tl.isBlack) balLeft(tree, _tail(tl), tree.right) + else tree.redWithLeft(_tail(tree.left)) + } + blacken(_tail(tree)) + } + + def init[A, B](tree: Tree[A, B]): Tree[A, B] = { + def _init(tree: Tree[A, B]): Tree[A, B] = + if (tree eq null) throw new NoSuchElementException("empty tree") + else { + val tr = tree.right + if (tr eq null) tree.left + else if (tr.isBlack) balRight(tree, tree.left, _init(tr)) + else tree.redWithRight(_init(tr)) + } + blacken(_init(tree)) + } + + /** + * Returns the smallest node with a key larger than or equal to `x`. Returns `null` if there is no such node. + */ + def minAfter[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + val cmp = ordering.compare(x, tree.key) + if (cmp == 0) tree + else if (cmp < 0) { + val l = minAfter(tree.left, x) + if (l != null) l else tree + } else minAfter(tree.right, x) + } + + /** + * Returns the largest node with a key smaller than `x`. Returns `null` if there is no such node. + */ + def maxBefore[A, B](tree: Tree[A, B], x: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + val cmp = ordering.compare(x, tree.key) + if (cmp <= 0) maxBefore(tree.left, x) + else { + val r = maxBefore(tree.right, x) + if (r != null) r else tree + } + } + + def foreach[A,B,U](tree:Tree[A,B], f:((A,B)) => U):Unit = if (tree ne null) _foreach(tree,f) + + def keysEqual[A: Ordering, X, Y](a: Tree[A, X], b: Tree[A, Y]): Boolean = { + if (a eq b) true + else if (a eq null) false + else if (b eq null) false + else a.count == b.count && (new EqualsIterator(a)).sameKeys(new EqualsIterator(b)) + } + def valuesEqual[A: Ordering, X, Y](a: Tree[A, X], b: Tree[A, Y]): Boolean = { + if (a eq b) true + else if (a eq null) false + else if (b eq null) false + else a.count == b.count && (new EqualsIterator(a)).sameValues(new EqualsIterator(b)) + } + def entriesEqual[A: Ordering, X, Y](a: Tree[A, X], b: Tree[A, Y]): Boolean = { + if (a eq b) true + else if (a eq null) false + else if (b eq null) false + else a.count == b.count && (new EqualsIterator(a)).sameEntries(new EqualsIterator(b)) + } + + private[this] def _foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U): Unit = { + if (tree.left ne null) _foreach(tree.left, f) + f((tree.key, tree.value)) + if (tree.right ne null) _foreach(tree.right, f) + } + + def foreachKey[A, U](tree:Tree[A,_], f: A => U):Unit = if (tree ne null) _foreachKey(tree,f) + + private[this] def _foreachKey[A, U](tree: Tree[A, _], f: A => U): Unit = { + if (tree.left ne null) _foreachKey(tree.left, f) + f((tree.key)) + if (tree.right ne null) _foreachKey(tree.right, f) + } + + def foreachEntry[A, B, U](tree:Tree[A,B], f: (A, B) => U):Unit = if (tree ne null) _foreachEntry(tree,f) + + private[this] def _foreachEntry[A, B, U](tree: Tree[A, B], f: (A, B) => U): Unit = { + if (tree.left ne null) _foreachEntry(tree.left, f) + f(tree.key, tree.value) + if (tree.right ne null) _foreachEntry(tree.right, f) + } + + def iterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[(A, B)] = new EntriesIterator(tree, start) + def keysIterator[A: Ordering](tree: Tree[A, _], start: Option[A] = None): Iterator[A] = new KeysIterator(tree, start) + def valuesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None): Iterator[B] = new ValuesIterator(tree, start) + + @tailrec + def nth[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = { + val count = this.count(tree.left) + if (n < count) nth(tree.left, n) + else if (n > count) nth(tree.right, n - count - 1) + else tree + } + + def isBlack(tree: Tree[_, _]) = (tree eq null) || tree.isBlack + + @`inline` private[this] def isRedTree(tree: Tree[_, _]) = (tree ne null) && tree.isRed + @`inline` private[this] def isBlackTree(tree: Tree[_, _]) = (tree ne null) && tree.isBlack + + private[this] def blacken[A, B](t: Tree[A, B]): Tree[A, B] = if (t eq null) null else t.black + + // Blacken if the tree is red and has a red child. This is necessary when using methods such as `upd` or `updNth` + // for building subtrees. Use `blacken` instead when building top-level trees. + private[this] def maybeBlacken[A, B](t: Tree[A, B]): Tree[A, B] = + if(isBlack(t)) t else if(isRedTree(t.left) || isRedTree(t.right)) t.black else t + + private[this] def mkTree[A, B](isBlack: Boolean, key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = { + val sizeAndColour = sizeOf(left) + sizeOf(right) + 1 | (if(isBlack) initialBlackCount else initialRedCount) + new Tree(key, value.asInstanceOf[AnyRef], left, right, sizeAndColour) + } + + /** Create a new balanced tree where `newLeft` replaces `tree.left`. */ + private[this] def balanceLeft[A, B1](tree: Tree[A, B1], newLeft: Tree[A, B1]): Tree[A, B1] = { + // Parameter trees + // tree | newLeft + // -- KV R | nl.L nl.KV nl.R + // | nl.R.L nl.R.KV nl.R.R + if (tree.left eq newLeft) tree + else { + if (newLeft.isRed) { + val newLeft_left = newLeft.left + val newLeft_right = newLeft.right + if (isRedTree(newLeft_left)) { + // RED + // black(nl.L) nl.KV black + // nl.R KV R + val resultLeft = newLeft_left.black + val resultRight = tree.blackWithLeft(newLeft_right) + + newLeft.withLeftRight(resultLeft, resultRight) + } else if (isRedTree(newLeft_right)) { + // RED + // black nl.R.KV black + // nl.L nl.KV nl.R.L nl.R.R KV R + val newLeft_right_right = newLeft_right.right + + val resultLeft = newLeft.blackWithRight(newLeft_right.left) + val resultRight = tree.blackWithLeft(newLeft_right_right) + + newLeft_right.withLeftRight(resultLeft, resultRight) + } else { + // tree + // newLeft KV R + tree.withLeft(newLeft) + } + } else { + // tree + // newLeft KV R + tree.withLeft(newLeft) + } + } + } + /** Create a new balanced tree where `newRight` replaces `tree.right`. */ + private[this] def balanceRight[A, B1](tree: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + // Parameter trees + // tree | newRight + // L KV -- | nr.L nr.KV nr.R + // | nr.L.L nr.L.KV nr.L.R + if (tree.right eq newRight) tree + else { + if (newRight.isRed) { + val newRight_left = newRight.left + if (isRedTree(newRight_left)) { + // RED + // black nr.L.KV black + // L KV nr.L.L nr.L.R nr.KV nr.R + val resultLeft = tree.blackWithRight(newRight_left.left) + val resultRight = newRight.blackWithLeft(newRight_left.right) + + newRight_left.withLeftRight(resultLeft, resultRight) + } else { + val newRight_right = newRight.right + if (isRedTree(newRight_right)) { + // RED + // black nr.KV black(nr.R) + // L KV nr.L + val resultLeft = tree.blackWithRight(newRight_left) + val resultRight = newRight_right.black + + newRight.withLeftRight(resultLeft, resultRight) + } else { + // tree + // L KV newRight + tree.withRight(newRight) + } + } + } else { + // tree + // L KV newRight + tree.withRight(newRight) + } + } + } + + private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { + RedTree(k, v, null, null) + } else if (k.asInstanceOf[AnyRef] eq tree.key.asInstanceOf[AnyRef]) { + if (overwrite) + tree.withV(v) + else tree + } else { + val cmp = ordering.compare(k, tree.key) + if (cmp < 0) + balanceLeft(tree, upd(tree.left, k, v, overwrite)) + else if (cmp > 0) + balanceRight(tree, upd(tree.right, k, v, overwrite)) + else if (overwrite && (v.asInstanceOf[AnyRef] ne tree.value.asInstanceOf[AnyRef])) + tree.withV(v) + else tree + } + private[this] def updNth[A, B, B1 >: B](tree: Tree[A, B], idx: Int, k: A, v: B1): Tree[A, B1] = if (tree eq null) { + RedTree(k, v, null, null) + } else { + val rank = count(tree.left) + 1 + if (idx < rank) + balanceLeft(tree, updNth(tree.left, idx, k, v)) + else if (idx > rank) + balanceRight(tree, updNth(tree.right, idx - rank, k, v)) + else tree + } + + private[this] def doFrom[A, B](tree: Tree[A, B], from: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + if (tree eq null) return null + if (ordering.lt(tree.key, from)) return doFrom(tree.right, from) + val newLeft = doFrom(tree.left, from) + if (newLeft eq tree.left) tree + else if (newLeft eq null) maybeBlacken(upd(tree.right, tree.key, tree.value, overwrite = false)) + else join(newLeft, tree.key, tree.value, tree.right) + } + private[this] def doTo[A, B](tree: Tree[A, B], to: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + if (tree eq null) return null + if (ordering.lt(to, tree.key)) return doTo(tree.left, to) + val newRight = doTo(tree.right, to) + if (newRight eq tree.right) tree + else if (newRight eq null) maybeBlacken(upd(tree.left, tree.key, tree.value, overwrite = false)) + else join(tree.left, tree.key, tree.value, newRight) + } + private[this] def doUntil[A, B](tree: Tree[A, B], until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + if (tree eq null) return null + if (ordering.lteq(until, tree.key)) return doUntil(tree.left, until) + val newRight = doUntil(tree.right, until) + if (newRight eq tree.right) tree + else if (newRight eq null) maybeBlacken(upd(tree.left, tree.key, tree.value, overwrite = false)) + else join(tree.left, tree.key, tree.value, newRight) + } + + private[this] def doRange[A, B](tree: Tree[A, B], from: A, until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { + if (tree eq null) return null + if (ordering.lt(tree.key, from)) return doRange(tree.right, from, until) + if (ordering.lteq(until, tree.key)) return doRange(tree.left, from, until) + val newLeft = doFrom(tree.left, from) + val newRight = doUntil(tree.right, until) + if ((newLeft eq tree.left) && (newRight eq tree.right)) tree + else if (newLeft eq null) upd(newRight, tree.key, tree.value, overwrite = false) + else if (newRight eq null) upd(newLeft, tree.key, tree.value, overwrite = false) + else join(newLeft, tree.key, tree.value, newRight) + } + + private[this] def doDrop[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = + if((tree eq null) || (n <= 0)) tree + else if(n >= tree.count) null + else { + val l = count(tree.left) + if(n > l) doDrop(tree.right, n-l-1) + else if(n == l) join(null, tree.key, tree.value, tree.right) + else join(doDrop(tree.left, n), tree.key, tree.value, tree.right) + } + + private[this] def doTake[A, B](tree: Tree[A, B], n: Int): Tree[A, B] = + if((tree eq null) || (n <= 0)) null + else if(n >= tree.count) tree + else { + val l = count(tree.left) + if(n <= l) doTake(tree.left, n) + else if(n == l+1) maybeBlacken(updNth(tree.left, n, tree.key, tree.value)) + else join(tree.left, tree.key, tree.value, doTake(tree.right, n-l-1)) + } + + private[this] def doSlice[A, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = + if((tree eq null) || (from >= until) || (from >= tree.count) || (until <= 0)) null + else if((from <= 0) && (until >= tree.count)) tree + else { + val l = count(tree.left) + if(until <= l) doSlice(tree.left, from, until) + else if(from > l) doSlice(tree.right, from-l-1, until-l-1) + else join(doDrop(tree.left, from), tree.key, tree.value, doTake(tree.right, until-l-1)) + } + + /* + * Forcing direct fields access using the @`inline` annotation helps speed up + * various operations (especially smallest/greatest and update/delete). + * + * Unfortunately the direct field access is not guaranteed to work (but + * works on the current implementation of the Scala compiler). + * + * An alternative is to implement the these classes using plain old Java code... + * + * Mutability + * This implementation encodes both mutable and immutable trees. + * Mutable trees are never exposed to the user code but we get significant reductions in both CPU and allocations + * by maintaining a mutable tree during internal operations, e.g. a builder building a Tree, and the other bulk + * API such as filter or ++ + * + * Mutable trees are only used within the confines of this bulk operation and not shared + * Mutable trees may transition to become immutable by calling beforePublish + * Mutable trees may have child nodes (left and right) which are immutable Trees (this promotes structural sharing) + * + * Immutable trees may only child nodes (left and right) which are immutable Trees, and as such the immutable + * trees the entire transitive subtree is immutable + * + * Colour, mutablity and size encoding + * The colour of the Tree, its mutablity and size are all encoded in the _count field + * The colour is encoded in the top bit (31) of the _count. This allows a mutable tree to change colour without + * additional allocation + * The mutable trees always have bits 0 .. 30 (inclusive) set to 0 + * The immutable trees always have bits 0 .. 30 containing the size of the transitive subtree + * + * Naming + * All of the methods that can yield a mutable result have "mutable" on their name, and generally there + * is another method similarly named with doesn't. This is to aid safety and to reduce the cognitive load when + * reviewing changes. e.g. + * def upd(...) will update an immutable Tree, producing an immutable Tree + * def mutableUpd(...) will update a mutable or immutable Tree and may return a mutable or immutable Tree + * a method that has mutable in its name may return a immutable tree if the operation can reuse the existing tree + * + */ + private[immutable] final class Tree[A, +B]( + @(`inline` @getter @setter) private var _key: A, + @(`inline` @getter @setter) private var _value: AnyRef, + @(`inline` @getter @setter) private var _left: Tree[A, _], + @(`inline` @getter @setter) private var _right: Tree[A, _], + @(`inline` @getter @setter) private var _count: Int) + { + @`inline` private[RedBlackTree] def isMutable: Boolean = (_count & colourMask) == 0 + // read only APIs + @`inline` private[RedBlackTree] final def count = { + //devTimeAssert((_count & 0x7FFFFFFF) != 0) + _count & colourMask + } + //retain the colour, and mark as mutable + @`inline` private def mutableRetainingColour = _count & colourBit + + //inlined here to avoid outer object null checks + @`inline` private[RedBlackTree] final def sizeOf(tree:Tree[_,_]) = if (tree eq null) 0 else tree.count + @`inline` private[immutable] final def key = _key + @`inline` private[immutable] final def value = _value.asInstanceOf[B] + @`inline` private[immutable] final def left = _left.asInstanceOf[Tree[A, B]] + @`inline` private[immutable] final def right = _right.asInstanceOf[Tree[A, B]] + //Note - only used in tests outside RedBlackTree + @`inline` private[immutable] final def isBlack = _count < 0 + //Note - only used in tests outside RedBlackTree + @`inline` private[immutable] final def isRed = _count >= 0 + + override def toString: String = s"${if(isRed) "RedTree" else "BlackTree"}($key, $value, $left, $right)" + + //mutable APIs + private[RedBlackTree] def makeImmutable: this.type = { + def makeImmutableImpl(): Unit = { + if (isMutable) { + var size = 1 + if (_left ne null) { + _left.makeImmutable + size += _left.count + } + if (_right ne null) { + _right.makeImmutable + size += _right.count + } + _count |= size //retains colour + } + } + makeImmutableImpl() + this + } + + private[RedBlackTree] def mutableBlack: Tree[A, B] = { + if (isBlack) this + else if (isMutable) { + _count = initialBlackCount + this + } + else new Tree(_key, _value, _left, _right, initialBlackCount) + } +// private[RedBlackTree] def mutableRed: Tree[A, B] = { +// if (isRed) this +// else if (mutable) { +// _count = initialRedCount +// this +// } +// else new Tree(_key, _value, _left, _right, initialRedCount) +// } + + private[RedBlackTree] def mutableWithV[B1 >: B](newValue: B1): Tree[A, B1] = { + if (newValue.asInstanceOf[AnyRef] eq _value.asInstanceOf[AnyRef]) this + else if (isMutable) { + _value = newValue.asInstanceOf[AnyRef] + this + } else new Tree(_key, newValue.asInstanceOf[AnyRef], _left, _right, mutableRetainingColour) + } + + private[RedBlackTree] def mutableWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + if (_left eq newLeft) this + else if (isMutable) { + _left = newLeft + this + } else new Tree(_key, _value, newLeft, _right, mutableRetainingColour) + } + private[RedBlackTree] def mutableWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + if (_right eq newRight) this + else if (isMutable) { + _right = newRight + this + } else new Tree(_key, _value, _left, newRight, mutableRetainingColour) + } + private[RedBlackTree] def mutableWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + if ((_left eq newLeft) && (_right eq newRight)) this + else if (isMutable) { + _left = newLeft + _right = newRight + this + } else new Tree(_key, _value, newLeft, newRight, mutableRetainingColour) + } + private[RedBlackTree] def mutableBlackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + if ((_left eq newLeft) && isBlack) this + else if (isMutable) { + _count = initialBlackCount + _left = newLeft + this + } else new Tree(_key, _value, newLeft, _right, initialBlackCount) + } + private[RedBlackTree] def mutableBlackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + if ((_right eq newRight) && isBlack) this + else if (isMutable) { + _count = initialBlackCount + _right = newRight + this + } else new Tree(_key, _value, _left, newRight, initialBlackCount) + } + + private[RedBlackTree] def black: Tree[A, B] = { + //assertNotMutable(this) + if (isBlack) this + else new Tree(_key, _value, _left, _right, _count ^ colourBit) + } + private[RedBlackTree] def red: Tree[A, B] = { + //assertNotMutable(this) + if (isRed) this + else new Tree(_key, _value, _left, _right, _count ^ colourBit) + } + private[RedBlackTree] def withKV[B1 >: B](newKey: A, newValue: B1): Tree[A, B1] = { + //assertNotMutable(this) + if ((newKey.asInstanceOf[AnyRef] eq _key.asInstanceOf[AnyRef]) && + (newValue.asInstanceOf[AnyRef] eq _value.asInstanceOf[AnyRef])) this + else new Tree(newKey, newValue.asInstanceOf[AnyRef], _left, _right, _count) + } + private[RedBlackTree] def withV[B1 >: B](newValue: B1): Tree[A, B1] = { + //assertNotMutable(this) + if (newValue.asInstanceOf[AnyRef] eq _value.asInstanceOf[AnyRef]) this + else new Tree(_key, newValue.asInstanceOf[AnyRef], _left, _right, _count) + } + + private[RedBlackTree] def withLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if (newLeft eq _left) this + else { + val size = sizeOf(newLeft) + sizeOf(_right) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, (_count & colourBit) | size) + } + } + private[RedBlackTree] def withRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newRight) + if (newRight eq _right) this + else { + val size = sizeOf(_left) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, (_count & colourBit) | size) + } + } + private[RedBlackTree] def blackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if ((newLeft eq _left) && isBlack) this + else { + val size = sizeOf(newLeft) + sizeOf(_right) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialBlackCount | size) + } + } + private[RedBlackTree] def redWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if ((newLeft eq _left) && isRed) this + else { + val size = sizeOf(newLeft) + sizeOf(_right) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialRedCount | size) + } + } + private[RedBlackTree] def blackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newRight) + if ((newRight eq _right) && isBlack) this + else { + val size = sizeOf(_left) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialBlackCount | size) + } + } + private[RedBlackTree] def redWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if ((newRight eq _right) && isRed) this + else { + val size = sizeOf(_left) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialRedCount | size) + } + } + private[RedBlackTree] def withLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + //assertNotMutable(newRight) + if ((newLeft eq _left) && (newRight eq _right)) this + else { + val size = sizeOf(newLeft) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, (_count & colourBit) | size) + } + } + private[RedBlackTree] def redWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + //assertNotMutable(newRight) + if ((newLeft eq _left) && (newRight eq _right) && isRed) this + else { + val size = sizeOf(newLeft) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, initialRedCount | size) + } + } + private[RedBlackTree] def blackWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + //assertNotMutable(newRight) + if ((newLeft eq _left) && (newRight eq _right) && isBlack) this + else { + val size = sizeOf(newLeft) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, initialBlackCount | size) + } + } + } + //see #Tree docs "Colour, mutablity and size encoding" + //we make these final vals because the optimiser inlines them, without reference to the enclosing module + private[RedBlackTree] final val colourBit = 0x80000000 + private[RedBlackTree] final val colourMask = ~colourBit + private[RedBlackTree] final val initialBlackCount = colourBit + private[RedBlackTree] final val initialRedCount = 0 + + @`inline` private[RedBlackTree] def mutableRedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialRedCount) + @`inline` private[RedBlackTree] def mutableBlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialBlackCount) + + /** create a new immutable red tree. + * left and right may be null + */ + private[immutable] def RedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = { + //assertNotMutable(left) + //assertNotMutable(right) + val size = sizeOf(left) + sizeOf(right) + 1 + new Tree(key, value.asInstanceOf[AnyRef], left, right, initialRedCount | size) + } + private[immutable] def BlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = { + //assertNotMutable(left) + //assertNotMutable(right) + val size = sizeOf(left) + sizeOf(right) + 1 + new Tree(key, value.asInstanceOf[AnyRef], left, right, initialBlackCount | size) + } + @`inline` private def sizeOf(tree:Tree[_,_]) = if (tree eq null) 0 else tree.count + //immutable APIs + //assertions - uncomment decls and callers when changing functionality + // private def devTimeAssert(assertion: Boolean) = { + // //uncomment this during development of the functionality + // assert(assertion) + // } + // private def assertNotMutable(t:Tree[_,_]) = { + // devTimeAssert ((t eq null) || t.count > 0) + // } + private[this] abstract class TreeIterator[A, B, R](root: Tree[A, B], start: Option[A])(protected implicit val ordering: Ordering[A]) extends AbstractIterator[R] { + protected[this] def nextResult(tree: Tree[A, B]): R + + override def hasNext: Boolean = lookahead ne null + + @throws[NoSuchElementException] + override def next(): R = { + val tree = lookahead + if(tree ne null) { + lookahead = findLeftMostOrPopOnEmpty(goRight(tree)) + nextResult(tree) + } else Iterator.empty.next() + } + + @tailrec + protected final def findLeftMostOrPopOnEmpty(tree: Tree[A, B]): Tree[A, B] = + if (tree eq null) popNext() + else if (tree.left eq null) tree + else findLeftMostOrPopOnEmpty(goLeft(tree)) + + @`inline` private[this] def pushNext(tree: Tree[A, B]): Unit = { + stackOfNexts(index) = tree + index += 1 + } + @`inline` protected final def popNext(): Tree[A, B] = if (index == 0) null else { + index -= 1 + stackOfNexts(index) + } + + protected[this] val stackOfNexts = if (root eq null) null else { + /* + * According to "Ralf Hinze. Constructing red-black trees" [https://www.cs.ox.ac.uk/ralf.hinze/publications/#P5] + * the maximum height of a red-black tree is 2*log_2(n + 2) - 2. + * + * According to {@see Integer#numberOfLeadingZeros} ceil(log_2(n)) = (32 - Integer.numberOfLeadingZeros(n - 1)) + * + * Although we don't store the deepest nodes in the path during iteration, + * we potentially do so in `startFrom`. + */ + val maximumHeight = 2 * (32 - Integer.numberOfLeadingZeros(root.count + 2 - 1)) - 2 + new Array[Tree[A, B]](maximumHeight) + } + private[this] var index = 0 + protected var lookahead: Tree[A, B] = if (start.isDefined) startFrom(start.get) else findLeftMostOrPopOnEmpty(root) + + /** + * Find the leftmost subtree whose key is equal to the given key, or if no such thing, + * the leftmost subtree with the key that would be "next" after it according + * to the ordering. Along the way build up the iterator's path stack so that "next" + * functionality works. + */ + private[this] def startFrom(key: A) : Tree[A,B] = if (root eq null) null else { + @tailrec def find(tree: Tree[A, B]): Tree[A, B] = + if (tree eq null) popNext() + else find( + if (ordering.lteq(key, tree.key)) goLeft(tree) + else goRight(tree) + ) + find(root) + } + + @`inline` private[this] def goLeft(tree: Tree[A, B]) = { + pushNext(tree) + tree.left + } + + @`inline` protected final def goRight(tree: Tree[A, B]) = tree.right + } + + private[this] class EqualsIterator[A: Ordering, B](tree: Tree[A, B]) extends TreeIterator[A, B, Unit](tree, None) { + override def nextResult(tree: Tree[A, B]): Nothing = ??? + + def sameKeys[X](that:EqualsIterator[A,X]): Boolean = { + var equal = true + while (equal && (this.lookahead ne null) && (that.lookahead ne null)) { + if (this.lookahead eq that.lookahead) { + this.lookahead = this.popNext() + that.lookahead = that.popNext() + } else { + equal = (this.lookahead.key.asInstanceOf[AnyRef] eq that.lookahead.key.asInstanceOf[AnyRef]) || + ordering.equiv(this.lookahead.key, that.lookahead.key) + this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead)) + that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead)) + } + } + equal && (this.lookahead eq null) && (that.lookahead eq null) + } + def sameValues[X](that:EqualsIterator[A,X]): Boolean = { + var equal = true + while (equal && (this.lookahead ne null) && (that.lookahead ne null)) { + if (this.lookahead eq that.lookahead) { + this.lookahead = this.popNext() + that.lookahead = that.popNext() + } else { + equal = this.lookahead.value == that.lookahead.value + this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead)) + that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead)) + } + } + equal && (this.lookahead eq null) && (that.lookahead eq null) + } + def sameEntries[X](that:EqualsIterator[A,X]): Boolean = { + var equal = true + while (equal && (this.lookahead ne null) && (that.lookahead ne null)) { + if (this.lookahead eq that.lookahead) { + this.lookahead = this.popNext() + that.lookahead = that.popNext() + } else { + equal = ((this.lookahead.key.asInstanceOf[AnyRef] eq that.lookahead.key.asInstanceOf[AnyRef]) || + ordering.equiv(this.lookahead.key, that.lookahead.key)) && this.lookahead.value == that.lookahead.value + this.lookahead = this.findLeftMostOrPopOnEmpty(this.goRight(this.lookahead)) + that.lookahead = that.findLeftMostOrPopOnEmpty(that.goRight(that.lookahead)) + } + } + equal && (this.lookahead eq null) && (that.lookahead eq null) + } + } + private[this] class EntriesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, (A, B)](tree, focus) { + override def nextResult(tree: Tree[A, B]) = (tree.key, tree.value) + } + + private[this] class KeysIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, A](tree, focus) { + override def nextResult(tree: Tree[A, B]) = tree.key + } + + private[this] class ValuesIterator[A: Ordering, B](tree: Tree[A, B], focus: Option[A]) extends TreeIterator[A, B, B](tree, focus) { + override def nextResult(tree: Tree[A, B]) = tree.value + } + + /** Build a Tree suitable for a TreeSet from an ordered sequence of keys */ + def fromOrderedKeys[A](xs: Iterator[A], size: Int): Tree[A, Null] = { + val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes + def f(level: Int, size: Int): Tree[A, Null] = size match { + case 0 => null + case 1 => mkTree(level != maxUsedDepth || level == 1, xs.next(), null, null, null) + case n => + val leftSize = (size-1)/2 + val left = f(level+1, leftSize) + val x = xs.next() + val right = f(level+1, size-1-leftSize) + BlackTree(x, null, left, right) + } + f(1, size) + } + + /** Build a Tree suitable for a TreeMap from an ordered sequence of key/value pairs */ + def fromOrderedEntries[A, B](xs: Iterator[(A, B)], size: Int): Tree[A, B] = { + val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes + def f(level: Int, size: Int): Tree[A, B] = size match { + case 0 => null + case 1 => + val (k, v) = xs.next() + mkTree(level != maxUsedDepth || level == 1, k, v, null, null) + case n => + val leftSize = (size-1)/2 + val left = f(level+1, leftSize) + val (k, v) = xs.next() + val right = f(level+1, size-1-leftSize) + BlackTree(k, v, left, right) + } + f(1, size) + } + + def transform[A, B, C](t: Tree[A, B], f: (A, B) => C): Tree[A, C] = + if(t eq null) null + else { + val k = t.key + val v = t.value + val l = t.left + val r = t.right + val l2 = transform(l, f) + val v2 = f(k, v) + val r2 = transform(r, f) + if((v2.asInstanceOf[AnyRef] eq v.asInstanceOf[AnyRef]) + && (l2 eq l) + && (r2 eq r)) t.asInstanceOf[Tree[A, C]] + else mkTree(t.isBlack, k, v2, l2, r2) + } + + def filterEntries[A, B](t: Tree[A, B], f: (A, B) => Boolean): Tree[A, B] = if(t eq null) null else { + def fk(t: Tree[A, B]): Tree[A, B] = { + val k = t.key + val v = t.value + val l = t.left + val r = t.right + val l2 = if(l eq null) null else fk(l) + val keep = f(k, v) + val r2 = if(r eq null) null else fk(r) + if(!keep) join2(l2, r2) + else if((l2 eq l) && (r2 eq r)) t + else join(l2, k, v, r2) + } + blacken(fk(t)) + } + + private[this] val null2 = (null, null) + + def partitionEntries[A, B](t: Tree[A, B], p: (A, B) => Boolean): (Tree[A, B], Tree[A, B]) = if(t eq null) (null, null) else { + if (t eq null) null2 + else { + object partitioner { + var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk + def fk(t: Tree[A, B]): Unit = { + val k = t.key + val v = t.value + val l = t.left + val r = t.right + var l2k, l2d, r2k, r2d = null: Tree[A, B] + if (l ne null) { + fk(l) + l2k = tmpk + l2d = tmpd + } + val keep = p(k, v) + if (r ne null) { + fk(r) + r2k = tmpk + r2d = tmpd + } + val jk = + if (!keep) join2(l2k, r2k) + else if ((l2k eq l) && (r2k eq r)) t + else join(l2k, k, v, r2k) + val jd = + if (keep) join2(l2d, r2d) + else if ((l2d eq l) && (r2d eq r)) t + else join(l2d, k, v, r2d) + tmpk = jk + tmpd = jd + } + } + + partitioner.fk(t) + (blacken(partitioner.tmpk), blacken(partitioner.tmpd)) + } + } + + // Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees + // Constructing Red-Black Trees, Ralf Hinze: [[https://www.cs.ox.ac.uk/ralf.hinze/publications/WAAAPL99b.ps.gz]] + // Red-Black Trees in a Functional Setting, Chris Okasaki: [[https://wiki.rice.edu/confluence/download/attachments/2761212/Okasaki-Red-Black.pdf]] */ + + private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { + val cmp = ordering.compare(k, tree.key) + if (cmp < 0) { + val newLeft = del(tree.left, k) + if (newLeft eq tree.left) tree + else if (isBlackTree(tree.left)) balLeft(tree, newLeft, tree.right) + else tree.redWithLeft(newLeft) + } else if (cmp > 0) { + val newRight = del(tree.right, k) + if (newRight eq tree.right) tree + else if (isBlackTree(tree.right)) balRight(tree, tree.left, newRight) + else tree.redWithRight(newRight) + } else append(tree.left, tree.right) + } + + private[this] def balance[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if (isRedTree(tl)) { + if (isRedTree(tr)) tree.redWithLeftRight(tl.black, tr.black) + else if (isRedTree(tl.left)) tl.withLeftRight(tl.left.black, tree.blackWithLeftRight(tl.right, tr)) + else if (isRedTree(tl.right)) tl.right.withLeftRight(tl.blackWithRight(tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) + else tree.blackWithLeftRight(tl, tr) + } else if (isRedTree(tr)) { + if (isRedTree(tr.right)) tr.withLeftRight(tree.blackWithLeftRight(tl, tr.left), tr.right.black) + else if (isRedTree(tr.left)) tr.left.withLeftRight(tree.blackWithLeftRight(tl, tr.left.left), tr.blackWithLeftRight(tr.left.right, tr.right)) + else tree.blackWithLeftRight(tl, tr) + } else tree.blackWithLeftRight(tl, tr) + + private[this] def balLeft[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if (isRedTree(tl)) tree.redWithLeftRight(tl.black, tr) + else if (isBlackTree(tr)) balance(tree, tl, tr.red) + else if (isRedTree(tr) && isBlackTree(tr.left)) + tr.left.redWithLeftRight(tree.blackWithLeftRight(tl, tr.left.left), balance(tr, tr.left.right, tr.right.red)) + else sys.error("Defect: invariance violation") + + private[this] def balRight[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if (isRedTree(tr)) tree.redWithLeftRight(tl, tr.black) + else if (isBlackTree(tl)) balance(tree, tl.red, tr) + else if (isRedTree(tl) && isBlackTree(tl.right)) + tl.right.redWithLeftRight(balance(tl, tl.left.red, tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) + else sys.error("Defect: invariance violation") + + /** `append` is similar to `join2` but requires that both subtrees have the same black height */ + private[this] def append[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = { + if (tl eq null) tr + else if (tr eq null) tl + else if (tl.isRed) { + if (tr.isRed) { + //tl is red, tr is red + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) + else tl.withRight(tr.withLeft(bc)) + } else { + //tl is red, tr is black + tl.withRight(append(tl.right, tr)) + } + } else { + if (tr.isBlack) { + //tl is black tr is black + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) + else balLeft(tl, tl.left, tr.withLeft(bc)) + } else { + //tl is black tr is red + tr.withLeft(append(tl, tr.left)) + } + } + } + + + // Bulk operations based on "Just Join for Parallel Ordered Sets" (https://www.cs.cmu.edu/~guyb/papers/BFS16.pdf) + // We don't store the black height in the tree so we pass it down into the join methods and derive the black height + // of child nodes from it. Where possible the black height is used directly instead of deriving the rank from it. + // Our trees are supposed to have a black root so we always blacken as the last step of union/intersect/difference. + + def union[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = blacken(_union(t1, t2)) + + def intersect[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = blacken(_intersect(t1, t2)) + + def difference[A, B](t1: Tree[A, B], t2: Tree[A, _])(implicit ordering: Ordering[A]): Tree[A, B] = + blacken(_difference(t1, t2.asInstanceOf[Tree[A, B]])) + + /** Compute the rank from a tree and its black height */ + @`inline` private[this] def rank(t: Tree[_, _], bh: Int): Int = { + if(t eq null) 0 + else if(t.isBlack) 2*(bh-1) + else 2*bh-1 + } + + private[this] def joinRight[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B], bhtl: Int, rtr: Int): Tree[A, B] = { + val rtl = rank(tl, bhtl) + if(rtl == (rtr/2)*2) RedTree(k, v, tl, tr) + else { + val tlBlack = isBlackTree(tl) + val bhtlr = if(tlBlack) bhtl-1 else bhtl + val ttr = joinRight(tl.right, k, v, tr, bhtlr, rtr) + if(tlBlack && isRedTree(ttr) && isRedTree(ttr.right)) + RedTree(ttr.key, ttr.value, + BlackTree(tl.key, tl.value, tl.left, ttr.left), + ttr.right.black) + else mkTree(tlBlack, tl.key, tl.value, tl.left, ttr) + } + } + + private[this] def joinLeft[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B], rtl: Int, bhtr: Int): Tree[A, B] = { + val rtr = rank(tr, bhtr) + if(rtr == (rtl/2)*2) RedTree(k, v, tl, tr) + else { + val trBlack = isBlackTree(tr) + val bhtrl = if(trBlack) bhtr-1 else bhtr + val ttl = joinLeft(tl, k, v, tr.left, rtl, bhtrl) + if(trBlack && isRedTree(ttl) && isRedTree(ttl.left)) + RedTree(ttl.key, ttl.value, + ttl.left.black, + BlackTree(tr.key, tr.value, ttl.right, tr.right)) + else mkTree(trBlack, tr.key, tr.value, ttl, tr.right) + } + } + + private[this] def join[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B]): Tree[A, B] = { + @tailrec def h(t: Tree[_, _], i: Int): Int = + if(t eq null) i+1 else h(t.left, if(t.isBlack) i+1 else i) + val bhtl = h(tl, 0) + val bhtr = h(tr, 0) + if(bhtl > bhtr) { + val tt = joinRight(tl, k, v, tr, bhtl, rank(tr, bhtr)) + if(isRedTree(tt) && isRedTree(tt.right)) tt.black + else tt + } else if(bhtr > bhtl) { + val tt = joinLeft(tl, k, v, tr, rank(tl, bhtl), bhtr) + if(isRedTree(tt) && isRedTree(tt.left)) tt.black + else tt + } else mkTree(isRedTree(tl) || isRedTree(tr), k, v, tl, tr) + } + + private[this] def split[A, B](t: Tree[A, B], k2: A)(implicit ordering: Ordering[A]): (Tree[A, B], Tree[A, B], Tree[A, B], A) = + if(t eq null) (null, null, null, k2) + else { + val cmp = ordering.compare(k2, t.key) + if(cmp == 0) (t.left, t, t.right, t.key) + else if(cmp < 0) { + val (ll, b, lr, k1) = split(t.left, k2) + (ll, b, join(lr, t.key, t.value, t.right), k1) + } else { + val (rl, b, rr, k1) = split(t.right, k2) + (join(t.left, t.key, t.value, rl), b, rr, k1) + } + } + + private[this] def splitLast[A, B](t: Tree[A, B]): (Tree[A, B], A, B) = + if(t.right eq null) (t.left, t.key, t.value) + else { + val (tt, kk, vv) = splitLast(t.right) + (join(t.left, t.key, t.value, tt), kk, vv) + } + + private[this] def join2[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if(tl eq null) tr + else if(tr eq null) tl + else { + val (ttl, k, v) = splitLast(tl) + join(ttl, k, v, tr) + } + + private[this] def _union[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = + if((t1 eq null) || (t1 eq t2)) t2 + else if(t2 eq null) t1 + else { + val (l1, _, r1, k1) = split(t1, t2.key) + val tl = _union(l1, t2.left) + val tr = _union(r1, t2.right) + join(tl, k1, t2.value, tr) + } + + private[this] def _intersect[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = + if((t1 eq null) || (t2 eq null)) null + else if (t1 eq t2) t1 + else { + val (l1, b, r1, k1) = split(t1, t2.key) + val tl = _intersect(l1, t2.left) + val tr = _intersect(r1, t2.right) + if(b ne null) join(tl, k1, t2.value, tr) + else join2(tl, tr) + } + + private[this] def _difference[A, B](t1: Tree[A, B], t2: Tree[A, B])(implicit ordering: Ordering[A]): Tree[A, B] = + if((t1 eq null) || (t2 eq null)) t1 + else if (t1 eq t2) null + else { + val (l1, _, r1, _) = split(t1, t2.key) + val tl = _difference(l1, t2.left) + val tr = _difference(r1, t2.right) + join2(tl, tr) + } +} diff --git a/library/src/scala/collection/immutable/Seq.scala b/library/src/scala/collection/immutable/Seq.scala new file mode 100644 index 000000000000..81a40c1c375b --- /dev/null +++ b/library/src/scala/collection/immutable/Seq.scala @@ -0,0 +1,155 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +trait Seq[+A] extends Iterable[A] + with collection.Seq[A] + with SeqOps[A, Seq, Seq[A]] + with IterableFactoryDefaults[A, Seq] { + + override final def toSeq: this.type = this + + override def iterableFactory: SeqFactory[Seq] = Seq +} + +/** + * @define coll immutable sequence + * @define Coll `immutable.Seq` + */ +trait SeqOps[+A, +CC[_], +C] extends Any with collection.SeqOps[A, CC, C] + +/** + * $factoryInfo + * @define coll immutable sequence + * @define Coll `immutable.Seq` + */ +@SerialVersionUID(3L) +object Seq extends SeqFactory.Delegate[Seq](List) { + override def from[E](it: IterableOnce[E]): Seq[E] = it match { + case s: Seq[E] => s + case _ => super.from(it) + } +} + +/** Base trait for immutable indexed sequences that have efficient `apply` and `length` */ +trait IndexedSeq[+A] extends Seq[A] + with collection.IndexedSeq[A] + with IndexedSeqOps[A, IndexedSeq, IndexedSeq[A]] + with IterableFactoryDefaults[A, IndexedSeq] { + + final override def toIndexedSeq: IndexedSeq[A] = this + + override def canEqual(that: Any): Boolean = that match { + case otherIndexedSeq: IndexedSeq[_] => length == otherIndexedSeq.length && super.canEqual(that) + case _ => super.canEqual(that) + } + + + override def sameElements[B >: A](o: IterableOnce[B]): Boolean = o match { + case that: IndexedSeq[_] => + (this eq that) || { + val length = this.length + var equal = length == that.length + if (equal) { + var index = 0 + // some IndexedSeq apply is less efficient than using Iterators + // e.g. Vector so we can compare the first few with apply and the rest with an iterator + // but if apply is more efficient than Iterators then we can use the apply for all the comparison + // we default to the minimum preferred length + val maxApplyCompare = { + val preferredLength = Math.min(applyPreferredMaxLength, that.applyPreferredMaxLength) + if (length > (preferredLength.toLong << 1)) preferredLength else length + } + while (index < maxApplyCompare && equal) { + equal = this (index) == that(index) + index += 1 + } + if ((index < length) && equal) { + val thisIt = this.iterator.drop(index) + val thatIt = that.iterator.drop(index) + while (equal && thisIt.hasNext) { + equal = thisIt.next() == thatIt.next() + } + } + } + equal + } + case _ => super.sameElements(o) + } + + /** a hint to the runtime when scanning values + * [[apply]] is preferred for scan with a max index less than this value + * [[iterator]] is preferred for scans above this range + * @return a hint about when to use [[apply]] or [[iterator]] + */ + protected def applyPreferredMaxLength: Int = IndexedSeqDefaults.defaultApplyPreferredMaxLength + + override def iterableFactory: SeqFactory[IndexedSeq] = IndexedSeq +} + +object IndexedSeqDefaults { + val defaultApplyPreferredMaxLength: Int = + try System.getProperty( + "scala.collection.immutable.IndexedSeq.defaultApplyPreferredMaxLength", "64").toInt + catch { + case _: SecurityException => 64 + } +} + +@SerialVersionUID(3L) +object IndexedSeq extends SeqFactory.Delegate[IndexedSeq](Vector) { + override def from[E](it: IterableOnce[E]): IndexedSeq[E] = it match { + case is: IndexedSeq[E] => is + case _ => super.from(it) + } +} + +/** Base trait for immutable indexed Seq operations */ +trait IndexedSeqOps[+A, +CC[_], +C] + extends SeqOps[A, CC, C] + with collection.IndexedSeqOps[A, CC, C] { + + override def slice(from: Int, until: Int): C = { + // since we are immutable we can just share the same collection + if (from <= 0 && until >= length) coll + else super.slice(from, until) + } + +} + +/** Base trait for immutable linear sequences that have efficient `head` and `tail` */ +trait LinearSeq[+A] + extends Seq[A] + with collection.LinearSeq[A] + with LinearSeqOps[A, LinearSeq, LinearSeq[A]] + with IterableFactoryDefaults[A, LinearSeq] { + + override def iterableFactory: SeqFactory[LinearSeq] = LinearSeq +} + +@SerialVersionUID(3L) +object LinearSeq extends SeqFactory.Delegate[LinearSeq](List) { + override def from[E](it: IterableOnce[E]): LinearSeq[E] = it match { + case ls: LinearSeq[E] => ls + case _ => super.from(it) + } +} + +trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with LinearSeqOps[A, CC, C]] + extends Any with SeqOps[A, CC, C] + with collection.LinearSeqOps[A, CC, C] + +/** Explicit instantiation of the `Seq` trait to reduce class file size in subclasses. */ +abstract class AbstractSeq[+A] extends scala.collection.AbstractSeq[A] with Seq[A] diff --git a/library/src/scala/collection/immutable/SeqMap.scala b/library/src/scala/collection/immutable/SeqMap.scala new file mode 100644 index 000000000000..03daef1481a8 --- /dev/null +++ b/library/src/scala/collection/immutable/SeqMap.scala @@ -0,0 +1,284 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.collection.mutable.{Builder, ReusableBuilder} + +/** A base trait for ordered, immutable maps. + * + * Note that the [[equals]] method for [[SeqMap]] compares key-value pairs + * without regard to ordering. + * + * All behavior is defined in terms of the abstract methods in `SeqMap`. + * It is sufficient for concrete subclasses to implement those methods. + * Methods that return a new map, in particular [[removed]] and [[updated]], must preserve ordering. + * + * @tparam K the type of the keys contained in this linked map. + * @tparam V the type of the values associated with the keys in this linked map. + * + * @define coll immutable seq map + * @define Coll `immutable.SeqMap` + */ + +trait SeqMap[K, +V] + extends Map[K, V] + with collection.SeqMap[K, V] + with MapOps[K, V, SeqMap, SeqMap[K, V]] + with MapFactoryDefaults[K, V, SeqMap, Iterable] { + override def mapFactory: MapFactory[SeqMap] = SeqMap +} + + +object SeqMap extends MapFactory[SeqMap] { + def empty[K, V]: SeqMap[K, V] = EmptySeqMap.asInstanceOf[SeqMap[K, V]] + + def from[K, V](it: collection.IterableOnce[(K, V)]): SeqMap[K, V] = + it match { + //case sm: SeqMap[K, V] => sm + case m: ListMap[K, V] => m + case m: TreeSeqMap[K, V] => m + case m: VectorMap[K, V] => m + case m: SeqMap1[K, V] => m + case m: SeqMap2[K, V] => m + case m: SeqMap3[K, V] => m + case m: SeqMap4[K, V] => m + case it: Iterable[_] if it.isEmpty => empty[K, V] + case _ => (newBuilder[K, V] ++= it).result() + } + + def newBuilder[K, V]: Builder[(K, V), SeqMap[K, V]] = new SeqMapBuilderImpl + + @SerialVersionUID(3L) + private object EmptySeqMap extends SeqMap[Any, Nothing] with Serializable { + override def size: Int = 0 + override def knownSize: Int = 0 + override def apply(key: Any) = throw new NoSuchElementException("key not found: " + key) + override def contains(key: Any) = false + def get(key: Any): Option[Nothing] = None + override def getOrElse [V1](key: Any, default: => V1): V1 = default + def iterator: Iterator[(Any, Nothing)] = Iterator.empty + def updated [V1] (key: Any, value: V1): SeqMap[Any, V1] = new SeqMap1(key, value) + def removed(key: Any): SeqMap[Any, Nothing] = this + } + + @SerialVersionUID(3L) + private[immutable] final class SeqMap1[K, +V](key1: K, value1: V) extends SeqMap[K,V] with Serializable { + override def size: Int = 1 + override def knownSize: Int = 1 + override def apply(key: K) = if (key == key1) value1 else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K) = key == key1 + def get(key: K): Option[V] = + if (key == key1) Some(value1) else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 else default + def iterator = Iterator.single((key1, value1)) + def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] = + if (key == key1) new SeqMap1(key1, value) + else new SeqMap2(key1, value1, key, value) + def removed(key: K): SeqMap[K, V] = + if (key == key1) SeqMap.empty else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)) + } + override def foreachEntry[U](f: (K, V) => U): Unit = { + f(key1, value1) + } + } + + @SerialVersionUID(3L) + private[immutable] final class SeqMap2[K, +V](key1: K, value1: V, key2: K, value2: V) extends SeqMap[K,V] with Serializable { + override def size: Int = 2 + override def knownSize: Int = 2 + override def apply(key: K) = + if (key == key1) value1 + else if (key == key2) value2 + else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K) = (key == key1) || (key == key2) + def get(key: K): Option[V] = + if (key == key1) Some(value1) + else if (key == key2) Some(value2) + else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 + else if (key == key2) value2 + else default + def iterator = ((key1, value1) :: (key2, value2) :: Nil).iterator + def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] = + if (key == key1) new SeqMap2(key1, value, key2, value2) + else if (key == key2) new SeqMap2(key1, value1, key2, value) + else new SeqMap3(key1, value1, key2, value2, key, value) + def removed(key: K): SeqMap[K, V] = + if (key == key1) new SeqMap1(key2, value2) + else if (key == key2) new SeqMap1(key1, value1) + else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)); f((key2, value2)) + } + override def foreachEntry[U](f: (K, V) => U): Unit = { + f(key1, value1) + f(key2, value2) + } + } + + @SerialVersionUID(3L) + private[immutable] class SeqMap3[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V) extends SeqMap[K,V] with Serializable { + override def size: Int = 3 + override def knownSize: Int = 3 + override def apply(key: K) = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K) = (key == key1) || (key == key2) || (key == key3) + def get(key: K): Option[V] = + if (key == key1) Some(value1) + else if (key == key2) Some(value2) + else if (key == key3) Some(value3) + else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else default + def iterator = ((key1, value1) :: (key2, value2) :: (key3, value3) :: Nil).iterator + def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] = + if (key == key1) new SeqMap3(key1, value, key2, value2, key3, value3) + else if (key == key2) new SeqMap3(key1, value1, key2, value, key3, value3) + else if (key == key3) new SeqMap3(key1, value1, key2, value2, key3, value) + else new SeqMap4(key1, value1, key2, value2, key3, value3, key, value) + def removed(key: K): SeqMap[K, V] = + if (key == key1) new SeqMap2(key2, value2, key3, value3) + else if (key == key2) new SeqMap2(key1, value1, key3, value3) + else if (key == key3) new SeqMap2(key1, value1, key2, value2) + else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)); f((key2, value2)); f((key3, value3)) + } + override def foreachEntry[U](f: (K, V) => U): Unit = { + f(key1, value1) + f(key2, value2) + f(key3, value3) + } + } + + @SerialVersionUID(3L) + private[immutable] final class SeqMap4[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V, key4: K, value4: V) extends SeqMap[K,V] with Serializable { + override def size: Int = 4 + override def knownSize: Int = 4 + override def apply(key: K) = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else if (key == key4) value4 + else throw new NoSuchElementException("key not found: " + key) + override def contains(key: K) = (key == key1) || (key == key2) || (key == key3) || (key == key4) + def get(key: K): Option[V] = + if (key == key1) Some(value1) + else if (key == key2) Some(value2) + else if (key == key3) Some(value3) + else if (key == key4) Some(value4) + else None + override def getOrElse [V1 >: V](key: K, default: => V1): V1 = + if (key == key1) value1 + else if (key == key2) value2 + else if (key == key3) value3 + else if (key == key4) value4 + else default + def iterator = ((key1, value1) :: (key2, value2) :: (key3, value3) :: (key4, value4) :: Nil).iterator + def updated[V1 >: V](key: K, value: V1): SeqMap[K, V1] = + if (key == key1) new SeqMap4(key1, value, key2, value2, key3, value3, key4, value4) + else if (key == key2) new SeqMap4(key1, value1, key2, value, key3, value3, key4, value4) + else if (key == key3) new SeqMap4(key1, value1, key2, value2, key3, value, key4, value4) + else if (key == key4) new SeqMap4(key1, value1, key2, value2, key3, value3, key4, value) + else { + // Directly create the elements for performance reasons + val fields = Vector(key1, key2, key3, key4, key) + val underlying: Map[K, (Int, V1)] = + HashMap( + (key1, (0, value1)), + (key2, (1, value2)), + (key3, (2, value3)), + (key4, (3, value4)), + (key, (4, value)) + ) + new VectorMap(fields, underlying) + } + def removed(key: K): SeqMap[K, V] = + if (key == key1) new SeqMap3(key2, value2, key3, value3, key4, value4) + else if (key == key2) new SeqMap3(key1, value1, key3, value3, key4, value4) + else if (key == key3) new SeqMap3(key1, value1, key2, value2, key4, value4) + else if (key == key4) new SeqMap3(key1, value1, key2, value2, key3, value3) + else this + override def foreach[U](f: ((K, V)) => U): Unit = { + f((key1, value1)); f((key2, value2)); f((key3, value3)); f((key4, value4)) + } + override def foreachEntry[U](f: (K, V) => U): Unit = { + f(key1, value1) + f(key2, value2) + f(key3, value3) + f(key4, value4) + } + + private[SeqMap] def buildTo[V1 >: V](builder: Builder[(K, V1), SeqMap[K, V1]]): builder.type = + builder.addOne((key1, value1)).addOne((key2, value2)).addOne((key3, value3)).addOne((key4, value4)) + } + + private final class SeqMapBuilderImpl[K, V] extends ReusableBuilder[(K, V), SeqMap[K, V]] { + private[this] var elems: SeqMap[K, V] = SeqMap.empty + private[this] var switchedToVectorMapBuilder: Boolean = false + private[this] var vectorMapBuilder: VectorMapBuilder[K, V] = _ + + override def clear(): Unit = { + elems = SeqMap.empty + if (vectorMapBuilder != null) { + vectorMapBuilder.clear() + } + switchedToVectorMapBuilder = false + } + + override def result(): SeqMap[K, V] = + if (switchedToVectorMapBuilder) vectorMapBuilder.result() else elems + + def addOne(elem: (K, V)) = { + if (switchedToVectorMapBuilder) { + vectorMapBuilder.addOne(elem) + } else if (elems.size < 4) { + elems = elems + elem + } else { + // assert(elems.size == 4) + if (elems.contains(elem._1)) { + elems = elems + elem // will not increase the size of the map + } else { + switchedToVectorMapBuilder = true + if (vectorMapBuilder == null) { + vectorMapBuilder = new VectorMapBuilder + } + elems.asInstanceOf[SeqMap4[K, V]].buildTo(vectorMapBuilder) + vectorMapBuilder.addOne(elem) + } + } + + this + } + + override def addAll(xs: IterableOnce[(K, V)]): this.type = + if (switchedToVectorMapBuilder) { + vectorMapBuilder.addAll(xs) + this + } else { + super.addAll(xs) + } + } +} diff --git a/library/src/scala/collection/immutable/Set.scala b/library/src/scala/collection/immutable/Set.scala new file mode 100644 index 000000000000..e8509b58016e --- /dev/null +++ b/library/src/scala/collection/immutable/Set.scala @@ -0,0 +1,407 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.collection.immutable.Set.Set4 +import scala.collection.mutable.{Builder, ReusableBuilder} + +/** Base trait for immutable set collections */ +trait Set[A] extends Iterable[A] + with collection.Set[A] + with SetOps[A, Set, Set[A]] + with IterableFactoryDefaults[A, Set] { + override def iterableFactory: IterableFactory[Set] = Set +} + +/** Base trait for immutable set operations + * + * @define coll immutable set + * @define Coll `immutable.Set` + */ +trait SetOps[A, +CC[X], +C <: SetOps[A, CC, C]] + extends collection.SetOps[A, CC, C] { + + /** Creates a new set with an additional element, unless the element is + * already present. + * + * @param elem the element to be added + * @return a new set that contains all elements of this set and that also + * contains `elem`. + */ + def incl(elem: A): C + + /** Alias for `incl` */ + override final def + (elem: A): C = incl(elem) // like in collection.Set but not deprecated + + /** Creates a new set with a given element removed from this set. + * + * @param elem the element to be removed + * @return a new set that contains all elements of this set but that does not + * contain `elem`. + */ + def excl(elem: A): C + + /** Alias for `excl` */ + @`inline` final override def - (elem: A): C = excl(elem) + + def diff(that: collection.Set[A]): C = + foldLeft(empty)((result, elem) => if (that contains elem) result else result + elem) + + /** Creates a new $coll from this $coll by removing all elements of another + * collection. + * + * @param that the collection containing the elements to remove. + * @return a new $coll with the given elements removed, omitting duplicates. + */ + def removedAll(that: IterableOnce[A]): C = that.iterator.foldLeft[C](coll)(_ - _) + + /** Alias for removedAll */ + override final def -- (that: IterableOnce[A]): C = removedAll(that) +} + +trait StrictOptimizedSetOps[A, +CC[X], +C <: SetOps[A, CC, C]] + extends SetOps[A, CC, C] + with collection.StrictOptimizedSetOps[A, CC, C] + with StrictOptimizedIterableOps[A, CC, C] { + + override def concat(that: collection.IterableOnce[A]): C = { + var result: C = coll + val it = that.iterator + while (it.hasNext) result = result + it.next() + result + } +} + +/** + * $factoryInfo + * @define coll immutable set + * @define Coll `immutable.Set` + */ +@SerialVersionUID(3L) +object Set extends IterableFactory[Set] { + + def empty[A]: Set[A] = EmptySet.asInstanceOf[Set[A]] + + def from[E](it: collection.IterableOnce[E]): Set[E] = + it match { + case _ if it.knownSize == 0 => empty[E] + // Since IterableOnce[E] launders the variance of E, + // identify only our implementations which can be soundly substituted. + // It's not sufficient to match `SortedSet[E]` to rebuild and `Set[E]` to retain. + case s: HashSet[E] => s + case s: ListSet[E] => s + case s: Set1[E] => s + case s: Set2[E] => s + case s: Set3[E] => s + case s: Set4[E] => s + case s: HashMap[E @unchecked, _]#HashKeySet => s + case s: MapOps[E, Any, Map, Map[E, Any]]#ImmutableKeySet @unchecked => s + // We also want `SortedSet` (and subclasses, such as `BitSet`) + // to rebuild themselves, to avoid element type widening issues. + case _ => newBuilder[E].addAll(it).result() + } + + def newBuilder[A]: Builder[A, Set[A]] = new SetBuilderImpl[A] + + /** An optimized representation for immutable empty sets */ + @SerialVersionUID(3L) + private object EmptySet extends AbstractSet[Any] with Serializable { + override def size: Int = 0 + override def isEmpty = true + override def knownSize: Int = size + override def filter(pred: Any => Boolean): Set[Any] = this + override def filterNot(pred: Any => Boolean): Set[Any] = this + override def removedAll(that: IterableOnce[Any]): Set[Any] = this + override def diff(that: collection.Set[Any]): Set[Any] = this + override def subsetOf(that: collection.Set[Any]): Boolean = true + override def intersect(that: collection.Set[Any]): Set[Any] = this + override def view: View[Any] = View.empty + def contains(elem: Any): Boolean = false + def incl(elem: Any): Set[Any] = new Set1(elem) + def excl(elem: Any): Set[Any] = this + def iterator: Iterator[Any] = Iterator.empty + override def foreach[U](f: Any => U): Unit = () + } + private[collection] def emptyInstance: Set[Any] = EmptySet + + @SerialVersionUID(3L) + private abstract class SetNIterator[A](n: Int) extends AbstractIterator[A] with Serializable { + private[this] var current = 0 + private[this] var remainder = n + override def knownSize: Int = remainder + def hasNext = remainder > 0 + def apply(i: Int): A + def next(): A = + if (hasNext) { + val r = apply(current) + current += 1 + remainder -= 1 + r + } else Iterator.empty.next() + + override def drop(n: Int): Iterator[A] = { + if (n > 0) { + current += n + remainder = Math.max(0, remainder - n) + } + this + } + } + + /** An optimized representation for immutable sets of size 1 */ + @SerialVersionUID(3L) + final class Set1[A] private[collection] (elem1: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable { + override def size: Int = 1 + override def isEmpty = false + override def knownSize: Int = size + def contains(elem: A): Boolean = elem == elem1 + def incl(elem: A): Set[A] = + if (contains(elem)) this + else new Set2(elem1, elem) + def excl(elem: A): Set[A] = + if (elem == elem1) Set.empty + else this + def iterator: Iterator[A] = Iterator.single(elem1) + override def foreach[U](f: A => U): Unit = f(elem1) + override def exists(p: A => Boolean): Boolean = p(elem1) + override def forall(p: A => Boolean): Boolean = p(elem1) + override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] = + if (pred(elem1) != isFlipped) this else Set.empty + + override def find(p: A => Boolean): Option[A] = + if (p(elem1)) Some(elem1) + else None + override def head: A = elem1 + override def tail: Set[A] = Set.empty + } + + /** An optimized representation for immutable sets of size 2 */ + @SerialVersionUID(3L) + final class Set2[A] private[collection] (elem1: A, elem2: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable { + override def size: Int = 2 + override def isEmpty = false + override def knownSize: Int = size + def contains(elem: A): Boolean = elem == elem1 || elem == elem2 + def incl(elem: A): Set[A] = + if (contains(elem)) this + else new Set3(elem1, elem2, elem) + def excl(elem: A): Set[A] = + if (elem == elem1) new Set1(elem2) + else if (elem == elem2) new Set1(elem1) + else this + def iterator: Iterator[A] = new SetNIterator[A](size) { + def apply(i: Int) = getElem(i) + } + private def getElem(i: Int) = i match { case 0 => elem1 case 1 => elem2 } + + override def foreach[U](f: A => U): Unit = { + f(elem1); f(elem2) + } + override def exists(p: A => Boolean): Boolean = { + p(elem1) || p(elem2) + } + override def forall(p: A => Boolean): Boolean = { + p(elem1) && p(elem2) + } + override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] = { + var r1: A = null.asInstanceOf[A] + var n = 0 + if (pred(elem1) != isFlipped) { r1 = elem1; n += 1} + if (pred(elem2) != isFlipped) { if (n == 0) r1 = elem2; n += 1} + + n match { + case 0 => Set.empty + case 1 => new Set1(r1) + case 2 => this + } + } + override def find(p: A => Boolean): Option[A] = { + if (p(elem1)) Some(elem1) + else if (p(elem2)) Some(elem2) + else None + } + override def head: A = elem1 + override def tail: Set[A] = new Set1(elem2) + } + + /** An optimized representation for immutable sets of size 3 */ + @SerialVersionUID(3L) + final class Set3[A] private[collection] (elem1: A, elem2: A, elem3: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable { + override def size: Int = 3 + override def isEmpty = false + override def knownSize: Int = size + def contains(elem: A): Boolean = + elem == elem1 || elem == elem2 || elem == elem3 + def incl(elem: A): Set[A] = + if (contains(elem)) this + else new Set4(elem1, elem2, elem3, elem) + def excl(elem: A): Set[A] = + if (elem == elem1) new Set2(elem2, elem3) + else if (elem == elem2) new Set2(elem1, elem3) + else if (elem == elem3) new Set2(elem1, elem2) + else this + def iterator: Iterator[A] = new SetNIterator[A](size) { + def apply(i: Int) = getElem(i) + } + private def getElem(i: Int) = i match { case 0 => elem1 case 1 => elem2 case 2 => elem3 } + + override def foreach[U](f: A => U): Unit = { + f(elem1); f(elem2); f(elem3) + } + override def exists(p: A => Boolean): Boolean = { + p(elem1) || p(elem2) || p(elem3) + } + override def forall(p: A => Boolean): Boolean = { + p(elem1) && p(elem2) && p(elem3) + } + override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] = { + var r1, r2: A = null.asInstanceOf[A] + var n = 0 + if (pred(elem1) != isFlipped) { r1 = elem1; n += 1} + if (pred(elem2) != isFlipped) { if (n == 0) r1 = elem2 else r2 = elem2; n += 1} + if (pred(elem3) != isFlipped) { if (n == 0) r1 = elem3 else if (n == 1) r2 = elem3; n += 1} + + n match { + case 0 => Set.empty + case 1 => new Set1(r1) + case 2 => new Set2(r1, r2) + case 3 => this + } + } + override def find(p: A => Boolean): Option[A] = { + if (p(elem1)) Some(elem1) + else if (p(elem2)) Some(elem2) + else if (p(elem3)) Some(elem3) + else None + } + override def head: A = elem1 + override def tail: Set[A] = new Set2(elem2, elem3) + } + + /** An optimized representation for immutable sets of size 4 */ + @SerialVersionUID(3L) + final class Set4[A] private[collection] (elem1: A, elem2: A, elem3: A, elem4: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable { + override def size: Int = 4 + override def isEmpty = false + override def knownSize: Int = size + def contains(elem: A): Boolean = + elem == elem1 || elem == elem2 || elem == elem3 || elem == elem4 + def incl(elem: A): Set[A] = + if (contains(elem)) this + else HashSet.empty[A] + elem1 + elem2 + elem3 + elem4 + elem + def excl(elem: A): Set[A] = + if (elem == elem1) new Set3(elem2, elem3, elem4) + else if (elem == elem2) new Set3(elem1, elem3, elem4) + else if (elem == elem3) new Set3(elem1, elem2, elem4) + else if (elem == elem4) new Set3(elem1, elem2, elem3) + else this + def iterator: Iterator[A] = new SetNIterator[A](size) { + def apply(i: Int) = getElem(i) + } + private def getElem(i: Int) = i match { case 0 => elem1 case 1 => elem2 case 2 => elem3 case 3 => elem4 } + + override def foreach[U](f: A => U): Unit = { + f(elem1); f(elem2); f(elem3); f(elem4) + } + override def exists(p: A => Boolean): Boolean = { + p(elem1) || p(elem2) || p(elem3) || p(elem4) + } + override def forall(p: A => Boolean): Boolean = { + p(elem1) && p(elem2) && p(elem3) && p(elem4) + } + override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] = { + var r1, r2, r3: A = null.asInstanceOf[A] + var n = 0 + if (pred(elem1) != isFlipped) { r1 = elem1; n += 1} + if (pred(elem2) != isFlipped) { if (n == 0) r1 = elem2 else r2 = elem2; n += 1} + if (pred(elem3) != isFlipped) { if (n == 0) r1 = elem3 else if (n == 1) r2 = elem3 else r3 = elem3; n += 1} + if (pred(elem4) != isFlipped) { if (n == 0) r1 = elem4 else if (n == 1) r2 = elem4 else if (n == 2) r3 = elem4; n += 1} + + n match { + case 0 => Set.empty + case 1 => new Set1(r1) + case 2 => new Set2(r1, r2) + case 3 => new Set3(r1, r2, r3) + case 4 => this + } + } + + override def find(p: A => Boolean): Option[A] = { + if (p(elem1)) Some(elem1) + else if (p(elem2)) Some(elem2) + else if (p(elem3)) Some(elem3) + else if (p(elem4)) Some(elem4) + else None + } + override def head: A = elem1 + override def tail: Set[A] = new Set3(elem2, elem3, elem4) + + private[immutable] def buildTo(builder: Builder[A, Set[A]]): builder.type = + builder.addOne(elem1).addOne(elem2).addOne(elem3).addOne(elem4) + } +} + +/** Explicit instantiation of the `Set` trait to reduce class file size in subclasses. */ +abstract class AbstractSet[A] extends scala.collection.AbstractSet[A] with Set[A] + +/** Builder for Set. + * $multipleResults + */ +private final class SetBuilderImpl[A] extends ReusableBuilder[A, Set[A]] { + private[this] var elems: Set[A] = Set.empty + private[this] var switchedToHashSetBuilder: Boolean = false + private[this] var hashSetBuilder: HashSetBuilder[A] = _ + + override def clear(): Unit = { + elems = Set.empty + if (hashSetBuilder != null) { + hashSetBuilder.clear() + } + switchedToHashSetBuilder = false + } + + override def result(): Set[A] = + if (switchedToHashSetBuilder) hashSetBuilder.result() else elems + + def addOne(elem: A) = { + if (switchedToHashSetBuilder) { + hashSetBuilder.addOne(elem) + } else if (elems.size < 4) { + elems = elems + elem + } else { + // assert(elems.size == 4) + if (elems.contains(elem)) { + () // do nothing + } else { + switchedToHashSetBuilder = true + if (hashSetBuilder == null) { + hashSetBuilder = new HashSetBuilder + } + elems.asInstanceOf[Set4[A]].buildTo(hashSetBuilder) + hashSetBuilder.addOne(elem) + } + } + + this + } + + override def addAll(xs: IterableOnce[A]): this.type = + if (switchedToHashSetBuilder) { + hashSetBuilder.addAll(xs) + this + } else { + super.addAll(xs) + } +} diff --git a/library/src/scala/collection/immutable/SortedMap.scala b/library/src/scala/collection/immutable/SortedMap.scala new file mode 100644 index 000000000000..120ae23ae024 --- /dev/null +++ b/library/src/scala/collection/immutable/SortedMap.scala @@ -0,0 +1,177 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.mutable.Builder + +/** An immutable map whose key-value pairs are sorted according to an [[scala.math.Ordering]] on the keys. + * + * Allows for range queries to be performed on its keys, and implementations must guarantee that traversal happens in + * sorted order, according to the map's [[scala.math.Ordering]]. + * + * @example {{{ + * import scala.collection.immutable.SortedMap + * + * // Make a SortedMap via the companion object factory + * val weekdays = SortedMap( + * 2 -> "Monday", + * 3 -> "Tuesday", + * 4 -> "Wednesday", + * 5 -> "Thursday", + * 6 -> "Friday" + * ) + * // TreeMap(2 -> Monday, 3 -> Tuesday, 4 -> Wednesday, 5 -> Thursday, 6 -> Friday) + * + * val days = weekdays ++ List(1 -> "Sunday", 7 -> "Saturday") + * // TreeMap(1 -> Sunday, 2 -> Monday, 3 -> Tuesday, 4 -> Wednesday, 5 -> Thursday, 6 -> Friday, 7 -> Saturday) + * + * val day3 = days.get(3) // Some("Tuesday") + * + * val rangeOfDays = days.range(2, 5) // TreeMap(2 -> Monday, 3 -> Tuesday, 4 -> Wednesday) + * + * val daysUntil2 = days.rangeUntil(2) // TreeMap(1 -> Sunday) + * val daysTo2 = days.rangeTo(2) // TreeMap(1 -> Sunday, 2 -> Monday) + * val daysAfter5 = days.rangeFrom(5) // TreeMap(5 -> Thursday, 6 -> Friday, 7 -> Saturday) + * }}} + * + * @tparam K the type of the keys contained in this tree map. + * @tparam V the type of the values associated with the keys. + */ +trait SortedMap[K, +V] + extends Map[K, V] + with collection.SortedMap[K, V] + with SortedMapOps[K, V, SortedMap, SortedMap[K, V]] + with SortedMapFactoryDefaults[K, V, SortedMap, Iterable, Map] { + + override def unsorted: Map[K, V] = this + + override def sortedMapFactory: SortedMapFactory[SortedMap] = SortedMap + + /** The same map with a given default function. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefault`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d the function mapping keys to values, used for non-present keys + * @return a wrapper of the map with a default value + */ + override def withDefault[V1 >: V](d: K => V1): SortedMap[K, V1] = new SortedMap.WithDefault[K, V1](this, d) + + /** The same map with a given default value. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefaultValue`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d default value used for non-present keys + * @return a wrapper of the map with a default value + */ + override def withDefaultValue[V1 >: V](d: V1): SortedMap[K, V1] = new SortedMap.WithDefault[K, V1](this, _ => d) +} + +trait SortedMapOps[K, +V, +CC[X, +Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]] + extends MapOps[K, V, Map, C] with collection.SortedMapOps[K, V, CC, C] { self => + + protected def coll: C with CC[K, V] + + def unsorted: Map[K, V] + + override def keySet: SortedSet[K] = new ImmutableKeySortedSet + + /** The implementation class of the set returned by `keySet` */ + protected class ImmutableKeySortedSet extends AbstractSet[K] with SortedSet[K] with GenKeySet with GenKeySortedSet { + def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = { + val map = self.rangeImpl(from, until) + new map.ImmutableKeySortedSet + } + def incl(elem: K): SortedSet[K] = fromSpecific(this).incl(elem) + def excl(elem: K): SortedSet[K] = fromSpecific(this).excl(elem) + } + + // We override these methods to fix their return type (which would be `Map` otherwise) + def updated[V1 >: V](key: K, value: V1): CC[K, V1] + @`inline` final override def +[V1 >: V](kv: (K, V1)): CC[K, V1] = updated(kv._1, kv._2) + override def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): CC[K, V1] = { + // Implementation has been copied from `MapOps` + val previousValue = this.get(key) + remappingFunction(previousValue) match { + case None => previousValue.fold(coll)(_ => this.removed(key).coll) + case Some(nextValue) => + if (previousValue.exists(_.asInstanceOf[AnyRef] eq nextValue.asInstanceOf[AnyRef])) coll + else coll.updated(key, nextValue) + } + } + override def transform[W](f: (K, V) => W): CC[K, W] = map({ case (k, v) => (k, f(k, v)) })(ordering) +} + +trait StrictOptimizedSortedMapOps[K, +V, +CC[X, +Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]] + extends SortedMapOps[K, V, CC, C] + with collection.StrictOptimizedSortedMapOps[K, V, CC, C] + with StrictOptimizedMapOps[K, V, Map, C] { + + override def concat[V2 >: V](xs: collection.IterableOnce[(K, V2)]): CC[K, V2] = { + var result: CC[K, V2] = coll + val it = xs.iterator + while (it.hasNext) result = result + it.next() + result + } +} + +@SerialVersionUID(3L) +object SortedMap extends SortedMapFactory.Delegate[SortedMap](TreeMap) { + + override def from[K: Ordering, V](it: IterableOnce[(K, V)]): SortedMap[K, V] = it match { + case sm: SortedMap[K, V] if Ordering[K] == sm.ordering => sm + case _ => super.from(it) + } + + final class WithDefault[K, +V](underlying: SortedMap[K, V], defaultValue: K => V) + extends Map.WithDefault[K, V](underlying, defaultValue) + with SortedMap[K, V] + with SortedMapOps[K, V, SortedMap, WithDefault[K, V]] with Serializable { + + implicit def ordering: Ordering[K] = underlying.ordering + + override def sortedMapFactory: SortedMapFactory[SortedMap] = underlying.sortedMapFactory + + def iteratorFrom(start: K): scala.collection.Iterator[(K, V)] = underlying.iteratorFrom(start) + + def keysIteratorFrom(start: K): scala.collection.Iterator[K] = underlying.keysIteratorFrom(start) + + def rangeImpl(from: Option[K], until: Option[K]): WithDefault[K, V] = + new WithDefault[K, V](underlying.rangeImpl(from, until), defaultValue) + + // Need to override following methods to match type signatures of `SortedMap.WithDefault` + // for operations preserving default value + + override def updated[V1 >: V](key: K, value: V1): WithDefault[K, V1] = + new WithDefault[K, V1](underlying.updated(key, value), defaultValue) + + override def concat [V2 >: V](xs: collection.IterableOnce[(K, V2)]): WithDefault[K, V2] = + new WithDefault( underlying.concat(xs) , defaultValue) + + override def removed(key: K): WithDefault[K, V] = new WithDefault[K, V](underlying.removed(key), defaultValue) + + override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) + + override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)] @uncheckedVariance): WithDefault[K, V] = + new WithDefault[K, V](sortedMapFactory.from(coll), defaultValue) + + override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] @uncheckedVariance = + SortedMap.newBuilder.mapResult((p: SortedMap[K, V]) => new WithDefault[K, V](p, defaultValue)) + } +} diff --git a/library/src/scala/collection/immutable/SortedSet.scala b/library/src/scala/collection/immutable/SortedSet.scala new file mode 100644 index 000000000000..2eda00ac6b2f --- /dev/null +++ b/library/src/scala/collection/immutable/SortedSet.scala @@ -0,0 +1,57 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +/** Base trait for sorted sets */ +trait SortedSet[A] + extends Set[A] + with collection.SortedSet[A] + with SortedSetOps[A, SortedSet, SortedSet[A]] + with SortedSetFactoryDefaults[A, SortedSet, Set] { + + override def unsorted: Set[A] = this + + override def sortedIterableFactory: SortedIterableFactory[SortedSet] = SortedSet +} + +/** + * @define coll immutable sorted set + * @define Coll `immutable.SortedSet` + */ +trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]] + extends SetOps[A, Set, C] + with collection.SortedSetOps[A, CC, C] { + + def unsorted: Set[A] +} + +trait StrictOptimizedSortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]] + extends SortedSetOps[A, CC, C] + with collection.StrictOptimizedSortedSetOps[A, CC, C] + with StrictOptimizedSetOps[A, Set, C] { +} + +/** + * $factoryInfo + * @define coll immutable sorted set + * @define Coll `immutable.SortedSet` + */ +@SerialVersionUID(3L) +object SortedSet extends SortedIterableFactory.Delegate[SortedSet](TreeSet) { + override def from[E: Ordering](it: IterableOnce[E]): SortedSet[E] = it match { + case ss: SortedSet[E] if Ordering[E] == ss.ordering => ss + case _ => super.from(it) + } +} diff --git a/library/src/scala/collection/immutable/Stream.scala b/library/src/scala/collection/immutable/Stream.scala new file mode 100644 index 000000000000..898a988735c6 --- /dev/null +++ b/library/src/scala/collection/immutable/Stream.scala @@ -0,0 +1,569 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import java.io.{ObjectInputStream, ObjectOutputStream} +import java.lang.{StringBuilder => JStringBuilder} + +import scala.annotation.tailrec +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.generic.SerializeEnd +import scala.collection.mutable.{ArrayBuffer, StringBuilder} +import scala.language.implicitConversions +import Stream.cons + +@deprecated("Use LazyList (which is fully lazy) instead of Stream (which has a lazy tail only)", "2.13.0") +@SerialVersionUID(3L) +sealed abstract class Stream[+A] extends AbstractSeq[A] + with LinearSeq[A] + with LinearSeqOps[A, Stream, Stream[A]] + with IterableFactoryDefaults[A, Stream] + with Serializable { + def tail: Stream[A] + + /** Forces evaluation of the whole `Stream` and returns it. + * + * @note Often we use `Stream`s to represent an infinite set or series. If + * that's the case for your particular `Stream` then this function will never + * return and will probably crash the VM with an `OutOfMemory` exception. + * This function will not hang on a finite cycle, however. + * + * @return The fully realized `Stream`. + */ + def force: this.type + + override def iterableFactory: SeqFactory[Stream] = Stream + + override protected[this] def className: String = "Stream" + + /** Apply the given function `f` to each element of this linear sequence + * (while respecting the order of the elements). + * + * @param f The treatment to apply to each element. + * @note Overridden here as final to trigger tail-call optimization, which + * replaces 'this' with 'tail' at each iteration. This is absolutely + * necessary for allowing the GC to collect the underlying Stream as elements + * are consumed. + * @note This function will force the realization of the entire Stream + * unless the `f` throws an exception. + */ + @tailrec + override final def foreach[U](f: A => U): Unit = { + if (!this.isEmpty) { + f(head) + tail.foreach(f) + } + } + + @tailrec + override final def find(p: A => Boolean): Option[A] = { + if(isEmpty) None + else if(p(head)) Some(head) + else tail.find(p) + } + + override def take(n: Int): Stream[A] = { + if (n <= 0 || isEmpty) Stream.empty + else if (n == 1) new Stream.Cons(head, Stream.empty) + else new Stream.Cons(head, tail.take(n - 1)) + } + + /** Stream specialization of foldLeft which allows GC to collect along the + * way. + * + * @tparam B The type of value being accumulated. + * @param z The initial value seeded into the function `op`. + * @param op The operation to perform on successive elements of the `Stream`. + * @return The accumulated value from successive applications of `op`. + */ + @tailrec + override final def foldLeft[B](z: B)(op: (B, A) => B): B = { + if (this.isEmpty) z + else tail.foldLeft(op(z, head))(op) + } + + /** The stream resulting from the concatenation of this stream with the argument stream. + * @param rest The collection that gets appended to this stream + * @return The stream containing elements of this stream and the iterable object. + */ + @deprecated("The `append` operation has been renamed `lazyAppendedAll`", "2.13.0") + @inline final def append[B >: A](rest: => IterableOnce[B]): Stream[B] = lazyAppendedAll(rest) + + protected[this] def writeReplace(): AnyRef = + if(nonEmpty && tailDefined) new Stream.SerializationProxy[A](this) else this + + /** Prints elements of this stream one by one, separated by commas. */ + @deprecated(message = """Use print(stream.force.mkString(", ")) instead""", since = "2.13.0") + @inline def print(): Unit = Console.print(this.force.mkString(", ")) + + /** Prints elements of this stream one by one, separated by `sep`. + * @param sep The separator string printed between consecutive elements. + */ + @deprecated(message = "Use print(stream.force.mkString(sep)) instead", since = "2.13.0") + @inline def print(sep: String): Unit = Console.print(this.force.mkString(sep)) + + /** The stream resulting from the concatenation of this stream with the argument stream. + * + * @param suffix The collection that gets appended to this stream + * @return The stream containing elements of this stream and the iterable object. + */ + def lazyAppendedAll[B >: A](suffix: => collection.IterableOnce[B]): Stream[B] = + if (isEmpty) iterableFactory.from(suffix) else cons[B](head, tail.lazyAppendedAll(suffix)) + + override def scanLeft[B](z: B)(op: (B, A) => B): Stream[B] = + if (isEmpty) z +: iterableFactory.empty + else cons(z, tail.scanLeft(op(z, head))(op)) + + /** Stream specialization of reduceLeft which allows GC to collect + * along the way. + * + * @tparam B The type of value being accumulated. + * @param f The operation to perform on successive elements of the `Stream`. + * @return The accumulated value from successive applications of `f`. + */ + override final def reduceLeft[B >: A](f: (B, A) => B): B = { + if (this.isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") + else { + var reducedRes: B = this.head + var left: Stream[A] = this.tail + while (!left.isEmpty) { + reducedRes = f(reducedRes, left.head) + left = left.tail + } + reducedRes + } + } + + override def partition(p: A => Boolean): (Stream[A], Stream[A]) = (filter(p(_)), filterNot(p(_))) + + override def filter(pred: A => Boolean): Stream[A] = filterImpl(pred, isFlipped = false) + + override def filterNot(pred: A => Boolean): Stream[A] = filterImpl(pred, isFlipped = true) + + private[immutable] def filterImpl(p: A => Boolean, isFlipped: Boolean): Stream[A] = { + // optimization: drop leading prefix of elems for which f returns false + // var rest = this dropWhile (!p(_)) - forget DRY principle - GC can't collect otherwise + var rest: Stream[A] = coll + while (rest.nonEmpty && p(rest.head) == isFlipped) rest = rest.tail + // private utility func to avoid `this` on stack (would be needed for the lazy arg) + if (rest.nonEmpty) Stream.filteredTail(rest, p, isFlipped) + else iterableFactory.empty + } + + /** A `collection.WithFilter` which allows GC of the head of stream during processing */ + override final def withFilter(p: A => Boolean): collection.WithFilter[A, Stream] = + Stream.withFilter(coll, p) + + override final def prepended[B >: A](elem: B): Stream[B] = cons(elem, coll) + + override final def map[B](f: A => B): Stream[B] = + if (isEmpty) iterableFactory.empty + else cons(f(head), tail.map(f)) + + @tailrec override final def collect[B](pf: PartialFunction[A, B]): Stream[B] = + if(isEmpty) Stream.empty + else { + var newHead: B = null.asInstanceOf[B] + val runWith = pf.runWith((b: B) => newHead = b) + if(runWith(head)) Stream.collectedTail(newHead, this, pf) + else tail.collect(pf) + } + + @tailrec override final def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = + if(isEmpty) None + else { + var newHead: B = null.asInstanceOf[B] + val runWith = pf.runWith((b: B) => newHead = b) + if(runWith(head)) Some(newHead) + else tail.collectFirst(pf) + } + + // optimisations are not for speed, but for functionality + // see tickets #153, #498, #2147, and corresponding tests in run/ (as well as run/stream_flatmap_odds.scala) + override final def flatMap[B](f: A => IterableOnce[B]): Stream[B] = + if (isEmpty) iterableFactory.empty + else { + // establish !prefix.isEmpty || nonEmptyPrefix.isEmpty + var nonEmptyPrefix: Stream[A] = coll + var prefix = iterableFactory.from(f(nonEmptyPrefix.head)) + while (!nonEmptyPrefix.isEmpty && prefix.isEmpty) { + nonEmptyPrefix = nonEmptyPrefix.tail + if(!nonEmptyPrefix.isEmpty) + prefix = iterableFactory.from(f(nonEmptyPrefix.head)) + } + + if (nonEmptyPrefix.isEmpty) iterableFactory.empty + else prefix.lazyAppendedAll(nonEmptyPrefix.tail.flatMap(f)) + } + + override final def zip[B](that: collection.IterableOnce[B]): Stream[(A, B)] = + if (this.isEmpty || that.isEmpty) iterableFactory.empty + else { + val thatIterable = that match { + case that: collection.Iterable[B] => that + case _ => LazyList.from(that) + } + cons[(A, B)]((this.head, thatIterable.head), this.tail.zip(thatIterable.tail)) + } + + override final def zipWithIndex: Stream[(A, Int)] = this.zip(LazyList.from(0)) + + protected def tailDefined: Boolean + + /** Appends all elements of this $coll to a string builder using start, end, and separator strings. + * The written text begins with the string `start` and ends with the string `end`. + * Inside, the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * Undefined elements are represented with `"_"`, an undefined tail is represented with `"<not computed>"`, + * and cycles are represented with `"<cycle>"`. + * + * @param sb the string builder to which elements are appended. + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return the string builder `b` to which elements were appended. + */ + override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = { + force + addStringNoForce(sb.underlying, start, sep, end) + sb + } + + private[this] def addStringNoForce(b: JStringBuilder, start: String, sep: String, end: String): b.type = { + b.append(start) + if (nonEmpty) { + b.append(head) + var cursor = this + def appendCursorElement(): Unit = b.append(sep).append(cursor.head) + if (tailDefined) { // If tailDefined, also !isEmpty + var scout = tail + if (cursor ne scout) { + cursor = scout + if (scout.tailDefined) { + scout = scout.tail + // Use 2x 1x iterator trick for cycle detection; slow iterator can add strings + while ((cursor ne scout) && scout.tailDefined) { + appendCursorElement() + cursor = cursor.tail + scout = scout.tail + if (scout.tailDefined) scout = scout.tail + } + } + } + if (!scout.tailDefined) { // Not a cycle, scout hit an end + while (cursor ne scout) { + appendCursorElement() + cursor = cursor.tail + } + if (cursor.nonEmpty) { + appendCursorElement() + } + } + else { + // Cycle. + // If we have a prefix of length P followed by a cycle of length C, + // the scout will be at position (P%C) in the cycle when the cursor + // enters it at P. They'll then collide when the scout advances another + // C - (P%C) ahead of the cursor. + // If we run the scout P farther, then it will be at the start of + // the cycle: (C - (P%C) + (P%C)) == C == 0. So if another runner + // starts at the beginning of the prefix, they'll collide exactly at + // the start of the loop. + var runner = this + var k = 0 + while (runner ne scout) { + runner = runner.tail + scout = scout.tail + k += 1 + } + // Now runner and scout are at the beginning of the cycle. Advance + // cursor, adding to string, until it hits; then we'll have covered + // everything once. If cursor is already at beginning, we'd better + // advance one first unless runner didn't go anywhere (in which case + // we've already looped once). + if ((cursor eq scout) && (k > 0)) { + appendCursorElement() + cursor = cursor.tail + } + while (cursor ne scout) { + appendCursorElement() + cursor = cursor.tail + } + } + } + if (cursor.nonEmpty) { + // Either undefined or cyclic; we can check with tailDefined + if (!cursor.tailDefined) b.append(sep).append("") + else b.append(sep).append("") + } + } + b.append(end) + b + } + + /** + * @return a string representation of this collection. Undefined elements are + * represented with `"_"`, an undefined tail is represented with `"<not computed>"`, + * and cycles are represented with `"<cycle>"` + * + * Examples: + * + * - `"Stream(_, <not computed>)"`, a non-empty stream, whose head has not been + * evaluated ; + * - `"Stream(_, 1, _, <not computed>)"`, a stream with at least three elements, + * the second one has been evaluated ; + * - `"Stream(1, 2, 3, <cycle>)"`, an infinite stream that contains + * a cycle at the fourth element. + */ + override def toString = addStringNoForce(new JStringBuilder(className), "(", ", ", ")").toString + + @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0") + override def hasDefiniteSize: Boolean = isEmpty || { + if (!tailDefined) false + else { + // Two-iterator trick (2x & 1x speed) for cycle detection. + var those = this + var these = tail + while (those ne these) { + if (these.isEmpty) return true + if (!these.tailDefined) return false + these = these.tail + if (these.isEmpty) return true + if (!these.tailDefined) return false + these = these.tail + if (those eq these) return false + those = those.tail + } + false // Cycle detected + } + } +} + +@deprecated("Use LazyList (which is fully lazy) instead of Stream (which has a lazy tail only)", "2.13.0") +@SerialVersionUID(3L) +object Stream extends SeqFactory[Stream] { + + /* !!! #11997 This `object cons` must be defined lexically *before* `class Cons` below. + * Otherwise it prevents Scala.js from building on Windows. + */ + /** An alternative way of building and matching Streams using Stream.cons(hd, tl). + */ + object cons { + /** A stream consisting of a given first element and remaining elements + * @param hd The first element of the result stream + * @param tl The remaining elements of the result stream + */ + def apply[A](hd: A, tl: => Stream[A]): Stream[A] = new Cons(hd, tl) + + /** Maps a stream to its head and tail */ + def unapply[A](xs: Stream[A]): Option[(A, Stream[A])] = #::.unapply(xs) + } + + //@SerialVersionUID(3L) //TODO Putting an annotation on Stream.empty causes a cyclic dependency in unpickling + object Empty extends Stream[Nothing] { + override def isEmpty: Boolean = true + override def head: Nothing = throw new NoSuchElementException("head of empty stream") + override def tail: Stream[Nothing] = throw new UnsupportedOperationException("tail of empty stream") + /** Forces evaluation of the whole `Stream` and returns it. + * + * @note Often we use `Stream`s to represent an infinite set or series. If + * that's the case for your particular `Stream` then this function will never + * return and will probably crash the VM with an `OutOfMemory` exception. + * This function will not hang on a finite cycle, however. + * + * @return The fully realized `Stream`. + */ + def force: this.type = this + override def knownSize: Int = 0 + protected def tailDefined: Boolean = false + } + + @SerialVersionUID(3L) + final class Cons[A](override val head: A, tl: => Stream[A]) extends Stream[A] { + override def isEmpty: Boolean = false + @volatile private[this] var tlVal: Stream[A] = _ + @volatile private[this] var tlGen = () => tl + protected def tailDefined: Boolean = tlGen eq null + override def tail: Stream[A] = { + if (!tailDefined) + synchronized { + if (!tailDefined) { + tlVal = tlGen() + tlGen = null + } + } + tlVal + } + + /** Forces evaluation of the whole `Stream` and returns it. + * + * @note Often we use `Stream`s to represent an infinite set or series. If + * that's the case for your particular `Stream` then this function will never + * return and will probably crash the VM with an `OutOfMemory` exception. + * This function will not hang on a finite cycle, however. + * + * @return The fully realized `Stream`. + */ + def force: this.type = { + // Use standard 2x 1x iterator trick for cycle detection ("those" is slow one) + var these, those: Stream[A] = this + if (!these.isEmpty) these = these.tail + while (those ne these) { + if (these.isEmpty) return this + these = these.tail + if (these.isEmpty) return this + these = these.tail + if (these eq those) return this + those = those.tail + } + this + } + + } + + implicit def toDeferrer[A](l: => Stream[A]): Deferrer[A] = new Deferrer[A](() => l) + + final class Deferrer[A] private[Stream] (private val l: () => Stream[A]) extends AnyVal { + /** Construct a Stream consisting of a given first element followed by elements + * from another Stream. + */ + def #:: [B >: A](elem: B): Stream[B] = new Cons(elem, l()) + /** Construct a Stream consisting of the concatenation of the given Stream and + * another Stream. + */ + def #:::[B >: A](prefix: Stream[B]): Stream[B] = prefix lazyAppendedAll l() + } + + object #:: { + def unapply[A](s: Stream[A]): Option[(A, Stream[A])] = + if (s.nonEmpty) Some((s.head, s.tail)) else None + } + + def from[A](coll: collection.IterableOnce[A]): Stream[A] = coll match { + case coll: Stream[A] => coll + case _ => fromIterator(coll.iterator) + } + + /** + * @return A `Stream[A]` that gets its elements from the given `Iterator`. + * + * @param it Source iterator + * @tparam A type of elements + */ + // Note that the resulting `Stream` will be effectively iterable more than once because + // `Stream` memoizes its elements + def fromIterator[A](it: Iterator[A]): Stream[A] = + if (it.hasNext) { + new Stream.Cons(it.next(), fromIterator(it)) + } else Stream.Empty + + def empty[A]: Stream[A] = Empty + + override def newBuilder[A]: mutable.Builder[A, Stream[A]] = ArrayBuffer.newBuilder[A].mapResult(array => from(array)) + + private[immutable] def withFilter[A](l: Stream[A] @uncheckedVariance, p: A => Boolean): collection.WithFilter[A, Stream] = + new WithFilter[A](l, p) + + private[this] final class WithFilter[A](l: Stream[A] @uncheckedVariance, p: A => Boolean) extends collection.WithFilter[A, Stream] { + private[this] var s = l // set to null to allow GC after filtered + private[this] lazy val filtered: Stream[A] = { val f = s.filter(p); s = null.asInstanceOf[Stream[A]]; f } // don't set to null if throw during filter + def map[B](f: A => B): Stream[B] = filtered.map(f) + def flatMap[B](f: A => IterableOnce[B]): Stream[B] = filtered.flatMap(f) + def foreach[U](f: A => U): Unit = filtered.foreach(f) + def withFilter(q: A => Boolean): collection.WithFilter[A, Stream] = new WithFilter(filtered, q) + } + + /** An infinite Stream that repeatedly applies a given function to a start value. + * + * @param start the start value of the Stream + * @param f the function that's repeatedly applied + * @return the Stream returning the infinite sequence of values `start, f(start), f(f(start)), ...` + */ + def iterate[A](start: A)(f: A => A): Stream[A] = { + cons(start, iterate(f(start))(f)) + } + + /** + * Create an infinite Stream starting at `start` and incrementing by + * step `step`. + * + * @param start the start value of the Stream + * @param step the increment value of the Stream + * @return the Stream starting at value `start`. + */ + def from(start: Int, step: Int): Stream[Int] = + cons(start, from(start + step, step)) + + /** + * Create an infinite Stream starting at `start` and incrementing by `1`. + * + * @param start the start value of the Stream + * @return the Stream starting at value `start`. + */ + def from(start: Int): Stream[Int] = from(start, 1) + + /** + * Create an infinite Stream containing the given element expression (which + * is computed for each occurrence). + * + * @param elem the element composing the resulting Stream + * @return the Stream containing an infinite number of elem + */ + def continually[A](elem: => A): Stream[A] = cons(elem, continually(elem)) + + + private[Stream] def filteredTail[A](stream: Stream[A] @uncheckedVariance, p: A => Boolean, isFlipped: Boolean) = { + cons(stream.head, stream.tail.filterImpl(p, isFlipped)) + } + + private[Stream] def collectedTail[A, B](head: B, stream: Stream[A] @uncheckedVariance, pf: PartialFunction[A, B]) = { + cons(head, stream.tail.collect(pf)) + } + + /** This serialization proxy is used for Streams which start with a sequence of evaluated cons cells. + * The forced sequence is serialized in a compact, sequential format, followed by the unevaluated tail, which uses + * standard Java serialization to store the complete structure of unevaluated thunks. This allows the serialization + * of long evaluated streams without exhausting the stack through recursive serialization of cons cells. + */ + @SerialVersionUID(3L) + class SerializationProxy[A](@transient protected var coll: Stream[A]) extends Serializable { + + private[this] def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + var these = coll + while(these.nonEmpty && these.tailDefined) { + out.writeObject(these.head) + these = these.tail + } + out.writeObject(SerializeEnd) + out.writeObject(these) + } + + private[this] def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val init = new ArrayBuffer[A] + var initRead = false + while (!initRead) in.readObject match { + case SerializeEnd => initRead = true + case a => init += a.asInstanceOf[A] + } + val tail = in.readObject().asInstanceOf[Stream[A]] + coll = (init ++: tail) + } + + protected[this] def readResolve(): Any = coll + } +} diff --git a/library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala b/library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala new file mode 100644 index 000000000000..90b803d54e70 --- /dev/null +++ b/library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala @@ -0,0 +1,84 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.collection.generic.CommonErrors + +/** Trait that overrides operations to take advantage of strict builders. + */ +trait StrictOptimizedSeqOps[+A, +CC[_], +C] + extends Any + with SeqOps[A, CC, C] + with collection.StrictOptimizedSeqOps[A, CC, C] + with StrictOptimizedIterableOps[A, CC, C] { + + override def distinctBy[B](f: A => B): C = { + if (lengthCompare(1) <= 0) coll + else { + val builder = newSpecificBuilder + val seen = mutable.HashSet.empty[B] + val it = this.iterator + var different = false + while (it.hasNext) { + val next = it.next() + if (seen.add(f(next))) builder += next else different = true + } + if (different) builder.result() else coll + } + } + + override def updated[B >: A](index: Int, elem: B): CC[B] = { + if (index < 0) + throw ( + if (knownSize >= 0) CommonErrors.indexOutOfBounds(index = index, max = knownSize) + else CommonErrors.indexOutOfBounds(index = index) + ) + val b = iterableFactory.newBuilder[B] + b.sizeHint(this) + var i = 0 + val it = iterator + while (i < index && it.hasNext) { + b += it.next() + i += 1 + } + if (!it.hasNext) + throw CommonErrors.indexOutOfBounds(index = index, max = i - 1) + b += elem + it.next() + while (it.hasNext) b += it.next() + b.result() + } + + override def patch[B >: A](from: Int, other: IterableOnce[B], replaced: Int): CC[B] = { + val b = iterableFactory.newBuilder[B] + var i = 0 + val it = iterator + while (i < from && it.hasNext) { + b += it.next() + i += 1 + } + b ++= other + i = replaced + while (i > 0 && it.hasNext) { + it.next() + i -= 1 + } + while (it.hasNext) b += it.next() + b.result() + } + + override def sorted[B >: A](implicit ord: Ordering[B]): C = super.sorted(ord) + +} diff --git a/library/src/scala/collection/immutable/TreeMap.scala b/library/src/scala/collection/immutable/TreeMap.scala new file mode 100644 index 000000000000..ff836499e779 --- /dev/null +++ b/library/src/scala/collection/immutable/TreeMap.scala @@ -0,0 +1,370 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.tailrec +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializable +import scala.collection.immutable.{RedBlackTree => RB} +import scala.collection.mutable.ReusableBuilder +import scala.runtime.AbstractFunction2 + +/** An immutable SortedMap whose values are stored in a red-black tree. + * + * This class is optimal when range queries will be performed, + * or when traversal in order of an ordering is desired. + * If you only need key lookups, and don't care in which order key-values + * are traversed in, consider using * [[scala.collection.immutable.HashMap]], + * which will generally have better performance. If you need insertion order, + * consider a * [[scala.collection.immutable.SeqMap]], which does not need to + * have an ordering supplied. + * + * @example {{{ + * import scala.collection.immutable.TreeMap + * + * // Make a TreeMap via the companion object factory + * val weekdays = TreeMap( + * 2 -> "Monday", + * 3 -> "Tuesday", + * 4 -> "Wednesday", + * 5 -> "Thursday", + * 6 -> "Friday" + * ) + * // TreeMap(2 -> Monday, 3 -> Tuesday, 4 -> Wednesday, 5 -> Thursday, 6 -> Friday) + * + * val days = weekdays ++ List(1 -> "Sunday", 7 -> "Saturday") + * // TreeMap(1 -> Sunday, 2 -> Monday, 3 -> Tuesday, 4 -> Wednesday, 5 -> Thursday, 6 -> Friday, 7 -> Saturday) + * + * val day3 = days.get(3) // Some("Tuesday") + * + * val rangeOfDays = days.range(2, 5) // TreeMap(2 -> Monday, 3 -> Tuesday, 4 -> Wednesday) + * + * val daysUntil2 = days.rangeUntil(2) // TreeMap(1 -> Sunday) + * val daysTo2 = days.rangeTo(2) // TreeMap(1 -> Sunday, 2 -> Monday) + * val daysAfter5 = days.rangeFrom(5) // TreeMap(5 -> Thursday, 6 -> Friday, 7 -> Saturday) + * }}} + * + * @tparam K the type of the keys contained in this tree map. + * @tparam V the type of the values associated with the keys. + * @param ordering the implicit ordering used to compare objects of type `A`. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] + * section on `Red-Black Trees` for more information. + * + * @define Coll immutable.TreeMap + * @define coll immutable tree map + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V])(implicit val ordering: Ordering[K]) + extends AbstractMap[K, V] + with SortedMap[K, V] + with StrictOptimizedSortedMapOps[K, V, TreeMap, TreeMap[K, V]] + with SortedMapFactoryDefaults[K, V, TreeMap, Iterable, Map] + with DefaultSerializable { + + def this()(implicit ordering: Ordering[K]) = this(null)(ordering) + private[immutable] def tree0: RB.Tree[K, V] = tree + + private[this] def newMapOrSelf[V1 >: V](t: RB.Tree[K, V1]): TreeMap[K, V1] = if(t eq tree) this else new TreeMap[K, V1](t) + + override def sortedMapFactory: SortedMapFactory[TreeMap] = TreeMap + + def iterator: Iterator[(K, V)] = RB.iterator(tree) + + def keysIteratorFrom(start: K): Iterator[K] = RB.keysIterator(tree, Some(start)) + + override def keySet: TreeSet[K] = new TreeSet(tree)(ordering) + + def iteratorFrom(start: K): Iterator[(K, V)] = RB.iterator(tree, Some(start)) + + override def valuesIteratorFrom(start: K): Iterator[V] = RB.valuesIterator(tree, Some(start)) + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[(K, V), S]): S with EfficientSplit = + shape.parUnbox( + scala.collection.convert.impl.AnyBinaryTreeStepper.from[(K, V), RB.Tree[K, V]]( + size, tree, _.left, _.right, x => (x.key, x.value) + ) + ) + + override def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S with EfficientSplit = { + import scala.collection.convert.impl._ + type T = RB.Tree[K, V] + val s = shape.shape match { + case StepperShape.IntShape => IntBinaryTreeStepper.from[T] (size, tree, _.left, _.right, _.key.asInstanceOf[Int]) + case StepperShape.LongShape => LongBinaryTreeStepper.from[T] (size, tree, _.left, _.right, _.key.asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleBinaryTreeStepper.from[T](size, tree, _.left, _.right, _.key.asInstanceOf[Double]) + case _ => shape.parUnbox(AnyBinaryTreeStepper.from[K, T](size, tree, _.left, _.right, _.key)) + } + s.asInstanceOf[S with EfficientSplit] + } + + override def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S with EfficientSplit = { + import scala.collection.convert.impl._ + type T = RB.Tree[K, V] + val s = shape.shape match { + case StepperShape.IntShape => IntBinaryTreeStepper.from[T] (size, tree, _.left, _.right, _.value.asInstanceOf[Int]) + case StepperShape.LongShape => LongBinaryTreeStepper.from[T] (size, tree, _.left, _.right, _.value.asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleBinaryTreeStepper.from[T] (size, tree, _.left, _.right, _.value.asInstanceOf[Double]) + case _ => shape.parUnbox(AnyBinaryTreeStepper.from[V, T] (size, tree, _.left, _.right, _.value.asInstanceOf[V])) + } + s.asInstanceOf[S with EfficientSplit] + } + + def get(key: K): Option[V] = RB.get(tree, key) + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + val resultOrNull = RB.lookup(tree, key) + if (resultOrNull eq null) default + else resultOrNull.value + } + + def removed(key: K): TreeMap[K,V] = + newMapOrSelf(RB.delete(tree, key)) + + def updated[V1 >: V](key: K, value: V1): TreeMap[K, V1] = + newMapOrSelf(RB.update(tree, key, value, overwrite = true)) + + override def concat[V1 >: V](that: collection.IterableOnce[(K, V1)]): TreeMap[K, V1] = + newMapOrSelf(that match { + case tm: TreeMap[K, V] @unchecked if ordering == tm.ordering => + RB.union(tree, tm.tree) + case ls: LinearSeq[(K,V1)] => + if (ls.isEmpty) tree //to avoid the creation of the adder + else { + val adder = new Adder[V1] + adder.addAll(ls) + adder.finalTree + } + case _ => + val adder = new Adder[V1] + val it = that.iterator + while (it.hasNext) { + adder.apply(it.next()) + } + adder.finalTree + }) + + override def removedAll(keys: IterableOnce[K]): TreeMap[K, V] = keys match { + case ts: TreeSet[K] if ordering == ts.ordering => + newMapOrSelf(RB.difference(tree, ts.tree)) + case _ => super.removedAll(keys) + } + + /** A new TreeMap with the entry added is returned, + * assuming that key is not in the TreeMap. + * + * @tparam V1 type of the values of the new bindings, a supertype of `V` + * @param key the key to be inserted + * @param value the value to be associated with `key` + * @return a new $coll with the inserted binding, if it wasn't present in the map + */ + @deprecated("Use `updated` instead", "2.13.0") + def insert[V1 >: V](key: K, value: V1): TreeMap[K, V1] = { + assert(!RB.contains(tree, key)) + updated(key, value) + } + + def rangeImpl(from: Option[K], until: Option[K]): TreeMap[K, V] = newMapOrSelf(RB.rangeImpl(tree, from, until)) + + override def minAfter(key: K): Option[(K, V)] = RB.minAfter(tree, key) match { + case null => Option.empty + case x => Some((x.key, x.value)) + } + + override def maxBefore(key: K): Option[(K, V)] = RB.maxBefore(tree, key) match { + case null => Option.empty + case x => Some((x.key, x.value)) + } + + override def range(from: K, until: K): TreeMap[K,V] = newMapOrSelf(RB.range(tree, from, until)) + + override def foreach[U](f: ((K, V)) => U): Unit = RB.foreach(tree, f) + override def foreachEntry[U](f: (K, V) => U): Unit = RB.foreachEntry(tree, f) + override def size: Int = RB.count(tree) + override def knownSize: Int = size + + override def isEmpty = size == 0 + + override def firstKey: K = RB.smallest(tree).key + + override def lastKey: K = RB.greatest(tree).key + + override def head: (K, V) = { + val smallest = RB.smallest(tree) + (smallest.key, smallest.value) + } + + override def last: (K, V) = { + val greatest = RB.greatest(tree) + (greatest.key, greatest.value) + } + + override def tail: TreeMap[K, V] = new TreeMap(RB.tail(tree)) + + override def init: TreeMap[K, V] = new TreeMap(RB.init(tree)) + + override def drop(n: Int): TreeMap[K, V] = { + if (n <= 0) this + else if (n >= size) empty + else new TreeMap(RB.drop(tree, n)) + } + + override def take(n: Int): TreeMap[K, V] = { + if (n <= 0) empty + else if (n >= size) this + else new TreeMap(RB.take(tree, n)) + } + + override def slice(from: Int, until: Int) = { + if (until <= from) empty + else if (from <= 0) take(until) + else if (until >= size) drop(from) + else new TreeMap(RB.slice(tree, from, until)) + } + + override def dropRight(n: Int): TreeMap[K, V] = take(size - math.max(n, 0)) + + override def takeRight(n: Int): TreeMap[K, V] = drop(size - math.max(n, 0)) + + private[this] def countWhile(p: ((K, V)) => Boolean): Int = { + var result = 0 + val it = iterator + while (it.hasNext && p(it.next())) result += 1 + result + } + + override def dropWhile(p: ((K, V)) => Boolean): TreeMap[K, V] = drop(countWhile(p)) + + override def takeWhile(p: ((K, V)) => Boolean): TreeMap[K, V] = take(countWhile(p)) + + override def span(p: ((K, V)) => Boolean): (TreeMap[K, V], TreeMap[K, V]) = splitAt(countWhile(p)) + + override def filter(f: ((K, V)) => Boolean): TreeMap[K, V] = + newMapOrSelf(RB.filterEntries[K, V](tree, (k, v) => f((k, v)))) + + override def partition(p: ((K, V)) => Boolean): (TreeMap[K, V], TreeMap[K, V]) = { + val (l, r) = RB.partitionEntries[K, V](tree, (k, v) => p((k, v))) + (newMapOrSelf(l), newMapOrSelf(r)) + } + + override def transform[W](f: (K, V) => W): TreeMap[K, W] = { + val t2 = RB.transform[K, V, W](tree, f) + if(t2 eq tree) this.asInstanceOf[TreeMap[K, W]] + else new TreeMap(t2) + } + + private final class Adder[B1 >: V] + extends RB.MapHelper[K, B1] with Function1[(K, B1), Unit] { + private var currentMutableTree: RB.Tree[K,B1] = tree0 + def finalTree = beforePublish(currentMutableTree) + override def apply(kv: (K, B1)): Unit = { + currentMutableTree = mutableUpd(currentMutableTree, kv._1, kv._2) + } + @tailrec def addAll(ls: LinearSeq[(K, B1)]): Unit = { + if (!ls.isEmpty) { + val kv = ls.head + currentMutableTree = mutableUpd(currentMutableTree, kv._1, kv._2) + addAll(ls.tail) + } + } + } + override def equals(obj: Any): Boolean = obj match { + case that: TreeMap[K @unchecked, _] if ordering == that.ordering => RB.entriesEqual(tree, that.tree) + case _ => super.equals(obj) + } + + override protected[this] def className = "TreeMap" +} + +/** $factoryInfo + * @define Coll immutable.TreeMap + * @define coll immutable tree map + */ +@SerialVersionUID(3L) +object TreeMap extends SortedMapFactory[TreeMap] { + + def empty[K : Ordering, V]: TreeMap[K, V] = new TreeMap() + + def from[K, V](it: IterableOnce[(K, V)])(implicit ordering: Ordering[K]): TreeMap[K, V] = + it match { + case tm: TreeMap[K, V] if ordering == tm.ordering => tm + case sm: scala.collection.SortedMap[K, V] if ordering == sm.ordering => + new TreeMap[K, V](RB.fromOrderedEntries(sm.iterator, sm.size)) + case _ => + var t: RB.Tree[K, V] = null + val i = it.iterator + while (i.hasNext) { + val (k, v) = i.next() + t = RB.update(t, k, v, overwrite = true) + } + new TreeMap[K, V](t) + } + + def newBuilder[K, V](implicit ordering: Ordering[K]): ReusableBuilder[(K, V), TreeMap[K, V]] = new TreeMapBuilder[K, V] + + private class TreeMapBuilder[K, V](implicit ordering: Ordering[K]) + extends RB.MapHelper[K, V] + with ReusableBuilder[(K, V), TreeMap[K, V]] { + type Tree = RB.Tree[K, V] + private var tree:Tree = null + + def addOne(elem: (K, V)): this.type = { + tree = mutableUpd(tree, elem._1, elem._2) + this + } + private object adder extends AbstractFunction2[K, V, Unit] { + // we cache tree to avoid the outer access to tree + // in the hot path (apply) + private[this] var accumulator :Tree = null + def addForEach(hasForEach: collection.Map[K, V]): Unit = { + accumulator = tree + hasForEach.foreachEntry(this) + tree = accumulator + // be friendly to GC + accumulator = null + } + + override def apply(key: K, value: V): Unit = { + accumulator = mutableUpd(accumulator, key, value) + } + } + + override def addAll(xs: IterableOnce[(K, V)]): this.type = { + xs match { + // TODO consider writing a mutable-safe union for TreeSet/TreeMap builder ++= + // for the moment we have to force immutability before the union + // which will waste some time and space + // calling `beforePublish` makes `tree` immutable + case ts: TreeMap[K, V] if ts.ordering == ordering => + if (tree eq null) tree = ts.tree0 + else tree = RB.union(beforePublish(tree), ts.tree0) + case that: collection.Map[K, V] => + //add avoiding creation of tuples + adder.addForEach(that) + case _ => + super.addAll(xs) + } + this + } + + override def clear(): Unit = { + tree = null + } + + override def result(): TreeMap[K, V] = new TreeMap[K, V](beforePublish(tree)) + } +} diff --git a/library/src/scala/collection/immutable/TreeSeqMap.scala b/library/src/scala/collection/immutable/TreeSeqMap.scala new file mode 100644 index 000000000000..4eaa8487b6ff --- /dev/null +++ b/library/src/scala/collection/immutable/TreeSeqMap.scala @@ -0,0 +1,650 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.tailrec + +/** This class implements an immutable map that preserves order using + * a hash map for the key to value mapping to provide efficient lookup, + * and a tree for the ordering of the keys to provide efficient + * insertion/modification order traversal and destructuring. + * + * By default insertion order (`TreeSeqMap.OrderBy.Insertion`) + * is used, but modification order (`TreeSeqMap.OrderBy.Modification`) + * can be used instead if so specified at creation. + * + * The `orderingBy(orderBy: TreeSeqMap.OrderBy): TreeSeqMap[K, V]` method + * can be used to switch to the specified ordering for the returned map. + * + * A key can be manually refreshed (i.e. placed at the end) via the + * `refresh(key: K): TreeSeqMap[K, V]` method (regardless of the ordering in + * use). + * + * Internally, an ordinal counter is increased for each insertion/modification + * and then the current ordinal is used as key in the tree map. After 2^32^ + * insertions/modifications the entire map is copied (thus resetting the ordinal + * counter). + * + * @tparam K the type of the keys contained in this map. + * @tparam V the type of the values associated with the keys in this map. + * @define coll immutable tree seq map + * @define Coll `immutable.TreeSeqMap` + */ +final class TreeSeqMap[K, +V] private ( + private val ordering: TreeSeqMap.Ordering[K], + private val mapping: TreeSeqMap.Mapping[K, V], + private val ordinal: Int, + val orderedBy: TreeSeqMap.OrderBy) + extends AbstractMap[K, V] + with SeqMap[K, V] + with MapOps[K, V, TreeSeqMap, TreeSeqMap[K, V]] + with StrictOptimizedIterableOps[(K, V), Iterable, TreeSeqMap[K, V]] + with StrictOptimizedMapOps[K, V, TreeSeqMap, TreeSeqMap[K, V]] + with MapFactoryDefaults[K, V, TreeSeqMap, Iterable] { + + import TreeSeqMap._ + + override protected[this] def className: String = "TreeSeqMap" + + override def mapFactory: MapFactory[TreeSeqMap] = TreeSeqMap + + override val size = mapping.size + + override def knownSize: Int = size + + override def isEmpty = size == 0 + + /* + // This should have been overridden in 2.13.0 but wasn't so it will have to wait since it is not forwards compatible + // Now handled in inherited method from scala.collection.MapFactoryDefaults instead. + override def empty = TreeSeqMap.empty[K, V](orderedBy) + */ + + def orderingBy(orderBy: OrderBy): TreeSeqMap[K, V] = { + if (orderBy == this.orderedBy) this + else if (isEmpty) TreeSeqMap.empty(orderBy) + else new TreeSeqMap(ordering, mapping, ordinal, orderBy) + } + + def updated[V1 >: V](key: K, value: V1): TreeSeqMap[K, V1] = { + mapping.get(key) match { + case e if ordinal == -1 && (orderedBy == OrderBy.Modification || e.isEmpty) => + // Reinsert into fresh instance to restart ordinal counting, expensive but only done after 2^32 updates. + TreeSeqMap.empty[K, V1](orderedBy) ++ this + (key -> value) + case Some((o, _)) if orderedBy == OrderBy.Insertion => + new TreeSeqMap( + ordering.include(o, key), + mapping.updated[(Int, V1)](key, (o, value)), + ordinal, // Do not increment the ordinal since the key is already present, i.e. o <= ordinal. + orderedBy) + case Some((o, _)) => + val o1 = increment(ordinal) + new TreeSeqMap( + ordering.exclude(o).append(o1, key), + mapping.updated[(Int, V1)](key, (o1, value)), + o1, + orderedBy) + case None => + val o1 = increment(ordinal) + new TreeSeqMap( + ordering.append(o1, key), + mapping.updated[(Int, V1)](key, (o1, value)), + o1, + orderedBy) + } + } + + def removed(key: K): TreeSeqMap[K, V] = { + mapping.get(key) match { + case Some((o, _)) => + new TreeSeqMap( + ordering.exclude(o), + mapping.removed(key), + ordinal, + orderedBy) + case None => + this + } + } + + def refresh(key: K): TreeSeqMap[K, V] = { + mapping.get(key) match { + case Some((o, _)) => + val o1 = increment(ordinal) + new TreeSeqMap( + ordering.exclude(o).append(o1, key), + mapping, + o1, + orderedBy) + case None => + this + } + } + + def get(key: K): Option[V] = mapping.get(key).map(value) + + def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] { + private[this] val iter = ordering.iterator + + override def hasNext: Boolean = iter.hasNext + + override def next(): (K, V) = binding(iter.next()) + } + + override def keysIterator: Iterator[K] = new AbstractIterator[K] { + private[this] val iter = ordering.iterator + + override def hasNext: Boolean = iter.hasNext + + override def next(): K = iter.next() + } + + override def valuesIterator: Iterator[V] = new AbstractIterator[V] { + private[this] val iter = ordering.iterator + + override def hasNext: Boolean = iter.hasNext + + override def next(): V = value(binding(iter.next())) + } + + override def contains(key: K): Boolean = mapping.contains(key) + + override def head: (K, V) = binding(ordering.head) + + override def headOption = ordering.headOption.map(binding) + + override def last: (K, V) = binding(ordering.last) + + override def lastOption: Option[(K, V)] = ordering.lastOption.map(binding) + + override def tail: TreeSeqMap[K, V] = { + val (head, tail) = ordering.headTail + new TreeSeqMap(tail, mapping.removed(head), ordinal, orderedBy) + } + + override def init: TreeSeqMap[K, V] = { + val (init, last) = ordering.initLast + new TreeSeqMap(init, mapping.removed(last), ordinal, orderedBy) + } + + override def slice(from: Int, until: Int): TreeSeqMap[K, V] = { + val sz = size + if (sz == 0 || from >= until) TreeSeqMap.empty[K, V](orderedBy) + else { + val sz = size + val f = if (from >= 0) from else 0 + val u = if (until <= sz) until else sz + val l = u - f + if (l <= 0) TreeSeqMap.empty[K, V](orderedBy) + else if (l > sz / 2) { + // Remove front and rear incrementally if majority of elements are to be kept + val (front, rest) = ordering.splitAt(f) + val (ong, rear) = rest.splitAt(l) + var mng = this.mapping + val frontIter = front.iterator + while (frontIter.hasNext) { + mng = mng - frontIter.next() + } + val rearIter = rear.iterator + while (rearIter.hasNext) { + mng = mng - rearIter.next() + } + new TreeSeqMap(ong, mng, ordinal, orderedBy) + } else { + // Populate with builder otherwise + val bdr = newBuilder[K, V](orderedBy) + val iter = ordering.iterator + var i = 0 + while (i < f) { + iter.next() + i += 1 + } + while (i < u) { + val k = iter.next() + bdr.addOne((k, mapping(k)._2)) + i += 1 + } + bdr.result() + } + } + } + + override def map[K2, V2](f: ((K, V)) => (K2, V2)): TreeSeqMap[K2, V2] = { + val bdr = newBuilder[K2, V2](orderedBy) + val iter = ordering.iterator + while (iter.hasNext) { + val k = iter.next() + val (_, v) = mapping(k) + val (k2, v2) = f((k, v)) + bdr.addOne((k2, v2)) + } + bdr.result() + } + + override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): TreeSeqMap[K2, V2] = { + val bdr = newBuilder[K2, V2](orderedBy) + val iter = ordering.iterator + while (iter.hasNext) { + val k = iter.next() + val (_, v) = mapping(k) + val jter = f((k, v)).iterator + while (jter.hasNext) { + val (k2, v2) = jter.next() + bdr.addOne((k2, v2)) + } + } + bdr.result() + } + + override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): TreeSeqMap[K2, V2] = { + val bdr = newBuilder[K2, V2](orderedBy) + val iter = ordering.iterator + while (iter.hasNext) { + val k = iter.next() + val (_, v) = mapping(k) + pf.runWith({ case (k2, v2) => bdr.addOne((k2, v2)) })((k, v)) + } + bdr.result() + } + + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): TreeSeqMap[K, V2] = { + var ong: Ordering[K] = ordering + var mng: Mapping[K, V2] = mapping + var ord = increment(ordinal) + val iter = suffix.iterator + while (iter.hasNext) { + val (k, v2) = iter.next() + mng.get(k) match { + case Some((o, v)) => + if (orderedBy == OrderBy.Insertion && v != v2) mng = mng.updated(k, (o, v2)) + else if (orderedBy == OrderBy.Modification) { + mng = mng.updated(k, (ord, v2)) + ong = ong.exclude(o).append(ord, k) + ord = increment(ord) + } + case None => + mng = mng.updated(k, (ord, v2)) + ong = ong.append(ord, k) + ord = increment(ord) + } + } + new TreeSeqMap[K, V2](ong, mng, ord, orderedBy) + } + + @`inline` private[this] def value(p: (_, V)) = p._2 + @`inline` private[this] def binding(k: K) = mapping(k).copy(_1 = k) +} +object TreeSeqMap extends MapFactory[TreeSeqMap] { + sealed trait OrderBy + object OrderBy { + case object Insertion extends OrderBy + case object Modification extends OrderBy + } + + private val EmptyByInsertion = new TreeSeqMap[Nothing, Nothing](Ordering.empty, HashMap.empty, 0, OrderBy.Insertion) + private val EmptyByModification = new TreeSeqMap[Nothing, Nothing](Ordering.empty, HashMap.empty, 0, OrderBy.Modification) + val Empty = EmptyByInsertion + def empty[K, V]: TreeSeqMap[K, V] = empty(OrderBy.Insertion) + def empty[K, V](orderBy: OrderBy): TreeSeqMap[K, V] = { + if (orderBy == OrderBy.Modification) EmptyByModification + else EmptyByInsertion + }.asInstanceOf[TreeSeqMap[K, V]] + + def from[K, V](it: collection.IterableOnce[(K, V)]): TreeSeqMap[K, V] = + it match { + case om: TreeSeqMap[K, V] => om + case _ => (newBuilder[K, V] ++= it).result() + } + + @inline private def increment(ord: Int) = if (ord == Int.MaxValue) Int.MinValue else ord + 1 + + def newBuilder[K, V]: mutable.Builder[(K, V), TreeSeqMap[K, V]] = newBuilder(OrderBy.Insertion) + def newBuilder[K, V](orderedBy: OrderBy): mutable.Builder[(K, V), TreeSeqMap[K, V]] = new Builder[K, V](orderedBy) + + final class Builder[K, V](orderedBy: OrderBy) extends mutable.Builder[(K, V), TreeSeqMap[K, V]] { + private[this] val bdr = new MapBuilderImpl[K, (Int, V)] + private[this] var ong = Ordering.empty[K] + private[this] var ord = 0 + private[this] var aliased: TreeSeqMap[K, V] = _ + + override def addOne(elem: (K, V)): this.type = addOne(elem._1, elem._2) + def addOne(key: K, value: V): this.type = { + if (aliased ne null) { + aliased = aliased.updated(key, value) + } else { + bdr.getOrElse(key, null) match { + case (o, v) => + if (orderedBy == OrderBy.Insertion && v != value) bdr.addOne(key, (o, value)) + else if (orderedBy == OrderBy.Modification) { + bdr.addOne(key, (ord, value)) + ong = ong.exclude(o).appendInPlace(ord, key) + ord = increment(ord) + } + case null => + bdr.addOne(key, (ord, value)) + ong = ong.appendInPlace(ord, key) + ord = increment(ord) + } + } + this + } + + override def clear(): Unit = { + ong = Ordering.empty + ord = 0 + bdr.clear() + aliased = null + } + + override def result(): TreeSeqMap[K, V] = { + if (aliased eq null) { + aliased = new TreeSeqMap(ong, bdr.result(), ord, orderedBy) + } + aliased + } + } + + private type Mapping[K, +V] = Map[K, (Int, V)] + @annotation.unused + private val Mapping = Map + + /* The ordering implementation below is an adapted version of immutable.IntMap. */ + private[immutable] object Ordering { + import scala.collection.generic.BitOperations.Int.{Int => _, _} + + @inline private[immutable] def toBinaryString(i: Int): String = s"$i/${i.toBinaryString}" + + def empty[T] : Ordering[T] = Zero + + def apply[T](elems: (Int, T)*): Ordering[T] = + elems.foldLeft(empty[T])((x, y) => x.include(y._1, y._2)) + + // Iterator over a non-empty Ordering. + final class Iterator[+V](it: Ordering[V]) { + // Basically this uses a simple stack to emulate conversion over the tree. However + // because we know that Ints are at least 32 bits we can have at most 32 Bins and + // one Tip sitting on the tree at any point. Therefore we know the maximum stack + // depth is 33 + private[this] var index = 0 + private[this] val buffer = new Array[AnyRef](33) + + private[this] def pop = { + index -= 1 + buffer(index).asInstanceOf[Ordering[V]] + } + + private[this] def push[V2 >: V](x: Ordering[V2]): Unit = { + buffer(index) = x.asInstanceOf[AnyRef] + index += 1 + } + + if (it != Zero) push(it) + + def hasNext = index > 0 + @tailrec + def next(): V = + if (!hasNext) scala.collection.Iterator.empty.next() + else pop match { + case Bin(_,_, Tip(_, v), right) => + push(right) + v + case Bin(_, _, left, right) => + push(right) + push(left) + next() + case Tip(_, v) => v + // This should never happen. We don't allow Ordering.Zero in subtrees of the Ordering + // and don't return an Ordering.Iterator for Ordering.Zero. + case Zero => throw new IllegalStateException("empty subtree not allowed") + } + } + + object Iterator { + val Empty = new Iterator[Nothing](Ordering.empty[Nothing]) + def empty[V]: Iterator[V] = Empty.asInstanceOf[Iterator[V]] + } + + case object Zero extends Ordering[Nothing] { + // Important! Without this equals method in place, an infinite + // loop from Map.equals => size => pattern-match-on-Nil => equals + // develops. Case objects and custom equality don't mix without + // careful handling. + override def equals(that : Any): Boolean = that match { + case _: this.type => true + case _: Ordering[_] => false // The only empty Orderings are eq Nil + case _ => super.equals(that) + } + protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit = sb ++= s"${prefix}Ø" + } + + final case class Tip[+T](ord: Int, value: T) extends Ordering[T] { + def withValue[S](s: S) = + if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[Tip[S]] + else Tip(ord, s) + protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit = sb ++= s"${prefix}Tip(${toBinaryString(ord)} -> $value)\n" + } + + final case class Bin[+T](prefix: Int, mask: Int, left: Ordering[T], var right: Ordering[T] @scala.annotation.unchecked.uncheckedVariance) extends Ordering[T] { + def bin[S](left: Ordering[S], right: Ordering[S]): Ordering[S] = { + if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[Bin[S]] + else Bin[S](prefix, mask, left, right) + } + protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit = { + sb ++= s"${prefix}Bin(${toBinaryString(this.prefix)}:${toBinaryString(mask)})\n" + left.format(sb, subPrefix + "├── ", subPrefix + "│ ") + right.format(sb, subPrefix + "└── ", subPrefix + " ") + } + } + + private def branchMask(i: Int, j: Int) = highestOneBit(i ^ j) + + private def join[T](p1: Int, t1: Ordering[T], p2: Int, t2: Ordering[T]): Ordering[T] = { + val m = branchMask(p1, p2) + val p = mask(p1, m) + if (zero(p1, m)) Bin(p, m, t1, t2) + else Bin(p, m, t2, t1) + } + + private def bin[T](prefix: Int, mask: Int, left: Ordering[T], right: Ordering[T]): Ordering[T] = (left, right) match { + case (l, Zero) => l + case (Zero, r) => r + case (l, r) => Bin(prefix, mask, l, r) + } + } + + sealed abstract class Ordering[+T] { + import Ordering._ + import scala.annotation.tailrec + import scala.collection.generic.BitOperations.Int._ + + override final def toString: String = format + final def format: String = { + val sb = new StringBuilder + format(sb, "", "") + sb.toString() + } + protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit + + @tailrec + final def head: T = this match { + case Zero => throw new NoSuchElementException("head of empty map") + case Tip(k, v) => v + case Bin(_, _, l, _) => l.head + } + + @tailrec + final def headOption: Option[T] = this match { + case Zero => None + case Tip(_, v) => Some(v) + case Bin(_, _, l, _) => l.headOption + } + + @tailrec + final def last: T = this match { + case Zero => throw new NoSuchElementException("last of empty map") + case Tip(_, v) => v + case Bin(_, _, _, r) => r.last + } + + @tailrec + final def lastOption: Option[T] = this match { + case Zero => None + case Tip(_, v) => Some(v) + case Bin(_, _, _, r) => r.lastOption + } + + @tailrec + final def ordinal: Int = this match { + case Zero => 0 + case Tip(o, _) => o + case Bin(_, _, _, r) => r.ordinal + } + + final def tail: Ordering[T] = this match { + case Zero => throw new NoSuchElementException("tail of empty map") + case Tip(_, _) => Zero + case Bin(p, m, l, r) => bin(p, m, l.tail, r) + } + + final def headTail: (T, Ordering[T]) = this match { + case Zero => throw new NoSuchElementException("init of empty map") + case Tip(_, v) => (v, Zero) + case Bin(p, m, l, r) => + val (head, tail) = l.headTail + (head, bin(p, m, tail, r)) + } + + final def init: Ordering[T] = this match { + case Zero => throw new NoSuchElementException("init of empty map") + case Tip(_, _) => Zero + case Bin(p, m, l, r) => + bin(p, m, l, r.init) + } + + final def initLast: (Ordering[T], T) = this match { + case Zero => throw new NoSuchElementException("init of empty map") + case Tip(_, v) => (Zero, v) + case Bin(p, m, l, r) => + val (init, last) = r.initLast + (bin(p, m, l, init), last) + } + + final def iterator: Iterator[T] = this match { + case Zero => Iterator.empty + case _ => new Iterator(this) + } + + final def include[S >: T](ordinal: Int, value: S): Ordering[S] = this match { + case Zero => + Tip(ordinal, value) + case Tip(o, _) => + if (ordinal == o) Tip(ordinal, value) + else join(ordinal, Tip(ordinal, value), o, this) + case Bin(p, m, l, r) => + if (!hasMatch(ordinal, p, m)) join(ordinal, Tip(ordinal, value), p, this) + else if (zero(ordinal, m)) Bin(p, m, l.include(ordinal, value), r) + else Bin(p, m, l, r.include(ordinal, value)) + } + + final def append[S >: T](ordinal: Int, value: S): Ordering[S] = this match { + case Zero => + Tip(ordinal, value) + case Tip(o, _) => + if (ordinal == o) Tip(ordinal, value) + else join(ordinal, Tip(ordinal, value), o, this) + case Bin(p, m, l, r) => + if (!hasMatch(ordinal, p, m)) join(ordinal, Tip(ordinal, value), p, this) + else if (zero(ordinal, m)) throw new IllegalArgumentException(s"Append called with ordinal out of range: $ordinal is not greater than current max ordinal ${this.ordinal}") + else Bin(p, m, l, r.append(ordinal, value)) + } + + @inline private[collection] final def appendInPlace[S >: T](ordinal: Int, value: S): Ordering[S] = appendInPlace1(null, ordinal, value) + private[collection] final def appendInPlace1[S >: T](parent: Bin[S], ordinal: Int, value: S): Ordering[S] = this match { + case Zero => + Tip(ordinal, value) + case Tip(o, _) if o >= ordinal => + throw new IllegalArgumentException(s"Append called with ordinal out of range: $o is not greater than current max ordinal ${this.ordinal}") + case Tip(o, _) if parent == null => + join(ordinal, Tip(ordinal, value), o, this) + case Tip(o, _) => + parent.right = join(ordinal, Tip(ordinal, value), o, this) + parent + case b @ Bin(p, m, _, r) => + if (!hasMatch(ordinal, p, m)) { + val b2 = join(ordinal, Tip(ordinal, value), p, this) + if (parent != null) { + parent.right = b2 + parent + } else b2 + } else if (zero(ordinal, m)) throw new IllegalArgumentException(s"Append called with ordinal out of range: $ordinal is not greater than current max ordinal ${this.ordinal}") + else { + r.appendInPlace1(b, ordinal, value) + this + } + } + + final def exclude(ordinal: Int): Ordering[T] = this match { + case Zero => + Zero + case Tip(o, _) => + if (ordinal == o) Zero + else this + case Bin(p, m, l, r) => + if (!hasMatch(ordinal, p, m)) this + else if (zero(ordinal, m)) bin(p, m, l.exclude(ordinal), r) + else bin(p, m, l, r.exclude(ordinal)) + } + + final def splitAt(n: Int): (Ordering[T], Ordering[T]) = { + var rear = Ordering.empty[T] + var i = n + (modifyOrRemove { (o, v) => + i -= 1 + if (i >= 0) Some(v) + else { + rear = rear.appendInPlace(o, v) + None + } + }, rear) + } + + /** + * A combined transform and filter function. Returns an `Ordering` such that + * for each `(key, value)` mapping in this map, if `f(key, value) == None` + * the map contains no mapping for key, and if `f(key, value) == Some(x)` the + * map contains `(key, x)`. + * + * @tparam S The type of the values in the resulting `LongMap`. + * @param f The transforming function. + * @return The modified map. + */ + final def modifyOrRemove[S](f: (Int, T) => Option[S]): Ordering[S] = this match { + case Zero => Zero + case Tip(key, value) => + f(key, value) match { + case None => Zero + case Some(value2) => + // hack to preserve sharing + if (value.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef]) this.asInstanceOf[Ordering[S]] + else Tip(key, value2) + } + case Bin(prefix, mask, left, right) => + val l = left.modifyOrRemove(f) + val r = right.modifyOrRemove(f) + if ((left eq l) && (right eq r)) this.asInstanceOf[Ordering[S]] + else bin(prefix, mask, l, r) + } + } +} diff --git a/library/src/scala/collection/immutable/TreeSet.scala b/library/src/scala/collection/immutable/TreeSet.scala new file mode 100644 index 000000000000..4348f62ece74 --- /dev/null +++ b/library/src/scala/collection/immutable/TreeSet.scala @@ -0,0 +1,296 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializable +import scala.collection.mutable.ReusableBuilder +import scala.collection.immutable.{RedBlackTree => RB} +import scala.runtime.AbstractFunction1 + + +/** This class implements immutable sorted sets using a tree. + * + * @tparam A the type of the elements contained in this tree set + * @param ordering the implicit ordering used to compare objects of type `A` + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] + * section on `Red-Black Trees` for more information. + * + * @define Coll `immutable.TreeSet` + * @define coll immutable tree set + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[A, Any])(implicit val ordering: Ordering[A]) + extends AbstractSet[A] + with SortedSet[A] + with SortedSetOps[A, TreeSet, TreeSet[A]] + with StrictOptimizedSortedSetOps[A, TreeSet, TreeSet[A]] + with SortedSetFactoryDefaults[A, TreeSet, Set] + with DefaultSerializable { + + if (ordering eq null) throw new NullPointerException("ordering must not be null") + + def this()(implicit ordering: Ordering[A]) = this(null)(ordering) + + override def sortedIterableFactory: TreeSet.type = TreeSet + + private[this] def newSetOrSelf(t: RB.Tree[A, Any]) = if(t eq tree) this else new TreeSet[A](t) + + override def size: Int = RB.count(tree) + + override def isEmpty = size == 0 + + override def head: A = RB.smallest(tree).key + + override def last: A = RB.greatest(tree).key + + override def tail: TreeSet[A] = new TreeSet(RB.tail(tree)) + + override def init: TreeSet[A] = new TreeSet(RB.init(tree)) + + override def min[A1 >: A](implicit ord: Ordering[A1]): A = { + if ((ord eq ordering) && nonEmpty) { + head + } else { + super.min(ord) + } + } + + override def max[A1 >: A](implicit ord: Ordering[A1]): A = { + if ((ord eq ordering) && nonEmpty) { + last + } else { + super.max(ord) + } + } + + override def drop(n: Int): TreeSet[A] = { + if (n <= 0) this + else if (n >= size) empty + else new TreeSet(RB.drop(tree, n)) + } + + override def take(n: Int): TreeSet[A] = { + if (n <= 0) empty + else if (n >= size) this + else new TreeSet(RB.take(tree, n)) + } + + override def slice(from: Int, until: Int): TreeSet[A] = { + if (until <= from) empty + else if (from <= 0) take(until) + else if (until >= size) drop(from) + else new TreeSet(RB.slice(tree, from, until)) + } + + override def dropRight(n: Int): TreeSet[A] = take(size - math.max(n, 0)) + + override def takeRight(n: Int): TreeSet[A] = drop(size - math.max(n, 0)) + + private[this] def countWhile(p: A => Boolean): Int = { + var result = 0 + val it = iterator + while (it.hasNext && p(it.next())) result += 1 + result + } + override def dropWhile(p: A => Boolean): TreeSet[A] = drop(countWhile(p)) + + override def takeWhile(p: A => Boolean): TreeSet[A] = take(countWhile(p)) + + override def span(p: A => Boolean): (TreeSet[A], TreeSet[A]) = splitAt(countWhile(p)) + + override def foreach[U](f: A => U): Unit = RB.foreachKey(tree, f) + + override def minAfter(key: A): Option[A] = { + val v = RB.minAfter(tree, key) + if (v eq null) Option.empty else Some(v.key) + } + + override def maxBefore(key: A): Option[A] = { + val v = RB.maxBefore(tree, key) + if (v eq null) Option.empty else Some(v.key) + } + + def iterator: Iterator[A] = RB.keysIterator(tree) + + def iteratorFrom(start: A): Iterator[A] = RB.keysIterator(tree, Some(start)) + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + import scala.collection.convert.impl._ + type T = RB.Tree[A, Any] + val s = shape.shape match { + case StepperShape.IntShape => IntBinaryTreeStepper.from[T] (size, tree, _.left, _.right, _.key.asInstanceOf[Int]) + case StepperShape.LongShape => LongBinaryTreeStepper.from[T] (size, tree, _.left, _.right, _.key.asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleBinaryTreeStepper.from[T](size, tree, _.left, _.right, _.key.asInstanceOf[Double]) + case _ => shape.parUnbox(AnyBinaryTreeStepper.from[A, T](size, tree, _.left, _.right, _.key)) + } + s.asInstanceOf[S with EfficientSplit] + } + + /** Checks if this set contains element `elem`. + * + * @param elem the element to check for membership. + * @return true, iff `elem` is contained in this set. + */ + def contains(elem: A): Boolean = RB.contains(tree, elem) + + override def range(from: A, until: A): TreeSet[A] = newSetOrSelf(RB.range(tree, from, until)) + + def rangeImpl(from: Option[A], until: Option[A]): TreeSet[A] = newSetOrSelf(RB.rangeImpl(tree, from, until)) + + /** Creates a new `TreeSet` with the entry added. + * + * @param elem a new element to add. + * @return a new $coll containing `elem` and all the elements of this $coll. + */ + def incl(elem: A): TreeSet[A] = + newSetOrSelf(RB.update(tree, elem, null, overwrite = false)) + + /** Creates a new `TreeSet` with the entry removed. + * + * @param elem a new element to add. + * @return a new $coll containing all the elements of this $coll except `elem`. + */ + def excl(elem: A): TreeSet[A] = + newSetOrSelf(RB.delete(tree, elem)) + + override def concat(that: collection.IterableOnce[A]): TreeSet[A] = { + val t = that match { + case ts: TreeSet[A] if ordering == ts.ordering => + RB.union(tree, ts.tree) + case _ => + val it = that.iterator + var t = tree + while (it.hasNext) t = RB.update(t, it.next(), null, overwrite = false) + t + } + newSetOrSelf(t) + } + + override def removedAll(that: IterableOnce[A]): TreeSet[A] = that match { + case ts: TreeSet[A] if ordering == ts.ordering => + newSetOrSelf(RB.difference(tree, ts.tree)) + case _ => + //TODO add an implementation of a mutable subtractor similar to TreeMap + //but at least this doesn't create a TreeSet for each iteration + object sub extends AbstractFunction1[A, Unit] { + var currentTree = tree + override def apply(k: A): Unit = { + currentTree = RB.delete(currentTree, k) + } + } + that.iterator.foreach(sub) + newSetOrSelf(sub.currentTree) + } + + override def intersect(that: collection.Set[A]): TreeSet[A] = that match { + case ts: TreeSet[A] if ordering == ts.ordering => + newSetOrSelf(RB.intersect(tree, ts.tree)) + case _ => + super.intersect(that) + } + + override def diff(that: collection.Set[A]): TreeSet[A] = that match { + case ts: TreeSet[A] if ordering == ts.ordering => + newSetOrSelf(RB.difference(tree, ts.tree)) + case _ => + super.diff(that) + } + + override def filter(f: A => Boolean): TreeSet[A] = newSetOrSelf(RB.filterEntries[A, Any](tree, {(k, _) => f(k)})) + + override def partition(p: A => Boolean): (TreeSet[A], TreeSet[A]) = { + val (l, r) = RB.partitionEntries(tree, {(a:A, _: Any) => p(a)}) + (newSetOrSelf(l), newSetOrSelf(r)) + } + + override def equals(obj: Any): Boolean = obj match { + case that: TreeSet[A @unchecked] if ordering == that.ordering => RB.keysEqual(tree, that.tree) + case _ => super.equals(obj) + } + + override protected[this] def className = "TreeSet" +} + +/** + * $factoryInfo + * + * @define Coll `immutable.TreeSet` + * @define coll immutable tree set + */ +@SerialVersionUID(3L) +object TreeSet extends SortedIterableFactory[TreeSet] { + + def empty[A: Ordering]: TreeSet[A] = new TreeSet[A] + + def from[E](it: scala.collection.IterableOnce[E])(implicit ordering: Ordering[E]): TreeSet[E] = + it match { + case ts: TreeSet[E] if ordering == ts.ordering => ts + case ss: scala.collection.SortedSet[E] if ordering == ss.ordering => + new TreeSet[E](RB.fromOrderedKeys(ss.iterator, ss.size)) + case r: Range if (ordering eq Ordering.Int) || (Ordering.Int isReverseOf ordering) => + val it = if((ordering eq Ordering.Int) == (r.step > 0)) r.iterator else r.reverseIterator + val tree = RB.fromOrderedKeys(it.asInstanceOf[Iterator[E]], r.size) + // The cast is needed to compile with Dotty: + // Dotty doesn't infer that E =:= Int, since instantiation of covariant GADTs is unsound + new TreeSet[E](tree) + case _ => + var t: RB.Tree[E, Null] = null + val i = it.iterator + while (i.hasNext) t = RB.update(t, i.next(), null, overwrite = false) + new TreeSet[E](t) + } + + def newBuilder[A](implicit ordering: Ordering[A]): ReusableBuilder[A, TreeSet[A]] = new TreeSetBuilder[A] + private class TreeSetBuilder[A](implicit ordering: Ordering[A]) + extends RB.SetHelper[A] + with ReusableBuilder[A, TreeSet[A]] { + type Tree = RB.Tree[A, Any] + private [this] var tree:RB.Tree[A, Any] = null + + override def addOne(elem: A): this.type = { + tree = mutableUpd(tree, elem) + this + } + + override def addAll(xs: IterableOnce[A]): this.type = { + xs match { + // TODO consider writing a mutable-safe union for TreeSet/TreeMap builder ++= + // for the moment we have to force immutability before the union + // which will waste some time and space + // calling `beforePublish` makes `tree` immutable + case ts: TreeSet[A] if ts.ordering == ordering => + if (tree eq null) tree = ts.tree + else tree = RB.union(beforePublish(tree), ts.tree)(ordering) + case ts: TreeMap[A @unchecked, _] if ts.ordering == ordering => + if (tree eq null) tree = ts.tree0 + else tree = RB.union(beforePublish(tree), ts.tree0)(ordering) + case _ => + super.addAll(xs) + } + this + } + + override def clear(): Unit = { + tree = null + } + + override def result(): TreeSet[A] = new TreeSet[A](beforePublish(tree))(ordering) + } +} diff --git a/library/src/scala/collection/immutable/Vector.scala b/library/src/scala/collection/immutable/Vector.scala new file mode 100644 index 000000000000..f38cdbc77b5d --- /dev/null +++ b/library/src/scala/collection/immutable/Vector.scala @@ -0,0 +1,2483 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package immutable + +import java.lang.Math.{abs, max => mmax, min => mmin} +import java.util.Arrays.{copyOf, copyOfRange} +import java.util.{Arrays, Spliterator} + +import scala.annotation.switch +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.{CommonErrors, DefaultSerializable} +import scala.collection.immutable.VectorInline._ +import scala.collection.immutable.VectorStatics._ +import scala.collection.mutable.ReusableBuilder + + +/** $factoryInfo + * @define Coll `Vector` + * @define coll vector + */ +@SerialVersionUID(3L) +object Vector extends StrictOptimizedSeqFactory[Vector] { + + def empty[A]: Vector[A] = Vector0 + + def from[E](it: collection.IterableOnce[E]): Vector[E] = + it match { + case v: Vector[E] => v + case _ => + val knownSize = it.knownSize + if (knownSize == 0) empty[E] + else if (knownSize > 0 && knownSize <= WIDTH) { + val a1: Arr1 = it match { + case as: ArraySeq.ofRef[_] if as.elemTag.runtimeClass == classOf[AnyRef] => + as.unsafeArray.asInstanceOf[Arr1] + case it: Iterable[E] => + val a1 = new Arr1(knownSize) + @annotation.unused val copied = it.copyToArray(a1.asInstanceOf[Array[Any]]) + //assert(copied == knownSize) + a1 + case _ => + val a1 = new Arr1(knownSize) + @annotation.unused val copied = it.iterator.copyToArray(a1.asInstanceOf[Array[Any]]) + //assert(copied == knownSize) + a1.asInstanceOf[Arr1] + } + new Vector1[E](a1) + } else { + (newBuilder ++= it).result() + } + } + + def newBuilder[A]: ReusableBuilder[A, Vector[A]] = new VectorBuilder[A] + + /** Create a Vector with the same element at each index. + * + * Unlike `fill`, which takes a by-name argument for the value and can thereby + * compute different values for each index, this method guarantees that all + * elements are identical. This allows sparse allocation in O(log n) time and space. + */ + private[collection] def fillSparse[A](n: Int)(elem: A): Vector[A] = { + //TODO Make public; this method is private for now because it is not forward binary compatible + if(n <= 0) Vector0 + else { + val b = new VectorBuilder[A] + b.initSparse(n, elem) + b.result() + } + } + + private val defaultApplyPreferredMaxLength: Int = + // explicit StringOps to avoid initialization cycle with Predef (scala/bug#13009) + try new StringOps(System.getProperty("scala.collection.immutable.Vector.defaultApplyPreferredMaxLength", + "250")).toInt + catch { + case _: SecurityException => 250 + } + + private val emptyIterator = new NewVectorIterator(Vector0, 0, 0) +} + + +/** Vector is a general-purpose, immutable data structure. It provides random access and updates + * in O(log n) time, as well as very fast append/prepend/tail/init (amortized O(1), worst case O(log n)). + * Because vectors strike a good balance between fast random selections and fast random functional updates, + * they are currently the default implementation of immutable indexed sequences. + * + * Vectors are implemented by radix-balanced finger trees of width 32. There is a separate subclass + * for each level (0 to 6, with 0 being the empty vector and 6 a tree with a maximum width of 64 at the + * top level). + * + * Tree balancing: + * - Only the first dimension of an array may have a size < WIDTH + * - In a `data` (central) array the first dimension may be up to WIDTH-2 long, in `prefix1` and `suffix1` up + * to WIDTH, and in other `prefix` and `suffix` arrays up to WIDTH-1 + * - `prefix1` and `suffix1` are never empty + * - Balancing does not cross the main data array (i.e. prepending never touches the suffix and appending never touches + * the prefix). The level is increased/decreased when the affected side plus main data is already full/empty + * - All arrays are left-aligned and truncated + * + * In addition to the data slices (`prefix1`, `prefix2`, ..., `dataN`, ..., `suffix2`, `suffix1`) we store a running + * count of elements after each prefix for more efficient indexing without having to dereference all prefix arrays. + */ +sealed abstract class Vector[+A] private[immutable] (private[immutable] final val prefix1: Arr1) + extends AbstractSeq[A] + with IndexedSeq[A] + with IndexedSeqOps[A, Vector, Vector[A]] + with StrictOptimizedSeqOps[A, Vector, Vector[A]] + with IterableFactoryDefaults[A, Vector] + with DefaultSerializable { + + override def iterableFactory: SeqFactory[Vector] = Vector + + override final def length: Int = + if(this.isInstanceOf[BigVector[_]]) this.asInstanceOf[BigVector[_]].length0 + else prefix1.length + + override final def iterator: Iterator[A] = + if(this.isInstanceOf[Vector0.type]) Vector.emptyIterator + else new NewVectorIterator(this, length, vectorSliceCount) + + override final protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Vector[A] = { + var i = 0 + val len = prefix1.length + while (i != len) { + if (pred(prefix1(i).asInstanceOf[A]) == isFlipped) { + // each 1 bit indicates that index passes the filter. + // all indices < i are also assumed to pass the filter + var bitmap = 0 + var j = i + 1 + while (j < len) { + if (pred(prefix1(j).asInstanceOf[A]) != isFlipped) { + bitmap |= (1 << j) + } + j += 1 + } + val newLen = i + java.lang.Integer.bitCount(bitmap) + + if(this.isInstanceOf[BigVector[_]]) { + val b = new VectorBuilder[A] + var k = 0 + while(k < i) { + b.addOne(prefix1(k).asInstanceOf[A]) + k += 1 + } + k = i + 1 + while (i != newLen) { + if (((1 << k) & bitmap) != 0) { + b.addOne(prefix1(k).asInstanceOf[A]) + i += 1 + } + k += 1 + } + this.asInstanceOf[BigVector[A]].foreachRest { v => if(pred(v) != isFlipped) b.addOne(v) } + return b.result() + } else { + if (newLen == 0) return Vector0 + val newData = new Array[AnyRef](newLen) + System.arraycopy(prefix1, 0, newData, 0, i) + var k = i + 1 + while (i != newLen) { + if (((1 << k) & bitmap) != 0) { + newData(i) = prefix1(k) + i += 1 + } + k += 1 + } + return new Vector1[A](newData) + } + } + i += 1 + } + if(this.isInstanceOf[BigVector[_]]) { + val b = new VectorBuilder[A] + b.initFrom(prefix1) + this.asInstanceOf[BigVector[A]].foreachRest { v => if(pred(v) != isFlipped) b.addOne(v) } + b.result() + } else this + } + + // Dummy overrides to refine result types for binary compatibility: + override def updated[B >: A](index: Int, elem: B): Vector[B] = super.updated(index, elem) + override def appended[B >: A](elem: B): Vector[B] = super.appended(elem) + override def prepended[B >: A](elem: B): Vector[B] = super.prepended(elem) + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): Vector[B] = { + val k = prefix.knownSize + if (k == 0) this + else if (k < 0) super.prependedAll(prefix) + else prependedAll0(prefix, k) + } + + override final def appendedAll[B >: A](suffix: collection.IterableOnce[B]): Vector[B] = { + val k = suffix.knownSize + if (k == 0) this + else if (k < 0) super.appendedAll(suffix) + else appendedAll0(suffix, k) + } + + protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = { + // k >= 0, k = prefix.knownSize + val tinyAppendLimit = 4 + vectorSliceCount + if (k < tinyAppendLimit /*|| k < (this.size >>> Log2ConcatFaster)*/) { + var v: Vector[B] = this + val it = IndexedSeq.from(prefix).reverseIterator + while (it.hasNext) v = it.next() +: v + v + } else if (this.size < (k >>> Log2ConcatFaster) && prefix.isInstanceOf[Vector[_]]) { + var v = prefix.asInstanceOf[Vector[B]] + val it = this.iterator + while (it.hasNext) v = v :+ it.next() + v + } else if (k < this.size - AlignToFaster) { + new VectorBuilder[B].alignTo(k, this).addAll(prefix).addAll(this).result() + } else super.prependedAll(prefix) + } + + protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + // k >= 0, k = suffix.knownSize + val tinyAppendLimit = 4 + vectorSliceCount + if (k < tinyAppendLimit) { + var v: Vector[B] = this + suffix match { + case it: Iterable[_] => it.asInstanceOf[Iterable[B]].foreach(x => v = v.appended(x)) + case _ => suffix.iterator.foreach(x => v = v.appended(x)) + } + v + } else if (this.size < (k >>> Log2ConcatFaster) && suffix.isInstanceOf[Vector[_]]) { + var v = suffix.asInstanceOf[Vector[B]] + val ri = this.reverseIterator + while (ri.hasNext) v = v.prepended(ri.next()) + v + } else if (this.size < k - AlignToFaster && suffix.isInstanceOf[Vector[_]]) { + val v = suffix.asInstanceOf[Vector[B]] + new VectorBuilder[B].alignTo(this.size, v).addAll(this).addAll(v).result() + } else new VectorBuilder[B].initFrom(this).addAll(suffix).result() + } + + override def className = "Vector" + + @inline override final def take(n: Int): Vector[A] = slice(0, n) + @inline override final def drop(n: Int): Vector[A] = slice(n, length) + @inline override final def takeRight(n: Int): Vector[A] = slice(length - mmax(n, 0), length) + @inline override final def dropRight(n: Int): Vector[A] = slice(0, length - mmax(n, 0)) + override def tail: Vector[A] = slice(1, length) + override def init: Vector[A] = slice(0, length-1) + + /** Like slice but parameters must be 0 <= lo < hi < length */ + protected[this] def slice0(lo: Int, hi: Int): Vector[A] + + /** Number of slices */ + protected[immutable] def vectorSliceCount: Int + /** Slice at index */ + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] + /** Length of all slices up to and including index */ + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int + + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = iterator.copyToArray(xs, start, len) + + override def toVector: Vector[A] = this + + override protected def applyPreferredMaxLength: Int = Vector.defaultApplyPreferredMaxLength + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + val s = shape.shape match { + case StepperShape.IntShape => new IntVectorStepper(iterator.asInstanceOf[NewVectorIterator[Int]]) + case StepperShape.LongShape => new LongVectorStepper(iterator.asInstanceOf[NewVectorIterator[Long]]) + case StepperShape.DoubleShape => new DoubleVectorStepper(iterator.asInstanceOf[NewVectorIterator[Double]]) + case _ => shape.parUnbox(new AnyVectorStepper[A](iterator.asInstanceOf[NewVectorIterator[A]])) + } + s.asInstanceOf[S with EfficientSplit] + } + + protected[this] def ioob(index: Int): IndexOutOfBoundsException = + CommonErrors.indexOutOfBounds(index = index, max = length - 1) + + override final def head: A = + if (prefix1.length == 0) throw new NoSuchElementException("empty.head") + else prefix1(0).asInstanceOf[A] + + override final def last: A = { + if(this.isInstanceOf[BigVector[_]]) { + val suffix = this.asInstanceOf[BigVector[_]].suffix1 + if(suffix.length == 0) throw new NoSuchElementException("empty.tail") + else suffix(suffix.length-1) + } else prefix1(prefix1.length-1) + }.asInstanceOf[A] + + override final def foreach[U](f: A => U): Unit = { + val c = vectorSliceCount + var i = 0 + while (i < c) { + foreachRec(vectorSliceDim(c, i) - 1, vectorSlice(i), f) + i += 1 + } + } + + // The following definitions are needed for binary compatibility with ParVector + private[collection] def startIndex: Int = 0 + private[collection] def endIndex: Int = length + private[collection] def initIterator[B >: A](s: VectorIterator[B]): Unit = + s.it = iterator.asInstanceOf[NewVectorIterator[B]] +} + + +/** This class only exists because we cannot override `slice` in `Vector` in a binary-compatible way */ +private sealed abstract class VectorImpl[+A](_prefix1: Arr1) extends Vector[A](_prefix1) { + + override final def slice(from: Int, until: Int): Vector[A] = { + val lo = mmax(from, 0) + val hi = mmin(until, length) + if (hi <= lo) Vector0 + else if (hi - lo == length) this + else slice0(lo, hi) + } +} + + +/** Vector with suffix and length fields; all Vector subclasses except Vector1 extend this */ +private sealed abstract class BigVector[+A](_prefix1: Arr1, private[immutable] val suffix1: Arr1, private[immutable] val length0: Int) extends VectorImpl[A](_prefix1) { + + protected[immutable] final def foreachRest[U](f: A => U): Unit = { + val c = vectorSliceCount + var i = 1 + while(i < c) { + foreachRec(vectorSliceDim(c, i)-1, vectorSlice(i), f) + i += 1 + } + } +} + + +/** Empty vector */ +private object Vector0 extends BigVector[Nothing](empty1, empty1, 0) { + + def apply(index: Int): Nothing = throw ioob(index) + + override def updated[B >: Nothing](index: Int, elem: B): Vector[B] = throw ioob(index) + + override def appended[B >: Nothing](elem: B): Vector[B] = new Vector1(wrap1(elem)) + + override def prepended[B >: Nothing](elem: B): Vector[B] = new Vector1(wrap1(elem)) + + override def map[B](f: Nothing => B): Vector[B] = this + + override def tail: Vector[Nothing] = throw new UnsupportedOperationException("empty.tail") + + override def init: Vector[Nothing] = throw new UnsupportedOperationException("empty.init") + + protected[this] def slice0(lo: Int, hi: Int): Vector[Nothing] = this + + protected[immutable] def vectorSliceCount: Int = 0 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = null + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = 0 + + override def equals(o: Any): Boolean = { + if(this eq o.asInstanceOf[AnyRef]) true + else o match { + case that: Vector[_] => false + case o => super.equals(o) + } + } + + override protected[this]def prependedAll0[B >: Nothing](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + Vector.from(prefix) + + override protected[this]def appendedAll0[B >: Nothing](suffix: collection.IterableOnce[B], k: Int): Vector[B] = + Vector.from(suffix) + + override protected[this] def ioob(index: Int): IndexOutOfBoundsException = + new IndexOutOfBoundsException(s"$index is out of bounds (empty vector)") +} + +/** Flat ArraySeq-like structure */ +private final class Vector1[+A](_data1: Arr1) extends VectorImpl[A](_data1) { + + @inline def apply(index: Int): A = { + if(index >= 0 && index < prefix1.length) + prefix1(index).asInstanceOf[A] + else throw ioob(index) + } + + override def updated[B >: A](index: Int, elem: B): Vector[B] = { + if(index >= 0 && index < prefix1.length) + new Vector1(copyUpdate(prefix1, index, elem)) + else throw ioob(index) + } + + override def appended[B >: A](elem: B): Vector[B] = { + val len1 = prefix1.length + if(len1 < WIDTH) new Vector1(copyAppend1(prefix1, elem)) + else new Vector2(prefix1, WIDTH, empty2, wrap1(elem), WIDTH+1) + } + + override def prepended[B >: A](elem: B): Vector[B] = { + val len1 = prefix1.length + if(len1 < WIDTH) new Vector1(copyPrepend1(elem, prefix1)) + else new Vector2(wrap1(elem), 1, empty2, prefix1, len1+1) + } + + override def map[B](f: A => B): Vector[B] = new Vector1(mapElems1(prefix1, f)) + + protected[this] def slice0(lo: Int, hi: Int): Vector[A] = + new Vector1(copyOfRange(prefix1, lo, hi)) + + override def tail: Vector[A] = + if(prefix1.length == 1) Vector0 + else new Vector1(copyTail(prefix1)) + + override def init: Vector[A] = + if(prefix1.length == 1) Vector0 + else new Vector1(copyInit(prefix1)) + + protected[immutable] def vectorSliceCount: Int = 1 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = prefix1 + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = prefix1.length + + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + prepend1IfSpace(prefix1, prefix) match { + case null => super.prependedAll0(prefix, k) + case data1b => new Vector1(data1b) + } + + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + val data1b = append1IfSpace(prefix1, suffix) + if(data1b ne null) new Vector1(data1b) + else super.appendedAll0(suffix, k) + } +} + + +/** 2-dimensional radix-balanced finger tree */ +private final class Vector2[+A](_prefix1: Arr1, private[immutable] val len1: Int, + private[immutable] val data2: Arr2, + _suffix1: Arr1, + _length0: Int) extends BigVector[A](_prefix1, _suffix1, _length0) { + + @inline private[this] def copy(prefix1: Arr1 = prefix1, len1: Int = len1, + data2: Arr2 = data2, + suffix1: Arr1 = suffix1, + length0: Int = length0) = + new Vector2(prefix1, len1, data2, suffix1, length0) + + @inline def apply(index: Int): A = { + if(index >= 0 && index < length0) { + val io = index - len1 + if(io >= 0) { + val i2 = io >>> BITS + val i1 = io & MASK + if(i2 < data2.length) data2(i2)(i1) + else suffix1(io & MASK) + } else prefix1(index) + }.asInstanceOf[A] else throw ioob(index) + } + + override def updated[B >: A](index: Int, elem: B): Vector[B] = { + if(index >= 0 && index < length0) { + if(index >= len1) { + val io = index - len1 + val i2 = io >>> BITS + val i1 = io & MASK + if(i2 < data2.length) copy(data2 = copyUpdate(data2, i2, i1, elem)) + else copy(suffix1 = copyUpdate(suffix1, i1, elem)) + } else { + copy(prefix1 = copyUpdate(prefix1, index, elem)) + } + } else throw ioob(index) + } + + override def appended[B >: A](elem: B): Vector[B] = { + if (suffix1.length < WIDTH ) copy(suffix1 = copyAppend1(suffix1, elem), length0 = length0+1) + else if(data2.length < WIDTH-2) copy(data2 = copyAppend(data2, suffix1), suffix1 = wrap1(elem), length0 = length0+1) + else new Vector3(prefix1, len1, data2, WIDTH*(WIDTH-2) + len1, empty3, wrap2(suffix1), wrap1(elem), length0+1) + } + + override def prepended[B >: A](elem: B): Vector[B] = { + if (len1 < WIDTH ) copy(copyPrepend1(elem, prefix1), len1+1, length0 = length0+1) + else if(data2.length < WIDTH-2) copy(wrap1(elem), 1, copyPrepend(prefix1, data2), length0 = length0+1) + else new Vector3(wrap1(elem), 1, wrap2(prefix1), len1+1, empty3, data2, suffix1, length0+1) + } + + override def map[B](f: A => B): Vector[B] = + copy(prefix1 = mapElems1(prefix1, f), data2 = mapElems(2, data2, f), suffix1 = mapElems1(suffix1, f)) + + protected[this] def slice0(lo: Int, hi: Int): Vector[A] = { + val b = new VectorSliceBuilder(lo, hi) + b.consider(1, prefix1) + b.consider(2, data2) + b.consider(1, suffix1) + b.result() + } + + override def tail: Vector[A] = + if(len1 > 1) copy(copyTail(prefix1), len1-1, length0 = length0-1) + else slice0(1, length0) + + override def init: Vector[A] = + if(suffix1.length > 1) copy(suffix1 = copyInit(suffix1), length0 = length0-1) + else slice0(0, length0-1) + + protected[immutable] def vectorSliceCount: Int = 3 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + case 0 => prefix1 + case 1 => data2 + case 2 => suffix1 + } + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = (idx: @switch) match { + case 0 => len1 + case 1 => length0 - suffix1.length + case 2 => length0 + } + + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + prepend1IfSpace(prefix1, prefix) match { + case null => super.prependedAll0(prefix, k) + case prefix1b => + val diff = prefix1b.length - prefix1.length + copy(prefix1 = prefix1b, + len1 = len1 + diff, + length0 = length0 + diff, + ) + } + + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + val suffix1b = append1IfSpace(suffix1, suffix) + if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) + else super.appendedAll0(suffix, k) + } +} + + +/** 3-dimensional radix-balanced finger tree */ +private final class Vector3[+A](_prefix1: Arr1, private[immutable] val len1: Int, + private[immutable] val prefix2: Arr2, private[immutable] val len12: Int, + private[immutable] val data3: Arr3, + private[immutable] val suffix2: Arr2, _suffix1: Arr1, + _length0: Int) extends BigVector[A](_prefix1, _suffix1, _length0) { + + @inline private[this] def copy(prefix1: Arr1 = prefix1, len1: Int = len1, + prefix2: Arr2 = prefix2, len12: Int = len12, + data3: Arr3 = data3, + suffix2: Arr2 = suffix2, suffix1: Arr1 = suffix1, + length0: Int = length0) = + new Vector3(prefix1, len1, prefix2, len12, data3, suffix2, suffix1, length0) + + @inline def apply(index: Int): A = { + if(index >= 0 && index < length0) { + val io = index - len12 + if(io >= 0) { + val i3 = io >>> BITS2 + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if(i3 < data3.length) data3(i3)(i2)(i1) + else if(i2 < suffix2.length) suffix2(i2)(i1) + else suffix1(i1) + } else if(index >= len1) { + val io = index - len1 + prefix2(io >>> BITS)(io & MASK) + } else prefix1(index) + }.asInstanceOf[A] else throw ioob(index) + } + + override def updated[B >: A](index: Int, elem: B): Vector[B] = { + if(index >= 0 && index < length0) { + if(index >= len12) { + val io = index - len12 + val i3 = io >>> BITS2 + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if (i3 < data3.length ) copy(data3 = copyUpdate(data3, i3, i2, i1, elem)) + else if(i2 < suffix2.length) copy(suffix2 = copyUpdate(suffix2, i2, i1, elem)) + else copy(suffix1 = copyUpdate(suffix1, i1, elem)) + } else if(index >= len1) { + val io = index - len1 + copy(prefix2 = copyUpdate(prefix2, io >>> BITS, io & MASK, elem)) + } else { + copy(prefix1 = copyUpdate(prefix1, index, elem)) + } + } else throw ioob(index) + } + + override def appended[B >: A](elem: B): Vector[B] = { + if (suffix1.length < WIDTH ) copy(suffix1 = copyAppend1(suffix1, elem), length0 = length0+1) + else if(suffix2.length < WIDTH-1) copy(suffix2 = copyAppend(suffix2, suffix1), suffix1 = wrap1(elem), length0 = length0+1) + else if(data3.length < WIDTH-2) copy(data3 = copyAppend(data3, copyAppend(suffix2, suffix1)), suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else new Vector4(prefix1, len1, prefix2, len12, data3, (WIDTH-2)*WIDTH2 + len12, empty4, wrap3(copyAppend(suffix2, suffix1)), empty2, wrap1(elem), length0+1) + } + + override def prepended[B >: A](elem: B): Vector[B] = { + if (len1 < WIDTH ) copy(prefix1 = copyPrepend1(elem, prefix1), len1 = len1+1, len12 = len12+1, length0 = length0+1) + else if(len12 < WIDTH2 ) copy(prefix1 = wrap1(elem), len1 = 1, prefix2 = copyPrepend(prefix1, prefix2), len12 = len12+1, length0 = length0+1) + else if(data3.length < WIDTH-2) copy(prefix1 = wrap1(elem), len1 = 1, prefix2 = empty2, len12 = 1, data3 = copyPrepend(copyPrepend(prefix1, prefix2), data3), length0 = length0+1) + else new Vector4(wrap1(elem), 1, empty2, 1, wrap3(copyPrepend(prefix1, prefix2)), len12+1, empty4, data3, suffix2, suffix1, length0+1) + } + + override def map[B](f: A => B): Vector[B] = + copy(prefix1 = mapElems1(prefix1, f), prefix2 = mapElems(2, prefix2, f), + data3 = mapElems(3, data3, f), + suffix2 = mapElems(2, suffix2, f), suffix1 = mapElems1(suffix1, f)) + + protected[this] def slice0(lo: Int, hi: Int): Vector[A] = { + val b = new VectorSliceBuilder(lo, hi) + b.consider(1, prefix1) + b.consider(2, prefix2) + b.consider(3, data3) + b.consider(2, suffix2) + b.consider(1, suffix1) + b.result() + } + + override def tail: Vector[A] = + if(len1 > 1) copy(prefix1 = copyTail(prefix1), len1 = len1-1, len12 = len12-1, length0 = length0-1) + else slice0(1, length0) + + override def init: Vector[A] = + if(suffix1.length > 1) copy(suffix1 = copyInit(suffix1), length0 = length0-1) + else slice0(0, length0-1) + + protected[immutable] def vectorSliceCount: Int = 5 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + case 0 => prefix1 + case 1 => prefix2 + case 2 => data3 + case 3 => suffix2 + case 4 => suffix1 + } + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = (idx: @switch) match { + case 0 => len1 + case 1 => len12 + case 2 => len12 + data3.length*WIDTH2 + case 3 => length0 - suffix1.length + case 4 => length0 + } + + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + prepend1IfSpace(prefix1, prefix) match { + case null => super.prependedAll0(prefix, k) + case prefix1b => + val diff = prefix1b.length - prefix1.length + copy(prefix1 = prefix1b, + len1 = len1 + diff, + len12 = len12 + diff, + length0 = length0 + diff, + ) + } + + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + val suffix1b = append1IfSpace(suffix1, suffix) + if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) + else super.appendedAll0(suffix, k) + } +} + + +/** 4-dimensional radix-balanced finger tree */ +private final class Vector4[+A](_prefix1: Arr1, private[immutable] val len1: Int, + private[immutable] val prefix2: Arr2, private[immutable] val len12: Int, + private[immutable] val prefix3: Arr3, private[immutable] val len123: Int, + private[immutable] val data4: Arr4, + private[immutable] val suffix3: Arr3, private[immutable] val suffix2: Arr2, _suffix1: Arr1, + _length0: Int) extends BigVector[A](_prefix1, _suffix1, _length0) { + + @inline private[this] def copy(prefix1: Arr1 = prefix1, len1: Int = len1, + prefix2: Arr2 = prefix2, len12: Int = len12, + prefix3: Arr3 = prefix3, len123: Int = len123, + data4: Arr4 = data4, + suffix3: Arr3 = suffix3, suffix2: Arr2 = suffix2, suffix1: Arr1 = suffix1, + length0: Int = length0) = + new Vector4(prefix1, len1, prefix2, len12, prefix3, len123, data4, suffix3, suffix2, suffix1, length0) + + @inline def apply(index: Int): A = { + if(index >= 0 && index < length0) { + val io = index - len123 + if(io >= 0) { + val i4 = io >>> BITS3 + val i3 = (io >>> BITS2) & MASK + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if(i4 < data4.length) data4(i4)(i3)(i2)(i1) + else if(i3 < suffix3.length) suffix3(i3)(i2)(i1) + else if(i2 < suffix2.length) suffix2(i2)(i1) + else suffix1(i1) + } else if(index >= len12) { + val io = index - len12 + prefix3(io >>> BITS2)((io >>> BITS) & MASK)(io & MASK) + } else if(index >= len1) { + val io = index - len1 + prefix2(io >>> BITS)(io & MASK) + } else prefix1(index) + }.asInstanceOf[A] else throw ioob(index) + } + + override def updated[B >: A](index: Int, elem: B): Vector[B] = { + if(index >= 0 && index < length0) { + if(index >= len123) { + val io = index - len123 + val i4 = io >>> BITS3 + val i3 = (io >>> BITS2) & MASK + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if (i4 < data4.length ) copy(data4 = copyUpdate(data4, i4, i3, i2, i1, elem)) + else if(i3 < suffix3.length) copy(suffix3 = copyUpdate(suffix3, i3, i2, i1, elem)) + else if(i2 < suffix2.length) copy(suffix2 = copyUpdate(suffix2, i2, i1, elem)) + else copy(suffix1 = copyUpdate(suffix1, i1, elem)) + } else if(index >= len12) { + val io = index - len12 + copy(prefix3 = copyUpdate(prefix3, io >>> BITS2, (io >>> BITS) & MASK, io & MASK, elem)) + } else if(index >= len1) { + val io = index - len1 + copy(prefix2 = copyUpdate(prefix2, io >>> BITS, io & MASK, elem)) + } else { + copy(prefix1 = copyUpdate(prefix1, index, elem)) + } + } else throw ioob(index) + } + + override def appended[B >: A](elem: B): Vector[B] = { + if (suffix1.length < WIDTH ) copy(suffix1 = copyAppend1(suffix1, elem), length0 = length0+1) + else if(suffix2.length < WIDTH-1) copy(suffix2 = copyAppend(suffix2, suffix1), suffix1 = wrap1(elem), length0 = length0+1) + else if(suffix3.length < WIDTH-1) copy(suffix3 = copyAppend(suffix3, copyAppend(suffix2, suffix1)), suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else if(data4.length < WIDTH-2) copy(data4 = copyAppend(data4, copyAppend(suffix3, copyAppend(suffix2, suffix1))), suffix3 = empty3, suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else new Vector5(prefix1, len1, prefix2, len12, prefix3, len123, data4, (WIDTH-2)*WIDTH3 + len123, empty5, wrap4(copyAppend(suffix3, copyAppend(suffix2, suffix1))), empty3, empty2, wrap1(elem), length0+1) + } + + override def prepended[B >: A](elem: B): Vector[B] = { + if (len1 < WIDTH ) copy(copyPrepend1(elem, prefix1), len1+1, len12 = len12+1, len123 = len123+1, length0 = length0+1) + else if(len12 < WIDTH2 ) copy(wrap1(elem), 1, copyPrepend(prefix1, prefix2), len12+1, len123 = len123+1, length0 = length0+1) + else if(len123 < WIDTH3 ) copy(wrap1(elem), 1, empty2, 1, copyPrepend(copyPrepend(prefix1, prefix2), prefix3), len123+1, length0 = length0+1) + else if(data4.length < WIDTH-2) copy(wrap1(elem), 1, empty2, 1, empty3, 1, copyPrepend(copyPrepend(copyPrepend(prefix1, prefix2), prefix3), data4), length0 = length0+1) + else new Vector5(wrap1(elem), 1, empty2, 1, empty3, 1, wrap4(copyPrepend(copyPrepend(prefix1, prefix2), prefix3)), len123+1, empty5, data4, suffix3, suffix2, suffix1, length0+1) + } + + override def map[B](f: A => B): Vector[B] = + copy(prefix1 = mapElems1(prefix1, f), prefix2 = mapElems(2, prefix2, f), prefix3 = mapElems(3, prefix3, f), + data4 = mapElems(4, data4, f), + suffix3 = mapElems(3, suffix3, f), suffix2 = mapElems(2, suffix2, f), suffix1 = mapElems1(suffix1, f)) + + protected[this] def slice0(lo: Int, hi: Int): Vector[A] = { + val b = new VectorSliceBuilder(lo, hi) + b.consider(1, prefix1) + b.consider(2, prefix2) + b.consider(3, prefix3) + b.consider(4, data4) + b.consider(3, suffix3) + b.consider(2, suffix2) + b.consider(1, suffix1) + b.result() + } + + override def tail: Vector[A] = + if(len1 > 1) copy(copyTail(prefix1), len1-1, len12 = len12-1, len123 = len123-1, length0 = length0-1) + else slice0(1, length0) + + override def init: Vector[A] = + if(suffix1.length > 1) copy(suffix1 = copyInit(suffix1), length0 = length0-1) + else slice0(0, length0-1) + + protected[immutable] def vectorSliceCount: Int = 7 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + case 0 => prefix1 + case 1 => prefix2 + case 2 => prefix3 + case 3 => data4 + case 4 => suffix3 + case 5 => suffix2 + case 6 => suffix1 + } + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = (idx: @switch) match { + case 0 => len1 + case 1 => len12 + case 2 => len123 + case 3 => len123 + data4.length*WIDTH3 + case 4 => len123 + data4.length*WIDTH3 + suffix3.length*WIDTH2 + case 5 => length0 - suffix1.length + case 6 => length0 + } + + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + prepend1IfSpace(prefix1, prefix) match { + case null => super.prependedAll0(prefix, k) + case prefix1b => + val diff = prefix1b.length - prefix1.length + copy(prefix1 = prefix1b, + len1 = len1 + diff, + len12 = len12 + diff, + len123 = len123 + diff, + length0 = length0 + diff, + ) + } + + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + val suffix1b = append1IfSpace(suffix1, suffix) + if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) + else super.appendedAll0(suffix, k) + } +} + + +/** 5-dimensional radix-balanced finger tree */ +private final class Vector5[+A](_prefix1: Arr1, private[immutable] val len1: Int, + private[immutable] val prefix2: Arr2, private[immutable] val len12: Int, + private[immutable] val prefix3: Arr3, private[immutable] val len123: Int, + private[immutable] val prefix4: Arr4, private[immutable] val len1234: Int, + private[immutable] val data5: Arr5, + private[immutable] val suffix4: Arr4, private[immutable] val suffix3: Arr3, private[immutable] val suffix2: Arr2, _suffix1: Arr1, + _length0: Int) extends BigVector[A](_prefix1, _suffix1, _length0) { + + @inline private[this] def copy(prefix1: Arr1 = prefix1, len1: Int = len1, + prefix2: Arr2 = prefix2, len12: Int = len12, + prefix3: Arr3 = prefix3, len123: Int = len123, + prefix4: Arr4 = prefix4, len1234: Int = len1234, + data5: Arr5 = data5, + suffix4: Arr4 = suffix4, suffix3: Arr3 = suffix3, suffix2: Arr2 = suffix2, suffix1: Arr1 = suffix1, + length0: Int = length0) = + new Vector5(prefix1, len1, prefix2, len12, prefix3, len123, prefix4, len1234, data5, suffix4, suffix3, suffix2, suffix1, length0) + + @inline def apply(index: Int): A = { + if(index >= 0 && index < length0) { + val io = index - len1234 + if(io >= 0) { + val i5 = io >>> BITS4 + val i4 = (io >>> BITS3) & MASK + val i3 = (io >>> BITS2) & MASK + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if(i5 < data5.length) data5(i5)(i4)(i3)(i2)(i1) + else if(i4 < suffix4.length) suffix4(i4)(i3)(i2)(i1) + else if(i3 < suffix3.length) suffix3(i3)(i2)(i1) + else if(i2 < suffix2.length) suffix2(i2)(i1) + else suffix1(i1) + } else if(index >= len123) { + val io = index - len123 + prefix4(io >>> BITS3)((io >>> BITS2) & MASK)((io >>> BITS) & MASK)(io & MASK) + } else if(index >= len12) { + val io = index - len12 + prefix3(io >>> BITS2)((io >>> BITS) & MASK)(io & MASK) + } else if(index >= len1) { + val io = index - len1 + prefix2(io >>> BITS)(io & MASK) + } else prefix1(index) + }.asInstanceOf[A] else throw ioob(index) + } + + override def updated[B >: A](index: Int, elem: B): Vector[B] = { + if(index >= 0 && index < length0) { + if(index >= len1234) { + val io = index - len1234 + val i5 = io >>> BITS4 + val i4 = (io >>> BITS3) & MASK + val i3 = (io >>> BITS2) & MASK + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if (i5 < data5.length ) copy(data5 = copyUpdate(data5, i5, i4, i3, i2, i1, elem)) + else if(i4 < suffix4.length) copy(suffix4 = copyUpdate(suffix4, i4, i3, i2, i1, elem)) + else if(i3 < suffix3.length) copy(suffix3 = copyUpdate(suffix3, i3, i2, i1, elem)) + else if(i2 < suffix2.length) copy(suffix2 = copyUpdate(suffix2, i2, i1, elem)) + else copy(suffix1 = copyUpdate(suffix1, i1, elem)) + } else if(index >= len123) { + val io = index - len123 + copy(prefix4 = copyUpdate(prefix4, io >>> BITS3, (io >>> BITS2) & MASK, (io >>> BITS) & MASK, io & MASK, elem)) + } else if(index >= len12) { + val io = index - len12 + copy(prefix3 = copyUpdate(prefix3, io >>> BITS2, (io >>> BITS) & MASK, io & MASK, elem)) + } else if(index >= len1) { + val io = index - len1 + copy(prefix2 = copyUpdate(prefix2, io >>> BITS, io & MASK, elem)) + } else { + copy(prefix1 = copyUpdate(prefix1, index, elem)) + } + } else throw ioob(index) + } + + override def appended[B >: A](elem: B): Vector[B] = { + if (suffix1.length < WIDTH ) copy(suffix1 = copyAppend1(suffix1, elem), length0 = length0+1) + else if(suffix2.length < WIDTH-1) copy(suffix2 = copyAppend(suffix2, suffix1), suffix1 = wrap1(elem), length0 = length0+1) + else if(suffix3.length < WIDTH-1) copy(suffix3 = copyAppend(suffix3, copyAppend(suffix2, suffix1)), suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else if(suffix4.length < WIDTH-1) copy(suffix4 = copyAppend(suffix4, copyAppend(suffix3, copyAppend(suffix2, suffix1))), suffix3 = empty3, suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else if(data5.length < WIDTH-2) copy(data5 = copyAppend(data5, copyAppend(suffix4, copyAppend(suffix3, copyAppend(suffix2, suffix1)))), suffix4 = empty4, suffix3 = empty3, suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else new Vector6(prefix1, len1, prefix2, len12, prefix3, len123, prefix4, len1234, data5, (WIDTH-2)*WIDTH4 + len1234, empty6, wrap5(copyAppend(suffix4, copyAppend(suffix3, copyAppend(suffix2, suffix1)))), empty4, empty3, empty2, wrap1(elem), length0+1) + } + + override def prepended[B >: A](elem: B): Vector[B] = { + if (len1 < WIDTH ) copy(copyPrepend1(elem, prefix1), len1+1, len12 = len12+1, len123 = len123+1, len1234 = len1234+1, length0 = length0+1) + else if(len12 < WIDTH2 ) copy(wrap1(elem), 1, copyPrepend(prefix1, prefix2), len12+1, len123 = len123+1, len1234 = len1234+1, length0 = length0+1) + else if(len123 < WIDTH3 ) copy(wrap1(elem), 1, empty2, 1, copyPrepend(copyPrepend(prefix1, prefix2), prefix3), len123+1, len1234 = len1234+1, length0 = length0+1) + else if(len1234 < WIDTH4 ) copy(wrap1(elem), 1, empty2, 1, empty3, 1, copyPrepend(copyPrepend(copyPrepend(prefix1, prefix2), prefix3), prefix4), len1234+1, length0 = length0+1) + else if(data5.length < WIDTH-2) copy(wrap1(elem), 1, empty2, 1, empty3, 1, empty4, 1, copyPrepend(copyPrepend(copyPrepend(copyPrepend(prefix1, prefix2), prefix3), prefix4), data5), length0 = length0+1) + else new Vector6(wrap1(elem), 1, empty2, 1, empty3, 1, empty4, 1, wrap5(copyPrepend(copyPrepend(copyPrepend(prefix1, prefix2), prefix3), prefix4)), len1234+1, empty6, data5, suffix4, suffix3, suffix2, suffix1, length0+1) + } + + override def map[B](f: A => B): Vector[B] = + copy(prefix1 = mapElems1(prefix1, f), prefix2 = mapElems(2, prefix2, f), prefix3 = mapElems(3, prefix3, f), prefix4 = mapElems(4, prefix4, f), + data5 = mapElems(5, data5, f), + suffix4 = mapElems(4, suffix4, f), suffix3 = mapElems(3, suffix3, f), suffix2 = mapElems(2, suffix2, f), suffix1 = mapElems1(suffix1, f)) + + protected[this] def slice0(lo: Int, hi: Int): Vector[A] = { + val b = new VectorSliceBuilder(lo, hi) + b.consider(1, prefix1) + b.consider(2, prefix2) + b.consider(3, prefix3) + b.consider(4, prefix4) + b.consider(5, data5) + b.consider(4, suffix4) + b.consider(3, suffix3) + b.consider(2, suffix2) + b.consider(1, suffix1) + b.result() + } + + override def tail: Vector[A] = + if(len1 > 1) copy(copyTail(prefix1), len1-1, len12 = len12-1, len123 = len123-1, len1234 = len1234-1, length0 = length0-1) + else slice0(1, length0) + + override def init: Vector[A] = + if(suffix1.length > 1) copy(suffix1 = copyInit(suffix1), length0 = length0-1) + else slice0(0, length0-1) + + protected[immutable] def vectorSliceCount: Int = 9 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + case 0 => prefix1 + case 1 => prefix2 + case 2 => prefix3 + case 3 => prefix4 + case 4 => data5 + case 5 => suffix4 + case 6 => suffix3 + case 7 => suffix2 + case 8 => suffix1 + } + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = (idx: @switch) match { + case 0 => len1 + case 1 => len12 + case 2 => len123 + case 3 => len1234 + case 4 => len1234 + data5.length*WIDTH4 + case 5 => len1234 + data5.length*WIDTH4 + suffix4.length*WIDTH3 + case 6 => len1234 + data5.length*WIDTH4 + suffix4.length*WIDTH3 + suffix3.length*WIDTH2 + case 7 => length0 - suffix1.length + case 8 => length0 + } + + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + prepend1IfSpace(prefix1, prefix) match { + case null => super.prependedAll0(prefix, k) + case prefix1b => + val diff = prefix1b.length - prefix1.length + copy(prefix1 = prefix1b, + len1 = len1 + diff, + len12 = len12 + diff, + len123 = len123 + diff, + len1234 = len1234 + diff, + length0 = length0 + diff, + ) + } + + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + val suffix1b = append1IfSpace(suffix1, suffix) + if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) + else super.appendedAll0(suffix, k) + } +} + + +/** 6-dimensional radix-balanced finger tree */ +private final class Vector6[+A](_prefix1: Arr1, private[immutable] val len1: Int, + private[immutable] val prefix2: Arr2, private[immutable] val len12: Int, + private[immutable] val prefix3: Arr3, private[immutable] val len123: Int, + private[immutable] val prefix4: Arr4, private[immutable] val len1234: Int, + private[immutable] val prefix5: Arr5, private[immutable] val len12345: Int, + private[immutable] val data6: Arr6, + private[immutable] val suffix5: Arr5, private[immutable] val suffix4: Arr4, private[immutable] val suffix3: Arr3, private[immutable] val suffix2: Arr2, _suffix1: Arr1, + _length0: Int) extends BigVector[A](_prefix1, _suffix1, _length0) { + + @inline private[this] def copy(prefix1: Arr1 = prefix1, len1: Int = len1, + prefix2: Arr2 = prefix2, len12: Int = len12, + prefix3: Arr3 = prefix3, len123: Int = len123, + prefix4: Arr4 = prefix4, len1234: Int = len1234, + prefix5: Arr5 = prefix5, len12345: Int = len12345, + data6: Arr6 = data6, + suffix5: Arr5 = suffix5, suffix4: Arr4 = suffix4, suffix3: Arr3 = suffix3, suffix2: Arr2 = suffix2, suffix1: Arr1 = suffix1, + length0: Int = length0) = + new Vector6(prefix1, len1, prefix2, len12, prefix3, len123, prefix4, len1234, prefix5, len12345, data6, suffix5, suffix4, suffix3, suffix2, suffix1, length0) + + @inline def apply(index: Int): A = { + if(index >= 0 && index < length0) { + val io = index - len12345 + if(io >= 0) { + val i6 = io >>> BITS5 + val i5 = (io >>> BITS4) & MASK + val i4 = (io >>> BITS3) & MASK + val i3 = (io >>> BITS2) & MASK + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if(i6 < data6.length) data6(i6)(i5)(i4)(i3)(i2)(i1) + else if(i5 < suffix5.length) suffix5(i5)(i4)(i3)(i2)(i1) + else if(i4 < suffix4.length) suffix4(i4)(i3)(i2)(i1) + else if(i3 < suffix3.length) suffix3(i3)(i2)(i1) + else if(i2 < suffix2.length) suffix2(i2)(i1) + else suffix1(i1) + } else if(index >= len1234) { + val io = index - len1234 + prefix5(io >>> BITS4)((io >>> BITS3) & MASK)((io >>> BITS2) & MASK)((io >>> BITS) & MASK)(io & MASK) + } else if(index >= len123) { + val io = index - len123 + prefix4(io >>> BITS3)((io >>> BITS2) & MASK)((io >>> BITS) & MASK)(io & MASK) + } else if(index >= len12) { + val io = index - len12 + prefix3(io >>> BITS2)((io >>> BITS) & MASK)(io & MASK) + } else if(index >= len1) { + val io = index - len1 + prefix2(io >>> BITS)(io & MASK) + } else prefix1(index) + }.asInstanceOf[A] else throw ioob(index) + } + + override def updated[B >: A](index: Int, elem: B): Vector[B] = { + if(index >= 0 && index < length0) { + if(index >= len12345) { + val io = index - len12345 + val i6 = io >>> BITS5 + val i5 = (io >>> BITS4) & MASK + val i4 = (io >>> BITS3) & MASK + val i3 = (io >>> BITS2) & MASK + val i2 = (io >>> BITS) & MASK + val i1 = io & MASK + if (i6 < data6.length ) copy(data6 = copyUpdate(data6, i6, i5, i4, i3, i2, i1, elem)) + else if(i5 < suffix5.length) copy(suffix5 = copyUpdate(suffix5, i5, i4, i3, i2, i1, elem)) + else if(i4 < suffix4.length) copy(suffix4 = copyUpdate(suffix4, i4, i3, i2, i1, elem)) + else if(i3 < suffix3.length) copy(suffix3 = copyUpdate(suffix3, i3, i2, i1, elem)) + else if(i2 < suffix2.length) copy(suffix2 = copyUpdate(suffix2, i2, i1, elem)) + else copy(suffix1 = copyUpdate(suffix1, i1, elem)) + } else if(index >= len1234) { + val io = index - len1234 + copy(prefix5 = copyUpdate(prefix5, io >>> BITS4, (io >>> BITS3) & MASK, (io >>> BITS2) & MASK, (io >>> BITS) & MASK, io & MASK, elem)) + } else if(index >= len123) { + val io = index - len123 + copy(prefix4 = copyUpdate(prefix4, io >>> BITS3, (io >>> BITS2) & MASK, (io >>> BITS) & MASK, io & MASK, elem)) + } else if(index >= len12) { + val io = index - len12 + copy(prefix3 = copyUpdate(prefix3, io >>> BITS2, (io >>> BITS) & MASK, io & MASK, elem)) + } else if(index >= len1) { + val io = index - len1 + copy(prefix2 = copyUpdate(prefix2, io >>> BITS, io & MASK, elem)) + } else { + copy(prefix1 = copyUpdate(prefix1, index, elem)) + } + } else throw ioob(index) + } + + override def appended[B >: A](elem: B): Vector[B] = { + if (suffix1.length < WIDTH ) copy(suffix1 = copyAppend1(suffix1, elem), length0 = length0+1) + else if(suffix2.length < WIDTH-1 ) copy(suffix2 = copyAppend(suffix2, suffix1), suffix1 = wrap1(elem), length0 = length0+1) + else if(suffix3.length < WIDTH-1 ) copy(suffix3 = copyAppend(suffix3, copyAppend(suffix2, suffix1)), suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else if(suffix4.length < WIDTH-1 ) copy(suffix4 = copyAppend(suffix4, copyAppend(suffix3, copyAppend(suffix2, suffix1))), suffix3 = empty3, suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else if(suffix5.length < WIDTH-1 ) copy(suffix5 = copyAppend(suffix5, copyAppend(suffix4, copyAppend(suffix3, copyAppend(suffix2, suffix1)))), suffix4 = empty4, suffix3 = empty3, suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else if(data6.length < LASTWIDTH-2) copy(data6 = copyAppend(data6, copyAppend(suffix5, copyAppend(suffix4, copyAppend(suffix3, copyAppend(suffix2, suffix1))))), suffix5 = empty5, suffix4 = empty4, suffix3 = empty3, suffix2 = empty2, suffix1 = wrap1(elem), length0 = length0+1) + else throw new IllegalArgumentException + } + + override def prepended[B >: A](elem: B): Vector[B] = { + if (len1 < WIDTH ) copy(copyPrepend1(elem, prefix1), len1+1, len12 = len12+1, len123 = len123+1, len1234 = len1234+1, len12345 = len12345+1, length0 = length0+1) + else if(len12 < WIDTH2 ) copy(wrap1(elem), 1, copyPrepend(prefix1, prefix2), len12+1, len123 = len123+1, len1234 = len1234+1, len12345 = len12345+1, length0 = length0+1) + else if(len123 < WIDTH3 ) copy(wrap1(elem), 1, empty2, 1, copyPrepend(copyPrepend(prefix1, prefix2), prefix3), len123+1, len1234 = len1234+1, len12345 = len12345+1, length0 = length0+1) + else if(len1234 < WIDTH4 ) copy(wrap1(elem), 1, empty2, 1, empty3, 1, copyPrepend(copyPrepend(copyPrepend(prefix1, prefix2), prefix3), prefix4), len1234+1, len12345 = len12345+1, length0 = length0+1) + else if(len12345 < WIDTH5 ) copy(wrap1(elem), 1, empty2, 1, empty3, 1, empty4, 1, copyPrepend(copyPrepend(copyPrepend(copyPrepend(prefix1, prefix2), prefix3), prefix4), prefix5), len12345+1, length0 = length0+1) + else if(data6.length < LASTWIDTH-2) copy(wrap1(elem), 1, empty2, 1, empty3, 1, empty4, 1, empty5, 1, copyPrepend(copyPrepend(copyPrepend(copyPrepend(copyPrepend(prefix1, prefix2), prefix3), prefix4), prefix5), data6), length0 = length0+1) + else throw new IllegalArgumentException + } + + override def map[B](f: A => B): Vector[B] = + copy(prefix1 = mapElems1(prefix1, f), prefix2 = mapElems(2, prefix2, f), prefix3 = mapElems(3, prefix3, f), prefix4 = mapElems(4, prefix4, f), prefix5 = mapElems(5, prefix5, f), + data6 = mapElems(6, data6, f), + suffix5 = mapElems(5, suffix5, f), suffix4 = mapElems(4, suffix4, f), suffix3 = mapElems(3, suffix3, f), suffix2 = mapElems(2, suffix2, f), suffix1 = mapElems1(suffix1, f)) + + protected[this] def slice0(lo: Int, hi: Int): Vector[A] = { + val b = new VectorSliceBuilder(lo, hi) + b.consider(1, prefix1) + b.consider(2, prefix2) + b.consider(3, prefix3) + b.consider(4, prefix4) + b.consider(5, prefix5) + b.consider(6, data6) + b.consider(5, suffix5) + b.consider(4, suffix4) + b.consider(3, suffix3) + b.consider(2, suffix2) + b.consider(1, suffix1) + b.result() + } + + override def tail: Vector[A] = + if(len1 > 1) copy(copyTail(prefix1), len1-1, len12 = len12-1, len123 = len123-1, len1234 = len1234-1, len12345 = len12345-1, length0 = length0-1) + else slice0(1, length0) + + override def init: Vector[A] = + if(suffix1.length > 1) copy(suffix1 = copyInit(suffix1), length0 = length0-1) + else slice0(0, length0-1) + + protected[immutable] def vectorSliceCount: Int = 11 + protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = (idx: @switch) match { + case 0 => prefix1 + case 1 => prefix2 + case 2 => prefix3 + case 3 => prefix4 + case 4 => prefix5 + case 5 => data6 + case 6 => suffix5 + case 7 => suffix4 + case 8 => suffix3 + case 9 => suffix2 + case 10 => suffix1 + } + protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = (idx: @switch) match { + case 0 => len1 + case 1 => len12 + case 2 => len123 + case 3 => len1234 + case 4 => len12345 + case 5 => len12345 + data6.length*WIDTH5 + case 6 => len12345 + data6.length*WIDTH5 + suffix5.length*WIDTH4 + case 7 => len12345 + data6.length*WIDTH5 + suffix5.length*WIDTH4 + suffix4.length*WIDTH3 + case 8 => len12345 + data6.length*WIDTH5 + suffix5.length*WIDTH4 + suffix4.length*WIDTH3 + suffix3.length*WIDTH2 + case 9 => length0 - suffix1.length + case 10 => length0 + } + + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + prepend1IfSpace(prefix1, prefix) match { + case null => super.prependedAll0(prefix, k) + case prefix1b => + val diff = prefix1b.length - prefix1.length + copy(prefix1 = prefix1b, + len1 = len1 + diff, + len12 = len12 + diff, + len123 = len123 + diff, + len1234 = len1234 + diff, + len12345 = len12345 + diff, + length0 = length0 + diff, + ) + } + + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + val suffix1b = append1IfSpace(suffix1, suffix) + if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) + else super.appendedAll0(suffix, k) + } +} + + +/** Helper class for vector slicing. It is initialized with the validated start and end index, + * then the vector slices are added in succession with `consider`. No matter what the dimension + * of the originating vector is or where the cut is performed, this always results in a + * structure with the highest-dimensional data in the middle and fingers of decreasing dimension + * at both ends, which can be turned into a new vector with very little rebalancing. + */ +private final class VectorSliceBuilder(lo: Int, hi: Int) { + //println(s"***** VectorSliceBuilder($lo, $hi)") + + private[this] val slices = new Array[Array[AnyRef]](11) + private[this] var len, pos, maxDim = 0 + + @inline private[this] def prefixIdx(n: Int) = n-1 + @inline private[this] def suffixIdx(n: Int) = 11-n + + def consider[T <: AnyRef](n: Int, a: Array[T]): Unit = { + //println(s"***** consider($n, /${a.length})") + val count = a.length * (1 << (BITS*(n-1))) + val lo0 = mmax(lo-pos, 0) + val hi0 = mmin(hi-pos, count) + if(hi0 > lo0) { + addSlice(n, a, lo0, hi0) + len += (hi0 - lo0) + } + pos += count + } + + private[this] def addSlice[T <: AnyRef](n: Int, a: Array[T], lo: Int, hi: Int): Unit = { + //println(s"***** addSlice($n, /${a.length}, $lo, $hi)") + if(n == 1) { + add(1, copyOrUse(a, lo, hi)) + } else { + val bitsN = BITS * (n-1) + val widthN = 1 << bitsN + val loN = lo >>> bitsN + val hiN = hi >>> bitsN + val loRest = lo & (widthN - 1) + val hiRest = hi & (widthN - 1) + //println(s"***** bitsN=$bitsN, loN=$loN, hiN=$hiN, loRest=$loRest, hiRest=$hiRest") + if(loRest == 0) { + if(hiRest == 0) { + add(n, copyOrUse(a, loN, hiN)) + } else { + if(hiN > loN) add(n, copyOrUse(a, loN, hiN)) + addSlice(n-1, a(hiN).asInstanceOf[Array[AnyRef]], 0, hiRest) + } + } else { + if(hiN == loN) { + addSlice(n-1, a(loN).asInstanceOf[Array[AnyRef]], loRest, hiRest) + } else { + addSlice(n-1, a(loN).asInstanceOf[Array[AnyRef]], loRest, widthN) + if(hiRest == 0) { + if(hiN > loN+1) add(n, copyOrUse(a, loN+1, hiN)) + } else { + if(hiN > loN+1) add(n, copyOrUse(a, loN+1, hiN)) + addSlice(n-1, a(hiN).asInstanceOf[Array[AnyRef]], 0, hiRest) + } + } + } + } + } + + private[this] def add[T <: AnyRef](n: Int, a: Array[T]): Unit = { + //println(s"***** add($n, /${a.length})") + val idx = + if(n <= maxDim) suffixIdx(n) + else { maxDim = n; prefixIdx(n) } + slices(idx) = a.asInstanceOf[Array[AnyRef]] + } + + def result[A](): Vector[A] = { + //println(s"***** result: $len, $maxDim") + if(len <= 32) { + if(len == 0) Vector0 + else { + val prefix1 = slices(prefixIdx(1)) + val suffix1 = slices(suffixIdx(1)) + //println(s"***** prefix1: ${if(prefix1 == null) "null" else prefix1.mkString("[", ",", "]")}, suffix1: ${if(suffix1 == null) "null" else suffix1.mkString("[", ",", "]")}") + val a: Arr1 = + if(prefix1 ne null) { + if(suffix1 ne null) concatArrays(prefix1, suffix1) + else prefix1 + } else if(suffix1 ne null) suffix1 + else { + val prefix2 = slices(prefixIdx(2)).asInstanceOf[Arr2] + if(prefix2 ne null) prefix2(0) + else { + val suffix2 = slices(suffixIdx(2)).asInstanceOf[Arr2] + suffix2(0) + } + } + new Vector1(a) + } + } else { + balancePrefix(1) + balanceSuffix(1) + var resultDim = maxDim + if(resultDim < 6) { + val pre = slices(prefixIdx(maxDim)) + val suf = slices(suffixIdx(maxDim)) + if((pre ne null) && (suf ne null)) { + // The highest-dimensional data consists of two slices: concatenate if they fit into the main data array, + // otherwise increase the dimension + if(pre.length + suf.length <= WIDTH-2) { + slices(prefixIdx(maxDim)) = concatArrays(pre, suf) + slices(suffixIdx(maxDim)) = null + } else resultDim += 1 + } else { + // A single highest-dimensional slice could have length WIDTH-1 if it came from a prefix or suffix but we + // only allow WIDTH-2 for the main data, so increase the dimension in this case + val one = if(pre ne null) pre else suf + if(one.length > WIDTH-2) resultDim += 1 + } + } + val prefix1 = slices(prefixIdx(1)) + val suffix1 = slices(suffixIdx(1)) + val len1 = prefix1.length + val res = (resultDim: @switch) match { + case 2 => + val data2 = dataOr(2, empty2) + new Vector2[A](prefix1, len1, data2, suffix1, len) + case 3 => + val prefix2 = prefixOr(2, empty2) + val data3 = dataOr(3, empty3) + val suffix2 = suffixOr(2, empty2) + val len12 = len1 + (prefix2.length * WIDTH) + new Vector3[A](prefix1, len1, prefix2, len12, data3, suffix2, suffix1, len) + case 4 => + val prefix2 = prefixOr(2, empty2) + val prefix3 = prefixOr(3, empty3) + val data4 = dataOr(4, empty4) + val suffix3 = suffixOr(3, empty3) + val suffix2 = suffixOr(2, empty2) + val len12 = len1 + (prefix2.length * WIDTH) + val len123 = len12 + (prefix3.length * WIDTH2) + new Vector4[A](prefix1, len1, prefix2, len12, prefix3, len123, data4, suffix3, suffix2, suffix1, len) + case 5 => + val prefix2 = prefixOr(2, empty2) + val prefix3 = prefixOr(3, empty3) + val prefix4 = prefixOr(4, empty4) + val data5 = dataOr(5, empty5) + val suffix4 = suffixOr(4, empty4) + val suffix3 = suffixOr(3, empty3) + val suffix2 = suffixOr(2, empty2) + val len12 = len1 + (prefix2.length * WIDTH) + val len123 = len12 + (prefix3.length * WIDTH2) + val len1234 = len123 + (prefix4.length * WIDTH3) + new Vector5[A](prefix1, len1, prefix2, len12, prefix3, len123, prefix4, len1234, data5, suffix4, suffix3, suffix2, suffix1, len) + case 6 => + val prefix2 = prefixOr(2, empty2) + val prefix3 = prefixOr(3, empty3) + val prefix4 = prefixOr(4, empty4) + val prefix5 = prefixOr(5, empty5) + val data6 = dataOr(6, empty6) + val suffix5 = suffixOr(5, empty5) + val suffix4 = suffixOr(4, empty4) + val suffix3 = suffixOr(3, empty3) + val suffix2 = suffixOr(2, empty2) + val len12 = len1 + (prefix2.length * WIDTH) + val len123 = len12 + (prefix3.length * WIDTH2) + val len1234 = len123 + (prefix4.length * WIDTH3) + val len12345 = len1234 + (prefix5.length * WIDTH4) + new Vector6[A](prefix1, len1, prefix2, len12, prefix3, len123, prefix4, len1234, prefix5, len12345, data6, suffix5, suffix4, suffix3, suffix2, suffix1, len) + } + res + } + } + + @inline private[this] def prefixOr[T <: AnyRef](n: Int, a: Array[T]): Array[T] = { + val p = slices(prefixIdx(n)) + if(p ne null) p.asInstanceOf[Array[T]] else a + } + + @inline private[this] def suffixOr[T <: AnyRef](n: Int, a: Array[T]): Array[T] = { + val s = slices(suffixIdx(n)) + if(s ne null) s.asInstanceOf[Array[T]] else a + } + + @inline private[this] def dataOr[T <: AnyRef](n: Int, a: Array[T]): Array[T] = { + val p = slices(prefixIdx(n)) + if(p ne null) p.asInstanceOf[Array[T]] + else { + val s = slices(suffixIdx(n)) + if(s ne null) s.asInstanceOf[Array[T]] else a + } + } + + /** Ensure prefix is not empty */ + private[this] def balancePrefix(n: Int): Unit = { + if(slices(prefixIdx(n)) eq null) { + if(n == maxDim) { + slices(prefixIdx(n)) = slices(suffixIdx(n)) + slices(suffixIdx(n)) = null + } else { + balancePrefix(n+1) + val preN1 = slices(prefixIdx(n+1)).asInstanceOf[Array[Array[AnyRef]]] + //assert(preN1 ne null) + slices(prefixIdx(n)) = preN1(0) + if(preN1.length == 1) { + slices(prefixIdx(n+1)) = null + if((maxDim == n+1) && (slices(suffixIdx(n+1)) eq null)) maxDim = n + } else { + slices(prefixIdx(n+1)) = copyOfRange(preN1, 1, preN1.length).asInstanceOf[Array[AnyRef]] + } + } + } + } + + /** Ensure suffix is not empty */ + private[this] def balanceSuffix(n: Int): Unit = { + if(slices(suffixIdx(n)) eq null) { + if(n == maxDim) { + slices(suffixIdx(n)) = slices(prefixIdx(n)) + slices(prefixIdx(n)) = null + } else { + balanceSuffix(n+1) + val sufN1 = slices(suffixIdx(n+1)).asInstanceOf[Array[Array[AnyRef]]] + //assert(sufN1 ne null, s"n=$n, maxDim=$maxDim, slices=${slices.mkString(",")}") + slices(suffixIdx(n)) = sufN1(sufN1.length-1) + if(sufN1.length == 1) { + slices(suffixIdx(n+1)) = null + if((maxDim == n+1) && (slices(prefixIdx(n+1)) eq null)) maxDim = n + } else { + slices(suffixIdx(n+1)) = copyOfRange(sufN1, 0, sufN1.length-1).asInstanceOf[Array[AnyRef]] + } + } + } + } + + override def toString: String = + s"VectorSliceBuilder(lo=$lo, hi=$hi, len=$len, pos=$pos, maxDim=$maxDim)" + + private[immutable] def getSlices: Array[Array[AnyRef]] = slices +} + + +final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { + + private[this] var a6: Arr6 = _ + private[this] var a5: Arr5 = _ + private[this] var a4: Arr4 = _ + private[this] var a3: Arr3 = _ + private[this] var a2: Arr2 = _ + private[this] var a1: Arr1 = new Arr1(WIDTH) + private[this] var len1, lenRest, offset = 0 + private[this] var prefixIsRightAligned = false + private[this] var depth = 1 + + @inline private[this] final def setLen(i: Int): Unit = { + len1 = i & MASK + lenRest = i - len1 + } + + override def knownSize: Int = len1 + lenRest - offset + + @inline def size: Int = knownSize + @inline def isEmpty: Boolean = knownSize == 0 + @inline def nonEmpty: Boolean = knownSize != 0 + + def clear(): Unit = { + a6 = null + a5 = null + a4 = null + a3 = null + a2 = null + a1 = new Arr1(WIDTH) + len1 = 0 + lenRest = 0 + offset = 0 + prefixIsRightAligned = false + depth = 1 + } + + private[immutable] def initSparse(size: Int, elem: A): Unit = { + setLen(size) + Arrays.fill(a1, elem) + if(size > WIDTH) { + a2 = new Array(WIDTH) + Arrays.fill(a2.asInstanceOf[Array[AnyRef]], a1) + if(size > WIDTH2) { + a3 = new Array(WIDTH) + Arrays.fill(a3.asInstanceOf[Array[AnyRef]], a2) + if(size > WIDTH3) { + a4 = new Array(WIDTH) + Arrays.fill(a4.asInstanceOf[Array[AnyRef]], a3) + if(size > WIDTH4) { + a5 = new Array(WIDTH) + Arrays.fill(a5.asInstanceOf[Array[AnyRef]], a4) + if(size > WIDTH5) { + a6 = new Array(LASTWIDTH) + Arrays.fill(a6.asInstanceOf[Array[AnyRef]], a5) + depth = 6 + } else depth = 5 + } else depth = 4 + } else depth = 3 + } else depth = 2 + } else depth = 1 + } + + private[immutable] def initFrom(prefix1: Arr1): Unit = { + depth = 1 + setLen(prefix1.length) + a1 = copyOrUse(prefix1, 0, WIDTH) + if(len1 == 0 && lenRest > 0) { + // force advance() on next addition: + len1 = WIDTH + lenRest -= WIDTH + } + } + + private[immutable] def initFrom(v: Vector[_]): this.type = { + (v.vectorSliceCount: @switch) match { + case 0 => + case 1 => + val v1 = v.asInstanceOf[Vector1[_]] + depth = 1 + setLen(v1.prefix1.length) + a1 = copyOrUse(v1.prefix1, 0, WIDTH) + case 3 => + val v2 = v.asInstanceOf[Vector2[_]] + val d2 = v2.data2 + a1 = copyOrUse(v2.suffix1, 0, WIDTH) + depth = 2 + offset = WIDTH - v2.len1 + setLen(v2.length0 + offset) + a2 = new Arr2(WIDTH) + a2(0) = v2.prefix1 + System.arraycopy(d2, 0, a2, 1, d2.length) + a2(d2.length+1) = a1 + case 5 => + val v3 = v.asInstanceOf[Vector3[_]] + val d3 = v3.data3 + val s2 = v3.suffix2 + a1 = copyOrUse(v3.suffix1, 0, WIDTH) + depth = 3 + offset = WIDTH2 - v3.len12 + setLen(v3.length0 + offset) + a3 = new Arr3(WIDTH) + a3(0) = copyPrepend(v3.prefix1, v3.prefix2) + System.arraycopy(d3, 0, a3, 1, d3.length) + a2 = copyOf(s2, WIDTH) + a3(d3.length+1) = a2 + a2(s2.length) = a1 + case 7 => + val v4 = v.asInstanceOf[Vector4[_]] + val d4 = v4.data4 + val s3 = v4.suffix3 + val s2 = v4.suffix2 + a1 = copyOrUse(v4.suffix1, 0, WIDTH) + depth = 4 + offset = WIDTH3 - v4.len123 + setLen(v4.length0 + offset) + a4 = new Arr4(WIDTH) + a4(0) = copyPrepend(copyPrepend(v4.prefix1, v4.prefix2), v4.prefix3) + System.arraycopy(d4, 0, a4, 1, d4.length) + a3 = copyOf(s3, WIDTH) + a2 = copyOf(s2, WIDTH) + a4(d4.length+1) = a3 + a3(s3.length) = a2 + a2(s2.length) = a1 + case 9 => + val v5 = v.asInstanceOf[Vector5[_]] + val d5 = v5.data5 + val s4 = v5.suffix4 + val s3 = v5.suffix3 + val s2 = v5.suffix2 + a1 = copyOrUse(v5.suffix1, 0, WIDTH) + depth = 5 + offset = WIDTH4 - v5.len1234 + setLen(v5.length0 + offset) + a5 = new Arr5(WIDTH) + a5(0) = copyPrepend(copyPrepend(copyPrepend(v5.prefix1, v5.prefix2), v5.prefix3), v5.prefix4) + System.arraycopy(d5, 0, a5, 1, d5.length) + a4 = copyOf(s4, WIDTH) + a3 = copyOf(s3, WIDTH) + a2 = copyOf(s2, WIDTH) + a5(d5.length+1) = a4 + a4(s4.length) = a3 + a3(s3.length) = a2 + a2(s2.length) = a1 + case 11 => + val v6 = v.asInstanceOf[Vector6[_]] + val d6 = v6.data6 + val s5 = v6.suffix5 + val s4 = v6.suffix4 + val s3 = v6.suffix3 + val s2 = v6.suffix2 + a1 = copyOrUse(v6.suffix1, 0, WIDTH) + depth = 6 + offset = WIDTH5 - v6.len12345 + setLen(v6.length0 + offset) + a6 = new Arr6(LASTWIDTH) + a6(0) = copyPrepend(copyPrepend(copyPrepend(copyPrepend(v6.prefix1, v6.prefix2), v6.prefix3), v6.prefix4), v6.prefix5) + System.arraycopy(d6, 0, a6, 1, d6.length) + a5 = copyOf(s5, WIDTH) + a4 = copyOf(s4, WIDTH) + a3 = copyOf(s3, WIDTH) + a2 = copyOf(s2, WIDTH) + a6(d6.length+1) = a5 + a5(s5.length) = a4 + a4(s4.length) = a3 + a3(s3.length) = a2 + a2(s2.length) = a1 + } + if(len1 == 0 && lenRest > 0) { + // force advance() on next addition: + len1 = WIDTH + lenRest -= WIDTH + } + this + } + + //TODO Make public; this method is only private for binary compatibility + private[collection] def alignTo(before: Int, bigVector: Vector[A]): this.type = { + if (len1 != 0 || lenRest != 0) + throw new UnsupportedOperationException("A non-empty VectorBuilder cannot be aligned retrospectively. Please call .reset() or use a new VectorBuilder.") + val (prefixLength, maxPrefixLength) = bigVector match { + case Vector0 => (0, 1) + case v1: Vector1[_] => (0, 1) + case v2: Vector2[_] => (v2.len1, WIDTH) + case v3: Vector3[_] => (v3.len12, WIDTH2) + case v4: Vector4[_] => (v4.len123, WIDTH3) + case v5: Vector5[_] => (v5.len1234, WIDTH4) + case v6: Vector6[_] => (v6.len12345, WIDTH5) + } + if (maxPrefixLength == 1) return this // does not really make sense to align for <= 32 element-vector + val overallPrefixLength = (before + prefixLength) % maxPrefixLength + offset = (maxPrefixLength - overallPrefixLength) % maxPrefixLength + // pretend there are already `offset` elements added + advanceN(offset & ~MASK) + len1 = offset & MASK + prefixIsRightAligned = true + this + } + + /** + * Removes `offset` leading `null`s in the prefix. + * This is needed after calling `alignTo` and subsequent additions, + * directly before the result is used for creating a new Vector. + * Note that the outermost array keeps its length to keep the + * Builder re-usable. + * + * example: + * a2 = Array(null, ..., null, Array(null, .., null, 0, 1, .., x), Array(x+1, .., x+32), ...) + * becomes + * a2 = Array(Array(0, 1, .., x), Array(x+1, .., x+32), ..., ?, ..., ?) + */ + private[this] def leftAlignPrefix(): Unit = { + @inline def shrinkOffsetIfToLarge(width: Int): Unit = { + val newOffset = offset % width + lenRest -= offset - newOffset + offset = newOffset + } + var a: Array[AnyRef] = null // the array we modify + var aParent: Array[AnyRef] = null // a's parent, so aParent(0) == a + if (depth >= 6) { + a = a6.asInstanceOf[Array[AnyRef]] + val i = offset >>> BITS5 + if (i > 0) System.arraycopy(a, i, a, 0, LASTWIDTH - i) + shrinkOffsetIfToLarge(WIDTH5) + if ((lenRest >>> BITS5) == 0) depth = 5 + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 5) { + if (a == null) a = a5.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS4) & MASK + if (depth == 5) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a5 = a.asInstanceOf[Arr5] + shrinkOffsetIfToLarge(WIDTH4) + if ((lenRest >>> BITS4) == 0) depth = 4 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 4) { + if (a == null) a = a4.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS3) & MASK + if (depth == 4) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a4 = a.asInstanceOf[Arr4] + shrinkOffsetIfToLarge(WIDTH3) + if ((lenRest >>> BITS3) == 0) depth = 3 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 3) { + if (a == null) a = a3.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS2) & MASK + if (depth == 3) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a3 = a.asInstanceOf[Arr3] + shrinkOffsetIfToLarge(WIDTH2) + if ((lenRest >>> BITS2) == 0) depth = 2 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 2) { + if (a == null) a = a2.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS) & MASK + if (depth == 2) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a2 = a.asInstanceOf[Arr2] + shrinkOffsetIfToLarge(WIDTH) + if ((lenRest >>> BITS) == 0) depth = 1 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 1) { + if (a == null) a = a1.asInstanceOf[Array[AnyRef]] + val i = offset & MASK + if (depth == 1) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a1 = a.asInstanceOf[Arr1] + len1 -= offset + offset = 0 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a + } + } + prefixIsRightAligned = false + } + + def addOne(elem: A): this.type = { + if(len1 == WIDTH) advance() + a1(len1) = elem.asInstanceOf[AnyRef] + len1 += 1 + this + } + + private[this] def addArr1(data: Arr1): Unit = { + val dl = data.length + if(dl > 0) { + if(len1 == WIDTH) advance() + val copy1 = mmin(WIDTH-len1, dl) + val copy2 = dl - copy1 + System.arraycopy(data, 0, a1, len1, copy1) + len1 += copy1 + if(copy2 > 0) { + advance() + System.arraycopy(data, copy1, a1, 0, copy2) + len1 += copy2 + } + } + } + + private[this] def addArrN(slice: Array[AnyRef], dim: Int): Unit = { +// assert(dim >= 2) +// assert(lenRest % WIDTH == 0) +// assert(len1 == 0 || len1 == WIDTH) + if (slice.isEmpty) return + if (len1 == WIDTH) advance() + val sl = slice.length + (dim: @switch) match { + case 2 => + // lenRest is always a multiple of WIDTH + val copy1 = mmin(((WIDTH2 - lenRest) >>> BITS) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS) & MASK + System.arraycopy(slice, 0, a2, destPos, copy1) + advanceN(WIDTH * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a2, 0, copy2) + advanceN(WIDTH * copy2) + } + case 3 => + if (lenRest % WIDTH2 != 0) { + // lenRest is not multiple of WIDTH2, so this slice does not align, need to try lower dimension + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 2)) + return + } + val copy1 = mmin(((WIDTH3 - lenRest) >>> BITS2) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS2) & MASK + System.arraycopy(slice, 0, a3, destPos, copy1) + advanceN(WIDTH2 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a3, 0, copy2) + advanceN(WIDTH2 * copy2) + } + case 4 => + if (lenRest % WIDTH3 != 0) { + // lenRest is not multiple of WIDTH3, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 3)) + return + } + val copy1 = mmin(((WIDTH4 - lenRest) >>> BITS3) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS3) & MASK + System.arraycopy(slice, 0, a4, destPos, copy1) + advanceN(WIDTH3 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a4, 0, copy2) + advanceN(WIDTH3 * copy2) + } + case 5 => + if (lenRest % WIDTH4 != 0) { + // lenRest is not multiple of WIDTH4, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 4)) + return + } + val copy1 = mmin(((WIDTH5 - lenRest) >>> BITS4) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS4) & MASK + System.arraycopy(slice, 0, a5, destPos, copy1) + advanceN(WIDTH4 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a5, 0, copy2) + advanceN(WIDTH4 * copy2) + } + case 6 => // note width is now LASTWIDTH + if (lenRest % WIDTH5 != 0) { + // lenRest is not multiple of WIDTH5, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 5)) + return + } + val copy1 = sl + // there is no copy2 because there can't be another a6 to copy to + val destPos = lenRest >>> BITS5 + if (destPos + copy1 > LASTWIDTH) + throw new IllegalArgumentException("exceeding 2^31 elements") + System.arraycopy(slice, 0, a6, destPos, copy1) + advanceN(WIDTH5 * copy1) + } + } + + private[this] def addVector(xs: Vector[A]): this.type = { + val sliceCount = xs.vectorSliceCount + var sliceIdx = 0 + while(sliceIdx < sliceCount) { + val slice = xs.vectorSlice(sliceIdx) + vectorSliceDim(sliceCount, sliceIdx) match { + case 1 => addArr1(slice.asInstanceOf[Arr1]) + case n if len1 == WIDTH || len1 == 0 => + addArrN(slice.asInstanceOf[Array[AnyRef]], n) + case n => foreachRec(n-2, slice, addArr1) + } + sliceIdx += 1 + } + this + } + + override def addAll(xs: IterableOnce[A]): this.type = xs match { + case v: Vector[_] => + if(len1 == 0 && lenRest == 0 && !prefixIsRightAligned) initFrom(v) + else addVector(v.asInstanceOf[Vector[A]]) + case _ => + super.addAll(xs) + } + + private[this] def advance(): Unit = { + val idx = lenRest + WIDTH + val xor = idx ^ lenRest + lenRest = idx + len1 = 0 + advance1(idx, xor) + } + + private[this] def advanceN(n: Int): Unit = if (n > 0) { + // assert(n % 32 == 0) + val idx = lenRest + n + val xor = idx ^ lenRest + lenRest = idx + len1 = 0 + advance1(idx, xor) + } + + private[this] def advance1(idx: Int, xor: Int): Unit = { + if (xor <= 0) { // level = 6 or something very unexpected happened + throw new IllegalArgumentException(s"advance1($idx, $xor): a1=$a1, a2=$a2, a3=$a3, a4=$a4, a5=$a5, a6=$a6, depth=$depth") + } else if (xor < WIDTH2) { // level = 1 + if (depth <= 1) { a2 = new Array(WIDTH); a2(0) = a1; depth = 2 } + a1 = new Array(WIDTH) + a2((idx >>> BITS) & MASK) = a1 + } else if (xor < WIDTH3) { // level = 2 + if (depth <= 2) { a3 = new Array(WIDTH); a3(0) = a2; depth = 3 } + a1 = new Array(WIDTH) + a2 = new Array(WIDTH) + a2((idx >>> BITS) & MASK) = a1 + a3((idx >>> BITS2) & MASK) = a2 + } else if (xor < WIDTH4) { // level = 3 + if (depth <= 3) { a4 = new Array(WIDTH); a4(0) = a3; depth = 4 } + a1 = new Array(WIDTH) + a2 = new Array(WIDTH) + a3 = new Array(WIDTH) + a2((idx >>> BITS) & MASK) = a1 + a3((idx >>> BITS2) & MASK) = a2 + a4((idx >>> BITS3) & MASK) = a3 + } else if (xor < WIDTH5) { // level = 4 + if (depth <= 4) { a5 = new Array(WIDTH); a5(0) = a4; depth = 5 } + a1 = new Array(WIDTH) + a2 = new Array(WIDTH) + a3 = new Array(WIDTH) + a4 = new Array(WIDTH) + a2((idx >>> BITS) & MASK) = a1 + a3((idx >>> BITS2) & MASK) = a2 + a4((idx >>> BITS3) & MASK) = a3 + a5((idx >>> BITS4) & MASK) = a4 + } else { // level = 5 + if (depth <= 5) { a6 = new Array(LASTWIDTH); a6(0) = a5; depth = 6 } + a1 = new Array(WIDTH) + a2 = new Array(WIDTH) + a3 = new Array(WIDTH) + a4 = new Array(WIDTH) + a5 = new Array(WIDTH) + a2((idx >>> BITS) & MASK) = a1 + a3((idx >>> BITS2) & MASK) = a2 + a4((idx >>> BITS3) & MASK) = a3 + a5((idx >>> BITS4) & MASK) = a4 + a6(idx >>> BITS5) = a5 + } + } + + def result(): Vector[A] = { + if (prefixIsRightAligned) leftAlignPrefix() + val len = len1 + lenRest + val realLen = len - offset + if(realLen == 0) Vector.empty + else if(len < 0) throw new IndexOutOfBoundsException(s"Vector cannot have negative size $len") + else if(len <= WIDTH) { + new Vector1(copyIfDifferentSize(a1, realLen)) + } else if(len <= WIDTH2) { + val i1 = (len-1) & MASK + val i2 = (len-1) >>> BITS + val data = copyOfRange(a2, 1, i2) + val prefix1 = a2(0) + val suffix1 = copyIfDifferentSize(a2(i2), i1+1) + new Vector2(prefix1, WIDTH-offset, data, suffix1, realLen) + } else if(len <= WIDTH3) { + val i1 = (len-1) & MASK + val i2 = ((len-1) >>> BITS) & MASK + val i3 = ((len-1) >>> BITS2) + val data = copyOfRange(a3, 1, i3) + val prefix2 = copyTail(a3(0)) + val prefix1 = a3(0)(0) + val suffix2 = copyOf(a3(i3), i2) + val suffix1 = copyIfDifferentSize(a3(i3)(i2), i1+1) + val len1 = prefix1.length + val len12 = len1 + prefix2.length*WIDTH + new Vector3(prefix1, len1, prefix2, len12, data, suffix2, suffix1, realLen) + } else if(len <= WIDTH4) { + val i1 = (len-1) & MASK + val i2 = ((len-1) >>> BITS) & MASK + val i3 = ((len-1) >>> BITS2) & MASK + val i4 = ((len-1) >>> BITS3) + val data = copyOfRange(a4, 1, i4) + val prefix3 = copyTail(a4(0)) + val prefix2 = copyTail(a4(0)(0)) + val prefix1 = a4(0)(0)(0) + val suffix3 = copyOf(a4(i4), i3) + val suffix2 = copyOf(a4(i4)(i3), i2) + val suffix1 = copyIfDifferentSize(a4(i4)(i3)(i2), i1+1) + val len1 = prefix1.length + val len12 = len1 + prefix2.length*WIDTH + val len123 = len12 + prefix3.length*WIDTH2 + new Vector4(prefix1, len1, prefix2, len12, prefix3, len123, data, suffix3, suffix2, suffix1, realLen) + } else if(len <= WIDTH5) { + val i1 = (len-1) & MASK + val i2 = ((len-1) >>> BITS) & MASK + val i3 = ((len-1) >>> BITS2) & MASK + val i4 = ((len-1) >>> BITS3) & MASK + val i5 = ((len-1) >>> BITS4) + val data = copyOfRange(a5, 1, i5) + val prefix4 = copyTail(a5(0)) + val prefix3 = copyTail(a5(0)(0)) + val prefix2 = copyTail(a5(0)(0)(0)) + val prefix1 = a5(0)(0)(0)(0) + val suffix4 = copyOf(a5(i5), i4) + val suffix3 = copyOf(a5(i5)(i4), i3) + val suffix2 = copyOf(a5(i5)(i4)(i3), i2) + val suffix1 = copyIfDifferentSize(a5(i5)(i4)(i3)(i2), i1+1) + val len1 = prefix1.length + val len12 = len1 + prefix2.length*WIDTH + val len123 = len12 + prefix3.length*WIDTH2 + val len1234 = len123 + prefix4.length*WIDTH3 + new Vector5(prefix1, len1, prefix2, len12, prefix3, len123, prefix4, len1234, data, suffix4, suffix3, suffix2, suffix1, realLen) + } else { + val i1 = (len-1) & MASK + val i2 = ((len-1) >>> BITS) & MASK + val i3 = ((len-1) >>> BITS2) & MASK + val i4 = ((len-1) >>> BITS3) & MASK + val i5 = ((len-1) >>> BITS4) & MASK + val i6 = ((len-1) >>> BITS5) + val data = copyOfRange(a6, 1, i6) + val prefix5 = copyTail(a6(0)) + val prefix4 = copyTail(a6(0)(0)) + val prefix3 = copyTail(a6(0)(0)(0)) + val prefix2 = copyTail(a6(0)(0)(0)(0)) + val prefix1 = a6(0)(0)(0)(0)(0) + val suffix5 = copyOf(a6(i6), i5) + val suffix4 = copyOf(a6(i6)(i5), i4) + val suffix3 = copyOf(a6(i6)(i5)(i4), i3) + val suffix2 = copyOf(a6(i6)(i5)(i4)(i3), i2) + val suffix1 = copyIfDifferentSize(a6(i6)(i5)(i4)(i3)(i2), i1+1) + val len1 = prefix1.length + val len12 = len1 + prefix2.length*WIDTH + val len123 = len12 + prefix3.length*WIDTH2 + val len1234 = len123 + prefix4.length*WIDTH3 + val len12345 = len1234 + prefix5.length*WIDTH4 + new Vector6(prefix1, len1, prefix2, len12, prefix3, len123, prefix4, len1234, prefix5, len12345, data, suffix5, suffix4, suffix3, suffix2, suffix1, realLen) + } + } + + override def toString: String = + s"VectorBuilder(len1=$len1, lenRest=$lenRest, offset=$offset, depth=$depth)" + + private[immutable] def getData: Array[Array[_]] = Array[Array[AnyRef]]( + a1, a2.asInstanceOf[Array[AnyRef]], a3.asInstanceOf[Array[AnyRef]], a4.asInstanceOf[Array[AnyRef]], + a5.asInstanceOf[Array[AnyRef]], a6.asInstanceOf[Array[AnyRef]] + ).asInstanceOf[Array[Array[_]]] +} + + +/** Compile-time definitions for Vector. No references to this object should appear in bytecode. */ +private[immutable] object VectorInline { + // compile-time numeric constants + final val BITS = 5 + final val WIDTH = 1 << BITS + final val MASK = WIDTH - 1 + final val BITS2 = BITS * 2 + final val WIDTH2 = 1 << BITS2 + final val BITS3 = BITS * 3 + final val WIDTH3 = 1 << BITS3 + final val BITS4 = BITS * 4 + final val WIDTH4 = 1 << BITS4 + final val BITS5 = BITS * 5 + final val WIDTH5 = 1 << BITS5 + final val LASTWIDTH = WIDTH << 1 // 1 extra bit in the last level to go up to Int.MaxValue (2^31-1) instead of 2^30: + final val Log2ConcatFaster = 5 + final val AlignToFaster = 64 + + type Arr1 = Array[AnyRef] + type Arr2 = Array[Array[AnyRef]] + type Arr3 = Array[Array[Array[AnyRef]]] + type Arr4 = Array[Array[Array[Array[AnyRef]]]] + type Arr5 = Array[Array[Array[Array[Array[AnyRef]]]]] + type Arr6 = Array[Array[Array[Array[Array[Array[AnyRef]]]]]] + + /** Dimension of the slice at index */ + @inline def vectorSliceDim(count: Int, idx: Int): Int = { + val c = count/2 + c+1-abs(idx-c) + } + + @inline def copyOrUse[T <: AnyRef](a: Array[T], start: Int, end: Int): Array[T] = + if(start == 0 && end == a.length) a else copyOfRange[T](a, start, end) + + @inline final def copyTail[T <: AnyRef](a: Array[T]): Array[T] = copyOfRange[T](a, 1, a.length) + + @inline final def copyInit[T <: AnyRef](a: Array[T]): Array[T] = copyOfRange[T](a, 0, a.length-1) + + @inline final def copyIfDifferentSize[T <: AnyRef](a: Array[T], len: Int): Array[T] = + if(a.length == len) a else copyOf[T](a, len) + + @inline final def wrap1(x: Any ): Arr1 = { val a = new Arr1(1); a(0) = x.asInstanceOf[AnyRef]; a } + @inline final def wrap2(x: Arr1): Arr2 = { val a = new Arr2(1); a(0) = x; a } + @inline final def wrap3(x: Arr2): Arr3 = { val a = new Arr3(1); a(0) = x; a } + @inline final def wrap4(x: Arr3): Arr4 = { val a = new Arr4(1); a(0) = x; a } + @inline final def wrap5(x: Arr4): Arr5 = { val a = new Arr5(1); a(0) = x; a } + + @inline final def copyUpdate(a1: Arr1, idx1: Int, elem: Any): Arr1 = { + val a1c = a1.clone() + a1c(idx1) = elem.asInstanceOf[AnyRef] + a1c + } + + @inline final def copyUpdate(a2: Arr2, idx2: Int, idx1: Int, elem: Any): Arr2 = { + val a2c = a2.clone() + a2c(idx2) = copyUpdate(a2c(idx2), idx1, elem) + a2c + } + + @inline final def copyUpdate(a3: Arr3, idx3: Int, idx2: Int, idx1: Int, elem: Any): Arr3 = { + val a3c = a3.clone() + a3c(idx3) = copyUpdate(a3c(idx3), idx2, idx1, elem) + a3c + } + + @inline final def copyUpdate(a4: Arr4, idx4: Int, idx3: Int, idx2: Int, idx1: Int, elem: Any): Arr4 = { + val a4c = a4.clone() + a4c(idx4) = copyUpdate(a4c(idx4), idx3, idx2, idx1, elem) + a4c + } + + @inline final def copyUpdate(a5: Arr5, idx5: Int, idx4: Int, idx3: Int, idx2: Int, idx1: Int, elem: Any): Arr5 = { + val a5c = a5.clone() + a5c(idx5) = copyUpdate(a5c(idx5), idx4, idx3, idx2, idx1, elem) + a5c + } + + @inline final def copyUpdate(a6: Arr6, idx6: Int, idx5: Int, idx4: Int, idx3: Int, idx2: Int, idx1: Int, elem: Any): Arr6 = { + val a6c = a6.clone() + a6c(idx6) = copyUpdate(a6c(idx6), idx5, idx4, idx3, idx2, idx1, elem) + a6c + } + + @inline final def concatArrays[T <: AnyRef](a: Array[T], b: Array[T]): Array[T] = { + val dest = copyOf[T](a, a.length+b.length) + System.arraycopy(b, 0, dest, a.length, b.length) + dest + } +} + + +/** Helper methods and constants for Vector. */ +private object VectorStatics { + + final def copyAppend1(a: Arr1, elem: Any): Arr1 = { + val alen = a.length + val ac = new Arr1(alen+1) + System.arraycopy(a, 0, ac, 0, alen) + ac(alen) = elem.asInstanceOf[AnyRef] + ac + } + + final def copyAppend[T <: AnyRef](a: Array[T], elem: T): Array[T] = { + val ac = copyOf(a, a.length+1) + ac(ac.length-1) = elem + ac + } + + final def copyPrepend1(elem: Any, a: Arr1): Arr1 = { + val ac = new Arr1(a.length+1) + System.arraycopy(a, 0, ac, 1, a.length) + ac(0) = elem.asInstanceOf[AnyRef] + ac + } + + final def copyPrepend[T <: AnyRef](elem: T, a: Array[T]): Array[T] = { + val ac = java.lang.reflect.Array.newInstance(a.getClass.getComponentType, a.length+1).asInstanceOf[Array[T]] + System.arraycopy(a, 0, ac, 1, a.length) + ac(0) = elem + ac + } + + final val empty1: Arr1 = new Array(0) + final val empty2: Arr2 = new Array(0) + final val empty3: Arr3 = new Array(0) + final val empty4: Arr4 = new Array(0) + final val empty5: Arr5 = new Array(0) + final val empty6: Arr6 = new Array(0) + + final def foreachRec[T <: AnyRef, A, U](level: Int, a: Array[T], f: A => U): Unit = { + var i = 0 + val len = a.length + if(level == 0) { + while(i < len) { + f(a(i).asInstanceOf[A]) + i += 1 + } + } else { + val l = level-1 + while(i < len) { + foreachRec(l, a(i).asInstanceOf[Array[AnyRef]], f) + i += 1 + } + } + } + + final def mapElems1[A, B](a: Arr1, f: A => B): Arr1 = { + var i = 0 + while(i < a.length) { + val v1 = a(i).asInstanceOf[AnyRef] + val v2 = f(v1.asInstanceOf[A]).asInstanceOf[AnyRef] + if(v1 ne v2) + return mapElems1Rest(a, f, i, v2) + i += 1 + } + a + } + + final def mapElems1Rest[A, B](a: Arr1, f: A => B, at: Int, v2: AnyRef): Arr1 = { + val ac = new Arr1(a.length) + if(at > 0) System.arraycopy(a, 0, ac, 0, at) + ac(at) = v2 + var i = at+1 + while(i < a.length) { + ac(i) = f(a(i).asInstanceOf[A]).asInstanceOf[AnyRef] + i += 1 + } + ac + } + + final def mapElems[A, B, T <: AnyRef](n: Int, a: Array[T], f: A => B): Array[T] = { + if(n == 1) + mapElems1[A, B](a.asInstanceOf[Arr1], f).asInstanceOf[Array[T]] + else { + var i = 0 + while(i < a.length) { + val v1 = a(i) + val v2 = mapElems(n-1, v1.asInstanceOf[Array[AnyRef]], f) + if(v1 ne v2) + return mapElemsRest(n, a, f, i, v2) + i += 1 + } + a + } + } + + final def mapElemsRest[A, B, T <: AnyRef](n: Int, a: Array[T], f: A => B, at: Int, v2: AnyRef): Array[T] = { + val ac = java.lang.reflect.Array.newInstance(a.getClass.getComponentType, a.length).asInstanceOf[Array[AnyRef]] + if(at > 0) System.arraycopy(a, 0, ac, 0, at) + ac(at) = v2 + var i = at+1 + while(i < a.length) { + ac(i) = mapElems(n-1, a(i).asInstanceOf[Array[AnyRef]], f) + i += 1 + } + ac.asInstanceOf[Array[T]] + } + + final def prepend1IfSpace(prefix1: Arr1, xs: IterableOnce[_]): Arr1 = xs match { + case it: Iterable[_] => + if(it.sizeCompare(WIDTH-prefix1.length) <= 0) { + it.size match { + case 0 => null + case 1 => copyPrepend(it.head.asInstanceOf[AnyRef], prefix1) + case s => + val prefix1b = new Arr1(prefix1.length + s) + System.arraycopy(prefix1, 0, prefix1b, s, prefix1.length) + @annotation.unused val copied = it.copyToArray(prefix1b.asInstanceOf[Array[Any]], 0) + //assert(copied == s) + prefix1b + } + } else null + case it => + val s = it.knownSize + if(s > 0 && s <= WIDTH-prefix1.length) { + val prefix1b = new Arr1(prefix1.length + s) + System.arraycopy(prefix1, 0, prefix1b, s, prefix1.length) + @annotation.unused val copied = it.iterator.copyToArray(prefix1b.asInstanceOf[Array[Any]], 0) + //assert(copied == s) + prefix1b + } else null + } + + final def append1IfSpace(suffix1: Arr1, xs: IterableOnce[_]): Arr1 = xs match { + case it: Iterable[_] => + if(it.sizeCompare(WIDTH-suffix1.length) <= 0) { + it.size match { + case 0 => null + case 1 => copyAppend(suffix1, it.head.asInstanceOf[AnyRef]) + case s => + val suffix1b = copyOf(suffix1, suffix1.length + s) + @annotation.unused val copied = it.copyToArray(suffix1b.asInstanceOf[Array[Any]], suffix1.length) + //assert(copied == s) + suffix1b + } + } else null + case it => + val s = it.knownSize + if(s > 0 && s <= WIDTH-suffix1.length) { + val suffix1b = copyOf(suffix1, suffix1.length + s) + @annotation.unused val copied = it.iterator.copyToArray(suffix1b.asInstanceOf[Array[Any]], suffix1.length) + //assert(copied == s) + suffix1b + } else null + } +} + + +private final class NewVectorIterator[A](v: Vector[A], private[this] var totalLength: Int, private[this] val sliceCount: Int) extends AbstractIterator[A] with java.lang.Cloneable { + + private[this] var a1: Arr1 = v.prefix1 + private[this] var a2: Arr2 = _ + private[this] var a3: Arr3 = _ + private[this] var a4: Arr4 = _ + private[this] var a5: Arr5 = _ + private[this] var a6: Arr6 = _ + private[this] var a1len = a1.length + private[this] var i1 = 0 // current index in a1 + private[this] var oldPos = 0 + private[this] var len1 = totalLength // remaining length relative to a1 + + private[this] var sliceIdx = 0 + private[this] var sliceDim = 1 + private[this] var sliceStart = 0 // absolute position + private[this] var sliceEnd = a1len // absolute position + + //override def toString: String = + // s"NewVectorIterator(v=$v, totalLength=$totalLength, sliceCount=$sliceCount): a1len=$a1len, len1=$len1, i1=$i1, sliceEnd=$sliceEnd" + + @inline override def knownSize = len1 - i1 + + @inline def hasNext: Boolean = len1 > i1 + + def next(): A = { + if(i1 == a1len) advance() + val r = a1(i1) + i1 += 1 + r.asInstanceOf[A] + } + + private[this] def advanceSlice(): Unit = { + if(!hasNext) Iterator.empty.next() + sliceIdx += 1 + var slice: Array[_ <: AnyRef] = v.vectorSlice(sliceIdx) + while(slice.length == 0) { + sliceIdx += 1 + slice = v.vectorSlice(sliceIdx) + } + sliceStart = sliceEnd + sliceDim = vectorSliceDim(sliceCount, sliceIdx) + (sliceDim: @switch) match { + case 1 => a1 = slice.asInstanceOf[Arr1] + case 2 => a2 = slice.asInstanceOf[Arr2] + case 3 => a3 = slice.asInstanceOf[Arr3] + case 4 => a4 = slice.asInstanceOf[Arr4] + case 5 => a5 = slice.asInstanceOf[Arr5] + case 6 => a6 = slice.asInstanceOf[Arr6] + } + sliceEnd = sliceStart + slice.length * (1 << (BITS*(sliceDim-1))) + if(sliceEnd > totalLength) sliceEnd = totalLength + if(sliceDim > 1) oldPos = (1 << (BITS*sliceDim))-1 + } + + private[this] def advance(): Unit = { + val pos = i1-len1+totalLength + if(pos == sliceEnd) advanceSlice() + if(sliceDim > 1) { + val io = pos - sliceStart + val xor = oldPos ^ io + advanceA(io, xor) + oldPos = io + } + len1 -= i1 + a1len = mmin(a1.length, len1) + i1 = 0 + } + + private[this] def advanceA(io: Int, xor: Int): Unit = { + if(xor < WIDTH2) { + a1 = a2((io >>> BITS) & MASK) + } else if(xor < WIDTH3) { + a2 = a3((io >>> BITS2) & MASK) + a1 = a2(0) + } else if(xor < WIDTH4) { + a3 = a4((io >>> BITS3) & MASK) + a2 = a3(0) + a1 = a2(0) + } else if(xor < WIDTH5) { + a4 = a5((io >>> BITS4) & MASK) + a3 = a4(0) + a2 = a3(0) + a1 = a2(0) + } else { + a5 = a6(io >>> BITS5) + a4 = a5(0) + a3 = a4(0) + a2 = a3(0) + a1 = a2(0) + } + } + + private[this] def setA(io: Int, xor: Int): Unit = { + if(xor < WIDTH2) { + a1 = a2((io >>> BITS) & MASK) + } else if(xor < WIDTH3) { + a2 = a3((io >>> BITS2) & MASK) + a1 = a2((io >>> BITS) & MASK) + } else if(xor < WIDTH4) { + a3 = a4((io >>> BITS3) & MASK) + a2 = a3((io >>> BITS2) & MASK) + a1 = a2((io >>> BITS) & MASK) + } else if(xor < WIDTH5) { + a4 = a5((io >>> BITS4) & MASK) + a3 = a4((io >>> BITS3) & MASK) + a2 = a3((io >>> BITS2) & MASK) + a1 = a2((io >>> BITS) & MASK) + } else { + a5 = a6(io >>> BITS5) + a4 = a5((io >>> BITS4) & MASK) + a3 = a4((io >>> BITS3) & MASK) + a2 = a3((io >>> BITS2) & MASK) + a1 = a2((io >>> BITS) & MASK) + } + } + + override def drop(n: Int): Iterator[A] = { + if(n > 0) { + val oldpos = i1-len1+totalLength + val newpos = mmin(oldpos + n, totalLength) + if(newpos == totalLength) { + i1 = 0 + len1 = 0 + a1len = 0 + } else { + while(newpos >= sliceEnd) advanceSlice() + val io = newpos - sliceStart + if(sliceDim > 1) { + val xor = oldPos ^ io + setA(io, xor) + oldPos = io + } + a1len = a1.length + i1 = io & MASK + len1 = i1 + (totalLength-newpos) + if(a1len > len1) a1len = len1 + } + } + this + } + + override def take(n: Int): Iterator[A] = { + if(n < knownSize) { + val trunc = knownSize - mmax(0, n) + totalLength -= trunc + len1 -= trunc + if(len1 < a1len) a1len = len1 + if(totalLength < sliceEnd) sliceEnd = totalLength + } + this + } + + override def slice(from: Int, until: Int): Iterator[A] = { + val _until = mmax(until, 0) + + val n = + if(from > 0) { + drop(from) + _until - from + } else _until + take(n) + } + + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { + val xsLen = xs.length + val total = IterableOnce.elemsToCopyToArray(knownSize, xsLen, start, len) + var copied = 0 + val isBoxed = xs.isInstanceOf[Array[AnyRef]] + while(copied < total) { + if(i1 == a1len) advance() + val count = mmin(total-copied, a1.length-i1) + if(isBoxed) System.arraycopy(a1, i1, xs, start+copied, count) + else Array.copy(a1, i1, xs, start+copied, count) + i1 += count + copied += count + } + total + } + + override def toVector: Vector[A] = + v.slice(i1-len1+totalLength, totalLength) + + protected[immutable] def split(at: Int): NewVectorIterator[A] = { + val it2 = clone().asInstanceOf[NewVectorIterator[A]] + it2.take(at) + drop(at) + it2 + } +} + + +private abstract class VectorStepperBase[A, Sub >: Null <: Stepper[A], Semi <: Sub](it: NewVectorIterator[A]) + extends Stepper[A] with EfficientSplit { + + protected[this] def build(it: NewVectorIterator[A]): Semi + + final def hasStep: Boolean = it.hasNext + + final def characteristics: Int = Spliterator.ORDERED + Spliterator.SIZED + Spliterator.SUBSIZED + + final def estimateSize: Long = it.knownSize + + def trySplit(): Sub = { + val len = it.knownSize + if(len > 1) build(it.split(len >>> 1)) + else null + } + + override final def iterator: Iterator[A] = it +} + +private class AnyVectorStepper[A](it: NewVectorIterator[A]) + extends VectorStepperBase[A, AnyStepper[A], AnyVectorStepper[A]](it) with AnyStepper[A] { + protected[this] def build(it: NewVectorIterator[A]) = new AnyVectorStepper(it) + def nextStep(): A = it.next() +} + +private class DoubleVectorStepper(it: NewVectorIterator[Double]) + extends VectorStepperBase[Double, DoubleStepper, DoubleVectorStepper](it) with DoubleStepper { + protected[this] def build(it: NewVectorIterator[Double]) = new DoubleVectorStepper(it) + def nextStep(): Double = it.next() +} + +private class IntVectorStepper(it: NewVectorIterator[Int]) + extends VectorStepperBase[Int, IntStepper, IntVectorStepper](it) with IntStepper { + protected[this] def build(it: NewVectorIterator[Int]) = new IntVectorStepper(it) + def nextStep(): Int = it.next() +} + +private class LongVectorStepper(it: NewVectorIterator[Long]) + extends VectorStepperBase[Long, LongStepper, LongVectorStepper](it) with LongStepper { + protected[this] def build(it: NewVectorIterator[Long]) = new LongVectorStepper(it) + def nextStep(): Long = it.next() +} + + +// The following definitions are needed for binary compatibility with ParVector +private[collection] class VectorIterator[+A](_startIndex: Int, private[this] var endIndex: Int) extends AbstractIterator[A] { + private[immutable] var it: NewVectorIterator[A @uncheckedVariance] = _ + def hasNext: Boolean = it.hasNext + def next(): A = it.next() + private[collection] def remainingElementCount: Int = it.size + private[collection] def remainingVector: Vector[A] = it.toVector +} diff --git a/library/src/scala/collection/immutable/VectorMap.scala b/library/src/scala/collection/immutable/VectorMap.scala new file mode 100644 index 000000000000..361427a86c53 --- /dev/null +++ b/library/src/scala/collection/immutable/VectorMap.scala @@ -0,0 +1,278 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.annotation.{nowarn, tailrec} + +/** This class implements immutable maps using a vector/map-based data structure, which preserves insertion order. + * + * Unlike `ListMap`, `VectorMap` has amortized effectively constant lookup at the expense + * of using extra memory and generally lower performance for other operations + * + * @tparam K the type of the keys contained in this vector map. + * @tparam V the type of the values associated with the keys in this vector map. + * + * @define coll immutable vector map + * @define Coll `immutable.VectorMap` + */ +final class VectorMap[K, +V] private ( + private[immutable] val fields: Vector[Any], // K | Tombstone | Null + private[immutable] val underlying: Map[K, (Int, V)], + dropped: Int) + extends AbstractMap[K, V] + with SeqMap[K, V] + with StrictOptimizedMapOps[K, V, VectorMap, VectorMap[K, V]] + with MapFactoryDefaults[K, V, VectorMap, Iterable] { + + import VectorMap._ + + override protected[this] def className: String = "VectorMap" + + private[immutable] def this(fields: Vector[K], underlying: Map[K, (Int, V)]) = this(fields, underlying, 0) + + override val size = underlying.size + + override def knownSize: Int = size + + override def isEmpty: Boolean = size == 0 + + def updated[V1 >: V](key: K, value: V1): VectorMap[K, V1] = { + underlying.get(key) match { + case Some((slot, _)) => + new VectorMap(fields, underlying.updated[(Int, V1)](key, (slot, value)), dropped) + case None => + new VectorMap(fields :+ key, underlying.updated[(Int, V1)](key, (fields.length + dropped, value)), dropped) + } + } + + override def withDefault[V1 >: V](d: K => V1): Map[K, V1] = + new Map.WithDefault(this, d) + + override def withDefaultValue[V1 >: V](d: V1): Map[K, V1] = + new Map.WithDefault[K, V1](this, _ => d) + + def get(key: K): Option[V] = underlying.get(key) match { + case Some(v) => Some(v._2) + case None => None + } + + @tailrec + private def nextValidField(slot: Int): (Int, K) = { + if (slot >= fields.size) (-1, null.asInstanceOf[K]) + else fields(slot) match { + case Tombstone(distance) => nextValidField(slot + distance) + case k /*: K | Null */ => (slot, k.asInstanceOf[K]) + } + } + + def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] { + private[this] val fieldsLength = fields.length + private[this] var slot = -1 + private[this] var key: K = null.asInstanceOf[K] + + private[this] def advance(): Unit = { + val nextSlot = slot + 1 + if (nextSlot >= fieldsLength) { + slot = fieldsLength + key = null.asInstanceOf[K] + } else { + nextValidField(nextSlot) match { + case (-1, _) => + slot = fieldsLength + key = null.asInstanceOf[K] + case (s, k) => + slot = s + key = k + } + } + } + + advance() + + override def hasNext: Boolean = slot < fieldsLength + + override def next(): (K, V) = + if (!hasNext) Iterator.empty.next() + else { + val result = (key, underlying(key)._2) + advance() + result + } + } + + // No-Op overrides to allow for more efficient steppers in a minor release. + // Refining the return type to `S with EfficientSplit` is binary compatible. + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[(K, V), S]): S = super.stepper(shape) + + override def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S = super.keyStepper(shape) + + override def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S = super.valueStepper(shape) + + + def removed(key: K): VectorMap[K, V] = { + if (isEmpty) empty + else { + var fs = fields + val sz = fs.size + underlying.get(key) match { + case Some(_) if size == 1 => empty + case Some((slot, _)) => + val s = slot - dropped + + // Calculate next of kin + val next = + if (s < sz - 1) fs(s + 1) match { + case Tombstone(d) => s + d + 1 + case _ => s + 1 + } else s + 1 + + fs = fs.updated(s, Tombstone(next - s)) + + // Calculate first index of preceding tombstone sequence + val first = + if (s > 0) { + fs(s - 1) match { + case Tombstone(d) if d < 0 => if (s + d >= 0) s + d else 0 + case Tombstone(d) if d == 1 => s - 1 + case Tombstone(d) => throw new IllegalStateException("tombstone indicate wrong position: " + d) + case _ => s + } + }else s + fs = fs.updated(first, Tombstone(next - first)) + + // Calculate last index of succeeding tombstone sequence + val last = next - 1 + if (last != first) { + fs = fs.updated(last, Tombstone(first - 1 - last)) + } + new VectorMap(fs, underlying - key, dropped) + case _ => + this + } + } + } + + override def mapFactory: MapFactory[VectorMap] = VectorMap + + override def contains(key: K): Boolean = underlying.contains(key) + + override def head: (K, V) = iterator.next() + + override def last: (K, V) = { + if (isEmpty) throw new UnsupportedOperationException("empty.last") + val lastSlot = fields.length - 1 + val last = fields.last match { + case Tombstone(d) if d < 0 => fields(lastSlot + d).asInstanceOf[K] + case Tombstone(d) if d == 1 => fields(lastSlot - 1).asInstanceOf[K] + case Tombstone(d) => throw new IllegalStateException("tombstone indicate wrong position: " + d) + case k => k.asInstanceOf[K] + } + (last, underlying(last)._2) + } + + override def lastOption: Option[(K, V)] = { + if (isEmpty) None + else Some(last) + } + + override def tail: VectorMap[K, V] = { + if (isEmpty) throw new UnsupportedOperationException("empty.tail") + val (slot, key) = nextValidField(0) + new VectorMap(fields.drop(slot + 1), underlying - key, dropped + slot + 1) + } + + override def init: VectorMap[K, V] = { + if (isEmpty) throw new UnsupportedOperationException("empty.init") + val lastSlot = fields.size - 1 + val (slot, key) = fields.last match { + case Tombstone(d) if d < 0 => (lastSlot + d, fields(lastSlot + d).asInstanceOf[K]) + case Tombstone(d) if d == 1 => (lastSlot - 1, fields(lastSlot - 1).asInstanceOf[K]) + case Tombstone(d) => throw new IllegalStateException("tombstone indicate wrong position: " + d) + case k => (lastSlot, k.asInstanceOf[K]) + } + new VectorMap(fields.dropRight(fields.size - slot), underlying - key, dropped) + } + + /** A [[Vector]] of the keys contained by this map. + * + * @return a [[Vector]] of the keys contained by this map. + */ + @nowarn("msg=overriding method keys") + override def keys: Vector[K] = keysIterator.toVector + + override def values: Iterable[V] = new Iterable[V] with IterableFactoryDefaults[V, Iterable] { + override def iterator: Iterator[V] = keysIterator.map(underlying(_)._2) + } +} + +object VectorMap extends MapFactory[VectorMap] { + //Class to mark deleted slots in 'fields'. + //When one or more consecutive slots are deleted, the 'distance' of the first 'Tombstone' + // represents the distance to the location of the next undeleted slot (or the last slot in 'fields' +1 if it does not exist). + //When two or more consecutive slots are deleted, the 'distance' of the trailing 'Tombstone' + // represents the distance to the location of the previous undeleted slot ( or -1 if it does not exist) multiplied by -1. + //For other deleted slots, it simply indicates that they have been deleted. + private[VectorMap] final case class Tombstone(distance: Int) + + private[this] final val EmptyMap: VectorMap[Nothing, Nothing] = + new VectorMap[Nothing, Nothing](Vector.empty[Nothing], HashMap.empty[Nothing, (Int, Nothing)]) + + def empty[K, V]: VectorMap[K, V] = EmptyMap.asInstanceOf[VectorMap[K, V]] + + def from[K, V](it: collection.IterableOnce[(K, V)]): VectorMap[K, V] = + it match { + case vm: VectorMap[K, V] => vm + case _ => (newBuilder[K, V] ++= it).result() + } + + def newBuilder[K, V]: mutable.Builder[(K, V), VectorMap[K, V]] = new VectorMapBuilder[K, V] +} + +private[immutable] final class VectorMapBuilder[K, V] extends mutable.Builder[(K, V), VectorMap[K, V]] { + private[this] val vectorBuilder = new VectorBuilder[K] + private[this] val mapBuilder = new MapBuilderImpl[K, (Int, V)] + private[this] var aliased: VectorMap[K, V] = _ + + override def clear(): Unit = { + vectorBuilder.clear() + mapBuilder.clear() + aliased = null + } + + override def result(): VectorMap[K, V] = { + if (aliased eq null) { + aliased = new VectorMap(vectorBuilder.result(), mapBuilder.result()) + } + aliased + } + def addOne(key: K, value: V): this.type = { + if (aliased ne null) { + aliased = aliased.updated(key, value) + } else { + mapBuilder.getOrElse(key, null) match { + case (slot, _) => + mapBuilder.addOne(key, (slot, value)) + case null => + val vectorSize = vectorBuilder.size + vectorBuilder.addOne(key) + mapBuilder.addOne(key, (vectorSize, value)) + } + } + this + } + + override def addOne(elem: (K, V)): this.type = addOne(elem._1, elem._2) +} diff --git a/library/src/scala/collection/immutable/WrappedString.scala b/library/src/scala/collection/immutable/WrappedString.scala new file mode 100644 index 000000000000..a6c0256fe800 --- /dev/null +++ b/library/src/scala/collection/immutable/WrappedString.scala @@ -0,0 +1,139 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package immutable + +import scala.Predef.{wrapString => _, assert} +import scala.collection.Stepper.EfficientSplit +import scala.collection.convert.impl.CharStringStepper +import scala.collection.mutable.{Builder, StringBuilder} + +/** + * This class serves as a wrapper augmenting `String`s with all the operations + * found in indexed sequences. + * + * The difference between this class and `StringOps` is that calling transformer + * methods such as `filter` and `map` will yield an object of type `WrappedString` + * rather than a `String`. + * + * @param self a string contained within this wrapped string + * + * @define Coll `WrappedString` + * @define coll wrapped string + */ +@SerialVersionUID(3L) +final class WrappedString(private val self: String) extends AbstractSeq[Char] with IndexedSeq[Char] + with IndexedSeqOps[Char, IndexedSeq, WrappedString] + with Serializable { + + def apply(i: Int): Char = self.charAt(i) + + override protected def fromSpecific(coll: scala.collection.IterableOnce[Char]): WrappedString = WrappedString.fromSpecific(coll) + override protected def newSpecificBuilder: Builder[Char, WrappedString] = WrappedString.newBuilder + override def empty: WrappedString = WrappedString.empty + + override def slice(from: Int, until: Int): WrappedString = { + val start = if (from < 0) 0 else from + if (until <= start || start >= self.length) + return WrappedString.empty + + val end = if (until > length) length else until + new WrappedString(self.substring(start, end)) + } + override def length = self.length + override def toString = self + override def view: StringView = new StringView(self) + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Char, S]): S with EfficientSplit = { + val st = new CharStringStepper(self, 0, self.length) + val r = + if (shape.shape == StepperShape.CharShape) st + else { + assert(shape.shape == StepperShape.ReferenceShape, s"unexpected StepperShape: $shape") + AnyStepper.ofParIntStepper(st) + } + r.asInstanceOf[S with EfficientSplit] + } + + override def startsWith[B >: Char](that: IterableOnce[B], offset: Int = 0): Boolean = + that match { + case s: WrappedString => self.startsWith(s.self, offset) + case _ => super.startsWith(that, offset) + } + + override def endsWith[B >: Char](that: collection.Iterable[B]): Boolean = + that match { + case s: WrappedString => self.endsWith(s.self) + case _ => super.endsWith(that) + } + + override def indexOf[B >: Char](elem: B, from: Int = 0): Int = elem match { + case c: Char => self.indexOf(c, from) + case _ => super.indexOf(elem, from) + } + + override def lastIndexOf[B >: Char](elem: B, end: Int = length - 1): Int = + elem match { + case c: Char => self.lastIndexOf(c, end) + case _ => super.lastIndexOf(elem, end) + } + + override def copyToArray[B >: Char](xs: Array[B], start: Int, len: Int): Int = + (xs: Any) match { + case chs: Array[Char] => + val copied = IterableOnce.elemsToCopyToArray(length, chs.length, start, len) + self.getChars(0, copied, chs, start) + copied + case _ => super.copyToArray(xs, start, len) + } + + override def appendedAll[B >: Char](suffix: IterableOnce[B]): IndexedSeq[B] = + suffix match { + case s: WrappedString => new WrappedString(self concat s.self) + case _ => super.appendedAll(suffix) + } + + override def sameElements[B >: Char](o: IterableOnce[B]) = o match { + case s: WrappedString => self == s.self + case _ => super.sameElements(o) + } + + override protected[this] def className = "WrappedString" + + override protected final def applyPreferredMaxLength: Int = Int.MaxValue + override def equals(other: Any): Boolean = other match { + case that: WrappedString => + this.self == that.self + case _ => + super.equals(other) + } +} + +/** A companion object for wrapped strings. + */ +@SerialVersionUID(3L) +object WrappedString extends SpecificIterableFactory[Char, WrappedString] { + def fromSpecific(it: IterableOnce[Char]): WrappedString = { + val b = newBuilder + b.sizeHint(it) + b ++= it + b.result() + } + val empty: WrappedString = new WrappedString("") + def newBuilder: Builder[Char, WrappedString] = + new StringBuilder().mapResult(x => new WrappedString(x)) + + implicit class UnwrapOp(private val value: WrappedString) extends AnyVal { + def unwrap: String = value.self + } +} diff --git a/library/src/scala/collection/immutable/package.scala b/library/src/scala/collection/immutable/package.scala new file mode 100644 index 000000000000..6a92c8cef284 --- /dev/null +++ b/library/src/scala/collection/immutable/package.scala @@ -0,0 +1,29 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + + +package object immutable { + type StringOps = scala.collection.StringOps + val StringOps = scala.collection.StringOps + type StringView = scala.collection.StringView + val StringView = scala.collection.StringView + + @deprecated("Use Iterable instead of Traversable", "2.13.0") + type Traversable[+X] = Iterable[X] + @deprecated("Use Iterable instead of Traversable", "2.13.0") + val Traversable = Iterable + + @deprecated("Use Map instead of DefaultMap", "2.13.0") + type DefaultMap[K, +V] = scala.collection.immutable.Map[K, V] +} diff --git a/library/src/scala/collection/mutable/AnyRefMap.scala b/library/src/scala/collection/mutable/AnyRefMap.scala new file mode 100644 index 000000000000..9ad433309b10 --- /dev/null +++ b/library/src/scala/collection/mutable/AnyRefMap.scala @@ -0,0 +1,627 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import scala.annotation.meta.companionClass +import scala.annotation.nowarn +import scala.collection.generic.DefaultSerializationProxy +import scala.language.implicitConversions + +/** This class implements mutable maps with `AnyRef` keys based on a hash table with open addressing. + * + * Basic map operations on single entries, including `contains` and `get`, + * are typically significantly faster with `AnyRefMap` than [[HashMap]]. + * Note that numbers and characters are not handled specially in AnyRefMap; + * only plain `equals` and `hashCode` are used in comparisons. + * + * Methods that traverse or regenerate the map, including `foreach` and `map`, + * are not in general faster than with `HashMap`. The methods `foreachKey`, + * `foreachValue`, `mapValuesNow`, and `transformValues` are, however, faster + * than alternative ways to achieve the same functionality. + * + * Maps with open addressing may become less efficient at lookup after + * repeated addition/removal of elements. Although `AnyRefMap` makes a + * decent attempt to remain efficient regardless, calling `repack` + * on a map that will no longer have elements removed but will be + * used heavily may save both time and storage space. + * + * This map is not intended to contain more than 2^29^ entries (approximately + * 500 million). The maximum capacity is 2^30^, but performance will degrade + * rapidly as 2^30^ is approached. + * + */ +@(deprecated @companionClass)("Use `scala.collection.mutable.HashMap` instead for better performance.", since = "2.13.16") +class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K => V, initialBufferSize: Int, initBlank: Boolean) + extends AbstractMap[K, V] + with MapOps[K, V, Map, AnyRefMap[K, V]] + with StrictOptimizedIterableOps[(K, V), Iterable, AnyRefMap[K, V]] + with Serializable { + + import AnyRefMap._ + def this() = this(AnyRefMap.exceptionDefault, 16, initBlank = true) + + /** Creates a new `AnyRefMap` that returns default values according to a supplied key-value mapping. */ + def this(defaultEntry: K => V) = this(defaultEntry, 16, initBlank = true) + + /** Creates a new `AnyRefMap` with an initial buffer of specified size. + * + * An `AnyRefMap` can typically contain half as many elements as its buffer size + * before it requires resizing. + */ + def this(initialBufferSize: Int) = this(AnyRefMap.exceptionDefault, initialBufferSize, initBlank = true) + + /** Creates a new `AnyRefMap` with specified default values and initial buffer size. */ + def this(defaultEntry: K => V, initialBufferSize: Int) = this(defaultEntry, initialBufferSize, initBlank = true) + + private[this] var mask = 0 + private[this] var _size = 0 + private[this] var _vacant = 0 + private[this] var _hashes: Array[Int] = null + private[this] var _keys: Array[AnyRef] = null + private[this] var _values: Array[AnyRef] = null + + if (initBlank) defaultInitialize(initialBufferSize) + + private[this] def defaultInitialize(n: Int): Unit = { + mask = + if (n<0) 0x7 + else (((1 << (32 - java.lang.Integer.numberOfLeadingZeros(n-1))) - 1) & 0x3FFFFFFF) | 0x7 + _hashes = new Array[Int](mask+1) + _keys = new Array[AnyRef](mask+1) + _values = new Array[AnyRef](mask+1) + } + + private[collection] def initializeTo( + m: Int, sz: Int, vc: Int, hz: Array[Int], kz: Array[AnyRef], vz: Array[AnyRef] + ): Unit = { + mask = m; _size = sz; _vacant = vc; _hashes = hz; _keys = kz; _values = vz + } + + override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]): AnyRefMap[K,V] = { + var sz = coll.knownSize + if(sz < 0) sz = 4 + val arm = new AnyRefMap[K, V](sz * 2) + coll.iterator.foreach{ case (k,v) => arm(k) = v } + if (arm.size < (sz>>3)) arm.repack() + arm + } + override protected def newSpecificBuilder: Builder[(K, V), AnyRefMap[K,V]] = new AnyRefMapBuilder + + override def size: Int = _size + override def knownSize: Int = size + override def isEmpty: Boolean = _size == 0 + override def empty: AnyRefMap[K,V] = new AnyRefMap(defaultEntry) + + private def imbalanced: Boolean = + (_size + _vacant) > 0.5*mask || _vacant > _size + + private def hashOf(key: K): Int = { + // Note: this method must not return 0 or Int.MinValue, as these indicate no element + if (key eq null) 0x41081989 + else { + val h = key.hashCode + // Part of the MurmurHash3 32 bit finalizer + val i = (h ^ (h >>> 16)) * 0x85EBCA6B + val j = (i ^ (i >>> 13)) & 0x7FFFFFFF + if (j==0) 0x41081989 else j + } + } + + private def seekEntry(h: Int, k: AnyRef): Int = { + var e = h & mask + var x = 0 + var g = 0 + val hashes = _hashes + val keys = _keys + while ({ g = hashes(e); g != 0}) { + if (g == h && { val q = keys(e); (q eq k) || ((q ne null) && (q equals k)) }) return e + x += 1 + e = (e + 2*(x+1)*x - 3) & mask + } + e | MissingBit + } + + @`inline` private def seekEntryOrOpen(h: Int, k: AnyRef): Int = { + var e = h & mask + var x = 0 + var g = 0 + var o = -1 + while ({ g = _hashes(e); g != 0}) { + if (g == h && { val q = _keys(e); (q eq k) || ((q ne null) && (q equals k)) }) return e + else if (o == -1 && g+g == 0) o = e + x += 1 + e = (e + 2*(x+1)*x - 3) & mask + } + if (o >= 0) o | MissVacant else e | MissingBit + } + + override def contains(key: K): Boolean = seekEntry(hashOf(key), key) >= 0 + + override def get(key: K): Option[V] = { + val i = seekEntry(hashOf(key), key) + if (i < 0) None else Some(_values(i).asInstanceOf[V]) + } + + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + val i = seekEntry(hashOf(key), key) + if (i < 0) default else _values(i).asInstanceOf[V] + } + + override def getOrElseUpdate(key: K, defaultValue: => V): V = { + val h = hashOf(key) + var i = seekEntryOrOpen(h, key) + if (i < 0) { + val value = { + val ohs = _hashes + val j = i & IndexMask + val oh = ohs(j) + val ans = defaultValue + // Evaluating `defaultValue` may change the map + // - repack: the array is different + // - element added at `j`: since `i < 0`, the key was missing and `oh` is either 0 or MinValue. + // If `defaultValue` added an element at `j` then `_hashes(j)` must be different now. + // (`hashOf` never returns 0 or MinValue.) + if (ohs.ne(_hashes) || oh != _hashes(j)) { + i = seekEntryOrOpen(h, key) + if (i >= 0) _size -= 1 + } + ans + } + _size += 1 + val j = i & IndexMask + _hashes(j) = h + _keys(j) = key.asInstanceOf[AnyRef] + _values(j) = value.asInstanceOf[AnyRef] + if ((i & VacantBit) != 0) _vacant -= 1 + else if (imbalanced) repack() + value + } + else _values(i).asInstanceOf[V] + } + + /** Retrieves the value associated with a key, or the default for that type if none exists + * (null for AnyRef, 0 for floats and integers). + * + * Note: this is the fastest way to retrieve a value that may or + * may not exist, if the default null/zero is acceptable. For key/value + * pairs that do exist, `apply` (i.e. `map(key)`) is equally fast. + */ + def getOrNull(key: K): V = { + val i = seekEntry(hashOf(key), key) + (if (i < 0) null else _values(i)).asInstanceOf[V] + } + + /** Retrieves the value associated with a key. + * If the key does not exist in the map, the `defaultEntry` for that key + * will be returned instead; an exception will be thrown if no + * `defaultEntry` was supplied. + */ + override def apply(key: K): V = { + val i = seekEntry(hashOf(key), key) + if (i < 0) defaultEntry(key) else _values(i).asInstanceOf[V] + } + + /** Defers to defaultEntry to find a default value for the key. Throws an + * exception if no other default behavior was specified. + */ + override def default(key: K): V = defaultEntry(key) + + private def repack(newMask: Int): Unit = { + val oh = _hashes + val ok = _keys + val ov = _values + mask = newMask + _hashes = new Array[Int](mask+1) + _keys = new Array[AnyRef](mask+1) + _values = new Array[AnyRef](mask+1) + _vacant = 0 + var i = 0 + while (i < oh.length) { + val h = oh(i) + if (h+h != 0) { + var e = h & mask + var x = 0 + while (_hashes(e) != 0) { x += 1; e = (e + 2*(x+1)*x - 3) & mask } + _hashes(e) = h + _keys(e) = ok(i) + _values(e) = ov(i) + } + i += 1 + } + } + + /** Repacks the contents of this `AnyRefMap` for maximum efficiency of lookup. + * + * For maps that undergo a complex creation process with both addition and + * removal of keys, and then are used heavily with no further removal of + * elements, calling `repack` after the end of the creation can result in + * improved performance. Repacking takes time proportional to the number + * of entries in the map. + */ + def repack(): Unit = { + var m = mask + if (_size + _vacant >= 0.5*mask && !(_vacant > 0.2*mask)) m = ((m << 1) + 1) & IndexMask + while (m > 8 && 8*_size < m) m = m >>> 1 + repack(m) + } + + override def put(key: K, value: V): Option[V] = { + val h = hashOf(key) + val i = seekEntryOrOpen(h, key) + if (i < 0) { + val j = i & IndexMask + _hashes(j) = h + _keys(j) = key + _values(j) = value.asInstanceOf[AnyRef] + _size += 1 + if ((i & VacantBit) != 0) _vacant -= 1 + else if (imbalanced) repack() + None + } + else { + val ans = Some(_values(i).asInstanceOf[V]) + _hashes(i) = h + _values(i) = value.asInstanceOf[AnyRef] + ans + } + } + + /** Updates the map to include a new key-value pair. + * + * This is the fastest way to add an entry to an `AnyRefMap`. + */ + override def update(key: K, value: V): Unit = { + val h = hashOf(key) + val i = seekEntryOrOpen(h, key) + if (i < 0) { + val j = i & IndexMask + _hashes(j) = h + _keys(j) = key + _values(j) = value.asInstanceOf[AnyRef] + _size += 1 + if ((i & VacantBit) != 0) _vacant -= 1 + else if (imbalanced) repack() + } + else { + _hashes(i) = h + _values(i) = value.asInstanceOf[AnyRef] + } + } + + /** Adds a new key/value pair to this map and returns the map. */ + @deprecated("Use `addOne` or `update` instead; infix operations with an operand of multiple args will be deprecated", "2.13.3") + def +=(key: K, value: V): this.type = { update(key, value); this } + + /** Adds a new key/value pair to this map and returns the map. */ + @inline final def addOne(key: K, value: V): this.type = { update(key, value); this } + + @inline override final def addOne(kv: (K, V)): this.type = { update(kv._1, kv._2); this } + + def subtractOne(key: K): this.type = { + val i = seekEntry(hashOf(key), key) + if (i >= 0) { + _size -= 1 + _vacant += 1 + _hashes(i) = Int.MinValue + _keys(i) = null + _values(i) = null + } + this + } + + def iterator: Iterator[(K, V)] = new AnyRefMapIterator[(K, V)] { + protected def nextResult(k: K, v: V) = (k, v) + } + override def keysIterator: Iterator[K] = new AnyRefMapIterator[K] { + protected def nextResult(k: K, v: V) = k + } + override def valuesIterator: Iterator[V] = new AnyRefMapIterator[V] { + protected def nextResult(k: K, v: V) = v + } + + private abstract class AnyRefMapIterator[A] extends AbstractIterator[A] { + private[this] val hz = _hashes + private[this] val kz = _keys + private[this] val vz = _values + + private[this] var index = 0 + + def hasNext: Boolean = index= hz.length) return false + h = hz(index) + } + true + } + + def next(): A = { + if (hasNext) { + val ans = nextResult(kz(index).asInstanceOf[K], vz(index).asInstanceOf[V]) + index += 1 + ans + } + else throw new NoSuchElementException("next") + } + + protected def nextResult(k: K, v: V): A + } + + + override def foreach[U](f: ((K,V)) => U): Unit = { + var i = 0 + var e = _size + while (e > 0) { + while(i < _hashes.length && { val h = _hashes(i); h+h == 0 && i < _hashes.length}) i += 1 + if (i < _hashes.length) { + f((_keys(i).asInstanceOf[K], _values(i).asInstanceOf[V])) + i += 1 + e -= 1 + } + else return + } + } + + override def foreachEntry[U](f: (K,V) => U): Unit = { + var i = 0 + var e = _size + while (e > 0) { + while(i < _hashes.length && { val h = _hashes(i); h+h == 0 && i < _hashes.length}) i += 1 + if (i < _hashes.length) { + f(_keys(i).asInstanceOf[K], _values(i).asInstanceOf[V]) + i += 1 + e -= 1 + } + else return + } + } + + override def clone(): AnyRefMap[K, V] = { + val hz = java.util.Arrays.copyOf(_hashes, _hashes.length) + val kz = java.util.Arrays.copyOf(_keys, _keys.length) + val vz = java.util.Arrays.copyOf(_values, _values.length) + val arm = new AnyRefMap[K, V](defaultEntry, 1, initBlank = false) + arm.initializeTo(mask, _size, _vacant, hz, kz, vz) + arm + } + + @deprecated("Consider requiring an immutable Map or fall back to Map.concat", "2.13.0") + override def + [V1 >: V](kv: (K, V1)): AnyRefMap[K, V1] = AnyRefMap.from(new View.Appended(this, kv)) + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): AnyRefMap[K, V1] = { + val m = this + elem1 + elem2 + if(elems.isEmpty) m else m.concat(elems) + } + + override def concat[V2 >: V](xs: scala.collection.IterableOnce[(K, V2)]): AnyRefMap[K, V2] = { + val arm = clone().asInstanceOf[AnyRefMap[K, V2]] + xs.iterator.foreach(kv => arm += kv) + arm + } + + override def ++[V2 >: V](xs: scala.collection.IterableOnce[(K, V2)]): AnyRefMap[K, V2] = concat(xs) + + @deprecated("Use m.clone().addOne(k,v) instead of m.updated(k, v)", "2.13.0") + override def updated[V1 >: V](key: K, value: V1): AnyRefMap[K, V1] = + clone().asInstanceOf[AnyRefMap[K, V1]].addOne(key, value) + + private[this] def foreachElement[A,B](elems: Array[AnyRef], f: A => B): Unit = { + var i,j = 0 + while (i < _hashes.length & j < _size) { + val h = _hashes(i) + if (h+h != 0) { + j += 1 + f(elems(i).asInstanceOf[A]) + } + i += 1 + } + } + + /** Applies a function to all keys of this map. */ + def foreachKey[A](f: K => A): Unit = foreachElement[K,A](_keys, f) + + /** Applies a function to all values of this map. */ + def foreachValue[A](f: V => A): Unit = foreachElement[V,A](_values, f) + + /** Creates a new `AnyRefMap` with different values. + * Unlike `mapValues`, this method generates a new + * collection immediately. + */ + def mapValuesNow[V1](f: V => V1): AnyRefMap[K, V1] = { + val arm = new AnyRefMap[K,V1](AnyRefMap.exceptionDefault, 1, initBlank = false) + val hz = java.util.Arrays.copyOf(_hashes, _hashes.length) + val kz = java.util.Arrays.copyOf(_keys, _keys.length) + val vz = new Array[AnyRef](_values.length) + var i,j = 0 + while (i < _hashes.length & j < _size) { + val h = _hashes(i) + if (h+h != 0) { + j += 1 + vz(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef] + } + i += 1 + } + arm.initializeTo(mask, _size, _vacant, hz, kz, vz) + arm + } + + /** Applies a transformation function to all values stored in this map. + * Note: the default, if any, is not transformed. + */ + @deprecated("Use transformValuesInPlace instead of transformValues", "2.13.0") + @`inline` final def transformValues(f: V => V): this.type = transformValuesInPlace(f) + + /** Applies a transformation function to all values stored in this map. + * Note: the default, if any, is not transformed. + */ + def transformValuesInPlace(f: V => V): this.type = { + var i,j = 0 + while (i < _hashes.length & j < _size) { + val h = _hashes(i) + if (h+h != 0) { + j += 1 + _values(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef] + } + i += 1 + } + this + } + + // The implicit dummy parameter is necessary to distinguish these methods from the base methods they overload (not override). + // Previously, in Scala 2, f took `K with AnyRef` scala/bug#11035 + /** + * An overload of `map` which produces an `AnyRefMap`. + * + * @param f the mapping function must produce a key-value pair where the key is an `AnyRef` + * @param dummy an implicit placeholder for purposes of distinguishing the (erased) signature of this method + */ + def map[K2 <: AnyRef, V2](f: ((K, V)) => (K2, V2))(implicit dummy: DummyImplicit): AnyRefMap[K2, V2] = + AnyRefMap.from(new View.Map(this, f)) + /** + * An overload of `flatMap` which produces an `AnyRefMap`. + * + * @param f the mapping function must produce key-value pairs where the key is an `AnyRef` + * @param dummy an implicit placeholder for purposes of distinguishing the (erased) signature of this method + */ + def flatMap[K2 <: AnyRef, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit dummy: DummyImplicit): AnyRefMap[K2, V2] = + AnyRefMap.from(new View.FlatMap(this, f)) + /** + * An overload of `collect` which produces an `AnyRefMap`. + * + * @param pf the mapping function must produce a key-value pair where the key is an `AnyRef` + * @param dummy an implicit placeholder for purposes of distinguishing the (erased) signature of this method + */ + def collect[K2 <: AnyRef, V2](pf: PartialFunction[(K, V), (K2, V2)])(implicit dummy: DummyImplicit): AnyRefMap[K2, V2] = + strictOptimizedCollect(AnyRefMap.newBuilder[K2, V2], pf) + + override def clear(): Unit = { + import java.util.Arrays.fill + fill(_keys, null) + fill(_values, null) + fill(_hashes, 0) + _size = 0 + _vacant = 0 + } + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(AnyRefMap.toFactory[K, V](AnyRefMap), this) + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "AnyRefMap" +} + +@deprecated("Use `scala.collection.mutable.HashMap` instead for better performance.", since = "2.13.16") +object AnyRefMap { + private final val IndexMask = 0x3FFFFFFF + private final val MissingBit = 0x80000000 + private final val VacantBit = 0x40000000 + private final val MissVacant = 0xC0000000 + + private class ExceptionDefault extends (Any => Nothing) with Serializable { + def apply(k: Any): Nothing = throw new NoSuchElementException(if (k == null) "(null)" else k.toString) + } + private val exceptionDefault = new ExceptionDefault + + /** A builder for instances of `AnyRefMap`. + * + * This builder can be reused to create multiple instances. + */ + final class AnyRefMapBuilder[K <: AnyRef, V] extends ReusableBuilder[(K, V), AnyRefMap[K, V]] { + private[collection] var elems: AnyRefMap[K, V] = new AnyRefMap[K, V] + def addOne(entry: (K, V)): this.type = { + elems += entry + this + } + def clear(): Unit = elems = new AnyRefMap[K, V] + def result(): AnyRefMap[K, V] = elems + override def knownSize: Int = elems.knownSize + } + + /** Creates a new `AnyRefMap` with zero or more key/value pairs. */ + def apply[K <: AnyRef, V](elems: (K, V)*): AnyRefMap[K, V] = buildFromIterableOnce(elems) + + def newBuilder[K <: AnyRef, V]: ReusableBuilder[(K, V), AnyRefMap[K, V]] = new AnyRefMapBuilder[K, V] + + private def buildFromIterableOnce[K <: AnyRef, V](elems: IterableOnce[(K, V)]): AnyRefMap[K, V] = { + var sz = elems.knownSize + if(sz < 0) sz = 4 + val arm = new AnyRefMap[K, V](sz * 2) + elems.iterator.foreach{ case (k,v) => arm(k) = v } + if (arm.size < (sz>>3)) arm.repack() + arm + } + + /** Creates a new empty `AnyRefMap`. */ + def empty[K <: AnyRef, V]: AnyRefMap[K, V] = new AnyRefMap[K, V] + + /** Creates a new empty `AnyRefMap` with the supplied default */ + def withDefault[K <: AnyRef, V](default: K => V): AnyRefMap[K, V] = new AnyRefMap[K, V](default) + + /** Creates a new `AnyRefMap` from an existing source collection. A source collection + * which is already an `AnyRefMap` gets cloned. + * + * @param source Source collection + * @tparam K the type of the keys + * @tparam V the type of the values + * @return a new `AnyRefMap` with the elements of `source` + */ + def from[K <: AnyRef, V](source: IterableOnce[(K, V)]): AnyRefMap[K, V] = source match { + case source: AnyRefMap[_, _] => source.clone().asInstanceOf[AnyRefMap[K, V]] + case _ => buildFromIterableOnce(source) + } + + /** Creates a new `AnyRefMap` from arrays of keys and values. + * Equivalent to but more efficient than `AnyRefMap((keys zip values): _*)`. + */ + def fromZip[K <: AnyRef, V](keys: Array[K], values: Array[V]): AnyRefMap[K, V] = { + val sz = math.min(keys.length, values.length) + val arm = new AnyRefMap[K, V](sz * 2) + var i = 0 + while (i < sz) { arm(keys(i)) = values(i); i += 1 } + if (arm.size < (sz>>3)) arm.repack() + arm + } + + /** Creates a new `AnyRefMap` from keys and values. + * Equivalent to but more efficient than `AnyRefMap((keys zip values): _*)`. + */ + def fromZip[K <: AnyRef, V](keys: Iterable[K], values: Iterable[V]): AnyRefMap[K, V] = { + val sz = math.min(keys.size, values.size) + val arm = new AnyRefMap[K, V](sz * 2) + val ki = keys.iterator + val vi = values.iterator + while (ki.hasNext && vi.hasNext) arm(ki.next()) = vi.next() + if (arm.size < (sz >> 3)) arm.repack() + arm + } + + implicit def toFactory[K <: AnyRef, V](dummy: AnyRefMap.type): Factory[(K, V), AnyRefMap[K, V]] = ToFactory.asInstanceOf[Factory[(K, V), AnyRefMap[K, V]]] + + @SerialVersionUID(3L) + private[this] object ToFactory extends Factory[(AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] with Serializable { + def fromSpecific(it: IterableOnce[(AnyRef, AnyRef)]): AnyRefMap[AnyRef, AnyRef] = AnyRefMap.from[AnyRef, AnyRef](it) + def newBuilder: Builder[(AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] = AnyRefMap.newBuilder[AnyRef, AnyRef] + } + + implicit def toBuildFrom[K <: AnyRef, V](factory: AnyRefMap.type): BuildFrom[Any, (K, V), AnyRefMap[K, V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (K, V), AnyRefMap[K, V]]] + private[this] object ToBuildFrom extends BuildFrom[Any, (AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] { + def fromSpecific(from: Any)(it: IterableOnce[(AnyRef, AnyRef)]): AnyRefMap[AnyRef, AnyRef] = AnyRefMap.from(it) + def newBuilder(from: Any): ReusableBuilder[(AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] = AnyRefMap.newBuilder[AnyRef, AnyRef] + } + + implicit def iterableFactory[K <: AnyRef, V]: Factory[(K, V), AnyRefMap[K, V]] = toFactory[K, V](this) + implicit def buildFromAnyRefMap[K <: AnyRef, V]: BuildFrom[AnyRefMap[_, _], (K, V), AnyRefMap[K, V]] = toBuildFrom(this) +} diff --git a/library/src/scala/collection/mutable/ArrayBuffer.scala b/library/src/scala/collection/mutable/ArrayBuffer.scala new file mode 100644 index 000000000000..035c35c88a26 --- /dev/null +++ b/library/src/scala/collection/mutable/ArrayBuffer.scala @@ -0,0 +1,401 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import java.util.Arrays +import scala.annotation.{nowarn, tailrec} +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.{CommonErrors, DefaultSerializable} +import scala.runtime.PStatics.VM_MaxArraySize + +/** An implementation of the `Buffer` class using an array to + * represent the assembled sequence internally. Append, update and random + * access take constant time (amortized time). Prepends and removes are + * linear in the buffer size. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#array-buffers "Scala's Collection Library overview"]] + * section on `Array Buffers` for more information. + + * + * @tparam A the type of this arraybuffer's elements. + * + * @define Coll `mutable.ArrayBuffer` + * @define coll array buffer + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(-1582447879429021880L) +class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int) + extends AbstractBuffer[A] + with IndexedBuffer[A] + with IndexedSeqOps[A, ArrayBuffer, ArrayBuffer[A]] + with StrictOptimizedSeqOps[A, ArrayBuffer, ArrayBuffer[A]] + with IterableFactoryDefaults[A, ArrayBuffer] + with DefaultSerializable { + + def this() = this(new Array[AnyRef](ArrayBuffer.DefaultInitialSize), 0) + + def this(initialSize: Int) = this(new Array[AnyRef](initialSize max 1), 0) + + @transient private[this] var mutationCount: Int = 0 + + // needs to be `private[collection]` or `protected[collection]` for parallel-collections + protected[collection] var array: Array[AnyRef] = initialElements + protected var size0 = initialSize + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + import scala.collection.convert.impl._ + shape.parUnbox(new ObjectArrayStepper(array, 0, length).asInstanceOf[AnyStepper[A] with EfficientSplit]) + } + + override def knownSize: Int = super[IndexedSeqOps].knownSize + + /** Ensure that the internal array has at least `n` cells. */ + protected def ensureSize(n: Int): Unit = { + array = ArrayBuffer.ensureSize(array, size0, n) + } + + /** Uses the given size to resize internal storage, if necessary. + * + * @param size Expected maximum number of elements. + */ + def sizeHint(size: Int): Unit = + if(size > length && size >= 1) ensureSize(size) + + /** Reduce length to `n`, nulling out all dropped elements */ + private def reduceToSize(n: Int): Unit = { + mutationCount += 1 + Arrays.fill(array, n, size0, null) + size0 = n + } + + /** Trims the ArrayBuffer to an appropriate size for the current + * number of elements (rounding up to the next natural size), + * which may replace the array by a shorter one. + * This allows releasing some unused memory. + */ + def trimToSize(): Unit = { + resize(length) + } + + /** Trims the `array` buffer size down to either a power of 2 + * or Int.MaxValue while keeping first `requiredLength` elements. + */ + private def resize(requiredLength: Int): Unit = + array = ArrayBuffer.downsize(array, requiredLength) + + @inline private def checkWithinBounds(lo: Int, hi: Int) = { + if (lo < 0) throw CommonErrors.indexOutOfBounds(index = lo, max = size0 - 1) + if (hi > size0) throw CommonErrors.indexOutOfBounds(index = hi - 1, max = size0 - 1) + } + + def apply(n: Int): A = { + checkWithinBounds(n, n + 1) + array(n).asInstanceOf[A] + } + + def update(@deprecatedName("n", "2.13.0") index: Int, elem: A): Unit = { + checkWithinBounds(index, index + 1) + mutationCount += 1 + array(index) = elem.asInstanceOf[AnyRef] + } + + def length = size0 + + // TODO: return `IndexedSeqView` rather than `ArrayBufferView` + override def view: ArrayBufferView[A] = new ArrayBufferView(this, () => mutationCount) + + override def iterableFactory: SeqFactory[ArrayBuffer] = ArrayBuffer + + /** Note: This does not actually resize the internal representation. + * See clearAndShrink if you want to also resize internally + */ + def clear(): Unit = reduceToSize(0) + + /** + * Clears this buffer and shrinks to @param size (rounding up to the next + * natural size) + * @param size + */ + def clearAndShrink(size: Int = ArrayBuffer.DefaultInitialSize): this.type = { + clear() + resize(size) + this + } + + def addOne(elem: A): this.type = { + mutationCount += 1 + val newSize = size0 + 1 + ensureSize(newSize) + size0 = newSize + this(size0 - 1) = elem + this + } + + // Overridden to use array copying for efficiency where possible. + override def addAll(elems: IterableOnce[A]): this.type = { + elems match { + case elems: ArrayBuffer[_] => + val elemsLength = elems.size0 + if (elemsLength > 0) { + mutationCount += 1 + ensureSize(size0 + elemsLength) + Array.copy(elems.array, 0, array, length, elemsLength) + size0 = length + elemsLength + } + case _ => super.addAll(elems) + } + this + } + + def insert(@deprecatedName("n", "2.13.0") index: Int, elem: A): Unit = { + checkWithinBounds(index, index) + mutationCount += 1 + ensureSize(size0 + 1) + Array.copy(array, index, array, index + 1, size0 - index) + size0 += 1 + this(index) = elem + } + + def prepend(elem: A): this.type = { + insert(0, elem) + this + } + + def insertAll(@deprecatedName("n", "2.13.0") index: Int, elems: IterableOnce[A]): Unit = { + checkWithinBounds(index, index) + elems match { + case elems: collection.Iterable[A] => + val elemsLength = elems.size + if (elemsLength > 0) { + mutationCount += 1 + ensureSize(size0 + elemsLength) + val len = size0 + Array.copy(array, index, array, index + elemsLength, len - index) + // if `elems eq this`, this copy is safe because + // - `elems.array eq this.array` + // - we didn't overwrite the values being inserted after moving them in + // the previous line + // - `copyElemsToArray` will call `System.arraycopy` + // - `System.arraycopy` will effectively "read" all the values before + // overwriting any of them when two arrays are the same reference + val actual = IterableOnce.copyElemsToArray(elems, array.asInstanceOf[Array[Any]], index, elemsLength) + if (actual != elemsLength) throw new IllegalStateException(s"Copied $actual of $elemsLength") + size0 = len + elemsLength // update size AFTER the copy, in case we're inserting a proxy + } + case _ => insertAll(index, ArrayBuffer.from(elems)) + } + } + + /** Note: This does not actually resize the internal representation. + * See trimToSize if you want to also resize internally + */ + def remove(@deprecatedName("n", "2.13.0") index: Int): A = { + checkWithinBounds(index, index + 1) + val res = this(index) + Array.copy(array, index + 1, array, index, size0 - (index + 1)) + reduceToSize(size0 - 1) + res + } + + /** Note: This does not actually resize the internal representation. + * See trimToSize if you want to also resize internally + */ + def remove(@deprecatedName("n", "2.13.0") index: Int, count: Int): Unit = + if (count > 0) { + checkWithinBounds(index, index + count) + Array.copy(array, index + count, array, index, size0 - (index + count)) + reduceToSize(size0 - count) + } else if (count < 0) { + throw new IllegalArgumentException("removing negative number of elements: " + count) + } + + @deprecated("Use 'this' instance instead", "2.13.0") + @deprecatedOverriding("ArrayBuffer[A] no longer extends Builder[A, ArrayBuffer[A]]", "2.13.0") + @inline def result(): this.type = this + + @deprecated("Use 'new GrowableBuilder(this).mapResult(f)' instead", "2.13.0") + @deprecatedOverriding("ArrayBuffer[A] no longer extends Builder[A, ArrayBuffer[A]]", "2.13.0") + @inline def mapResult[NewTo](f: (ArrayBuffer[A]) => NewTo): Builder[A, NewTo] = new GrowableBuilder[A, ArrayBuffer[A]](this).mapResult(f) + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "ArrayBuffer" + + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { + val copied = IterableOnce.elemsToCopyToArray(length, xs.length, start, len) + if(copied > 0) { + Array.copy(array, 0, xs, start, copied) + } + copied + } + + /** Sorts this $coll in place according to an Ordering. + * + * @see [[scala.collection.mutable.IndexedSeqOps.sortInPlace]] + * @param ord the ordering to be used to compare elements. + * @return modified input $coll sorted according to the ordering `ord`. + */ + override def sortInPlace[B >: A]()(implicit ord: Ordering[B]): this.type = { + if (length > 1) { + mutationCount += 1 + scala.util.Sorting.stableSort(array.asInstanceOf[Array[B]], 0, length) + } + this + } + + @tailrec private def foldl[B](start: Int, end: Int, z: B, op: (B, A) => B): B = + if (start == end) z + else foldl(start + 1, end, op(z, array(start).asInstanceOf[A]), op) + + @tailrec private def foldr[B](start: Int, end: Int, z: B, op: (A, B) => B): B = + if (start == end) z + else foldr(start, end - 1, op(array(end - 1).asInstanceOf[A], z), op) + + override def foldLeft[B](z: B)(op: (B, A) => B): B = foldl(0, length, z, op) + + override def foldRight[B](z: B)(op: (A, B) => B): B = foldr(0, length, z, op) + + override def reduceLeft[B >: A](op: (B, A) => B): B = if (length > 0) foldl(1, length, array(0).asInstanceOf[B], op) else super.reduceLeft(op) + + override def reduceRight[B >: A](op: (A, B) => B): B = if (length > 0) foldr(0, length - 1, array(length - 1).asInstanceOf[B], op) else super.reduceRight(op) +} + +/** + * Factory object for the `ArrayBuffer` class. + * + * $factoryInfo + * + * @define coll array buffer + * @define Coll `mutable.ArrayBuffer` + */ +@SerialVersionUID(3L) +object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] { + final val DefaultInitialSize = 16 + private[this] val emptyArray = new Array[AnyRef](0) + + def from[B](coll: collection.IterableOnce[B]): ArrayBuffer[B] = { + val k = coll.knownSize + if (k >= 0) { + // Avoid reallocation of buffer if length is known + val array = ensureSize(emptyArray, 0, k) // don't duplicate sizing logic, and check VM array size limit + val actual = IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + if (actual != k) throw new IllegalStateException(s"Copied $actual of $k") + new ArrayBuffer[B](array, k) + } + else new ArrayBuffer[B] ++= coll + } + + def newBuilder[A]: Builder[A, ArrayBuffer[A]] = new GrowableBuilder[A, ArrayBuffer[A]](empty[A]) { + override def sizeHint(size: Int): Unit = elems.sizeHint(size) + } + + def empty[A]: ArrayBuffer[A] = new ArrayBuffer[A]() + + /** + * The increased size for an array-backed collection. + * + * @param arrayLen the length of the backing array + * @param targetLen the minimum length to resize up to + * @return + * - `-1` if no resizing is needed, else + * - `VM_MaxArraySize` if `arrayLen` is too large to be doubled, else + * - `max(targetLen, arrayLen * 2, DefaultInitialSize)`. + * - Throws an exception if `targetLen` exceeds `VM_MaxArraySize` or is negative (overflow). + */ + private[mutable] def resizeUp(arrayLen: Int, targetLen: Int): Int = + if (targetLen < 0) throw new RuntimeException(s"Overflow while resizing array of array-backed collection. Requested length: $targetLen; current length: $arrayLen; increase: ${targetLen - arrayLen}") + else if (targetLen <= arrayLen) -1 + else if (targetLen > VM_MaxArraySize) throw new RuntimeException(s"Array of array-backed collection exceeds VM length limit of $VM_MaxArraySize. Requested length: $targetLen; current length: $arrayLen") + else if (arrayLen > VM_MaxArraySize / 2) VM_MaxArraySize + else math.max(targetLen, math.max(arrayLen * 2, DefaultInitialSize)) + + // if necessary, copy (curSize elements of) the array to a new array of capacity n. + // Should use Array.copyOf(array, resizeEnsuring(array.length))? + private def ensureSize(array: Array[AnyRef], curSize: Int, targetSize: Int): Array[AnyRef] = { + val newLen = resizeUp(array.length, targetSize) + if (newLen < 0) array + else { + val res = new Array[AnyRef](newLen) + System.arraycopy(array, 0, res, 0, curSize) + res + } + } + + /** + * @param arrayLen the length of the backing array + * @param targetLen the length to resize down to, if smaller than `arrayLen` + * @return -1 if no resizing is needed, or the size for the new array otherwise + */ + private def resizeDown(arrayLen: Int, targetLen: Int): Int = + if (targetLen >= arrayLen) -1 else math.max(targetLen, 0) + private def downsize(array: Array[AnyRef], targetSize: Int): Array[AnyRef] = { + val newLen = resizeDown(array.length, targetSize) + if (newLen < 0) array + else if (newLen == 0) emptyArray + else { + val res = new Array[AnyRef](newLen) + System.arraycopy(array, 0, res, 0, targetSize) + res + } + } +} + +// TODO: use `CheckedIndexedSeqView.Id` once we can change the return type of `ArrayBuffer#view` +final class ArrayBufferView[A] private[mutable](underlying: ArrayBuffer[A], mutationCount: () => Int) + extends AbstractIndexedSeqView[A] { + @deprecated("never intended to be public; call ArrayBuffer#view instead", since = "2.13.7") + def this(array: Array[AnyRef], length: Int) = { + // this won't actually track mutation, but it would be a pain to have the implementation + // check if we have a method to get the current mutation count or not on every method and + // change what it does based on that. hopefully no one ever calls this. + this({ + val _array = array + val _length = length + new ArrayBuffer[A](0) { + this.array = _array + this.size0 = _length + } + }, () => 0) + } + + @deprecated("never intended to be public", since = "2.13.7") + def array: Array[AnyRef] = underlying.toArray[Any].asInstanceOf[Array[AnyRef]] + + @throws[IndexOutOfBoundsException] + def apply(n: Int): A = underlying(n) + def length: Int = underlying.length + override protected[this] def className = "ArrayBufferView" + + // we could inherit all these from `CheckedIndexedSeqView`, except this class is public + override def iterator: Iterator[A] = new CheckedIndexedSeqView.CheckedIterator(this, mutationCount()) + override def reverseIterator: Iterator[A] = new CheckedIndexedSeqView.CheckedReverseIterator(this, mutationCount()) + + override def appended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Appended(this, elem)(mutationCount) + override def prepended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Prepended(elem, this)(mutationCount) + override def take(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Take(this, n)(mutationCount) + override def takeRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.TakeRight(this, n)(mutationCount) + override def drop(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Drop(this, n)(mutationCount) + override def dropRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.DropRight(this, n)(mutationCount) + override def map[B](f: A => B): IndexedSeqView[B] = new CheckedIndexedSeqView.Map(this, f)(mutationCount) + override def reverse: IndexedSeqView[A] = new CheckedIndexedSeqView.Reverse(this)(mutationCount) + override def slice(from: Int, until: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Slice(this, from, until)(mutationCount) + override def tapEach[U](f: A => U): IndexedSeqView[A] = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount) + + override def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(prefix, this)(mutationCount) +} diff --git a/library/src/scala/collection/mutable/ArrayBuilder.scala b/library/src/scala/collection/mutable/ArrayBuilder.scala new file mode 100644 index 000000000000..e962dd024836 --- /dev/null +++ b/library/src/scala/collection/mutable/ArrayBuilder.scala @@ -0,0 +1,536 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.collection.mutable.ArrayBuffer.resizeUp +import scala.reflect.ClassTag + +/** A builder class for arrays. + * + * @tparam T the type of the elements for the builder. + */ +@SerialVersionUID(3L) +sealed abstract class ArrayBuilder[T] + extends ReusableBuilder[T, Array[T]] + with Serializable { + protected[this] var capacity: Int = 0 + protected[this] def elems: Array[T] // may not be allocated at size = capacity = 0 + protected var size: Int = 0 + + /** Current number of elements. */ + def length: Int = size + + /** Current number of elements. */ + override def knownSize: Int = size + + protected[this] final def ensureSize(size: Int): Unit = { + val newLen = resizeUp(capacity, size) + if (newLen > 0) resize(newLen) + } + + override final def sizeHint(size: Int): Unit = if (capacity < size) resize(size) + + def clear(): Unit = size = 0 + + protected[this] def resize(size: Int): Unit + + /** Add all elements of an array. */ + def addAll(xs: Array[_ <: T]): this.type = addAll(xs, 0, xs.length) + + /** Add a slice of an array. */ + def addAll(xs: Array[_ <: T], offset: Int, length: Int): this.type = { + val offset1 = offset.max(0) + val length1 = length.max(0) + val effectiveLength = length1.min(xs.length - offset1) + doAddAll(xs, offset1, effectiveLength) + } + + private def doAddAll(xs: Array[_ <: T], offset: Int, length: Int): this.type = { + if (length > 0) { + ensureSize(this.size + length) + Array.copy(xs, offset, elems, this.size, length) + size += length + } + this + } + + override def addAll(xs: IterableOnce[T]): this.type = { + val k = xs.knownSize + if (k > 0) { + ensureSize(this.size + k) + val actual = IterableOnce.copyElemsToArray(xs, elems, this.size) + if (actual != k) throw new IllegalStateException(s"Copied $actual of $k") + size += k + } else if (k < 0) super.addAll(xs) + this + } +} + +/** A companion object for array builders. + */ +object ArrayBuilder { + + /** Creates a new arraybuilder of type `T`. + * + * @tparam T type of the elements for the array builder, with a `ClassTag` context bound. + * @return a new empty array builder. + */ + @inline def make[T: ClassTag]: ArrayBuilder[T] = { + val tag = implicitly[ClassTag[T]] + tag.runtimeClass match { + case java.lang.Byte.TYPE => new ArrayBuilder.ofByte().asInstanceOf[ArrayBuilder[T]] + case java.lang.Short.TYPE => new ArrayBuilder.ofShort().asInstanceOf[ArrayBuilder[T]] + case java.lang.Character.TYPE => new ArrayBuilder.ofChar().asInstanceOf[ArrayBuilder[T]] + case java.lang.Integer.TYPE => new ArrayBuilder.ofInt().asInstanceOf[ArrayBuilder[T]] + case java.lang.Long.TYPE => new ArrayBuilder.ofLong().asInstanceOf[ArrayBuilder[T]] + case java.lang.Float.TYPE => new ArrayBuilder.ofFloat().asInstanceOf[ArrayBuilder[T]] + case java.lang.Double.TYPE => new ArrayBuilder.ofDouble().asInstanceOf[ArrayBuilder[T]] + case java.lang.Boolean.TYPE => new ArrayBuilder.ofBoolean().asInstanceOf[ArrayBuilder[T]] + case java.lang.Void.TYPE => new ArrayBuilder.ofUnit().asInstanceOf[ArrayBuilder[T]] + case _ => new ArrayBuilder.ofRef[T with AnyRef]()(tag.asInstanceOf[ClassTag[T with AnyRef]]).asInstanceOf[ArrayBuilder[T]] + } + } + + /** A class for array builders for arrays of reference types. + * + * This builder can be reused. + * + * @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound. + */ + @SerialVersionUID(3L) + final class ofRef[T <: AnyRef](implicit ct: ClassTag[T]) extends ArrayBuilder[T] { + + protected var elems: Array[T] = _ + + private def mkArray(size: Int): Array[T] = { + if (capacity == size && capacity > 0) elems + else if (elems eq null) new Array[T](size) + else java.util.Arrays.copyOf[T](elems, size) + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: T): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[T] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def clear(): Unit = { + super.clear() + if(elems ne null) java.util.Arrays.fill(elems.asInstanceOf[Array[AnyRef]], null) + } + + override def equals(other: Any): Boolean = other match { + case x: ofRef[_] => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofRef" + } + + /** A class for array builders for arrays of `byte`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofByte extends ArrayBuilder[Byte] { + + protected var elems: Array[Byte] = _ + + private def mkArray(size: Int): Array[Byte] = { + val newelems = new Array[Byte](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Byte): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Byte] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofByte => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofByte" + } + + /** A class for array builders for arrays of `short`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofShort extends ArrayBuilder[Short] { + + protected var elems: Array[Short] = _ + + private def mkArray(size: Int): Array[Short] = { + val newelems = new Array[Short](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Short): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Short] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofShort => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofShort" + } + + /** A class for array builders for arrays of `char`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofChar extends ArrayBuilder[Char] { + + protected var elems: Array[Char] = _ + + private def mkArray(size: Int): Array[Char] = { + val newelems = new Array[Char](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Char): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Char] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofChar => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofChar" + } + + /** A class for array builders for arrays of `int`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofInt extends ArrayBuilder[Int] { + + protected var elems: Array[Int] = _ + + private def mkArray(size: Int): Array[Int] = { + val newelems = new Array[Int](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Int): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Int] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofInt => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofInt" + } + + /** A class for array builders for arrays of `long`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofLong extends ArrayBuilder[Long] { + + protected var elems: Array[Long] = _ + + private def mkArray(size: Int): Array[Long] = { + val newelems = new Array[Long](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Long): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Long] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofLong => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofLong" + } + + /** A class for array builders for arrays of `float`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofFloat extends ArrayBuilder[Float] { + + protected var elems: Array[Float] = _ + + private def mkArray(size: Int): Array[Float] = { + val newelems = new Array[Float](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Float): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Float] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofFloat => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofFloat" + } + + /** A class for array builders for arrays of `double`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofDouble extends ArrayBuilder[Double] { + + protected var elems: Array[Double] = _ + + private def mkArray(size: Int): Array[Double] = { + val newelems = new Array[Double](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Double): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Double] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofDouble => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofDouble" + } + + /** A class for array builders for arrays of `boolean`s. It can be reused. */ + @SerialVersionUID(3L) + class ofBoolean extends ArrayBuilder[Boolean] { + + protected var elems: Array[Boolean] = _ + + private def mkArray(size: Int): Array[Boolean] = { + val newelems = new Array[Boolean](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Boolean): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result(): Array[Boolean] = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofBoolean => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofBoolean" + } + + /** A class for array builders for arrays of `Unit` type. It can be reused. */ + @SerialVersionUID(3L) + final class ofUnit extends ArrayBuilder[Unit] { + + protected def elems: Array[Unit] = throw new UnsupportedOperationException() + + def addOne(elem: Unit): this.type = { + val newSize = size + 1 + ensureSize(newSize) + size = newSize + this + } + + override def addAll(xs: IterableOnce[Unit]): this.type = { + val newSize = size + xs.iterator.size + ensureSize(newSize) + size = newSize + this + } + + override def addAll(xs: Array[_ <: Unit], offset: Int, length: Int): this.type = { + val newSize = size + length + ensureSize(newSize) + size = newSize + this + } + + def result() = { + val ans = new Array[Unit](size) + var i = 0 + while (i < size) { ans(i) = (); i += 1 } + ans + } + + override def equals(other: Any): Boolean = other match { + case x: ofUnit => (size == x.size) + case _ => false + } + + protected[this] def resize(size: Int): Unit = capacity = size + + override def toString = "ArrayBuilder.ofUnit" + } +} diff --git a/library/src/scala/collection/mutable/ArrayDeque.scala b/library/src/scala/collection/mutable/ArrayDeque.scala new file mode 100644 index 000000000000..d7fbf932fc53 --- /dev/null +++ b/library/src/scala/collection/mutable/ArrayDeque.scala @@ -0,0 +1,648 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import scala.annotation.nowarn +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.{CommonErrors, DefaultSerializable} +import scala.reflect.ClassTag + +/** An implementation of a double-ended queue that internally uses a resizable circular buffer. + * + * Append, prepend, removeHead, removeLast and random-access (indexed-lookup and indexed-replacement) + * take amortized constant time. In general, removals and insertions at i-th index are O(min(i, n-i)) + * and thus insertions and removals from end/beginning are fast. + * + * @note Subclasses ''must'' override the `ofArray` protected method to return a more specific type. + * + * @tparam A the type of this ArrayDeque's elements. + * + * @define Coll `mutable.ArrayDeque` + * @define coll array deque + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +class ArrayDeque[A] protected ( + protected var array: Array[AnyRef], + private[ArrayDeque] var start: Int, + private[ArrayDeque] var end: Int +) extends AbstractBuffer[A] + with IndexedBuffer[A] + with IndexedSeqOps[A, ArrayDeque, ArrayDeque[A]] + with StrictOptimizedSeqOps[A, ArrayDeque, ArrayDeque[A]] + with IterableFactoryDefaults[A, ArrayDeque] + with ArrayDequeOps[A, ArrayDeque, ArrayDeque[A]] + with Cloneable[ArrayDeque[A]] + with DefaultSerializable { + + reset(array, start, end) + + private[this] def reset(array: Array[AnyRef], start: Int, end: Int) = { + assert((array.length & (array.length - 1)) == 0, s"Array.length must be power of 2") + requireBounds(idx = start, until = array.length) + requireBounds(idx = end, until = array.length) + this.array = array + this.start = start + this.end = end + } + + def this(initialSize: Int = ArrayDeque.DefaultInitialSize) = this(ArrayDeque.alloc(initialSize), start = 0, end = 0) + + override def knownSize: Int = super[IndexedSeqOps].knownSize + + // No-Op override to allow for more efficient stepper in a minor release. + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = super.stepper(shape) + + def apply(idx: Int): A = { + requireBounds(idx) + _get(idx) + } + + def update(idx: Int, elem: A): Unit = { + requireBounds(idx) + _set(idx, elem) + } + + def addOne(elem: A): this.type = { + ensureSize(length + 1) + appendAssumingCapacity(elem) + } + + def prepend(elem: A): this.type = { + ensureSize(length + 1) + prependAssumingCapacity(elem) + } + + @inline private[ArrayDeque] def appendAssumingCapacity(elem: A): this.type = { + array(end) = elem.asInstanceOf[AnyRef] + end = end_+(1) + this + } + + @inline private[ArrayDeque] def prependAssumingCapacity(elem: A): this.type = { + start = start_-(1) + array(start) = elem.asInstanceOf[AnyRef] + this + } + + override def prependAll(elems: IterableOnce[A]): this.type = { + val it = elems.iterator + if (it.nonEmpty) { + val n = length + // The following code resizes the current collection at most once and traverses elems at most twice + elems.knownSize match { + // Size is too expensive to compute AND we can traverse it only once - can't do much but retry with an IndexedSeq + case srcLength if srcLength < 0 => prependAll(it.to(IndexedSeq: Factory[A, IndexedSeq[A]] /* type ascription needed by Dotty */)) + + // We know for sure we need to resize to hold everything, might as well resize and memcopy upfront + case srcLength if mustGrow(srcLength + n) => + val finalLength = srcLength + n + val array2 = ArrayDeque.alloc(finalLength) + @annotation.unused val copied = it.copyToArray(array2.asInstanceOf[Array[A]]) + //assert(copied == srcLength) + copySliceToArray(srcStart = 0, dest = array2, destStart = srcLength, maxItems = n) + reset(array = array2, start = 0, end = finalLength) + + // Just fill up from (start - srcLength) to (start - 1) and move back start + case srcLength => + // Optimized version of `elems.zipWithIndex.foreach((elem, i) => _set(i - srcLength, elem))` + var i = 0 + while(i < srcLength) { + _set(i - srcLength, it.next()) + i += 1 + } + start = start_-(srcLength) + } + } + this + } + + override def addAll(elems: IterableOnce[A]): this.type = { + elems.knownSize match { + case srcLength if srcLength > 0 => + ensureSize(srcLength + length) + elems.iterator.foreach(appendAssumingCapacity) + case _ => elems.iterator.foreach(+=) + } + this + } + + def insert(idx: Int, elem: A): Unit = { + requireBounds(idx, length+1) + val n = length + if (idx == 0) { + prepend(elem) + } else if (idx == n) { + addOne(elem) + } else { + val finalLength = n + 1 + if (mustGrow(finalLength)) { + val array2 = ArrayDeque.alloc(finalLength) + copySliceToArray(srcStart = 0, dest = array2, destStart = 0, maxItems = idx) + array2(idx) = elem.asInstanceOf[AnyRef] + copySliceToArray(srcStart = idx, dest = array2, destStart = idx + 1, maxItems = n) + reset(array = array2, start = 0, end = finalLength) + } else if (n <= idx * 2) { + var i = n - 1 + while(i >= idx) { + _set(i + 1, _get(i)) + i -= 1 + } + end = end_+(1) + i += 1 + _set(i, elem) + } else { + var i = 0 + while(i < idx) { + _set(i - 1, _get(i)) + i += 1 + } + start = start_-(1) + _set(i, elem) + } + } + } + + def insertAll(idx: Int, elems: IterableOnce[A]): Unit = { + requireBounds(idx, length+1) + val n = length + if (idx == 0) { + prependAll(elems) + } else if (idx == n) { + addAll(elems) + } else { + // Get both an iterator and the length of the source (by copying the source to an IndexedSeq if needed) + val (it, srcLength) = { + val _srcLength = elems.knownSize + if (_srcLength >= 0) (elems.iterator, _srcLength) + else { + val indexed = IndexedSeq.from(elems) + (indexed.iterator, indexed.size) + } + } + if (it.nonEmpty) { + val finalLength = srcLength + n + // Either we resize right away or move prefix left or suffix right + if (mustGrow(finalLength)) { + val array2 = ArrayDeque.alloc(finalLength) + copySliceToArray(srcStart = 0, dest = array2, destStart = 0, maxItems = idx) + @annotation.unused val copied = it.copyToArray(array2.asInstanceOf[Array[A]], idx) + //assert(copied == srcLength) + copySliceToArray(srcStart = idx, dest = array2, destStart = idx + srcLength, maxItems = n) + reset(array = array2, start = 0, end = finalLength) + } else if (2*idx >= n) { // Cheaper to shift the suffix right + var i = n - 1 + while(i >= idx) { + _set(i + srcLength, _get(i)) + i -= 1 + } + end = end_+(srcLength) + while(it.hasNext) { + i += 1 + _set(i, it.next()) + } + } else { // Cheaper to shift prefix left + var i = 0 + while(i < idx) { + _set(i - srcLength, _get(i)) + i += 1 + } + start = start_-(srcLength) + while(it.hasNext) { + _set(i, it.next()) + i += 1 + } + } + } + } + } + + def remove(idx: Int, count: Int): Unit = { + if (count > 0) { + requireBounds(idx) + val n = length + val removals = Math.min(n - idx, count) + val finalLength = n - removals + val suffixStart = idx + removals + // If we know we can resize after removing, do it right away using arrayCopy + // Else, choose the shorter: either move the prefix (0 until idx) right OR the suffix (idx+removals until n) left + if (shouldShrink(finalLength)) { + val array2 = ArrayDeque.alloc(finalLength) + copySliceToArray(srcStart = 0, dest = array2, destStart = 0, maxItems = idx) + copySliceToArray(srcStart = suffixStart, dest = array2, destStart = idx, maxItems = n) + reset(array = array2, start = 0, end = finalLength) + } else if (2*idx <= finalLength) { // Cheaper to move the prefix right + var i = suffixStart - 1 + while(i >= removals) { + _set(i, _get(i - removals)) + i -= 1 + } + while(i >= 0) { + _set(i, null.asInstanceOf[A]) + i -= 1 + } + start = start_+(removals) + } else { // Cheaper to move the suffix left + var i = idx + while(i < finalLength) { + _set(i, _get(i + removals)) + i += 1 + } + while(i < n) { + _set(i, null.asInstanceOf[A]) + i += 1 + } + end = end_-(removals) + } + } else { + require(count == 0, s"removing negative number of elements: $count") + } + } + + def remove(idx: Int): A = { + val elem = this(idx) + remove(idx, 1) + elem + } + + override def subtractOne(elem: A): this.type = { + val idx = indexOf(elem) + if (idx >= 0) remove(idx, 1) //TODO: SeqOps should be fluent API + this + } + + /** + * + * @param resizeInternalRepr If this is set, resize the internal representation to reclaim space once in a while + * @return + */ + def removeHeadOption(resizeInternalRepr: Boolean = false): Option[A] = + if (isEmpty) None else Some(removeHeadAssumingNonEmpty(resizeInternalRepr)) + + /** + * Unsafely remove the first element (throws exception when empty) + * See also removeHeadOption() + * + * @param resizeInternalRepr If this is set, resize the internal representation to reclaim space once in a while + * @throws NoSuchElementException when empty + * @return + */ + def removeHead(resizeInternalRepr: Boolean = false): A = + if (isEmpty) throw new NoSuchElementException(s"empty collection") else removeHeadAssumingNonEmpty(resizeInternalRepr) + + @inline private[this] def removeHeadAssumingNonEmpty(resizeInternalRepr: Boolean = false): A = { + val elem = array(start) + array(start) = null + start = start_+(1) + if (resizeInternalRepr) resize(length) + elem.asInstanceOf[A] + } + + /** + * + * @param resizeInternalRepr If this is set, resize the internal representation to reclaim space once in a while + * @return + */ + def removeLastOption(resizeInternalRepr: Boolean = false): Option[A] = + if (isEmpty) None else Some(removeLastAssumingNonEmpty(resizeInternalRepr)) + + /** + * Unsafely remove the last element (throws exception when empty) + * See also removeLastOption() + * + * @param resizeInternalRepr If this is set, resize the internal representation to reclaim space once in a while + * @throws NoSuchElementException when empty + * @return + */ + def removeLast(resizeInternalRepr: Boolean = false): A = + if (isEmpty) throw new NoSuchElementException(s"empty collection") else removeLastAssumingNonEmpty(resizeInternalRepr) + + @`inline` private[this] def removeLastAssumingNonEmpty(resizeInternalRepr: Boolean = false): A = { + end = end_-(1) + val elem = array(end) + array(end) = null + if (resizeInternalRepr) resize(length) + elem.asInstanceOf[A] + } + + /** + * Remove all elements from this collection and return the elements while emptying this data structure + * @return + */ + def removeAll(): scala.collection.immutable.Seq[A] = { + val elems = scala.collection.immutable.Seq.newBuilder[A] + elems.sizeHint(length) + while(nonEmpty) { + elems += removeHeadAssumingNonEmpty() + } + elems.result() + } + + /** + * Remove all elements from this collection and return the elements in reverse while emptying this data structure + * @return + */ + def removeAllReverse(): scala.collection.immutable.Seq[A] = { + val elems = scala.collection.immutable.Seq.newBuilder[A] + elems.sizeHint(length) + while(nonEmpty) { + elems += removeLastAssumingNonEmpty() + } + elems.result() + } + + /** + * Returns and removes all elements from the left of this queue which satisfy the given predicate + * + * @param f the predicate used for choosing elements + * @return + */ + def removeHeadWhile(f: A => Boolean): scala.collection.immutable.Seq[A] = { + val elems = scala.collection.immutable.Seq.newBuilder[A] + while(headOption.exists(f)) { + elems += removeHeadAssumingNonEmpty() + } + elems.result() + } + + /** + * Returns and removes all elements from the right of this queue which satisfy the given predicate + * + * @param f the predicate used for choosing elements + * @return + */ + def removeLastWhile(f: A => Boolean): scala.collection.immutable.Seq[A] = { + val elems = scala.collection.immutable.Seq.newBuilder[A] + while(lastOption.exists(f)) { + elems += removeLastAssumingNonEmpty() + } + elems.result() + } + + /** Returns the first element which satisfies the given predicate after or at some start index + * and removes this element from the collections + * + * @param p the predicate used for choosing the first element + * @param from the start index + * @return the first element of the queue for which p yields true + */ + def removeFirst(p: A => Boolean, from: Int = 0): Option[A] = { + val i = indexWhere(p, from) + if (i < 0) None else Some(remove(i)) + } + + /** Returns all elements in this collection which satisfy the given predicate + * and removes those elements from this collections. + * + * @param p the predicate used for choosing elements + * @return a sequence of all elements in the queue for which + * p yields true. + */ + def removeAll(p: A => Boolean): scala.collection.immutable.Seq[A] = { + val res = scala.collection.immutable.Seq.newBuilder[A] + var i, j = 0 + while (i < size) { + if (p(this(i))) { + res += this(i) + } else { + if (i != j) { + this(j) = this(i) + } + j += 1 + } + i += 1 + } + if (i != j) takeInPlace(j) + res.result() + } + + @inline def ensureSize(hint: Int) = if (hint > length && mustGrow(hint)) resize(hint) + + def length = end_-(start) + + override def isEmpty = start == end + + override protected def klone(): ArrayDeque[A] = new ArrayDeque(array.clone(), start = start, end = end) + + override def iterableFactory: SeqFactory[ArrayDeque] = ArrayDeque + + /** + * Note: This does not actually resize the internal representation. + * See clearAndShrink if you want to also resize internally + */ + def clear(): Unit = { + while(nonEmpty) { + removeHeadAssumingNonEmpty() + } + } + + /** + * Clears this buffer and shrinks to @param size + * + * @param size + * @return + */ + def clearAndShrink(size: Int = ArrayDeque.DefaultInitialSize): this.type = { + reset(array = ArrayDeque.alloc(size), start = 0, end = 0) + this + } + + protected def ofArray(array: Array[AnyRef], end: Int): ArrayDeque[A] = + new ArrayDeque[A](array, start = 0, end) + + override def copyToArray[B >: A](dest: Array[B], destStart: Int, len: Int): Int = { + val copied = IterableOnce.elemsToCopyToArray(length, dest.length, destStart, len) + if (copied > 0) { + copySliceToArray(srcStart = 0, dest = dest, destStart = destStart, maxItems = len) + } + copied + } + + override def toArray[B >: A: ClassTag]: Array[B] = + copySliceToArray(srcStart = 0, dest = new Array[B](length), destStart = 0, maxItems = length) + + /** + * Trims the capacity of this ArrayDeque's instance to be the current size + */ + def trimToSize(): Unit = resize(length) + + // Utils for common modular arithmetic: + @inline protected def start_+(idx: Int) = (start + idx) & (array.length - 1) + @inline private[this] def start_-(idx: Int) = (start - idx) & (array.length - 1) + @inline private[this] def end_+(idx: Int) = (end + idx) & (array.length - 1) + @inline private[this] def end_-(idx: Int) = (end - idx) & (array.length - 1) + + // Note: here be overflow dragons! This is used for int overflow + // assumptions in resize(). Use caution changing. + @inline private[this] def mustGrow(len: Int) = { + len >= array.length + } + + // Assumes that 0 <= len < array.length! + @inline private[this] def shouldShrink(len: Int) = { + // To avoid allocation churn, only shrink when array is large + // and less than 2/5 filled. + array.length > ArrayDeque.StableSize && array.length - len - (len >> 1) > len + } + + // Assumes that 0 <= len < array.length! + @inline private[this] def canShrink(len: Int) = { + array.length > ArrayDeque.DefaultInitialSize && array.length - len > len + } + + @inline private[this] def _get(idx: Int): A = array(start_+(idx)).asInstanceOf[A] + + @inline private[this] def _set(idx: Int, elem: A) = array(start_+(idx)) = elem.asInstanceOf[AnyRef] + + // Assumes that 0 <= len. + private[this] def resize(len: Int) = if (mustGrow(len) || canShrink(len)) { + val n = length + val array2 = copySliceToArray(srcStart = 0, dest = ArrayDeque.alloc(len), destStart = 0, maxItems = n) + reset(array = array2, start = 0, end = n) + } + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "ArrayDeque" +} + +/** + * $factoryInfo + * @define coll array deque + * @define Coll `ArrayDeque` + */ +@SerialVersionUID(3L) +object ArrayDeque extends StrictOptimizedSeqFactory[ArrayDeque] { + + def from[B](coll: collection.IterableOnce[B]): ArrayDeque[B] = { + val s = coll.knownSize + if (s >= 0) { + val array = alloc(s) + val actual = IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + if (actual != s) throw new IllegalStateException(s"Copied $actual of $s") + new ArrayDeque[B](array, start = 0, end = s) + } else new ArrayDeque[B]() ++= coll + } + + def newBuilder[A]: Builder[A, ArrayDeque[A]] = + new GrowableBuilder[A, ArrayDeque[A]](empty) { + override def sizeHint(size: Int): Unit = { + elems.ensureSize(size) + } + } + + def empty[A]: ArrayDeque[A] = new ArrayDeque[A]() + + final val DefaultInitialSize = 16 + + /** + * We try to not repeatedly resize arrays smaller than this + */ + private[ArrayDeque] final val StableSize = 128 + + /** + * Allocates an array whose size is next power of 2 > `len` + * Largest possible len is 1<<30 - 1 + * + * @param len + * @return + */ + private[mutable] def alloc(len: Int) = { + require(len >= 0, s"Non-negative array size required") + val size = (1 << 31) >>> java.lang.Integer.numberOfLeadingZeros(len) << 1 + require(size >= 0, s"ArrayDeque too big - cannot allocate ArrayDeque of length $len") + new Array[AnyRef](Math.max(size, DefaultInitialSize)) + } +} + +trait ArrayDequeOps[A, +CC[_], +C <: AnyRef] extends StrictOptimizedSeqOps[A, CC, C] { + protected def array: Array[AnyRef] + + final override def clone(): C = klone() + + protected def klone(): C + + protected def ofArray(array: Array[AnyRef], end: Int): C + + protected def start_+(idx: Int): Int + + @inline protected final def requireBounds(idx: Int, until: Int = length): Unit = + if (idx < 0 || idx >= until) + throw CommonErrors.indexOutOfBounds(index = idx, max = until - 1) + + /** + * This is a more general version of copyToArray - this also accepts a srcStart unlike copyToArray + * This copies maxItems elements from this collections srcStart to dest's destStart + * If we reach the end of either collections before we could copy maxItems, we simply stop copying + * + * @param dest + * @param srcStart + * @param destStart + * @param maxItems + */ + def copySliceToArray(srcStart: Int, dest: Array[_], destStart: Int, maxItems: Int): dest.type = { + requireBounds(destStart, dest.length+1) + val toCopy = Math.min(maxItems, Math.min(length - srcStart, dest.length - destStart)) + if (toCopy > 0) { + requireBounds(srcStart) + val startIdx = start_+(srcStart) + val block1 = Math.min(toCopy, array.length - startIdx) + Array.copy(src = array, srcPos = startIdx, dest = dest, destPos = destStart, length = block1) + val block2 = toCopy - block1 + if (block2 > 0) Array.copy(src = array, srcPos = 0, dest = dest, destPos = destStart + block1, length = block2) + } + dest + } + + override def reverse: C = { + val n = length + val arr = ArrayDeque.alloc(n) + var i = 0 + while(i < n) { + arr(i) = this(n - i - 1).asInstanceOf[AnyRef] + i += 1 + } + ofArray(arr, n) + } + + override def slice(from: Int, until: Int): C = { + val n = length + val left = Math.max(0, Math.min(n, from)) + val right = Math.max(0, Math.min(n, until)) + val len = right - left + if (len <= 0) { + empty + } else if (len >= n) { + klone() + } else { + val array2 = copySliceToArray(srcStart = left, dest = ArrayDeque.alloc(len), destStart = 0, maxItems = len) + ofArray(array2, len) + } + } + + override def sliding(window: Int, step: Int): Iterator[C] = { + require(window > 0 && step > 0, s"window=$window and step=$step, but both must be positive") + length match { + case 0 => Iterator.empty + case n if n <= window => Iterator.single(slice(0, length)) + case n => + val lag = if (window > step) window - step else 0 + Iterator.range(start = 0, end = n - lag, step = step).map(i => slice(i, i + window)) + } + } + + override def grouped(n: Int): Iterator[C] = sliding(n, n) +} diff --git a/library/src/scala/collection/mutable/ArraySeq.scala b/library/src/scala/collection/mutable/ArraySeq.scala new file mode 100644 index 000000000000..0537092d0b13 --- /dev/null +++ b/library/src/scala/collection/mutable/ArraySeq.scala @@ -0,0 +1,353 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable +import java.util.Arrays +import scala.collection.Stepper.EfficientSplit +import scala.collection.convert.impl._ +import scala.reflect.ClassTag +import scala.util.hashing.MurmurHash3 + +/** + * A collection representing `Array[T]`. Unlike `ArrayBuffer` it is always backed by the same + * underlying `Array`, therefore it is not growable or shrinkable. + * + * @tparam T type of the elements in this wrapped array. + * + * @define Coll `ArraySeq` + * @define coll wrapped array + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(3L) +sealed abstract class ArraySeq[T] + extends AbstractSeq[T] + with IndexedSeq[T] + with IndexedSeqOps[T, ArraySeq, ArraySeq[T]] + with StrictOptimizedSeqOps[T, ArraySeq, ArraySeq[T]] + with Serializable { + + override def iterableFactory: scala.collection.SeqFactory[ArraySeq] = ArraySeq.untagged + + override protected def fromSpecific(coll: scala.collection.IterableOnce[T]): ArraySeq[T] = { + val b = ArrayBuilder.make(using elemTag).asInstanceOf[ArrayBuilder[T]] + b.sizeHint(coll, delta = 0) + b ++= coll + ArraySeq.make(b.result()) + } + override protected def newSpecificBuilder: Builder[T, ArraySeq[T]] = ArraySeq.newBuilder(using elemTag).asInstanceOf[Builder[T, ArraySeq[T]]] + override def empty: ArraySeq[T] = ArraySeq.empty(using elemTag.asInstanceOf[ClassTag[T]]) + + /** The tag of the element type. This does not have to be equal to the element type of this ArraySeq. A primitive + * ArraySeq can be backed by an array of boxed values and a reference ArraySeq can be backed by an array of a supertype + * or subtype of the element type. */ + def elemTag: ClassTag[_] + + /** Update element at given index */ + def update(@deprecatedName("idx", "2.13.0") index: Int, elem: T): Unit + + /** The underlying array. Its element type does not have to be equal to the element type of this ArraySeq. A primitive + * ArraySeq can be backed by an array of boxed values and a reference ArraySeq can be backed by an array of a supertype + * or subtype of the element type. */ + def array: Array[_] + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[T, S]): S with EfficientSplit + + override protected[this] def className = "ArraySeq" + + /** Clones this object, including the underlying Array. */ + override def clone(): ArraySeq[T] = ArraySeq.make(array.clone()).asInstanceOf[ArraySeq[T]] + + override def copyToArray[B >: T](xs: Array[B], start: Int, len: Int): Int = { + val copied = IterableOnce.elemsToCopyToArray(length, xs.length, start, len) + if(copied > 0) { + Array.copy(array, 0, xs, start, copied) + } + copied + } + + override def equals(other: Any): Boolean = other match { + case that: ArraySeq[_] if this.array.length != that.array.length => + false + case _ => + super.equals(other) + } + + override def sorted[B >: T](implicit ord: Ordering[B]): ArraySeq[T] = + ArraySeq.make(array.sorted(ord.asInstanceOf[Ordering[Any]])).asInstanceOf[ArraySeq[T]] + + override def sortInPlace[B >: T]()(implicit ord: Ordering[B]): this.type = { + if (length > 1) scala.util.Sorting.stableSort(array.asInstanceOf[Array[B]]) + this + } +} + +/** A companion object used to create instances of `ArraySeq`. + */ +@SerialVersionUID(3L) +object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self => + val untagged: SeqFactory[ArraySeq] = new ClassTagSeqFactory.AnySeqDelegate(self) + + // This is reused for all calls to empty. + private[this] val EmptyArraySeq = new ofRef[AnyRef](new Array[AnyRef](0)) + def empty[T : ClassTag]: ArraySeq[T] = EmptyArraySeq.asInstanceOf[ArraySeq[T]] + + def from[A : ClassTag](it: scala.collection.IterableOnce[A]): ArraySeq[A] = make(Array.from[A](it)) + + def newBuilder[A : ClassTag]: Builder[A, ArraySeq[A]] = ArrayBuilder.make[A].mapResult(make) + + /** + * Wrap an existing `Array` into a `ArraySeq` of the proper primitive specialization type + * without copying. + * + * Note that an array containing boxed primitives can be converted to a `ArraySeq` without + * copying. For example, `val a: Array[Any] = Array(1)` is an array of `Object` at runtime, + * containing `Integer`s. An `ArraySeq[Int]` can be obtained with a cast: + * `ArraySeq.make(a).asInstanceOf[ArraySeq[Int]]`. The values are still + * boxed, the resulting instance is an [[ArraySeq.ofRef]]. Writing + * `ArraySeq.make(a.asInstanceOf[Array[Int]])` does not work, it throws a `ClassCastException` + * at runtime. + */ + def make[T](x: Array[T]): ArraySeq[T] = ((x: @unchecked) match { + case null => null + case x: Array[AnyRef] => new ofRef[AnyRef](x) + case x: Array[Int] => new ofInt(x) + case x: Array[Double] => new ofDouble(x) + case x: Array[Long] => new ofLong(x) + case x: Array[Float] => new ofFloat(x) + case x: Array[Char] => new ofChar(x) + case x: Array[Byte] => new ofByte(x) + case x: Array[Short] => new ofShort(x) + case x: Array[Boolean] => new ofBoolean(x) + case x: Array[Unit] => new ofUnit(x) + }).asInstanceOf[ArraySeq[T]] + + @SerialVersionUID(3L) + final class ofRef[T <: AnyRef](val array: Array[T]) extends ArraySeq[T] { + def elemTag: ClassTag[T] = ClassTag[T](array.getClass.getComponentType) + def length: Int = array.length + def apply(index: Int): T = array(index) + def update(index: Int, elem: T): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofRef[_] => + Array.equals( + this.array.asInstanceOf[Array[AnyRef]], + that.array.asInstanceOf[Array[AnyRef]]) + case _ => super.equals(that) + } + override def iterator: Iterator[T] = new ArrayOps.ArrayIterator[T](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[T, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + new ObjectArrayStepper(array, 0, array.length) + else shape.parUnbox(new ObjectArrayStepper(array, 0, array.length).asInstanceOf[AnyStepper[T] with EfficientSplit]) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofByte(val array: Array[Byte]) extends ArraySeq[Byte] { + // Type erases to `ManifestFactory.ByteManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Byte.type = ClassTag.Byte + def length: Int = array.length + def apply(index: Int): Byte = array(index) + def update(index: Int, elem: Byte): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofByte => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Byte] = new ArrayOps.ArrayIterator[Byte](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Byte, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new WidenedByteArrayStepper(array, 0, array.length)) + else new WidenedByteArrayStepper(array, 0, array.length) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofShort(val array: Array[Short]) extends ArraySeq[Short] { + // Type erases to `ManifestFactory.ShortManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Short.type = ClassTag.Short + def length: Int = array.length + def apply(index: Int): Short = array(index) + def update(index: Int, elem: Short): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofShort => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Short] = new ArrayOps.ArrayIterator[Short](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Short, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new WidenedShortArrayStepper(array, 0, array.length)) + else new WidenedShortArrayStepper(array, 0, array.length) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofChar(val array: Array[Char]) extends ArraySeq[Char] { + // Type erases to `ManifestFactory.CharManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Char.type = ClassTag.Char + def length: Int = array.length + def apply(index: Int): Char = array(index) + def update(index: Int, elem: Char): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofChar => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Char] = new ArrayOps.ArrayIterator[Char](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Char, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new WidenedCharArrayStepper(array, 0, array.length)) + else new WidenedCharArrayStepper(array, 0, array.length) + ).asInstanceOf[S with EfficientSplit] + + override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = { + val jsb = sb.underlying + if (start.length != 0) jsb.append(start) + val len = array.length + if (len != 0) { + if (sep.isEmpty) jsb.append(array) + else { + jsb.ensureCapacity(jsb.length + len + end.length + (len - 1) * sep.length) + jsb.append(array(0)) + var i = 1 + while (i < len) { + jsb.append(sep) + jsb.append(array(i)) + i += 1 + } + } + } + if (end.length != 0) jsb.append(end) + sb + } + } + + @SerialVersionUID(3L) + final class ofInt(val array: Array[Int]) extends ArraySeq[Int] { + // Type erases to `ManifestFactory.IntManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Int.type = ClassTag.Int + def length: Int = array.length + def apply(index: Int): Int = array(index) + def update(index: Int, elem: Int): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofInt => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Int] = new ArrayOps.ArrayIterator[Int](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Int, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParIntStepper(new IntArrayStepper(array, 0, array.length)) + else new IntArrayStepper(array, 0, array.length) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofLong(val array: Array[Long]) extends ArraySeq[Long] { + // Type erases to `ManifestFactory.LongManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Long.type = ClassTag.Long + def length: Int = array.length + def apply(index: Int): Long = array(index) + def update(index: Int, elem: Long): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofLong => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Long] = new ArrayOps.ArrayIterator[Long](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Long, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParLongStepper(new LongArrayStepper(array, 0, array.length)) + else new LongArrayStepper(array, 0, array.length) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofFloat(val array: Array[Float]) extends ArraySeq[Float] { + // Type erases to `ManifestFactory.FloatManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Float.type = ClassTag.Float + def length: Int = array.length + def apply(index: Int): Float = array(index) + def update(index: Int, elem: Float): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofFloat => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Float] = new ArrayOps.ArrayIterator[Float](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Float, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParDoubleStepper(new WidenedFloatArrayStepper(array, 0, array.length)) + else new WidenedFloatArrayStepper(array, 0, array.length) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofDouble(val array: Array[Double]) extends ArraySeq[Double] { + // Type erases to `ManifestFactory.DoubleManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Double.type = ClassTag.Double + def length: Int = array.length + def apply(index: Int): Double = array(index) + def update(index: Int, elem: Double): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofDouble => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Double] = new ArrayOps.ArrayIterator[Double](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Double, S]): S with EfficientSplit = ( + if(shape.shape == StepperShape.ReferenceShape) + AnyStepper.ofParDoubleStepper(new DoubleArrayStepper(array, 0, array.length)) + else new DoubleArrayStepper(array, 0, array.length) + ).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofBoolean(val array: Array[Boolean]) extends ArraySeq[Boolean] { + // Type erases to `ManifestFactory.BooleanManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Boolean.type = ClassTag.Boolean + def length: Int = array.length + def apply(index: Int): Boolean = array(index) + def update(index: Int, elem: Boolean): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofBoolean => Arrays.equals(array, that.array) + case _ => super.equals(that) + } + override def iterator: Iterator[Boolean] = new ArrayOps.ArrayIterator[Boolean](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Boolean, S]): S with EfficientSplit = + new BoxedBooleanArrayStepper(array, 0, array.length).asInstanceOf[S with EfficientSplit] + } + + @SerialVersionUID(3L) + final class ofUnit(val array: Array[Unit]) extends ArraySeq[Unit] { + // Type erases to `ManifestFactory.UnitManifest`, but can't annotate that because it's not accessible + def elemTag: ClassTag.Unit.type = ClassTag.Unit + def length: Int = array.length + def apply(index: Int): Unit = array(index) + def update(index: Int, elem: Unit): Unit = { array(index) = elem } + override def hashCode = MurmurHash3.arraySeqHash(array) + override def equals(that: Any) = that match { + case that: ofUnit => array.length == that.array.length + case _ => super.equals(that) + } + override def iterator: Iterator[Unit] = new ArrayOps.ArrayIterator[Unit](array) + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[Unit, S]): S with EfficientSplit = + new ObjectArrayStepper[AnyRef](array.asInstanceOf[Array[AnyRef]], 0, array.length).asInstanceOf[S with EfficientSplit] + } +} diff --git a/library/src/scala/collection/mutable/BitSet.scala b/library/src/scala/collection/mutable/BitSet.scala new file mode 100644 index 000000000000..ba77d7161a0b --- /dev/null +++ b/library/src/scala/collection/mutable/BitSet.scala @@ -0,0 +1,392 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import scala.collection.immutable.Range +import BitSetOps.{LogWL, MaxSize} +import scala.annotation.implicitNotFound + +/** + * A class for mutable bitsets. + * + * $bitsetinfo + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#mutable-bitsets "Scala's Collection Library overview"]] + * section on `Mutable Bitsets` for more information. + * + * @define Coll `BitSet` + * @define coll bitset + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +class BitSet(protected[collection] final var elems: Array[Long]) + extends AbstractSet[Int] + with SortedSet[Int] + with SortedSetOps[Int, SortedSet, BitSet] + with StrictOptimizedIterableOps[Int, Set, BitSet] + with StrictOptimizedSortedSetOps[Int, SortedSet, BitSet] + with collection.BitSet + with collection.BitSetOps[BitSet] + with Serializable { + + def this(initSize: Int) = this(new Array[Long](math.max((initSize + 63) >> 6, 1))) + + def this() = this(0) + + override protected def fromSpecific(coll: IterableOnce[Int]): BitSet = bitSetFactory.fromSpecific(coll) + override protected def newSpecificBuilder: Builder[Int, BitSet] = bitSetFactory.newBuilder + override def empty: BitSet = bitSetFactory.empty + + def bitSetFactory: BitSet.type = BitSet + + override def unsorted: Set[Int] = this + + protected[collection] final def nwords: Int = elems.length + + protected[collection] final def word(idx: Int): Long = + if (idx < nwords) elems(idx) else 0L + + protected[collection] def fromBitMaskNoCopy(elems: Array[Long]): BitSet = + if (elems.length == 0) empty + else new BitSet(elems) + + def addOne(elem: Int): this.type = { + require(elem >= 0) + if (!contains(elem)) { + val idx = elem >> LogWL + updateWord(idx, word(idx) | (1L << elem)) + } + this + } + + def subtractOne(elem: Int): this.type = { + require(elem >= 0) + if (contains(elem)) { + val idx = elem >> LogWL + updateWord(idx, word(idx) & ~(1L << elem)) + } + this + } + + def clear(): Unit = { + elems = new Array[Long](elems.length) + } + + protected final def updateWord(idx: Int, w: Long): Unit = { + ensureCapacity(idx) + elems(idx) = w + } + + protected final def ensureCapacity(idx: Int): Unit = { + require(idx < MaxSize) + if (idx >= nwords) { + var newlen = nwords + while (idx >= newlen) newlen = math.min(newlen * 2, MaxSize) + val elems1 = new Array[Long](newlen) + Array.copy(elems, 0, elems1, 0, nwords) + elems = elems1 + } + } + + def unconstrained: collection.Set[Int] = this + + /** Updates this bitset to the union with another bitset by performing a bitwise "or". + * + * @param other the bitset to form the union with. + * @return the bitset itself. + */ + def |= (other: collection.BitSet): this.type = { + ensureCapacity(other.nwords - 1) + var i = 0 + val othernwords = other.nwords + while (i < othernwords) { + elems(i) = elems(i) | other.word(i) + i += 1 + } + this + } + /** Updates this bitset to the intersection with another bitset by performing a bitwise "and". + * + * @param other the bitset to form the intersection with. + * @return the bitset itself. + */ + def &= (other: collection.BitSet): this.type = { + // Different from other operations: no need to ensure capacity because + // anything beyond the capacity is 0. Since we use other.word which is 0 + // off the end, we also don't need to make sure we stay in bounds there. + var i = 0 + val thisnwords = nwords + while (i < thisnwords) { + elems(i) = elems(i) & other.word(i) + i += 1 + } + this + } + /** Updates this bitset to the symmetric difference with another bitset by performing a bitwise "xor". + * + * @param other the bitset to form the symmetric difference with. + * @return the bitset itself. + */ + def ^= (other: collection.BitSet): this.type = { + ensureCapacity(other.nwords - 1) + var i = 0 + val othernwords = other.nwords + while (i < othernwords) { + + elems(i) = elems(i) ^ other.word(i) + i += 1 + } + this + } + /** Updates this bitset to the difference with another bitset by performing a bitwise "and-not". + * + * @param other the bitset to form the difference with. + * @return the bitset itself. + */ + def &~= (other: collection.BitSet): this.type = { + var i = 0 + val max = Math.min(nwords, other.nwords) + while (i < max) { + elems(i) = elems(i) & ~other.word(i) + i += 1 + } + this + } + + override def clone(): BitSet = new BitSet(java.util.Arrays.copyOf(elems, elems.length)) + + def toImmutable: immutable.BitSet = immutable.BitSet.fromBitMask(elems) + + override def map(f: Int => Int): BitSet = strictOptimizedMap(newSpecificBuilder, f) + override def map[B](f: Int => B)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + super[StrictOptimizedSortedSetOps].map(f) + + override def flatMap(f: Int => IterableOnce[Int]): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f) + override def flatMap[B](f: Int => IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + super[StrictOptimizedSortedSetOps].flatMap(f) + + override def collect(pf: PartialFunction[Int, Int]): BitSet = strictOptimizedCollect(newSpecificBuilder, pf) + override def collect[B](pf: scala.PartialFunction[Int, B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + super[StrictOptimizedSortedSetOps].collect(pf) + + // necessary for disambiguation + override def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] = + super.zip(that) + + override def addAll(xs: IterableOnce[Int]): this.type = xs match { + case bs: collection.BitSet => + this |= bs + case range: Range => + if (range.nonEmpty) { + val start = range.min + if (start >= 0) { + val end = range.max + val endIdx = end >> LogWL + ensureCapacity(endIdx) + + if (range.step == 1 || range.step == -1) { + val startIdx = start >> LogWL + val wordStart = startIdx * BitSetOps.WordLength + val wordMask = -1L << (start - wordStart) + + if (endIdx > startIdx) { + elems(startIdx) |= wordMask + java.util.Arrays.fill(elems, startIdx + 1, endIdx, -1L) + elems(endIdx) |= -1L >>> (BitSetOps.WordLength - (end - endIdx * BitSetOps.WordLength) - 1) + } else elems(endIdx) |= (wordMask & (-1L >>> (BitSetOps.WordLength - (end - wordStart) - 1))) + } else super.addAll(range) + } else super.addAll(range) + } + this + + case sorted: collection.SortedSet[Int] => + // if `sorted` is using the regular Int ordering, ensure capacity for the largest + // element up front to avoid multiple resizing allocations + if (sorted.nonEmpty) { + val ord = sorted.ordering + if (ord eq Ordering.Int) { + ensureCapacity(sorted.lastKey >> LogWL) + } else if (ord eq Ordering.Int.reverse) { + ensureCapacity(sorted.firstKey >> LogWL) + } + val iter = sorted.iterator + while (iter.hasNext) { + addOne(iter.next()) + } + } + + this + + case other => + super.addAll(other) + } + + override def subsetOf(that: collection.Set[Int]): Boolean = that match { + case bs: collection.BitSet => + val thisnwords = this.nwords + val bsnwords = bs.nwords + val minWords = Math.min(thisnwords, bsnwords) + + // if any bits are set to `1` in words out of range of `bs`, then this is not a subset. Start there + var i = bsnwords + while (i < thisnwords) { + if (word(i) != 0L) return false + i += 1 + } + + // the higher range of `this` is all `0`s, fall back to lower range + var j = 0 + while (j < minWords) { + if ((word(j) & ~bs.word(j)) != 0L) return false + j += 1 + } + + true + case other => + super.subsetOf(other) + } + + override def subtractAll(xs: IterableOnce[Int]): this.type = xs match { + case bs: collection.BitSet => this &~= bs + case other => super.subtractAll(other) + } + + protected[this] def writeReplace(): AnyRef = new BitSet.SerializationProxy(this) + + override def diff(that: collection.Set[Int]): BitSet = that match { + case bs: collection.BitSet => + /* + * Algorithm: + * + * We iterate, word-by-word, backwards from the shortest of the two bitsets (this, or bs) i.e. the one with + * the fewer words. + * + * Array Shrinking: + * If `this` is not longer than `bs`, then since we must iterate through the full array of words, + * we can track the new highest index word which is non-zero, at little additional cost. At the end, the new + * Array[Long] allocated for the returned BitSet will only be of size `maxNonZeroIndex + 1` + */ + + val bsnwords = bs.nwords + val thisnwords = nwords + if (bsnwords >= thisnwords) { + // here, we may have opportunity to shrink the size of the array + // so, track the highest index which is non-zero. That ( + 1 ) will be our new array length + var i = thisnwords - 1 + var currentWord = 0L + + while (i >= 0 && currentWord == 0L) { + val oldWord = word(i) + currentWord = oldWord & ~bs.word(i) + i -= 1 + } + + if (i < 0) { + fromBitMaskNoCopy(Array(currentWord)) + } else { + val minimumNonZeroIndex: Int = i + 1 + val newArray = elems.take(minimumNonZeroIndex + 1) + newArray(i + 1) = currentWord + while (i >= 0) { + newArray(i) = word(i) & ~bs.word(i) + i -= 1 + } + fromBitMaskNoCopy(newArray) + } + } else { + // here, there is no opportunity to shrink the array size, no use in tracking highest non-zero index + val newElems = elems.clone() + var i = bsnwords - 1 + while (i >= 0) { + newElems(i) = word(i) & ~bs.word(i) + i -= 1 + } + fromBitMaskNoCopy(newElems) + } + case _ => super.diff(that) + } + + override def filterImpl(pred: Int => Boolean, isFlipped: Boolean): BitSet = { + // We filter the BitSet from highest to lowest, so we can determine exactly the highest non-zero word + // index which lets us avoid: + // * over-allocating -- the resulting array will be exactly the right size + // * multiple resizing allocations -- the array is allocated one time, not log(n) times. + var i = nwords - 1 + var newArray: Array[Long] = null + while (i >= 0) { + val w = BitSetOps.computeWordForFilter(pred, isFlipped, word(i), i) + if (w != 0L) { + if (newArray eq null) { + newArray = new Array(i + 1) + } + newArray(i) = w + } + i -= 1 + } + if (newArray eq null) { + empty + } else { + fromBitMaskNoCopy(newArray) + } + } + + override def filterInPlace(p: Int => Boolean): this.type = { + val thisnwords = nwords + var i = 0 + while (i < thisnwords) { + elems(i) = BitSetOps.computeWordForFilter(p, isFlipped = false, elems(i), i) + i += 1 + } + this + } + + override def toBitMask: Array[Long] = elems.clone() +} + +@SerialVersionUID(3L) +object BitSet extends SpecificIterableFactory[Int, BitSet] { + + def fromSpecific(it: scala.collection.IterableOnce[Int]): BitSet = Growable.from(empty, it) + + def empty: BitSet = new BitSet() + + def newBuilder: Builder[Int, BitSet] = new GrowableBuilder(empty) + + /** A bitset containing all the bits in an array */ + def fromBitMask(elems: Array[Long]): BitSet = { + val len = elems.length + if (len == 0) empty + else { + val a = java.util.Arrays.copyOf(elems, len) + new BitSet(a) + } + } + + /** A bitset containing all the bits in an array, wrapping the existing + * array without copying. + */ + def fromBitMaskNoCopy(elems: Array[Long]): BitSet = { + val len = elems.length + if (len == 0) empty + else new BitSet(elems) + } + + @SerialVersionUID(3L) + private final class SerializationProxy(coll: BitSet) extends scala.collection.BitSet.SerializationProxy(coll) { + protected[this] def readResolve(): Any = BitSet.fromBitMaskNoCopy(elems) + } +} diff --git a/library/src/scala/collection/mutable/Buffer.scala b/library/src/scala/collection/mutable/Buffer.scala new file mode 100644 index 000000000000..1a0151273894 --- /dev/null +++ b/library/src/scala/collection/mutable/Buffer.scala @@ -0,0 +1,314 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.annotation.nowarn + + +/** A `Buffer` is a growable and shrinkable `Seq`. */ +trait Buffer[A] + extends Seq[A] + with SeqOps[A, Buffer, Buffer[A]] + with Growable[A] + with Shrinkable[A] + with IterableFactoryDefaults[A, Buffer] { + + override def iterableFactory: SeqFactory[Buffer] = Buffer + + override def knownSize: Int = super[Seq].knownSize + + //TODO Prepend is a logical choice for a readable name of `+=:` but it conflicts with the renaming of `append` to `add` + /** Prepends a single element at the front of this $coll. + * + * @param elem the element to $add. + * @return the $coll itself + */ + def prepend(elem: A): this.type + + /** Appends the given elements to this buffer. + * + * @param elem the element to append. + * @return this $coll + */ + @`inline` final def append(elem: A): this.type = addOne(elem) + + @deprecated("Use appendAll instead", "2.13.0") + @`inline` final def append(elems: A*): this.type = addAll(elems) + + /** Appends the elements contained in a iterable object to this buffer. + * @param elems the iterable object containing the elements to append. + * @return this $coll + */ + @`inline` final def appendAll(@deprecatedName("xs") elems: IterableOnce[A]): this.type = addAll(elems) + + /** Alias for `prepend` */ + @`inline` final def +=: (elem: A): this.type = prepend(elem) + + /** Prepends the elements contained in a iterable object to this buffer. + * @param elems the iterable object containing the elements to append. + * @return this $coll + */ + def prependAll(elems: IterableOnce[A]): this.type = { insertAll(0, elems); this } + + @deprecated("Use prependAll instead", "2.13.0") + @`inline` final def prepend(elems: A*): this.type = prependAll(elems) + + /** Alias for `prependAll` */ + @inline final def ++=:(elems: IterableOnce[A]): this.type = prependAll(elems) + + /** Inserts a new element at a given index into this buffer. + * + * @param idx the index where the new elements is inserted. + * @param elem the element to insert. + * @throws IndexOutOfBoundsException if the index `idx` is not in the valid range + * `0 <= idx <= length`. + */ + @throws[IndexOutOfBoundsException] + def insert(idx: Int, elem: A): Unit + + /** Inserts new elements at the index `idx`. Opposed to method + * `update`, this method will not replace an element with a new + * one. Instead, it will insert a new element at index `idx`. + * + * @param idx the index where a new element will be inserted. + * @param elems the iterable object providing all elements to insert. + * @throws IndexOutOfBoundsException if `idx` is out of bounds. + */ + @throws[IndexOutOfBoundsException] + def insertAll(idx: Int, elems: IterableOnce[A]): Unit + + /** Removes the element at a given index position. + * + * @param idx the index which refers to the element to delete. + * @return the element that was formerly at index `idx`. + */ + @throws[IndexOutOfBoundsException] + def remove(idx: Int): A + + /** Removes the element on a given index position. It takes time linear in + * the buffer size. + * + * @param idx the index which refers to the first element to remove. + * @param count the number of elements to remove. + * @throws IndexOutOfBoundsException if the index `idx` is not in the valid range + * `0 <= idx <= length - count` (with `count > 0`). + * @throws IllegalArgumentException if `count < 0`. + */ + @throws[IndexOutOfBoundsException] + @throws[IllegalArgumentException] + def remove(idx: Int, count: Int): Unit + + /** Removes a single element from this buffer, at its first occurrence. + * If the buffer does not contain that element, it is unchanged. + * + * @param x the element to remove. + * @return the buffer itself + */ + def subtractOne (x: A): this.type = { + val i = indexOf(x) + if (i != -1) remove(i) + this + } + + /** Removes the first ''n'' elements of this buffer. + * + * @param n the number of elements to remove from the beginning + * of this buffer. + */ + @deprecated("use dropInPlace instead", since = "2.13.4") + def trimStart(n: Int): Unit = dropInPlace(n) + + /** Removes the last ''n'' elements of this buffer. + * + * @param n the number of elements to remove from the end + * of this buffer. + */ + @deprecated("use dropRightInPlace instead", since = "2.13.4") + def trimEnd(n: Int): Unit = dropRightInPlace(n) + + /** Replaces a slice of elements in this $coll by another sequence of elements. + * + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original $coll appends the patch to the end. + * If the `replaced` count would exceed the available elements, the difference in excess is ignored. + * + * @param from the index of the first replaced element + * @param patch the replacement sequence + * @param replaced the number of elements to drop in the original $coll + * @return this $coll + */ + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type + + // +=, ++=, clear inherited from Growable + // Per remark of @ichoran, we should preferably not have these: + // + // def +=:(elem: A): this.type = { insert(0, elem); this } + // def +=:(elem1: A, elem2: A, elems: A*): this.type = elem1 +=: elem2 +=: elems ++=: this + // def ++=:(elems: IterableOnce[A]): this.type = { insertAll(0, elems); this } + + /** Removes the first `n` elements from this $coll. + * + * @param n the number of elements to remove + * @return this $coll + * + */ + def dropInPlace(n: Int): this.type = { remove(0, normalized(n)); this } + + /** Removes the last `n` elements from this $coll. + * + * @param n the number of elements to remove + * @return this $coll + * + */ + def dropRightInPlace(n: Int): this.type = { + val norm = normalized(n) + remove(length - norm, norm) + this + } + + /** Retains the first `n` elements from this $coll and removes the rest. + * + * @param n the number of elements to retain + * @return this $coll + * + */ + def takeInPlace(n: Int): this.type = { + val norm = normalized(n) + remove(norm, length - norm) + this + } + + /** Retains the last `n` elements from this $coll and removes the rest. + * + * @param n the number of elements to retain + * @return this $coll + * + */ + def takeRightInPlace(n: Int): this.type = { remove(0, length - normalized(n)); this } + + /** Retains the specified slice from this $coll and removes the rest. + * + * @param start the lowest index to include + * @param end the lowest index to exclude + * @return this $coll + * + */ + def sliceInPlace(start: Int, end: Int): this.type = takeInPlace(end).dropInPlace(start) + + private def normalized(n: Int): Int = math.min(math.max(n, 0), length) + + /** Drops the longest prefix of elements that satisfy a predicate. + * + * @param p The predicate used to test elements. + * @return this $coll + * @see [[dropWhile]] + */ + def dropWhileInPlace(p: A => Boolean): this.type = { + val idx = indexWhere(!p(_)) + if (idx < 0) { clear(); this } else dropInPlace(idx) + } + + /** Retains the longest prefix of elements that satisfy a predicate. + * + * @param p The predicate used to test elements. + * @return this $coll + * @see [[takeWhile]] + */ + def takeWhileInPlace(p: A => Boolean): this.type = { + val idx = indexWhere(!p(_)) + if (idx < 0) this else takeInPlace(idx) + } + + /** Append the given element to this $coll until a target length is reached. + * + * @param len the target length + * @param elem the padding value + * @return this $coll + */ + def padToInPlace(len: Int, elem: A): this.type = { + while (length < len) +=(elem) + this + } + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "Buffer" +} + +trait IndexedBuffer[A] extends IndexedSeq[A] + with IndexedSeqOps[A, IndexedBuffer, IndexedBuffer[A]] + with Buffer[A] + with IterableFactoryDefaults[A, IndexedBuffer] { + + override def iterableFactory: SeqFactory[IndexedBuffer] = IndexedBuffer + + /** Replace the contents of this $coll with the flatmapped result. + * + * @param f the mapping function + * @return this $coll + */ + def flatMapInPlace(f: A => IterableOnce[A]): this.type = { + // There's scope for a better implementation which copies elements in place. + var i = 0 + val s = size + val newElems = new Array[IterableOnce[A]](s) + while (i < s) { newElems(i) = f(this(i)); i += 1 } + clear() + i = 0 + while (i < s) { ++=(newElems(i)); i += 1 } + this + } + + /** Replace the contents of this $coll with the filtered result. + * + * @param f the filtering function + * @return this $coll + */ + def filterInPlace(p: A => Boolean): this.type = { + var i, j = 0 + while (i < size) { + if (p(apply(i))) { + if (i != j) { + this(j) = this(i) + } + j += 1 + } + i += 1 + } + + if (i == j) this else takeInPlace(j) + } + + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type = { + val replaced0 = math.min(math.max(replaced, 0), length) + val i = math.min(math.max(from, 0), length) + var j = 0 + val iter = patch.iterator + while (iter.hasNext && j < replaced0 && i + j < length) { + update(i + j, iter.next()) + j += 1 + } + if (iter.hasNext) insertAll(i + j, iter) + else if (j < replaced0) remove(i + j, math.min(replaced0 - j, length - i - j)) + this + } +} + +@SerialVersionUID(3L) +object Buffer extends SeqFactory.Delegate[Buffer](ArrayBuffer) + +@SerialVersionUID(3L) +object IndexedBuffer extends SeqFactory.Delegate[IndexedBuffer](ArrayBuffer) + +/** Explicit instantiation of the `Buffer` trait to reduce class file size in subclasses. */ +abstract class AbstractBuffer[A] extends AbstractSeq[A] with Buffer[A] diff --git a/library/src/scala/collection/mutable/Builder.scala b/library/src/scala/collection/mutable/Builder.scala new file mode 100644 index 000000000000..e59fc8639104 --- /dev/null +++ b/library/src/scala/collection/mutable/Builder.scala @@ -0,0 +1,103 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + +/** Base trait for collection builders. + * + * After calling `result()` the behavior of a Builder (which is not also a [[scala.collection.mutable.ReusableBuilder]]) + * is undefined. No further methods should be called. It is common for mutable collections to be their own non-reusable + * Builder, in which case `result()` simply returns `this`. + * + * @see [[scala.collection.mutable.ReusableBuilder]] for Builders which can be reused after calling `result()` + */ +trait Builder[-A, +To] extends Growable[A] { self => + + /** Clears the contents of this builder. + * After execution of this method the builder will contain no elements. + */ + def clear(): Unit + + /** Result collection consisting of all elements appended so far. */ + def result(): To + + /** Gives a hint how many elements are expected to be added in total + * by the time `result` is called. + * + * Some builder classes will optimize their representation based on the hint. + * However, builder implementations are required to work correctly even if the hint is + * wrong, e.g., a different number of elements is added, or the hint is out of range. + * + * The default implementation simply ignores the hint. + * + * @param size the hint how many elements will be added. + */ + def sizeHint(size: Int): Unit = () + + /** Gives a hint that the `result` of this builder is expected + * to have the same size as the given collection, plus some delta. + * + * This method provides a hint only if the collection has a known size, + * as specified by the following pseudocode: + * + * {{{ + * if (coll.knownSize != -1) + * if (coll.knownSize + delta <= 0) sizeHint(0) + * else sizeHint(coll.knownSize + delta) + * }}} + * + * If the delta is negative and the result size is known to be negative, + * then the size hint is issued at zero. + * + * Some builder classes will optimize their representation based on the hint. + * However, builder implementations are required to work correctly even if the hint is + * wrong, i.e., if a different number of elements is added. + * + * @param coll the collection which serves as a hint for the result's size. + * @param delta a correction to add to the `coll.size` to produce the size hint (zero if omitted). + */ + final def sizeHint(coll: scala.collection.IterableOnce[_], delta: Int = 0): Unit = + coll.knownSize match { + case -1 => + case sz => sizeHint(0 max sz + delta) + } + + /** Gives a hint how many elements are expected to be added + * when the next `result` is called, together with an upper bound + * given by the size of some other collection. Some builder classes + * will optimize their representation based on the hint. However, + * builder implementations are still required to work correctly even if the hint is + * wrong, i.e. a different number of elements is added. + * + * @param size the hint how many elements will be added. + * @param boundingColl the bounding collection. If it is + * an IndexedSeqLike, then sizes larger + * than collection's size are reduced. + */ + // should probably be `boundingColl: IterableOnce[_]`, but binary compatibility + final def sizeHintBounded(size: Int, boundingColl: scala.collection.Iterable[_]): Unit = { + val s = boundingColl.knownSize + if (s != -1) { + sizeHint(scala.math.min(s, size)) + } + } + + /** A builder resulting from this builder by mapping the result using `f`. */ + def mapResult[NewTo](f: To => NewTo): Builder[A, NewTo] = new Builder[A, NewTo] { + def addOne(x: A): this.type = { self += x; this } + def clear(): Unit = self.clear() + override def addAll(xs: IterableOnce[A]): this.type = { self ++= xs; this } + override def sizeHint(size: Int): Unit = self.sizeHint(size) + def result(): NewTo = f(self.result()) + override def knownSize: Int = self.knownSize + } +} diff --git a/library/src/scala/collection/mutable/CheckedIndexedSeqView.scala b/library/src/scala/collection/mutable/CheckedIndexedSeqView.scala new file mode 100644 index 000000000000..6953dd0ed660 --- /dev/null +++ b/library/src/scala/collection/mutable/CheckedIndexedSeqView.scala @@ -0,0 +1,117 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +private[mutable] trait CheckedIndexedSeqView[+A] extends IndexedSeqView[A] { + protected val mutationCount: () => Int + + override def iterator: Iterator[A] = new CheckedIndexedSeqView.CheckedIterator(this, mutationCount()) + override def reverseIterator: Iterator[A] = new CheckedIndexedSeqView.CheckedReverseIterator(this, mutationCount()) + + override def appended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Appended(this, elem)(mutationCount) + override def prepended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Prepended(elem, this)(mutationCount) + override def take(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Take(this, n)(mutationCount) + override def takeRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.TakeRight(this, n)(mutationCount) + override def drop(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Drop(this, n)(mutationCount) + override def dropRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.DropRight(this, n)(mutationCount) + override def map[B](f: A => B): IndexedSeqView[B] = new CheckedIndexedSeqView.Map(this, f)(mutationCount) + override def reverse: IndexedSeqView[A] = new CheckedIndexedSeqView.Reverse(this)(mutationCount) + override def slice(from: Int, until: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Slice(this, from, until)(mutationCount) + override def tapEach[U](f: A => U): IndexedSeqView[A] = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount) + + override def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(prefix, this)(mutationCount) +} + +private[mutable] object CheckedIndexedSeqView { + import IndexedSeqView.SomeIndexedSeqOps + + @SerialVersionUID(3L) + private[mutable] class CheckedIterator[A](self: IndexedSeqView[A], mutationCount: => Int) + extends IndexedSeqView.IndexedSeqViewIterator[A](self) { + private[this] val expectedCount = mutationCount + override def hasNext: Boolean = { + MutationTracker.checkMutationsForIteration(expectedCount, mutationCount) + super.hasNext + } + } + + @SerialVersionUID(3L) + private[mutable] class CheckedReverseIterator[A](self: IndexedSeqView[A], mutationCount: => Int) + extends IndexedSeqView.IndexedSeqViewReverseIterator[A](self) { + private[this] val expectedCount = mutationCount + override def hasNext: Boolean = { + MutationTracker.checkMutationsForIteration(expectedCount, mutationCount) + super.hasNext + } + } + + @SerialVersionUID(3L) + class Id[+A](underlying: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + extends IndexedSeqView.Id(underlying) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class Appended[+A](underlying: SomeIndexedSeqOps[A], elem: A)(protected val mutationCount: () => Int) + extends IndexedSeqView.Appended(underlying, elem) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + extends IndexedSeqView.Prepended(elem, underlying) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class Concat[A](prefix: SomeIndexedSeqOps[A], suffix: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + extends IndexedSeqView.Concat[A](prefix, suffix) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class Take[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + extends IndexedSeqView.Take(underlying, n) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class TakeRight[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + extends IndexedSeqView.TakeRight(underlying, n) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class Drop[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + extends IndexedSeqView.Drop[A](underlying, n) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class DropRight[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + extends IndexedSeqView.DropRight[A](underlying, n) with CheckedIndexedSeqView[A] + + @SerialVersionUID(3L) + class Map[A, B](underlying: SomeIndexedSeqOps[A], f: A => B)(protected val mutationCount: () => Int) + extends IndexedSeqView.Map(underlying, f) with CheckedIndexedSeqView[B] + + @SerialVersionUID(3L) + class Reverse[A](underlying: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + extends IndexedSeqView.Reverse[A](underlying) with CheckedIndexedSeqView[A] { + override def reverse: IndexedSeqView[A] = underlying match { + case x: IndexedSeqView[A] => x + case _ => super.reverse + } + } + + @SerialVersionUID(3L) + class Slice[A](underlying: SomeIndexedSeqOps[A], from: Int, until: Int)(protected val mutationCount: () => Int) + extends AbstractIndexedSeqView[A] with CheckedIndexedSeqView[A] { + protected val lo = from max 0 + protected val hi = (until max 0) min underlying.length + protected val len = (hi - lo) max 0 + @throws[IndexOutOfBoundsException] + def apply(i: Int): A = underlying(lo + i) + def length: Int = len + } +} diff --git a/library/src/scala/collection/mutable/Cloneable.scala b/library/src/scala/collection/mutable/Cloneable.scala new file mode 100644 index 000000000000..5c11faea155e --- /dev/null +++ b/library/src/scala/collection/mutable/Cloneable.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + + +/** A trait for cloneable collections. + * + * @tparam C Type of the collection, covariant and with reference types as upperbound. + */ +trait Cloneable[+C <: AnyRef] extends scala.Cloneable { + override def clone(): C = super.clone().asInstanceOf[C] +} diff --git a/library/src/scala/collection/mutable/CollisionProofHashMap.scala b/library/src/scala/collection/mutable/CollisionProofHashMap.scala new file mode 100644 index 000000000000..f56e679df2d2 --- /dev/null +++ b/library/src/scala/collection/mutable/CollisionProofHashMap.scala @@ -0,0 +1,887 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.{unchecked => uc} +import scala.annotation.{implicitNotFound, tailrec, unused} +import scala.annotation.unchecked.uncheckedVariance +import scala.collection.generic.DefaultSerializationProxy +import scala.runtime.Statics + +/** This class implements mutable maps using a hashtable with red-black trees in the buckets for good + * worst-case performance on hash collisions. An `Ordering` is required for the element type. Equality + * as determined by the `Ordering` has to be consistent with `equals` and `hashCode`. Universal equality + * of numeric types is not supported (similar to `AnyRefMap`). + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * section on `Hash Tables` for more information. + * + * @define Coll `mutable.CollisionProofHashMap` + * @define coll mutable collision-proof hash map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double)(implicit ordering: Ordering[K]) + extends AbstractMap[K, V] + with MapOps[K, V, Map, CollisionProofHashMap[K, V]] //-- + with StrictOptimizedIterableOps[(K, V), Iterable, CollisionProofHashMap[K, V]] + with StrictOptimizedMapOps[K, V, Map, CollisionProofHashMap[K, V]] { //-- + + private[this] final def sortedMapFactory: SortedMapFactory[CollisionProofHashMap] = CollisionProofHashMap + + def this()(implicit ordering: Ordering[K]) = this(CollisionProofHashMap.defaultInitialCapacity, CollisionProofHashMap.defaultLoadFactor)(ordering) + + import CollisionProofHashMap.Node + private[this] type RBNode = CollisionProofHashMap.RBNode[K, V] + private[this] type LLNode = CollisionProofHashMap.LLNode[K, V] + + /** The actual hash table. */ + private[this] var table: Array[Node] = new Array[Node](tableSizeFor(initialCapacity)) + + /** The next size value at which to resize (capacity * load factor). */ + private[this] var threshold: Int = newThreshold(table.length) + + private[this] var contentSize = 0 + + override def size: Int = contentSize + + @`inline` private[this] final def computeHash(o: K): Int = { + val h = if(o.asInstanceOf[AnyRef] eq null) 0 else o.hashCode + h ^ (h >>> 16) + } + + @`inline` private[this] final def index(hash: Int) = hash & (table.length - 1) + + override protected def fromSpecific(coll: IterableOnce[(K, V)] @uncheckedVariance): CollisionProofHashMap[K, V] @uncheckedVariance = CollisionProofHashMap.from(coll) + override protected def newSpecificBuilder: Builder[(K, V), CollisionProofHashMap[K, V]] @uncheckedVariance = CollisionProofHashMap.newBuilder[K, V] + + override def empty: CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V] + + override def contains(key: K): Boolean = findNode(key) ne null + + def get(key: K): Option[V] = findNode(key) match { + case null => None + case nd => Some(nd match { + case nd: LLNode @uc => nd.value + case nd: RBNode @uc => nd.value + }) + } + + @throws[NoSuchElementException] + override def apply(key: K): V = findNode(key) match { + case null => default(key) + case nd => nd match { + case nd: LLNode @uc => nd.value + case nd: RBNode @uc => nd.value + } + } + + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + val nd = findNode(key) + if (nd eq null) default else nd match { + case nd: LLNode @uc => nd.value + case n => n.asInstanceOf[RBNode].value + } + } + + @`inline` private[this] def findNode(elem: K): Node = { + val hash = computeHash(elem) + table(index(hash)) match { + case null => null + case n: LLNode @uc => n.getNode(elem, hash) + case n => n.asInstanceOf[RBNode].getNode(elem, hash) + } + } + + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / loadFactor).toInt) + if(target > table.length) { + if(size == 0) reallocTable(target) + else growTable(target) + } + } + + override def update(key: K, value: V): Unit = put0(key, value, getOld = false) + + override def put(key: K, value: V): Option[V] = put0(key, value, getOld = true) match { + case null => None + case sm => sm + } + + def addOne(elem: (K, V)): this.type = { put0(elem._1, elem._2, getOld = false); this } + + @`inline` private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + if(contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(key) + val idx = index(hash) + put0(key, value, getOld, hash, idx) + } + + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + val res = table(idx) match { + case n: RBNode @uc => + insert(n, idx, key, hash, value) + case _old => + val old: LLNode = _old.asInstanceOf[LLNode] + if(old eq null) { + table(idx) = new LLNode(key, hash, value, null) + } else { + var remaining = CollisionProofHashMap.treeifyThreshold + var prev: LLNode = null + var n = old + while((n ne null) && n.hash <= hash && remaining > 0) { + if(n.hash == hash && key == n.key) { + val old = n.value + n.value = value + return (if(getOld) Some(old) else null) + } + prev = n + n = n.next + remaining -= 1 + } + if(remaining == 0) { + treeify(old, idx) + return put0(key, value, getOld, hash, idx) + } + if(prev eq null) table(idx) = new LLNode(key, hash, value, old) + else prev.next = new LLNode(key, hash, value, prev.next) + } + true + } + if(res) contentSize += 1 + if(res) Some(null.asInstanceOf[V]) else null //TODO + } + + private[this] def treeify(old: LLNode, idx: Int): Unit = { + table(idx) = CollisionProofHashMap.leaf(old.key, old.hash, old.value, red = false, null) + var n: LLNode = old.next + while(n ne null) { + val root = table(idx).asInstanceOf[RBNode] + insertIntoExisting(root, idx, n.key, n.hash, n.value, root) + n = n.next + } + } + + override def addAll(xs: IterableOnce[(K, V)]): this.type = { + sizeHint(xs, delta = contentSize) + super.addAll(xs) + } + + // returns the old value or Statics.pfMarker if not found + private[this] def remove0(elem: K) : Any = { + val hash = computeHash(elem) + val idx = index(hash) + table(idx) match { + case null => Statics.pfMarker + case t: RBNode @uc => + val v = delete(t, idx, elem, hash) + if(v.asInstanceOf[AnyRef] ne Statics.pfMarker) contentSize -= 1 + v + case nd: LLNode @uc if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + contentSize -= 1 + nd.value + case nd: LLNode @uc => + // find an element that matches + var prev = nd + var next = nd.next + while((next ne null) && next.hash <= hash) { + if(next.hash == hash && next.key == elem) { + prev.next = next.next + contentSize -= 1 + return next.value + } + prev = next + next = next.next + } + Statics.pfMarker + } + } + + private[this] abstract class MapIterator[R] extends AbstractIterator[R] { + protected[this] def extract(node: LLNode): R + protected[this] def extract(node: RBNode): R + + private[this] var i = 0 + private[this] var node: Node = null + private[this] val len = table.length + + def hasNext: Boolean = { + if(node ne null) true + else { + while(i < len) { + val n = table(i) + i += 1 + n match { + case null => + case n: RBNode @uc => + node = CollisionProofHashMap.minNodeNonNull(n) + return true + case n: LLNode @uc => + node = n + return true + } + } + false + } + } + + def next(): R = + if(!hasNext) Iterator.empty.next() + else node match { + case n: RBNode @uc => + val r = extract(n) + node = CollisionProofHashMap.successor(n ) + r + case n: LLNode @uc => + val r = extract(n) + node = n.next + r + } + } + + override def keysIterator: Iterator[K] = { + if (isEmpty) Iterator.empty + else new MapIterator[K] { + protected[this] def extract(node: LLNode) = node.key + protected[this] def extract(node: RBNode) = node.key + } + } + + override def iterator: Iterator[(K, V)] = { + if (isEmpty) Iterator.empty + else new MapIterator[(K, V)] { + protected[this] def extract(node: LLNode) = (node.key, node.value) + protected[this] def extract(node: RBNode) = (node.key, node.value) + } + } + + private[this] def growTable(newlen: Int) = { + var oldlen = table.length + table = java.util.Arrays.copyOf(table, newlen) + threshold = newThreshold(table.length) + while(oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if(old ne null) splitBucket(old, i, i + oldlen, oldlen) + i += 1 + } + oldlen *= 2 + } + } + + @`inline` private[this] def reallocTable(newlen: Int) = { + table = new Array(newlen) + threshold = newThreshold(table.length) + } + + @`inline` private[this] def splitBucket(tree: Node, lowBucket: Int, highBucket: Int, mask: Int): Unit = tree match { + case t: LLNode @uc => splitBucket(t, lowBucket, highBucket, mask) + case t: RBNode @uc => splitBucket(t, lowBucket, highBucket, mask) + } + + private[this] def splitBucket(list: LLNode, lowBucket: Int, highBucket: Int, mask: Int): Unit = { + val preLow: LLNode = new LLNode(null.asInstanceOf[K], 0, null.asInstanceOf[V], null) + val preHigh: LLNode = new LLNode(null.asInstanceOf[K], 0, null.asInstanceOf[V], null) + //preLow.next = null + //preHigh.next = null + var lastLow: LLNode = preLow + var lastHigh: LLNode = preHigh + var n = list + while(n ne null) { + val next = n.next + if((n.hash & mask) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if(list ne preLow.next) table(lowBucket) = preLow.next + if(preHigh.next ne null) { + table(highBucket) = preHigh.next + lastHigh.next = null + } + } + + private[this] def splitBucket(tree: RBNode, lowBucket: Int, highBucket: Int, mask: Int): Unit = { + var lowCount, highCount = 0 + tree.foreachNode((n: RBNode) => if((n.hash & mask) != 0) highCount += 1 else lowCount += 1) + if(highCount != 0) { + if(lowCount == 0) { + table(lowBucket) = null + table(highBucket) = tree + } else { + table(lowBucket) = fromNodes(new CollisionProofHashMap.RBNodesIterator(tree).filter(n => (n.hash & mask) == 0), lowCount) + table(highBucket) = fromNodes(new CollisionProofHashMap.RBNodesIterator(tree).filter(n => (n.hash & mask) != 0), highCount) + } + } + } + + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * loadFactor).toInt + + override def clear(): Unit = { + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 + } + + override def remove(key: K): Option[V] = { + val v = remove0(key) + if(v.asInstanceOf[AnyRef] eq Statics.pfMarker) None else Some(v.asInstanceOf[V]) + } + + def subtractOne(elem: K): this.type = { remove0(elem); this } + + override def knownSize: Int = size + + override def isEmpty: Boolean = size == 0 + + override def foreach[U](f: ((K, V)) => U): Unit = { + val len = table.length + var i = 0 + while(i < len) { + val n = table(i) + if(n ne null) n match { + case n: LLNode @uc => n.foreach(f) + case n: RBNode @uc => n.foreach(f) + } + i += 1 + } + } + + override def foreachEntry[U](f: (K, V) => U): Unit = { + val len = table.length + var i = 0 + while(i < len) { + val n = table(i) + if(n ne null) n match { + case n: LLNode @uc => n.foreachEntry(f) + case n: RBNode @uc => n.foreachEntry(f) + } + i += 1 + } + } + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(new CollisionProofHashMap.DeserializationFactory[K, V](table.length, loadFactor, ordering), this) + + override protected[this] def className = "CollisionProofHashMap" + + override def getOrElseUpdate(key: K, defaultValue: => V): V = { + val hash = computeHash(key) + val idx = index(hash) + table(idx) match { + case null => () + case n: LLNode @uc => + val nd = n.getNode(key, hash) + if(nd != null) return nd.value + case n => + val nd = n.asInstanceOf[RBNode].getNode(key, hash) + if(nd != null) return nd.value + } + val table0 = table + val default = defaultValue + if(contentSize + 1 >= threshold) growTable(table.length * 2) + // Avoid recomputing index if the `defaultValue()` or new element hasn't triggered a table resize. + val newIdx = if (table0 eq table) idx else index(hash) + put0(key, default, getOld = false, hash, newIdx) + default + } + + ///////////////////// Overrides code from SortedMapOps + + /** Builds a new `CollisionProofHashMap` by applying a function to all elements of this $coll. + * + * @param f the function to apply to each element. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. + */ + def map[K2, V2](f: ((K, V)) => (K2, V2)) + (implicit @implicitNotFound(CollisionProofHashMap.ordMsg) ordering: Ordering[K2]): CollisionProofHashMap[K2, V2] = + sortedMapFactory.from(new View.Map[(K, V), (K2, V2)](this, f)) + + /** Builds a new `CollisionProofHashMap` by applying a function to all elements of this $coll + * and using the elements of the resulting collections. + * + * @param f the function to apply to each element. + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]) + (implicit @implicitNotFound(CollisionProofHashMap.ordMsg) ordering: Ordering[K2]): CollisionProofHashMap[K2, V2] = + sortedMapFactory.from(new View.FlatMap(this, f)) + + /** Builds a new sorted map by applying a partial function to all elements of this $coll + * on which the function is defined. + * + * @param pf the partial function which filters and maps the $coll. + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ + def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]) + (implicit @implicitNotFound(CollisionProofHashMap.ordMsg) ordering: Ordering[K2]): CollisionProofHashMap[K2, V2] = + sortedMapFactory.from(new View.Collect(this, pf)) + + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): CollisionProofHashMap[K, V2] = sortedMapFactory.from(suffix match { + case it: Iterable[(K, V2)] => new View.Concat(this, it) + case _ => iterator.concat(suffix.iterator) + }) + + /** Alias for `concat` */ + @`inline` override final def ++ [V2 >: V](xs: IterableOnce[(K, V2)]): CollisionProofHashMap[K, V2] = concat(xs) + + @deprecated("Consider requiring an immutable Map or fall back to Map.concat", "2.13.0") + override def + [V1 >: V](kv: (K, V1)): CollisionProofHashMap[K, V1] = + sortedMapFactory.from(new View.Appended(this, kv)) + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CollisionProofHashMap[K, V1] = + sortedMapFactory.from(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems)) + + ///////////////////// RedBlackTree code derived from mutable.RedBlackTree: + + @`inline` private[this] def isRed(node: RBNode) = (node ne null) && node.red + @`inline` private[this] def isBlack(node: RBNode) = (node eq null) || !node.red + + @unused @`inline` private[this] def compare(key: K, hash: Int, node: LLNode): Int = { + val i = hash - node.hash + if(i != 0) i else ordering.compare(key, node.key) + } + + @`inline` private[this] def compare(key: K, hash: Int, node: RBNode): Int = { + /*val i = hash - node.hash + if(i != 0) i else*/ ordering.compare(key, node.key) + } + + // ---- insertion ---- + + @tailrec private[this] final def insertIntoExisting(_root: RBNode, bucket: Int, key: K, hash: Int, value: V, x: RBNode): Boolean = { + val cmp = compare(key, hash, x) + if(cmp == 0) { + x.value = value + false + } else { + val next = if(cmp < 0) x.left else x.right + if(next eq null) { + val z = CollisionProofHashMap.leaf(key, hash, value, red = true, x) + if (cmp < 0) x.left = z else x.right = z + table(bucket) = fixAfterInsert(_root, z) + return true + } + else insertIntoExisting(_root, bucket, key, hash, value, next) + } + } + + private[this] final def insert(tree: RBNode, bucket: Int, key: K, hash: Int, value: V): Boolean = { + if(tree eq null) { + table(bucket) = CollisionProofHashMap.leaf(key, hash, value, red = false, null) + true + } else insertIntoExisting(tree, bucket, key, hash, value, tree) + } + + private[this] def fixAfterInsert(_root: RBNode, node: RBNode): RBNode = { + var root = _root + var z = node + while (isRed(z.parent)) { + if (z.parent eq z.parent.parent.left) { + val y = z.parent.parent.right + if (isRed(y)) { + z.parent.red = false + y.red = false + z.parent.parent.red = true + z = z.parent.parent + } else { + if (z eq z.parent.right) { + z = z.parent + root = rotateLeft(root, z) + } + z.parent.red = false + z.parent.parent.red = true + root = rotateRight(root, z.parent.parent) + } + } else { // symmetric cases + val y = z.parent.parent.left + if (isRed(y)) { + z.parent.red = false + y.red = false + z.parent.parent.red = true + z = z.parent.parent + } else { + if (z eq z.parent.left) { + z = z.parent + root = rotateRight(root, z) + } + z.parent.red = false + z.parent.parent.red = true + root = rotateLeft(root, z.parent.parent) + } + } + } + root.red = false + root + } + + // ---- deletion ---- + + // returns the old value or Statics.pfMarker if not found + private[this] def delete(_root: RBNode, bucket: Int, key: K, hash: Int): Any = { + var root = _root + val z = root.getNode(key, hash: Int) + if (z ne null) { + val oldValue = z.value + var y = z + var yIsRed = y.red + var x: RBNode = null + var xParent: RBNode = null + + if (z.left eq null) { + x = z.right + root = transplant(root, z, z.right) + xParent = z.parent + } + else if (z.right eq null) { + x = z.left + root = transplant(root, z, z.left) + xParent = z.parent + } + else { + y = CollisionProofHashMap.minNodeNonNull(z.right) + yIsRed = y.red + x = y.right + + if (y.parent eq z) xParent = y + else { + xParent = y.parent + root = transplant(root, y, y.right) + y.right = z.right + y.right.parent = y + } + root = transplant(root, z, y) + y.left = z.left + y.left.parent = y + y.red = z.red + } + + if (!yIsRed) root = fixAfterDelete(root, x, xParent) + if(root ne _root) table(bucket) = root + oldValue + } else Statics.pfMarker + } + + private[this] def fixAfterDelete(_root: RBNode, node: RBNode, parent: RBNode): RBNode = { + var root = _root + var x = node + var xParent = parent + while ((x ne root) && isBlack(x)) { + if (x eq xParent.left) { + var w = xParent.right + // assert(w ne null) + + if (w.red) { + w.red = false + xParent.red = true + root = rotateLeft(root, xParent) + w = xParent.right + } + if (isBlack(w.left) && isBlack(w.right)) { + w.red = true + x = xParent + } else { + if (isBlack(w.right)) { + w.left.red = false + w.red = true + root = rotateRight(root, w) + w = xParent.right + } + w.red = xParent.red + xParent.red = false + w.right.red = false + root = rotateLeft(root, xParent) + x = root + } + } else { // symmetric cases + var w = xParent.left + // assert(w ne null) + + if (w.red) { + w.red = false + xParent.red = true + root = rotateRight(root, xParent) + w = xParent.left + } + if (isBlack(w.right) && isBlack(w.left)) { + w.red = true + x = xParent + } else { + if (isBlack(w.left)) { + w.right.red = false + w.red = true + root = rotateLeft(root, w) + w = xParent.left + } + w.red = xParent.red + xParent.red = false + w.left.red = false + root = rotateRight(root, xParent) + x = root + } + } + xParent = x.parent + } + if (x ne null) x.red = false + root + } + + // ---- helpers ---- + + @`inline` private[this] def rotateLeft(_root: RBNode, x: RBNode): RBNode = { + var root = _root + val y = x.right + x.right = y.left + + val xp = x.parent + if (y.left ne null) y.left.parent = x + y.parent = xp + + if (xp eq null) root = y + else if (x eq xp.left) xp.left = y + else xp.right = y + + y.left = x + x.parent = y + root + } + + @`inline` private[this] def rotateRight(_root: RBNode, x: RBNode): RBNode = { + var root = _root + val y = x.left + x.left = y.right + + val xp = x.parent + if (y.right ne null) y.right.parent = x + y.parent = xp + + if (xp eq null) root = y + else if (x eq xp.right) xp.right = y + else xp.left = y + + y.right = x + x.parent = y + root + } + + /** + * Transplant the node `from` to the place of node `to`. This is done by setting `from` as a child of `to`'s previous + * parent and setting `from`'s parent to the `to`'s previous parent. The children of `from` are left unchanged. + */ + private[this] def transplant(_root: RBNode, to: RBNode, from: RBNode): RBNode = { + var root = _root + if (to.parent eq null) root = from + else if (to eq to.parent.left) to.parent.left = from + else to.parent.right = from + if (from ne null) from.parent = to.parent + root + } + + // building + + def fromNodes(xs: Iterator[Node], size: Int): RBNode = { + val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes + def f(level: Int, size: Int): RBNode = size match { + case 0 => null + case 1 => + val nn = xs.next() + val (key, hash, value) = nn match { + case nn: LLNode @uc => (nn.key, nn.hash, nn.value) + case nn: RBNode @uc => (nn.key, nn.hash, nn.value) + } + new RBNode(key, hash, value, level == maxUsedDepth && level != 1, null, null, null) + case n => + val leftSize = (size-1)/2 + val left = f(level+1, leftSize) + val nn = xs.next() + val right = f(level+1, size-1-leftSize) + val (key, hash, value) = nn match { + case nn: LLNode @uc => (nn.key, nn.hash, nn.value) + case nn: RBNode @uc => (nn.key, nn.hash, nn.value) + } + val n = new RBNode(key, hash, value, red = false, left, right, null) + if(left ne null) left.parent = n + right.parent = n + n + } + f(1, size) + } +} + +/** + * $factoryInfo + * @define Coll `mutable.CollisionProofHashMap` + * @define coll mutable collision-proof hash map + */ +@SerialVersionUID(3L) +object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { + private[collection] final val ordMsg = "No implicit Ordering[${K2}] found to build a CollisionProofHashMap[${K2}, ${V2}]. You may want to upcast to a Map[${K}, ${V}] first by calling `unsorted`." + + def from[K : Ordering, V](it: scala.collection.IterableOnce[(K, V)]): CollisionProofHashMap[K, V] = { + val k = it.knownSize + val cap = if(k > 0) ((k + 1).toDouble / defaultLoadFactor).toInt else defaultInitialCapacity + new CollisionProofHashMap[K, V](cap, defaultLoadFactor) ++= it + } + + def empty[K : Ordering, V]: CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V] + + def newBuilder[K : Ordering, V]: Builder[(K, V), CollisionProofHashMap[K, V]] = newBuilder(defaultInitialCapacity, defaultLoadFactor) + + def newBuilder[K : Ordering, V](initialCapacity: Int, loadFactor: Double): Builder[(K, V), CollisionProofHashMap[K, V]] = + new GrowableBuilder[(K, V), CollisionProofHashMap[K, V]](new CollisionProofHashMap[K, V](initialCapacity, loadFactor)) { + override def sizeHint(size: Int) = elems.sizeHint(size) + } + + /** The default load factor for the hash table */ + final def defaultLoadFactor: Double = 0.75 + + /** The default initial capacity for the hash table */ + final def defaultInitialCapacity: Int = 16 + + @SerialVersionUID(3L) + private final class DeserializationFactory[K, V](val tableLength: Int, val loadFactor: Double, val ordering: Ordering[K]) extends Factory[(K, V), CollisionProofHashMap[K, V]] with Serializable { + def fromSpecific(it: IterableOnce[(K, V)]): CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V](tableLength, loadFactor)(ordering) ++= it + def newBuilder: Builder[(K, V), CollisionProofHashMap[K, V]] = CollisionProofHashMap.newBuilder(tableLength, loadFactor)(using ordering) + } + + @unused @`inline` private def compare[K, V](key: K, hash: Int, node: LLNode[K, V])(implicit ord: Ordering[K]): Int = { + val i = hash - node.hash + if(i != 0) i else ord.compare(key, node.key) + } + + @`inline` private def compare[K, V](key: K, hash: Int, node: RBNode[K, V])(implicit ord: Ordering[K]): Int = { + /*val i = hash - node.hash + if(i != 0) i else*/ ord.compare(key, node.key) + } + + private final val treeifyThreshold = 8 + + // Superclass for RBNode and LLNode to help the JIT with optimizing instance checks, but no shared common fields. + // Keeping calls monomorphic where possible and dispatching manually where needed is faster. + sealed abstract class Node + + /////////////////////////// Red-Black Tree Node + + final class RBNode[K, V](var key: K, var hash: Int, var value: V, var red: Boolean, var left: RBNode[K, V], var right: RBNode[K, V], var parent: RBNode[K, V]) extends Node { + override def toString: String = "RBNode(" + key + ", " + hash + ", " + value + ", " + red + ", " + left + ", " + right + ")" + + @tailrec def getNode(k: K, h: Int)(implicit ord: Ordering[K]): RBNode[K, V] = { + val cmp = compare(k, h, this) + if (cmp < 0) { + if(left ne null) left.getNode(k, h) else null + } else if (cmp > 0) { + if(right ne null) right.getNode(k, h) else null + } else this + } + + def foreach[U](f: ((K, V)) => U): Unit = { + if(left ne null) left.foreach(f) + f((key, value)) + if(right ne null) right.foreach(f) + } + + def foreachEntry[U](f: (K, V) => U): Unit = { + if(left ne null) left.foreachEntry(f) + f(key, value) + if(right ne null) right.foreachEntry(f) + } + + def foreachNode[U](f: RBNode[K, V] => U): Unit = { + if(left ne null) left.foreachNode(f) + f(this) + if(right ne null) right.foreachNode(f) + } + } + + @`inline` private def leaf[A, B](key: A, hash: Int, value: B, red: Boolean, parent: RBNode[A, B]): RBNode[A, B] = + new RBNode(key, hash, value, red, null, null, parent) + + @tailrec private def minNodeNonNull[A, B](node: RBNode[A, B]): RBNode[A, B] = + if (node.left eq null) node else minNodeNonNull(node.left) + + /** + * Returns the node that follows `node` in an in-order tree traversal. If `node` has the maximum key (and is, + * therefore, the last node), this method returns `null`. + */ + private def successor[A, B](node: RBNode[A, B]): RBNode[A, B] = { + if (node.right ne null) minNodeNonNull(node.right) + else { + var x = node + var y = x.parent + while ((y ne null) && (x eq y.right)) { + x = y + y = y.parent + } + y + } + } + + private final class RBNodesIterator[A, B](tree: RBNode[A, B])(implicit @unused ord: Ordering[A]) extends AbstractIterator[RBNode[A, B]] { + private[this] var nextNode: RBNode[A, B] = if(tree eq null) null else minNodeNonNull(tree) + + def hasNext: Boolean = nextNode ne null + + @throws[NoSuchElementException] + def next(): RBNode[A, B] = nextNode match { + case null => Iterator.empty.next() + case node => + nextNode = successor(node) + node + } + } + + /////////////////////////// Linked List Node + + private final class LLNode[K, V](var key: K, var hash: Int, var value: V, var next: LLNode[K, V]) extends Node { + override def toString = s"LLNode($key, $value, $hash) -> $next" + + private[this] def eq(a: Any, b: Any): Boolean = + if(a.asInstanceOf[AnyRef] eq null) b.asInstanceOf[AnyRef] eq null else a.asInstanceOf[AnyRef].equals(b) + + @tailrec def getNode(k: K, h: Int)(implicit ord: Ordering[K]): LLNode[K, V] = { + if(h == hash && eq(k, key) /*ord.compare(k, key) == 0*/) this + else if((next eq null) || (hash > h)) null + else next.getNode(k, h) + } + + @tailrec def foreach[U](f: ((K, V)) => U): Unit = { + f((key, value)) + if(next ne null) next.foreach(f) + } + + @tailrec def foreachEntry[U](f: (K, V) => U): Unit = { + f(key, value) + if(next ne null) next.foreachEntry(f) + } + + @tailrec def foreachNode[U](f: LLNode[K, V] => U): Unit = { + f(this) + if(next ne null) next.foreachNode(f) + } + } +} diff --git a/library/src/scala/collection/mutable/Growable.scala b/library/src/scala/collection/mutable/Growable.scala new file mode 100644 index 000000000000..b2d4806089dc --- /dev/null +++ b/library/src/scala/collection/mutable/Growable.scala @@ -0,0 +1,101 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +/** This trait forms part of collections that can be augmented + * using a `+=` operator and that can be cleared of all elements using + * a `clear` method. + * + * @define coll growable collection + * @define Coll `Growable` + * @define add add + * @define Add Add + */ +trait Growable[-A] extends Clearable { + + /** ${Add}s a single element to this $coll. + * + * @param elem the element to $add. + * @return the $coll itself + */ + def addOne(elem: A): this.type + + /** Alias for `addOne` */ + @inline final def += (elem: A): this.type = addOne(elem) + + //TODO This causes a conflict in StringBuilder; looks like a compiler bug + //@deprecated("Use addOne or += instead of append", "2.13.0") + //@`inline` final def append(elem: A): Unit = addOne(elem) + + /** ${Add}s two or more elements to this $coll. + * + * @param elem1 the first element to $add. + * @param elem2 the second element to $add. + * @param elems the remaining elements to $add. + * @return the $coll itself + */ + @deprecated("Use `++=` aka `addAll` instead of varargs `+=`; infix operations with an operand of multiple args will be deprecated", "2.13.0") + @inline final def += (elem1: A, elem2: A, elems: A*): this.type = this += elem1 += elem2 ++= (elems: IterableOnce[A]) + + /** ${Add}s all elements produced by an IterableOnce to this $coll. + * + * @param elems the IterableOnce producing the elements to $add. + * @return the $coll itself. + */ + def addAll(@deprecatedName("xs") elems: IterableOnce[A]): this.type = { + if (elems.asInstanceOf[AnyRef] eq this) addAll(Buffer.from(elems)) // avoid mutating under our own iterator + else { + val it = elems.iterator + while (it.hasNext) { + addOne(it.next()) + } + } + this + } + + /** Alias for `addAll` */ + @inline final def ++= (@deprecatedName("xs") elems: IterableOnce[A]): this.type = addAll(elems) + + /** The number of elements in the collection under construction, if it can be cheaply computed, -1 otherwise. + * + * @return The number of elements. The default implementation always returns -1. + */ + def knownSize: Int = -1 +} + +object Growable { + + /** + * Fills a `Growable` instance with the elements of a given iterable + * @param empty Instance to fill + * @param it Elements to add + * @tparam A Element type + * @return The filled instance + */ + def from[A](empty: Growable[A], it: collection.IterableOnce[A]): empty.type = empty ++= it + +} + +/** This trait forms part of collections that can be cleared + * with a clear() call. + * + * @define coll collection + */ +trait Clearable { + /** Clears the $coll's contents. After this operation, the + * $coll is empty. + */ + def clear(): Unit +} diff --git a/library/src/scala/collection/mutable/GrowableBuilder.scala b/library/src/scala/collection/mutable/GrowableBuilder.scala new file mode 100644 index 000000000000..247bc58da150 --- /dev/null +++ b/library/src/scala/collection/mutable/GrowableBuilder.scala @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.mutable + + +/** The canonical builder for collections that are growable, i.e. that support an + * efficient `+=` method which adds an element to the collection. + * + * GrowableBuilders can produce only a single instance of the collection they are growing. + * + * @define Coll `GrowingBuilder` + * @define coll growing builder + */ +class GrowableBuilder[Elem, To <: Growable[Elem]](protected val elems: To) + extends Builder[Elem, To] { + + def clear(): Unit = elems.clear() + + def result(): To = elems + + def addOne(elem: Elem): this.type = { elems += elem; this } + + override def addAll(xs: IterableOnce[Elem]): this.type = { elems.addAll(xs); this } + + override def knownSize: Int = elems.knownSize +} diff --git a/library/src/scala/collection/mutable/HashMap.scala b/library/src/scala/collection/mutable/HashMap.scala new file mode 100644 index 000000000000..86aa9541c4e3 --- /dev/null +++ b/library/src/scala/collection/mutable/HashMap.scala @@ -0,0 +1,654 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.annotation.{nowarn, tailrec} +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializationProxy +import scala.util.hashing.MurmurHash3 + +/** This class implements mutable maps using a hashtable. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * section on `Hash Tables` for more information. + * + * @tparam K the type of the keys contained in this hash map. + * @tparam V the type of the values assigned to keys in this hash map. + * + * @define Coll `mutable.HashMap` + * @define coll mutable hash map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@deprecatedInheritance("HashMap will be made final; use .withDefault for the common use case of computing a default value", "2.13.0") +class HashMap[K, V](initialCapacity: Int, loadFactor: Double) + extends AbstractMap[K, V] + with MapOps[K, V, HashMap, HashMap[K, V]] + with StrictOptimizedIterableOps[(K, V), Iterable, HashMap[K, V]] + with StrictOptimizedMapOps[K, V, HashMap, HashMap[K, V]] + with MapFactoryDefaults[K, V, HashMap, Iterable] + with Serializable { + + /* The HashMap class holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. + * - Every bucket is sorted in ascendent hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ + def this() = this(HashMap.defaultInitialCapacity, HashMap.defaultLoadFactor) + + import HashMap.Node + + /** The actual hash table. */ + private[this] var table = new Array[Node[K, V]](tableSizeFor(initialCapacity)) + + /** The next size value at which to resize (capacity * load factor). */ + private[this] var threshold: Int = newThreshold(table.length) + + private[this] var contentSize = 0 + + override def size: Int = contentSize + + /** Performs the inverse operation of improveHash. In this case, it happens to be identical to improveHash*/ + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) + + /** Computes the improved hash of an original (`any.##`) hash. */ + @`inline` private[this] def improveHash(originalHash: Int): Int = { + // Improve the hash by xoring the high 16 bits into the low 16 bits just in case entropy is skewed towards the + // high-value bits. We only use the lowest bits to determine the hash bucket. This is the same improvement + // algorithm as in java.util.HashMap. + // + // This function is also its own inverse. That is, for all ints i, improveHash(improveHash(i)) = i + // this allows us to retrieve the original hash when we need it, for instance when appending to an immutable.HashMap + // and that is why unimproveHash simply forwards to this method + originalHash ^ (originalHash >>> 16) + } + + /** Computes the improved hash of this key */ + @`inline` private[this] def computeHash(o: K): Int = improveHash(o.##) + + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + override def contains(key: K): Boolean = findNode(key) ne null + + @`inline` private[this] def findNode(key: K): Node[K, V] = { + val hash = computeHash(key) + table(index(hash)) match { + case null => null + case nd => nd.findNode(key, hash) + } + } + + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / loadFactor).toInt) + if(target > table.length) growTable(target) + } + + override def addAll(xs: IterableOnce[(K, V)]): this.type = { + sizeHint(xs) + + xs match { + case hm: immutable.HashMap[K, V] => + hm.foreachWithHash((k, v, h) => put0(k, v, improveHash(h), getOld = false)) + this + case hm: mutable.HashMap[K, V] => + val iter = hm.nodeIterator + while (iter.hasNext) { + val next = iter.next() + put0(next.key, next.value, next.hash, getOld = false) + } + this + case lhm: mutable.LinkedHashMap[K, V] => + val iter = lhm.entryIterator + while (iter.hasNext) { + val entry = iter.next() + put0(entry.key, entry.value, entry.hash, getOld = false) + } + this + case thatMap: Map[K, V] => + thatMap.foreachEntry { (key: K, value: V) => + put0(key, value, improveHash(key.##), getOld = false) + } + this + case _ => + super.addAll(xs) + } + } + + // Override updateWith for performance, so we can do the update while hashing + // the input key only once and performing one lookup into the hash table + override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { + if (getClass != classOf[HashMap[_, _]]) { + // subclasses of HashMap might customise `get` ... + super.updateWith(key)(remappingFunction) + } else { + val hash = computeHash(key) + val indexedHash = index(hash) + + var foundNode: Node[K, V] = null + var previousNode: Node[K, V] = null + table(indexedHash) match { + case null => + case nd => + @tailrec + def findNode(prev: Node[K, V], nd: Node[K, V], k: K, h: Int): Unit = { + if (h == nd.hash && k == nd.key) { + previousNode = prev + foundNode = nd + } + else if ((nd.next eq null) || (nd.hash > h)) () + else findNode(nd, nd.next, k, h) + } + + findNode(null, nd, key, hash) + } + + val previousValue = foundNode match { + case null => None + case nd => Some(nd.value) + } + + val nextValue = remappingFunction(previousValue) + + (previousValue, nextValue) match { + case (None, None) => // do nothing + + case (Some(_), None) => + if (previousNode != null) previousNode.next = foundNode.next + else table(indexedHash) = foundNode.next + contentSize -= 1 + + case (None, Some(value)) => + val newIndexedHash = + if (contentSize + 1 >= threshold) { + growTable(table.length * 2) + index(hash) + } else indexedHash + put0(key, value, getOld = false, hash, newIndexedHash) + + case (Some(_), Some(newValue)) => foundNode.value = newValue + } + nextValue + } + } + + override def subtractAll(xs: IterableOnce[K]): this.type = { + if (size == 0) { + return this + } + + xs match { + case hs: immutable.HashSet[K] => + hs.foreachWithHashWhile { (k, h) => + remove0(k, improveHash(h)) + size > 0 + } + this + case hs: mutable.HashSet[K] => + val iter = hs.nodeIterator + while (iter.hasNext) { + val next = iter.next() + remove0(next.key, next.hash) + if (size == 0) return this + } + this + case lhs: mutable.LinkedHashSet[K] => + val iter = lhs.entryIterator + while (iter.hasNext) { + val next = iter.next() + remove0(next.key, next.hash) + if (size == 0) return this + } + this + case _ => super.subtractAll(xs) + } + } + + /** Adds a key-value pair to this map + * + * @param key the key to add + * @param value the value to add + * @param hash the **improved** hashcode of `key` (see computeHash) + * @param getOld if true, then the previous value for `key` will be returned, otherwise, false + */ + private[this] def put0(key: K, value: V, hash: Int, getOld: Boolean): Some[V] = { + if(contentSize + 1 >= threshold) growTable(table.length * 2) + val idx = index(hash) + put0(key, value, getOld, hash, idx) + } + + private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + if(contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(key) + val idx = index(hash) + put0(key, value, getOld, hash, idx) + } + + + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + table(idx) match { + case null => + table(idx) = new Node[K, V](key, hash, value, null) + case old => + var prev: Node[K, V] = null + var n = old + while((n ne null) && n.hash <= hash) { + if(n.hash == hash && key == n.key) { + val old = n.value + n.value = value + return if(getOld) Some(old) else null + } + prev = n + n = n.next + } + if(prev eq null) table(idx) = new Node(key, hash, value, old) + else prev.next = new Node(key, hash, value, prev.next) + } + contentSize += 1 + null + } + + private def remove0(elem: K) : Node[K, V] = remove0(elem, computeHash(elem)) + + /** Removes a key from this map if it exists + * + * @param elem the element to remove + * @param hash the **improved** hashcode of `element` (see computeHash) + * @return the node that contained element if it was present, otherwise null + */ + private[this] def remove0(elem: K, hash: Int) : Node[K, V] = { + val idx = index(hash) + table(idx) match { + case null => null + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + contentSize -= 1 + nd + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while((next ne null) && next.hash <= hash) { + if(next.hash == hash && next.key == elem) { + prev.next = next.next + contentSize -= 1 + return next + } + prev = next + next = next.next + } + null + } + } + + private[this] abstract class HashMapIterator[A] extends AbstractIterator[A] { + private[this] var i = 0 + private[this] var node: Node[K, V] = null + private[this] val len = table.length + + protected[this] def extract(nd: Node[K, V]): A + + def hasNext: Boolean = { + if(node ne null) true + else { + while(i < len) { + val n = table(i) + i += 1 + if(n ne null) { node = n; return true } + } + false + } + } + + def next(): A = + if(!hasNext) Iterator.empty.next() + else { + val r = extract(node) + node = node.next + r + } + } + + override def iterator: Iterator[(K, V)] = + if(size == 0) Iterator.empty + else new HashMapIterator[(K, V)] { + protected[this] def extract(nd: Node[K, V]) = (nd.key, nd.value) + } + + override def keysIterator: Iterator[K] = + if(size == 0) Iterator.empty + else new HashMapIterator[K] { + protected[this] def extract(nd: Node[K, V]) = nd.key + } + + override def valuesIterator: Iterator[V] = + if(size == 0) Iterator.empty + else new HashMapIterator[V] { + protected[this] def extract(nd: Node[K, V]) = nd.value + } + + + /** Returns an iterator over the nodes stored in this HashMap */ + private[collection] def nodeIterator: Iterator[Node[K, V]] = + if(size == 0) Iterator.empty + else new HashMapIterator[Node[K, V]] { + protected[this] def extract(nd: Node[K, V]) = nd + } + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[(K, V), S]): S with EfficientSplit = + shape. + parUnbox(new convert.impl.AnyTableStepper[(K, V), Node[K, V]](size, table, _.next, node => (node.key, node.value), 0, table.length)). + asInstanceOf[S with EfficientSplit] + + override def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S with EfficientSplit = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntTableStepper[Node[K, V]] (size, table, _.next, _.key.asInstanceOf[Int], 0, table.length) + case StepperShape.LongShape => new LongTableStepper[Node[K, V]] (size, table, _.next, _.key.asInstanceOf[Long], 0, table.length) + case StepperShape.DoubleShape => new DoubleTableStepper[Node[K, V]](size, table, _.next, _.key.asInstanceOf[Double], 0, table.length) + case _ => shape.parUnbox(new AnyTableStepper[K, Node[K, V]](size, table, _.next, _.key, 0, table.length)) + } + s.asInstanceOf[S with EfficientSplit] + } + + override def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S with EfficientSplit = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntTableStepper[Node[K, V]] (size, table, _.next, _.value.asInstanceOf[Int], 0, table.length) + case StepperShape.LongShape => new LongTableStepper[Node[K, V]] (size, table, _.next, _.value.asInstanceOf[Long], 0, table.length) + case StepperShape.DoubleShape => new DoubleTableStepper[Node[K, V]](size, table, _.next, _.value.asInstanceOf[Double], 0, table.length) + case _ => shape.parUnbox(new AnyTableStepper[V, Node[K, V]](size, table, _.next, _.value, 0, table.length)) + } + s.asInstanceOf[S with EfficientSplit] + } + + private[this] def growTable(newlen: Int) = { + if (newlen < 0) + throw new RuntimeException(s"new HashMap table size $newlen exceeds maximum") + var oldlen = table.length + threshold = newThreshold(newlen) + if(size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow: Node[K, V] = new Node(null.asInstanceOf[K], 0, null.asInstanceOf[V], null) + val preHigh: Node[K, V] = new Node(null.asInstanceOf[K], 0, null.asInstanceOf[V], null) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while(oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if(old ne null) { + preLow.next = null + preHigh.next = null + var lastLow: Node[K, V] = preLow + var lastHigh: Node[K, V] = preHigh + var n = old + while(n ne null) { + val next = n.next + if((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if(old ne preLow.next) table(i) = preLow.next + if(preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * loadFactor).toInt + + override def clear(): Unit = { + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 + } + + def get(key: K): Option[V] = findNode(key) match { + case null => None + case nd => Some(nd.value) + } + + @throws[NoSuchElementException] + override def apply(key: K): V = findNode(key) match { + case null => default(key) + case nd => nd.value + } + + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + if (getClass != classOf[HashMap[_, _]]) { + // subclasses of HashMap might customise `get` ... + super.getOrElse(key, default) + } else { + // .. but in the common case, we can avoid the Option boxing. + val nd = findNode(key) + if (nd eq null) default else nd.value + } + } + + override def getOrElseUpdate(key: K, defaultValue: => V): V = { + if (getClass != classOf[HashMap[_, _]]) { + // subclasses of HashMap might customise `get` ... + super.getOrElseUpdate(key, defaultValue) + } else { + val hash = computeHash(key) + val idx = index(hash) + val nd = table(idx) match { + case null => null + case nd => nd.findNode(key, hash) + } + if(nd != null) nd.value + else { + val table0 = table + val default = defaultValue + if(contentSize + 1 >= threshold) growTable(table.length * 2) + // Avoid recomputing index if the `defaultValue()` or new element hasn't triggered a table resize. + val newIdx = if (table0 eq table) idx else index(hash) + put0(key, default, getOld = false, hash, newIdx) + default + } + } + } + + override def put(key: K, value: V): Option[V] = put0(key, value, getOld = true) match { + case null => None + case sm => sm + } + + override def remove(key: K): Option[V] = remove0(key) match { + case null => None + case nd => Some(nd.value) + } + + override def update(key: K, value: V): Unit = put0(key, value, getOld = false) + + def addOne(elem: (K, V)): this.type = { put0(elem._1, elem._2, getOld = false); this } + + def subtractOne(elem: K): this.type = { remove0(elem); this } + + override def knownSize: Int = size + + override def isEmpty: Boolean = size == 0 + + override def foreach[U](f: ((K, V)) => U): Unit = { + val len = table.length + var i = 0 + while(i < len) { + val n = table(i) + if(n ne null) n.foreach(f) + i += 1 + } + } + + override def foreachEntry[U](f: (K, V) => U): Unit = { + val len = table.length + var i = 0 + while(i < len) { + val n = table(i) + if(n ne null) n.foreachEntry(f) + i += 1 + } + } + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(new mutable.HashMap.DeserializationFactory[K, V](table.length, loadFactor), this) + + override def filterInPlace(p: (K, V) => Boolean): this.type = { + if (nonEmpty) { + var bucket = 0 + + while (bucket < table.length) { + var head = table(bucket) + + while ((head ne null) && !p(head.key, head.value)) { + head = head.next + contentSize -= 1 + } + + if (head ne null) { + var prev = head + var next = head.next + + while (next ne null) { + if (p(next.key, next.value)) { + prev = next + } else { + prev.next = next.next + contentSize -= 1 + } + next = next.next + } + } + + table(bucket) = head + bucket += 1 + } + } + this + } + + // TODO: rename to `mapValuesInPlace` and override the base version (not binary compatible) + private[mutable] def mapValuesInPlaceImpl(f: (K, V) => V): this.type = { + val len = table.length + var i = 0 + while (i < len) { + var n = table(i) + while (n ne null) { + n.value = f(n.key, n.value) + n = n.next + } + i += 1 + } + this + } + + override def mapFactory: MapFactory[HashMap] = HashMap + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "HashMap" + + override def hashCode: Int = { + if (isEmpty) MurmurHash3.emptyMapHash + else { + val tupleHashIterator = new HashMapIterator[Any] { + var hash: Int = 0 + override def hashCode: Int = hash + override protected[this] def extract(nd: Node[K, V]): Any = { + hash = MurmurHash3.tuple2Hash(unimproveHash(nd.hash), nd.value.##) + this + } + } + MurmurHash3.unorderedHash(tupleHashIterator, MurmurHash3.mapSeed) + } + } +} + +/** + * $factoryInfo + * @define Coll `mutable.HashMap` + * @define coll mutable hash map + */ +@SerialVersionUID(3L) +object HashMap extends MapFactory[HashMap] { + + def empty[K, V]: HashMap[K, V] = new HashMap[K, V] + + def from[K, V](it: collection.IterableOnce[(K, V)]): HashMap[K, V] = { + val k = it.knownSize + val cap = if(k > 0) ((k + 1).toDouble / defaultLoadFactor).toInt else defaultInitialCapacity + new HashMap[K, V](cap, defaultLoadFactor).addAll(it) + } + + def newBuilder[K, V]: Builder[(K, V), HashMap[K, V]] = newBuilder(defaultInitialCapacity, defaultLoadFactor) + + def newBuilder[K, V](initialCapacity: Int, loadFactor: Double): Builder[(K, V), HashMap[K, V]] = + new GrowableBuilder[(K, V), HashMap[K, V]](new HashMap[K, V](initialCapacity, loadFactor)) { + override def sizeHint(size: Int) = elems.sizeHint(size) + } + + /** The default load factor for the hash table */ + final def defaultLoadFactor: Double = 0.75 + + /** The default initial capacity for the hash table */ + final def defaultInitialCapacity: Int = 16 + + @SerialVersionUID(3L) + private final class DeserializationFactory[K, V](val tableLength: Int, val loadFactor: Double) extends Factory[(K, V), HashMap[K, V]] with Serializable { + def fromSpecific(it: IterableOnce[(K, V)]): HashMap[K, V] = new HashMap[K, V](tableLength, loadFactor).addAll(it) + def newBuilder: Builder[(K, V), HashMap[K, V]] = HashMap.newBuilder(tableLength, loadFactor) + } + + private[collection] final class Node[K, V](_key: K, _hash: Int, private[this] var _value: V, private[this] var _next: Node[K, V]) { + def key: K = _key + def hash: Int = _hash + def value: V = _value + def value_= (v: V): Unit = _value = v + def next: Node[K, V] = _next + def next_= (n: Node[K, V]): Unit = _next = n + + @tailrec + def findNode(k: K, h: Int): Node[K, V] = + if(h == _hash && k == _key) this + else if((_next eq null) || (_hash > h)) null + else _next.findNode(k, h) + + @tailrec + def foreach[U](f: ((K, V)) => U): Unit = { + f((_key, _value)) + if(_next ne null) _next.foreach(f) + } + + @tailrec + def foreachEntry[U](f: (K, V) => U): Unit = { + f(_key, _value) + if(_next ne null) _next.foreachEntry(f) + } + + override def toString = s"Node($key, $value, $hash) -> $next" + } +} diff --git a/library/src/scala/collection/mutable/HashSet.scala b/library/src/scala/collection/mutable/HashSet.scala new file mode 100644 index 000000000000..9f0abbfa6cfd --- /dev/null +++ b/library/src/scala/collection/mutable/HashSet.scala @@ -0,0 +1,456 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.annotation.tailrec +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializationProxy +import scala.util.hashing.MurmurHash3 + +/** This class implements mutable sets using a hashtable. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * section on `Hash Tables` for more information. + * + * @define Coll `mutable.HashSet` + * @define coll mutable hash set + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +final class HashSet[A](initialCapacity: Int, loadFactor: Double) + extends AbstractSet[A] + with SetOps[A, HashSet, HashSet[A]] + with StrictOptimizedIterableOps[A, HashSet, HashSet[A]] + with IterableFactoryDefaults[A, HashSet] + with Serializable { + + def this() = this(HashSet.defaultInitialCapacity, HashSet.defaultLoadFactor) + + import HashSet.Node + + /* The Hashset class holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains elements whose hash-index is i. + * - Every bucket is sorted in ascendent hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ + /** The actual hash table. */ + private[this] var table = new Array[Node[A]](tableSizeFor(initialCapacity)) + + /** The next size value at which to resize (capacity * load factor). */ + private[this] var threshold: Int = newThreshold(table.length) + + private[this] var contentSize = 0 + + override def size: Int = contentSize + + /** Performs the inverse operation of improveHash. In this case, it happens to be identical to improveHash*/ + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) + + /** Computes the improved hash of an original (`any.##`) hash. */ + private[this] def improveHash(originalHash: Int): Int = { + // Improve the hash by xoring the high 16 bits into the low 16 bits just in case entropy is skewed towards the + // high-value bits. We only use the lowest bits to determine the hash bucket. This is the same improvement + // algorithm as in java.util.HashMap. + originalHash ^ (originalHash >>> 16) + } + + /** Computes the improved hash of this element */ + @`inline` private[this] def computeHash(o: A): Int = improveHash(o.##) + + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + override def contains(elem: A): Boolean = findNode(elem) ne null + + @`inline` private[this] def findNode(elem: A): Node[A] = { + val hash = computeHash(elem) + table(index(hash)) match { + case null => null + case nd => nd.findNode(elem, hash) + } + } + + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / loadFactor).toInt) + if(target > table.length) growTable(target) + } + + override def add(elem: A) : Boolean = { + if(contentSize + 1 >= threshold) growTable(table.length * 2) + addElem(elem, computeHash(elem)) + } + + override def addAll(xs: IterableOnce[A]): this.type = { + sizeHint(xs, delta = 0) + xs match { + case hs: immutable.HashSet[A] => + hs.foreachWithHash((k, h) => addElem(k, improveHash(h))) + this + case hs: mutable.HashSet[A] => + val iter = hs.nodeIterator + while (iter.hasNext) { + val next = iter.next() + addElem(next.key, next.hash) + } + this + case lhs: mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator + while (iter.hasNext) { + val next = iter.next() + addElem(next.key, next.hash) + } + this + case _ => super.addAll(xs) + } + } + + override def subtractAll(xs: IterableOnce[A]): this.type = { + if (size == 0) { + return this + } + + xs match { + case hs: immutable.HashSet[A] => + hs.foreachWithHashWhile { (k, h) => + remove(k, improveHash(h)) + size > 0 + } + this + case hs: mutable.HashSet[A] => + val iter = hs.nodeIterator + while (iter.hasNext) { + val next = iter.next() + remove(next.key, next.hash) + if (size == 0) return this + } + this + case lhs: mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator + while (iter.hasNext) { + val next = iter.next() + remove(next.key, next.hash) + if (size == 0) return this + } + this + case _ => super.subtractAll(xs) + } + } + + /** Adds an element to this set + * @param elem element to add + * @param hash the **improved** hash of `elem` (see computeHash) + */ + private[this] def addElem(elem: A, hash: Int) : Boolean = { + val idx = index(hash) + table(idx) match { + case null => + table(idx) = new Node(elem, hash, null) + case old => + var prev: Node[A] = null + var n = old + while((n ne null) && n.hash <= hash) { + if(n.hash == hash && elem == n.key) return false + prev = n + n = n.next + } + if(prev eq null) + table(idx) = new Node(elem, hash, old) + else + prev.next = new Node(elem, hash, prev.next) + } + contentSize += 1 + true + } + + private[this] def remove(elem: A, hash: Int): Boolean = { + val idx = index(hash) + table(idx) match { + case null => false + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + contentSize -= 1 + true + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while((next ne null) && next.hash <= hash) { + if(next.hash == hash && next.key == elem) { + prev.next = next.next + contentSize -= 1 + return true + } + prev = next + next = next.next + } + false + } + } + + override def remove(elem: A) : Boolean = remove(elem, computeHash(elem)) + + private[this] abstract class HashSetIterator[B] extends AbstractIterator[B] { + private[this] var i = 0 + private[this] var node: Node[A] = null + private[this] val len = table.length + + protected[this] def extract(nd: Node[A]): B + + def hasNext: Boolean = { + if(node ne null) true + else { + while(i < len) { + val n = table(i) + i += 1 + if(n ne null) { node = n; return true } + } + false + } + } + + def next(): B = + if(!hasNext) Iterator.empty.next() + else { + val r = extract(node) + node = node.next + r + } + } + + override def iterator: Iterator[A] = new HashSetIterator[A] { + override protected[this] def extract(nd: Node[A]): A = nd.key + } + + /** Returns an iterator over the nodes stored in this HashSet */ + private[collection] def nodeIterator: Iterator[Node[A]] = new HashSetIterator[Node[A]] { + override protected[this] def extract(nd: Node[A]): Node[A] = nd + } + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + import convert.impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntTableStepper[Node[A]] (size, table, _.next, _.key.asInstanceOf[Int], 0, table.length) + case StepperShape.LongShape => new LongTableStepper[Node[A]] (size, table, _.next, _.key.asInstanceOf[Long], 0, table.length) + case StepperShape.DoubleShape => new DoubleTableStepper[Node[A]](size, table, _.next, _.key.asInstanceOf[Double], 0, table.length) + case _ => shape.parUnbox(new AnyTableStepper[A, Node[A]](size, table, _.next, _.key, 0, table.length)) + } + s.asInstanceOf[S with EfficientSplit] + } + + private[this] def growTable(newlen: Int) = { + var oldlen = table.length + threshold = newThreshold(newlen) + if(size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow: Node[A] = new Node(null.asInstanceOf[A], 0, null) + val preHigh: Node[A] = new Node(null.asInstanceOf[A], 0, null) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while(oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if(old ne null) { + preLow.next = null + preHigh.next = null + var lastLow: Node[A] = preLow + var lastHigh: Node[A] = preHigh + var n = old + while(n ne null) { + val next = n.next + if((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if(old ne preLow.next) table(i) = preLow.next + if(preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + override def filterInPlace(p: A => Boolean): this.type = { + if (nonEmpty) { + var bucket = 0 + + while (bucket < table.length) { + var head = table(bucket) + + while ((head ne null) && !p(head.key)) { + head = head.next + contentSize -= 1 + } + + if (head ne null) { + var prev = head + var next = head.next + + while (next ne null) { + if (p(next.key)) { + prev = next + } else { + prev.next = next.next + contentSize -= 1 + } + next = next.next + } + } + + table(bucket) = head + bucket += 1 + } + } + this + } + + /* + private[mutable] def checkTable(): Unit = { + var i = 0 + var count = 0 + var prev: Node[A] = null + while(i < table.length) { + var n = table(i) + prev = null + while(n != null) { + count += 1 + assert(index(n.hash) == i) + if(prev ne null) assert(prev.hash <= n.hash) + prev = n + n = n.next + } + i += 1 + } + assert(contentSize == count) + } + */ + + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * loadFactor).toInt + + def clear(): Unit = { + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 + } + + override def iterableFactory: IterableFactory[HashSet] = HashSet + + @`inline` def addOne(elem: A): this.type = { add(elem); this } + + @`inline` def subtractOne(elem: A): this.type = { remove(elem); this } + + override def knownSize: Int = size + + override def isEmpty: Boolean = size == 0 + + override def foreach[U](f: A => U): Unit = { + val len = table.length + var i = 0 + while(i < len) { + val n = table(i) + if(n ne null) n.foreach(f) + i += 1 + } + } + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(new HashSet.DeserializationFactory[A](table.length, loadFactor), this) + + override protected[this] def className = "HashSet" + + override def hashCode: Int = { + val setIterator = this.iterator + val hashIterator: Iterator[Any] = + if (setIterator.isEmpty) setIterator + else new HashSetIterator[Any] { + var hash: Int = 0 + override def hashCode: Int = hash + override protected[this] def extract(nd: Node[A]): Any = { + hash = unimproveHash(nd.hash) + this + } + } + MurmurHash3.unorderedHash(hashIterator, MurmurHash3.setSeed) + } +} + +/** + * $factoryInfo + * @define Coll `mutable.HashSet` + * @define coll mutable hash set + */ +@SerialVersionUID(3L) +object HashSet extends IterableFactory[HashSet] { + + def from[B](it: scala.collection.IterableOnce[B]): HashSet[B] = { + val k = it.knownSize + val cap = if(k > 0) ((k + 1).toDouble / defaultLoadFactor).toInt else defaultInitialCapacity + new HashSet[B](cap, defaultLoadFactor) ++= it + } + + def empty[A]: HashSet[A] = new HashSet[A] + + def newBuilder[A]: Builder[A, HashSet[A]] = newBuilder(defaultInitialCapacity, defaultLoadFactor) + + def newBuilder[A](initialCapacity: Int, loadFactor: Double): Builder[A, HashSet[A]] = + new GrowableBuilder[A, HashSet[A]](new HashSet[A](initialCapacity, loadFactor)) { + override def sizeHint(size: Int) = elems.sizeHint(size) + } + + /** The default load factor for the hash table */ + final def defaultLoadFactor: Double = 0.75 + + /** The default initial capacity for the hash table */ + final def defaultInitialCapacity: Int = 16 + + @SerialVersionUID(3L) + private final class DeserializationFactory[A](val tableLength: Int, val loadFactor: Double) extends Factory[A, HashSet[A]] with Serializable { + def fromSpecific(it: IterableOnce[A]): HashSet[A] = new HashSet[A](tableLength, loadFactor) ++= it + def newBuilder: Builder[A, HashSet[A]] = HashSet.newBuilder(tableLength, loadFactor) + } + + private[collection] final class Node[K](_key: K, _hash: Int, private[this] var _next: Node[K]) { + def key: K = _key + def hash: Int = _hash + def next: Node[K] = _next + def next_= (n: Node[K]): Unit = _next = n + + @tailrec + def findNode(k: K, h: Int): Node[K] = + if(h == _hash && k == _key) this + else if((_next eq null) || (_hash > h)) null + else _next.findNode(k, h) + + @tailrec + def foreach[U](f: K => U): Unit = { + f(_key) + if(_next ne null) _next.foreach(f) + } + + override def toString = s"Node($key, $hash) -> $next" + } +} diff --git a/library/src/scala/collection/mutable/HashTable.scala b/library/src/scala/collection/mutable/HashTable.scala new file mode 100644 index 000000000000..d58f6e01b7ac --- /dev/null +++ b/library/src/scala/collection/mutable/HashTable.scala @@ -0,0 +1,417 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.mutable + +import collection.{AbstractIterator, Iterator} + +import java.lang.Integer.{numberOfLeadingZeros, rotateRight} +import scala.util.hashing.byteswap32 + +import java.lang.Integer + +/** This class can be used to construct data structures that are based + * on hashtables. Class `HashTable[A]` implements a hashtable + * that maps keys of type `A` to values of the fully abstract + * member type `Entry`. Classes that make use of `HashTable` + * have to provide an implementation for `Entry`. + * + * There are mainly two parameters that affect the performance of a hashtable: + * the initial size and the load factor. The size + * refers to the number of buckets in the hashtable, and the load + * factor is a measure of how full the hashtable is allowed to get before + * its size is automatically doubled. Both parameters may be changed by + * overriding the corresponding values in class `HashTable`. + * + * @tparam A type of the elements contained in this hash table. + */ +// Not used in the standard library, but used in scala-parallel-collections +private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] { + // Replacing Entry type parameter by abstract type member here allows to not expose to public + // implementation-specific entry classes such as `DefaultEntry` or `LinkedEntry`. + // However, I'm afraid it's too late now for such breaking change. + import HashTable._ + + protected var _loadFactor = defaultLoadFactor + + /** The actual hash table. + */ + protected[collection] var table: Array[HashEntry[A, Entry]] = new Array(initialCapacity) + + /** The number of mappings contained in this hash table. + */ + protected[collection] var tableSize: Int = 0 + + final def size: Int = tableSize + + /** The next size value at which to resize (capacity * load factor). + */ + protected[collection] var threshold: Int = initialThreshold(_loadFactor) + + /** The array keeping track of the number of elements in 32 element blocks. + */ + protected var sizemap: Array[Int] = null + + protected var seedvalue: Int = tableSizeSeed + + protected def tableSizeSeed = Integer.bitCount(table.length - 1) + + /** The initial size of the hash table. + */ + protected def initialSize: Int = 16 + + /** The initial threshold. + */ + private def initialThreshold(_loadFactor: Int): Int = newThreshold(_loadFactor, initialCapacity) + + private def initialCapacity = capacity(initialSize) + + private def lastPopulatedIndex = { + var idx = table.length - 1 + while (table(idx) == null && idx > 0) + idx -= 1 + + idx + } + + /** + * Initializes the collection from the input stream. `readEntry` will be called for each + * entry to be read from the input stream. + */ + private[collection] def init(in: java.io.ObjectInputStream, readEntry: => Entry): Unit = { + _loadFactor = in.readInt() + assert(_loadFactor > 0) + + val size = in.readInt() + tableSize = 0 + assert(size >= 0) + + seedvalue = in.readInt() + + val smDefined = in.readBoolean() + + table = new Array(capacity(sizeForThreshold(_loadFactor, size))) + threshold = newThreshold(_loadFactor, table.length) + + if (smDefined) sizeMapInit(table.length) else sizemap = null + + var index = 0 + while (index < size) { + addEntry(readEntry) + index += 1 + } + } + + /** + * Serializes the collection to the output stream by saving the load factor, collection + * size and collection entries. `writeEntry` is responsible for writing an entry to the stream. + * + * `foreachEntry` determines the order in which the key/value pairs are saved to the stream. To + * deserialize, `init` should be used. + */ + private[collection] def serializeTo(out: java.io.ObjectOutputStream, writeEntry: Entry => Unit): Unit = { + out.writeInt(_loadFactor) + out.writeInt(tableSize) + out.writeInt(seedvalue) + out.writeBoolean(isSizeMapDefined) + + foreachEntry(writeEntry) + } + + /** Find entry with given key in table, null if not found. + */ + final def findEntry(key: A): Entry = + findEntry0(key, index(elemHashCode(key))) + + protected[collection] final def findEntry0(key: A, h: Int): Entry = { + var e = table(h).asInstanceOf[Entry] + while (e != null && !elemEquals(e.key, key)) e = e.next + e + } + + /** Add entry to table + * pre: no entry with same key exists + */ + protected[collection] final def addEntry(e: Entry): Unit = { + addEntry0(e, index(elemHashCode(e.key))) + } + + protected[collection] final def addEntry0(e: Entry, h: Int): Unit = { + e.next = table(h).asInstanceOf[Entry] + table(h) = e + tableSize = tableSize + 1 + nnSizeMapAdd(h) + if (tableSize > threshold) + resize(2 * table.length) + } + + /** Find entry with given key in table, or add new one if not found. + * May be somewhat faster then `findEntry`/`addEntry` pair as it + * computes entry's hash index only once. + * Returns entry found in table or null. + * New entries are created by calling `createNewEntry` method. + */ + def findOrAddEntry(key: A, value: B): Entry = { + val h = index(elemHashCode(key)) + val e = findEntry0(key, h) + if (e ne null) e else { addEntry0(createNewEntry(key, value), h); null } + } + + /** Creates new entry to be immediately inserted into the hashtable. + * This method is guaranteed to be called only once and in case that the entry + * will be added. In other words, an implementation may be side-effecting. + */ + def createNewEntry(key: A, value: B): Entry + + /** Remove entry from table if present. + */ + final def removeEntry(key: A) : Entry = { + removeEntry0(key, index(elemHashCode(key))) + } + /** Remove entry from table if present. + */ + private[collection] final def removeEntry0(key: A, h: Int) : Entry = { + var e = table(h).asInstanceOf[Entry] + if (e != null) { + if (elemEquals(e.key, key)) { + table(h) = e.next + tableSize = tableSize - 1 + nnSizeMapRemove(h) + e.next = null + return e + } else { + var e1 = e.next + while (e1 != null && !elemEquals(e1.key, key)) { + e = e1 + e1 = e1.next + } + if (e1 != null) { + e.next = e1.next + tableSize = tableSize - 1 + nnSizeMapRemove(h) + e1.next = null + return e1 + } + } + } + null + } + + /** An iterator returning all entries. + */ + def entriesIterator: Iterator[Entry] = new AbstractIterator[Entry] { + val iterTable = table + var idx = lastPopulatedIndex + var es = iterTable(idx) + + def hasNext = es != null + def next() = { + val res = es + es = es.next + while (es == null && idx > 0) { + idx = idx - 1 + es = iterTable(idx) + } + res.asInstanceOf[Entry] + } + } + + /** Avoid iterator for a 2x faster traversal. */ + def foreachEntry[U](f: Entry => U): Unit = { + val iterTable = table + var idx = lastPopulatedIndex + var es = iterTable(idx) + + while (es != null) { + val next = es.next // Cache next in case f removes es. + f(es.asInstanceOf[Entry]) + es = next + + while (es == null && idx > 0) { + idx -= 1 + es = iterTable(idx) + } + } + } + + /** Remove all entries from table + */ + def clearTable(): Unit = { + var i = table.length - 1 + while (i >= 0) { table(i) = null; i = i - 1 } + tableSize = 0 + nnSizeMapReset(0) + } + + private def resize(newSize: Int): Unit = { + val oldTable = table + table = new Array(newSize) + nnSizeMapReset(table.length) + var i = oldTable.length - 1 + while (i >= 0) { + var e = oldTable(i) + while (e != null) { + val h = index(elemHashCode(e.key)) + val e1 = e.next + e.next = table(h).asInstanceOf[Entry] + table(h) = e + e = e1 + nnSizeMapAdd(h) + } + i = i - 1 + } + threshold = newThreshold(_loadFactor, newSize) + } + + /* Size map handling code */ + + /* + * The following three sizeMap* functions (Add, Remove, Reset) + * are used to update the size map of the hash table. + * + * The size map logically divides the hash table into `sizeMapBucketSize` element buckets + * by keeping an integer entry for each such bucket. Each integer entry simply denotes + * the number of elements in the corresponding bucket. + * Best understood through an example, see: + * table = [/, 1, /, 6, 90, /, -3, 5] (8 entries) + * sizemap = [ 2 | 3 ] (2 entries) + * where sizeMapBucketSize == 4. + * + * By default the size map is not initialized, so these methods don't do anything, thus, + * their impact on hash table performance is negligible. However, if the hash table + * is converted into a parallel hash table, the size map is initialized, as it will be needed + * there. + */ + protected final def nnSizeMapAdd(h: Int) = if (sizemap ne null) { + sizemap(h >> sizeMapBucketBitSize) += 1 + } + + protected final def nnSizeMapRemove(h: Int) = if (sizemap ne null) { + sizemap(h >> sizeMapBucketBitSize) -= 1 + } + + protected final def nnSizeMapReset(tableLength: Int) = if (sizemap ne null) { + val nsize = calcSizeMapSize(tableLength) + if (sizemap.length != nsize) sizemap = new Array[Int](nsize) + else java.util.Arrays.fill(sizemap, 0) + } + + private[collection] final def totalSizeMapBuckets = if (sizeMapBucketSize < table.length) 1 else table.length / sizeMapBucketSize + + protected final def calcSizeMapSize(tableLength: Int) = (tableLength >> sizeMapBucketBitSize) + 1 + + // discards the previous sizemap and only allocates a new one + protected def sizeMapInit(tableLength: Int): Unit = { + sizemap = new Array[Int](calcSizeMapSize(tableLength)) + } + + // discards the previous sizemap and populates the new one + protected final def sizeMapInitAndRebuild() = { + sizeMapInit(table.length) + + // go through the buckets, count elements + var tableidx = 0 + var bucketidx = 0 + val tbl = table + var tableuntil = 0 + if (tbl.length < sizeMapBucketSize) tableuntil = tbl.length else tableuntil = sizeMapBucketSize + val totalbuckets = totalSizeMapBuckets + while (bucketidx < totalbuckets) { + var currbucketsize = 0 + while (tableidx < tableuntil) { + var e = tbl(tableidx) + while (e ne null) { + currbucketsize += 1 + e = e.next + } + tableidx += 1 + } + sizemap(bucketidx) = currbucketsize + tableuntil += sizeMapBucketSize + bucketidx += 1 + } + } + + private[collection] def printSizeMap() = { + println(sizemap.to(collection.immutable.List)) + } + + protected final def sizeMapDisable() = sizemap = null + + protected final def isSizeMapDefined = sizemap ne null + + // override to automatically initialize the size map + protected def alwaysInitSizeMap = false + + /* End of size map handling code */ + + protected def elemEquals(key1: A, key2: A): Boolean = (key1 == key2) + + /** + * Note: we take the most significant bits of the hashcode, not the lower ones + * this is of crucial importance when populating the table in parallel + */ + protected[collection] final def index(hcode: Int): Int = { + val ones = table.length - 1 + val exponent = Integer.numberOfLeadingZeros(ones) + (improve(hcode, seedvalue) >>> exponent) & ones + } +} + +private[collection] object HashTable { + /** The load factor for the hash table (in 0.001 step). + */ + private[collection] final def defaultLoadFactor: Int = 750 // corresponds to 75% + private[collection] final def loadFactorDenum = 1000 // should be loadFactorDenom, but changing that isn't binary compatible + + private[collection] final def newThreshold(_loadFactor: Int, size: Int) = ((size.toLong * _loadFactor) / loadFactorDenum).toInt + + private[collection] final def sizeForThreshold(_loadFactor: Int, thr: Int) = ((thr.toLong * loadFactorDenum) / _loadFactor).toInt + + private[collection] final def capacity(expectedSize: Int) = nextPositivePowerOfTwo(expectedSize) + + trait HashUtils[KeyType] { + protected final def sizeMapBucketBitSize = 5 + // so that: + protected final def sizeMapBucketSize = 1 << sizeMapBucketBitSize + + protected[collection] def elemHashCode(key: KeyType) = key.## + + /** + * Defer to a high-quality hash in [[scala.util.hashing]]. + * The goal is to distribute across bins as well as possible even if a hash code has low entropy at some bits. + *

+ * OLD VERSION - quick, but bad for sequence 0-10000 - little entropy in higher bits - since 2003 + * {{{ + * var h: Int = hcode + ~(hcode << 9) + * h = h ^ (h >>> 14) + * h = h + (h << 4) + * h ^ (h >>> 10) + * }}} + * the rest of the computation is due to SI-5293 + */ + protected final def improve(hcode: Int, seed: Int): Int = rotateRight(byteswap32(hcode), seed) + } + + /** + * Returns a power of two >= `target`. + */ + private[collection] def nextPositivePowerOfTwo(target: Int): Int = 1 << -numberOfLeadingZeros(target - 1) +} + +/** Class used internally. + */ +private[collection] trait HashEntry[A, E <: HashEntry[A, E]] { + val key: A + var next: E = _ +} diff --git a/library/src/scala/collection/mutable/ImmutableBuilder.scala b/library/src/scala/collection/mutable/ImmutableBuilder.scala new file mode 100644 index 000000000000..3907cfd55305 --- /dev/null +++ b/library/src/scala/collection/mutable/ImmutableBuilder.scala @@ -0,0 +1,31 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + + +/** + * Reusable builder for immutable collections + */ +abstract class ImmutableBuilder[-A, C <: IterableOnce[_]](empty: C) + extends ReusableBuilder[A, C] { + + protected var elems: C = empty + + def clear(): Unit = { elems = empty } + + def result(): C = elems + + override def knownSize: Int = elems.knownSize +} diff --git a/library/src/scala/collection/mutable/IndexedSeq.scala b/library/src/scala/collection/mutable/IndexedSeq.scala new file mode 100644 index 000000000000..464bc00d45db --- /dev/null +++ b/library/src/scala/collection/mutable/IndexedSeq.scala @@ -0,0 +1,83 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +trait IndexedSeq[T] extends Seq[T] + with scala.collection.IndexedSeq[T] + with IndexedSeqOps[T, IndexedSeq, IndexedSeq[T]] + with IterableFactoryDefaults[T, IndexedSeq] { + + override def iterableFactory: SeqFactory[IndexedSeq] = IndexedSeq +} + +@SerialVersionUID(3L) +object IndexedSeq extends SeqFactory.Delegate[IndexedSeq](ArrayBuffer) + +trait IndexedSeqOps[A, +CC[_], +C <: AnyRef] + extends scala.collection.IndexedSeqOps[A, CC, C] + with SeqOps[A, CC, C] { + + /** Modifies this $coll by applying a function to all elements of this $coll. + * + * @param f the function to apply to each element. + * @return this $coll modified by replacing all elements with the + * result of applying the given function `f` to each element + * of this $coll. + */ + def mapInPlace(f: A => A): this.type = { + var i = 0 + val siz = size + while (i < siz) { this(i) = f(this(i)); i += 1 } + this + } + + /** Sorts this $coll in place according to an Ordering. + * + * @see [[scala.collection.SeqOps.sorted]] + * @param ord the ordering to be used to compare elements. + * @return modified input $coll sorted according to the ordering `ord`. + */ + def sortInPlace[B >: A]()(implicit ord: Ordering[B]): this.type = { + val len = this.length + if (len > 1) { + val arr = new Array[AnyRef](len) + var i = 0 + for (x <- this) { + arr(i) = x.asInstanceOf[AnyRef] + i += 1 + } + java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]]) + i = 0 + while (i < arr.length) { + update(i, arr(i).asInstanceOf[A]) + i += 1 + } + } + this + } + + /** Sorts this $coll in place according to a comparison function. + * + * @see [[scala.collection.SeqOps.sortWith]] + */ + def sortInPlaceWith(lt: (A, A) => Boolean): this.type = sortInPlace()(Ordering.fromLessThan(lt)) + + /** Sorts this $coll in place according to the Ordering which results from transforming + * an implicitly given Ordering with a transformation function. + * + * @see [[scala.collection.SeqOps.sortBy]] + */ + def sortInPlaceBy[B](f: A => B)(implicit ord: Ordering[B]): this.type = sortInPlace()(ord on f) + +} diff --git a/library/src/scala/collection/mutable/Iterable.scala b/library/src/scala/collection/mutable/Iterable.scala new file mode 100644 index 000000000000..c84d0e6ec675 --- /dev/null +++ b/library/src/scala/collection/mutable/Iterable.scala @@ -0,0 +1,34 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + +import scala.collection.{IterableFactory, IterableFactoryDefaults} + +trait Iterable[A] + extends collection.Iterable[A] + with collection.IterableOps[A, Iterable, Iterable[A]] + with IterableFactoryDefaults[A, Iterable] { + + override def iterableFactory: IterableFactory[Iterable] = Iterable +} + +/** + * $factoryInfo + * @define coll mutable collection + * @define Coll `mutable.Iterable` + */ +@SerialVersionUID(3L) +object Iterable extends IterableFactory.Delegate[Iterable](ArrayBuffer) + +/** Explicit instantiation of the `Iterable` trait to reduce class file size in subclasses. */ +abstract class AbstractIterable[A] extends scala.collection.AbstractIterable[A] with Iterable[A] diff --git a/library/src/scala/collection/mutable/LinkedHashMap.scala b/library/src/scala/collection/mutable/LinkedHashMap.scala new file mode 100644 index 000000000000..d529fee42596 --- /dev/null +++ b/library/src/scala/collection/mutable/LinkedHashMap.scala @@ -0,0 +1,509 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import scala.annotation.{nowarn, tailrec} +import scala.collection.generic.DefaultSerializable +import scala.util.hashing.MurmurHash3 + + +/** This class implements mutable maps using a hashtable. + * The iterator and all traversal methods of this class visit elements in the order they were inserted. + * + * @tparam K the type of the keys contained in this hash map. + * @tparam V the type of the values assigned to keys in this hash map. + * + * @define Coll `LinkedHashMap` + * @define coll linked hash map + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define orderDependent + * @define orderDependentFold + */ +@deprecatedInheritance("LinkedHashMap will be made final; use .withDefault for the common use case of computing a default value", "2.13.11") +class LinkedHashMap[K, V] + extends AbstractMap[K, V] + with SeqMap[K, V] + with MapOps[K, V, LinkedHashMap, LinkedHashMap[K, V]] + with StrictOptimizedIterableOps[(K, V), Iterable, LinkedHashMap[K, V]] + with StrictOptimizedMapOps[K, V, LinkedHashMap, LinkedHashMap[K, V]] + with MapFactoryDefaults[K, V, LinkedHashMap, Iterable] + with DefaultSerializable { + + override def mapFactory: MapFactory[LinkedHashMap] = LinkedHashMap + + // stepper / keyStepper / valueStepper are not overridden to use XTableStepper because that stepper + // would not return the elements in insertion order + + private[collection] type Entry = LinkedHashMap.LinkedEntry[K, V] + + private[collection] def _firstEntry: Entry = firstEntry + + protected var firstEntry: Entry = null + + protected var lastEntry: Entry = null + + /* Uses the same implementation as mutable.HashMap. The hashtable holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. + * - Every bucket is sorted in ascendant hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ + private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) + + private[this] var threshold: Int = newThreshold(table.length) + + private[this] var contentSize = 0 + + override def last: (K, V) = + if (size > 0) (lastEntry.key, lastEntry.value) + else throw new NoSuchElementException("Cannot call .last on empty LinkedHashMap") + + override def lastOption: Option[(K, V)] = + if (size > 0) Some((lastEntry.key, lastEntry.value)) + else None + + override def head: (K, V) = + if (size > 0) (firstEntry.key, firstEntry.value) + else throw new NoSuchElementException("Cannot call .head on empty LinkedHashMap") + + override def headOption: Option[(K, V)] = + if (size > 0) Some((firstEntry.key, firstEntry.value)) + else None + + override def size = contentSize + override def knownSize: Int = size + override def isEmpty: Boolean = size == 0 + + def get(key: K): Option[V] = { + val e = findEntry(key) + if (e == null) None + else Some(e.value) + } + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / LinkedHashMap.defaultLoadFactor).toInt) + if (target > table.length) growTable(target) + } + + override def contains(key: K): Boolean = { + if (getClass eq classOf[LinkedHashMap[_, _]]) + findEntry(key) != null + else + super.contains(key) // A subclass might override `get`, use the default implementation `contains`. + } + + override def put(key: K, value: V): Option[V] = put0(key, value, getOld = true) match { + case null => None + case sm => sm + } + + override def update(key: K, value: V): Unit = put0(key, value, getOld = false) + + override def remove(key: K): Option[V] = removeEntry0(key) match { + case null => None + case nd => Some(nd.value) + } + + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.getOrElse(key, default) + } else { + // .. but in the common case, we can avoid the Option boxing. + val nd = findEntry(key) + if (nd eq null) default else nd.value + } + } + + override def getOrElseUpdate(key: K, defaultValue: => V): V = { + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.getOrElseUpdate(key, defaultValue) + } else { + val hash = computeHash(key) + val idx = index(hash) + val nd = table(idx) match { + case null => null + case nd => nd.findEntry(key, hash) + } + if (nd != null) nd.value + else { + val table0 = table + val default = defaultValue + if (contentSize + 1 >= threshold) growTable(table.length * 2) + // Avoid recomputing index if the `defaultValue()` or new element hasn't triggered a table resize. + val newIdx = if (table0 eq table) idx else index(hash) + put0(key, default, getOld = false, hash, newIdx) + default + } + } + } + + private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem)) + + /** Removes a key from this map if it exists + * + * @param elem the element to remove + * @param hash the **improved** hashcode of `element` (see computeHash) + * @return the node that contained element if it was present, otherwise null + */ + private[this] def removeEntry0(elem: K, hash: Int): Entry = { + val idx = index(hash) + table(idx) match { + case null => null + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + deleteEntry(nd) + contentSize -= 1 + nd + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while ((next ne null) && next.hash <= hash) { + if (next.hash == hash && next.key == elem) { + prev.next = next.next + deleteEntry(next) + contentSize -= 1 + return next + } + prev = next + next = next.next + } + null + } + } + + /** Computes the improved hash of an original (`any.##`) hash. */ + @`inline` private[this] def improveHash(originalHash: Int): Int = { + originalHash ^ (originalHash >>> 16) + } + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) + + /** Computes the improved hash of this key */ + @`inline` private[this] def computeHash(o: K): Int = improveHash(o.##) + + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + @`inline` private[this] def findEntry(key: K): Entry = { + val hash = computeHash(key) + table(index(hash)) match { + case null => null + case nd => nd.findEntry(key, hash) + } + } + + def addOne(kv: (K, V)): this.type = { + put(kv._1, kv._2) + this + } + + def subtractOne(key: K): this.type = { + remove(key) + this + } + + private[this] abstract class LinkedHashMapIterator[T] extends AbstractIterator[T] { + private[this] var cur = firstEntry + def extract(nd: Entry): T + def hasNext: Boolean = cur ne null + def next(): T = + if (hasNext) { val r = extract(cur); cur = cur.later; r } + else Iterator.empty.next() + } + + def iterator: Iterator[(K, V)] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[(K, V)] { + def extract(nd: Entry): (K, V) = (nd.key, nd.value) + } + + protected class LinkedKeySet extends KeySet { + override def iterableFactory: IterableFactory[collection.Set] = LinkedHashSet + } + + override def keySet: collection.Set[K] = new LinkedKeySet + + override def keysIterator: Iterator[K] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[K] { + def extract(nd: Entry): K = nd.key + } + + private[collection] def entryIterator: Iterator[Entry] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[Entry] { + def extract(nd: Entry): Entry = nd + } + + + // Override updateWith for performance, so we can do the update while hashing + // the input key only once and performing one lookup into the hash table + override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.updateWith(key)(remappingFunction) + } else { + val hash = computeHash(key) + val indexedHash = index(hash) + + var foundEntry: Entry = null + var previousEntry: Entry = null + table(indexedHash) match { + case null => + case nd => + @tailrec + def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { + if (h == nd.hash && k == nd.key) { + previousEntry = prev + foundEntry = nd + } + else if ((nd.next eq null) || (nd.hash > h)) () + else findEntry(nd, nd.next, k, h) + } + + findEntry(null, nd, key, hash) + } + + val previousValue = foundEntry match { + case null => None + case nd => Some(nd.value) + } + + val nextValue = remappingFunction(previousValue) + + (previousValue, nextValue) match { + case (None, None) => // do nothing + + case (Some(_), None) => + if (previousEntry != null) previousEntry.next = foundEntry.next + else table(indexedHash) = foundEntry.next + deleteEntry(foundEntry) + contentSize -= 1 + + case (None, Some(value)) => + val newIndexedHash = + if (contentSize + 1 >= threshold) { + growTable(table.length * 2) + index(hash) + } else indexedHash + put0(key, value, getOld = false, hash, newIndexedHash) + + case (Some(_), Some(newValue)) => foundEntry.value = newValue + } + nextValue + } + } + + override def valuesIterator: Iterator[V] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[V] { + def extract(nd: Entry): V = nd.value + } + + + override def foreach[U](f: ((K, V)) => U): Unit = { + var cur = firstEntry + while (cur ne null) { + f((cur.key, cur.value)) + cur = cur.later + } + } + + override def foreachEntry[U](f: (K, V) => U): Unit = { + var cur = firstEntry + while (cur ne null) { + f(cur.key, cur.value) + cur = cur.later + } + } + + override def clear(): Unit = { + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 + firstEntry = null + lastEntry = null + } + + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashMap.defaultLoadFactor).toInt + + /*create a new entry. If table is empty(firstEntry is null), then the + * new entry will be the firstEntry. If not, just set the new entry to + * be the lastEntry. + * */ + private[this] def createNewEntry(key: K, hash: Int, value: V): Entry = { + val e = new Entry(key, hash, value) + if (firstEntry eq null) firstEntry = e + else { + lastEntry.later = e + e.earlier = lastEntry + } + lastEntry = e + e + } + + /** Delete the entry from the LinkedHashMap, set the `earlier` and `later` pointers correctly */ + private[this] def deleteEntry(e: Entry): Unit = { + if (e.earlier eq null) firstEntry = e.later + else e.earlier.later = e.later + if (e.later eq null) lastEntry = e.earlier + else e.later.earlier = e.earlier + e.earlier = null + e.later = null + e.next = null + } + + private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + if (contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(key) + val idx = index(hash) + put0(key, value, getOld, hash, idx) + } + + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + table(idx) match { + case null => + table(idx) = createNewEntry(key, hash, value) + case old => + var prev: Entry = null + var n = old + while ((n ne null) && n.hash <= hash) { + if (n.hash == hash && key == n.key) { + val old = n.value + n.value = value + return if (getOld) Some(old) else null + } + prev = n + n = n.next + } + val nnode = createNewEntry(key, hash, value) + if (prev eq null) { + nnode.next = old + table(idx) = nnode + } else { + nnode.next = prev.next + prev.next = nnode + } + } + contentSize += 1 + null + } + + private[this] def growTable(newlen: Int): Unit = { + if (newlen < 0) + throw new RuntimeException(s"new hash table size $newlen exceeds maximum") + var oldlen = table.length + threshold = newThreshold(newlen) + if (size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow = new Entry(null.asInstanceOf[K], 0, null.asInstanceOf[V]) + val preHigh = new Entry(null.asInstanceOf[K], 0, null.asInstanceOf[V]) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while (oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if (old ne null) { + preLow.next = null + preHigh.next = null + var lastLow = preLow + var lastHigh = preHigh + var n = old + while (n ne null) { + val next = n.next + if ((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if (old ne preLow.next) table(i) = preLow.next + if (preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + override def hashCode: Int = { + if (isEmpty) MurmurHash3.emptyMapHash + else { + val tupleHashIterator = new LinkedHashMapIterator[Any] { + var hash: Int = 0 + override def hashCode: Int = hash + override def extract(nd: Entry): Any = { + hash = MurmurHash3.tuple2Hash(unimproveHash(nd.hash), nd.value.##) + this + } + } + MurmurHash3.unorderedHash(tupleHashIterator, MurmurHash3.mapSeed) + } + } + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "LinkedHashMap" +} + +/** $factoryInfo + * @define Coll `LinkedHashMap` + * @define coll linked hash map + */ +@SerialVersionUID(3L) +object LinkedHashMap extends MapFactory[LinkedHashMap] { + + def empty[K, V] = new LinkedHashMap[K, V] + + def from[K, V](it: collection.IterableOnce[(K, V)]) = { + val newlhm = empty[K, V] + newlhm.sizeHint(it, delta = 0) + newlhm.addAll(it) + newlhm + } + + def newBuilder[K, V]: GrowableBuilder[(K, V), LinkedHashMap[K, V]] = new GrowableBuilder(empty[K, V]) + + /** Class for the linked hash map entry, used internally. + */ + private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V) { + var earlier: LinkedEntry[K, V] = null + var later: LinkedEntry[K, V] = null + var next: LinkedEntry[K, V] = null + + @tailrec + final def findEntry(k: K, h: Int): LinkedEntry[K, V] = + if (h == hash && k == key) this + else if ((next eq null) || (hash > h)) null + else next.findEntry(k, h) + } + + /** The default load factor for the hash table */ + private[collection] final def defaultLoadFactor: Double = 0.75 + + /** The default initial capacity for the hash table */ + private[collection] final def defaultinitialSize: Int = 16 +} diff --git a/library/src/scala/collection/mutable/LinkedHashSet.scala b/library/src/scala/collection/mutable/LinkedHashSet.scala new file mode 100644 index 000000000000..1a189d607010 --- /dev/null +++ b/library/src/scala/collection/mutable/LinkedHashSet.scala @@ -0,0 +1,348 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import scala.annotation.{nowarn, tailrec} +import scala.collection.generic.DefaultSerializable +import scala.util.hashing.MurmurHash3 + +/** This class implements mutable sets using a hashtable. + * The iterator and all traversal methods of this class visit elements in the order they were inserted. + * + * @tparam A the type of the elements contained in this set. + * + * @define Coll `LinkedHashSet` + * @define coll linked hash set + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define orderDependent + * @define orderDependentFold + */ +@deprecatedInheritance("LinkedHashSet will be made final", "2.13.11") +class LinkedHashSet[A] + extends AbstractSet[A] + with SetOps[A, LinkedHashSet, LinkedHashSet[A]] + with StrictOptimizedIterableOps[A, LinkedHashSet, LinkedHashSet[A]] + with IterableFactoryDefaults[A, LinkedHashSet] + with DefaultSerializable { + + override def iterableFactory: IterableFactory[LinkedHashSet] = LinkedHashSet + + // stepper is not overridden to use XTableStepper because that stepper would not return the + // elements in insertion order + + /*private*/ type Entry = LinkedHashSet.Entry[A] + + protected var firstEntry: Entry = null + + protected var lastEntry: Entry = null + + /* Uses the same implementation as mutable.HashSet. The hashtable holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. + * - Every bucket is sorted in ascendant hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ + private[this] var table = new Array[Entry](tableSizeFor(LinkedHashSet.defaultinitialSize)) + + private[this] var threshold: Int = newThreshold(table.length) + + private[this] var contentSize = 0 + + override def last: A = + if (size > 0) lastEntry.key + else throw new NoSuchElementException("Cannot call .last on empty LinkedHashSet") + + override def lastOption: Option[A] = + if (size > 0) Some(lastEntry.key) + else None + + override def head: A = + if (size > 0) firstEntry.key + else throw new NoSuchElementException("Cannot call .head on empty LinkedHashSet") + + override def headOption: Option[A] = + if (size > 0) Some(firstEntry.key) + else None + + override def size: Int = contentSize + override def knownSize: Int = size + override def isEmpty: Boolean = size == 0 + + def contains(elem: A): Boolean = findEntry(elem) ne null + + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / LinkedHashSet.defaultLoadFactor).toInt) + if (target > table.length) growTable(target) + } + + override def add(elem: A): Boolean = { + if (contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(elem) + put0(elem, hash, index(hash)) + } + + def addOne(elem: A): this.type = { + add(elem) + this + } + + def subtractOne(elem: A): this.type = { + remove(elem) + this + } + + override def remove(elem: A): Boolean = remove0(elem, computeHash(elem)) + + private[this] abstract class LinkedHashSetIterator[T] extends AbstractIterator[T] { + private[this] var cur = firstEntry + def extract(nd: Entry): T + def hasNext: Boolean = cur ne null + def next(): T = + if (hasNext) { val r = extract(cur); cur = cur.later; r } + else Iterator.empty.next() + } + + def iterator: Iterator[A] = new LinkedHashSetIterator[A] { + override def extract(nd: Entry): A = nd.key + } + + private[collection] def entryIterator: Iterator[Entry] = new LinkedHashSetIterator[Entry] { + override def extract(nd: Entry): Entry = nd + } + + override def foreach[U](f: A => U): Unit = { + var cur = firstEntry + while (cur ne null) { + f(cur.key) + cur = cur.later + } + } + + override def clear(): Unit = { + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 + firstEntry = null + lastEntry = null + } + + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashSet.defaultLoadFactor).toInt + + @`inline` private[this] def improveHash(originalHash: Int): Int = { + originalHash ^ (originalHash >>> 16) + } + + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) + + /** Computes the improved hash of this key */ + @`inline` private[this] def computeHash(o: A): Int = improveHash(o.##) + + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + @`inline` private[this] def findEntry(key: A): Entry = { + val hash = computeHash(key) + table(index(hash)) match { + case null => null + case nd => nd.findEntry(key, hash) + } + } + + /*create a new entry. If table is empty(firstEntry is null), then the + * new entry will be the firstEntry. If not, just set the new entry to + * be the lastEntry. + * */ + private[this] def createNewEntry(key: A, hash: Int): Entry = { + val e = new Entry(key, hash) + if (firstEntry eq null) firstEntry = e + else { + lastEntry.later = e + e.earlier = lastEntry + } + lastEntry = e + e + } + + /** Delete the entry from the LinkedHashSet, set the `earlier` and `later` pointers correctly */ + private[this] def deleteEntry(e: Entry): Unit = { + if (e.earlier eq null) firstEntry = e.later + else e.earlier.later = e.later + if (e.later eq null) lastEntry = e.earlier + else e.later.earlier = e.earlier + e.earlier = null + e.later = null + e.next = null + } + + private[this] def put0(elem: A, hash: Int, idx: Int): Boolean = { + table(idx) match { + case null => + table(idx) = createNewEntry(elem, hash) + case old => + var prev: Entry = null + var n = old + while ((n ne null) && n.hash <= hash) { + if (n.hash == hash && elem == n.key) return false + prev = n + n = n.next + } + val nnode = createNewEntry(elem, hash) + if (prev eq null) { + nnode.next = old + table(idx) = nnode + } else { + nnode.next = prev.next + prev.next = nnode + } + } + contentSize += 1 + true + } + + private[this] def remove0(elem: A, hash: Int): Boolean = { + val idx = index(hash) + table(idx) match { + case null => false + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + deleteEntry(nd) + contentSize -= 1 + true + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while ((next ne null) && next.hash <= hash) { + if (next.hash == hash && next.key == elem) { + prev.next = next.next + deleteEntry(next) + contentSize -= 1 + return true + } + prev = next + next = next.next + } + false + } + } + + private[this] def growTable(newlen: Int): Unit = { + if (newlen < 0) + throw new RuntimeException(s"new hash table size $newlen exceeds maximum") + var oldlen = table.length + threshold = newThreshold(newlen) + if (size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow = new Entry(null.asInstanceOf[A], 0) + val preHigh = new Entry(null.asInstanceOf[A], 0) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while (oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if (old ne null) { + preLow.next = null + preHigh.next = null + var lastLow = preLow + var lastHigh = preHigh + var n = old + while (n ne null) { + val next = n.next + if ((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if (old ne preLow.next) table(i) = preLow.next + if (preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + override def hashCode: Int = { + val setHashIterator = + if (isEmpty) this.iterator + else { + new LinkedHashSetIterator[Any] { + var hash: Int = 0 + override def hashCode: Int = hash + override def extract(nd: Entry): Any = { + hash = unimproveHash(nd.hash) + this + } + } + } + MurmurHash3.unorderedHash(setHashIterator, MurmurHash3.setSeed) + } + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "LinkedHashSet" +} + +/** $factoryInfo + * @define Coll `LinkedHashSet` + * @define coll linked hash set + */ +@SerialVersionUID(3L) +object LinkedHashSet extends IterableFactory[LinkedHashSet] { + + override def empty[A]: LinkedHashSet[A] = new LinkedHashSet[A] + + def from[E](it: collection.IterableOnce[E]) = { + val newlhs = empty[E] + newlhs.sizeHint(it, delta = 0) + newlhs.addAll(it) + newlhs + } + + def newBuilder[A]: GrowableBuilder[A, LinkedHashSet[A]] = new GrowableBuilder(empty[A]) + + /** Class for the linked hash set entry, used internally. + */ + private[mutable] final class Entry[A](val key: A, val hash: Int) { + var earlier: Entry[A] = null + var later: Entry[A] = null + var next: Entry[A] = null + + @tailrec + final def findEntry(k: A, h: Int): Entry[A] = + if (h == hash && k == key) this + else if ((next eq null) || (hash > h)) null + else next.findEntry(k, h) + } + + /** The default load factor for the hash table */ + private[collection] final def defaultLoadFactor: Double = 0.75 + + /** The default initial capacity for the hash table */ + private[collection] final def defaultinitialSize: Int = 16 +} + diff --git a/library/src/scala/collection/mutable/ListBuffer.scala b/library/src/scala/collection/mutable/ListBuffer.scala new file mode 100644 index 000000000000..273704592abd --- /dev/null +++ b/library/src/scala/collection/mutable/ListBuffer.scala @@ -0,0 +1,420 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.annotation.{nowarn, tailrec} +import scala.collection.generic.CommonErrors +import scala.collection.immutable.{::, List, Nil} +import java.lang.{IllegalArgumentException, IndexOutOfBoundsException} + +import scala.collection.generic.DefaultSerializable +import scala.runtime.Statics.releaseFence + +/** A `Buffer` implementation backed by a list. It provides constant time + * prepend and append. Most other operations are linear. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#list-buffers "Scala's Collection Library overview"]] + * section on `List Buffers` for more information. + * + * @tparam A the type of this list buffer's elements. + * + * @define Coll `ListBuffer` + * @define coll list buffer + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(-8428291952499836345L) +class ListBuffer[A] + extends AbstractBuffer[A] + with SeqOps[A, ListBuffer, ListBuffer[A]] + with StrictOptimizedSeqOps[A, ListBuffer, ListBuffer[A]] + with ReusableBuilder[A, immutable.List[A]] + with IterableFactoryDefaults[A, ListBuffer] + with DefaultSerializable { + @transient private[this] var mutationCount: Int = 0 + + private var first: List[A] = Nil + private var last0: ::[A] = null // last element (`last0` just because the name `last` is already taken) + private[this] var aliased = false + private[this] var len = 0 + + private type Predecessor[A0] = ::[A0] /*| Null*/ + + def iterator: Iterator[A] = new MutationTracker.CheckedIterator(first.iterator, mutationCount) + + override def iterableFactory: SeqFactory[ListBuffer] = ListBuffer + + @throws[IndexOutOfBoundsException] + def apply(i: Int) = first.apply(i) + + def length = len + override def knownSize = len + + override def isEmpty: Boolean = len == 0 + + private def copyElems(): Unit = { + val buf = new ListBuffer[A].freshFrom(this) + first = buf.first + last0 = buf.last0 + aliased = false + } + + // we only call this before mutating things, so it's + // a good place to track mutations for the iterator + private def ensureUnaliased(): Unit = { + mutationCount += 1 + if (aliased) copyElems() + } + + // Avoids copying where possible. + override def toList: List[A] = { + aliased = nonEmpty + // We've accumulated a number of mutations to `List.tail` by this stage. + // Make sure they are visible to threads that the client of this ListBuffer might be about + // to share this List with. + releaseFence() + first + } + + def result(): immutable.List[A] = toList + + /** Prepends the elements of this buffer to a given list + * + * @param xs the list to which elements are prepended + */ + def prependToList(xs: List[A]): List[A] = { + if (isEmpty) xs + else { + ensureUnaliased() + last0.next = xs + toList + } + } + + def clear(): Unit = { + mutationCount += 1 + first = Nil + len = 0 + last0 = null + aliased = false + } + + final def addOne(elem: A): this.type = { + ensureUnaliased() + val last1 = new ::[A](elem, Nil) + if (len == 0) first = last1 else last0.next = last1 + last0 = last1 + len += 1 + this + } + + // MUST only be called on fresh instances + private def freshFrom(xs: IterableOnce[A]): this.type = { + val it = xs.iterator + if (it.hasNext) { + var len = 1 + var last0 = new ::[A](it.next(), Nil) + first = last0 + while (it.hasNext) { + val last1 = new ::[A](it.next(), Nil) + last0.next = last1 + last0 = last1 + len += 1 + } + // copy local vars into instance + this.len = len + this.last0 = last0 + } + this + } + + override final def addAll(xs: IterableOnce[A]): this.type = { + val it = xs.iterator + if (it.hasNext) { + val fresh = new ListBuffer[A].freshFrom(it) + ensureUnaliased() + if (len == 0) first = fresh.first + else last0.next = fresh.first + last0 = fresh.last0 + len += fresh.length + } + this + } + + override def subtractOne(elem: A): this.type = { + ensureUnaliased() + if (isEmpty) {} + else if (first.head == elem) { + first = first.tail + reduceLengthBy(1) + } + else { + var cursor = first + while (!cursor.tail.isEmpty && cursor.tail.head != elem) { + cursor = cursor.tail + } + if (!cursor.tail.isEmpty) { + val z = cursor.asInstanceOf[::[A]] + if (z.next == last0) + last0 = z + z.next = cursor.tail.tail + reduceLengthBy(1) + } + } + this + } + + /** Reduce the length of the buffer, and null out last0 + * if this reduces the length to 0. + */ + private def reduceLengthBy(num: Int): Unit = { + len -= num + if (len <= 0) // obviously shouldn't be < 0, but still better not to leak + last0 = null + } + + private def locate(i: Int): Predecessor[A] = + if (i == 0) null + else if (i == len) last0 + else { + var j = i - 1 + var p = first + while (j > 0) { + p = p.tail + j -= 1 + } + p.asInstanceOf[Predecessor[A]] + } + + private def getNext(p: Predecessor[A]): List[A] = + if (p == null) first else p.next + + def update(idx: Int, elem: A): Unit = { + ensureUnaliased() + if (idx < 0 || idx >= len) throw CommonErrors.indexOutOfBounds(index = idx, max = len - 1) + if (idx == 0) { + val newElem = new :: (elem, first.tail) + if (last0 eq first) { + last0 = newElem + } + first = newElem + } else { + // `p` can not be `null` because the case where `idx == 0` is handled above + val p = locate(idx) + val newElem = new :: (elem, p.tail.tail) + if (last0 eq p.tail) { + last0 = newElem + } + p.asInstanceOf[::[A]].next = newElem + } + } + + def insert(idx: Int, elem: A): Unit = { + ensureUnaliased() + if (idx < 0 || idx > len) throw CommonErrors.indexOutOfBounds(index = idx, max = len - 1) + if (idx == len) addOne(elem) + else { + val p = locate(idx) + val nx = elem :: getNext(p) + if(p eq null) first = nx else p.next = nx + len += 1 + } + } + + def prepend(elem: A): this.type = { + insert(0, elem) + this + } + + // `fresh` must be a `ListBuffer` that only we have access to + private def insertAfter(prev: Predecessor[A], fresh: ListBuffer[A]): Unit = { + if (!fresh.isEmpty) { + val follow = getNext(prev) + if (prev eq null) first = fresh.first else prev.next = fresh.first + fresh.last0.next = follow + if (follow.isEmpty) last0 = fresh.last0 + len += fresh.length + } + } + + def insertAll(idx: Int, elems: IterableOnce[A]): Unit = { + if (idx < 0 || idx > len) throw CommonErrors.indexOutOfBounds(index = idx, max = len - 1) + val it = elems.iterator + if (it.hasNext) { + if (idx == len) addAll(it) + else { + val fresh = new ListBuffer[A].freshFrom(it) + ensureUnaliased() + insertAfter(locate(idx), fresh) + } + } + } + + def remove(idx: Int): A = { + ensureUnaliased() + if (idx < 0 || idx >= len) throw CommonErrors.indexOutOfBounds(index = idx, max = len - 1) + val p = locate(idx) + val nx = getNext(p) + if(p eq null) { + first = nx.tail + if(first.isEmpty) last0 = null + } else { + if(last0 eq nx) last0 = p + p.next = nx.tail + } + len -= 1 + nx.head + } + + def remove(idx: Int, count: Int): Unit = + if (count > 0) { + ensureUnaliased() + if (idx < 0 || idx + count > len) throw new IndexOutOfBoundsException(s"$idx to ${idx + count} is out of bounds (min 0, max ${len - 1})") + removeAfter(locate(idx), count) + } else if (count < 0) { + throw new IllegalArgumentException("removing negative number of elements: " + count) + } + + private def removeAfter(prev: Predecessor[A], n: Int) = { + @tailrec def ahead(p: List[A], n: Int): List[A] = + if (n == 0) p else ahead(p.tail, n - 1) + val nx = ahead(getNext(prev), n) + if(prev eq null) first = nx else prev.next = nx + if(nx.isEmpty) last0 = prev + len -= n + } + + /** Replace the contents of this $coll with the mapped result. + * + * @param f the mapping function + * @return this $coll + */ + def mapInPlace(f: A => A): this.type = { + mutationCount += 1 + val buf = new ListBuffer[A] + for (elem <- this) buf += f(elem) + first = buf.first + last0 = buf.last0 + aliased = false // we just assigned from a new instance + this + } + + /** Replace the contents of this $coll with the flatmapped result. + * + * @param f the mapping function + * @return this $coll + */ + def flatMapInPlace(f: A => IterableOnce[A]): this.type = { + mutationCount += 1 + var src = first + var dst: List[A] = null + last0 = null + len = 0 + while(!src.isEmpty) { + val it = f(src.head).iterator + while(it.hasNext) { + val v = new ::(it.next(), Nil) + if(dst eq null) dst = v else last0.next = v + last0 = v + len += 1 + } + src = src.tail + } + first = if(dst eq null) Nil else dst + aliased = false // we just rebuilt a fresh, unaliased instance + this + } + + /** Replace the contents of this $coll with the filtered result. + * + * @param p the filtering predicate + * @return this $coll + */ + def filterInPlace(p: A => Boolean): this.type = { + ensureUnaliased() + var prev: Predecessor[A] = null + var cur: List[A] = first + while (!cur.isEmpty) { + val follow = cur.tail + if (!p(cur.head)) { + if(prev eq null) first = follow + else prev.next = follow + len -= 1 + } else { + prev = cur.asInstanceOf[Predecessor[A]] + } + cur = follow + } + last0 = prev + this + } + + def patchInPlace(from: Int, patch: collection.IterableOnce[A], replaced: Int): this.type = { + val _len = len + val _from = math.max(from, 0) // normalized + val _replaced = math.max(replaced, 0) // normalized + val it = patch.iterator + + val nonEmptyPatch = it.hasNext + val nonEmptyReplace = (_from < _len) && (_replaced > 0) + + // don't want to add a mutation or check aliasing (potentially expensive) + // if there's no patching to do + if (nonEmptyPatch || nonEmptyReplace) { + val fresh = new ListBuffer[A].freshFrom(it) + ensureUnaliased() + val i = math.min(_from, _len) + val n = math.min(_replaced, _len) + val p = locate(i) + removeAfter(p, math.min(n, _len - i)) + insertAfter(p, fresh) + } + this + } + + /** + * Selects the last element. + * + * Runs in constant time. + * + * @return The last element of this $coll. + * @throws NoSuchElementException If the $coll is empty. + */ + override def last: A = if (last0 eq null) throw new NoSuchElementException("last of empty ListBuffer") else last0.head + + /** + * Optionally selects the last element. + * + * Runs in constant time. + * + * @return the last element of this $coll$ if it is nonempty, `None` if it is empty. + */ + override def lastOption: Option[A] = if (last0 eq null) None else Some(last0.head) + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "ListBuffer" + +} + +@SerialVersionUID(3L) +object ListBuffer extends StrictOptimizedSeqFactory[ListBuffer] { + + def from[A](coll: collection.IterableOnce[A]): ListBuffer[A] = new ListBuffer[A].freshFrom(coll) + + def newBuilder[A]: Builder[A, ListBuffer[A]] = new GrowableBuilder(empty[A]) + + def empty[A]: ListBuffer[A] = new ListBuffer[A] +} diff --git a/library/src/scala/collection/mutable/ListMap.scala b/library/src/scala/collection/mutable/ListMap.scala new file mode 100644 index 000000000000..e1a273bfd5af --- /dev/null +++ b/library/src/scala/collection/mutable/ListMap.scala @@ -0,0 +1,82 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.annotation.tailrec +import scala.collection.generic.DefaultSerializable +import scala.collection.immutable.List + +/** A simple mutable map backed by a list, so it preserves insertion order. + * + * @tparam K the type of the keys contained in this list map. + * @tparam V the type of the values assigned to keys in this list map. + * + * @define Coll `mutable.ListMap` + * @define coll mutable list map + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define orderDependent + * @define orderDependentFold + */ +@deprecated("Use an immutable.ListMap assigned to a var instead of mutable.ListMap", "2.13.0") +class ListMap[K, V] + extends AbstractMap[K, V] + with MapOps[K, V, ListMap, ListMap[K, V]] + with StrictOptimizedIterableOps[(K, V), Iterable, ListMap[K, V]] + with StrictOptimizedMapOps[K, V, ListMap, ListMap[K, V]] + with MapFactoryDefaults[K, V, ListMap, Iterable] + with DefaultSerializable { + + override def mapFactory: MapFactory[ListMap] = ListMap + + private[this] var elems: List[(K, V)] = List() + private[this] var siz: Int = 0 + + def get(key: K): Option[V] = elems find (_._1 == key) map (_._2) + def iterator: Iterator[(K, V)] = elems.iterator + + final override def addOne(kv: (K, V)) = { + val (e, key0) = remove(kv._1, elems, List()) + elems = (key0, kv._2) :: e + siz += 1; this + } + + final override def subtractOne(key: K) = { elems = remove(key, elems, List())._1; this } + + @tailrec + private def remove(key: K, elems: List[(K, V)], acc: List[(K, V)]): (List[(K, V)], K) = { + if (elems.isEmpty) (acc, key) + else if (elems.head._1 == key) { siz -= 1; (acc ::: elems.tail, elems.head._1) } + else remove(key, elems.tail, elems.head :: acc) + } + + final override def clear(): Unit = { elems = List(); siz = 0 } + + final override def size: Int = siz + override def knownSize: Int = size + override def isEmpty: Boolean = size == 0 + override protected[this] def stringPrefix = "ListMap" +} + +/** $factoryInfo + * @define Coll `mutable.ListMap` + * @define coll mutable list map + */ +@SerialVersionUID(3L) +@deprecated("Use an immutable.ListMap assigned to a var instead of mutable.ListMap", "2.13.0") +object ListMap extends MapFactory[ListMap] { + def empty[K, V]: ListMap[K, V] = new ListMap[K, V] + def from[K, V](it: IterableOnce[(K, V)]): ListMap[K,V] = Growable.from(empty[K, V], it) + def newBuilder[K, V]: Builder[(K, V), ListMap[K,V]] = new GrowableBuilder(empty[K, V]) +} diff --git a/library/src/scala/collection/mutable/LongMap.scala b/library/src/scala/collection/mutable/LongMap.scala new file mode 100644 index 000000000000..e36c337437e3 --- /dev/null +++ b/library/src/scala/collection/mutable/LongMap.scala @@ -0,0 +1,691 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.collection.generic.DefaultSerializationProxy +import scala.language.implicitConversions + +/** This class implements mutable maps with `Long` keys based on a hash table with open addressing. + * + * Basic map operations on single entries, including `contains` and `get`, + * are typically substantially faster with `LongMap` than [[HashMap]]. Methods + * that act on the whole map, including `foreach` and `map` are not in + * general expected to be faster than with a generic map, save for those + * that take particular advantage of the internal structure of the map: + * `foreachKey`, `foreachValue`, `mapValuesNow`, and `transformValues`. + * + * Maps with open addressing may become less efficient at lookup after + * repeated addition/removal of elements. Although `LongMap` makes a + * decent attempt to remain efficient regardless, calling `repack` + * on a map that will no longer have elements removed but will be + * used heavily may save both time and storage space. + * + * This map is not intended to contain more than 2^29 entries (approximately + * 500 million). The maximum capacity is 2^30, but performance will degrade + * rapidly as 2^30 is approached. + * + */ +final class LongMap[V] private[collection] (defaultEntry: Long => V, initialBufferSize: Int, initBlank: Boolean) + extends AbstractMap[Long, V] + with MapOps[Long, V, Map, LongMap[V]] + with StrictOptimizedIterableOps[(Long, V), Iterable, LongMap[V]] + with Serializable { + import LongMap._ + + def this() = this(LongMap.exceptionDefault, 16, initBlank = true) + + // TODO: override clear() with an optimization more tailored for efficiency. + override protected def fromSpecific(coll: scala.collection.IterableOnce[(Long, V)]): LongMap[V] = { + //TODO should this be the default implementation of this method in StrictOptimizedIterableOps? + val b = newSpecificBuilder + b.sizeHint(coll) + b.addAll(coll) + b.result() + } + override protected def newSpecificBuilder: Builder[(Long, V),LongMap[V]] = new GrowableBuilder(LongMap.empty[V]) + + /** Creates a new `LongMap` that returns default values according to a supplied key-value mapping. */ + def this(defaultEntry: Long => V) = this(defaultEntry, 16, initBlank = true) + + /** Creates a new `LongMap` with an initial buffer of specified size. + * + * A LongMap can typically contain half as many elements as its buffer size + * before it requires resizing. + */ + def this(initialBufferSize: Int) = this(LongMap.exceptionDefault, initialBufferSize, initBlank = true) + + /** Creates a new `LongMap` with specified default values and initial buffer size. */ + def this(defaultEntry: Long => V, initialBufferSize: Int) = this(defaultEntry, initialBufferSize, initBlank = true) + + private[this] var mask = 0 + private[this] var extraKeys: Int = 0 + private[this] var zeroValue: AnyRef = null + private[this] var minValue: AnyRef = null + private[this] var _size = 0 + private[this] var _vacant = 0 + private[this] var _keys: Array[Long] = null + private[this] var _values: Array[AnyRef] = null + + if (initBlank) defaultInitialize(initialBufferSize) + + private[this] def defaultInitialize(n: Int) = { + mask = + if (n<0) 0x7 + else (((1 << (32 - java.lang.Integer.numberOfLeadingZeros(n-1))) - 1) & 0x3FFFFFFF) | 0x7 + _keys = new Array[Long](mask+1) + _values = new Array[AnyRef](mask+1) + } + + private[collection] def initializeTo( + m: Int, ek: Int, zv: AnyRef, mv: AnyRef, sz: Int, vc: Int, kz: Array[Long], vz: Array[AnyRef] + ): Unit = { + mask = m; extraKeys = ek; zeroValue = zv; minValue = mv; _size = sz; _vacant = vc; _keys = kz; _values = vz + } + + override def size: Int = _size + (extraKeys+1)/2 + override def knownSize: Int = size + override def isEmpty: Boolean = size == 0 + override def empty: LongMap[V] = new LongMap() + + private def imbalanced: Boolean = + (_size + _vacant) > 0.5*mask || _vacant > _size + + private def toIndex(k: Long): Int = { + // Part of the MurmurHash3 32 bit finalizer + val h = ((k ^ (k >>> 32)) & 0xFFFFFFFFL).toInt + val x = (h ^ (h >>> 16)) * 0x85EBCA6B + (x ^ (x >>> 13)) & mask + } + + private def seekEmpty(k: Long): Int = { + var e = toIndex(k) + var x = 0 + while (_keys(e) != 0) { x += 1; e = (e + 2*(x+1)*x - 3) & mask } + e + } + + private def seekEntry(k: Long): Int = { + var e = toIndex(k) + var x = 0 + var q = 0L + while ({ q = _keys(e); if (q==k) return e; q != 0}) { x += 1; e = (e + 2*(x+1)*x - 3) & mask } + e | MissingBit + } + + private def seekEntryOrOpen(k: Long): Int = { + var e = toIndex(k) + var x = 0 + var q = 0L + while ({ q = _keys(e); if (q==k) return e; q+q != 0}) { + x += 1 + e = (e + 2*(x+1)*x - 3) & mask + } + if (q == 0) return e | MissingBit + val o = e | MissVacant + while ({ q = _keys(e); if (q==k) return e; q != 0}) { + x += 1 + e = (e + 2*(x+1)*x - 3) & mask + } + o + } + + override def contains(key: Long): Boolean = { + if (key == -key) (((key>>>63).toInt+1) & extraKeys) != 0 + else seekEntry(key) >= 0 + } + + override def get(key: Long): Option[V] = { + if (key == -key) { + if ((((key>>>63).toInt+1) & extraKeys) == 0) None + else if (key == 0) Some(zeroValue.asInstanceOf[V]) + else Some(minValue.asInstanceOf[V]) + } + else { + val i = seekEntry(key) + if (i < 0) None else Some(_values(i).asInstanceOf[V]) + } + } + + override def getOrElse[V1 >: V](key: Long, default: => V1): V1 = { + if (key == -key) { + if ((((key>>>63).toInt+1) & extraKeys) == 0) default + else if (key == 0) zeroValue.asInstanceOf[V1] + else minValue.asInstanceOf[V1] + } + else { + val i = seekEntry(key) + if (i < 0) default else _values(i).asInstanceOf[V1] + } + } + + override def getOrElseUpdate(key: Long, defaultValue: => V): V = { + if (key == -key) { + val kbits = (key>>>63).toInt + 1 + if ((kbits & extraKeys) == 0) { + val value = defaultValue + extraKeys |= kbits + if (key == 0) zeroValue = value.asInstanceOf[AnyRef] + else minValue = value.asInstanceOf[AnyRef] + value + } + else if (key == 0) zeroValue.asInstanceOf[V] + else minValue.asInstanceOf[V] + } + else { + var i = seekEntryOrOpen(key) + if (i < 0) { + val value = { + val oks = _keys + val j = i & IndexMask + val ok = oks(j) + val ans = defaultValue + // Evaluating `defaultValue` may change the map + // - repack: the array is different + // - element added at `j`: since `i < 0`, the key was missing and `ok` is either 0 or MinValue. + // If `defaultValue` added an element at `j` then `_keys(j)` must be different now. + // (`_keys` never contains 0 or MinValue.) + if (oks.ne(_keys) || ok != _keys(j)) { + i = seekEntryOrOpen(key) + if (i >= 0) _size -= 1 + } + ans + } + _size += 1 + val j = i & IndexMask + _keys(j) = key + _values(j) = value.asInstanceOf[AnyRef] + if ((i & VacantBit) != 0) _vacant -= 1 + else if (imbalanced) repack() + value + } + else _values(i).asInstanceOf[V] + } + } + + /** Retrieves the value associated with a key, or the default for that type if none exists + * (null for AnyRef, 0 for floats and integers). + * + * Note: this is the fastest way to retrieve a value that may or + * may not exist, if the default null/zero is acceptable. For key/value + * pairs that do exist, `apply` (i.e. `map(key)`) is equally fast. + */ + def getOrNull(key: Long): V = { + if (key == -key) { + if ((((key>>>63).toInt+1) & extraKeys) == 0) null.asInstanceOf[V] + else if (key == 0) zeroValue.asInstanceOf[V] + else minValue.asInstanceOf[V] + } + else { + val i = seekEntry(key) + if (i < 0) null.asInstanceOf[V] else _values(i).asInstanceOf[V] + } + } + + /** Retrieves the value associated with a key. + * If the key does not exist in the map, the `defaultEntry` for that key + * will be returned instead. + */ + override def apply(key: Long): V = { + if (key == -key) { + if ((((key>>>63).toInt+1) & extraKeys) == 0) defaultEntry(key) + else if (key == 0) zeroValue.asInstanceOf[V] + else minValue.asInstanceOf[V] + } + else { + val i = seekEntry(key) + if (i < 0) defaultEntry(key) else _values(i).asInstanceOf[V] + } + } + + /** The user-supplied default value for the key. Throws an exception + * if no other default behavior was specified. + */ + override def default(key: Long) = defaultEntry(key) + + private def repack(newMask: Int): Unit = { + val ok = _keys + val ov = _values + mask = newMask + _keys = new Array[Long](mask+1) + _values = new Array[AnyRef](mask+1) + _vacant = 0 + var i = 0 + while (i < ok.length) { + val k = ok(i) + if (k != -k) { + val j = seekEmpty(k) + _keys(j) = k + _values(j) = ov(i) + } + i += 1 + } + } + + /** Repacks the contents of this `LongMap` for maximum efficiency of lookup. + * + * For maps that undergo a complex creation process with both addition and + * removal of keys, and then are used heavily with no further removal of + * elements, calling `repack` after the end of the creation can result in + * improved performance. Repacking takes time proportional to the number + * of entries in the map. + */ + def repack(): Unit = repack(repackMask(mask, _size = _size, _vacant = _vacant)) + + override def put(key: Long, value: V): Option[V] = { + if (key == -key) { + if (key == 0) { + val ans = if ((extraKeys&1) == 1) Some(zeroValue.asInstanceOf[V]) else None + zeroValue = value.asInstanceOf[AnyRef] + extraKeys |= 1 + ans + } + else { + val ans = if ((extraKeys&2) == 1) Some(minValue.asInstanceOf[V]) else None + minValue = value.asInstanceOf[AnyRef] + extraKeys |= 2 + ans + } + } + else { + val i = seekEntryOrOpen(key) + if (i < 0) { + val j = i & IndexMask + _keys(j) = key + _values(j) = value.asInstanceOf[AnyRef] + _size += 1 + if ((i & VacantBit) != 0) _vacant -= 1 + else if (imbalanced) repack() + None + } + else { + val ans = Some(_values(i).asInstanceOf[V]) + _keys(i) = key + _values(i) = value.asInstanceOf[AnyRef] + ans + } + } + } + + /** Updates the map to include a new key-value pair. + * + * This is the fastest way to add an entry to a `LongMap`. + */ + override def update(key: Long, value: V): Unit = { + if (key == -key) { + if (key == 0) { + zeroValue = value.asInstanceOf[AnyRef] + extraKeys |= 1 + } + else { + minValue = value.asInstanceOf[AnyRef] + extraKeys |= 2 + } + } + else { + val i = seekEntryOrOpen(key) + if (i < 0) { + val j = i & IndexMask + _keys(j) = key + _values(j) = value.asInstanceOf[AnyRef] + _size += 1 + if ((i & VacantBit) != 0) _vacant -= 1 + else if (imbalanced) repack() + } + else { + _keys(i) = key + _values(i) = value.asInstanceOf[AnyRef] + } + } + } + + /** Adds a new key/value pair to this map and returns the map. */ + @deprecated("Use `addOne` or `update` instead; infix operations with an operand of multiple args will be deprecated", "2.13.3") + def +=(key: Long, value: V): this.type = { update(key, value); this } + + /** Adds a new key/value pair to this map and returns the map. */ + @inline final def addOne(key: Long, value: V): this.type = { update(key, value); this } + + @inline override final def addOne(kv: (Long, V)): this.type = { update(kv._1, kv._2); this } + + def subtractOne(key: Long): this.type = { + if (key == -key) { + if (key == 0L) { + extraKeys &= 0x2 + zeroValue = null + } + else { + extraKeys &= 0x1 + minValue = null + } + } + else { + val i = seekEntry(key) + if (i >= 0) { + _size -= 1 + _vacant += 1 + _keys(i) = Long.MinValue + _values(i) = null + } + } + this + } + + def iterator: Iterator[(Long, V)] = new AbstractIterator[(Long, V)] { + private[this] val kz = _keys + private[this] val vz = _values + + private[this] var nextPair: (Long, V) = + if (extraKeys==0) null + else if ((extraKeys&1)==1) (0L, zeroValue.asInstanceOf[V]) + else (Long.MinValue, minValue.asInstanceOf[V]) + + private[this] var anotherPair: (Long, V) = + if (extraKeys==3) (Long.MinValue, minValue.asInstanceOf[V]) + else null + + private[this] var index = 0 + + def hasNext: Boolean = nextPair != null || (index < kz.length && { + var q = kz(index) + while (q == -q) { + index += 1 + if (index >= kz.length) return false + q = kz(index) + } + nextPair = (kz(index), vz(index).asInstanceOf[V]) + index += 1 + true + }) + def next() = { + if (nextPair == null && !hasNext) throw new NoSuchElementException("next") + val ans = nextPair + if (anotherPair != null) { + nextPair = anotherPair + anotherPair = null + } + else nextPair = null + ans + } + } + + // TODO PERF override these for efficiency. See immutable.LongMap for how to organize the code. + override def keysIterator: Iterator[Long] = super.keysIterator + override def valuesIterator: Iterator[V] = super.valuesIterator + + override def foreach[U](f: ((Long,V)) => U): Unit = { + if ((extraKeys & 1) == 1) f((0L, zeroValue.asInstanceOf[V])) + if ((extraKeys & 2) == 2) f((Long.MinValue, minValue.asInstanceOf[V])) + var i,j = 0 + while (i < _keys.length & j < _size) { + val k = _keys(i) + if (k != -k) { + j += 1 + f((k, _values(i).asInstanceOf[V])) + } + i += 1 + } + } + + override def foreachEntry[U](f: (Long,V) => U): Unit = { + if ((extraKeys & 1) == 1) f(0L, zeroValue.asInstanceOf[V]) + if ((extraKeys & 2) == 2) f(Long.MinValue, minValue.asInstanceOf[V]) + var i,j = 0 + while (i < _keys.length & j < _size) { + val k = _keys(i) + if (k != -k) { + j += 1 + f(k, _values(i).asInstanceOf[V]) + } + i += 1 + } + } + + override def clone(): LongMap[V] = { + val kz = java.util.Arrays.copyOf(_keys, _keys.length) + val vz = java.util.Arrays.copyOf(_values, _values.length) + val lm = new LongMap[V](defaultEntry, 1, initBlank = false) + lm.initializeTo(mask, extraKeys, zeroValue, minValue, _size, _vacant, kz, vz) + lm + } + + @deprecated("Consider requiring an immutable Map or fall back to Map.concat", "2.13.0") + override def +[V1 >: V](kv: (Long, V1)): LongMap[V1] = { + val lm = clone().asInstanceOf[LongMap[V1]] + lm += kv + lm + } + + @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") + override def + [V1 >: V](elem1: (Long, V1), elem2: (Long, V1), elems: (Long, V1)*): LongMap[V1] = { + val m = this + elem1 + elem2 + if(elems.isEmpty) m else m.concat(elems) + } + + override def concat[V1 >: V](xs: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = { + val lm = clone().asInstanceOf[LongMap[V1]] + xs.iterator.foreach(kv => lm += kv) + lm + } + + override def ++ [V1 >: V](xs: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = concat(xs) + + @deprecated("Use m.clone().addOne(k,v) instead of m.updated(k, v)", "2.13.0") + override def updated[V1 >: V](key: Long, value: V1): LongMap[V1] = + clone().asInstanceOf[LongMap[V1]].addOne(key, value) + + /** Applies a function to all keys of this map. */ + def foreachKey[A](f: Long => A): Unit = { + if ((extraKeys & 1) == 1) f(0L) + if ((extraKeys & 2) == 2) f(Long.MinValue) + var i,j = 0 + while (i < _keys.length & j < _size) { + val k = _keys(i) + if (k != -k) { + j += 1 + f(k) + } + i += 1 + } + } + + /** Applies a function to all values of this map. */ + def foreachValue[A](f: V => A): Unit = { + if ((extraKeys & 1) == 1) f(zeroValue.asInstanceOf[V]) + if ((extraKeys & 2) == 2) f(minValue.asInstanceOf[V]) + var i,j = 0 + while (i < _keys.length & j < _size) { + val k = _keys(i) + if (k != -k) { + j += 1 + f(_values(i).asInstanceOf[V]) + } + i += 1 + } + } + + /** Creates a new `LongMap` with different values. + * Unlike `mapValues`, this method generates a new + * collection immediately. + */ + def mapValuesNow[V1](f: V => V1): LongMap[V1] = { + val zv = if ((extraKeys & 1) == 1) f(zeroValue.asInstanceOf[V]).asInstanceOf[AnyRef] else null + val mv = if ((extraKeys & 2) == 2) f(minValue.asInstanceOf[V]).asInstanceOf[AnyRef] else null + val lm = new LongMap[V1](LongMap.exceptionDefault, 1, initBlank = false) + val kz = java.util.Arrays.copyOf(_keys, _keys.length) + val vz = new Array[AnyRef](_values.length) + var i,j = 0 + while (i < _keys.length & j < _size) { + val k = _keys(i) + if (k != -k) { + j += 1 + vz(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef] + } + i += 1 + } + lm.initializeTo(mask, extraKeys, zv, mv, _size, _vacant, kz, vz) + lm + } + + /** Applies a transformation function to all values stored in this map. + * Note: the default, if any, is not transformed. + */ + @deprecated("Use transformValuesInPlace instead of transformValues", "2.13.0") + @`inline` final def transformValues(f: V => V): this.type = transformValuesInPlace(f) + + /** Applies a transformation function to all values stored in this map. + * Note: the default, if any, is not transformed. + */ + def transformValuesInPlace(f: V => V): this.type = { + if ((extraKeys & 1) == 1) zeroValue = f(zeroValue.asInstanceOf[V]).asInstanceOf[AnyRef] + if ((extraKeys & 2) == 2) minValue = f(minValue.asInstanceOf[V]).asInstanceOf[AnyRef] + var i,j = 0 + while (i < _keys.length & j < _size) { + val k = _keys(i) + if (k != -k) { + j += 1 + _values(i) = f(_values(i).asInstanceOf[V]).asInstanceOf[AnyRef] + } + i += 1 + } + this + } + + /** An overload of `map` which produces a `LongMap`. + * + * @param f the mapping function + */ + def map[V2](f: ((Long, V)) => (Long, V2)): LongMap[V2] = LongMap.from(new View.Map(coll, f)) + + /** An overload of `flatMap` which produces a `LongMap`. + * + * @param f the mapping function + */ + def flatMap[V2](f: ((Long, V)) => IterableOnce[(Long, V2)]): LongMap[V2] = LongMap.from(new View.FlatMap(coll, f)) + + /** An overload of `collect` which produces a `LongMap`. + * + * @param pf the mapping function + */ + def collect[V2](pf: PartialFunction[(Long, V), (Long, V2)]): LongMap[V2] = + strictOptimizedCollect(LongMap.newBuilder[V2], pf) + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(LongMap.toFactory[V](LongMap), this) + + override protected[this] def className = "LongMap" +} + +object LongMap { + private final val IndexMask = 0x3FFF_FFFF + private final val MissingBit = 0x8000_0000 + private final val VacantBit = 0x4000_0000 + private final val MissVacant = 0xC000_0000 + + private val exceptionDefault: Long => Nothing = (k: Long) => throw new NoSuchElementException(k.toString) + + /** A builder for instances of `LongMap`. + * + * This builder can be reused to create multiple instances. + */ + final class LongMapBuilder[V] extends ReusableBuilder[(Long, V), LongMap[V]] { + private[collection] var elems: LongMap[V] = new LongMap[V] + override def addOne(entry: (Long, V)): this.type = { + elems += entry + this + } + def clear(): Unit = elems = new LongMap[V] + def result(): LongMap[V] = elems + override def knownSize: Int = elems.knownSize + } + + /** Creates a new `LongMap` with zero or more key/value pairs. */ + def apply[V](elems: (Long, V)*): LongMap[V] = buildFromIterableOnce(elems) + + private def buildFromIterableOnce[V](elems: IterableOnce[(Long, V)]): LongMap[V] = { + var sz = elems.knownSize + if(sz < 0) sz = 4 + val lm = new LongMap[V](sz * 2) + elems.iterator.foreach{ case (k,v) => lm(k) = v } + if (lm.size < (sz>>3)) lm.repack() + lm + } + + /** Creates a new empty `LongMap`. */ + def empty[V]: LongMap[V] = new LongMap[V] + + /** Creates a new empty `LongMap` with the supplied default */ + def withDefault[V](default: Long => V): LongMap[V] = new LongMap[V](default) + + /** Creates a new `LongMap` from an existing source collection. A source collection + * which is already a `LongMap` gets cloned. + * + * @param source Source collection + * @tparam A the type of the collection’s elements + * @return a new `LongMap` with the elements of `source` + */ + def from[V](source: IterableOnce[(Long, V)]): LongMap[V] = source match { + case source: LongMap[_] => source.clone().asInstanceOf[LongMap[V]] + case _ => buildFromIterableOnce(source) + } + + def newBuilder[V]: ReusableBuilder[(Long, V), LongMap[V]] = new LongMapBuilder[V] + + /** Creates a new `LongMap` from arrays of keys and values. + * Equivalent to but more efficient than `LongMap((keys zip values): _*)`. + */ + def fromZip[V](keys: Array[Long], values: Array[V]): LongMap[V] = { + val sz = math.min(keys.length, values.length) + val lm = new LongMap[V](sz * 2) + var i = 0 + while (i < sz) { lm(keys(i)) = values(i); i += 1 } + if (lm.size < (sz>>3)) lm.repack() + lm + } + + /** Creates a new `LongMap` from keys and values. + * Equivalent to but more efficient than `LongMap((keys zip values): _*)`. + */ + def fromZip[V](keys: scala.collection.Iterable[Long], values: scala.collection.Iterable[V]): LongMap[V] = { + val sz = math.min(keys.size, values.size) + val lm = new LongMap[V](sz * 2) + val ki = keys.iterator + val vi = values.iterator + while (ki.hasNext && vi.hasNext) lm(ki.next()) = vi.next() + if (lm.size < (sz >> 3)) lm.repack() + lm + } + + implicit def toFactory[V](dummy: LongMap.type): Factory[(Long, V), LongMap[V]] = ToFactory.asInstanceOf[Factory[(Long, V), LongMap[V]]] + + @SerialVersionUID(3L) + private[this] object ToFactory extends Factory[(Long, AnyRef), LongMap[AnyRef]] with Serializable { + def fromSpecific(it: IterableOnce[(Long, AnyRef)]): LongMap[AnyRef] = LongMap.from[AnyRef](it) + def newBuilder: Builder[(Long, AnyRef), LongMap[AnyRef]] = LongMap.newBuilder[AnyRef] + } + + implicit def toBuildFrom[V](factory: LongMap.type): BuildFrom[Any, (Long, V), LongMap[V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (Long, V), LongMap[V]]] + private object ToBuildFrom extends BuildFrom[Any, (Long, AnyRef), LongMap[AnyRef]] { + def fromSpecific(from: Any)(it: IterableOnce[(Long, AnyRef)]) = LongMap.from(it) + def newBuilder(from: Any): ReusableBuilder[(Long, AnyRef), LongMap[AnyRef]] = LongMap.newBuilder[AnyRef] + } + + implicit def iterableFactory[V]: Factory[(Long, V), LongMap[V]] = toFactory(this) + implicit def buildFromLongMap[V]: BuildFrom[LongMap[_], (Long, V), LongMap[V]] = toBuildFrom(this) + + private def repackMask(mask: Int, _size: Int, _vacant: Int): Int = { + var m = mask + if (_size + _vacant >= 0.5*mask && !(_vacant > 0.2*mask)) m = ((m << 1) + 1) & IndexMask + while (m > 8 && _size < (m >>> 3)) m = m >>> 1 + m /*.ensuring(_size <= _ + 1)*/ + } +} diff --git a/library/src/scala/collection/mutable/Map.scala b/library/src/scala/collection/mutable/Map.scala new file mode 100644 index 000000000000..8659b45e86e6 --- /dev/null +++ b/library/src/scala/collection/mutable/Map.scala @@ -0,0 +1,268 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +/** Base type of mutable Maps */ +trait Map[K, V] + extends Iterable[(K, V)] + with collection.Map[K, V] + with MapOps[K, V, Map, Map[K, V]] + with Growable[(K, V)] + with Shrinkable[K] + with MapFactoryDefaults[K, V, Map, Iterable] { + + override def mapFactory: scala.collection.MapFactory[Map] = Map + + /* + //TODO consider keeping `remove` because it returns the removed entry + @deprecated("Use subtract or -= instead of remove", "2.13.0") + def remove(key: K): Option[V] = { + val old = get(key) + if(old.isDefined) subtract(key) + old + } + */ + + /** The same map with a given default function. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefaultValue`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d the function mapping keys to values, used for non-present keys + * @return a wrapper of the map with a default value + */ + def withDefault(d: K => V): Map[K, V] = new Map.WithDefault[K, V](this, d) + + /** The same map with a given default value. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefaultValue`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d default value used for non-present keys + * @return a wrapper of the map with a default value + */ + def withDefaultValue(d: V): Map[K, V] = new Map.WithDefault[K, V](this, x => d) +} + +/** + * @define coll mutable map + * @define Coll `mutable.Map` + */ +trait MapOps[K, V, +CC[X, Y] <: MapOps[X, Y, CC, _], +C <: MapOps[K, V, CC, C]] + extends IterableOps[(K, V), Iterable, C] + with collection.MapOps[K, V, CC, C] + with Cloneable[C] + with Builder[(K, V), C] + with Growable[(K, V)] + with Shrinkable[K] { + + def result(): C = coll + + @deprecated("Use - or remove on an immutable Map", "2.13.0") + final def - (key: K): C = clone() -= key + + @deprecated("Use -- or removeAll on an immutable Map", "2.13.0") + final def - (key1: K, key2: K, keys: K*): C = clone() -= key1 -= key2 --= keys + + /** Adds a new key/value pair to this map and optionally returns previously bound value. + * If the map already contains a + * mapping for the key, it will be overridden by the new value. + * + * @param key the key to update + * @param value the new value + * @return an option value containing the value associated with the key + * before the `put` operation was executed, or `None` if `key` + * was not defined in the map before. + */ + def put(key: K, value: V): Option[V] = { + val r = get(key) + update(key, value) + r + } + + /** Adds a new key/value pair to this map. + * If the map already contains a + * mapping for the key, it will be overridden by the new value. + * + * @param key The key to update + * @param value The new value + */ + def update(key: K, value: V): Unit = { coll += ((key, value)) } + + /** + * Update a mapping for the specified key and its current optionally mapped value + * (`Some` if there is current mapping, `None` if not). + * + * If the remapping function returns `Some(v)`, the mapping is updated with the new value `v`. + * If the remapping function returns `None`, the mapping is removed (or remains absent if initially absent). + * If the function itself throws an exception, the exception is rethrown, and the current mapping is left unchanged. + * + * @param key the key value + * @param remappingFunction a function that receives current optionally mapped value and return a new mapping + * @return the new value associated with the specified key + */ + def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { + val previousValue = this.get(key) + val nextValue = remappingFunction(previousValue) + (previousValue, nextValue) match { + case (None, None) => // do nothing + case (Some(_), None) => this.remove(key) + case (_, Some(v)) => this.update(key,v) + } + nextValue + } + + /** If given key is already in this map, returns associated value. + * + * Otherwise, computes value from given expression `defaultValue`, stores with key + * in map and returns that value. + * + * Concurrent map implementations may evaluate the expression `defaultValue` + * multiple times, or may evaluate `defaultValue` without inserting the result. + * + * @param key the key to test + * @param defaultValue the computation yielding the value to associate with `key`, if + * `key` is previously unbound. + * @return the value associated with key (either previously or as a result + * of executing the method). + */ + def getOrElseUpdate(key: K, @deprecatedName("op", since="2.13.13") defaultValue: => V): V = + get(key) match { + case Some(v) => v + case None => val d = defaultValue; this(key) = d; d + } + + /** Removes a key from this map, returning the value associated previously + * with that key as an option. + * @param key the key to be removed + * @return an option value containing the value associated previously with `key`, + * or `None` if `key` was not defined in the map before. + */ + def remove(key: K): Option[V] = { + val r = get(key) + if (r.isDefined) this -= key + r + } + + def clear(): Unit = { keysIterator foreach -= } + + override def clone(): C = empty ++= this + + @deprecated("Use filterInPlace instead", "2.13.0") + @inline final def retain(p: (K, V) => Boolean): this.type = filterInPlace(p) + + /** Retains only those mappings for which the predicate + * `p` returns `true`. + * + * @param p The test predicate + */ + def filterInPlace(p: (K, V) => Boolean): this.type = { + if (!isEmpty) this match { + case tm: concurrent.Map[_, _] => tm.asInstanceOf[concurrent.Map[K, V]].filterInPlaceImpl(p) + case _ => + val array = this.toArray[Any] // scala/bug#7269 toArray avoids ConcurrentModificationException + val arrayLength = array.length + var i = 0 + while (i < arrayLength) { + val (k, v) = array(i).asInstanceOf[(K, V)] + if (!p(k, v)) { + this -= k + } + i += 1 + } + } + this + } + + @deprecated("Use mapValuesInPlace instead", "2.13.0") + @inline final def transform(f: (K, V) => V): this.type = mapValuesInPlace(f) + + /** Applies a transformation function to all values contained in this map. + * The transformation function produces new values from existing keys + * associated values. + * + * @param f the transformation to apply + * @return the map itself. + */ + def mapValuesInPlace(f: (K, V) => V): this.type = { + if (!isEmpty) this match { + case hm: mutable.HashMap[_, _] => hm.asInstanceOf[mutable.HashMap[K, V]].mapValuesInPlaceImpl(f) + case tm: concurrent.Map[_, _] => tm.asInstanceOf[concurrent.Map[K, V]].mapValuesInPlaceImpl(f) + case _ => + val array = this.toArray[Any] + val arrayLength = array.length + var i = 0 + while (i < arrayLength) { + val (k, v) = array(i).asInstanceOf[(K, V)] + update(k, f(k, v)) + i += 1 + } + } + this + } + + @deprecated("Use m.clone().addOne((k,v)) instead of m.updated(k, v)", "2.13.0") + def updated[V1 >: V](key: K, value: V1): CC[K, V1] = + clone().asInstanceOf[CC[K, V1]].addOne((key, value)) + + override def knownSize: Int = super[IterableOps].knownSize +} + +/** + * $factoryInfo + * @define coll mutable map + * @define Coll `mutable.Map` + */ +@SerialVersionUID(3L) +object Map extends MapFactory.Delegate[Map](HashMap) { + + @SerialVersionUID(3L) + class WithDefault[K, V](val underlying: Map[K, V], val defaultValue: K => V) + extends AbstractMap[K, V] + with MapOps[K, V, Map, WithDefault[K, V]] with Serializable { + + override def default(key: K): V = defaultValue(key) + + def iterator: scala.collection.Iterator[(K, V)] = underlying.iterator + override def isEmpty: Boolean = underlying.isEmpty + override def knownSize: Int = underlying.knownSize + override def mapFactory: MapFactory[Map] = underlying.mapFactory + + override def clear(): Unit = underlying.clear() + + def get(key: K): Option[V] = underlying.get(key) + + def subtractOne(elem: K): WithDefault.this.type = { underlying.subtractOne(elem); this } + + def addOne(elem: (K, V)): WithDefault.this.type = { underlying.addOne(elem); this } + + override def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]): Map[K, V2] = + underlying.concat(suffix).withDefault(defaultValue) + + override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) + + override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]): WithDefault[K, V] = + new WithDefault[K, V](mapFactory.from(coll), defaultValue) + + override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] = + Map.newBuilder.mapResult((p: Map[K, V]) => new WithDefault[K, V](p, defaultValue)) + } + +} + +/** Explicit instantiation of the `Map` trait to reduce class file size in subclasses. */ +abstract class AbstractMap[K, V] extends scala.collection.AbstractMap[K, V] with Map[K, V] diff --git a/library/src/scala/collection/mutable/MultiMap.scala b/library/src/scala/collection/mutable/MultiMap.scala new file mode 100644 index 000000000000..b06a99b15d51 --- /dev/null +++ b/library/src/scala/collection/mutable/MultiMap.scala @@ -0,0 +1,115 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + + +/** A trait for mutable maps with multiple values assigned to a key. + * + * This class is typically used as a mixin. It turns maps which map `K` + * to `Set[V]` objects into multimaps that map `K` to `V` objects. + * + * @example {{{ + * // first import all necessary types from package `collection.mutable` + * import collection.mutable.{ HashMap, MultiMap, Set } + * + * // to create a `MultiMap` the easiest way is to mixin it into a normal + * // `Map` instance + * val mm = new HashMap[Int, Set[String]] with MultiMap[Int, String] + * + * // to add key-value pairs to a multimap it is important to use + * // the method `addBinding` because standard methods like `+` will + * // overwrite the complete key-value pair instead of adding the + * // value to the existing key + * mm.addBinding(1, "a") + * mm.addBinding(2, "b") + * mm.addBinding(1, "c") + * + * // mm now contains `Map(2 -> Set(b), 1 -> Set(c, a))` + * + * // to check if the multimap contains a value there is method + * // `entryExists`, which allows to traverse the including set + * mm.entryExists(1, _ == "a") == true + * mm.entryExists(1, _ == "b") == false + * mm.entryExists(2, _ == "b") == true + * + * // to remove a previous added value there is the method `removeBinding` + * mm.removeBinding(1, "a") + * mm.entryExists(1, _ == "a") == false + * }}} + * + * @define coll multimap + * @define Coll `MultiMap` + */ +@deprecated("Use a scala.collection.mutable.MultiDict in the scala-collection-contrib module", "2.13.0") +trait MultiMap[K, V] extends Map[K, Set[V]] { + /** Creates a new set. + * + * Classes that use this trait as a mixin can override this method + * to have the desired implementation of sets assigned to new keys. + * By default this is `HashSet`. + * + * @return An empty set of values of type `V`. + */ + protected def makeSet: Set[V] = new HashSet[V] + + /** Assigns the specified `value` to a specified `key`. If the key + * already has a binding to equal to `value`, nothing is changed; + * otherwise a new binding is added for that `key`. + * + * @param key The key to which to bind the new value. + * @param value The value to bind to the key. + * @return A reference to this multimap. + */ + def addBinding(key: K, value: V): this.type = { + get(key) match { + case None => + val set = makeSet + set += value + this(key) = set + case Some(set) => + set += value + } + this + } + + /** Removes the binding of `value` to `key` if it exists, otherwise this + * operation doesn't have any effect. + * + * If this was the last value assigned to the specified key, the + * set assigned to that key will be removed as well. + * + * @param key The key of the binding. + * @param value The value to remove. + * @return A reference to this multimap. + */ + def removeBinding(key: K, value: V): this.type = { + get(key) match { + case None => + case Some(set) => + set -= value + if (set.isEmpty) this -= key + } + this + } + + /** Checks if there exists a binding to `key` such that it satisfies the predicate `p`. + * + * @param key The key for which the predicate is checked. + * @param p The predicate which a value assigned to the key must satisfy. + * @return A boolean if such a binding exists + */ + def entryExists(key: K, p: V => Boolean): Boolean = get(key) match { + case None => false + case Some(set) => set exists p + } +} diff --git a/library/src/scala/collection/mutable/MutationTracker.scala b/library/src/scala/collection/mutable/MutationTracker.scala new file mode 100644 index 000000000000..fe0314068a43 --- /dev/null +++ b/library/src/scala/collection/mutable/MutationTracker.scala @@ -0,0 +1,78 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import java.util.ConcurrentModificationException + +/** + * Utilities to check that mutations to a client that tracks + * its mutations have not occurred since a given point. + * [[Iterator `Iterator`]]s that perform this check automatically + * during iteration can be created by wrapping an `Iterator` + * in a [[MutationTracker.CheckedIterator `CheckedIterator`]], + * or by manually using the [[MutationTracker.checkMutations() `checkMutations`]] + * and [[MutationTracker.checkMutationsForIteration() `checkMutationsForIteration`]] + * methods. + */ +private object MutationTracker { + + /** + * Checks whether or not the actual mutation count differs from + * the expected one, throwing an exception, if it does. + * + * @param expectedCount the expected mutation count + * @param actualCount the actual mutation count + * @param message the exception message in case of mutations + * @throws ConcurrentModificationException if the expected and actual + * mutation counts differ + */ + @throws[ConcurrentModificationException] + def checkMutations(expectedCount: Int, actualCount: Int, message: String): Unit = { + if (actualCount != expectedCount) throw new ConcurrentModificationException(message) + } + + /** + * Checks whether or not the actual mutation count differs from + * the expected one, throwing an exception, if it does. This method + * produces an exception message saying that it was called because a + * backing collection was mutated during iteration. + * + * @param expectedCount the expected mutation count + * @param actualCount the actual mutation count + * @throws ConcurrentModificationException if the expected and actual + * mutation counts differ + */ + @throws[ConcurrentModificationException] + @inline def checkMutationsForIteration(expectedCount: Int, actualCount: Int): Unit = + checkMutations(expectedCount, actualCount, "mutation occurred during iteration") + + /** + * An iterator wrapper that checks if the underlying collection has + * been mutated. + * + * @param underlying the underlying iterator + * @param mutationCount a by-name provider of the current mutation count + * @tparam A the type of the iterator's elements + */ + final class CheckedIterator[A](underlying: Iterator[A], mutationCount: => Int) extends AbstractIterator[A] { + private[this] val expectedCount = mutationCount + + def hasNext: Boolean = { + checkMutationsForIteration(expectedCount, mutationCount) + underlying.hasNext + } + def next(): A = underlying.next() + } +} diff --git a/library/src/scala/collection/mutable/OpenHashMap.scala b/library/src/scala/collection/mutable/OpenHashMap.scala new file mode 100644 index 000000000000..5840a0abc954 --- /dev/null +++ b/library/src/scala/collection/mutable/OpenHashMap.scala @@ -0,0 +1,306 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import java.lang.Integer.numberOfLeadingZeros +import java.util.ConcurrentModificationException +import scala.collection.generic.DefaultSerializable + +/** + * @define Coll `OpenHashMap` + * @define coll open hash map + */ +@deprecated("Use HashMap or one of the specialized versions (LongMap, AnyRefMap) instead of OpenHashMap", "2.13.0") +@SerialVersionUID(3L) +object OpenHashMap extends MapFactory[OpenHashMap] { + + def empty[K, V] = new OpenHashMap[K, V] + def from[K, V](it: IterableOnce[(K, V)]): OpenHashMap[K,V] = empty ++= it + + def newBuilder[K, V]: Builder[(K, V), OpenHashMap[K,V]] = + new GrowableBuilder[(K, V), OpenHashMap[K, V]](empty) + + /** A hash table entry. + * + * The entry is occupied if and only if its `value` is a `Some`; + * deleted if and only if its `value` is `None`. + * If its `key` is not the default value of type `Key`, the entry is occupied. + * If the entry is occupied, `hash` contains the hash value of `key`. + */ + final private class OpenEntry[Key, Value](var key: Key, + var hash: Int, + var value: Option[Value]) + + private[mutable] def nextPositivePowerOfTwo(target: Int): Int = 1 << -numberOfLeadingZeros(target - 1) +} + +/** A mutable hash map based on an open addressing method. The precise scheme is + * undefined, but it should make a reasonable effort to ensure that an insert + * with consecutive hash codes is not unnecessarily penalised. In particular, + * mappings of consecutive integer keys should work without significant + * performance loss. + * + * @tparam Key type of the keys in this map. + * @tparam Value type of the values in this map. + * @param initialSize the initial size of the internal hash table. + * + * @define Coll `OpenHashMap` + * @define coll open hash map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@deprecated("Use HashMap or one of the specialized versions (LongMap, AnyRefMap) instead of OpenHashMap", "2.13.0") +class OpenHashMap[Key, Value](initialSize : Int) + extends AbstractMap[Key, Value] + with MapOps[Key, Value, OpenHashMap, OpenHashMap[Key, Value]] + with StrictOptimizedIterableOps[(Key, Value), Iterable, OpenHashMap[Key, Value]] + with MapFactoryDefaults[Key, Value, OpenHashMap, Iterable] + with DefaultSerializable { + + import OpenHashMap.OpenEntry + private type Entry = OpenEntry[Key, Value] + + /** A default constructor creates a hashmap with initial size `8`. + */ + def this() = this(8) + + override def mapFactory: MapFactory[OpenHashMap] = OpenHashMap + + private[this] val actualInitialSize = OpenHashMap.nextPositivePowerOfTwo(initialSize) + + private[this] var mask = actualInitialSize - 1 + + /** The hash table. + * + * The table's entries are initialized to `null`, indication of an empty slot. + * A slot is either deleted or occupied if and only if the entry is non-`null`. + */ + private[this] var table = new Array[Entry](actualInitialSize) + + private[this] var _size = 0 + private[this] var deleted = 0 + + // Used for tracking inserts so that iterators can determine if concurrent modification has occurred. + private[this] var modCount = 0 + + override def size = _size + override def knownSize: Int = size + private[this] def size_=(s : Int): Unit = _size = s + override def isEmpty: Boolean = _size == 0 + /** Returns a mangled hash code of the provided key. */ + protected def hashOf(key: Key) = { + var h = key.## + h ^= ((h >>> 20) ^ (h >>> 12)) + h ^ (h >>> 7) ^ (h >>> 4) + } + + /** Increase the size of the table. + * Copy only the occupied slots, effectively eliminating the deleted slots. + */ + private[this] def growTable() = { + val oldSize = mask + 1 + val newSize = 4 * oldSize + val oldTable = table + table = new Array[Entry](newSize) + mask = newSize - 1 + oldTable.foreach( entry => + if (entry != null && entry.value != None) + table(findIndex(entry.key, entry.hash)) = entry ) + deleted = 0 + } + + /** Return the index of the first slot in the hash table (in probe order) + * that is, in order of preference, either occupied by the given key, deleted, or empty. + * + * @param hash hash value for `key` + */ + private[this] def findIndex(key: Key, hash: Int): Int = { + var index = hash & mask + var j = 0 + + // Index of the first slot containing a deleted entry, or -1 if none found yet + var firstDeletedIndex = -1 + + var entry = table(index) + while (entry != null) { + if (entry.hash == hash && entry.key == key && entry.value != None) + return index + + if (firstDeletedIndex == -1 && entry.value == None) + firstDeletedIndex = index + + j += 1 + index = (index + j) & mask + entry = table(index) + } + + if (firstDeletedIndex == -1) index else firstDeletedIndex + } + + // TODO refactor `put` to extract `findOrAddEntry` and implement this in terms of that to avoid Some boxing. + override def update(key: Key, value: Value): Unit = put(key, value) + + @deprecatedOverriding("addOne should not be overridden in order to maintain consistency with put.", "2.11.0") + def addOne (kv: (Key, Value)): this.type = { put(kv._1, kv._2); this } + + @deprecatedOverriding("subtractOne should not be overridden in order to maintain consistency with remove.", "2.11.0") + def subtractOne (key: Key): this.type = { remove(key); this } + + override def put(key: Key, value: Value): Option[Value] = + put(key, hashOf(key), value) + + private def put(key: Key, hash: Int, value: Value): Option[Value] = { + if (2 * (size + deleted) > mask) growTable() + val index = findIndex(key, hash) + val entry = table(index) + if (entry == null) { + table(index) = new OpenEntry(key, hash, Some(value)) + modCount += 1 + size += 1 + None + } else { + val res = entry.value + if (entry.value == None) { + entry.key = key + entry.hash = hash + size += 1 + deleted -= 1 + modCount += 1 + } + entry.value = Some(value) + res + } + } + + /** Delete the hash table slot contained in the given entry. */ + @`inline` + private[this] def deleteSlot(entry: Entry) = { + entry.key = null.asInstanceOf[Key] + entry.hash = 0 + entry.value = None + + size -= 1 + deleted += 1 + } + + override def remove(key : Key): Option[Value] = { + val entry = table(findIndex(key, hashOf(key))) + if (entry != null && entry.value != None) { + val res = entry.value + deleteSlot(entry) + res + } else None + } + + def get(key : Key) : Option[Value] = { + val hash = hashOf(key) + var index = hash & mask + var entry = table(index) + var j = 0 + while(entry != null){ + if (entry.hash == hash && + entry.key == key){ + return entry.value + } + + j += 1 + index = (index + j) & mask + entry = table(index) + } + None + } + + /** An iterator over the elements of this map. Use of this iterator follows + * the same contract for concurrent modification as the foreach method. + * + * @return the iterator + */ + def iterator: Iterator[(Key, Value)] = new OpenHashMapIterator[(Key, Value)] { + override protected def nextResult(node: Entry): (Key, Value) = (node.key, node.value.get) + } + + override def keysIterator: Iterator[Key] = new OpenHashMapIterator[Key] { + override protected def nextResult(node: Entry): Key = node.key + } + override def valuesIterator: Iterator[Value] = new OpenHashMapIterator[Value] { + override protected def nextResult(node: Entry): Value = node.value.get + } + + private abstract class OpenHashMapIterator[A] extends AbstractIterator[A] { + private[this] var index = 0 + private[this] val initialModCount = modCount + + private[this] def advance(): Unit = { + if (initialModCount != modCount) throw new ConcurrentModificationException + while((index <= mask) && (table(index) == null || table(index).value == None)) index+=1 + } + + def hasNext = {advance(); index <= mask } + + def next() = { + advance() + val result = table(index) + index += 1 + nextResult(result) + } + protected def nextResult(node: Entry): A + } + + override def clone() = { + val it = new OpenHashMap[Key, Value] + foreachUndeletedEntry(entry => it.put(entry.key, entry.hash, entry.value.get)) + it + } + + /** Loop over the key, value mappings of this map. + * + * The behaviour of modifying the map during an iteration is as follows: + * - Deleting a mapping is always permitted. + * - Changing the value of mapping which is already present is permitted. + * - Anything else is not permitted. It will usually, but not always, throw an exception. + * + * @tparam U The return type of the specified function `f`, return result of which is ignored. + * @param f The function to apply to each key, value mapping. + */ + override def foreach[U](f : ((Key, Value)) => U): Unit = { + val startModCount = modCount + foreachUndeletedEntry(entry => { + if (modCount != startModCount) throw new ConcurrentModificationException + f((entry.key, entry.value.get))} + ) + } + override def foreachEntry[U](f : (Key, Value) => U): Unit = { + val startModCount = modCount + foreachUndeletedEntry(entry => { + if (modCount != startModCount) throw new ConcurrentModificationException + f(entry.key, entry.value.get)} + ) + } + + private[this] def foreachUndeletedEntry(f : Entry => Unit): Unit = { + table.foreach(entry => if (entry != null && entry.value != None) f(entry)) + } + + override def mapValuesInPlace(f : (Key, Value) => Value): this.type = { + foreachUndeletedEntry(entry => entry.value = Some(f(entry.key, entry.value.get))) + this + } + + override def filterInPlace(f : (Key, Value) => Boolean): this.type = { + foreachUndeletedEntry(entry => if (!f(entry.key, entry.value.get)) deleteSlot(entry)) + this + } + + override protected[this] def stringPrefix = "OpenHashMap" +} diff --git a/library/src/scala/collection/mutable/PriorityQueue.scala b/library/src/scala/collection/mutable/PriorityQueue.scala new file mode 100644 index 000000000000..147cffc22a95 --- /dev/null +++ b/library/src/scala/collection/mutable/PriorityQueue.scala @@ -0,0 +1,413 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.collection.generic.DefaultSerializationProxy +import scala.math.Ordering + +/** A heap-based priority queue. + * + * To prioritize elements of type `A` there must be an implicit + * `Ordering[A]` available at creation. Elements are retrieved + * in priority order by using [[dequeue]] or [[dequeueAll]]. + * + * If multiple elements have the same priority as determined by the ordering for this + * `PriorityQueue`, no guarantees are made regarding the order in which those elements + * are returned by `dequeue` or `dequeueAll`. In particular, that means this + * class does not guarantee first-in-first-out behavior, as may be + * incorrectly inferred from the fact that this data structure is + * called a "queue". + * + * Only the `dequeue` and `dequeueAll` methods will return elements in priority + * order (while removing elements from the heap). Standard collection methods + * such as `drop`, `iterator`, `toList` and `toString` use an arbitrary + * iteration order: they will traverse the heap or remove elements + * in whichever order seems most convenient. + * + * Therefore, printing a `PriorityQueue` will not show elements in priority order, + * though the highest-priority element will be printed first. + * To print the elements in order, it's necessary to `dequeue` them. + * To do this non-destructively, duplicate the `PriorityQueue` first; + * the `clone` method is a suitable way to obtain a disposable copy. + * + * Client keys are assumed to be immutable. Mutating keys may violate + * the invariant of the underlying heap-ordered tree. Note that [[clone]] + * does not rebuild the underlying tree. + * + * {{{ + * scala> val pq = collection.mutable.PriorityQueue(1, 2, 5, 3, 7) + * val pq: scala.collection.mutable.PriorityQueue[Int] = PriorityQueue(7, 3, 5, 1, 2) + * + * scala> pq.toList // also not in order + * val res0: List[Int] = List(7, 3, 5, 1, 2) + * + * scala> pq.clone.dequeueAll + * val res1: Seq[Int] = ArraySeq(7, 5, 3, 2, 1) + * }}} + * + * @tparam A type of the elements in this priority queue. + * @param ord implicit ordering used to compare the elements of type `A`. + * + * @define Coll PriorityQueue + * @define coll priority queue + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +sealed class PriorityQueue[A](implicit val ord: Ordering[A]) + extends AbstractIterable[A] + with Iterable[A] + with IterableOps[A, Iterable, PriorityQueue[A]] + with StrictOptimizedIterableOps[A, Iterable, PriorityQueue[A]] + with Builder[A, PriorityQueue[A]] + with Cloneable[PriorityQueue[A]] + with Growable[A] + with Serializable +{ + + private class ResizableArrayAccess[A0] extends ArrayBuffer[A0] { + override def mapInPlace(f: A0 => A0): this.type = { + var i = 1 // see "we do not use array(0)" comment below (???) + val siz = this.size + while (i < siz) { this(i) = f(this(i)); i += 1 } + this + } + + def p_size0 = size0 + def p_size0_=(s: Int) = size0 = s + def p_array = array + def p_ensureSize(n: Int) = super.ensureSize(n) + def p_ensureAdditionalSize(n: Int) = super.ensureSize(size0 + n) + def p_swap(a: Int, b: Int): Unit = { + val h = array(a) + array(a) = array(b) + array(b) = h + } + } + + private val resarr = new ResizableArrayAccess[A] + + // we do not use array(0) + // storing the root of the heap at array(1) simplifies the calculations for + // parent and child indices: for a given index k, the parent of k is k / 2, + // the left child is k * 2, and the right child is k * 2 + 1 + resarr.p_size0 += 1 + /** Alias for [[size]]. */ + def length: Int = resarr.length - 1 // adjust length accordingly + override def size: Int = length + override def knownSize: Int = length + override def isEmpty: Boolean = resarr.p_size0 < 2 + + // not eligible for EvidenceIterableFactoryDefaults since C != CC[A] (PriorityQueue[A] != Iterable[A]) + override protected def fromSpecific(coll: scala.collection.IterableOnce[A]): PriorityQueue[A] = PriorityQueue.from(coll) + override protected def newSpecificBuilder: Builder[A, PriorityQueue[A]] = PriorityQueue.newBuilder + override def empty: PriorityQueue[A] = PriorityQueue.empty + + /** Replace the contents of this $coll with the mapped result. + * + * @param f the mapping function + * @return this $coll + */ + def mapInPlace(f: A => A): this.type = { + resarr.mapInPlace(f) + heapify(1) + this + } + + def result() = this + + private def toA(x: AnyRef): A = x.asInstanceOf[A] + protected def fixUp(as: Array[AnyRef], m: Int): Unit = { + var k: Int = m + // use `ord` directly to avoid allocating `OrderingOps` + while (k > 1 && ord.lt(toA(as(k / 2)), toA(as(k)))) { + resarr.p_swap(k, k / 2) + k = k / 2 + } + } + + protected def fixDown(as: Array[AnyRef], m: Int, n: Int): Boolean = { + // returns true if any swaps were done (used in heapify) + var k: Int = m + while (n >= 2 * k) { + var j = 2 * k + // use `ord` directly to avoid allocating `OrderingOps` + if (j < n && ord.lt(toA(as(j)), toA(as(j + 1)))) + j += 1 + if (ord.gteq(toA(as(k)), toA(as(j)))) + return k != m + else { + val h = as(k) + as(k) = as(j) + as(j) = h + k = j + } + } + k != m + } + + /** Inserts a single element into the priority queue. + * + * @param elem the element to insert. + * @return this $coll. + */ + def addOne(elem: A): this.type = { + resarr.p_ensureAdditionalSize(1) + resarr.p_array(resarr.p_size0) = elem.asInstanceOf[AnyRef] + fixUp(resarr.p_array, resarr.p_size0) + resarr.p_size0 += 1 + this + } + + override def addAll(xs: IterableOnce[A]): this.type = { + val from = resarr.p_size0 + for (x <- xs.iterator) unsafeAdd(x) + heapify(from) + this + } + + private def unsafeAdd(elem: A): Unit = { + // like += but skips fixUp, which breaks the ordering invariant + // a series of unsafeAdds MUST be followed by heapify + resarr.p_ensureAdditionalSize(1) + resarr.p_array(resarr.p_size0) = elem.asInstanceOf[AnyRef] + resarr.p_size0 += 1 + } + + private def heapify(from: Int): Unit = { + // elements at indices 1..from-1 were already in heap order before any adds + // elements at indices from..n are newly added, their order must be fixed + val n = length + + if (from <= 2) { + // no pre-existing order to maintain, do the textbook heapify algorithm + for (i <- n/2 to 1 by -1) fixDown(resarr.p_array, i, n) + } + else if (n - from < 4) { + // for very small adds, doing the simplest fix is faster + for (i <- from to n) fixUp(resarr.p_array, i) + } + else { + var min = from/2 // tracks the minimum element in the queue + val queue = scala.collection.mutable.Queue[Int](min) + + // do fixDown on the parents of all the new elements + // except the parent of the first new element, which is in the queue + // (that parent is treated specially because it might be the root) + for (i <- n/2 until min by -1) { + if (fixDown(resarr.p_array, i, n)) { + // there was a swap, so also need to fixDown i's parent + val parent = i/2 + if (parent < min) { // make sure same parent isn't added twice + min = parent + queue += parent + } + } + } + + while (queue.nonEmpty) { + val i = queue.dequeue() + if (fixDown(resarr.p_array, i, n)) { + val parent = i/2 + if (parent < min && parent > 0) { + // the "parent > 0" is to avoid adding the parent of the root + min = parent + queue += parent + } + } + } + } + } + + /** Adds all elements provided by a `IterableOnce` object + * into the priority queue. + * + * @param xs a iterable object. + * @return a new priority queue containing elements of both `xs` and `this`. + */ + def ++(xs: IterableOnce[A]): PriorityQueue[A] = { this.clone() ++= xs } + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + def enqueue(elems: A*): Unit = { this ++= elems } + + /** Returns the element with the highest priority in the queue, + * and removes this element from the queue. + * + * @return the element with the highest priority. + * @throws NoSuchElementException if no element to remove from heap + */ + def dequeue(): A = + if (resarr.p_size0 > 1) { + resarr.p_size0 = resarr.p_size0 - 1 + val result = resarr.p_array(1) + resarr.p_array(1) = resarr.p_array(resarr.p_size0) + resarr.p_array(resarr.p_size0) = null // erase reference from array + fixDown(resarr.p_array, 1, resarr.p_size0 - 1) + toA(result) + } else + throw new NoSuchElementException("no element to remove from heap") + + /** Dequeues all elements and returns them in a sequence, in priority order. */ + def dequeueAll[A1 >: A]: immutable.Seq[A1] = { + val b = ArrayBuilder.make[Any] + b.sizeHint(size) + while (nonEmpty) { + b += dequeue() + } + immutable.ArraySeq.unsafeWrapArray(b.result()).asInstanceOf[immutable.ArraySeq[A1]] + } + + /** Returns the element with the highest priority in the queue, + * or throws an error if there is no element contained in the queue. + * + * @return the element with the highest priority. + */ + override def head: A = if (resarr.p_size0 > 1) toA(resarr.p_array(1)) else throw new NoSuchElementException("queue is empty") + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + def clear(): Unit = { + resarr.clear() + resarr.p_size0 = 1 + } + + /** Returns an iterator which yields all the elements. + * + * Note: The order of elements returned is undefined. + * If you want to traverse the elements in priority queue + * order, use `clone().dequeueAll.iterator`. + * + * @return an iterator over all the elements. + */ + override def iterator: Iterator[A] = resarr.iterator.drop(1) + + /** Returns the reverse of this priority queue. The new priority queue has + * the same elements as the original, but the opposite ordering. + * + * For example, the element with the highest priority in `pq` has the lowest + * priority in `pq.reverse`, and vice versa. + * + * Ties are handled arbitrarily. Elements with equal priority may or + * may not be reversed with respect to each other. + * + * @return the reversed priority queue. + */ + def reverse: PriorityQueue[A] = { + val revq = new PriorityQueue[A]()(ord.reverse) + // copy the existing data into the new array backwards + // this won't put it exactly into the correct order, + // but will require less fixing than copying it in + // the original order + val n = resarr.p_size0 + revq.resarr.p_ensureSize(n) + revq.resarr.p_size0 = n + val from = resarr.p_array + val to = revq.resarr.p_array + for (i <- 1 until n) to(i) = from(n-i) + revq.heapify(1) + revq + } + + + /** Returns an iterator which yields all the elements in the reverse order + * than that returned by the method `iterator`. + * + * Note: The order of elements returned is undefined. + * + * @return an iterator over all elements sorted in descending order. + */ + def reverseIterator: Iterator[A] = new AbstractIterator[A] { + private[this] var i = resarr.p_size0 - 1 + def hasNext: Boolean = i >= 1 + def next(): A = { + val n = resarr.p_array(i) + i -= 1 + toA(n) + } + } + + /** Returns a regular queue containing the same elements. + * + * Note: the order of elements is undefined. + */ + def toQueue: Queue[A] = new Queue[A] ++= this.iterator + + /** Returns a textual representation of a queue as a string. + * + * @return the string representation of this queue. + */ + override def toString() = toList.mkString("PriorityQueue(", ", ", ")") + + /** Converts this $coll to a list. + * + * Note: the order of elements is undefined. + * + * @return a list containing all elements of this $coll. + */ + override def toList: immutable.List[A] = immutable.List.from(this.iterator) + + /** This method clones the priority queue. + * + * @return a priority queue with the same elements. + */ + override def clone(): PriorityQueue[A] = { + val pq = new PriorityQueue[A] + val n = resarr.p_size0 + pq.resarr.p_ensureSize(n) + java.lang.System.arraycopy(resarr.p_array, 1, pq.resarr.p_array, 1, n-1) + pq.resarr.p_size0 = n + pq + } + + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { + val copied = IterableOnce.elemsToCopyToArray(length, xs.length, start, len) + if (copied > 0) { + Array.copy(resarr.p_array, 1, xs, start, copied) + } + copied + } + + @deprecated("Use `PriorityQueue` instead", "2.13.0") + def orderedCompanion: PriorityQueue.type = PriorityQueue + + protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(PriorityQueue.evidenceIterableFactory[A], this) + + override protected[this] def className = "PriorityQueue" +} + + +@SerialVersionUID(3L) +object PriorityQueue extends SortedIterableFactory[PriorityQueue] { + def newBuilder[A : Ordering]: Builder[A, PriorityQueue[A]] = { + new Builder[A, PriorityQueue[A]] { + val pq = new PriorityQueue[A] + def addOne(elem: A): this.type = { pq.unsafeAdd(elem); this } + def result(): PriorityQueue[A] = { pq.heapify(1); pq } + def clear(): Unit = pq.clear() + } + } + + def empty[A : Ordering]: PriorityQueue[A] = new PriorityQueue[A] + + def from[E : Ordering](it: IterableOnce[E]): PriorityQueue[E] = { + val b = newBuilder[E] + b ++= it + b.result() + } +} diff --git a/library/src/scala/collection/mutable/Queue.scala b/library/src/scala/collection/mutable/Queue.scala new file mode 100644 index 000000000000..cc3dad2e2495 --- /dev/null +++ b/library/src/scala/collection/mutable/Queue.scala @@ -0,0 +1,138 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.annotation.nowarn +import scala.collection.generic.DefaultSerializable + + +/** `Queue` objects implement data structures that allow to + * insert and retrieve elements in a first-in-first-out (FIFO) manner. + * + * @define Coll `mutable.Queue` + * @define coll mutable queue + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +class Queue[A] protected (array: Array[AnyRef], start: Int, end: Int) + extends ArrayDeque[A](array, start, end) + with IndexedSeqOps[A, Queue, Queue[A]] + with StrictOptimizedSeqOps[A, Queue, Queue[A]] + with IterableFactoryDefaults[A, Queue] + with ArrayDequeOps[A, Queue, Queue[A]] + with Cloneable[Queue[A]] + with DefaultSerializable { + + def this(initialSize: Int = ArrayDeque.DefaultInitialSize) = + this(ArrayDeque.alloc(initialSize), start = 0, end = 0) + + override def iterableFactory: SeqFactory[Queue] = Queue + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "Queue" + + /** + * Add elements to the end of this queue + * + * @param elem + * @return this + */ + def enqueue(elem: A): this.type = this += elem + + /** Enqueue two or more elements at the end of the queue. The last element + * of the sequence will be on end of the queue. + * + * @param elems the element sequence. + * @return this + */ + def enqueue(elem1: A, elem2: A, elems: A*): this.type = enqueue(elem1).enqueue(elem2).enqueueAll(elems) + + /** Enqueues all elements in the given iterable object into the queue. The + * last element in the iterable object will be on front of the new queue. + * + * @param elems the iterable object. + * @return this + */ + def enqueueAll(elems: scala.collection.IterableOnce[A]): this.type = this ++= elems + + /** + * Removes the first element from this queue and returns it + * + * @return + * @throws NoSuchElementException when queue is empty + */ + def dequeue(): A = removeHead() + + /** Returns the first element in the queue which satisfies the + * given predicate, and removes this element from the queue. + * + * @param p the predicate used for choosing the first element + * @return the first element of the queue for which p yields true + */ + def dequeueFirst(p: A => Boolean): Option[A] = + removeFirst(p) + + /** Returns all elements in the queue which satisfy the + * given predicate, and removes those elements from the queue. + * + * @param p the predicate used for choosing elements + * @return a sequence of all elements in the queue for which + * p yields true. + */ + def dequeueAll(p: A => Boolean): scala.collection.immutable.Seq[A] = + removeAll(p) + + /** + * Returns and dequeues all elements from the queue which satisfy the given predicate + * + * @param f the predicate used for choosing elements + * @return The removed elements + */ + def dequeueWhile(f: A => Boolean): scala.collection.Seq[A] = removeHeadWhile(f) + + /** Returns the first element in the queue, or throws an error if there + * is no element contained in the queue. + * + * @return the first element. + */ + @`inline` final def front: A = head + + override protected def klone(): Queue[A] = { + val bf = newSpecificBuilder + bf ++= this + bf.result() + } + + override protected def ofArray(array: Array[AnyRef], end: Int): Queue[A] = + new Queue(array, start = 0, end) + +} + +/** + * $factoryInfo + * @define coll queue + * @define Coll `Queue` + */ +@SerialVersionUID(3L) +object Queue extends StrictOptimizedSeqFactory[Queue] { + + def from[A](source: IterableOnce[A]): Queue[A] = empty ++= source + + def empty[A]: Queue[A] = new Queue + + def newBuilder[A]: Builder[A, Queue[A]] = new GrowableBuilder[A, Queue[A]](empty) + +} diff --git a/library/src/scala/collection/mutable/RedBlackTree.scala b/library/src/scala/collection/mutable/RedBlackTree.scala new file mode 100644 index 000000000000..aca36f0271d8 --- /dev/null +++ b/library/src/scala/collection/mutable/RedBlackTree.scala @@ -0,0 +1,652 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.mutable + +import scala.annotation.tailrec +import collection.{AbstractIterator, Iterator} +import java.lang.String + +/** + * An object containing the red-black tree implementation used by mutable `TreeMaps`. + * + * The trees implemented in this object are *not* thread safe. + */ +private[collection] object RedBlackTree { + + // ---- class structure ---- + + // For performance reasons, this implementation uses `null` references to represent leaves instead of a sentinel node. + // Currently, the internal nodes do not store their subtree size - only the tree object keeps track of their size. + // Therefore, while obtaining the size of the whole tree is O(1), knowing the number of entries inside a range is O(n) + // on the size of the range. + + final class Tree[A, B](var root: Node[A, B], var size: Int) { + def treeCopy(): Tree[A, B] = new Tree(copyTree(root), size) + } + + final class Node[A, B](var key: A, var value: B, var red: Boolean, var left: Node[A, B], var right: Node[A, B], var parent: Node[A, B]) { + override def toString: String = "Node(" + key + ", " + value + ", " + red + ", " + left + ", " + right + ")" + } + + object Tree { + def empty[A, B]: Tree[A, B] = new Tree(null, 0) + } + + object Node { + + @`inline` def apply[A, B](key: A, value: B, red: Boolean, + left: Node[A, B], right: Node[A, B], parent: Node[A, B]): Node[A, B] = + new Node(key, value, red, left, right, parent) + + @`inline` def leaf[A, B](key: A, value: B, red: Boolean, parent: Node[A, B]): Node[A, B] = + new Node(key, value, red, null, null, parent) + + def unapply[A, B](t: Node[A, B]) = Some((t.key, t.value, t.left, t.right, t.parent)) + } + + // ---- getters ---- + + def isRed(node: Node[_, _]) = (node ne null) && node.red + def isBlack(node: Node[_, _]) = (node eq null) || !node.red + + // ---- size ---- + + def size(node: Node[_, _]): Int = if (node eq null) 0 else 1 + size(node.left) + size(node.right) + def size(tree: Tree[_, _]): Int = tree.size + def isEmpty(tree: Tree[_, _]) = tree.root eq null + def clear(tree: Tree[_, _]): Unit = { tree.root = null; tree.size = 0 } + + // ---- search ---- + + def get[A: Ordering, B](tree: Tree[A, B], key: A): Option[B] = getNode(tree.root, key) match { + case null => None + case node => Some(node.value) + } + + @tailrec private[this] def getNode[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = + if (node eq null) null + else { + val cmp = ord.compare(key, node.key) + if (cmp < 0) getNode(node.left, key) + else if (cmp > 0) getNode(node.right, key) + else node + } + + def contains[A: Ordering](tree: Tree[A, _], key: A): Boolean = getNode(tree.root, key) ne null + + def min[A, B](tree: Tree[A, B]): Option[(A, B)] = minNode(tree.root) match { + case null => None + case node => Some((node.key, node.value)) + } + + def minKey[A](tree: Tree[A, _]): Option[A] = minNode(tree.root) match { + case null => None + case node => Some(node.key) + } + + private def minNode[A, B](node: Node[A, B]): Node[A, B] = + if (node eq null) null else minNodeNonNull(node) + + @tailrec def minNodeNonNull[A, B](node: Node[A, B]): Node[A, B] = + if (node.left eq null) node else minNodeNonNull(node.left) + + def max[A, B](tree: Tree[A, B]): Option[(A, B)] = maxNode(tree.root) match { + case null => None + case node => Some((node.key, node.value)) + } + + def maxKey[A](tree: Tree[A, _]): Option[A] = maxNode(tree.root) match { + case null => None + case node => Some(node.key) + } + + private def maxNode[A, B](node: Node[A, B]): Node[A, B] = + if (node eq null) null else maxNodeNonNull(node) + + @tailrec def maxNodeNonNull[A, B](node: Node[A, B]): Node[A, B] = + if (node.right eq null) node else maxNodeNonNull(node.right) + + /** + * Returns the first (lowest) map entry with a key equal or greater than `key`. Returns `None` if there is no such + * node. + */ + def minAfter[A, B](tree: Tree[A, B], key: A)(implicit ord: Ordering[A]): Option[(A, B)] = + minNodeAfter(tree.root, key) match { + case null => None + case node => Some((node.key, node.value)) + } + + def minKeyAfter[A](tree: Tree[A, _], key: A)(implicit ord: Ordering[A]): Option[A] = + minNodeAfter(tree.root, key) match { + case null => None + case node => Some(node.key) + } + + private[this] def minNodeAfter[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = { + if (node eq null) null + else { + var y: Node[A, B] = null + var x = node + var cmp = 1 + while ((x ne null) && cmp != 0) { + y = x + cmp = ord.compare(key, x.key) + x = if (cmp < 0) x.left else x.right + } + if (cmp <= 0) y else successor(y) + } + } + + /** + * Returns the last (highest) map entry with a key smaller than `key`. Returns `None` if there is no such node. + */ + def maxBefore[A, B](tree: Tree[A, B], key: A)(implicit ord: Ordering[A]): Option[(A, B)] = + maxNodeBefore(tree.root, key) match { + case null => None + case node => Some((node.key, node.value)) + } + + def maxKeyBefore[A](tree: Tree[A, _], key: A)(implicit ord: Ordering[A]): Option[A] = + maxNodeBefore(tree.root, key) match { + case null => None + case node => Some(node.key) + } + + private[this] def maxNodeBefore[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = { + if (node eq null) null + else { + var y: Node[A, B] = null + var x = node + var cmp = 1 + while ((x ne null) && cmp != 0) { + y = x + cmp = ord.compare(key, x.key) + x = if (cmp < 0) x.left else x.right + } + if (cmp > 0) y else predecessor(y) + } + } + + // ---- insertion ---- + + def insert[A, B](tree: Tree[A, B], key: A, value: B)(implicit ord: Ordering[A]): Unit = { + var y: Node[A, B] = null + var x = tree.root + var cmp = 1 + while ((x ne null) && cmp != 0) { + y = x + cmp = ord.compare(key, x.key) + x = if (cmp < 0) x.left else x.right + } + + if (cmp == 0) y.value = value + else { + val z = Node.leaf(key, value, red = true, y) + + if (y eq null) tree.root = z + else if (cmp < 0) y.left = z + else y.right = z + + fixAfterInsert(tree, z) + tree.size += 1 + } + } + + private[this] def fixAfterInsert[A, B](tree: Tree[A, B], node: Node[A, B]): Unit = { + var z = node + while (isRed(z.parent)) { + if (z.parent eq z.parent.parent.left) { + val y = z.parent.parent.right + if (isRed(y)) { + z.parent.red = false + y.red = false + z.parent.parent.red = true + z = z.parent.parent + } else { + if (z eq z.parent.right) { + z = z.parent + rotateLeft(tree, z) + } + z.parent.red = false + z.parent.parent.red = true + rotateRight(tree, z.parent.parent) + } + } else { // symmetric cases + val y = z.parent.parent.left + if (isRed(y)) { + z.parent.red = false + y.red = false + z.parent.parent.red = true + z = z.parent.parent + } else { + if (z eq z.parent.left) { + z = z.parent + rotateRight(tree, z) + } + z.parent.red = false + z.parent.parent.red = true + rotateLeft(tree, z.parent.parent) + } + } + } + tree.root.red = false + } + + // ---- deletion ---- + + def delete[A, B](tree: Tree[A, B], key: A)(implicit ord: Ordering[A]): Unit = { + val z = getNode(tree.root, key) + if (z ne null) { + var y = z + var yIsRed = y.red + var x: Node[A, B] = null + var xParent: Node[A, B] = null + + if (z.left eq null) { + x = z.right + transplant(tree, z, z.right) + xParent = z.parent + } + else if (z.right eq null) { + x = z.left + transplant(tree, z, z.left) + xParent = z.parent + } + else { + y = minNodeNonNull(z.right) + yIsRed = y.red + x = y.right + + if (y.parent eq z) xParent = y + else { + xParent = y.parent + transplant(tree, y, y.right) + y.right = z.right + y.right.parent = y + } + transplant(tree, z, y) + y.left = z.left + y.left.parent = y + y.red = z.red + } + + if (!yIsRed) fixAfterDelete(tree, x, xParent) + tree.size -= 1 + } + } + + private[this] def fixAfterDelete[A, B](tree: Tree[A, B], node: Node[A, B], parent: Node[A, B]): Unit = { + var x = node + var xParent = parent + while ((x ne tree.root) && isBlack(x)) { + if (x eq xParent.left) { + var w = xParent.right + // assert(w ne null) + + if (w.red) { + w.red = false + xParent.red = true + rotateLeft(tree, xParent) + w = xParent.right + } + if (isBlack(w.left) && isBlack(w.right)) { + w.red = true + x = xParent + } else { + if (isBlack(w.right)) { + w.left.red = false + w.red = true + rotateRight(tree, w) + w = xParent.right + } + w.red = xParent.red + xParent.red = false + w.right.red = false + rotateLeft(tree, xParent) + x = tree.root + } + } else { // symmetric cases + var w = xParent.left + // assert(w ne null) + + if (w.red) { + w.red = false + xParent.red = true + rotateRight(tree, xParent) + w = xParent.left + } + if (isBlack(w.right) && isBlack(w.left)) { + w.red = true + x = xParent + } else { + if (isBlack(w.left)) { + w.right.red = false + w.red = true + rotateLeft(tree, w) + w = xParent.left + } + w.red = xParent.red + xParent.red = false + w.left.red = false + rotateRight(tree, xParent) + x = tree.root + } + } + xParent = x.parent + } + if (x ne null) x.red = false + } + + // ---- helpers ---- + + /** + * Returns the node that follows `node` in an in-order tree traversal. If `node` has the maximum key (and is, + * therefore, the last node), this method returns `null`. + */ + private[this] def successor[A, B](node: Node[A, B]): Node[A, B] = { + if (node.right ne null) minNodeNonNull(node.right) + else { + var x = node + var y = x.parent + while ((y ne null) && (x eq y.right)) { + x = y + y = y.parent + } + y + } + } + + /** + * Returns the node that precedes `node` in an in-order tree traversal. If `node` has the minimum key (and is, + * therefore, the first node), this method returns `null`. + */ + private[this] def predecessor[A, B](node: Node[A, B]): Node[A, B] = { + if (node.left ne null) maxNodeNonNull(node.left) + else { + var x = node + var y = x.parent + while ((y ne null) && (x eq y.left)) { + x = y + y = y.parent + } + y + } + } + + private[this] def rotateLeft[A, B](tree: Tree[A, B], x: Node[A, B]): Unit = if (x ne null) { + // assert(x.right ne null) + val y = x.right + x.right = y.left + + if (y.left ne null) y.left.parent = x + y.parent = x.parent + + if (x.parent eq null) tree.root = y + else if (x eq x.parent.left) x.parent.left = y + else x.parent.right = y + + y.left = x + x.parent = y + } + + private[this] def rotateRight[A, B](tree: Tree[A, B], x: Node[A, B]): Unit = if (x ne null) { + // assert(x.left ne null) + val y = x.left + x.left = y.right + + if (y.right ne null) y.right.parent = x + y.parent = x.parent + + if (x.parent eq null) tree.root = y + else if (x eq x.parent.right) x.parent.right = y + else x.parent.left = y + + y.right = x + x.parent = y + } + + /** + * Transplant the node `from` to the place of node `to`. This is done by setting `from` as a child of `to`'s previous + * parent and setting `from`'s parent to the `to`'s previous parent. The children of `from` are left unchanged. + */ + private[this] def transplant[A, B](tree: Tree[A, B], to: Node[A, B], from: Node[A, B]): Unit = { + if (to.parent eq null) tree.root = from + else if (to eq to.parent.left) to.parent.left = from + else to.parent.right = from + + if (from ne null) from.parent = to.parent + } + + // ---- tree traversal ---- + + def foreach[A, B, U](tree: Tree[A, B], f: ((A, B)) => U): Unit = foreachNode(tree.root, f) + + private[this] def foreachNode[A, B, U](node: Node[A, B], f: ((A, B)) => U): Unit = + if (node ne null) foreachNodeNonNull(node, f) + + private[this] def foreachNodeNonNull[A, B, U](node: Node[A, B], f: ((A, B)) => U): Unit = { + if (node.left ne null) foreachNodeNonNull(node.left, f) + f((node.key, node.value)) + if (node.right ne null) foreachNodeNonNull(node.right, f) + } + + def foreachKey[A, U](tree: Tree[A, _], f: A => U): Unit = { + def g(node: Node[A, _]): Unit = { + val l = node.left + if(l ne null) g(l) + f(node.key) + val r = node.right + if(r ne null) g(r) + } + val r = tree.root + if(r ne null) g(r) + } + + def foreachEntry[A, B, U](tree: Tree[A, B], f: (A, B) => U): Unit = { + def g(node: Node[A, B]): Unit = { + val l = node.left + if(l ne null) g(l) + f(node.key, node.value) + val r = node.right + if(r ne null) g(r) + } + val r = tree.root + if(r ne null) g(r) + } + + def transform[A, B](tree: Tree[A, B], f: (A, B) => B): Unit = transformNode(tree.root, f) + + private[this] def transformNode[A, B, U](node: Node[A, B], f: (A, B) => B): Unit = + if (node ne null) transformNodeNonNull(node, f) + + private[this] def transformNodeNonNull[A, B, U](node: Node[A, B], f: (A, B) => B): Unit = { + if (node.left ne null) transformNodeNonNull(node.left, f) + node.value = f(node.key, node.value) + if (node.right ne null) transformNodeNonNull(node.right, f) + } + + def iterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None, end: Option[A] = None): Iterator[(A, B)] = + new EntriesIterator(tree, start, end) + + def keysIterator[A: Ordering](tree: Tree[A, _], start: Option[A] = None, end: Option[A] = None): Iterator[A] = + new KeysIterator(tree, start, end) + + def valuesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A] = None, end: Option[A] = None): Iterator[B] = + new ValuesIterator(tree, start, end) + + private[this] abstract class TreeIterator[A, B, R](tree: Tree[A, B], start: Option[A], end: Option[A]) + (implicit ord: Ordering[A]) extends AbstractIterator[R] { + + protected def nextResult(node: Node[A, B]): R + + def hasNext: Boolean = nextNode ne null + + @throws[NoSuchElementException] + def next(): R = nextNode match { + case null => throw new NoSuchElementException("next on empty iterator") + case node => + nextNode = successor(node) + setNullIfAfterEnd() + nextResult(node) + } + + private[this] var nextNode: Node[A, B] = start match { + case None => minNode(tree.root) + case Some(from) => minNodeAfter(tree.root, from) + } + + private[this] def setNullIfAfterEnd(): Unit = + if (end.isDefined && (nextNode ne null) && ord.compare(nextNode.key, end.get) >= 0) + nextNode = null + + setNullIfAfterEnd() + } + + private[this] final class EntriesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A], end: Option[A]) + extends TreeIterator[A, B, (A, B)](tree, start, end) { + + def nextResult(node: Node[A, B]) = (node.key, node.value) + } + + private[this] final class KeysIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A], end: Option[A]) + extends TreeIterator[A, B, A](tree, start, end) { + + def nextResult(node: Node[A, B]) = node.key + } + + private[this] final class ValuesIterator[A: Ordering, B](tree: Tree[A, B], start: Option[A], end: Option[A]) + extends TreeIterator[A, B, B](tree, start, end) { + + def nextResult(node: Node[A, B]) = node.value + } + + // ---- debugging ---- + + /** + * Checks if the tree is in a valid state. That happens if: + * - It is a valid binary search tree; + * - All red-black properties are satisfied; + * - All non-null nodes have their `parent` reference correct; + * - The size variable in `tree` corresponds to the actual size of the tree. + */ + def isValid[A: Ordering, B](tree: Tree[A, B]): Boolean = + isValidBST(tree.root) && hasProperParentRefs(tree) && isValidRedBlackTree(tree) && size(tree.root) == tree.size + + /** + * Returns true if all non-null nodes have their `parent` reference correct. + */ + private[this] def hasProperParentRefs[A, B](tree: Tree[A, B]): Boolean = { + + def hasProperParentRefs(node: Node[A, B]): Boolean = { + if (node eq null) true + else { + if ((node.left ne null) && (node.left.parent ne node) || + (node.right ne null) && (node.right.parent ne node)) false + else hasProperParentRefs(node.left) && hasProperParentRefs(node.right) + } + } + + if(tree.root eq null) true + else (tree.root.parent eq null) && hasProperParentRefs(tree.root) + } + + /** + * Returns true if this node follows the properties of a binary search tree. + */ + private[this] def isValidBST[A, B](node: Node[A, B])(implicit ord: Ordering[A]): Boolean = { + if (node eq null) true + else { + if ((node.left ne null) && (ord.compare(node.key, node.left.key) <= 0) || + (node.right ne null) && (ord.compare(node.key, node.right.key) >= 0)) false + else isValidBST(node.left) && isValidBST(node.right) + } + } + + /** + * Returns true if the tree has all the red-black tree properties: if the root node is black, if all children of red + * nodes are black and if the path from any node to any of its null children has the same number of black nodes. + */ + private[this] def isValidRedBlackTree[A, B](tree: Tree[A, B]): Boolean = { + + def noRedAfterRed(node: Node[A, B]): Boolean = { + if (node eq null) true + else if (node.red && (isRed(node.left) || isRed(node.right))) false + else noRedAfterRed(node.left) && noRedAfterRed(node.right) + } + + def blackHeight(node: Node[A, B]): Int = { + if (node eq null) 1 + else { + val lh = blackHeight(node.left) + val rh = blackHeight(node.right) + + if (lh == -1 || lh != rh) -1 + else if (isRed(node)) lh + else lh + 1 + } + } + + isBlack(tree.root) && noRedAfterRed(tree.root) && blackHeight(tree.root) >= 0 + } + + // building + + /** Build a Tree suitable for a TreeSet from an ordered sequence of keys */ + def fromOrderedKeys[A](xs: Iterator[A], size: Int): Tree[A, Null] = { + val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes + def f(level: Int, size: Int): Node[A, Null] = size match { + case 0 => null + case 1 => new Node(xs.next(), null, level == maxUsedDepth && level != 1, null, null, null) + case n => + val leftSize = (size-1)/2 + val left = f(level+1, leftSize) + val x = xs.next() + val right = f(level+1, size-1-leftSize) + val n = new Node(x, null, red = false, left, right, null) + if(left ne null) left.parent = n + right.parent = n + n + } + new Tree(f(1, size), size) + } + + /** Build a Tree suitable for a TreeMap from an ordered sequence of key/value pairs */ + def fromOrderedEntries[A, B](xs: Iterator[(A, B)], size: Int): Tree[A, B] = { + val maxUsedDepth = 32 - Integer.numberOfLeadingZeros(size) // maximum depth of non-leaf nodes + def f(level: Int, size: Int): Node[A, B] = size match { + case 0 => null + case 1 => + val (k, v) = xs.next() + new Node(k, v, level == maxUsedDepth && level != 1, null, null, null) + case n => + val leftSize = (size-1)/2 + val left = f(level+1, leftSize) + val (k, v) = xs.next() + val right = f(level+1, size-1-leftSize) + val n = new Node(k, v, red = false, left, right, null) + if(left ne null) left.parent = n + right.parent = n + n + } + new Tree(f(1, size), size) + } + + def copyTree[A, B](n: Node[A, B]): Node[A, B] = + if(n eq null) null else { + val c = new Node(n.key, n.value, n.red, copyTree(n.left), copyTree(n.right), null) + if(c.left != null) c.left.parent = c + if(c.right != null) c.right.parent = c + c + } +} diff --git a/library/src/scala/collection/mutable/ReusableBuilder.scala b/library/src/scala/collection/mutable/ReusableBuilder.scala new file mode 100644 index 000000000000..c8565d6953f1 --- /dev/null +++ b/library/src/scala/collection/mutable/ReusableBuilder.scala @@ -0,0 +1,55 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + + +/** `ReusableBuilder` is a marker trait that indicates that a `Builder` + * can be reused to build more than one instance of a collection. In + * particular, calling `result()` followed by `clear()` will produce a + * collection and reset the builder to begin building a new collection + * of the same type. + * + * In general no method other than `clear()` may be called after `result()`. + * It is up to subclasses to implement and to document other allowed sequences + * of operations (e.g. calling other methods after `result()` in order to obtain + * different snapshots of a collection under construction). + * + * @tparam Elem the type of elements that get added to the builder. + * @tparam To the type of collection that it produced. + * + * @define multipleResults + * + * This Builder can be reused after calling `result()` without an + * intermediate call to `clear()` in order to build multiple related results. + */ +trait ReusableBuilder[-Elem, +To] extends Builder[Elem, To] { + /** Clears the contents of this builder. + * After execution of this method, the builder will contain no elements. + * + * If executed immediately after a call to `result()`, this allows a new + * instance of the same type of collection to be built. + */ + override def clear(): Unit // Note: overriding for Scaladoc only! + + /** Produces a collection from the added elements. + * + * After a call to `result`, the behavior of all other methods is undefined + * save for `clear()`. If `clear()` is called, then the builder is reset and + * may be used to build another instance. + * + * @return a collection containing the elements added to this builder. + */ + override def result(): To // Note: overriding for Scaladoc only! +} diff --git a/library/src/scala/collection/mutable/Seq.scala b/library/src/scala/collection/mutable/Seq.scala new file mode 100644 index 000000000000..afabb834a63f --- /dev/null +++ b/library/src/scala/collection/mutable/Seq.scala @@ -0,0 +1,67 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + +import scala.collection.{IterableFactoryDefaults, SeqFactory} + +trait Seq[A] + extends Iterable[A] + with collection.Seq[A] + with SeqOps[A, Seq, Seq[A]] + with IterableFactoryDefaults[A, Seq] { + + override def iterableFactory: SeqFactory[Seq] = Seq +} + +/** + * $factoryInfo + * @define coll mutable sequence + * @define Coll `mutable.Seq` + */ +@SerialVersionUID(3L) +object Seq extends SeqFactory.Delegate[Seq](ArrayBuffer) + +/** + * @define coll mutable sequence + * @define Coll `mutable.Seq` + */ +trait SeqOps[A, +CC[_], +C <: AnyRef] + extends collection.SeqOps[A, CC, C] + with Cloneable[C] { + + override def clone(): C = { + val b = newSpecificBuilder + b ++= this + b.result() + } + + /** Replaces element at given index with a new value. + * + * @param idx the index of the element to replace. + * @param elem the new value. + * @throws IndexOutOfBoundsException if the index is not valid. + */ + @throws[IndexOutOfBoundsException] + def update(idx: Int, elem: A): Unit + + @deprecated("Use `mapInPlace` on an `IndexedSeq` instead", "2.13.0") + @`inline`final def transform(f: A => A): this.type = { + var i = 0 + val siz = size + while (i < siz) { this(i) = f(this(i)); i += 1 } + this + } +} + +/** Explicit instantiation of the `Seq` trait to reduce class file size in subclasses. */ +abstract class AbstractSeq[A] extends scala.collection.AbstractSeq[A] with Seq[A] diff --git a/library/src/scala/collection/mutable/SeqMap.scala b/library/src/scala/collection/mutable/SeqMap.scala new file mode 100644 index 000000000000..dda2c47c3447 --- /dev/null +++ b/library/src/scala/collection/mutable/SeqMap.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +/** + * A generic trait for ordered mutable maps. Concrete classes have to provide + * functionality for the abstract methods in `SeqMap`. + * + * Note that when checking for equality [[SeqMap]] does not take into account + * ordering. + * + * @tparam K the type of the keys contained in this linked map. + * @tparam V the type of the values associated with the keys in this linked map. + * + * @define coll mutable Seq map + * @define Coll `mutable.SeqMap` + */ + +trait SeqMap[K, V] extends Map[K, V] + with collection.SeqMap[K, V] + with MapOps[K, V, SeqMap, SeqMap[K, V]] + with MapFactoryDefaults[K, V, SeqMap, Iterable] { + override def mapFactory: MapFactory[SeqMap] = SeqMap +} + +object SeqMap extends MapFactory.Delegate[SeqMap](LinkedHashMap) diff --git a/library/src/scala/collection/mutable/Set.scala b/library/src/scala/collection/mutable/Set.scala new file mode 100644 index 000000000000..cede5411a349 --- /dev/null +++ b/library/src/scala/collection/mutable/Set.scala @@ -0,0 +1,122 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + +import scala.collection.{IterableFactory, IterableFactoryDefaults, IterableOps} + +/** Base trait for mutable sets */ +trait Set[A] + extends Iterable[A] + with collection.Set[A] + with SetOps[A, Set, Set[A]] + with IterableFactoryDefaults[A, Set] { + + override def iterableFactory: IterableFactory[Set] = Set +} + +/** + * @define coll mutable set + * @define Coll `mutable.Set` + */ +trait SetOps[A, +CC[X], +C <: SetOps[A, CC, C]] + extends collection.SetOps[A, CC, C] + with IterableOps[A, CC, C] // only needed so we can use super[IterableOps] below + with Cloneable[C] + with Builder[A, C] + with Growable[A] + with Shrinkable[A] { + + def result(): C = coll + + /** Check whether the set contains the given element, and add it if not. + * + * @param elem the element to be added + * @return true if the element was added + */ + def add(elem: A): Boolean = + !contains(elem) && { + coll += elem; true + } + + /** Updates the presence of a single element in this set. + * + * This method allows one to add or remove an element `elem` + * from this set depending on the value of parameter `included`. + * Typically, one would use the following syntax: + * {{{ + * set(elem) = true // adds element + * set(elem) = false // removes element + * }}} + * + * @param elem the element to be added or removed + * @param included a flag indicating whether element should be included or excluded. + */ + def update(elem: A, included: Boolean): Unit = { + if (included) add(elem) + else remove(elem) + } + + /** Removes an element from this set. + * + * @param elem the element to be removed + * @return true if this set contained the element before it was removed + */ + def remove(elem: A): Boolean = { + val res = contains(elem) + coll -= elem + res + } + + def diff(that: collection.Set[A]): C = + foldLeft(empty)((result, elem) => if (that contains elem) result else result += elem) + + @deprecated("Use filterInPlace instead", "2.13.0") + @inline final def retain(p: A => Boolean): Unit = filterInPlace(p) + + /** Removes all elements from the set for which do not satisfy a predicate. + * @param p the predicate used to test elements. Only elements for + * which `p` returns `true` are retained in the set; all others + * are removed. + */ + def filterInPlace(p: A => Boolean): this.type = { + if (nonEmpty) { + val array = this.toArray[Any] // scala/bug#7269 toArray avoids ConcurrentModificationException + val arrayLength = array.length + var i = 0 + while (i < arrayLength) { + val elem = array(i).asInstanceOf[A] + if (!p(elem)) { + this -= elem + } + i += 1 + } + } + this + } + + override def clone(): C = empty ++= this + + override def knownSize: Int = super[IterableOps].knownSize +} + +/** + * $factoryInfo + * @define coll mutable set + * @define Coll `mutable.Set` + */ +@SerialVersionUID(3L) +object Set extends IterableFactory.Delegate[Set](HashSet) + + +/** Explicit instantiation of the `Set` trait to reduce class file size in subclasses. */ +abstract class AbstractSet[A] extends scala.collection.AbstractSet[A] with Set[A] diff --git a/library/src/scala/collection/mutable/Shrinkable.scala b/library/src/scala/collection/mutable/Shrinkable.scala new file mode 100644 index 000000000000..b068f61bf8f5 --- /dev/null +++ b/library/src/scala/collection/mutable/Shrinkable.scala @@ -0,0 +1,79 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.mutable + +import scala.annotation.tailrec + +/** This trait forms part of collections that can be reduced + * using a `-=` operator. + * + * @define coll shrinkable collection + * @define Coll `Shrinkable` + */ +trait Shrinkable[-A] { + + /** Removes a single element from this $coll. + * + * @param elem the element to remove. + * @return the $coll itself + */ + def subtractOne(elem: A): this.type + + /** Alias for `subtractOne` */ + @`inline` final def -= (elem: A): this.type = subtractOne(elem) + + /** Removes two or more elements from this $coll. + * + * @param elem1 the first element to remove. + * @param elem2 the second element to remove. + * @param elems the remaining elements to remove. + * @return the $coll itself + */ + @deprecated("Use `--=` aka `subtractAll` instead of varargs `-=`; infix operations with an operand of multiple args will be deprecated", "2.13.3") + def -= (elem1: A, elem2: A, elems: A*): this.type = { + this -= elem1 + this -= elem2 + this --= elems + } + + /** Removes all elements produced by an iterator from this $coll. + * + * @param xs the iterator producing the elements to remove. + * @return the $coll itself + */ + def subtractAll(xs: collection.IterableOnce[A]): this.type = { + @tailrec def loop(xs: collection.LinearSeq[A]): Unit = { + if (xs.nonEmpty) { + subtractOne(xs.head) + loop(xs.tail) + } + } + if (xs.asInstanceOf[AnyRef] eq this) { // avoid mutating under our own iterator + xs match { + case xs: Clearable => xs.clear() + case xs => subtractAll(Buffer.from(xs)) + } + } else { + xs match { + case xs: collection.LinearSeq[A] => loop(xs) + case xs => xs.iterator.foreach(subtractOne) + } + } + this + } + + /** Alias for `subtractAll` */ + @`inline` final def --= (xs: collection.IterableOnce[A]): this.type = subtractAll(xs) + +} diff --git a/library/src/scala/collection/mutable/SortedMap.scala b/library/src/scala/collection/mutable/SortedMap.scala new file mode 100644 index 000000000000..1884840f91e2 --- /dev/null +++ b/library/src/scala/collection/mutable/SortedMap.scala @@ -0,0 +1,103 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.mutable + +import scala.collection.{SortedMapFactory, SortedMapFactoryDefaults} + +/** + * Base type for mutable sorted map collections + */ +trait SortedMap[K, V] + extends collection.SortedMap[K, V] + with Map[K, V] + with SortedMapOps[K, V, SortedMap, SortedMap[K, V]] + with SortedMapFactoryDefaults[K, V, SortedMap, Iterable, Map] { + + override def unsorted: Map[K, V] = this + + override def sortedMapFactory: SortedMapFactory[SortedMap] = SortedMap + + /** The same sorted map with a given default function. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefault`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d the function mapping keys to values, used for non-present keys + * @return a wrapper of the map with a default value + */ + override def withDefault(d: K => V): SortedMap[K, V] = new SortedMap.WithDefault[K, V](this, d) + + /** The same map with a given default value. + * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. + * are not affected by `withDefaultValue`. + * + * Invoking transformer methods (e.g. `map`) will not preserve the default value. + * + * @param d default value used for non-present keys + * @return a wrapper of the map with a default value + */ + override def withDefaultValue(d: V): SortedMap[K, V] = new SortedMap.WithDefault[K, V](this, _ => d) +} + +trait SortedMapOps[K, V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]] + extends collection.SortedMapOps[K, V, CC, C] + with MapOps[K, V, Map, C] { + + def unsorted: Map[K, V] + + @deprecated("Use m.clone().addOne((k,v)) instead of m.updated(k, v)", "2.13.0") + override def updated[V1 >: V](key: K, value: V1): CC[K, V1] = + clone().asInstanceOf[CC[K, V1]].addOne((key, value)) +} + +@SerialVersionUID(3L) +object SortedMap extends SortedMapFactory.Delegate[SortedMap](TreeMap) { + + @SerialVersionUID(3L) + final class WithDefault[K, V](underlying: SortedMap[K, V], defaultValue: K => V) + extends Map.WithDefault[K, V](underlying, defaultValue) + with SortedMap[K, V] + with SortedMapOps[K, V, SortedMap, WithDefault[K, V]] + with Serializable { + + override def sortedMapFactory: SortedMapFactory[SortedMap] = underlying.sortedMapFactory + + def iteratorFrom(start: K): scala.collection.Iterator[(K, V)] = underlying.iteratorFrom(start) + + def keysIteratorFrom(start: K): scala.collection.Iterator[K] = underlying.keysIteratorFrom(start) + + implicit def ordering: Ordering[K] = underlying.ordering + + def rangeImpl(from: Option[K], until: Option[K]): WithDefault[K, V] = + new WithDefault[K, V](underlying.rangeImpl(from, until), defaultValue) + + // Need to override following methods to match type signatures of `SortedMap.WithDefault` + // for operations preserving default value + override def subtractOne(elem: K): WithDefault.this.type = { underlying.subtractOne(elem); this } + + override def addOne(elem: (K, V)): WithDefault.this.type = { underlying.addOne(elem); this } + + override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) + + override def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]): SortedMap[K, V2] = + underlying.concat(suffix).withDefault(defaultValue) + + override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]): WithDefault[K, V] = + new WithDefault[K, V](sortedMapFactory.from(coll), defaultValue) + + override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] = + SortedMap.newBuilder.mapResult((p: SortedMap[K, V]) => new WithDefault[K, V](p, defaultValue)) + } +} diff --git a/library/src/scala/collection/mutable/SortedSet.scala b/library/src/scala/collection/mutable/SortedSet.scala new file mode 100644 index 000000000000..b5d9ee8feff6 --- /dev/null +++ b/library/src/scala/collection/mutable/SortedSet.scala @@ -0,0 +1,48 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +/** + * Base type for mutable sorted set collections + */ +trait SortedSet[A] + extends Set[A] + with collection.SortedSet[A] + with SortedSetOps[A, SortedSet, SortedSet[A]] + with SortedSetFactoryDefaults[A, SortedSet, Set] { + + override def unsorted: Set[A] = this + + override def sortedIterableFactory: SortedIterableFactory[SortedSet] = SortedSet +} + +/** + * @define coll mutable sorted set + * @define Coll `mutable.Sortedset` + */ +trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]] + extends SetOps[A, Set, C] + with collection.SortedSetOps[A, CC, C] { + + def unsorted: Set[A] +} + +/** + * $factoryInfo + * @define coll mutable sorted set + * @define Coll `mutable.Sortedset` + */ +@SerialVersionUID(3L) +object SortedSet extends SortedIterableFactory.Delegate[SortedSet](TreeSet) diff --git a/library/src/scala/collection/mutable/Stack.scala b/library/src/scala/collection/mutable/Stack.scala new file mode 100644 index 000000000000..01aacc22c65e --- /dev/null +++ b/library/src/scala/collection/mutable/Stack.scala @@ -0,0 +1,142 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + +import scala.annotation.{migration, nowarn} +import scala.collection.generic.DefaultSerializable +import scala.collection.{IterableFactoryDefaults, IterableOnce, SeqFactory, StrictOptimizedSeqFactory, StrictOptimizedSeqOps} + +/** A stack implements a data structure which allows to store and retrieve + * objects in a last-in-first-out (LIFO) fashion. + * + * Note that operations which consume and produce iterables preserve order, + * rather than reversing it (as would be expected from building a new stack + * by pushing an element at a time). + * + * @tparam A type of the elements contained in this stack. + * + * @define Coll `Stack` + * @define coll stack + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@migration("Stack is now based on an ArrayDeque instead of a linked list", "2.13.0") +class Stack[A] protected (array: Array[AnyRef], start: Int, end: Int) + extends ArrayDeque[A](array, start, end) + with IndexedSeqOps[A, Stack, Stack[A]] + with StrictOptimizedSeqOps[A, Stack, Stack[A]] + with IterableFactoryDefaults[A, Stack] + with ArrayDequeOps[A, Stack, Stack[A]] + with Cloneable[Stack[A]] + with DefaultSerializable { + + def this(initialSize: Int = ArrayDeque.DefaultInitialSize) = + this(ArrayDeque.alloc(initialSize), start = 0, end = 0) + + override def iterableFactory: SeqFactory[Stack] = Stack + + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "Stack" + + /** + * Add elements to the top of this stack + * + * @param elem + * @return + */ + def push(elem: A): this.type = prepend(elem) + + /** Push two or more elements onto the stack. The last element + * of the sequence will be on top of the new stack. + * + * @param elems the element sequence. + * @return the stack with the new elements on top. + */ + def push(elem1: A, elem2: A, elems: A*): this.type = { + val k = elems.knownSize + ensureSize(length + (if(k >= 0) k + 2 else 3)) + prepend(elem1).prepend(elem2).pushAll(elems) + } + + /** Push all elements in the given iterable object onto the stack. The + * last element in the iterable object will be on top of the new stack. + * + * @param elems the iterable object. + * @return the stack with the new elements on top. + */ + def pushAll(elems: scala.collection.IterableOnce[A]): this.type = + prependAll(elems match { + case it: scala.collection.Seq[A] => it.view.reverse + case it => IndexedSeq.from(it).view.reverse + }) + + /** + * Removes the top element from this stack and return it + * + * @return + * @throws NoSuchElementException when stack is empty + */ + def pop(): A = removeHead() + + /** + * Pop all elements from this stack and return it + * + * @return The removed elements + */ + def popAll(): scala.collection.Seq[A] = removeAll() + + /** + * Returns and removes all elements from the top of this stack which satisfy the given predicate + * + * @param f the predicate used for choosing elements + * @return The removed elements + */ + def popWhile(f: A => Boolean): scala.collection.Seq[A] = removeHeadWhile(f) + + /** Returns the top element of the stack. This method will not remove + * the element from the stack. An error is signaled if there is no + * element on the stack. + * + * @throws NoSuchElementException if the stack is empty + * @return the top element + */ + @`inline` final def top: A = head + + override protected def klone(): Stack[A] = { + val bf = newSpecificBuilder + bf ++= this + bf.result() + } + + override protected def ofArray(array: Array[AnyRef], end: Int): Stack[A] = + new Stack(array, start = 0, end) + +} + +/** + * $factoryInfo + * @define coll stack + * @define Coll `Stack` + */ +@SerialVersionUID(3L) +object Stack extends StrictOptimizedSeqFactory[Stack] { + + def from[A](source: IterableOnce[A]): Stack[A] = empty ++= source + + def empty[A]: Stack[A] = new Stack + + def newBuilder[A]: Builder[A, Stack[A]] = new GrowableBuilder[A, Stack[A]](empty) + +} diff --git a/library/src/scala/collection/mutable/StringBuilder.scala b/library/src/scala/collection/mutable/StringBuilder.scala new file mode 100644 index 000000000000..ad9755389c48 --- /dev/null +++ b/library/src/scala/collection/mutable/StringBuilder.scala @@ -0,0 +1,495 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.mutable + +import scala.collection.{IterableFactoryDefaults, IterableOnce} +import scala.collection.immutable.WrappedString + +import scala.Predef.{ // unimport char-related implicit conversions to avoid triggering them accidentally + genericArrayOps => _, + charArrayOps => _, + genericWrapArray => _, + wrapCharArray => _, + wrapString => _, + //_ +} + +/** A builder of `String` which is also a mutable sequence of characters. + * + * This class provides an API mostly compatible with `java.lang.StringBuilder`, + * except where there are conflicts with the Scala collections API, such as the `reverse` method: + * [[reverse]] produces a new `StringBuilder`, and [[reverseInPlace]] mutates this builder. + * + * Mutating operations return either `this.type`, i.e., the current builder, or `Unit`. + * + * Other methods extract data or information from the builder without mutating it. + * + * The distinction is also reflected in naming conventions used by collections, + * such as `append`, which mutates, and `appended`, which does not, or `reverse`, + * which does not mutate, and `reverseInPlace`, which does. + * + * The `String` result may be obtained using either `result()` or `toString`. + * + * $multipleResults + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#stringbuilders "Scala's Collection Library overview"]] + * section on `StringBuilders` for more information. + * + * @define Coll `mutable.IndexedSeq` + * @define coll string builder + */ +@SerialVersionUID(3L) +final class StringBuilder(val underlying: java.lang.StringBuilder) extends AbstractSeq[Char] + with ReusableBuilder[Char, String] + with IndexedSeq[Char] + with IndexedSeqOps[Char, IndexedSeq, StringBuilder] + with IterableFactoryDefaults[Char, IndexedSeq] + with java.lang.CharSequence + with Serializable { + + def this() = this(new java.lang.StringBuilder) + + /** Constructs a string builder with no characters in it and an + * initial capacity specified by the `capacity` argument. + * + * @param capacity the initial capacity. + * @throws java.lang.NegativeArraySizeException if capacity < 0. + */ + def this(capacity: Int) = this(new java.lang.StringBuilder(capacity)) + + /** Constructs a string builder with initial characters + * equal to characters of `str`. + */ + def this(str: String) = this(new java.lang.StringBuilder(str)) + + /** Constructs a string builder initialized with string value `initValue` + * and with additional character capacity `initCapacity`. + */ + def this(initCapacity: Int, initValue: String) = + this(new java.lang.StringBuilder(initValue.length + initCapacity) append initValue) + + // Methods required to make this an IndexedSeq: + def apply(i: Int): Char = underlying.charAt(i) + + override protected def fromSpecific(coll: scala.collection.IterableOnce[Char]): StringBuilder = + new StringBuilder() appendAll coll + + override protected def newSpecificBuilder: Builder[Char, StringBuilder] = + new GrowableBuilder(new StringBuilder()) + + override def empty: StringBuilder = new StringBuilder() + + @inline def length: Int = underlying.length + + def length_=(n: Int): Unit = underlying.setLength(n) + + override def knownSize: Int = super[IndexedSeqOps].knownSize + + def addOne(x: Char): this.type = { underlying.append(x); this } + + def clear(): Unit = underlying.setLength(0) + + /** Overloaded version of `addAll` that takes a string */ + def addAll(s: String): this.type = { underlying.append(s); this } + + /** Alias for `addAll` */ + def ++= (s: String): this.type = addAll(s) + + def result() = underlying.toString + + override def toString: String = result() + + override def toArray[B >: Char](implicit ct: scala.reflect.ClassTag[B]) = + ct.runtimeClass match { + case java.lang.Character.TYPE => toCharArray.asInstanceOf[Array[B]] + case _ => super.toArray + } + + /** Returns the contents of this StringBuilder as an `Array[Char]`. + * + * @return An array with the characters from this builder. + */ + def toCharArray: Array[Char] = { + val len = underlying.length + val arr = new Array[Char](len) + underlying.getChars(0, len, arr, 0) + arr + } + + // append* methods delegate to the underlying java.lang.StringBuilder: + + def appendAll(xs: String): this.type = { + underlying append xs + this + } + + /** Appends the string representation of the given argument, + * which is converted to a String with `String.valueOf`. + * + * @param x an `Any` object. + * @return this StringBuilder. + */ + def append(x: Any): this.type = { + underlying append String.valueOf(x) + this + } + + /** Appends the given String to this sequence. + * + * @param s a String. + * @return this StringBuilder. + */ + def append(s: String): this.type = { + underlying append s + this + } + + /** Appends the given CharSequence to this sequence. + * + * @param cs a CharSequence. + * @return this StringBuilder. + */ + def append(cs: java.lang.CharSequence): this.type = { + underlying.append(cs match { + // Both cases call into append(), but java SB + // looks up type at runtime and has fast path for SB. + case s: StringBuilder => s.underlying + case _ => cs + }) + this + } + + /** Appends the specified string builder to this sequence. + * + * @param s + * @return + */ + def append(s: StringBuilder): this.type = { + underlying append s.underlying + this + } + + /** Appends all the Chars in the given IterableOnce[Char] to this sequence. + * + * @param xs the characters to be appended. + * @return this StringBuilder. + */ + def appendAll(xs: IterableOnce[Char]): this.type = { + xs match { + case x: WrappedString => underlying append x.unwrap + case x: ArraySeq.ofChar => underlying append x.array + case x: StringBuilder => underlying append x.underlying + case _ => + val ks = xs.knownSize + if (ks != 0) { + val b = underlying + if (ks > 0) b.ensureCapacity(b.length + ks) + val it = xs.iterator + while (it.hasNext) { b append it.next() } + } + } + this + } + + /** Appends all the Chars in the given Array[Char] to this sequence. + * + * @param xs the characters to be appended. + * @return a reference to this object. + */ + def appendAll(xs: Array[Char]): this.type = { + underlying append xs + this + } + + /** Appends a portion of the given Array[Char] to this sequence. + * + * @param xs the Array containing Chars to be appended. + * @param offset the index of the first Char to append. + * @param len the numbers of Chars to append. + * @return this StringBuilder. + */ + def appendAll(xs: Array[Char], offset: Int, len: Int): this.type = { + underlying.append(xs, offset, len) + this + } + + /** Append the String representation of the given primitive type + * to this sequence. The argument is converted to a String with + * String.valueOf. + * + * @param x a primitive value + * @return This StringBuilder. + */ + def append(x: Boolean): this.type = { underlying append x ; this } + def append(x: Byte): this.type = append(x.toInt) + def append(x: Short): this.type = append(x.toInt) + def append(x: Int): this.type = { underlying append x ; this } + def append(x: Long): this.type = { underlying append x ; this } + def append(x: Float): this.type = { underlying append x ; this } + def append(x: Double): this.type = { underlying append x ; this } + def append(x: Char): this.type = { underlying append x ; this } + + /** Remove a subsequence of Chars from this sequence, starting at the + * given start index (inclusive) and extending to the end index (exclusive) + * or to the end of the String, whichever comes first. + * + * @param start The beginning index, inclusive. + * @param end The ending index, exclusive. + * @return This StringBuilder. + * @throws StringIndexOutOfBoundsException if start < 0 || start > end + */ + def delete(start: Int, end: Int): this.type = { + underlying.delete(start, end) + this + } + + /** Replaces a subsequence of Chars with the given String. The semantics + * are as in delete, with the String argument then inserted at index 'start'. + * + * @param start The beginning index, inclusive. + * @param end The ending index, exclusive. + * @param str The String to be inserted at the start index. + * @return This StringBuilder. + * @throws StringIndexOutOfBoundsException if start < 0, start > length, or start > end + */ + def replace(start: Int, end: Int, str: String): this.type = { + underlying.replace(start, end, str) + this + } + + /** Inserts a subarray of the given Array[Char] at the given index + * of this sequence. + * + * @param index index at which to insert the subarray. + * @param str the Array from which Chars will be taken. + * @param offset the index of the first Char to insert. + * @param len the number of Chars from 'str' to insert. + * @return This StringBuilder. + * + * @throws StringIndexOutOfBoundsException if index < 0, index > length, + * offset < 0, len < 0, or (offset + len) > str.length. + */ + def insertAll(index: Int, str: Array[Char], offset: Int, len: Int): this.type = { + underlying.insert(index, str, offset, len) + this + } + + /** Inserts the String representation (via String.valueOf) of the given + * argument into this sequence at the given index. + * + * @param index the index at which to insert. + * @param x a value. + * @return this StringBuilder. + * @throws StringIndexOutOfBoundsException if the index is out of bounds. + */ + def insert(index: Int, x: Any): this.type = insert(index, String.valueOf(x)) + + /** Inserts the String into this character sequence. + * + * @param index the index at which to insert. + * @param x a String. + * @return this StringBuilder. + * @throws StringIndexOutOfBoundsException if the index is out of bounds. + */ + def insert(index: Int, x: String): this.type = { + underlying.insert(index, x) + this + } + + /** Inserts the given Seq[Char] into this sequence at the given index. + * + * @param index the index at which to insert. + * @param xs the Seq[Char]. + * @return this StringBuilder. + * @throws StringIndexOutOfBoundsException if the index is out of bounds. + */ + def insertAll(index: Int, xs: IterableOnce[Char]): this.type = + insertAll(index, (ArrayBuilder.make[Char] ++= xs).result()) + + /** Inserts the given Array[Char] into this sequence at the given index. + * + * @param index the index at which to insert. + * @param xs the Array[Char]. + * @return this StringBuilder. + * @throws StringIndexOutOfBoundsException if the index is out of bounds. + */ + def insertAll(index: Int, xs: Array[Char]): this.type = { + underlying.insert(index, xs) + this + } + + /** Calls String.valueOf on the given primitive value, and inserts the + * String at the given index. + * + * @param index the offset position. + * @param x a primitive value. + * @return this StringBuilder. + */ + def insert(index: Int, x: Boolean): this.type = insert(index, String.valueOf(x)) + def insert(index: Int, x: Byte): this.type = insert(index, x.toInt) + def insert(index: Int, x: Short): this.type = insert(index, x.toInt) + def insert(index: Int, x: Int): this.type = insert(index, String.valueOf(x)) + def insert(index: Int, x: Long): this.type = insert(index, String.valueOf(x)) + def insert(index: Int, x: Float): this.type = insert(index, String.valueOf(x)) + def insert(index: Int, x: Double): this.type = insert(index, String.valueOf(x)) + def insert(index: Int, x: Char): this.type = insert(index, String.valueOf(x)) + + /** Sets the length of the character sequence. If the current sequence + * is shorter than the given length, it is padded with nulls; if it is + * longer, it is truncated. + * + * @param len the new length + * @throws IndexOutOfBoundsException if the argument is negative. + */ + def setLength(len: Int): Unit = underlying.setLength(len) + + def update(idx: Int, elem: Char): Unit = underlying.setCharAt(idx, elem) + + + /** Like reverse, but destructively updates the target StringBuilder. + * + * @return the reversed StringBuilder (same as the target StringBuilder) + */ + @deprecated("Use reverseInPlace instead", "2.13.0") + final def reverseContents(): this.type = reverseInPlace() + + /** Like reverse, but destructively updates the target StringBuilder. + * + * @return the reversed StringBuilder (same as the target StringBuilder) + */ + def reverseInPlace(): this.type = { + underlying.reverse() + this + } + + + /** Returns the current capacity, which is the size of the underlying array. + * A new array will be allocated if the current capacity is exceeded. + * + * @return the capacity + */ + def capacity: Int = underlying.capacity + + /** Ensure that the capacity is at least the given argument. + * If the argument is greater than the current capacity, new + * storage will be allocated with size equal to the given + * argument or to `(2 * capacity + 2)`, whichever is larger. + * + * @param newCapacity the minimum desired capacity. + */ + def ensureCapacity(newCapacity: Int): Unit = { underlying.ensureCapacity(newCapacity) } + + /** Returns the Char at the specified index, counting from 0 as in Arrays. + * + * @param index the index to look up + * @return the Char at the given index. + * @throws IndexOutOfBoundsException if the index is out of bounds. + */ + def charAt(index: Int): Char = underlying.charAt(index) + + /** Removes the Char at the specified index. The sequence is + * shortened by one. + * + * @param index The index to remove. + * @return This StringBuilder. + * @throws IndexOutOfBoundsException if the index is out of bounds. + */ + def deleteCharAt(index: Int): this.type = { + underlying.deleteCharAt(index) + this + } + + /** Update the sequence at the given index to hold the specified Char. + * + * @param index the index to modify. + * @param ch the new Char. + * @throws IndexOutOfBoundsException if the index is out of bounds. + */ + def setCharAt(index: Int, ch: Char): this.type = { + underlying.setCharAt(index, ch) + this + } + + /** Returns a new String made up of a subsequence of this sequence, + * beginning at the given index and extending to the end of the sequence. + * + * target.substring(start) is equivalent to target.drop(start) + * + * @param start The starting index, inclusive. + * @return The new String. + * @throws IndexOutOfBoundsException if the index is out of bounds. + */ + def substring(start: Int): String = underlying.substring(start, length) + + /** Returns a new String made up of a subsequence of this sequence, + * beginning at the start index (inclusive) and extending to the + * end index (exclusive). + * + * target.substring(start, end) is equivalent to target.slice(start, end).mkString + * + * @param start The beginning index, inclusive. + * @param end The ending index, exclusive. + * @return The new String. + * @throws StringIndexOutOfBoundsException If either index is out of bounds, + * or if start > end. + */ + def substring(start: Int, end: Int): String = underlying.substring(start, end) + + /** For implementing CharSequence. + */ + def subSequence(start: Int, end: Int): java.lang.CharSequence = + underlying.substring(start, end) + + /** Finds the index of the first occurrence of the specified substring. + * + * @param str the target string to search for + * @return the first applicable index where target occurs, or -1 if not found. + */ + def indexOf(str: String): Int = underlying.indexOf(str) + + /** Finds the index of the first occurrence of the specified substring. + * + * @param str the target string to search for + * @param fromIndex the smallest index in the source string to consider + * @return the first applicable index where target occurs, or -1 if not found. + */ + def indexOf(str: String, fromIndex: Int): Int = underlying.indexOf(str, fromIndex) + + /** Finds the index of the last occurrence of the specified substring. + * + * @param str the target string to search for + * @return the last applicable index where target occurs, or -1 if not found. + */ + def lastIndexOf(str: String): Int = underlying.lastIndexOf(str) + + /** Finds the index of the last occurrence of the specified substring. + * + * @param str the target string to search for + * @param fromIndex the smallest index in the source string to consider + * @return the last applicable index where target occurs, or -1 if not found. + */ + def lastIndexOf(str: String, fromIndex: Int): Int = underlying.lastIndexOf(str, fromIndex) + + /** Tests whether this builder is empty. + * + * This method is required for JDK15+ compatibility + * + * @return `true` if this builder contains nothing, `false` otherwise. + */ + override def isEmpty: Boolean = underlying.length() == 0 +} + +object StringBuilder { + @deprecated("Use `new StringBuilder()` instead of `StringBuilder.newBuilder`", "2.13.0") + def newBuilder = new StringBuilder +} diff --git a/library/src/scala/collection/mutable/TreeMap.scala b/library/src/scala/collection/mutable/TreeMap.scala new file mode 100644 index 000000000000..076239278299 --- /dev/null +++ b/library/src/scala/collection/mutable/TreeMap.scala @@ -0,0 +1,257 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializable +import scala.collection.mutable.{RedBlackTree => RB} + +/** + * A mutable sorted map implemented using a mutable red-black tree as underlying data structure. + * + * @param ordering the implicit ordering used to compare objects of type `A`. + * @tparam K the type of the keys contained in this tree map. + * @tparam V the type of the values associated with the keys. + * + * @define Coll mutable.TreeMap + * @define coll mutable tree map + */ +sealed class TreeMap[K, V] private (tree: RB.Tree[K, V])(implicit val ordering: Ordering[K]) + extends AbstractMap[K, V] + with SortedMap[K, V] + with SortedMapOps[K, V, TreeMap, TreeMap[K, V]] + with StrictOptimizedIterableOps[(K, V), Iterable, TreeMap[K, V]] + with StrictOptimizedMapOps[K, V, Map, TreeMap[K, V]] + with StrictOptimizedSortedMapOps[K, V, TreeMap, TreeMap[K, V]] + with SortedMapFactoryDefaults[K, V, TreeMap, Iterable, Map] + with DefaultSerializable { + + override def sortedMapFactory: TreeMap.type = TreeMap + + /** + * Creates an empty `TreeMap`. + * @param ord the implicit ordering used to compare objects of type `K`. + * @return an empty `TreeMap`. + */ + def this()(implicit ord: Ordering[K]) = this(RB.Tree.empty)(ord) + + def iterator: Iterator[(K, V)] = { + if (isEmpty) Iterator.empty + else RB.iterator(tree) + } + + override def keysIterator: Iterator[K] = { + if (isEmpty) Iterator.empty + else RB.keysIterator(tree, None) + } + + override def valuesIterator: Iterator[V] = { + if (isEmpty) Iterator.empty + else RB.valuesIterator(tree, None) + } + + def keysIteratorFrom(start: K): Iterator[K] = { + if (isEmpty) Iterator.empty + else RB.keysIterator(tree, Some(start)) + } + + def iteratorFrom(start: K): Iterator[(K, V)] = { + if (isEmpty) Iterator.empty + else RB.iterator(tree, Some(start)) + } + + override def valuesIteratorFrom(start: K): Iterator[V] = { + if (isEmpty) Iterator.empty + else RB.valuesIterator(tree, Some(start)) + } + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[(K, V), S]): S with EfficientSplit = + shape.parUnbox( + scala.collection.convert.impl.AnyBinaryTreeStepper.from[(K, V), RB.Node[K, V]]( + size, tree.root, _.left, _.right, x => (x.key, x.value) + ) + ) + + override def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S with EfficientSplit = { + import scala.collection.convert.impl._ + type T = RB.Node[K, V] + val s = shape.shape match { + case StepperShape.IntShape => IntBinaryTreeStepper.from[T] (size, tree.root, _.left, _.right, _.key.asInstanceOf[Int]) + case StepperShape.LongShape => LongBinaryTreeStepper.from[T] (size, tree.root, _.left, _.right, _.key.asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleBinaryTreeStepper.from[T](size, tree.root, _.left, _.right, _.key.asInstanceOf[Double]) + case _ => shape.parUnbox(AnyBinaryTreeStepper.from[K, T](size, tree.root, _.left, _.right, _.key)) + } + s.asInstanceOf[S with EfficientSplit] + } + + override def valueStepper[S <: Stepper[_]](implicit shape: StepperShape[V, S]): S with EfficientSplit = { + import scala.collection.convert.impl._ + type T = RB.Node[K, V] + val s = shape.shape match { + case StepperShape.IntShape => IntBinaryTreeStepper.from[T] (size, tree.root, _.left, _.right, _.value.asInstanceOf[Int]) + case StepperShape.LongShape => LongBinaryTreeStepper.from[T] (size, tree.root, _.left, _.right, _.value.asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleBinaryTreeStepper.from[T] (size, tree.root, _.left, _.right, _.value.asInstanceOf[Double]) + case _ => shape.parUnbox(AnyBinaryTreeStepper.from[V, T] (size, tree.root, _.left, _.right, _.value)) + } + s.asInstanceOf[S with EfficientSplit] + } + + def addOne(elem: (K, V)): this.type = { RB.insert(tree, elem._1, elem._2); this } + + def subtractOne(elem: K): this.type = { RB.delete(tree, elem); this } + + override def clear(): Unit = RB.clear(tree) + + def get(key: K): Option[V] = RB.get(tree, key) + + /** + * Creates a ranged projection of this map. Any mutations in the ranged projection will update the original map and + * vice versa. + * + * Only entries with keys between this projection's key range will ever appear as elements of this map, independently + * of whether the entries are added through the original map or through this view. That means that if one inserts a + * key-value in a view whose key is outside the view's bounds, calls to `get` or `contains` will _not_ consider the + * newly added entry. Mutations are always reflected in the original map, though. + * + * @param from the lower bound (inclusive) of this projection wrapped in a `Some`, or `None` if there is no lower + * bound. + * @param until the upper bound (exclusive) of this projection wrapped in a `Some`, or `None` if there is no upper + * bound. + */ + def rangeImpl(from: Option[K], until: Option[K]): TreeMap[K, V] = new TreeMapProjection(from, until) + + override def foreach[U](f: ((K, V)) => U): Unit = RB.foreach(tree, f) + override def foreachEntry[U](f: (K, V) => U): Unit = RB.foreachEntry(tree, f) + + override def size: Int = RB.size(tree) + override def knownSize: Int = size + override def isEmpty: Boolean = RB.isEmpty(tree) + + override def contains(key: K): Boolean = RB.contains(tree, key) + + override def head: (K, V) = RB.min(tree).get + + override def last: (K, V) = RB.max(tree).get + + override def minAfter(key: K): Option[(K, V)] = RB.minAfter(tree, key) + + override def maxBefore(key: K): Option[(K, V)] = RB.maxBefore(tree, key) + + override protected[this] def className: String = "TreeMap" + + + /** + * A ranged projection of a [[TreeMap]]. Mutations on this map affect the original map and vice versa. + * + * Only entries with keys between this projection's key range will ever appear as elements of this map, independently + * of whether the entries are added through the original map or through this view. That means that if one inserts a + * key-value in a view whose key is outside the view's bounds, calls to `get` or `contains` will _not_ consider the + * newly added entry. Mutations are always reflected in the original map, though. + * + * @param from the lower bound (inclusive) of this projection wrapped in a `Some`, or `None` if there is no lower + * bound. + * @param until the upper bound (exclusive) of this projection wrapped in a `Some`, or `None` if there is no upper + * bound. + */ + private[this] final class TreeMapProjection(from: Option[K], until: Option[K]) extends TreeMap[K, V](tree) { + + /** + * Given a possible new lower bound, chooses and returns the most constraining one (the maximum). + */ + private[this] def pickLowerBound(newFrom: Option[K]): Option[K] = (from, newFrom) match { + case (Some(fr), Some(newFr)) => Some(ordering.max(fr, newFr)) + case (None, _) => newFrom + case _ => from + } + + /** + * Given a possible new upper bound, chooses and returns the most constraining one (the minimum). + */ + private[this] def pickUpperBound(newUntil: Option[K]): Option[K] = (until, newUntil) match { + case (Some(unt), Some(newUnt)) => Some(ordering.min(unt, newUnt)) + case (None, _) => newUntil + case _ => until + } + + /** + * Returns true if the argument is inside the view bounds (between `from` and `until`). + */ + private[this] def isInsideViewBounds(key: K): Boolean = { + val afterFrom = from.isEmpty || ordering.compare(from.get, key) <= 0 + val beforeUntil = until.isEmpty || ordering.compare(key, until.get) < 0 + afterFrom && beforeUntil + } + + override def rangeImpl(from: Option[K], until: Option[K]): TreeMap[K, V] = + new TreeMapProjection(pickLowerBound(from), pickUpperBound(until)) + + override def get(key: K) = if (isInsideViewBounds(key)) RB.get(tree, key) else None + + override def iterator = if (RB.size(tree) == 0) Iterator.empty else RB.iterator(tree, from, until) + override def keysIterator: Iterator[K] = if (RB.size(tree) == 0) Iterator.empty else RB.keysIterator(tree, from, until) + override def valuesIterator: Iterator[V] = if (RB.size(tree) == 0) Iterator.empty else RB.valuesIterator(tree, from, until) + override def keysIteratorFrom(start: K) = if (RB.size(tree) == 0) Iterator.empty else RB.keysIterator(tree, pickLowerBound(Some(start)), until) + override def iteratorFrom(start: K) = if (RB.size(tree) == 0) Iterator.empty else RB.iterator(tree, pickLowerBound(Some(start)), until) + override def valuesIteratorFrom(start: K) = if (RB.size(tree) == 0) Iterator.empty else RB.valuesIterator(tree, pickLowerBound(Some(start)), until) + override def size = if (RB.size(tree) == 0) 0 else iterator.length + override def knownSize: Int = if (RB.size(tree) == 0) 0 else -1 + override def isEmpty = RB.size(tree) == 0 || !iterator.hasNext + override def contains(key: K) = isInsideViewBounds(key) && RB.contains(tree, key) + + override def head = headOption.get + override def headOption = { + val entry = if (from.isDefined) RB.minAfter(tree, from.get) else RB.min(tree) + (entry, until) match { + case (Some(e), Some(unt)) if ordering.compare(e._1, unt) >= 0 => None + case _ => entry + } + } + + override def last = lastOption.get + override def lastOption = { + val entry = if (until.isDefined) RB.maxBefore(tree, until.get) else RB.max(tree) + (entry, from) match { + case (Some(e), Some(fr)) if ordering.compare(e._1, fr) < 0 => None + case _ => entry + } + } + + // Using the iterator should be efficient enough; if performance is deemed a problem later, specialized + // `foreach(f, from, until)` and `transform(f, from, until)` methods can be created in `RedBlackTree`. See + // https://github.com/scala/scala/pull/4608#discussion_r34307985 for a discussion about this. + override def foreach[U](f: ((K, V)) => U): Unit = iterator.foreach(f) + + override def clone() = super.clone().rangeImpl(from, until) + } + +} + +/** + * $factoryInfo + * + * @define Coll mutable.TreeMap + * @define coll mutable tree map + */ +@SerialVersionUID(3L) +object TreeMap extends SortedMapFactory[TreeMap] { + + def from[K : Ordering, V](it: IterableOnce[(K, V)]): TreeMap[K, V] = + Growable.from(empty[K, V], it) + + def empty[K : Ordering, V]: TreeMap[K, V] = new TreeMap[K, V]() + + def newBuilder[K: Ordering, V]: Builder[(K, V), TreeMap[K, V]] = new GrowableBuilder(empty[K, V]) + +} diff --git a/library/src/scala/collection/mutable/TreeSet.scala b/library/src/scala/collection/mutable/TreeSet.scala new file mode 100644 index 000000000000..9820af9037ca --- /dev/null +++ b/library/src/scala/collection/mutable/TreeSet.scala @@ -0,0 +1,218 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.mutable + +import scala.collection.Stepper.EfficientSplit +import scala.collection.generic.DefaultSerializable +import scala.collection.mutable.{RedBlackTree => RB} +import scala.collection.{SortedIterableFactory, SortedSetFactoryDefaults, Stepper, StepperShape, StrictOptimizedIterableOps, StrictOptimizedSortedSetOps, mutable} + +/** + * A mutable sorted set implemented using a mutable red-black tree as underlying data structure. + * + * @param ordering the implicit ordering used to compare objects of type `A`. + * @tparam A the type of the keys contained in this tree set. + * + * @define Coll mutable.TreeSet + * @define coll mutable tree set + */ +// Original API designed in part by Lucien Pereira +sealed class TreeSet[A] private (private val tree: RB.Tree[A, Null])(implicit val ordering: Ordering[A]) + extends AbstractSet[A] + with SortedSet[A] + with SortedSetOps[A, TreeSet, TreeSet[A]] + with StrictOptimizedIterableOps[A, Set, TreeSet[A]] + with StrictOptimizedSortedSetOps[A, TreeSet, TreeSet[A]] + with SortedSetFactoryDefaults[A, TreeSet, Set] + with DefaultSerializable { + + if (ordering eq null) + throw new NullPointerException("ordering must not be null") + + /** + * Creates an empty `TreeSet`. + * @param ord the implicit ordering used to compare objects of type `A`. + * @return an empty `TreeSet`. + */ + def this()(implicit ord: Ordering[A]) = this(RB.Tree.empty)(ord) + + override def sortedIterableFactory: SortedIterableFactory[TreeSet] = TreeSet + + def iterator: collection.Iterator[A] = RB.keysIterator(tree) + + def iteratorFrom(start: A): collection.Iterator[A] = RB.keysIterator(tree, Some(start)) + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { + import scala.collection.convert.impl._ + type T = RB.Node[A, Null] + val s = shape.shape match { + case StepperShape.IntShape => IntBinaryTreeStepper.from[T] (size, tree.root, _.left, _.right, _.key.asInstanceOf[Int]) + case StepperShape.LongShape => LongBinaryTreeStepper.from[T] (size, tree.root, _.left, _.right, _.key.asInstanceOf[Long]) + case StepperShape.DoubleShape => DoubleBinaryTreeStepper.from[T](size, tree.root, _.left, _.right, _.key.asInstanceOf[Double]) + case _ => shape.parUnbox(AnyBinaryTreeStepper.from[A, T](size, tree.root, _.left, _.right, _.key)) + } + s.asInstanceOf[S with EfficientSplit] + } + + def addOne(elem: A): this.type = { + RB.insert(tree, elem, null) + this + } + + def subtractOne(elem: A): this.type = { + RB.delete(tree, elem) + this + } + + def clear(): Unit = RB.clear(tree) + + def contains(elem: A): Boolean = RB.contains(tree, elem) + + def unconstrained: collection.Set[A] = this + + def rangeImpl(from: Option[A], until: Option[A]): TreeSet[A] = new TreeSetProjection(from, until) + + override protected[this] def className: String = "TreeSet" + + override def size: Int = RB.size(tree) + override def knownSize: Int = size + override def isEmpty: Boolean = RB.isEmpty(tree) + + override def head: A = RB.minKey(tree).get + + override def last: A = RB.maxKey(tree).get + + override def minAfter(key: A): Option[A] = RB.minKeyAfter(tree, key) + + override def maxBefore(key: A): Option[A] = RB.maxKeyBefore(tree, key) + + override def foreach[U](f: A => U): Unit = RB.foreachKey(tree, f) + + + /** + * A ranged projection of a [[TreeSet]]. Mutations on this set affect the original set and vice versa. + * + * Only keys between this projection's key range will ever appear as elements of this set, independently of whether + * the elements are added through the original set or through this view. That means that if one inserts an element in + * a view whose key is outside the view's bounds, calls to `contains` will _not_ consider the newly added element. + * Mutations are always reflected in the original set, though. + * + * @param from the lower bound (inclusive) of this projection wrapped in a `Some`, or `None` if there is no lower + * bound. + * @param until the upper bound (exclusive) of this projection wrapped in a `Some`, or `None` if there is no upper + * bound. + */ + private[this] final class TreeSetProjection(from: Option[A], until: Option[A]) extends TreeSet[A](tree) { + + /** + * Given a possible new lower bound, chooses and returns the most constraining one (the maximum). + */ + private[this] def pickLowerBound(newFrom: Option[A]): Option[A] = (from, newFrom) match { + case (Some(fr), Some(newFr)) => Some(ordering.max(fr, newFr)) + case (None, _) => newFrom + case _ => from + } + + /** + * Given a possible new upper bound, chooses and returns the most constraining one (the minimum). + */ + private[this] def pickUpperBound(newUntil: Option[A]): Option[A] = (until, newUntil) match { + case (Some(unt), Some(newUnt)) => Some(ordering.min(unt, newUnt)) + case (None, _) => newUntil + case _ => until + } + + /** + * Returns true if the argument is inside the view bounds (between `from` and `until`). + */ + private[this] def isInsideViewBounds(key: A): Boolean = { + val afterFrom = from.isEmpty || ordering.compare(from.get, key) <= 0 + val beforeUntil = until.isEmpty || ordering.compare(key, until.get) < 0 + afterFrom && beforeUntil + } + + override def rangeImpl(from: Option[A], until: Option[A]): TreeSet[A] = + new TreeSetProjection(pickLowerBound(from), pickUpperBound(until)) + + override def contains(key: A) = isInsideViewBounds(key) && RB.contains(tree, key) + + override def iterator = RB.keysIterator(tree, from, until) + override def iteratorFrom(start: A) = RB.keysIterator(tree, pickLowerBound(Some(start)), until) + + override def size = if (RB.size(tree) == 0) 0 else iterator.length + override def knownSize: Int = if (RB.size(tree) == 0) 0 else -1 + override def isEmpty: Boolean = RB.size(tree) == 0 || !iterator.hasNext + + override def head: A = headOption.get + override def headOption: Option[A] = { + val elem = if (from.isDefined) RB.minKeyAfter(tree, from.get) else RB.minKey(tree) + (elem, until) match { + case (Some(e), Some(unt)) if ordering.compare(e, unt) >= 0 => None + case _ => elem + } + } + + override def last: A = lastOption.get + override def lastOption = { + val elem = if (until.isDefined) RB.maxKeyBefore(tree, until.get) else RB.maxKey(tree) + (elem, from) match { + case (Some(e), Some(fr)) if ordering.compare(e, fr) < 0 => None + case _ => elem + } + } + + // Using the iterator should be efficient enough; if performance is deemed a problem later, a specialized + // `foreachKey(f, from, until)` method can be created in `RedBlackTree`. See + // https://github.com/scala/scala/pull/4608#discussion_r34307985 for a discussion about this. + override def foreach[U](f: A => U): Unit = iterator.foreach(f) + + override def clone(): mutable.TreeSet[A] = super.clone().rangeImpl(from, until) + + } + +} + +/** + * $factoryInfo + * @define Coll `mutable.TreeSet` + * @define coll mutable tree set + */ +@SerialVersionUID(3L) +object TreeSet extends SortedIterableFactory[TreeSet] { + + def empty[A : Ordering]: TreeSet[A] = new TreeSet[A]() + + def from[E](it: IterableOnce[E])(implicit ordering: Ordering[E]): TreeSet[E] = + it match { + case ts: TreeSet[E] if ordering == ts.ordering => + new TreeSet[E](ts.tree.treeCopy()) + case ss: scala.collection.SortedSet[E] if ordering == ss.ordering => + new TreeSet[E](RB.fromOrderedKeys(ss.iterator, ss.size)) + case r: Range if (ordering eq Ordering.Int) || (ordering eq Ordering.Int.reverse) => + val it = if((ordering eq Ordering.Int) == (r.step > 0)) r.iterator else r.reverseIterator + new TreeSet[E](RB.fromOrderedKeys(it.asInstanceOf[Iterator[E]], r.size)) + case _ => + val t: RB.Tree[E, Null] = RB.Tree.empty + val i = it.iterator + while (i.hasNext) RB.insert(t, i.next(), null) + new TreeSet[E](t) + } + + def newBuilder[A](implicit ordering: Ordering[A]): Builder[A, TreeSet[A]] = new ReusableBuilder[A, TreeSet[A]] { + private[this] var tree: RB.Tree[A, Null] = RB.Tree.empty + def addOne(elem: A): this.type = { RB.insert(tree, elem, null); this } + def result(): TreeSet[A] = new TreeSet[A](tree) + def clear(): Unit = { tree = RB.Tree.empty } + } +} diff --git a/library/src/scala/collection/mutable/UnrolledBuffer.scala b/library/src/scala/collection/mutable/UnrolledBuffer.scala new file mode 100644 index 000000000000..4aecac001505 --- /dev/null +++ b/library/src/scala/collection/mutable/UnrolledBuffer.scala @@ -0,0 +1,447 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.annotation.tailrec +import scala.collection.generic.{CommonErrors, DefaultSerializable} +import scala.reflect.ClassTag +import scala.collection.immutable.Nil + +/** A buffer that stores elements in an unrolled linked list. + * + * Unrolled linked lists store elements in linked fixed size + * arrays. + * + * Unrolled buffers retain locality and low memory overhead + * properties of array buffers, but offer much more efficient + * element addition, since they never reallocate and copy the + * internal array. + * + * However, they provide `O(n/m)` complexity random access, + * where `n` is the number of elements, and `m` the size of + * internal array chunks. + * + * Ideal to use when: + * - elements are added to the buffer and then all of the + * elements are traversed sequentially + * - two unrolled buffers need to be concatenated (see `concat`) + * + * Better than singly linked lists for random access, but + * should still be avoided for such a purpose. + * + * @define coll unrolled buffer + * @define Coll `UnrolledBuffer` + * + */ +@SerialVersionUID(3L) +sealed class UnrolledBuffer[T](implicit val tag: ClassTag[T]) + extends AbstractBuffer[T] + with Buffer[T] + with Seq[T] + with SeqOps[T, UnrolledBuffer, UnrolledBuffer[T]] + with StrictOptimizedSeqOps[T, UnrolledBuffer, UnrolledBuffer[T]] + with EvidenceIterableFactoryDefaults[T, UnrolledBuffer, ClassTag] + with Builder[T, UnrolledBuffer[T]] + with DefaultSerializable { + + import UnrolledBuffer.Unrolled + + @transient private var headptr = newUnrolled + @transient private var lastptr = headptr + @transient private var sz = 0 + + private[collection] def headPtr = headptr + private[collection] def headPtr_=(head: Unrolled[T]) = headptr = head + private[collection] def lastPtr = lastptr + private[collection] def lastPtr_=(last: Unrolled[T]) = lastptr = last + private[collection] def size_=(s: Int) = sz = s + + protected def evidenceIterableFactory: UnrolledBuffer.type = UnrolledBuffer + protected def iterableEvidence: ClassTag[T] = tag + + override def iterableFactory: SeqFactory[UnrolledBuffer] = UnrolledBuffer.untagged + + protected def newUnrolled = new Unrolled[T](this) + + // The below would allow more flexible behavior without requiring inheritance + // that is risky because all the important internals are private. + // private var myLengthPolicy: Int => Int = x => x + // + // /** Specifies how the array lengths should vary. + // * + // * By default, `UnrolledBuffer` uses arrays of a fixed size. A length + // * policy can be given that changes this scheme to, for instance, an + // * exponential growth. + // * + // * @param nextLength computes the length of the next array from the length of the latest one + // */ + // def setLengthPolicy(nextLength: Int => Int): Unit = { myLengthPolicy = nextLength } + private[collection] def calcNextLength(sz: Int) = sz // myLengthPolicy(sz) + + def classTagCompanion: UnrolledBuffer.type = UnrolledBuffer + + /** Concatenates the target unrolled buffer to this unrolled buffer. + * + * The specified buffer `that` is cleared after this operation. This is + * an O(1) operation. + * + * @param that the unrolled buffer whose elements are added to this buffer + */ + def concat(that: UnrolledBuffer[T]) = { + // bind the two together + if (!lastptr.bind(that.headptr)) lastptr = that.lastPtr + + // update size + sz += that.sz + + // `that` is no longer usable, so clear it + // here we rely on the fact that `clear` allocates + // new nodes instead of modifying the previous ones + that.clear() + + // return a reference to this + this + } + + def addOne(elem: T) = { + lastptr = lastptr.append(elem) + sz += 1 + this + } + + def clear(): Unit = { + headptr = newUnrolled + lastptr = headptr + sz = 0 + } + + def iterator: Iterator[T] = new AbstractIterator[T] { + var pos: Int = -1 + var node: Unrolled[T] = headptr + scan() + + private def scan(): Unit = { + pos += 1 + while (pos >= node.size) { + pos = 0 + node = node.next + if (node eq null) return + } + } + def hasNext = node ne null + def next() = if (hasNext) { + val r = node.array(pos) + scan() + r + } else Iterator.empty.next() + } + + // this should be faster than the iterator + override def foreach[U](f: T => U) = headptr.foreach(f) + + def result() = this + + def length = sz + + override def knownSize: Int = sz + + def apply(idx: Int) = + if (idx >= 0 && idx < sz) headptr(idx) + else throw CommonErrors.indexOutOfBounds(index = idx, max = sz - 1) + + def update(idx: Int, newelem: T) = + if (idx >= 0 && idx < sz) headptr(idx) = newelem + else throw CommonErrors.indexOutOfBounds(index = idx, max = sz - 1) + + /** Replace the contents of this $coll with the mapped result. + * + * @param f the mapping function + * @return this $coll + */ + def mapInPlace(f: T => T): this.type = { + headptr.mapInPlace(f) + this + } + + def remove(idx: Int) = + if (idx >= 0 && idx < sz) { + sz -= 1 + headptr.remove(idx, this) + } else throw CommonErrors.indexOutOfBounds(index = idx, max = sz - 1) + + @tailrec final def remove(idx: Int, count: Int): Unit = + if (count > 0) { + remove(idx) + remove(idx, count-1) + } + + def prepend(elem: T) = { + headptr = headptr prepend elem + sz += 1 + this + } + + def insert(idx: Int, elem: T): Unit = + insertAll(idx, elem :: Nil) + + def insertAll(idx: Int, elems: IterableOnce[T]): Unit = + if (idx >= 0 && idx <= sz) { + sz += headptr.insertAll(idx, elems, this) + } else throw CommonErrors.indexOutOfBounds(index = idx, max = sz - 1) + + override def subtractOne(elem: T): this.type = { + if (headptr.subtractOne(elem, this)) { + sz -= 1 + } + this + } + + def patchInPlace(from: Int, patch: collection.IterableOnce[T], replaced: Int): this.type = { + remove(from, replaced) + insertAll(from, patch) + this + } + + private def writeObject(out: java.io.ObjectOutputStream): Unit = { + out.defaultWriteObject + out writeInt sz + for (elem <- this) out writeObject elem + } + + private def readObject(in: java.io.ObjectInputStream): Unit = { + in.defaultReadObject + + val num = in.readInt + + headPtr = newUnrolled + lastPtr = headPtr + sz = 0 + var i = 0 + while (i < num) { + this += in.readObject.asInstanceOf[T] + i += 1 + } + } + + override def clone(): UnrolledBuffer[T] = new UnrolledBuffer[T] ++= this + + override protected[this] def className = "UnrolledBuffer" +} + + +@SerialVersionUID(3L) +object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] { self => + + val untagged: SeqFactory[UnrolledBuffer] = new ClassTagSeqFactory.AnySeqDelegate(self) + + def empty[A : ClassTag]: UnrolledBuffer[A] = new UnrolledBuffer[A] + + def from[A : ClassTag](source: scala.collection.IterableOnce[A]): UnrolledBuffer[A] = newBuilder[A].addAll(source) + + def newBuilder[A : ClassTag]: UnrolledBuffer[A] = new UnrolledBuffer[A] + + final val waterline: Int = 50 + + final def waterlineDenom: Int = 100 + + @deprecated("Use waterlineDenom instead.", "2.13.0") + final val waterlineDelim: Int = waterlineDenom + + private[collection] val unrolledlength = 32 + + /** Unrolled buffer node. + */ + class Unrolled[T: ClassTag] private[collection] (var size: Int, var array: Array[T], var next: Unrolled[T], val buff: UnrolledBuffer[T] = null) { + private[collection] def this() = this(0, new Array[T](unrolledlength), null, null) + private[collection] def this(b: UnrolledBuffer[T]) = this(0, new Array[T](unrolledlength), null, b) + + private def nextlength = if (buff eq null) unrolledlength else buff.calcNextLength(array.length) + + // adds and returns itself or the new unrolled if full + @tailrec final def append(elem: T): Unrolled[T] = if (size < array.length) { + array(size) = elem + size += 1 + this + } else { + next = new Unrolled[T](0, new Array[T](nextlength), null, buff) + next append elem + } + def foreach[U](f: T => U): Unit = { + var unrolled = this + var i = 0 + while (unrolled ne null) { + val chunkarr = unrolled.array + val chunksz = unrolled.size + while (i < chunksz) { + val elem = chunkarr(i) + f(elem) + i += 1 + } + i = 0 + unrolled = unrolled.next + } + } + def mapInPlace(f: T => T): Unit = { + var unrolled = this + var i = 0 + while (unrolled ne null) { + val chunkarr = unrolled.array + val chunksz = unrolled.size + while (i < chunksz) { + val elem = chunkarr(i) + chunkarr(i) = f(elem) + i += 1 + } + i = 0 + unrolled = unrolled.next + } + } + @tailrec final def apply(idx: Int): T = + if (idx < size) array(idx) else next.apply(idx - size) + @tailrec final def update(idx: Int, newelem: T): Unit = + if (idx < size) array(idx) = newelem else next.update(idx - size, newelem) + @tailrec final def locate(idx: Int): Unrolled[T] = + if (idx < size) this else next.locate(idx - size) + def prepend(elem: T) = if (size < array.length) { + // shift the elements of the array right + // then insert the element + shiftright() + array(0) = elem + size += 1 + this + } else { + // allocate a new node and store element + // then make it point to this + val newhead = new Unrolled[T](buff) + newhead append elem + newhead.next = this + newhead + } + // shifts right assuming enough space + private def shiftright(): Unit = { + var i = size - 1 + while (i >= 0) { + array(i + 1) = array(i) + i -= 1 + } + } + // returns pointer to new last if changed + @tailrec final def remove(idx: Int, buffer: UnrolledBuffer[T]): T = + if (idx < size) { + // remove the element + // then try to merge with the next bucket + val r = array(idx) + shiftleft(idx) + size -= 1 + if (tryMergeWithNext()) buffer.lastPtr = this + r + } else next.remove(idx - size, buffer) + + @tailrec final def subtractOne(elem: T, buffer: UnrolledBuffer[T]): Boolean = { + var i = 0 + while (i < size) { + if(array(i) == elem) { + remove(i, buffer) + return true + } + i += 1 + } + if(next ne null) next.subtractOne(elem, buffer) else false + } + + // shifts left elements after `leftb` (overwrites `leftb`) + private def shiftleft(leftb: Int): Unit = { + var i = leftb + while (i < (size - 1)) { + array(i) = array(i + 1) + i += 1 + } + nullout(i, i + 1) + } + protected def tryMergeWithNext() = if (next != null && (size + next.size) < (array.length * waterline / waterlineDenom)) { + // copy the next array, then discard the next node + Array.copy(next.array, 0, array, size, next.size) + size = size + next.size + next = next.next + if (next eq null) true else false // checks if last node was thrown out + } else false + + @tailrec final def insertAll(idx: Int, t: scala.collection.IterableOnce[T], buffer: UnrolledBuffer[T]): Int = { + if (idx < size) { + // divide this node at the appropriate position and insert all into head + // update new next + val newnextnode = new Unrolled[T](0, new Array(array.length), null, buff) + Array.copy(array, idx, newnextnode.array, 0, size - idx) + newnextnode.size = size - idx + newnextnode.next = next + + // update this + nullout(idx, size) + size = idx + next = null + + // insert everything from iterable to this + var curr = this + var appended = 0 + for (elem <- t.iterator) { + curr = curr append elem + appended += 1 + } + curr.next = newnextnode + + // try to merge the last node of this with the newnextnode and fix tail pointer if needed + if (curr.tryMergeWithNext()) buffer.lastPtr = curr + else if (newnextnode.next eq null) buffer.lastPtr = newnextnode + appended + } + else if (idx == size || (next eq null)) { + var curr = this + var appended = 0 + for (elem <- t.iterator) { + curr = curr append elem + appended += 1 + } + appended + } + else next.insertAll(idx - size, t, buffer) + } + + private def nullout(from: Int, until: Int): Unit = { + var idx = from + while (idx < until) { + array(idx) = null.asInstanceOf[T] // TODO find a way to assign a default here!! + idx += 1 + } + } + + // assumes this is the last node + // `thathead` and `thatlast` are head and last node + // of the other unrolled list, respectively + def bind(thathead: Unrolled[T]) = { + assert(next eq null) + next = thathead + tryMergeWithNext() + } + + override def toString: String = + array.take(size).mkString("Unrolled@%08x".format(System.identityHashCode(this)) + "[" + size + "/" + array.length + "](", ", ", ")") + " -> " + (if (next ne null) next.toString else "") + } +} + +// This is used by scala.collection.parallel.mutable.UnrolledParArrayCombiner: +// Todo -- revisit whether inheritance is the best way to achieve this functionality +private[collection] class DoublingUnrolledBuffer[T](implicit t: ClassTag[T]) extends UnrolledBuffer[T]()(t) { + override def calcNextLength(sz: Int) = if (sz < 10000) sz * 2 else sz + override protected def newUnrolled = new UnrolledBuffer.Unrolled[T](0, new Array[T](4), null, this) +} diff --git a/library/src/scala/collection/mutable/WeakHashMap.scala b/library/src/scala/collection/mutable/WeakHashMap.scala new file mode 100644 index 000000000000..ae0230c8ab83 --- /dev/null +++ b/library/src/scala/collection/mutable/WeakHashMap.scala @@ -0,0 +1,55 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package mutable + +import scala.annotation.nowarn +import scala.collection.convert.JavaCollectionWrappers.{JMapWrapper, JMapWrapperLike} + +/** A hash map with references to entries which are weakly reachable. Entries are + * removed from this map when the key is no longer (strongly) referenced. This class wraps + * `java.util.WeakHashMap`. + * + * @tparam K type of keys contained in this map + * @tparam V type of values associated with the keys + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#weak-hash-maps "Scala's Collection Library overview"]] + * section on `Weak Hash Maps` for more information. + * + * @define Coll `WeakHashMap` + * @define coll weak hash map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(3L) +class WeakHashMap[K, V] extends JMapWrapper[K, V](new java.util.WeakHashMap) + with JMapWrapperLike[K, V, WeakHashMap, WeakHashMap[K, V]] + with MapFactoryDefaults[K, V, WeakHashMap, Iterable] { + override def empty = new WeakHashMap[K, V] + override def mapFactory: MapFactory[WeakHashMap] = WeakHashMap + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") + override protected[this] def stringPrefix = "WeakHashMap" +} + +/** $factoryInfo + * @define Coll `WeakHashMap` + * @define coll weak hash map + */ +@SerialVersionUID(3L) +object WeakHashMap extends MapFactory[WeakHashMap] { + def empty[K, V]: WeakHashMap[K,V] = new WeakHashMap[K, V] + def from[K, V](it: collection.IterableOnce[(K, V)]): WeakHashMap[K,V] = Growable.from(empty[K, V], it) + def newBuilder[K, V]: Builder[(K, V), WeakHashMap[K,V]] = new GrowableBuilder(WeakHashMap.empty[K, V]) +} + diff --git a/library/src/scala/collection/mutable/package.scala b/library/src/scala/collection/mutable/package.scala new file mode 100644 index 000000000000..4ad5df4813d8 --- /dev/null +++ b/library/src/scala/collection/mutable/package.scala @@ -0,0 +1,41 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection + + +package object mutable { + @deprecated("Use ArraySeq instead of WrappedArray; it can represent both, boxed and unboxed arrays", "2.13.0") + type WrappedArray[X] = ArraySeq[X] + @deprecated("Use ArraySeq instead of WrappedArray; it can represent both, boxed and unboxed arrays", "2.13.0") + val WrappedArray = ArraySeq + @deprecated("Use Iterable instead of Traversable", "2.13.0") + type Traversable[X] = Iterable[X] + @deprecated("Use Iterable instead of Traversable", "2.13.0") + val Traversable = Iterable + @deprecated("Use Stack instead of ArrayStack; it now uses an array-based implementation", "2.13.0") + type ArrayStack[X] = Stack[X] + @deprecated("Use Stack instead of ArrayStack; it now uses an array-based implementation", "2.13.0") + val ArrayStack = Stack + + @deprecated("mutable.LinearSeq has been removed; use LinearSeq with mutable.Seq instead", "2.13.0") + type LinearSeq[X] = Seq[X] with scala.collection.LinearSeq[X] + + @deprecated("GrowingBuilder has been renamed to GrowableBuilder", "2.13.0") + type GrowingBuilder[Elem, To <: Growable[Elem]] = GrowableBuilder[Elem, To] + + @deprecated("IndexedOptimizedSeq has been renamed to IndexedSeq", "2.13.0") + type IndexedOptimizedSeq[A] = IndexedSeq[A] + + @deprecated("IndexedOptimizedBuffer has been renamed to IndexedBuffer", "2.13.0") + type IndexedOptimizedBuffer[A] = IndexedBuffer[A] +} diff --git a/library/src/scala/collection/package.scala b/library/src/scala/collection/package.scala new file mode 100644 index 000000000000..f6a89b5c288c --- /dev/null +++ b/library/src/scala/collection/package.scala @@ -0,0 +1,80 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +package object collection { + @deprecated("Use Iterable instead of Traversable", "2.13.0") + type Traversable[+X] = Iterable[X] + @deprecated("Use Iterable instead of Traversable", "2.13.0") + val Traversable = Iterable + @deprecated("Use IterableOnce instead of TraversableOnce", "2.13.0") + type TraversableOnce[+X] = IterableOnce[X] + @deprecated("Use IterableOnce instead of TraversableOnce", "2.13.0") + val TraversableOnce = IterableOnce + @deprecated("Use SeqOps instead of SeqLike", "2.13.0") + type SeqLike[A, T] = SeqOps[A, Seq, T] + @deprecated("Use SeqOps (for the methods) or IndexedSeqOps (for fast indexed access) instead of ArrayLike", "2.13.0") + type ArrayLike[A] = SeqOps[A, Seq, Seq[A]] + + @deprecated("Gen* collection types have been removed", "2.13.0") + type GenTraversableOnce[+X] = IterableOnce[X] + @deprecated("Gen* collection types have been removed", "2.13.0") + val GenTraversableOnce = IterableOnce + @deprecated("Gen* collection types have been removed", "2.13.0") + type GenTraversable[+X] = Iterable[X] + @deprecated("Gen* collection types have been removed", "2.13.0") + val GenTraversable = Iterable + @deprecated("Gen* collection types have been removed", "2.13.0") + type GenIterable[+X] = Iterable[X] + @deprecated("Gen* collection types have been removed", "2.13.0") + val GenIterable = Iterable + @deprecated("Gen* collection types have been removed", "2.13.0") + type GenSeq[+X] = Seq[X] + @deprecated("Gen* collection types have been removed", "2.13.0") + val GenSeq = Seq + @deprecated("Gen* collection types have been removed", "2.13.0") + type GenSet[X] = Set[X] + @deprecated("Gen* collection types have been removed", "2.13.0") + val GenSet = Set + @deprecated("Gen* collection types have been removed", "2.13.0") + type GenMap[K, +V] = Map[K, V] + @deprecated("Gen* collection types have been removed", "2.13.0") + val GenMap = Map + + /** Needed to circumvent a difficulty between dotty and scalac concerning + * the right top type for a type parameter of kind * -> *. + * In Scalac, we can provide `Any`, as `Any` is kind-polymorphic. In dotty this is not allowed. + * In dotty, we can provide `[X] => Any`. But Scalac does not know lambda syntax. + */ + private[scala] type AnyConstr[X] = Any + + /** An extractor used to head/tail deconstruct sequences. */ + object +: { + /** Splits a sequence into head +: tail. + * @return Some((head, tail)) if sequence is non-empty. None otherwise. + */ + def unapply[A, CC[_] <: Seq[_], C <: SeqOps[A, CC, C]](t: C with SeqOps[A, CC, C]): Option[(A, C)] = + if(t.isEmpty) None + else Some(t.head -> t.tail) + } + + /** An extractor used to init/last deconstruct sequences. */ + object :+ { + /** Splits a sequence into init :+ last. + * @return Some((init, last)) if sequence is non-empty. None otherwise. + */ + def unapply[A, CC[_] <: Seq[_], C <: SeqOps[A, CC, C]](t: C with SeqOps[A, CC, C]): Option[(C, A)] = + if(t.isEmpty) None + else Some(t.init -> t.last) + } +} From bdb1a7e118d928d66369f7ea9511353baccc7321 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:34:30 +0100 Subject: [PATCH 08/28] chore: add all the files in scala.compat --- library/src/scala/compat/Platform.scala | 146 ++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 library/src/scala/compat/Platform.scala diff --git a/library/src/scala/compat/Platform.scala b/library/src/scala/compat/Platform.scala new file mode 100644 index 000000000000..b3ecb194a12c --- /dev/null +++ b/library/src/scala/compat/Platform.scala @@ -0,0 +1,146 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package compat + +@deprecated("Will be removed in the future.", since = "2.13.0") +object Platform { + + /** Thrown when a stack overflow occurs because a method or function recurses too deeply. + * + * On the JVM, this is a type alias for `java.lang.StackOverflowError`, which itself extends `java.lang.Error`. + * The same rules apply to catching a `java.lang.Error` as for Java, that it indicates a serious problem that a reasonable application should not try and catch. + */ + @deprecated("Use `java.lang.StackOverflowError` instead.", since = "2.13.0") + type StackOverflowError = java.lang.StackOverflowError + + /** This is a type alias for `java.util.ConcurrentModificationException`, + * which may be thrown by methods that detect an invalid modification of an object. + * For example, many common collection types do not allow modifying a collection + * while it is being iterated over. + */ + @deprecated("Use `java.util.ConcurrentModificationException` instead.", since = "2.13.0") + type ConcurrentModificationException = java.util.ConcurrentModificationException + + /** Copies `length` elements of array `src` starting at position `srcPos` to the + * array `dest` starting at position `destPos`. If `src`==`dest`, the copying will + * behave as if the elements copied from `src` were first copied to a temporary + * array before being copied back into the array at the destination positions. + * + * @param src A non-null array as source for the copy. + * @param srcPos The starting index in the source array. + * @param dest A non-null array as destination for the copy. + * @param destPos The starting index in the destination array. + * @param length The number of elements to be copied. + * @throws java.lang.NullPointerException If either `src` or `dest` are `null`. + * @throws java.lang.ArrayStoreException If either `src` or `dest` are not of type + * [java.lang.Array]; or if the element type of `src` is not + * compatible with that of `dest`. + * @throws java.lang.IndexOutOfBoundsException If either `srcPos` or `destPos` are + * outside of the bounds of their respective arrays; or if `length` + * is negative; or if there are less than `length` elements available + * after `srcPos` or `destPos` in `src` and `dest` respectively. + */ + @inline + @deprecated("Use `java.lang.System#arraycopy` instead.", since = "2.13.0") + def arraycopy(src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int): Unit = { + System.arraycopy(src, srcPos, dest, destPos, length) + } + + /** Creates a new array of the specified type and given length. + * + * Note that if `elemClass` is a subclass of [[scala.AnyVal]] then the returned value is an Array of the corresponding java primitive type. + * For example, the following code `scala.compat.Platform.createArray(classOf[Int], 4)` returns an array of the java primitive type `int`. + * + * For a [[scala.AnyVal]] array, the values of the array are set to 0 for ''numeric value types'' ([[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], + * [[scala.Short]], and [[scala.Byte]]), and `false` for [[scala.Boolean]]. Creation of an array of type [[scala.Unit]] is not possible. + * + * For subclasses of [[scala.AnyRef]], the values of the array are set to `null`. + * + * The caller must cast the returned value to the correct type. + * + * @example {{{ + * val a = scala.compat.Platform.createArray(classOf[Int], 4).asInstanceOf[Array[Int]] // returns Array[Int](0, 0, 0, 0) + * }}} + * + * @param elemClass the `Class` object of the component type of the array + * @param length the length of the new array. + * @return an array of the given component type as an `AnyRef`. + * @throws java.lang.NullPointerException If `elemClass` is `null`. + * @throws java.lang.IllegalArgumentException if componentType is [[scala.Unit]] or `java.lang.Void.TYPE` + * @throws java.lang.NegativeArraySizeException if the specified length is negative + */ + @inline + @deprecated("Use `java.lang.reflect.Array#newInstance` instead.", since = "2.13.0") + def createArray(elemClass: Class[_], length: Int): AnyRef = + java.lang.reflect.Array.newInstance(elemClass, length) + + /** Assigns the value of 0 to each element in the array. + * @param arr A non-null Array[Int]. + * @throws java.lang.NullPointerException If `arr` is `null`. + */ + @inline + @deprecated("Use `java.util.Arrays#fill` instead.", since = "2.13.0") + def arrayclear(arr: Array[Int]): Unit = { java.util.Arrays.fill(arr, 0) } + + /** Returns the `Class` object associated with the class or interface with the given string name using the current `ClassLoader`. + * On the JVM, invoking this method is equivalent to: `java.lang.Class.forName(name)` + * + * For more information, please see the Java documentation for [[java.lang.Class]]. + * + * @param name the fully qualified name of the desired class. + * @return the `Class` object for the class with the specified name. + * @throws java.lang.LinkageError if the linkage fails + * @throws java.lang.ExceptionInInitializerError if the initialization provoked by this method fails + * @throws java.lang.ClassNotFoundException if the class cannot be located + * @example {{{ + * val a = scala.compat.Platform.getClassForName("java.lang.Integer") // returns the Class[_] for java.lang.Integer + * }}} + */ + @inline + @deprecated("Use `java.lang.Class#forName` instead.", since = "2.13.0") + def getClassForName(name: String): Class[_] = java.lang.Class.forName(name) + + /** The default line separator. + * + * On the JVM, this is equivalent to calling the method: + * `java.lang.System.lineSeparator` + */ + @deprecated("Use `java.lang.System#lineSeparator` instead.", since = "2.13.0") + val EOL: String = System.lineSeparator + + /** The current time in milliseconds. The time is counted since 1 January 1970 + * UTC. + * + * Note that the operating system timer used to obtain this value may be less + * precise than a millisecond. + */ + @inline + @deprecated("Use `java.lang.System#currentTimeMillis` instead.", since = "2.13.0") + def currentTime: Long = System.currentTimeMillis() + + /** Runs the garbage collector. + * + * This is a request that the underlying JVM runs the garbage collector. + * The results of this call depends heavily on the JVM used. + * The underlying JVM is free to ignore this request. + */ + @inline + @deprecated("Use `java.lang.System#gc` instead.", since = "2.13.0") + def collectGarbage(): Unit = System.gc() + + /** The name of the default character set encoding as a string */ + @inline + @deprecated("Use `java.nio.charset.Charset.defaultCharset#name` instead.", since = "2.13.0") + def defaultCharsetName: String = java.nio.charset.Charset.defaultCharset.name +} From 33493b24657c2383d2ab6ebfb11dcfd4b887e03b Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:34:59 +0100 Subject: [PATCH 09/28] chore: add all the files in scala.concurrent --- library/src/scala/concurrent/Awaitable.scala | 69 ++ .../scala/concurrent/BatchingExecutor.scala | 270 ++++++ .../src/scala/concurrent/BlockContext.scala | 110 +++ library/src/scala/concurrent/Channel.scala | 59 ++ .../src/scala/concurrent/DelayedLazyVal.scala | 47 + .../scala/concurrent/ExecutionContext.scala | 295 ++++++ library/src/scala/concurrent/Future.scala | 878 ++++++++++++++++++ .../scala/concurrent/JavaConversions.scala | 38 + library/src/scala/concurrent/Promise.scala | 148 +++ .../src/scala/concurrent/SyncChannel.scala | 77 ++ library/src/scala/concurrent/SyncVar.scala | 122 +++ .../scala/concurrent/duration/Deadline.scala | 85 ++ .../scala/concurrent/duration/Duration.scala | 742 +++++++++++++++ .../duration/DurationConversions.scala | 96 ++ .../scala/concurrent/duration/package.scala | 87 ++ .../impl/ExecutionContextImpl.scala | 137 +++ .../impl/FutureConvertersImpl.scala | 101 ++ .../src/scala/concurrent/impl/Promise.scala | 510 ++++++++++ library/src/scala/concurrent/package.scala | 204 ++++ 19 files changed, 4075 insertions(+) create mode 100644 library/src/scala/concurrent/Awaitable.scala create mode 100644 library/src/scala/concurrent/BatchingExecutor.scala create mode 100644 library/src/scala/concurrent/BlockContext.scala create mode 100644 library/src/scala/concurrent/Channel.scala create mode 100644 library/src/scala/concurrent/DelayedLazyVal.scala create mode 100644 library/src/scala/concurrent/ExecutionContext.scala create mode 100644 library/src/scala/concurrent/Future.scala create mode 100644 library/src/scala/concurrent/JavaConversions.scala create mode 100644 library/src/scala/concurrent/Promise.scala create mode 100644 library/src/scala/concurrent/SyncChannel.scala create mode 100644 library/src/scala/concurrent/SyncVar.scala create mode 100644 library/src/scala/concurrent/duration/Deadline.scala create mode 100644 library/src/scala/concurrent/duration/Duration.scala create mode 100644 library/src/scala/concurrent/duration/DurationConversions.scala create mode 100644 library/src/scala/concurrent/duration/package.scala create mode 100644 library/src/scala/concurrent/impl/ExecutionContextImpl.scala create mode 100644 library/src/scala/concurrent/impl/FutureConvertersImpl.scala create mode 100644 library/src/scala/concurrent/impl/Promise.scala create mode 100644 library/src/scala/concurrent/package.scala diff --git a/library/src/scala/concurrent/Awaitable.scala b/library/src/scala/concurrent/Awaitable.scala new file mode 100644 index 000000000000..1372e1614ac8 --- /dev/null +++ b/library/src/scala/concurrent/Awaitable.scala @@ -0,0 +1,69 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + + + +import scala.concurrent.duration.Duration + + + +/** + * An object that may eventually be completed with a result value of type `T` which may be + * awaited using blocking methods. + * + * The [[Await]] object provides methods that allow accessing the result of an `Awaitable` + * by blocking the current thread until the `Awaitable` has been completed or a timeout has + * occurred. + */ +trait Awaitable[+T] { + + /** + * Await the "completed" state of this `Awaitable`. + * + * '''''This method should not be called directly; use [[Await.ready]] instead.''''' + * + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[scala.concurrent.duration.Duration.Inf Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return this `Awaitable` + * @throws InterruptedException if the current thread is interrupted while waiting + * @throws TimeoutException if after waiting for the specified time this `Awaitable` is still not ready + * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]] + */ + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) + def ready(atMost: Duration)(implicit permit: CanAwait): this.type + + /** + * Await and return the result (of type `T`) of this `Awaitable`. + * + * '''''This method should not be called directly; use [[Await.result]] instead.''''' + * + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[scala.concurrent.duration.Duration.Inf Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return the result value if the `Awaitable` is completed within the specific maximum wait time + * @throws InterruptedException if the current thread is interrupted while waiting + * @throws TimeoutException if after waiting for the specified time this `Awaitable` is still not ready + * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]] + */ + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) + def result(atMost: Duration)(implicit permit: CanAwait): T +} + + + diff --git a/library/src/scala/concurrent/BatchingExecutor.scala b/library/src/scala/concurrent/BatchingExecutor.scala new file mode 100644 index 000000000000..ac197c89f8c1 --- /dev/null +++ b/library/src/scala/concurrent/BatchingExecutor.scala @@ -0,0 +1,270 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +import java.util.concurrent.Executor +import java.util.Objects +import scala.util.control.NonFatal +import scala.annotation.{switch, tailrec} + +/** + * Marker trait to indicate that a Runnable is Batchable by BatchingExecutors + */ +trait Batchable { + self: Runnable => +} + +private[concurrent] object BatchingExecutorStatics { + final val emptyBatchArray: Array[Runnable] = new Array[Runnable](0) + + // Max number of Runnables executed nested before starting to batch (to prevent stack exhaustion) + final val syncPreBatchDepth = 16 + + // Max number of Runnables processed in one go (to prevent starvation of other tasks on the pool) + final val runLimit = 1024 + + object MissingParentBlockContext extends BlockContext { + override def blockOn[T](thunk: => T)(implicit permission: CanAwait): T = + try thunk finally throw new IllegalStateException("BUG in BatchingExecutor.Batch: parentBlockContext is null") + } +} + +/** + * Mixin trait for an Executor + * which groups multiple nested `Runnable.run()` calls + * into a single Runnable passed to the original + * Executor. This can be a useful optimization + * because it bypasses the original context's task + * queue and keeps related (nested) code on a single + * thread which may improve CPU affinity. However, + * if tasks passed to the Executor are blocking + * or expensive, this optimization can prevent work-stealing + * and make performance worse. + * A batching executor can create deadlocks if code does + * not use `scala.concurrent.blocking` when it should, + * because tasks created within other tasks will block + * on the outer task completing. + * This executor may run tasks in any order, including LIFO order. + * There are no ordering guarantees. + * + * WARNING: Only use *EITHER* `submitAsyncBatched` OR `submitSyncBatched`!! + * + * When you implement this trait for async executors like thread pools, + * you're going to need to implement it something like the following: + * + * {{{ + * final override def submitAsync(runnable: Runnable): Unit = + * super[SuperClass].execute(runnable) // To prevent reentrancy into `execute` + * + * final override def execute(runnable: Runnable): Unit = + * if (runnable.isInstanceOf[Batchable]) // Or other logic + * submitAsyncBatched(runnable) + * else + * submitAsync(runnable) + * + * final override def reportFailure(cause: Throwable): Unit = … + * }}} + * + * And if you want to implement if for a sync, trampolining, executor you're + * going to implement it something like this: + * + * {{{ + * final override def submitAsync(runnable: Runnable): Unit = () + * + * final override def execute(runnable: Runnable): Unit = + * submitSyncBatched(runnable) // You typically will want to batch everything + * + * final override def reportFailure(cause: Throwable): Unit = + * ExecutionContext.defaultReporter(cause) // Or choose something more fitting + * }}} + * + */ +private[concurrent] trait BatchingExecutor extends Executor { + private[this] final val _tasksLocal = new ThreadLocal[AnyRef]() + + /* + * Batch implements a LIFO queue (stack) and is used as a trampolining Runnable. + * In order to conserve allocations, the first element in the batch is stored "unboxed" in + * the `first` field. Subsequent Runnables are stored in the array called `other`. + */ + private[this] sealed abstract class AbstractBatch protected (protected final var first: Runnable, protected final var other: Array[Runnable], protected final var size: Int) { + + private[this] final def ensureCapacity(curSize: Int): Array[Runnable] = { + val curOther = this.other + val curLen = curOther.length + if (curSize <= curLen) curOther + else { + val newLen = if (curLen == 0) 4 else curLen << 1 + + if (newLen <= curLen) throw new StackOverflowError("Space limit of asynchronous stack reached: " + curLen) + val newOther = new Array[Runnable](newLen) + System.arraycopy(curOther, 0, newOther, 0, curLen) + this.other = newOther + newOther + } + } + + final def push(r: Runnable): Unit = { + val sz = this.size + if(sz == 0) + this.first = r + else + ensureCapacity(sz)(sz - 1) = r + this.size = sz + 1 + } + + @tailrec protected final def runN(n: Int): Unit = + if (n > 0) + (this.size: @switch) match { + case 0 => + case 1 => + val next = this.first + this.first = null + this.size = 0 + next.run() + runN(n - 1) + case sz => + val o = this.other + val next = o(sz - 2) + o(sz - 2) = null + this.size = sz - 1 + next.run() + runN(n - 1) + } + } + + private[this] final class AsyncBatch private(_first: Runnable, _other: Array[Runnable], _size: Int) extends AbstractBatch(_first, _other, _size) with Runnable with BlockContext with (BlockContext => Throwable) { + private[this] final var parentBlockContext: BlockContext = BatchingExecutorStatics.MissingParentBlockContext + + final def this(runnable: Runnable) = this(runnable, BatchingExecutorStatics.emptyBatchArray, 1) + + override final def run(): Unit = { + _tasksLocal.set(this) // This is later cleared in `apply` or `runWithoutResubmit` + + val f = resubmit(BlockContext.usingBlockContext(this)(this)) + + if (f != null) + throw f + } + + /* LOGIC FOR ASYNCHRONOUS BATCHES */ + override final def apply(prevBlockContext: BlockContext): Throwable = try { + parentBlockContext = prevBlockContext + runN(BatchingExecutorStatics.runLimit) + null + } catch { + case t: Throwable => t // We are handling exceptions on the outside of this method + } finally { + parentBlockContext = BatchingExecutorStatics.MissingParentBlockContext + _tasksLocal.remove() + } + + /* Attempts to resubmit this Batch to the underlying ExecutionContext, + * this only happens for Batches where `resubmitOnBlock` is `true`. + * Only attempt to resubmit when there are `Runnables` left to process. + * Note that `cause` can be `null`. + */ + private[this] final def resubmit(cause: Throwable): Throwable = + if (this.size > 0) { + try { submitForExecution(this); cause } catch { + case inner: Throwable => + if (NonFatal(inner)) { + val e = new ExecutionException("Non-fatal error occurred and resubmission failed, see suppressed exception.", cause) + e.addSuppressed(inner) + e + } else inner + } + } else cause // TODO: consider if NonFatals should simply be `reportFailure`:ed rather than rethrown + + private[this] final def cloneAndClear(): AsyncBatch = { + val newBatch = new AsyncBatch(this.first, this.other, this.size) + this.first = null + this.other = BatchingExecutorStatics.emptyBatchArray + this.size = 0 + newBatch + } + + override final def blockOn[T](thunk: => T)(implicit permission: CanAwait): T = { + // If we know there will be blocking, we don't want to keep tasks queued up because it could deadlock. + if(this.size > 0) + submitForExecution(cloneAndClear()) // If this throws then we have bigger problems + + parentBlockContext.blockOn(thunk) // Now delegate the blocking to the previous BC + } + } + + private[this] final class SyncBatch(runnable: Runnable) extends AbstractBatch(runnable, BatchingExecutorStatics.emptyBatchArray, 1) with Runnable { + @tailrec override final def run(): Unit = { + try runN(BatchingExecutorStatics.runLimit) catch { + case ie: InterruptedException => + reportFailure(ie) // TODO: Handle InterruptedException differently? + case f if NonFatal(f) => + reportFailure(f) + } + + if (this.size > 0) + run() + } + } + + /** MUST throw a NullPointerException when `runnable` is null + * When implementing a sync BatchingExecutor, it is RECOMMENDED + * to implement this method as `runnable.run()` + */ + protected def submitForExecution(runnable: Runnable): Unit + + /** Reports that an asynchronous computation failed. + * See `ExecutionContext.reportFailure(throwable: Throwable)` + */ + protected def reportFailure(throwable: Throwable): Unit + + /** + * WARNING: Never use both `submitAsyncBatched` and `submitSyncBatched` in the same + * implementation of `BatchingExecutor` + */ + protected final def submitAsyncBatched(runnable: Runnable): Unit = { + val b = _tasksLocal.get + if (b.isInstanceOf[AsyncBatch]) b.asInstanceOf[AsyncBatch].push(runnable) + else submitForExecution(new AsyncBatch(runnable)) + } + + /** + * WARNING: Never use both `submitAsyncBatched` and `submitSyncBatched` in the same + * implementation of `BatchingExecutor` + */ + protected final def submitSyncBatched(runnable: Runnable): Unit = { + Objects.requireNonNull(runnable, "runnable is null") + val tl = _tasksLocal + val b = tl.get + if (b.isInstanceOf[SyncBatch]) b.asInstanceOf[SyncBatch].push(runnable) + else { + val i = if (b ne null) b.asInstanceOf[java.lang.Integer].intValue else 0 + if (i < BatchingExecutorStatics.syncPreBatchDepth) { + tl.set(java.lang.Integer.valueOf(i + 1)) + try submitForExecution(runnable) // User code so needs to be try-finally guarded here + catch { + case ie: InterruptedException => + reportFailure(ie) // TODO: Handle InterruptedException differently? + case f if NonFatal(f) => + reportFailure(f) + } + finally tl.set(b) + } else { + val batch = new SyncBatch(runnable) + tl.set(batch) + submitForExecution(batch) + tl.set(b) // Batch only throws fatals so no need for try-finally here + } + } + } +} diff --git a/library/src/scala/concurrent/BlockContext.scala b/library/src/scala/concurrent/BlockContext.scala new file mode 100644 index 000000000000..37483c307fd0 --- /dev/null +++ b/library/src/scala/concurrent/BlockContext.scala @@ -0,0 +1,110 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +/** + * A context to be notified by [[scala.concurrent.blocking]] when + * a thread is about to block. In effect this trait provides + * the implementation for [[scala.concurrent.Await]]. + * [[scala.concurrent.Await.result]] and [[scala.concurrent.Await.ready]] + * locates an instance of `BlockContext` by first looking for one + * provided through [[BlockContext.withBlockContext]] and failing that, + * checking whether `Thread.currentThread` is an instance of `BlockContext`. + * So a thread pool can have its `java.lang.Thread` instances implement + * `BlockContext`. There's a default `BlockContext` used if the thread + * doesn't implement `BlockContext`. + * + * Typically, you'll want to chain to the previous `BlockContext`, + * like this: + * {{{ + * val oldContext = BlockContext.current + * val myContext = new BlockContext { + * override def blockOn[T](thunk: => T)(implicit permission: CanAwait): T = { + * // you'd have code here doing whatever you need to do + * // when the thread is about to block. + * // Then you'd chain to the previous context: + * oldContext.blockOn(thunk) + * } + * } + * BlockContext.withBlockContext(myContext) { + * // then this block runs with myContext as the handler + * // for scala.concurrent.blocking + * } + * }}} + */ +trait BlockContext { + + /** Used internally by the framework; + * Designates (and eventually executes) a thunk which potentially blocks the calling `java.lang.Thread`. + * + * Clients must use `scala.concurrent.blocking` or `scala.concurrent.Await` instead. + * + * In implementations of this method it is RECOMMENDED to first check if `permission` is `null` and + * if it is, throw an `IllegalArgumentException`. + * + * @throws IllegalArgumentException if the `permission` is `null` + */ + def blockOn[T](thunk: => T)(implicit permission: CanAwait): T +} + +object BlockContext { + private[this] object DefaultBlockContext extends BlockContext { + override final def blockOn[T](thunk: => T)(implicit permission: CanAwait): T = thunk + } + + /** + * The default block context will execute the supplied thunk immediately. + * @return the `BlockContext` that will be used if no other is found. + **/ + final def defaultBlockContext: BlockContext = DefaultBlockContext + + private[this] final val contextLocal = new ThreadLocal[BlockContext]() + + private[this] final def prefer(candidate: BlockContext): BlockContext = + if (candidate ne null) candidate + else { + val t = Thread.currentThread + if (t.isInstanceOf[BlockContext]) t.asInstanceOf[BlockContext] + else DefaultBlockContext + } + + /** + * @return the `BlockContext` that would be used for the current `java.lang.Thread` at this point + **/ + final def current: BlockContext = prefer(contextLocal.get) + + /** + * Installs a current `BlockContext` around executing `body`. + **/ + final def withBlockContext[T](blockContext: BlockContext)(body: => T): T = { + val old = contextLocal.get // can be null + if (old eq blockContext) body + else { + contextLocal.set(blockContext) + try body finally contextLocal.set(old) + } + } + + /** + * Installs the BlockContext `blockContext` around the invocation to `f` and passes in the previously installed BlockContext to `f`. + * @return the value produced by applying `f` + **/ + final def usingBlockContext[I, T](blockContext: BlockContext)(f: BlockContext => T): T = { + val old = contextLocal.get // can be null + if (old eq blockContext) f(prefer(old)) + else { + contextLocal.set(blockContext) + try f(prefer(old)) finally contextLocal.set(old) + } + } +} diff --git a/library/src/scala/concurrent/Channel.scala b/library/src/scala/concurrent/Channel.scala new file mode 100644 index 000000000000..a9ada60e3da0 --- /dev/null +++ b/library/src/scala/concurrent/Channel.scala @@ -0,0 +1,59 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +/** This class provides a simple FIFO queue of data objects, + * which are read by one or more reader threads. + * + * @tparam A type of data exchanged + */ +@deprecated("Use `java.util.concurrent.LinkedTransferQueue` instead.", since = "2.13.0") +class Channel[A] { + private class LinkedList { + var elem: A = _ + var next: LinkedList = _ + } + private[this] var written = new LinkedList // FIFO queue, realized through + private[this] var lastWritten = written // aliasing of a linked list + private[this] var nreaders = 0 + + /** Append a value to the FIFO queue to be read by `read`. + * This operation is nonblocking and can be executed by any thread. + * + * @param x object to enqueue to this channel + */ + def write(x: A): Unit = synchronized { + lastWritten.elem = x + lastWritten.next = new LinkedList + lastWritten = lastWritten.next + if (nreaders > 0) notify() + } + + /** Retrieve the next waiting object from the FIFO queue, + * blocking if necessary until an object is available. + * + * @return next object dequeued from this channel + */ + def read: A = synchronized { + while (written.next == null) { + try { + nreaders += 1 + wait() + } + finally nreaders -= 1 + } + val x = written.elem + written = written.next + x + } +} diff --git a/library/src/scala/concurrent/DelayedLazyVal.scala b/library/src/scala/concurrent/DelayedLazyVal.scala new file mode 100644 index 000000000000..1a450c3c0458 --- /dev/null +++ b/library/src/scala/concurrent/DelayedLazyVal.scala @@ -0,0 +1,47 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + + +/** A `DelayedLazyVal` is a wrapper for lengthy computations which have a + * valid partially computed result. + * + * The first argument is a function for obtaining the result at any given + * point in time, and the second is the lengthy computation. Once the + * computation is complete, the `apply` method will stop recalculating it + * and return a fixed value from that point forward. + * + * @param f the function to obtain the current value at any point in time + * @param body the computation to run to completion in another thread + */ +@deprecated("`DelayedLazyVal` Will be removed in the future.", since = "2.13.0") +class DelayedLazyVal[T](f: () => T, body: => Unit)(implicit exec: ExecutionContext){ + @volatile private[this] var _isDone = false + private[this] lazy val complete = f() + + /** Whether the computation is complete. + * + * @return true if the computation is complete. + */ + def isDone: Boolean = _isDone + + /** The current result of f(), or the final result if complete. + * + * @return the current value + */ + def apply(): T = if (isDone) complete else f() + + exec.execute(() => { + body; _isDone = true + }) +} diff --git a/library/src/scala/concurrent/ExecutionContext.scala b/library/src/scala/concurrent/ExecutionContext.scala new file mode 100644 index 000000000000..b132e2dee5b7 --- /dev/null +++ b/library/src/scala/concurrent/ExecutionContext.scala @@ -0,0 +1,295 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + + +import java.util.concurrent.{ ExecutorService, Executor } +import scala.annotation.implicitNotFound + +/** + * An `ExecutionContext` can execute program logic asynchronously, + * typically but not necessarily on a thread pool. + * + * A general purpose `ExecutionContext` must be asynchronous in executing + * any `Runnable` that is passed into its `execute`-method. A special purpose + * `ExecutionContext` may be synchronous but must only be passed to code that + * is explicitly safe to be run using a synchronously executing `ExecutionContext`. + * + * APIs such as `Future.onComplete` require you to provide a callback + * and an implicit `ExecutionContext`. The implicit `ExecutionContext` + * will be used to execute the callback. + * + * While it is possible to simply import + * `scala.concurrent.ExecutionContext.Implicits.global` to obtain an + * implicit `ExecutionContext`, application developers should carefully + * consider where they want to define the execution policy; + * ideally, one place per application — or per logically related section of code — + * will make a decision about which `ExecutionContext` to use. + * That is, you will mostly want to avoid hardcoding, especially via an import, + * `scala.concurrent.ExecutionContext.Implicits.global`. + * The recommended approach is to add `(implicit ec: ExecutionContext)` to methods, + * or class constructor parameters, which need an `ExecutionContext`. + * + * Then locally import a specific `ExecutionContext` in one place for the entire + * application or module, passing it implicitly to individual methods. + * Alternatively define a local implicit val with the required `ExecutionContext`. + * + * A custom `ExecutionContext` may be appropriate to execute code + * which blocks on IO or performs long-running computations. + * `ExecutionContext.fromExecutorService` and `ExecutionContext.fromExecutor` + * are good ways to create a custom `ExecutionContext`. + * + * The intent of `ExecutionContext` is to lexically scope code execution. + * That is, each method, class, file, package, or application determines + * how to run its own code. This avoids issues such as running + * application callbacks on a thread pool belonging to a networking library. + * The size of a networking library's thread pool can be safely configured, + * knowing that only that library's network operations will be affected. + * Application callback execution can be configured separately. + */ +@implicitNotFound("""Cannot find an implicit ExecutionContext. You might add +an (implicit ec: ExecutionContext) parameter to your method. + +The ExecutionContext is used to configure how and on which +thread pools asynchronous tasks (such as Futures) will run, +so the specific ExecutionContext that is selected is important. + +If your application does not define an ExecutionContext elsewhere, +consider using Scala's global ExecutionContext by defining +the following: + +implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global""") +trait ExecutionContext { + + /** Runs a block of code on this execution context. + * + * @param runnable the task to execute + */ + def execute(runnable: Runnable): Unit + + /** Reports that an asynchronous computation failed. + * + * @param cause the cause of the failure + */ + def reportFailure(@deprecatedName("t") cause: Throwable): Unit + + /** Prepares for the execution of a task. Returns the prepared + * execution context. The recommended implementation of + * `prepare` is to return `this`. + * + * This method should no longer be overridden or called. It was + * originally expected that `prepare` would be called by + * all libraries that consume ExecutionContexts, in order to + * capture thread local context. However, this usage has proven + * difficult to implement in practice and instead it is + * now better to avoid using `prepare` entirely. + * + * Instead, if an `ExecutionContext` needs to capture thread + * local context, it should capture that context when it is + * constructed, so that it doesn't need any additional + * preparation later. + */ + @deprecated("preparation of ExecutionContexts will be removed", "2.12.0") + // This cannot be removed until there is a suitable replacement + def prepare(): ExecutionContext = this +} + +/** + * An [[ExecutionContext]] that is also a + * Java [[java.util.concurrent.Executor Executor]]. + */ +trait ExecutionContextExecutor extends ExecutionContext with Executor + +/** + * An [[ExecutionContext]] that is also a + * Java [[java.util.concurrent.ExecutorService ExecutorService]]. + */ +trait ExecutionContextExecutorService extends ExecutionContextExecutor with ExecutorService + + +/** Contains factory methods for creating execution contexts. + */ +object ExecutionContext { + /** + * The global [[ExecutionContext]]. This default `ExecutionContext` implementation is backed by a work-stealing thread + * pool. It can be configured via the following system properties: + * + * - `scala.concurrent.context.minThreads` = defaults to "1" + * - `scala.concurrent.context.numThreads` = defaults to "x1" (i.e. the current number of available processors * 1) + * - `scala.concurrent.context.maxThreads` = defaults to "x1" (i.e. the current number of available processors * 1) + * - `scala.concurrent.context.maxExtraThreads` = defaults to "256" + * + * The pool size of threads is then `numThreads` bounded by `minThreads` on the lower end and `maxThreads` on the high end. + * + * The `maxExtraThreads` is the maximum number of extra threads to have at any given time to evade deadlock, + * see [[scala.concurrent.blocking]]. + * + * The `global` execution context can be used explicitly, by defining an + * `implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global`, or by importing + * [[ExecutionContext.Implicits.global]]. + * + * == Batching short-lived nested tasks == + * + * Asynchronous code with short-lived nested tasks is executed more efficiently when using + * `ExecutionContext.opportunistic` (continue reading to learn why it is `private[scala]` and how to access it). + * + * `ExecutionContext.opportunistic` uses the same thread pool as `ExecutionContext.global`. It attempts to batch + * nested task and execute them on the same thread as the enclosing task. This is ideally suited to execute + * short-lived tasks as it reduces the overhead of context switching. + * + * WARNING: long-running and/or blocking tasks should be demarcated within [[scala.concurrent.blocking]]-blocks + * to ensure that any pending tasks in the current batch can be executed by another thread on `global`. + * + * === How to use === + * + * This field is `private[scala]` to maintain binary compatibility. It was added in 2.13.4, code that references it + * directly fails to run with a 2.13.0-3 Scala library. + * + * Libraries should not reference this field directly because users of the library might be using an earlier Scala + * version. In order to use the batching `ExecutionContext` in a library, the code needs to fall back to `global` + * in case the `opportunistic` field is missing (example below). The resulting `ExecutionContext` has batching + * behavior in all Scala 2.13 versions (`global` is batching in 2.13.0-3). + * + * {{{ + * implicit val ec: scala.concurrent.ExecutionContext = try { + * scala.concurrent.ExecutionContext.getClass + * .getDeclaredMethod("opportunistic") + * .invoke(scala.concurrent.ExecutionContext) + * .asInstanceOf[scala.concurrent.ExecutionContext] + * } catch { + * case _: NoSuchMethodException => + * scala.concurrent.ExecutionContext.global + * } + * }}} + * + * Application authors can safely use the field because the Scala version at run time is the same as at compile time. + * Options to bypass the access restriction include: + * + * 1. Using a structural type (example below). This uses reflection at run time. + * 1. Writing a Scala `object` in the `scala` package (example below). + * 1. Writing a Java source file. This works because `private[scala]` is emitted as `public` in Java bytecode. + * + * {{{ + * // Option 1 + * implicit val ec: scala.concurrent.ExecutionContext = + * (scala.concurrent.ExecutionContext: + * {def opportunistic: scala.concurrent.ExecutionContextExecutor} + * ).opportunistic + * + * // Option 2 + * package scala { + * object OpportunisticEC { + * implicit val ec: scala.concurrent.ExecutionContext = + * scala.concurrent.ExecutionContext.opportunistic + * } + * } + * }}} + * + * @return the global [[ExecutionContext]] + */ + final lazy val global: ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(null: Executor) + + /** + * WARNING: Only ever execute logic which will quickly return control to the caller. + * + * This `ExecutionContext` steals execution time from other threads by having its + * `Runnable`s run on the `Thread` which calls `execute` and then yielding back control + * to the caller after *all* its `Runnable`s have been executed. + * Nested invocations of `execute` will be trampolined to prevent uncontrolled stack space growth. + * + * When using `parasitic` with abstractions such as `Future` it will in many cases be non-deterministic + * as to which `Thread` will be executing the logic, as it depends on when/if that `Future` is completed. + * + * Do *not* call any blocking code in the `Runnable`s submitted to this `ExecutionContext` + * as it will prevent progress by other enqueued `Runnable`s and the calling `Thread`. + * + * Symptoms of misuse of this `ExecutionContext` include, but are not limited to, deadlocks + * and severe performance problems. + * + * Any `NonFatal` or `InterruptedException`s will be reported to the `defaultReporter`. + */ + object parasitic extends ExecutionContextExecutor with BatchingExecutor { + override final def submitForExecution(runnable: Runnable): Unit = runnable.run() + override final def execute(runnable: Runnable): Unit = submitSyncBatched(runnable) + override final def reportFailure(t: Throwable): Unit = defaultReporter(t) + } + + /** + * See [[ExecutionContext.global]]. + */ + private[scala] lazy val opportunistic: ExecutionContextExecutor = new ExecutionContextExecutor with BatchingExecutor { + final override def submitForExecution(runnable: Runnable): Unit = global.execute(runnable) + + final override def execute(runnable: Runnable): Unit = + if ((!runnable.isInstanceOf[impl.Promise.Transformation[_,_]] || runnable.asInstanceOf[impl.Promise.Transformation[_,_]].benefitsFromBatching) && runnable.isInstanceOf[Batchable]) + submitAsyncBatched(runnable) + else + submitForExecution(runnable) + + override final def reportFailure(t: Throwable): Unit = global.reportFailure(t) + } + + object Implicits { + /** + * An accessor that can be used to import the global `ExecutionContext` into the implicit scope, + * see [[ExecutionContext.global]]. + */ + implicit final def global: ExecutionContext = ExecutionContext.global + } + + /** Creates an `ExecutionContext` from the given `ExecutorService`. + * + * @param e the `ExecutorService` to use. If `null`, a new `ExecutorService` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @param reporter a function for error reporting + * @return the `ExecutionContext` using the given `ExecutorService` + */ + def fromExecutorService(e: ExecutorService, reporter: Throwable => Unit): ExecutionContextExecutorService = + impl.ExecutionContextImpl.fromExecutorService(e, reporter) + + /** Creates an `ExecutionContext` from the given `ExecutorService` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. + * + * If it is guaranteed that none of the executed tasks are blocking, a single-threaded `ExecutorService` + * can be used to create an `ExecutionContext` as follows: + * + * {{{ + * import java.util.concurrent.Executors + * val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor()) + * }}} + * + * @param e the `ExecutorService` to use. If `null`, a new `ExecutorService` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @return the `ExecutionContext` using the given `ExecutorService` + */ + def fromExecutorService(e: ExecutorService): ExecutionContextExecutorService = fromExecutorService(e, defaultReporter) + + /** Creates an `ExecutionContext` from the given `Executor`. + * + * @param e the `Executor` to use. If `null`, a new `Executor` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @param reporter a function for error reporting + * @return the `ExecutionContext` using the given `Executor` + */ + def fromExecutor(e: Executor, reporter: Throwable => Unit): ExecutionContextExecutor = + impl.ExecutionContextImpl.fromExecutor(e, reporter) + + /** Creates an `ExecutionContext` from the given `Executor` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. + * + * @param e the `Executor` to use. If `null`, a new `Executor` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @return the `ExecutionContext` using the given `Executor` + */ + def fromExecutor(e: Executor): ExecutionContextExecutor = fromExecutor(e, defaultReporter) + + /** The default reporter simply prints the stack trace of the `Throwable` to [[java.lang.System#err System.err]]. + * + * @return the function for error reporting + */ + final val defaultReporter: Throwable => Unit = _.printStackTrace() +} diff --git a/library/src/scala/concurrent/Future.scala b/library/src/scala/concurrent/Future.scala new file mode 100644 index 000000000000..371575918e1c --- /dev/null +++ b/library/src/scala/concurrent/Future.scala @@ -0,0 +1,878 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +import java.util.concurrent.atomic.AtomicReference +import java.util.concurrent.locks.LockSupport + +import scala.util.control.{NonFatal, NoStackTrace} +import scala.util.{Failure, Success, Try} +import scala.concurrent.duration._ +import scala.collection.BuildFrom +import scala.collection.mutable.{Builder, ArrayBuffer} +import scala.reflect.ClassTag + +import scala.concurrent.ExecutionContext.parasitic + +/** A `Future` represents a value which may or may not be currently available, + * but will be available at some point, or an exception if that value could not be made available. + * + * Asynchronous computations are created by calling `Future.apply`, which yields instances of `Future`. + * Computations are executed using an `ExecutionContext`, which is usually supplied implicitly, + * and which is commonly backed by a thread pool. + * + * {{{ + * import ExecutionContext.Implicits.global + * val s = "Hello" + * val f: Future[String] = Future { + * s + " future!" + * } + * f foreach { + * msg => println(msg) + * } + * }}} + * + * Note that the `global` context is convenient but restricted: + * "fatal" exceptions are reported only by printing a stack trace, + * and the underlying thread pool may be shared by a mix of jobs. + * For any nontrivial application, see the caveats explained at [[ExecutionContext]] + * and also the overview linked below, which explains + * [[https://docs.scala-lang.org/overviews/core/futures.html#exceptions exception handling]] + * in depth. + * + * + * @see [[https://docs.scala-lang.org/overviews/core/futures.html Futures and Promises]] + * + * @define multipleCallbacks + * Multiple callbacks may be registered; there is no guarantee that they will be + * executed in a particular order. + * + * @define caughtThrowables + * This future may contain a throwable object and this means that the future failed. + * Futures obtained through combinators have the same exception as the future they were obtained from. + * The following throwable objects are not contained in the future: + * - `Error` - fatal errors are not contained within futures + * - `InterruptedException` - not contained within futures + * - all `scala.util.control.ControlThrowable` except `NonLocalReturnControl` - not contained within futures + * + * Instead, the future is completed with an ExecutionException that has one of the exceptions above as its cause. + * If a future is failed with a `scala.runtime.NonLocalReturnControl`, + * it is completed with a value from that throwable instead. + * + * @define swallowsExceptions + * Since this method executes asynchronously and does not produce a return value, + * any non-fatal exceptions thrown will be reported to the `ExecutionContext`. + * + * @define nonDeterministic + * Note: using this method yields nondeterministic dataflow programs. + * + * @define forComprehensionExamples + * Example: + * + * {{{ + * val f = Future { 5 } + * val g = Future { 3 } + * val h = for { + * x: Int <- f // returns Future(5) + * y: Int <- g // returns Future(3) + * } yield x + y + * }}} + * + * is translated to: + * + * {{{ + * f flatMap { (x: Int) => g map { (y: Int) => x + y } } + * }}} + * + * @define callbackInContext + * The provided callback always runs in the provided implicit + *`ExecutionContext`, though there is no guarantee that the + * `execute()` method on the `ExecutionContext` will be called once + * per callback or that `execute()` will be called in the current + * thread. That is, the implementation may run multiple callbacks + * in a batch within a single `execute()` and it may run + * `execute()` either immediately or asynchronously. + * Completion of the Future must *happen-before* the invocation of the callback. + */ +trait Future[+T] extends Awaitable[T] { + + /* Callbacks */ + + /** When this future is completed, either through an exception, or a value, + * apply the provided function. + * + * If the future has already been completed, + * this will either be applied immediately or be scheduled asynchronously. + * + * Note that the returned value of `f` will be discarded. + * + * $swallowsExceptions + * $multipleCallbacks + * $callbackInContext + * + * @tparam U only used to accept any return type of the given callback function + * @param f the function to be executed when this `Future` completes + * @group Callbacks + */ + def onComplete[U](f: Try[T] => U)(implicit executor: ExecutionContext): Unit + + + /* Miscellaneous */ + + /** Returns whether the future had already been completed with + * a value or an exception. + * + * $nonDeterministic + * + * @return `true` if the future was completed, `false` otherwise + * @group Polling + */ + def isCompleted: Boolean + + /** The current value of this `Future`. + * + * $nonDeterministic + * + * If the future was not completed the returned value will be `None`. + * If the future was completed the value will be `Some(Success(t))` + * if it contained a valid result, or `Some(Failure(error))` if it contained + * an exception. + * + * @return `None` if the `Future` wasn't completed, `Some` if it was. + * @group Polling + */ + def value: Option[Try[T]] + + + /* Projections */ + + /** The returned `Future` will be successfully completed with the `Throwable` of the original `Future` + * if the original `Future` fails. + * + * If the original `Future` is successful, the returned `Future` is failed with a `NoSuchElementException`. + * + * $caughtThrowables + * + * @return a failed projection of this `Future`. + * @group Transformations + */ + def failed: Future[Throwable] = transform(Future.failedFun)(parasitic) + + + /* Monadic operations */ + + /** Asynchronously processes the value in the future once the value becomes available. + * + * WARNING: Will not be called if this future is never completed or if it is completed with a failure. + * + * $swallowsExceptions + * + * @tparam U only used to accept any return type of the given callback function + * @param f the function which will be executed if this `Future` completes with a result, + * the return value of `f` will be discarded. + * @group Callbacks + */ + def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = onComplete { _ foreach f } + + /** Creates a new future by applying the 's' function to the successful result of + * this future, or the 'f' function to the failed result. If there is any non-fatal + * exception thrown when 's' or 'f' is applied, that exception will be propagated + * to the resulting future. + * + * @tparam S the type of the returned `Future` + * @param s function that transforms a successful result of the receiver into a successful result of the returned future + * @param f function that transforms a failure of the receiver into a failure of the returned future + * @return a `Future` that will be completed with the transformed value + * @group Transformations + */ + def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S] = + transform { + t => + if (t.isInstanceOf[Success[T]]) t map s + else throw f(t.asInstanceOf[Failure[T]].exception) // will throw fatal errors! + } + + /** Creates a new Future by applying the specified function to the result + * of this Future. If there is any non-fatal exception thrown when 'f' + * is applied then that exception will be propagated to the resulting future. + * + * @tparam S the type of the returned `Future` + * @param f function that transforms the result of this future + * @return a `Future` that will be completed with the transformed value + * @group Transformations + */ + def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] + + /** Creates a new Future by applying the specified function, which produces a Future, to the result + * of this Future. If there is any non-fatal exception thrown when 'f' + * is applied then that exception will be propagated to the resulting future. + * + * @tparam S the type of the returned `Future` + * @param f function that transforms the result of this future + * @return a `Future` that will be completed with the transformed value + * @group Transformations + */ + def transformWith[S](f: Try[T] => Future[S])(implicit executor: ExecutionContext): Future[S] + + + /** Creates a new future by applying a function to the successful result of + * this future. If this future is completed with an exception then the new + * future will also contain this exception. + * + * Example: + * + * {{{ + * val f = Future { "The future" } + * val g = f map { x: String => x + " is now!" } + * }}} + * + * Note that a for comprehension involving a `Future` + * may expand to include a call to `map` and or `flatMap` + * and `withFilter`. See [[scala.concurrent.Future#flatMap]] for an example of such a comprehension. + * + * + * @tparam S the type of the returned `Future` + * @param f the function which will be applied to the successful result of this `Future` + * @return a `Future` which will be completed with the result of the application of the function + * @group Transformations + */ + def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = transform(_ map f) + + /** Creates a new future by applying a function to the successful result of + * this future, and returns the result of the function as the new future. + * If this future is completed with an exception then the new future will + * also contain this exception. + * + * $forComprehensionExamples + * + * @tparam S the type of the returned `Future` + * @param f the function which will be applied to the successful result of this `Future` + * @return a `Future` which will be completed with the result of the application of the function + * @group Transformations + */ + def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = transformWith { + t => + if(t.isInstanceOf[Success[T]]) f(t.asInstanceOf[Success[T]].value) + else this.asInstanceOf[Future[S]] // Safe cast + } + + /** Creates a new future with one level of nesting flattened, this method is equivalent + * to `flatMap(identity)`. + * + * @tparam S the type of the returned `Future` + * @group Transformations + */ + def flatten[S](implicit ev: T <:< Future[S]): Future[S] = flatMap(ev)(parasitic) + + /** Creates a new future by filtering the value of the current future with a predicate. + * + * If the current future contains a value which satisfies the predicate, the new future will also hold that value. + * Otherwise, the resulting future will fail with a `NoSuchElementException`. + * + * If the current future fails, then the resulting future also fails. + * + * Example: + * {{{ + * val f = Future { 5 } + * val g = f filter { _ % 2 == 1 } + * val h = f filter { _ % 2 == 0 } + * g foreach println // Eventually prints 5 + * Await.result(h, Duration.Zero) // throw a NoSuchElementException + * }}} + * + * @param p the predicate to apply to the successful result of this `Future` + * @return a `Future` which will hold the successful result of this `Future` if it matches the predicate or a `NoSuchElementException` + * @group Transformations + */ + def filter(p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = + transform { + t => + if (t.isInstanceOf[Success[T]]) { + if (p(t.asInstanceOf[Success[T]].value)) t + else Future.filterFailure + } else t + } + + /** Used by for-comprehensions. + * @group Transformations + */ + final def withFilter(p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = filter(p)(executor) + + /** Creates a new future by mapping the value of the current future, if the given partial function is defined at that value. + * + * If the current future contains a value for which the partial function is defined, the new future will also hold that value. + * Otherwise, the resulting future will fail with a `NoSuchElementException`. + * + * If the current future fails, then the resulting future also fails. + * + * Example: + * {{{ + * val f = Future { -5 } + * val g = f collect { + * case x if x < 0 => -x + * } + * val h = f collect { + * case x if x > 0 => x * 2 + * } + * g foreach println // Eventually prints 5 + * Await.result(h, Duration.Zero) // throw a NoSuchElementException + * }}} + * + * @tparam S the type of the returned `Future` + * @param pf the `PartialFunction` to apply to the successful result of this `Future` + * @return a `Future` holding the result of application of the `PartialFunction` or a `NoSuchElementException` + * @group Transformations + */ + def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] = + transform { + t => + if (t.isInstanceOf[Success[T]]) + Success(pf.applyOrElse(t.asInstanceOf[Success[T]].value, Future.collectFailed)) + else t.asInstanceOf[Failure[S]] + } + + /** Creates a new future that will handle any matching throwable that this + * future might contain. If there is no match, or if this future contains + * a valid result then the new future will contain the same. + * + * Example: + * + * {{{ + * Future (6 / 0) recover { case e: ArithmeticException => 0 } // result: 0 + * Future (6 / 0) recover { case e: NotFoundException => 0 } // result: exception + * Future (6 / 2) recover { case e: ArithmeticException => 0 } // result: 3 + * }}} + * + * @tparam U the type of the returned `Future` + * @param pf the `PartialFunction` to apply if this `Future` fails + * @return a `Future` with the successful value of this `Future` or the result of the `PartialFunction` + * @group Transformations + */ + def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = + transform { _ recover pf } + + /** Creates a new future that will handle any matching throwable that this + * future might contain by assigning it a value of another future. + * + * If there is no match, or if this future contains + * a valid result then the new future will contain the same result. + * + * Example: + * + * {{{ + * val f = Future { Int.MaxValue } + * Future (6 / 0) recoverWith { case e: ArithmeticException => f } // result: Int.MaxValue + * }}} + * + * @tparam U the type of the returned `Future` + * @param pf the `PartialFunction` to apply if this `Future` fails + * @return a `Future` with the successful value of this `Future` or the outcome of the `Future` returned by the `PartialFunction` + * @group Transformations + */ + def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = + transformWith { + t => + if (t.isInstanceOf[Failure[T]]) { + val result = pf.applyOrElse(t.asInstanceOf[Failure[T]].exception, Future.recoverWithFailed) + if (result ne Future.recoverWithFailedMarker) result + else this + } else this + } + + /** Zips the values of `this` and `that` future, and creates + * a new future holding the tuple of their results. + * + * If either input future fails, the resulting future is failed with the same + * throwable, without waiting for the other input future to complete. + * + * If the application of `f` throws a non-fatal throwable, the resulting future + * is failed with that throwable. + * + * @tparam U the type of the other `Future` + * @param that the other `Future` + * @return a `Future` with the results of both futures or the failure of the first of them that failed + * @group Transformations + */ + def zip[U](that: Future[U]): Future[(T, U)] = + zipWith(that)(Future.zipWithTuple2Fun)(parasitic) + + /** Zips the values of `this` and `that` future using a function `f`, + * and creates a new future holding the result. + * + * If either input future fails, the resulting future is failed with the same + * throwable, without waiting for the other input future to complete. + * + * If the application of `f` throws a non-fatal throwable, the resulting future + * is failed with that throwable. + * + * @tparam U the type of the other `Future` + * @tparam R the type of the resulting `Future` + * @param that the other `Future` + * @param f the function to apply to the results of `this` and `that` + * @return a `Future` with the result of the application of `f` to the results of `this` and `that` + * @group Transformations + */ + def zipWith[U, R](that: Future[U])(f: (T, U) => R)(implicit executor: ExecutionContext): Future[R] = { + // This is typically overriden by the implementation in DefaultPromise, which provides + // symmetric fail-fast behavior regardless of which future fails first. + // + // TODO: remove this implementation and make Future#zipWith abstract + // when we're next willing to make a binary incompatible change + flatMap(r1 => that.map(r2 => f(r1, r2)))(if (executor.isInstanceOf[BatchingExecutor]) executor else parasitic) + } + + /** Creates a new future which holds the result of this future if it was completed successfully, or, if not, + * the result of the `that` future if `that` is completed successfully. + * If both futures are failed, the resulting future holds the throwable object of the first future. + * + * Using this method will not cause concurrent programs to become nondeterministic. + * + * Example: + * {{{ + * val f = Future { throw new RuntimeException("failed") } + * val g = Future { 5 } + * val h = f fallbackTo g + * h foreach println // Eventually prints 5 + * }}} + * + * @tparam U the type of the other `Future` and the resulting `Future` + * @param that the `Future` whose result we want to use if this `Future` fails. + * @return a `Future` with the successful result of this or that `Future` or the failure of this `Future` if both fail + * @group Transformations + */ + def fallbackTo[U >: T](that: Future[U]): Future[U] = + if (this eq that) this + else { + implicit val ec = parasitic + transformWith { + t => + if (t.isInstanceOf[Success[T]]) this + else that transform { tt => if (tt.isInstanceOf[Success[U]]) tt else t } + } + } + + /** Creates a new `Future[S]` which is completed with this `Future`'s result if + * that conforms to `S`'s erased type or a `ClassCastException` otherwise. + * + * @tparam S the type of the returned `Future` + * @param tag the `ClassTag` which will be used to cast the result of this `Future` + * @return a `Future` holding the casted result of this `Future` or a `ClassCastException` otherwise + * @group Transformations + */ + def mapTo[S](implicit tag: ClassTag[S]): Future[S] = { + implicit val ec = parasitic + val boxedClass = { + val c = tag.runtimeClass + if (c.isPrimitive) Future.toBoxed(c) else c + } + require(boxedClass ne null) + map(s => boxedClass.cast(s).asInstanceOf[S]) + } + + /** Applies the side-effecting function to the result of this future, and returns + * a new future with the result of this future. + * + * This method allows one to enforce that the callbacks are executed in a + * specified order. + * + * Note that if one of the chained `andThen` callbacks throws + * an exception, that exception is not propagated to the subsequent `andThen` + * callbacks. Instead, the subsequent `andThen` callbacks are given the original + * value of this future. + * + * The following example prints out `5`: + * + * {{{ + * val f = Future { 5 } + * f andThen { + * case r => throw new RuntimeException("runtime exception") + * } andThen { + * case Failure(t) => println(t) + * case Success(v) => println(v) + * } + * }}} + * + * $swallowsExceptions + * + * @tparam U only used to accept any return type of the given `PartialFunction` + * @param pf a `PartialFunction` which will be conditionally applied to the outcome of this `Future` + * @return a `Future` which will be completed with the exact same outcome as this `Future` but after the `PartialFunction` has been executed. + * @group Callbacks + */ + def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] = + transform { + result => + try pf.applyOrElse[Try[T], Any](result, Future.id[Try[T]]) + catch { case t if NonFatal(t) => executor.reportFailure(t) } + // TODO: use `finally`? + result + } +} + + + +/** Future companion object. + * + * @define nonDeterministic + * Note: using this method yields nondeterministic dataflow programs. + */ +object Future { + + /** + * Utilities, hoisted functions, etc. + */ + + private[concurrent] final val toBoxed = Map[Class[_], Class[_]]( + classOf[Boolean] -> classOf[java.lang.Boolean], + classOf[Byte] -> classOf[java.lang.Byte], + classOf[Char] -> classOf[java.lang.Character], + classOf[Short] -> classOf[java.lang.Short], + classOf[Int] -> classOf[java.lang.Integer], + classOf[Long] -> classOf[java.lang.Long], + classOf[Float] -> classOf[java.lang.Float], + classOf[Double] -> classOf[java.lang.Double], + classOf[Unit] -> classOf[scala.runtime.BoxedUnit] + ) + + private[this] final val _cachedId: AnyRef => AnyRef = Predef.identity _ + + private[concurrent] final def id[T]: T => T = _cachedId.asInstanceOf[T => T] + + private[concurrent] final val collectFailed = + (t: Any) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t) with NoStackTrace + + private[concurrent] final val filterFailure = + Failure[Nothing](new NoSuchElementException("Future.filter predicate is not satisfied") with NoStackTrace) + + private[this] final val failedFailure = + Failure[Nothing](new NoSuchElementException("Future.failed not completed with a throwable.") with NoStackTrace) + + private[concurrent] final val failedFailureFuture: Future[Nothing] = + scala.concurrent.Future.fromTry(failedFailure) + + private[this] final val _failedFun: Try[Any] => Try[Throwable] = + v => if (v.isInstanceOf[Failure[Any]]) Success(v.asInstanceOf[Failure[Any]].exception) else failedFailure + + private[concurrent] final def failedFun[T]: Try[T] => Try[Throwable] = _failedFun.asInstanceOf[Try[T] => Try[Throwable]] + + private[concurrent] final val recoverWithFailedMarker: Future[Nothing] = + scala.concurrent.Future.failed(new Throwable with NoStackTrace) + + private[concurrent] final val recoverWithFailed = (t: Throwable) => recoverWithFailedMarker + + private[this] final val _zipWithTuple2: (Any, Any) => (Any, Any) = Tuple2.apply _ + private[concurrent] final def zipWithTuple2Fun[T,U] = _zipWithTuple2.asInstanceOf[(T,U) => (T,U)] + + private[this] final val _addToBuilderFun: (Builder[Any, Nothing], Any) => Builder[Any, Nothing] = (b: Builder[Any, Nothing], e: Any) => b += e + private[concurrent] final def addToBuilderFun[A, M] = _addToBuilderFun.asInstanceOf[Function2[Builder[A, M], A, Builder[A, M]]] + + /** A Future which is never completed. + */ + object never extends Future[Nothing] { + + @throws[TimeoutException] + @throws[InterruptedException] + override final def ready(atMost: Duration)(implicit permit: CanAwait): this.type = { + import Duration.{Undefined, Inf, MinusInf} + atMost match { + case u if u eq Undefined => throw new IllegalArgumentException("cannot wait for Undefined period") + case `Inf` => + while(!Thread.interrupted()) { + LockSupport.park(this) + } + throw new InterruptedException + case `MinusInf` => // Drop out + case f: FiniteDuration if f > Duration.Zero => + var now = System.nanoTime() + val deadline = now + f.toNanos + while((deadline - now) > 0) { + LockSupport.parkNanos(this, deadline - now) + if (Thread.interrupted()) + throw new InterruptedException + now = System.nanoTime() + } + // Done waiting, drop out + case _: FiniteDuration => // Drop out if 0 or less + case x: Duration.Infinite => throw new MatchError(x) + } + throw new TimeoutException(s"Future timed out after [$atMost]") + } + + @throws[TimeoutException] + @throws[InterruptedException] + override final def result(atMost: Duration)(implicit permit: CanAwait): Nothing = { + ready(atMost) + throw new TimeoutException(s"Future timed out after [$atMost]") + } + + override final def onComplete[U](f: Try[Nothing] => U)(implicit executor: ExecutionContext): Unit = () + override final def isCompleted: Boolean = false + override final def value: Option[Try[Nothing]] = None + override final def failed: Future[Throwable] = this + override final def foreach[U](f: Nothing => U)(implicit executor: ExecutionContext): Unit = () + override final def transform[S](s: Nothing => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S] = this + override final def transform[S](f: Try[Nothing] => Try[S])(implicit executor: ExecutionContext): Future[S] = this + override final def transformWith[S](f: Try[Nothing] => Future[S])(implicit executor: ExecutionContext): Future[S] = this + override final def map[S](f: Nothing => S)(implicit executor: ExecutionContext): Future[S] = this + override final def flatMap[S](f: Nothing => Future[S])(implicit executor: ExecutionContext): Future[S] = this + override final def flatten[S](implicit ev: Nothing <:< Future[S]): Future[S] = this + override final def filter(p: Nothing => Boolean)(implicit executor: ExecutionContext): Future[Nothing] = this + override final def collect[S](pf: PartialFunction[Nothing, S])(implicit executor: ExecutionContext): Future[S] = this + override final def recover[U >: Nothing](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = this + override final def recoverWith[U >: Nothing](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = this + override final def zip[U](that: Future[U]): Future[(Nothing, U)] = this + override final def zipWith[U, R](that: Future[U])(f: (Nothing, U) => R)(implicit executor: ExecutionContext): Future[R] = this + override final def fallbackTo[U >: Nothing](that: Future[U]): Future[U] = this + override final def mapTo[S](implicit tag: ClassTag[S]): Future[S] = this + override final def andThen[U](pf: PartialFunction[Try[Nothing], U])(implicit executor: ExecutionContext): Future[Nothing] = this + override final def toString: String = "Future()" + } + + /** A Future which is completed with the Unit value. + */ + final val unit: Future[Unit] = fromTry(Success(())) + + /** Creates an already completed Future with the specified exception. + * + * @tparam T the type of the value in the future + * @param exception the non-null instance of `Throwable` + * @return the newly created `Future` instance + */ + final def failed[T](exception: Throwable): Future[T] = Promise.failed(exception).future + + /** Creates an already completed Future with the specified result. + * + * @tparam T the type of the value in the future + * @param result the given successful value + * @return the newly created `Future` instance + */ + final def successful[T](result: T): Future[T] = Promise.successful(result).future + + /** Creates an already completed Future with the specified result or exception. + * + * @tparam T the type of the value in the `Future` + * @param result the result of the returned `Future` instance + * @return the newly created `Future` instance + */ + final def fromTry[T](result: Try[T]): Future[T] = Promise.fromTry(result).future + + /** Starts an asynchronous computation and returns a `Future` instance with the result of that computation. + * + * The following expressions are equivalent: + * + * {{{ + * val f1 = Future(expr) + * val f2 = Future.unit.map(_ => expr) + * val f3 = Future.unit.transform(_ => Success(expr)) + * }}} + * + * The result becomes available once the asynchronous computation is completed. + * + * @tparam T the type of the result + * @param body the asynchronous computation + * @param executor the execution context on which the future is run + * @return the `Future` holding the result of the computation + */ + final def apply[T](body: => T)(implicit executor: ExecutionContext): Future[T] = + unit.map(_ => body) + + /** Starts an asynchronous computation and returns a `Future` instance with the result of that computation once it completes. + * + * The following expressions are semantically equivalent: + * + * {{{ + * val f1 = Future(expr).flatten + * val f2 = Future.delegate(expr) + * val f3 = Future.unit.flatMap(_ => expr) + * }}} + * + * The result becomes available once the resulting Future of the asynchronous computation is completed. + * + * @tparam T the type of the result + * @param body the asynchronous computation, returning a Future + * @param executor the execution context on which the `body` is evaluated in + * @return the `Future` holding the result of the computation + */ + final def delegate[T](body: => Future[T])(implicit executor: ExecutionContext): Future[T] = + unit.flatMap(_ => body) + + /** Simple version of `Future.traverse`. Asynchronously and non-blockingly transforms, in essence, a `IterableOnce[Future[A]]` + * into a `Future[IterableOnce[A]]`. Useful for reducing many `Future`s into a single `Future`. + * + * @tparam A the type of the value inside the Futures + * @tparam CC the type of the `IterableOnce` of Futures + * @tparam To the type of the resulting collection + * @param in the `IterableOnce` of Futures which will be sequenced + * @return the `Future` of the resulting collection + */ + final def sequence[A, CC[X] <: IterableOnce[X], To](in: CC[Future[A]])(implicit bf: BuildFrom[CC[Future[A]], A, To], executor: ExecutionContext): Future[To] = + in.iterator.foldLeft(successful(bf.newBuilder(in))) { + (fr, fa) => fr.zipWith(fa)(Future.addToBuilderFun) + }.map(_.result())(if (executor.isInstanceOf[BatchingExecutor]) executor else parasitic) + + /** Asynchronously and non-blockingly returns a new `Future` to the result of the first future + * in the list that is completed. This means no matter if it is completed as a success or as a failure. + * + * @tparam T the type of the value in the future + * @param futures the `IterableOnce` of Futures in which to find the first completed + * @return the `Future` holding the result of the future that is first to be completed + */ + final def firstCompletedOf[T](futures: IterableOnce[Future[T]])(implicit executor: ExecutionContext): Future[T] = { + val i = futures.iterator + if (!i.hasNext) Future.never + else { + val p = Promise[T]() + val firstCompleteHandler = new AtomicReference[Promise[T]](p) with (Try[T] => Unit) { + override final def apply(v1: Try[T]): Unit = { + val r = getAndSet(null) + if (r ne null) + r tryComplete v1 // tryComplete is likely to be cheaper than complete + } + } + while(i.hasNext && firstCompleteHandler.get != null) // exit early if possible + i.next().onComplete(firstCompleteHandler) + p.future + } + } + + /** Asynchronously and non-blockingly returns a `Future` that will hold the optional result + * of the first `Future` with a result that matches the predicate, failed `Future`s will be ignored. + * + * @tparam T the type of the value in the future + * @param futures the `scala.collection.immutable.Iterable` of Futures to search + * @param p the predicate which indicates if it's a match + * @return the `Future` holding the optional result of the search + */ + final def find[T](futures: scala.collection.immutable.Iterable[Future[T]])(p: T => Boolean)(implicit executor: ExecutionContext): Future[Option[T]] = { + def searchNext(i: Iterator[Future[T]]): Future[Option[T]] = + if (!i.hasNext) successful(None) + else i.next().transformWith { + case Success(r) if p(r) => successful(Some(r)) + case _ => searchNext(i) + } + + searchNext(futures.iterator) + } + + /** A non-blocking, asynchronous left fold over the specified futures, + * with the start value of the given zero. + * The fold is performed asynchronously in left-to-right order as the futures become completed. + * The result will be the first failure of any of the futures, or any failure in the actual fold, + * or the result of the fold. + * + * Example: + * {{{ + * val futureSum = Future.foldLeft(futures)(0)(_ + _) + * }}} + * + * @tparam T the type of the value of the input Futures + * @tparam R the type of the value of the returned `Future` + * @param futures the `scala.collection.immutable.Iterable` of Futures to be folded + * @param zero the start value of the fold + * @param op the fold operation to be applied to the zero and futures + * @return the `Future` holding the result of the fold + */ + final def foldLeft[T, R](futures: scala.collection.immutable.Iterable[Future[T]])(zero: R)(op: (R, T) => R)(implicit executor: ExecutionContext): Future[R] = + foldNext(futures.iterator, zero, op) + + private[this] final def foldNext[T, R](i: Iterator[Future[T]], prevValue: R, op: (R, T) => R)(implicit executor: ExecutionContext): Future[R] = + if (!i.hasNext) successful(prevValue) + else i.next().flatMap { value => foldNext(i, op(prevValue, value), op) } + + /** A non-blocking, asynchronous fold over the specified futures, with the start value of the given zero. + * The fold is performed on the thread where the last future is completed, + * the result will be the first failure of any of the futures, or any failure in the actual fold, + * or the result of the fold. + * + * Example: + * {{{ + * val futureSum = Future.fold(futures)(0)(_ + _) + * }}} + * + * @tparam T the type of the value of the input Futures + * @tparam R the type of the value of the returned `Future` + * @param futures the `IterableOnce` of Futures to be folded + * @param zero the start value of the fold + * @param op the fold operation to be applied to the zero and futures + * @return the `Future` holding the result of the fold + */ + @deprecated("use Future.foldLeft instead", "2.12.0") + // not removed in 2.13, to facilitate 2.11/2.12/2.13 cross-building; remove further down the line (see scala/scala#6319) + def fold[T, R](futures: IterableOnce[Future[T]])(zero: R)(@deprecatedName("foldFun") op: (R, T) => R)(implicit executor: ExecutionContext): Future[R] = + if (futures.isEmpty) successful(zero) + else sequence(futures)(ArrayBuffer, executor).map(_.foldLeft(zero)(op)) + + /** Initiates a non-blocking, asynchronous, fold over the supplied futures + * where the fold-zero is the result value of the first `Future` in the collection. + * + * Example: + * {{{ + * val futureSum = Future.reduce(futures)(_ + _) + * }}} + * @tparam T the type of the value of the input Futures + * @tparam R the type of the value of the returned `Future` + * @param futures the `IterableOnce` of Futures to be reduced + * @param op the reduce operation which is applied to the results of the futures + * @return the `Future` holding the result of the reduce + */ + @deprecated("use Future.reduceLeft instead", "2.12.0") + // not removed in 2.13, to facilitate 2.11/2.12/2.13 cross-building; remove further down the line (see scala/scala#6319) + final def reduce[T, R >: T](futures: IterableOnce[Future[T]])(op: (R, T) => R)(implicit executor: ExecutionContext): Future[R] = + if (futures.isEmpty) failed(new NoSuchElementException("reduce attempted on empty collection")) + else sequence(futures)(ArrayBuffer, executor).map(_ reduceLeft op) + + /** Initiates a non-blocking, asynchronous, left reduction over the supplied futures + * where the zero is the result value of the first `Future`. + * + * Example: + * {{{ + * val futureSum = Future.reduceLeft(futures)(_ + _) + * }}} + * @tparam T the type of the value of the input Futures + * @tparam R the type of the value of the returned `Future` + * @param futures the `scala.collection.immutable.Iterable` of Futures to be reduced + * @param op the reduce operation which is applied to the results of the futures + * @return the `Future` holding the result of the reduce + */ + final def reduceLeft[T, R >: T](futures: scala.collection.immutable.Iterable[Future[T]])(op: (R, T) => R)(implicit executor: ExecutionContext): Future[R] = { + val i = futures.iterator + if (!i.hasNext) failed(new NoSuchElementException("reduceLeft attempted on empty collection")) + else i.next() flatMap { v => foldNext(i, v, op) } + } + + /** Asynchronously and non-blockingly transforms a `IterableOnce[A]` into a `Future[IterableOnce[B]]` + * using the provided function `A => Future[B]`. + * This is useful for performing a parallel map. For example, to apply a function to all items of a list + * in parallel: + * + * {{{ + * val myFutureList = Future.traverse(myList)(x => Future(myFunc(x))) + * }}} + * @tparam A the type of the value inside the Futures in the collection + * @tparam B the type of the value of the returned `Future` + * @tparam M the type of the collection of Futures + * @param in the collection to be mapped over with the provided function to produce a collection of Futures that is then sequenced into a Future collection + * @param fn the function to be mapped over the collection to produce a collection of Futures + * @return the `Future` of the collection of results + */ + final def traverse[A, B, M[X] <: IterableOnce[X]](in: M[A])(fn: A => Future[B])(implicit bf: BuildFrom[M[A], B, M[B]], executor: ExecutionContext): Future[M[B]] = + in.iterator.foldLeft(successful(bf.newBuilder(in))) { + (fr, a) => fr.zipWith(fn(a))(Future.addToBuilderFun) + }.map(_.result())(if (executor.isInstanceOf[BatchingExecutor]) executor else parasitic) +} + +@deprecated("Superseded by `scala.concurrent.Batchable`", "2.13.0") +trait OnCompleteRunnable extends Batchable { + self: Runnable => +} + diff --git a/library/src/scala/concurrent/JavaConversions.scala b/library/src/scala/concurrent/JavaConversions.scala new file mode 100644 index 000000000000..3250e656941a --- /dev/null +++ b/library/src/scala/concurrent/JavaConversions.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +import java.util.concurrent.{ExecutorService, Executor} +import scala.language.implicitConversions + +/** The `JavaConversions` object provides implicit conversions supporting + * interoperability between Scala and Java concurrency classes. + */ +@deprecated("Use the factory methods in `ExecutionContext` instead", "2.13.0") +object JavaConversions { + + /** + * Creates a new `ExecutionContext` which uses the provided `ExecutorService`. + */ + @deprecated("Use `ExecutionContext.fromExecutorService` instead", "2.13.0") + implicit def asExecutionContext(exec: ExecutorService): ExecutionContextExecutorService = + ExecutionContext.fromExecutorService(exec) + + /** + * Creates a new `ExecutionContext` which uses the provided `Executor`. + */ + @deprecated("Use `ExecutionContext.fromExecutor` instead", "2.13.0") + implicit def asExecutionContext(exec: Executor): ExecutionContextExecutor = + ExecutionContext.fromExecutor(exec) + +} diff --git a/library/src/scala/concurrent/Promise.scala b/library/src/scala/concurrent/Promise.scala new file mode 100644 index 000000000000..cf3f23543c5a --- /dev/null +++ b/library/src/scala/concurrent/Promise.scala @@ -0,0 +1,148 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +import scala.util.{ Try, Success, Failure } + +/** Promise is an object which can be completed with a value or failed + * with an exception. + * + * A promise should always eventually be completed, whether for success or failure, + * in order to avoid unintended resource retention for any associated Futures' + * callbacks or transformations. + * + * @define promiseCompletion + * If the promise has already been fulfilled, failed or has timed out, + * calling this method will throw an IllegalStateException. + * + * @define allowedThrowables + * If the throwable used to fail this promise is an error, a control exception + * or an interrupted exception, it will be wrapped as a cause within an + * `ExecutionException` which will fail the promise. + * + * @define nonDeterministic + * Note: Using this method may result in non-deterministic concurrent programs. + */ +trait Promise[T] { + /** Future containing the value of this promise. + */ + def future: Future[T] + + /** Returns whether the promise has already been completed with + * a value or an exception. + * + * $nonDeterministic + * + * @return `true` if the promise is already completed, `false` otherwise + */ + def isCompleted: Boolean + + /** Completes the promise with either an exception or a value. + * + * @param result Either the value or the exception to complete the promise with. + * + * $promiseCompletion + */ + def complete(result: Try[T]): this.type = + if (tryComplete(result)) this else throw new IllegalStateException("Promise already completed.") + + /** Tries to complete the promise with either a value or the exception. + * + * $nonDeterministic + * + * @return If the promise has already been completed returns `false`, or `true` otherwise. + */ + def tryComplete(result: Try[T]): Boolean + + /** Completes this promise with the specified future, once that future is completed. + * + * @return This promise + */ + def completeWith(other: Future[T]): this.type = { + if (other ne this.future) // this tryCompleteWith this doesn't make much sense + other.onComplete(this tryComplete _)(ExecutionContext.parasitic) + + this + } + + /** Attempts to complete this promise with the specified future, once that future is completed. + * + * @return This promise + */ + @deprecated("Since this method is semantically equivalent to `completeWith`, use that instead.", "2.13.0") + final def tryCompleteWith(other: Future[T]): this.type = completeWith(other) + + /** Completes the promise with a value. + * + * @param value The value to complete the promise with. + * + * $promiseCompletion + */ + def success(value: T): this.type = complete(Success(value)) + + /** Tries to complete the promise with a value. + * + * $nonDeterministic + * + * @return If the promise has already been completed returns `false`, or `true` otherwise. + */ + def trySuccess(value: T): Boolean = tryComplete(Success(value)) + + /** Completes the promise with an exception. + * + * @param cause The throwable to complete the promise with. + * + * $allowedThrowables + * + * $promiseCompletion + */ + def failure(cause: Throwable): this.type = complete(Failure(cause)) + + /** Tries to complete the promise with an exception. + * + * $nonDeterministic + * + * @return If the promise has already been completed returns `false`, or `true` otherwise. + */ + def tryFailure(cause: Throwable): Boolean = tryComplete(Failure(cause)) +} + +object Promise { + /** Creates a promise object which can be completed with a value. + * + * @tparam T the type of the value in the promise + * @return the newly created `Promise` instance + */ + final def apply[T](): Promise[T] = new impl.Promise.DefaultPromise[T]() + + /** Creates an already completed Promise with the specified exception. + * + * @tparam T the type of the value in the promise + * @return the newly created `Promise` instance + */ + final def failed[T](exception: Throwable): Promise[T] = fromTry(Failure(exception)) + + /** Creates an already completed Promise with the specified result. + * + * @tparam T the type of the value in the promise + * @return the newly created `Promise` instance + */ + final def successful[T](result: T): Promise[T] = fromTry(Success(result)) + + /** Creates an already completed Promise with the specified result or exception. + * + * @tparam T the type of the value in the promise + * @return the newly created `Promise` instance + */ + final def fromTry[T](result: Try[T]): Promise[T] = new impl.Promise.DefaultPromise[T](result) +} diff --git a/library/src/scala/concurrent/SyncChannel.scala b/library/src/scala/concurrent/SyncChannel.scala new file mode 100644 index 000000000000..8792524524c3 --- /dev/null +++ b/library/src/scala/concurrent/SyncChannel.scala @@ -0,0 +1,77 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +/** A `SyncChannel` allows one to exchange data synchronously between + * a reader and a writer thread. The writer thread is blocked until the + * data to be written has been read by a corresponding reader thread. + */ +@deprecated("Use `java.util.concurrent.Exchanger` instead.", since = "2.13.0") +class SyncChannel[A] { + + private final val Signal = () + private type Signal = Unit + private[this] var pendingWrites = List[(A, SyncVar[Signal])]() + private[this] var pendingReads = List[SyncVar[A]]() + + def write(data: A): Unit = { + // create write request + val writeReq = new SyncVar[Signal] + + this.synchronized { + // check whether there is a reader waiting + if (pendingReads.nonEmpty) { + val readReq = pendingReads.head + pendingReads = pendingReads.tail + + // let reader continue + readReq.put(data) + + // resolve write request + writeReq.put(Signal) + } + else { + // enqueue write request + pendingWrites = pendingWrites ::: List((data, writeReq)) + } + } + + writeReq.get + } + + def read: A = { + // create read request + val readReq = new SyncVar[A] + + this.synchronized { + // check whether there is a writer waiting + if (pendingWrites.nonEmpty) { + // read data + val (data, writeReq) = pendingWrites.head + pendingWrites = pendingWrites.tail + + // let writer continue + writeReq.put(Signal) + + // resolve read request + readReq.put(data) + } + else { + // enqueue read request + pendingReads = pendingReads ::: List(readReq) + } + } + + readReq.get + } +} diff --git a/library/src/scala/concurrent/SyncVar.scala b/library/src/scala/concurrent/SyncVar.scala new file mode 100644 index 000000000000..66c5fd1bb81d --- /dev/null +++ b/library/src/scala/concurrent/SyncVar.scala @@ -0,0 +1,122 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +import java.util.concurrent.TimeUnit + +/** A class to provide safe concurrent access to a mutable cell. + * All methods are synchronized. + * + * @tparam A type of the contained value + */ +@deprecated("Use `java.util.concurrent.LinkedBlockingQueue with capacity 1` instead.", since = "2.13.0") +class SyncVar[A] { + private[this] var isDefined: Boolean = false + private[this] var value: A = _ + + /** + * Wait for this SyncVar to become defined and then get + * the stored value without modifying it. + * + * @return value that is held in this container + */ + def get: A = synchronized { + while (!isDefined) wait() + value + } + + /** Waits `timeout` millis. If `timeout <= 0` just returns 0. + * It never returns negative results. + */ + private def waitMeasuringElapsed(timeout: Long): Long = if (timeout <= 0) 0 else { + val start = System.nanoTime() + wait(timeout) + val elapsed = System.nanoTime() - start + // nanoTime should be monotonic, but it's not possible to rely on that. + // See https://bugs.java.com/view_bug.do?bug_id=6458294 + if (elapsed < 0) 0 else TimeUnit.NANOSECONDS.toMillis(elapsed) + } + + /** Wait at least `timeout` milliseconds (possibly more) for this `SyncVar` + * to become defined and then get its value. + * + * @param timeout time in milliseconds to wait + * @return `None` if variable is undefined after `timeout`, `Some(value)` otherwise + */ + def get(timeout: Long): Option[A] = synchronized { + /* Defending against the system clock going backward + * by counting time elapsed directly. Loop required + * to deal with spurious wakeups. + */ + var rest = timeout + while (!isDefined && rest > 0) { + val elapsed = waitMeasuringElapsed(rest) + rest -= elapsed + } + if (isDefined) Some(value) else None + } + + /** + * Wait for this SyncVar to become defined and then get + * the stored value, unsetting it as a side effect. + * + * @return value that was held in this container + */ + def take(): A = synchronized { + try get + finally unsetVal() + } + + /** Wait at least `timeout` milliseconds (possibly more) for this `SyncVar` + * to become defined and then get the stored value, unsetting it + * as a side effect. + * + * @param timeout the amount of milliseconds to wait + * @return the value or a throws an exception if the timeout occurs + * @throws NoSuchElementException on timeout + */ + def take(timeout: Long): A = synchronized { + try get(timeout).get + finally unsetVal() + } + + /** Place a value in the SyncVar. If the SyncVar already has a stored value, + * wait until another thread takes it. */ + def put(x: A): Unit = synchronized { + while (isDefined) wait() + setVal(x) + } + + /** Check whether a value is stored in the synchronized variable. */ + def isSet: Boolean = synchronized { + isDefined + } + + // `setVal` exists so as to retroactively deprecate `set` without + // deprecation warnings where we use `set` internally. The + // implementation of `set` was moved to `setVal` to achieve this + private def setVal(x: A): Unit = synchronized { + isDefined = true + value = x + notifyAll() + } + + // `unsetVal` exists so as to retroactively deprecate `unset` without + // deprecation warnings where we use `unset` internally. The + // implementation of `unset` was moved to `unsetVal` to achieve this + private def unsetVal(): Unit = synchronized { + isDefined = false + value = null.asInstanceOf[A] + notifyAll() + } +} diff --git a/library/src/scala/concurrent/duration/Deadline.scala b/library/src/scala/concurrent/duration/Deadline.scala new file mode 100644 index 000000000000..353d0f30fff8 --- /dev/null +++ b/library/src/scala/concurrent/duration/Deadline.scala @@ -0,0 +1,85 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent.duration + +/** + * This class stores a deadline, as obtained via `Deadline.now` or the + * duration DSL: + * + * {{{ + * import scala.concurrent.duration._ + * 3.seconds.fromNow + * }}} + * + * Its main purpose is to manage repeated attempts to achieve something (like + * awaiting a condition) by offering the methods `hasTimeLeft` and `timeLeft`. All + * durations are measured according to `System.nanoTime`; this + * does not take into account changes to the system clock (such as leap + * seconds). + */ +case class Deadline private (time: FiniteDuration) extends Ordered[Deadline] { + /** + * Return a deadline advanced (i.e., moved into the future) by the given duration. + */ + def +(other: FiniteDuration): Deadline = copy(time = time + other) + /** + * Return a deadline moved backwards (i.e., towards the past) by the given duration. + */ + def -(other: FiniteDuration): Deadline = copy(time = time - other) + /** + * Calculate time difference between this and the other deadline, where the result is directed (i.e., may be negative). + */ + def -(other: Deadline): FiniteDuration = time - other.time + /** + * Calculate time difference between this duration and now; the result is negative if the deadline has passed. + * + * '''''Note that on some systems this operation is costly because it entails a system call.''''' + * Check `System.nanoTime` for your platform. + */ + def timeLeft: FiniteDuration = this - Deadline.now + /** + * Determine whether the deadline still lies in the future at the point where this method is called. + * + * '''''Note that on some systems this operation is costly because it entails a system call.''''' + * Check `System.nanoTime` for your platform. + */ + def hasTimeLeft(): Boolean = !isOverdue() + /** + * Determine whether the deadline lies in the past at the point where this method is called. + * + * '''''Note that on some systems this operation is costly because it entails a system call.''''' + * Check `System.nanoTime` for your platform. + */ + def isOverdue(): Boolean = (time.toNanos - System.nanoTime()) < 0 + /** + * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration. + */ + def compare(other: Deadline): Int = time compare other.time +} + +object Deadline { + /** + * Construct a deadline due exactly at the point where this method is called. Useful for then + * advancing it to obtain a future deadline, or for sampling the current time exactly once and + * then comparing it to multiple deadlines (using subtraction). + */ + def now: Deadline = Deadline(Duration(System.nanoTime, NANOSECONDS)) + + /** + * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration. + */ + implicit object DeadlineIsOrdered extends Ordering[Deadline] { + def compare(a: Deadline, b: Deadline): Int = a compare b + } + +} diff --git a/library/src/scala/concurrent/duration/Duration.scala b/library/src/scala/concurrent/duration/Duration.scala new file mode 100644 index 000000000000..1312bb12d1d5 --- /dev/null +++ b/library/src/scala/concurrent/duration/Duration.scala @@ -0,0 +1,742 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent.duration + +import java.lang.{ Double => JDouble } +import scala.collection.StringParsers + +object Duration { + + /** + * Construct a Duration from the given length and unit. Observe that nanosecond precision may be lost if + * + * - the unit is NANOSECONDS + * - and the length has an absolute value greater than `2^53` + * + * Infinite inputs (and NaN) are converted into [[Duration.Inf]], [[Duration.MinusInf]] and [[Duration.Undefined]], respectively. + * + * @throws IllegalArgumentException if the length was finite but the resulting duration cannot be expressed as a [[FiniteDuration]] + */ + def apply(length: Double, unit: TimeUnit): Duration = fromNanos(unit.toNanos(1) * length) + + /** + * Construct a finite duration from the given length and time unit. The unit given is retained + * throughout calculations as long as possible, so that it can be retrieved later. + */ + def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit) + + /** + * Construct a finite duration from the given length and time unit, where the latter is + * looked up in a list of string representation. Valid choices are: + * + * `d, day, h, hr, hour, m, min, minute, s, sec, second, ms, milli, millisecond, µs, micro, microsecond, ns, nano, nanosecond` + * and their pluralized forms (for every but the first mentioned form of each unit, i.e. no "ds", but "days"). + */ + def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit)) + + // Double stores 52 bits mantissa, but there is an implied '1' in front, making the limit 2^53 + // private[this] final val maxPreciseDouble = 9007199254740992d // not used after https://github.com/scala/scala/pull/9233 + + /** + * Parse String into Duration. Format is `""`, where + * whitespace is allowed before, between and after the parts. Infinities are + * designated by `"Inf"`, `"PlusInf"`, `"+Inf"`, `"Duration.Inf"` and `"-Inf"`, `"MinusInf"` or `"Duration.MinusInf"`. + * Undefined is designated by `"Duration.Undefined"`. + * + * @throws NumberFormatException if format is not parsable + */ + def apply(s: String): Duration = { + val s1: String = s filterNot (_.isWhitespace) + s1 match { + case "Inf" | "PlusInf" | "+Inf" | "Duration.Inf" => Inf + case "MinusInf" | "-Inf" | "Duration.MinusInf" => MinusInf + case "Duration.Undefined" => Undefined + case _ => + val unitName = s1.reverse.takeWhile(_.isLetter).reverse + timeUnit get unitName match { + case Some(unit) => + val valueStr = s1 dropRight unitName.length + StringParsers.parseLong(valueStr).map(Duration(_, unit)) + .getOrElse(Duration(JDouble.parseDouble(valueStr), unit)) + case _ => throw new NumberFormatException("format error " + s) + } + } + } + + // "ms milli millisecond" -> List("ms", "milli", "millis", "millisecond", "milliseconds") + private[this] def words(s: String) = (s.trim split "\\s+").toList + private[this] def expandLabels(labels: String): List[String] = { + val hd :: rest = words(labels): @unchecked + hd :: rest.flatMap(s => List(s, s + "s")) + } + private[this] val timeUnitLabels = List( + DAYS -> "d day", + HOURS -> "h hr hour", + MINUTES -> "m min minute", + SECONDS -> "s sec second", + MILLISECONDS -> "ms milli millisecond", + MICROSECONDS -> "µs micro microsecond", + NANOSECONDS -> "ns nano nanosecond" + ) + + // TimeUnit => standard label + protected[duration] val timeUnitName: Map[TimeUnit, String] = + timeUnitLabels.toMap.view.mapValues(s => words(s).last).toMap + + // Label => TimeUnit + protected[duration] val timeUnit: Map[String, TimeUnit] = + timeUnitLabels.flatMap{ case (unit, names) => expandLabels(names) map (_ -> unit) }.toMap + + /** + * Extract length and time unit out of a string, where the format must match the description for [[Duration$.apply(s:String)* apply(String)]]. + * The extractor will not match for malformed strings or non-finite durations. + */ + def unapply(s: String): Option[(Long, TimeUnit)] = + ( try Some(apply(s)) catch { case _: RuntimeException => None } ) flatMap unapply + + /** + * Extract length and time unit out of a duration, if it is finite. + */ + def unapply(d: Duration): Option[(Long, TimeUnit)] = + if (d.isFinite) Some((d.length, d.unit)) else None + + /** + * Construct a possibly infinite or undefined Duration from the given number of nanoseconds. + * + * - `Double.PositiveInfinity` is mapped to [[Duration.Inf]] + * - `Double.NegativeInfinity` is mapped to [[Duration.MinusInf]] + * - `Double.NaN` is mapped to [[Duration.Undefined]] + * - `-0d` is mapped to [[Duration.Zero]] (exactly like `0d`) + * + * The semantics of the resulting Duration objects matches the semantics of their Double + * counterparts with respect to arithmetic operations. + * + * @throws IllegalArgumentException if the length was finite but the resulting duration cannot be expressed as a [[FiniteDuration]] + */ + def fromNanos(nanos: Double): Duration = { + if (nanos.isInfinite) + if (nanos > 0) Inf else MinusInf + else if (JDouble.isNaN(nanos)) + Undefined + else if (nanos > Long.MaxValue || nanos < Long.MinValue) + throw new IllegalArgumentException("trying to construct too large duration with " + nanos + "ns") + else + fromNanos(nanos.round) + } + + private[this] final val ns_per_µs = 1000L + private[this] final val ns_per_ms = ns_per_µs * 1000 + private[this] final val ns_per_s = ns_per_ms * 1000 + private[this] final val ns_per_min = ns_per_s * 60 + private[this] final val ns_per_h = ns_per_min * 60 + private[this] final val ns_per_d = ns_per_h * 24 + + /** + * Construct a finite duration from the given number of nanoseconds. The + * result will have the coarsest possible time unit which can exactly express + * this duration. + * + * @throws IllegalArgumentException for `Long.MinValue` since that would lead to inconsistent behavior afterwards (cannot be negated) + */ + def fromNanos(nanos: Long): FiniteDuration = { + if (nanos % ns_per_d == 0) Duration(nanos / ns_per_d , DAYS) + else if (nanos % ns_per_h == 0) Duration(nanos / ns_per_h , HOURS) + else if (nanos % ns_per_min == 0) Duration(nanos / ns_per_min, MINUTES) + else if (nanos % ns_per_s == 0) Duration(nanos / ns_per_s , SECONDS) + else if (nanos % ns_per_ms == 0) Duration(nanos / ns_per_ms , MILLISECONDS) + else if (nanos % ns_per_µs == 0) Duration(nanos / ns_per_µs , MICROSECONDS) + else Duration(nanos, NANOSECONDS) + } + + /** + * Preconstructed value of `0.days`. + */ + // unit as coarse as possible to keep (_ + Zero) sane unit-wise + val Zero: FiniteDuration = new FiniteDuration(0, DAYS) + + /** + * The Undefined value corresponds closely to Double.NaN: + * + * - it is the result of otherwise invalid operations + * - it does not equal itself (according to `equals()`) + * - it compares greater than any other Duration apart from itself (for which `compare` returns 0) + * + * The particular comparison semantics mirror those of Double.NaN. + * + * '''''Use [[eq]] when checking an input of a method against this value.''''' + */ + val Undefined: Infinite = new Infinite { + override def toString = "Duration.Undefined" + override def equals(other: Any): Boolean = false + override def +(other: Duration): Duration = this + override def -(other: Duration): Duration = this + override def *(factor: Double): Duration = this + override def /(factor: Double): Duration = this + override def /(other: Duration): Double = Double.NaN + def compare(other: Duration): Int = if (other eq this) 0 else 1 + def unary_- : Duration = this + def toUnit(unit: TimeUnit): Double = Double.NaN + private def readResolve(): AnyRef = Undefined // Instructs deserialization to use this same instance + } + + sealed abstract class Infinite extends Duration { + def +(other: Duration): Duration = other match { + case x if x eq Undefined => Undefined + case x: Infinite if x ne this => Undefined + case _ => this + } + def -(other: Duration): Duration = other match { + case x if x eq Undefined => Undefined + case x: Infinite if x eq this => Undefined + case _ => this + } + + def *(factor: Double): Duration = + if (factor == 0d || JDouble.isNaN(factor)) Undefined + else if (factor < 0d) -this + else this + def /(divisor: Double): Duration = + if (JDouble.isNaN(divisor) || divisor.isInfinite) Undefined + else if ((divisor compare 0d) < 0) -this + else this + def /(divisor: Duration): Double = divisor match { + case _: Infinite => Double.NaN + case x => Double.PositiveInfinity * (if ((this > Zero) ^ (divisor >= Zero)) -1 else 1) + } + + final def isFinite = false + + private[this] def fail(what: String) = throw new IllegalArgumentException(s"$what not allowed on infinite Durations") + final def length: Long = fail("length") + final def unit: TimeUnit = fail("unit") + final def toNanos: Long = fail("toNanos") + final def toMicros: Long = fail("toMicros") + final def toMillis: Long = fail("toMillis") + final def toSeconds: Long = fail("toSeconds") + final def toMinutes: Long = fail("toMinutes") + final def toHours: Long = fail("toHours") + final def toDays: Long = fail("toDays") + + final def toCoarsest: Duration = this + } + + /** + * Infinite duration: greater than any other (apart from Undefined) and not equal to any other + * but itself. This value closely corresponds to Double.PositiveInfinity, + * matching its semantics in arithmetic operations. + */ + val Inf: Infinite = new Infinite { + override def toString: String = "Duration.Inf" + def compare(other: Duration): Int = other match { + case x if x eq Undefined => -1 // Undefined != Undefined + case x if x eq this => 0 // `case Inf` will include null checks in the byte code + case _ => 1 + } + def unary_- : Duration = MinusInf + def toUnit(unit: TimeUnit): Double = Double.PositiveInfinity + private def readResolve(): AnyRef = Inf // Instructs deserialization to use this same instance + } + + /** + * Infinite duration: less than any other and not equal to any other + * but itself. This value closely corresponds to Double.NegativeInfinity, + * matching its semantics in arithmetic operations. + */ + val MinusInf: Infinite = new Infinite { + override def toString: String = "Duration.MinusInf" + def compare(other: Duration): Int = if (other eq this) 0 else -1 + def unary_- : Duration = Inf + def toUnit(unit: TimeUnit): Double = Double.NegativeInfinity + private def readResolve(): AnyRef = MinusInf // Instructs deserialization to use this same instance + } + + // Java Factories + + /** + * Construct a finite duration from the given length and time unit. The unit given is retained + * throughout calculations as long as possible, so that it can be retrieved later. + */ + def create(length: Long, unit: TimeUnit): FiniteDuration = apply(length, unit) + /** + * Construct a Duration from the given length and unit. Observe that nanosecond precision may be lost if + * + * - the unit is NANOSECONDS + * - and the length has an absolute value greater than `2^53` + * + * Infinite inputs (and NaN) are converted into [[Duration.Inf]], [[Duration.MinusInf]] and [[Duration.Undefined]], respectively. + * + * @throws IllegalArgumentException if the length was finite but the resulting duration cannot be expressed as a [[FiniteDuration]] + */ + def create(length: Double, unit: TimeUnit): Duration = apply(length, unit) + /** + * Construct a finite duration from the given length and time unit, where the latter is + * looked up in a list of string representation. Valid choices are: + * + * `d, day, h, hour, min, minute, s, sec, second, ms, milli, millisecond, µs, micro, microsecond, ns, nano, nanosecond` + * and their pluralized forms (for every but the first mentioned form of each unit, i.e. no "ds", but "days"). + */ + def create(length: Long, unit: String): FiniteDuration = apply(length, unit) + /** + * Parse String into Duration. Format is `""`, where + * whitespace is allowed before, between and after the parts. Infinities are + * designated by `"Inf"`, `"PlusInf"`, `"+Inf"` and `"-Inf"` or `"MinusInf"`. + * + * @throws NumberFormatException if format is not parsable + */ + def create(s: String): Duration = apply(s) + + /** + * The natural ordering of durations matches the natural ordering for Double, including non-finite values. + */ + implicit object DurationIsOrdered extends Ordering[Duration] { + def compare(a: Duration, b: Duration): Int = a compare b + } +} + +/** + *

Utility for working with java.util.concurrent.TimeUnit durations.

+ * + * '''''This class is not meant as a general purpose representation of time, it is + * optimized for the needs of `scala.concurrent`.''''' + * + *

Basic Usage

+ * + *

+ * Examples: + * {{{ + * import scala.concurrent.duration._ + * + * val duration = Duration(100, MILLISECONDS) + * val duration = Duration(100, "millis") + * + * duration.toNanos + * duration < 1.second + * duration <= Duration.Inf + * }}} + * + * '''''Invoking inexpressible conversions (like calling `toSeconds` on an infinite duration) will throw an IllegalArgumentException.''''' + * + *

+ * Implicits are also provided for Int, Long and Double. Example usage: + * {{{ + * import scala.concurrent.duration._ + * + * val duration = 100.millis + * }}} + * + * '''''The DSL provided by the implicit conversions always allows construction of finite durations, even for infinite Double inputs; use Duration.Inf instead.''''' + * + * Extractors, parsing and arithmetic are also included: + * {{{ + * val d = Duration("1.2 µs") + * val Duration(length, unit) = 5 millis + * val d2 = d * 2.5 + * val d3 = d2 + 1.millisecond + * }}} + * + *

Handling of Time Units

+ * + * Calculations performed on finite durations always retain the more precise unit of either operand, no matter + * whether a coarser unit would be able to exactly express the same duration. This means that Duration can be + * used as a lossless container for a (length, unit) pair if it is constructed using the corresponding methods + * and no arithmetic is performed on it; adding/subtracting durations should in that case be done with care. + * + *

Correspondence to Double Semantics

+ * + * The semantics of arithmetic operations on Duration are two-fold: + * + * - exact addition/subtraction with nanosecond resolution for finite durations, independent of the summands' magnitude + * - isomorphic to `java.lang.Double` when it comes to infinite or undefined values + * + * The conversion between Duration and Double is done using [[Duration.toUnit]] (with unit NANOSECONDS) + * and [[Duration$.fromNanos(nanos:Double)* Duration.fromNanos(Double)]] + * + *

Ordering

+ * + * The default ordering is consistent with the ordering of Double numbers, which means that Undefined is + * considered greater than all other durations, including [[Duration.Inf]]. + * + * @define exc @throws IllegalArgumentException when invoked on a non-finite duration + * + * @define ovf @throws IllegalArgumentException in case of a finite overflow: the range of a finite duration is `+-(2^63-1)`ns, and no conversion to infinite durations takes place. + */ +sealed abstract class Duration extends Serializable with Ordered[Duration] { + /** + * Obtain the length of this Duration measured in the unit obtained by the `unit` method. + * + * $exc + */ + def length: Long + /** + * Obtain the time unit in which the length of this duration is measured. + * + * $exc + */ + def unit: TimeUnit + /** + * Return the length of this duration measured in whole nanoseconds, rounding towards zero. + * + * $exc + */ + def toNanos: Long + /** + * Return the length of this duration measured in whole microseconds, rounding towards zero. + * + * $exc + */ + def toMicros: Long + /** + * Return the length of this duration measured in whole milliseconds, rounding towards zero. + * + * $exc + */ + def toMillis: Long + /** + * Return the length of this duration measured in whole seconds, rounding towards zero. + * + * $exc + */ + def toSeconds: Long + /** + * Return the length of this duration measured in whole minutes, rounding towards zero. + * + * $exc + */ + def toMinutes: Long + /** + * Return the length of this duration measured in whole hours, rounding towards zero. + * + * $exc + */ + def toHours: Long + /** + * Return the length of this duration measured in whole days, rounding towards zero. + * + * $exc + */ + def toDays: Long + /** + * Return the number of nanoseconds as floating point number, scaled down to the given unit. + * The result may not precisely represent this duration due to the Double datatype's inherent + * limitations (mantissa size effectively 53 bits). Non-finite durations are represented as + * - [[Duration.Undefined]] is mapped to Double.NaN + * - [[Duration.Inf]] is mapped to Double.PositiveInfinity + * - [[Duration.MinusInf]] is mapped to Double.NegativeInfinity + */ + def toUnit(unit: TimeUnit): Double + + /** + * Return the sum of that duration and this. When involving non-finite summands the semantics match those + * of Double. + * + * $ovf + */ + def +(other: Duration): Duration + /** + * Return the difference of that duration and this. When involving non-finite summands the semantics match those + * of Double. + * + * $ovf + */ + def -(other: Duration): Duration + /** + * Return this duration multiplied by the scalar factor. When involving non-finite factors the semantics match those + * of Double. + * + * $ovf + */ + def *(factor: Double): Duration + /** + * Return this duration divided by the scalar factor. When involving non-finite factors the semantics match those + * of Double. + * + * $ovf + */ + def /(divisor: Double): Duration + /** + * Return the quotient of this and that duration as floating-point number. The semantics are + * determined by Double as if calculating the quotient of the nanosecond lengths of both factors. + */ + def /(divisor: Duration): Double + /** + * Negate this duration. The only two values which are mapped to themselves are [[Duration.Zero]] and [[Duration.Undefined]]. + */ + def unary_- : Duration + /** + * This method returns whether this duration is finite, which is not the same as + * `!isInfinite` for Double because this method also returns `false` for [[Duration.Undefined]]. + */ + def isFinite: Boolean + /** + * Return the smaller of this and that duration as determined by the natural ordering. + */ + def min(other: Duration): Duration = if (this < other) this else other + /** + * Return the larger of this and that duration as determined by the natural ordering. + */ + def max(other: Duration): Duration = if (this > other) this else other + + // Java API + + /** + * Return this duration divided by the scalar factor. When involving non-finite factors the semantics match those + * of Double. + * + * $ovf + */ + def div(divisor: Double): Duration = this / divisor + /** + * Return the quotient of this and that duration as floating-point number. The semantics are + * determined by Double as if calculating the quotient of the nanosecond lengths of both factors. + */ + def div(other: Duration): Double = this / other + def gt(other: Duration): Boolean = this > other + def gteq(other: Duration): Boolean = this >= other + def lt(other: Duration): Boolean = this < other + def lteq(other: Duration): Boolean = this <= other + /** + * Return the difference of that duration and this. When involving non-finite summands the semantics match those + * of Double. + * + * $ovf + */ + def minus(other: Duration): Duration = this - other + /** + * Return this duration multiplied by the scalar factor. When involving non-finite factors the semantics match those + * of Double. + * + * $ovf + */ + def mul(factor: Double): Duration = this * factor + /** + * Negate this duration. The only two values which are mapped to themselves are [[Duration.Zero]] and [[Duration.Undefined]]. + */ + def neg(): Duration = -this + /** + * Return the sum of that duration and this. When involving non-finite summands the semantics match those + * of Double. + * + * $ovf + */ + def plus(other: Duration): Duration = this + other + /** + * Return duration which is equal to this duration but with a coarsest Unit, or self in case it is already the coarsest Unit + *

+ * Examples: + * {{{ + * Duration(60, MINUTES).toCoarsest // Duration(1, HOURS) + * Duration(1000, MILLISECONDS).toCoarsest // Duration(1, SECONDS) + * Duration(48, HOURS).toCoarsest // Duration(2, DAYS) + * Duration(5, SECONDS).toCoarsest // Duration(5, SECONDS) + * }}} + */ + def toCoarsest: Duration +} + +object FiniteDuration { + + implicit object FiniteDurationIsOrdered extends Ordering[FiniteDuration] { + def compare(a: FiniteDuration, b: FiniteDuration): Int = a compare b + } + + def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit) + def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit)) + + // limit on abs. value of durations in their units + private final val max_ns = Long.MaxValue + private final val max_µs = max_ns / 1000 + private final val max_ms = max_µs / 1000 + private final val max_s = max_ms / 1000 + private final val max_min= max_s / 60 + private final val max_h = max_min / 60 + private final val max_d = max_h / 24 +} + +/** + * This class represents a finite duration. Its addition and subtraction operators are overloaded to retain + * this guarantee statically. The range of this class is limited to `+-(2^63-1)`ns, which is roughly 292 years. + */ +final class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration { + import FiniteDuration._ + import Duration._ + + private[this] def bounded(max: Long) = -max <= length && length <= max + + require(unit match { + /* + * enforce the 2^63-1 ns limit, must be pos/neg symmetrical because of unary_- + */ + case NANOSECONDS => bounded(max_ns) + case MICROSECONDS => bounded(max_µs) + case MILLISECONDS => bounded(max_ms) + case SECONDS => bounded(max_s) + case MINUTES => bounded(max_min) + case HOURS => bounded(max_h) + case DAYS => bounded(max_d) + case _ => + val v = DAYS.convert(length, unit) + -max_d <= v && v <= max_d + }, "Duration is limited to +-(2^63-1)ns (ca. 292 years)") + + def toNanos: Long = unit.toNanos(length) + def toMicros: Long = unit.toMicros(length) + def toMillis: Long = unit.toMillis(length) + def toSeconds: Long = unit.toSeconds(length) + def toMinutes: Long = unit.toMinutes(length) + def toHours: Long = unit.toHours(length) + def toDays: Long = unit.toDays(length) + def toUnit(u: TimeUnit): Double = toNanos.toDouble / NANOSECONDS.convert(1, u) + + /** + * Construct a [[Deadline]] from this duration by adding it to the current instant `Deadline.now`. + */ + def fromNow: Deadline = Deadline.now + this + + private[this] def unitString = timeUnitName(unit) + ( if (length == 1) "" else "s" ) + override def toString: String = "" + length + " " + unitString + + def compare(other: Duration): Int = other match { + case x: FiniteDuration => toNanos compare x.toNanos + case _ => -(other compare this) + } + + // see https://www.securecoding.cert.org/confluence/display/java/NUM00-J.+Detect+or+prevent+integer+overflow + private[this] def safeAdd(a: Long, b: Long): Long = { + if ((b > 0) && (a > Long.MaxValue - b) || + (b < 0) && (a < Long.MinValue - b)) throw new IllegalArgumentException("integer overflow") + a + b + } + private[this] def add(otherLength: Long, otherUnit: TimeUnit): FiniteDuration = { + val commonUnit = if (otherUnit.convert(1, unit) == 0) unit else otherUnit + val totalLength = safeAdd(commonUnit.convert(length, unit), commonUnit.convert(otherLength, otherUnit)) + new FiniteDuration(totalLength, commonUnit) + } + + def +(other: Duration): Duration = other match { + case x: FiniteDuration => add(x.length, x.unit) + case _ => other + } + def -(other: Duration): Duration = other match { + case x: FiniteDuration => add(-x.length, x.unit) + case _ => -other + } + + def *(factor: Double): Duration = + if (!factor.isInfinite) fromNanos(toNanos * factor) + else if (JDouble.isNaN(factor)) Undefined + else if ((factor > 0) ^ (this < Zero)) Inf + else MinusInf + + def /(divisor: Double): Duration = + if (!divisor.isInfinite) fromNanos(toNanos / divisor) + else if (JDouble.isNaN(divisor)) Undefined + else Zero + + // if this is made a constant, then scalac will elide the conditional and always return +0.0, scala/bug#6331 + private[this] def minusZero = -0d + def /(divisor: Duration): Double = + if (divisor.isFinite) toNanos.toDouble / divisor.toNanos + else if (divisor eq Undefined) Double.NaN + else if ((length < 0) ^ (divisor > Zero)) 0d + else minusZero + + // overloaded methods taking FiniteDurations, so that you can calculate while statically staying finite + def +(other: FiniteDuration): FiniteDuration = add(other.length, other.unit) + def -(other: FiniteDuration): FiniteDuration = add(-other.length, other.unit) + def plus(other: FiniteDuration): FiniteDuration = this + other + def minus(other: FiniteDuration): FiniteDuration = this - other + def min(other: FiniteDuration): FiniteDuration = if (this < other) this else other + def max(other: FiniteDuration): FiniteDuration = if (this > other) this else other + + // overloaded methods taking Long so that you can calculate while statically staying finite + + /** + * Return the quotient of this duration and the given integer factor. + * + * @throws java.lang.ArithmeticException if the factor is 0 + */ + def /(divisor: Long): FiniteDuration = fromNanos(toNanos / divisor) + + /** + * Return the product of this duration and the given integer factor. + * + * @throws IllegalArgumentException if the result would overflow the range of FiniteDuration + */ + def *(factor: Long): FiniteDuration = new FiniteDuration(safeMul(length, factor), unit) + + /* + * This method avoids the use of Long division, which saves 95% of the time spent, + * by checking that there are enough leading zeros so that the result has a chance + * to fit into a Long again; the remaining edge cases are caught by using the sign + * of the product for overflow detection. + * + * This method is not general purpose because it disallows the (otherwise legal) + * case of Long.MinValue * 1, but that is okay for use in FiniteDuration, since + * Long.MinValue is not a legal `length` anyway. + */ + private def safeMul(_a: Long, _b: Long): Long = { + val a = scala.math.abs(_a) + val b = scala.math.abs(_b) + import java.lang.Long.{ numberOfLeadingZeros => leading } + if (leading(a) + leading(b) < 64) throw new IllegalArgumentException("multiplication overflow") + val product = a * b + if (product < 0) throw new IllegalArgumentException("multiplication overflow") + if (a == _a ^ b == _b) -product else product + } + + /** + * Return the quotient of this duration and the given integer factor. + * + * @throws java.lang.ArithmeticException if the factor is 0 + */ + def div(divisor: Long): FiniteDuration = this / divisor + + /** + * Return the product of this duration and the given integer factor. + * + * @throws IllegalArgumentException if the result would overflow the range of FiniteDuration + */ + def mul(factor: Long): FiniteDuration = this * factor + + def unary_- : FiniteDuration = Duration(-length, unit) + + final def isFinite = true + + final override def toCoarsest: FiniteDuration = { + def loop(length: Long, unit: TimeUnit): FiniteDuration = { + def coarserOrThis(coarser: TimeUnit, divider: Int): FiniteDuration = + if (length % divider == 0) loop(length / divider, coarser) + else if (unit == this.unit) this + else FiniteDuration(length, unit) + + unit match { + case DAYS => FiniteDuration(length, unit) + case HOURS => coarserOrThis(DAYS, 24) + case MINUTES => coarserOrThis(HOURS, 60) + case SECONDS => coarserOrThis(MINUTES, 60) + case MILLISECONDS => coarserOrThis(SECONDS, 1000) + case MICROSECONDS => coarserOrThis(MILLISECONDS, 1000) + case NANOSECONDS => coarserOrThis(MICROSECONDS, 1000) + } + } + + if (unit == DAYS || length == 0) this + else loop(length, unit) + } + + override def equals(other: Any): Boolean = other match { + case x: FiniteDuration => toNanos == x.toNanos + case _ => super.equals(other) + } + override def hashCode: Int = toNanos.toInt +} diff --git a/library/src/scala/concurrent/duration/DurationConversions.scala b/library/src/scala/concurrent/duration/DurationConversions.scala new file mode 100644 index 000000000000..30036331be73 --- /dev/null +++ b/library/src/scala/concurrent/duration/DurationConversions.scala @@ -0,0 +1,96 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent.duration + +import DurationConversions._ + +// Would be nice to limit the visibility of this trait a little bit, +// but it crashes scalac to do so. +trait DurationConversions extends Any { + protected def durationIn(unit: TimeUnit): FiniteDuration + + def nanoseconds: FiniteDuration = durationIn(NANOSECONDS) + def nanos: FiniteDuration = nanoseconds + def nanosecond: FiniteDuration = nanoseconds + def nano: FiniteDuration = nanoseconds + + def microseconds: FiniteDuration = durationIn(MICROSECONDS) + def micros: FiniteDuration = microseconds + def microsecond: FiniteDuration = microseconds + def micro: FiniteDuration = microseconds + + def milliseconds: FiniteDuration = durationIn(MILLISECONDS) + def millis: FiniteDuration = milliseconds + def millisecond: FiniteDuration = milliseconds + def milli: FiniteDuration = milliseconds + + def seconds: FiniteDuration = durationIn(SECONDS) + def second: FiniteDuration = seconds + + def minutes: FiniteDuration = durationIn(MINUTES) + def minute: FiniteDuration = minutes + + def hours: FiniteDuration = durationIn(HOURS) + def hour: FiniteDuration = hours + + def days: FiniteDuration = durationIn(DAYS) + def day: FiniteDuration = days + + def nanoseconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(nanoseconds) + def nanos[C](c: C)(implicit ev: Classifier[C]): ev.R = nanoseconds(c) + def nanosecond[C](c: C)(implicit ev: Classifier[C]): ev.R = nanoseconds(c) + def nano[C](c: C)(implicit ev: Classifier[C]): ev.R = nanoseconds(c) + + def microseconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(microseconds) + def micros[C](c: C)(implicit ev: Classifier[C]): ev.R = microseconds(c) + def microsecond[C](c: C)(implicit ev: Classifier[C]): ev.R = microseconds(c) + def micro[C](c: C)(implicit ev: Classifier[C]): ev.R = microseconds(c) + + def milliseconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(milliseconds) + def millis[C](c: C)(implicit ev: Classifier[C]): ev.R = milliseconds(c) + def millisecond[C](c: C)(implicit ev: Classifier[C]): ev.R = milliseconds(c) + def milli[C](c: C)(implicit ev: Classifier[C]): ev.R = milliseconds(c) + + def seconds[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(seconds) + def second[C](c: C)(implicit ev: Classifier[C]): ev.R = seconds(c) + + def minutes[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(minutes) + def minute[C](c: C)(implicit ev: Classifier[C]): ev.R = minutes(c) + + def hours[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(hours) + def hour[C](c: C)(implicit ev: Classifier[C]): ev.R = hours(c) + + def days[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(days) + def day[C](c: C)(implicit ev: Classifier[C]): ev.R = days(c) +} + +/** + * This object just holds some cogs which make the DSL machine work, not for direct consumption. + */ +object DurationConversions { + trait Classifier[C] { + type R + def convert(d: FiniteDuration): R + } + + implicit object spanConvert extends Classifier[span.type] { + type R = FiniteDuration + def convert(d: FiniteDuration): FiniteDuration = d + } + + implicit object fromNowConvert extends Classifier[fromNow.type] { + type R = Deadline + def convert(d: FiniteDuration): Deadline = Deadline.now + d + } + +} diff --git a/library/src/scala/concurrent/duration/package.scala b/library/src/scala/concurrent/duration/package.scala new file mode 100644 index 000000000000..f81b8777f6d0 --- /dev/null +++ b/library/src/scala/concurrent/duration/package.scala @@ -0,0 +1,87 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + +import scala.language.implicitConversions + +package object duration { + /** + * This object can be used as closing token if you prefer dot-less style but do not want + * to enable language.postfixOps: + * + * {{{ + * import scala.concurrent.duration._ + * + * val duration = 2 seconds span + * }}} + */ + object span + + /** + * This object can be used as closing token for declaring a deadline at some future point + * in time: + * + * {{{ + * import scala.concurrent.duration._ + * + * val deadline = 3 seconds fromNow + * }}} + */ + object fromNow + + type TimeUnit = java.util.concurrent.TimeUnit + final val DAYS = java.util.concurrent.TimeUnit.DAYS + final val HOURS = java.util.concurrent.TimeUnit.HOURS + final val MICROSECONDS = java.util.concurrent.TimeUnit.MICROSECONDS + final val MILLISECONDS = java.util.concurrent.TimeUnit.MILLISECONDS + final val MINUTES = java.util.concurrent.TimeUnit.MINUTES + final val NANOSECONDS = java.util.concurrent.TimeUnit.NANOSECONDS + final val SECONDS = java.util.concurrent.TimeUnit.SECONDS + + implicit def pairIntToDuration(p: (Int, TimeUnit)): Duration = Duration(p._1.toLong, p._2) + implicit def pairLongToDuration(p: (Long, TimeUnit)): FiniteDuration = Duration(p._1, p._2) + implicit def durationToPair(d: Duration): (Long, TimeUnit) = (d.length, d.unit) + + implicit final class DurationInt(private val n: Int) extends AnyVal with DurationConversions { + override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n.toLong, unit) + } + + implicit final class DurationLong(private val n: Long) extends AnyVal with DurationConversions { + override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit) + } + + implicit final class DurationDouble(private val d: Double) extends AnyVal with DurationConversions { + override protected def durationIn(unit: TimeUnit): FiniteDuration = + Duration(d, unit) match { + case f: FiniteDuration => f + case _ => throw new IllegalArgumentException("Duration DSL not applicable to " + d) + } + } + + /* + * Avoid reflection based invocation by using non-duck type + */ + implicit final class IntMult(private val i: Int) extends AnyVal { + def *(d: Duration): Duration = d * i.toDouble + def *(d: FiniteDuration): FiniteDuration = d * i.toLong + } + + implicit final class LongMult(private val i: Long) extends AnyVal { + def *(d: Duration): Duration = d * i.toDouble + def *(d: FiniteDuration): FiniteDuration = d * i.toLong + } + + implicit final class DoubleMult(private val f: Double) extends AnyVal { + def *(d: Duration): Duration = d * f.toDouble + } +} diff --git a/library/src/scala/concurrent/impl/ExecutionContextImpl.scala b/library/src/scala/concurrent/impl/ExecutionContextImpl.scala new file mode 100644 index 000000000000..262a12b1b4b9 --- /dev/null +++ b/library/src/scala/concurrent/impl/ExecutionContextImpl.scala @@ -0,0 +1,137 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent.impl + +import java.util.concurrent.{ Semaphore, ForkJoinPool, ForkJoinWorkerThread, Callable, Executor, ExecutorService, ThreadFactory, TimeUnit } +import java.util.Collection +import scala.concurrent.{ BlockContext, ExecutionContext, CanAwait, ExecutionContextExecutor, ExecutionContextExecutorService } + +private[scala] class ExecutionContextImpl private[impl] (final val executor: Executor, final val reporter: Throwable => Unit) extends ExecutionContextExecutor { + require(executor ne null, "Executor must not be null") + override final def execute(runnable: Runnable): Unit = executor execute runnable + override final def reportFailure(t: Throwable): Unit = reporter(t) +} + +private[concurrent] object ExecutionContextImpl { + + final class DefaultThreadFactory( + final val daemonic: Boolean, + final val maxBlockers: Int, + final val prefix: String, + final val uncaught: Thread.UncaughtExceptionHandler) extends ThreadFactory with ForkJoinPool.ForkJoinWorkerThreadFactory { + + require(prefix ne null, "DefaultThreadFactory.prefix must be non null") + require(maxBlockers >= 0, "DefaultThreadFactory.maxBlockers must be greater-or-equal-to 0") + + private final val blockerPermits = new Semaphore(maxBlockers) + + @annotation.nowarn("cat=deprecation") + def wire[T <: Thread](thread: T): T = { + thread.setDaemon(daemonic) + thread.setUncaughtExceptionHandler(uncaught) + thread.setName(prefix + "-" + thread.getId()) + thread + } + + def newThread(runnable: Runnable): Thread = wire(new Thread(runnable)) + + def newThread(fjp: ForkJoinPool): ForkJoinWorkerThread = + wire(new ForkJoinWorkerThread(fjp) with BlockContext { + private[this] final var isBlocked: Boolean = false // This is only ever read & written if this thread is the current thread + final override def blockOn[T](thunk: => T)(implicit permission: CanAwait): T = + if ((Thread.currentThread eq this) && !isBlocked && blockerPermits.tryAcquire()) { + try { + val b: ForkJoinPool.ManagedBlocker with (() => T) = + new ForkJoinPool.ManagedBlocker with (() => T) { + private[this] final var result: T = null.asInstanceOf[T] + private[this] final var done: Boolean = false + final override def block(): Boolean = { + if (!done) { + result = thunk // If this throws then it will stop blocking. + done = true + } + + isReleasable + } + + final override def isReleasable = done + final override def apply(): T = result + } + isBlocked = true + ForkJoinPool.managedBlock(b) + b() + } finally { + isBlocked = false + blockerPermits.release() + } + } else thunk // Unmanaged blocking + }) + } + + def createDefaultExecutorService(reporter: Throwable => Unit): ExecutionContextExecutorService = { + def getInt(name: String, default: String) = (try System.getProperty(name, default) catch { + case e: SecurityException => default + }) match { + case s if s.charAt(0) == 'x' => (Runtime.getRuntime.availableProcessors * s.substring(1).toDouble).ceil.toInt + case other => other.toInt + } + + val desiredParallelism = // A range between min and max given num + scala.math.min( + scala.math.max( + getInt("scala.concurrent.context.minThreads", "1"), + getInt("scala.concurrent.context.numThreads", "x1")), + getInt("scala.concurrent.context.maxThreads", "x1") + ) + + val threadFactory = new DefaultThreadFactory(daemonic = true, + maxBlockers = getInt("scala.concurrent.context.maxExtraThreads", "256"), + prefix = "scala-execution-context-global", + uncaught = (thread: Thread, cause: Throwable) => reporter(cause)) + + new ForkJoinPool(desiredParallelism, threadFactory, threadFactory.uncaught, true) with ExecutionContextExecutorService { + final override def reportFailure(cause: Throwable): Unit = + getUncaughtExceptionHandler() match { + case null => + case some => some.uncaughtException(Thread.currentThread, cause) + } + } + } + + def fromExecutor(e: Executor, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextExecutor = + e match { + case null => createDefaultExecutorService(reporter) + case some => new ExecutionContextImpl(some, reporter) + } + + def fromExecutorService(es: ExecutorService, reporter: Throwable => Unit = ExecutionContext.defaultReporter): + ExecutionContextExecutorService = es match { + case null => createDefaultExecutorService(reporter) + case some => + new ExecutionContextImpl(some, reporter) with ExecutionContextExecutorService { + private[this] final def asExecutorService: ExecutorService = executor.asInstanceOf[ExecutorService] + final override def shutdown() = asExecutorService.shutdown() + final override def shutdownNow() = asExecutorService.shutdownNow() + final override def isShutdown = asExecutorService.isShutdown + final override def isTerminated = asExecutorService.isTerminated + final override def awaitTermination(l: Long, timeUnit: TimeUnit) = asExecutorService.awaitTermination(l, timeUnit) + final override def submit[T](callable: Callable[T]) = asExecutorService.submit(callable) + final override def submit[T](runnable: Runnable, t: T) = asExecutorService.submit(runnable, t) + final override def submit(runnable: Runnable) = asExecutorService.submit(runnable) + final override def invokeAll[T](callables: Collection[_ <: Callable[T]]) = asExecutorService.invokeAll(callables) + final override def invokeAll[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = asExecutorService.invokeAll(callables, l, timeUnit) + final override def invokeAny[T](callables: Collection[_ <: Callable[T]]) = asExecutorService.invokeAny(callables) + final override def invokeAny[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = asExecutorService.invokeAny(callables, l, timeUnit) + } + } +} diff --git a/library/src/scala/concurrent/impl/FutureConvertersImpl.scala b/library/src/scala/concurrent/impl/FutureConvertersImpl.scala new file mode 100644 index 000000000000..a9eed4cbb055 --- /dev/null +++ b/library/src/scala/concurrent/impl/FutureConvertersImpl.scala @@ -0,0 +1,101 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent.impl + +import java.util.concurrent.{CompletableFuture, CompletionStage, TimeUnit} +import java.util.function.{BiConsumer, BiFunction, Consumer, Function => JFunction} + +import scala.concurrent.Future +import scala.concurrent.impl.Promise.DefaultPromise +import scala.util.{Failure, Success, Try} + +private[scala] object FutureConvertersImpl { + final class CF[T](val wrapped: Future[T]) extends CompletableFuture[T] with (Try[T] => Unit) { + override def apply(t: Try[T]): Unit = t match { + case Success(v) => complete(v) + case Failure(e) => completeExceptionally(e) + } + + // Ensure that completions of this future cannot hold the Scala Future's completer hostage + + override def thenApply[U](fn: JFunction[_ >: T, _ <: U]): CompletableFuture[U] = thenApplyAsync(fn) + + override def thenAccept(fn: Consumer[_ >: T]): CompletableFuture[Void] = thenAcceptAsync(fn) + + override def thenRun(fn: Runnable): CompletableFuture[Void] = thenRunAsync(fn) + + override def thenCombine[U, V](cs: CompletionStage[_ <: U], fn: BiFunction[_ >: T, _ >: U, _ <: V]): CompletableFuture[V] = thenCombineAsync(cs, fn) + + override def thenAcceptBoth[U](cs: CompletionStage[_ <: U], fn: BiConsumer[_ >: T, _ >: U]): CompletableFuture[Void] = thenAcceptBothAsync(cs, fn) + + override def runAfterBoth(cs: CompletionStage[_], fn: Runnable): CompletableFuture[Void] = runAfterBothAsync(cs, fn) + + override def applyToEither[U](cs: CompletionStage[_ <: T], fn: JFunction[_ >: T, U]): CompletableFuture[U] = applyToEitherAsync(cs, fn) + + override def acceptEither(cs: CompletionStage[_ <: T], fn: Consumer[_ >: T]): CompletableFuture[Void] = acceptEitherAsync(cs, fn) + + override def runAfterEither(cs: CompletionStage[_], fn: Runnable): CompletableFuture[Void] = runAfterEitherAsync(cs, fn) + + override def thenCompose[U](fn: JFunction[_ >: T, _ <: CompletionStage[U]]): CompletableFuture[U] = thenComposeAsync(fn) + + override def whenComplete(fn: BiConsumer[_ >: T, _ >: Throwable]): CompletableFuture[T] = whenCompleteAsync(fn) + + override def handle[U](fn: BiFunction[_ >: T, Throwable, _ <: U]): CompletableFuture[U] = handleAsync(fn) + + override def exceptionally(fn: JFunction[Throwable, _ <: T]): CompletableFuture[T] = { + val cf = new CompletableFuture[T] + whenCompleteAsync((t, e) => { + if (e == null) cf.complete(t) + else { + val n: AnyRef = + try { + fn(e).asInstanceOf[AnyRef] + } catch { + case thr: Throwable => + cf.completeExceptionally(thr) + this + } + if (n ne this) cf.complete(n.asInstanceOf[T]) + } + } + ) + cf + } + + /** + * @inheritdoc + * + * WARNING: completing the result of this method will not complete the underlying + * Scala Future or Promise (ie, the one that that was passed to `toJava`.) + */ + override def toCompletableFuture: CompletableFuture[T] = this + + override def obtrudeValue(value: T): Unit = throw new UnsupportedOperationException("obtrudeValue may not be used on the result of toJava(scalaFuture)") + + override def obtrudeException(ex: Throwable): Unit = throw new UnsupportedOperationException("obtrudeException may not be used on the result of toJava(scalaFuture)") + + override def get(): T = scala.concurrent.blocking(super.get()) + + override def get(timeout: Long, unit: TimeUnit): T = scala.concurrent.blocking(super.get(timeout, unit)) + + override def toString(): String = super[CompletableFuture].toString + } + + final class P[T](val wrapped: CompletionStage[T]) extends DefaultPromise[T] with BiFunction[T, Throwable, Unit] { + override def apply(v: T, e: Throwable): Unit = { + if (e == null) success(v) + else failure(e) + } + } +} + diff --git a/library/src/scala/concurrent/impl/Promise.scala b/library/src/scala/concurrent/impl/Promise.scala new file mode 100644 index 000000000000..bf89e6ef2217 --- /dev/null +++ b/library/src/scala/concurrent/impl/Promise.scala @@ -0,0 +1,510 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent.impl +import scala.concurrent.{Batchable, CanAwait, ExecutionContext, ExecutionException, Future, TimeoutException} +import scala.concurrent.duration.Duration +import scala.annotation.{nowarn, switch, tailrec} +import scala.util.control.{ControlThrowable, NonFatal} +import scala.util.{Failure, Success, Try} +import scala.runtime.NonLocalReturnControl +import java.util.concurrent.locks.AbstractQueuedSynchronizer +import java.util.concurrent.atomic.AtomicReference +import java.util.Objects.requireNonNull +import java.io.{IOException, NotSerializableException, ObjectInputStream, ObjectOutputStream} + +/** + * Latch used to implement waiting on a DefaultPromise's result. + * + * Inspired by: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/locks/AbstractQueuedSynchronizer.java + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * https://creativecommons.org/publicdomain/zero/1.0/ + */ +private[impl] final class CompletionLatch[T] extends AbstractQueuedSynchronizer with (Try[T] => Unit) { + //@volatie not needed since we use acquire/release + /*@volatile*/ private[this] var _result: Try[T] = null + final def result: Try[T] = _result + override protected def tryAcquireShared(ignored: Int): Int = if (getState != 0) 1 else -1 + override protected def tryReleaseShared(ignore: Int): Boolean = { + setState(1) + true + } + override def apply(value: Try[T]): Unit = { + _result = value // This line MUST go before releaseShared + releaseShared(1) + } +} + +private[concurrent] object Promise { + /** + * Link represents a completion dependency between 2 DefaultPromises. + * As the DefaultPromise referred to by a Link can itself be linked to another promise + * `relink` traverses such chains and compresses them so that the link always points + * to the root of the dependency chain. + * + * In order to conserve memory, the owner of a Link (a DefaultPromise) is not stored + * on the Link, but is instead passed in as a parameter to the operation(s). + * + * If when compressing a chain of Links it is discovered that the root has been completed, + * the `owner`'s value is completed with that value, and the Link chain is discarded. + **/ + private[concurrent] final class Link[T](to: DefaultPromise[T]) extends AtomicReference[DefaultPromise[T]](to) { + /** + * Compresses this chain and returns the currently known root of this chain of Links. + **/ + final def promise(owner: DefaultPromise[T]): DefaultPromise[T] = { + val c = get() + compressed(current = c, target = c, owner = owner) + } + + /** + * The combination of traversing and possibly unlinking of a given `target` DefaultPromise. + **/ + @inline @tailrec private[this] final def compressed(current: DefaultPromise[T], target: DefaultPromise[T], owner: DefaultPromise[T]): DefaultPromise[T] = { + val value = target.get() + if (value.isInstanceOf[Callbacks[_]]) { + if (compareAndSet(current, target)) target // Link + else compressed(current = get(), target = target, owner = owner) // Retry + } else if (value.isInstanceOf[Link[_]]) compressed(current = current, target = value.asInstanceOf[Link[T]].get(), owner = owner) // Compress + else /*if (value.isInstanceOf[Try[T]])*/ { + owner.unlink(value.asInstanceOf[Try[T]]) // Discard links + owner + } + } + } + + /** + * The process of "resolving" a Try is to validate that it only contains + * those values which makes sense in the context of Futures. + **/ + // requireNonNull is paramount to guard against null completions + private[this] final def resolve[T](value: Try[T]): Try[T] = + if (requireNonNull(value).isInstanceOf[Success[T]]) value + else { + val t = value.asInstanceOf[Failure[T]].exception + if (t.isInstanceOf[ControlThrowable] || t.isInstanceOf[InterruptedException] || t.isInstanceOf[Error]) { + if (t.isInstanceOf[NonLocalReturnControl[T @unchecked]]) + Success(t.asInstanceOf[NonLocalReturnControl[T]].value) + else + Failure(new ExecutionException("Boxed Exception", t)) + } else value + } + + // Left non-final to enable addition of extra fields by Java/Scala converters in scala-java8-compat. + class DefaultPromise[T] private[this] (initial: AnyRef) extends AtomicReference[AnyRef](initial) with scala.concurrent.Promise[T] with scala.concurrent.Future[T] with (Try[T] => Unit) { + /** + * Constructs a new, completed, Promise. + */ + final def this(result: Try[T]) = this(resolve(result): AnyRef) + + /** + * Constructs a new, un-completed, Promise. + */ + final def this() = this(Noop: AnyRef) + + /** + * WARNING: the `resolved` value needs to have been pre-resolved using `resolve()` + * INTERNAL API + */ + override final def apply(resolved: Try[T]): Unit = + tryComplete0(get(), resolved) + + /** + * Returns the associated `Future` with this `Promise` + */ + override final def future: Future[T] = this + + override final def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] = + dispatchOrAddCallbacks(get(), new Transformation[T, S](Xform_transform, f, executor)) + + override final def transformWith[S](f: Try[T] => Future[S])(implicit executor: ExecutionContext): Future[S] = + dispatchOrAddCallbacks(get(), new Transformation[T, S](Xform_transformWith, f, executor)) + + override final def zipWith[U, R](that: Future[U])(f: (T, U) => R)(implicit executor: ExecutionContext): Future[R] = { + val state = get() + if (state.isInstanceOf[Try[_]]) { + if (state.asInstanceOf[Try[T]].isFailure) this.asInstanceOf[Future[R]] + else { + val l = state.asInstanceOf[Success[T]].get + that.map(r => f(l, r)) + } + } else { + val buffer = new AtomicReference[Success[Any]]() + val zipped = new DefaultPromise[R]() + + val thisF: Try[T] => Unit = { + case left: Success[_] => + val right = buffer.getAndSet(left).asInstanceOf[Success[U]] + if (right ne null) + zipped.tryComplete(try Success(f(left.get, right.get)) catch { case e if NonFatal(e) => Failure(e) }) + case f => // Can only be Failure + zipped.tryComplete(f.asInstanceOf[Failure[R]]) + } + + val thatF: Try[U] => Unit = { + case right: Success[_] => + val left = buffer.getAndSet(right).asInstanceOf[Success[T]] + if (left ne null) + zipped.tryComplete(try Success(f(left.get, right.get)) catch { case e if NonFatal(e) => Failure(e) }) + case f => // Can only be Failure + zipped.tryComplete(f.asInstanceOf[Failure[R]]) + } + // Cheaper than this.onComplete since we already polled the state + this.dispatchOrAddCallbacks(state, new Transformation[T, Unit](Xform_onComplete, thisF, executor)) + that.onComplete(thatF) + zipped.future + } + } + + override final def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = { + val state = get() + if (!state.isInstanceOf[Failure[_]]) dispatchOrAddCallbacks(state, new Transformation[T, Unit](Xform_foreach, f, executor)) + } + + override final def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = { + val state = get() + if (!state.isInstanceOf[Failure[_]]) dispatchOrAddCallbacks(state, new Transformation[T, S](Xform_flatMap, f, executor)) + else this.asInstanceOf[Future[S]] + } + + override final def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { + val state = get() + if (!state.isInstanceOf[Failure[_]]) dispatchOrAddCallbacks(state, new Transformation[T, S](Xform_map, f, executor)) + else this.asInstanceOf[Future[S]] + } + + override final def filter(p: T => Boolean)(implicit executor: ExecutionContext): Future[T] = { + val state = get() + if (!state.isInstanceOf[Failure[_]]) dispatchOrAddCallbacks(state, new Transformation[T, T](Xform_filter, p, executor)) // Short-circuit if we get a Success + else this + } + + override final def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] = { + val state = get() + if (!state.isInstanceOf[Failure[_]]) dispatchOrAddCallbacks(state, new Transformation[T, S](Xform_collect, pf, executor)) // Short-circuit if we get a Success + else this.asInstanceOf[Future[S]] + } + + override final def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = { + val state = get() + if (!state.isInstanceOf[Success[_]]) dispatchOrAddCallbacks(state, new Transformation[T, U](Xform_recoverWith, pf, executor)) // Short-circuit if we get a Failure + else this.asInstanceOf[Future[U]] + } + + override final def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = { + val state = get() + if (!state.isInstanceOf[Success[_]]) dispatchOrAddCallbacks(state, new Transformation[T, U](Xform_recover, pf, executor)) // Short-circuit if we get a Failure + else this.asInstanceOf[Future[U]] + } + + override final def mapTo[S](implicit tag: scala.reflect.ClassTag[S]): Future[S] = + if (!get().isInstanceOf[Failure[_]]) super[Future].mapTo[S](tag) // Short-circuit if we get a Success + else this.asInstanceOf[Future[S]] + + + override final def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit = + dispatchOrAddCallbacks(get(), new Transformation[T, Unit](Xform_onComplete, func, executor)) + + override final def failed: Future[Throwable] = + if (!get().isInstanceOf[Success[_]]) super.failed + else Future.failedFailureFuture // Cached instance in case of already known success + + @tailrec override final def toString: String = { + val state = get() + if (state.isInstanceOf[Try[_]]) "Future("+state+")" + else if (state.isInstanceOf[Link[_]]) state.asInstanceOf[Link[T]].promise(this).toString + else /*if (state.isInstanceOf[Callbacks[T]]) */ "Future()" + } + + private[this] final def tryAwait0(atMost: Duration): Try[T] = + if (atMost ne Duration.Undefined) { + val v = value0 + if (v ne null) v + else { + val r = + if (atMost <= Duration.Zero) null + else { + val l = new CompletionLatch[T]() + onComplete(l)(ExecutionContext.parasitic) + + if (atMost.isFinite) + l.tryAcquireSharedNanos(1, atMost.toNanos) + else + l.acquireSharedInterruptibly(1) + + l.result + } + if (r ne null) r + else throw new TimeoutException("Future timed out after [" + atMost + "]") + } + } else throw new IllegalArgumentException("Cannot wait for Undefined duration of time") + + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) + final def ready(atMost: Duration)(implicit permit: CanAwait): this.type = { + tryAwait0(atMost) + this + } + + @throws(classOf[Exception]) + final def result(atMost: Duration)(implicit permit: CanAwait): T = + tryAwait0(atMost).get // returns the value, or throws the contained exception + + override final def isCompleted: Boolean = value0 ne null + + override final def value: Option[Try[T]] = Option(value0) + + @tailrec // returns null if not completed + private final def value0: Try[T] = { + val state = get() + if (state.isInstanceOf[Try[_]]) state.asInstanceOf[Try[T]] + else if (state.isInstanceOf[Link[_]]) state.asInstanceOf[Link[T]].promise(this).value0 + else /*if (state.isInstanceOf[Callbacks[T]])*/ null + } + + override final def tryComplete(value: Try[T]): Boolean = { + val state = get() + if (state.isInstanceOf[Try[_]]) false + else tryComplete0(state, resolve(value)) + } + + @tailrec // WARNING: important that the supplied Try really is resolve():d + private[Promise] final def tryComplete0(state: AnyRef, resolved: Try[T]): Boolean = + if (state.isInstanceOf[Callbacks[_]]) { + if (compareAndSet(state, resolved)) { + if (state ne Noop) submitWithValue(state.asInstanceOf[Callbacks[T]], resolved) + true + } else tryComplete0(get(), resolved) + } else if (state.isInstanceOf[Link[_]]) { + val p = state.asInstanceOf[Link[T]].promise(this) // If this returns owner/this, we are in a completed link + (p ne this) && p.tryComplete0(p.get(), resolved) // Use this to get tailcall optimization and avoid re-resolution + } else /* if(state.isInstanceOf[Try[T]]) */ false + + override final def completeWith(other: Future[T]): this.type = { + if (other ne this) { + val state = get() + if (!state.isInstanceOf[Try[_]]) { + val resolved = if (other.isInstanceOf[DefaultPromise[_]]) other.asInstanceOf[DefaultPromise[T]].value0 else other.value.orNull + if (resolved ne null) tryComplete0(state, resolved) + else other.onComplete(this)(ExecutionContext.parasitic) + } + } + + this + } + + /** Tries to add the callback, if already completed, it dispatches the callback to be executed. + * Used by `onComplete()` to add callbacks to a promise and by `link()` to transfer callbacks + * to the root promise when linking two promises together. + */ + @tailrec private final def dispatchOrAddCallbacks[C <: Callbacks[T]](state: AnyRef, callbacks: C): C = + if (state.isInstanceOf[Try[_]]) { + submitWithValue(callbacks, state.asInstanceOf[Try[T]]) // invariant: callbacks should never be Noop here + callbacks + } else if (state.isInstanceOf[Callbacks[_]]) { + if(compareAndSet(state, if (state ne Noop) concatCallbacks(callbacks, state.asInstanceOf[Callbacks[T]]) else callbacks)) callbacks + else dispatchOrAddCallbacks(get(), callbacks) + } else /*if (state.isInstanceOf[Link[T]])*/ { + val p = state.asInstanceOf[Link[T]].promise(this) + p.dispatchOrAddCallbacks(p.get(), callbacks) + } + + // IMPORTANT: Noop should never be passed in here, neither as left OR as right + @tailrec private[this] final def concatCallbacks(left: Callbacks[T], right: Callbacks[T]): Callbacks[T] = + if (left.isInstanceOf[Transformation[T,_]]) new ManyCallbacks[T](left.asInstanceOf[Transformation[T,_]], right) + else /*if (left.isInstanceOf[ManyCallbacks[T]) */ { // This should only happen when linking + val m = left.asInstanceOf[ManyCallbacks[T]] + concatCallbacks(m.rest, new ManyCallbacks(m.first, right)) + } + + // IMPORTANT: Noop should not be passed in here, `callbacks` cannot be null + @tailrec + private[this] final def submitWithValue(callbacks: Callbacks[T], resolved: Try[T]): Unit = + if(callbacks.isInstanceOf[ManyCallbacks[T]]) { + val m: ManyCallbacks[T] = callbacks.asInstanceOf[ManyCallbacks[T]] + m.first.submitWithValue(resolved) + submitWithValue(m.rest, resolved) + } else { + callbacks.asInstanceOf[Transformation[T, _]].submitWithValue(resolved) + } + + /** Link this promise to the root of another promise. + */ + @tailrec private[concurrent] final def linkRootOf(target: DefaultPromise[T], link: Link[T]): Unit = + if (this ne target) { + val state = get() + if (state.isInstanceOf[Try[_]]) { + if(!target.tryComplete0(target.get(), state.asInstanceOf[Try[T]])) + throw new IllegalStateException("Cannot link completed promises together") + } else if (state.isInstanceOf[Callbacks[_]]) { + val l = if (link ne null) link else new Link(target) + val p = l.promise(this) + if ((this ne p) && compareAndSet(state, l)) { + if (state ne Noop) p.dispatchOrAddCallbacks(p.get(), state.asInstanceOf[Callbacks[T]]) // Noop-check is important here + } else linkRootOf(p, l) + } else /* if (state.isInstanceOf[Link[T]]) */ + state.asInstanceOf[Link[T]].promise(this).linkRootOf(target, link) + } + + /** + * Unlinks (removes) the link chain if the root is discovered to be already completed, + * and completes the `owner` with that result. + **/ + @tailrec private[concurrent] final def unlink(resolved: Try[T]): Unit = { + val state = get() + if (state.isInstanceOf[Link[_]]) { + val next = if (compareAndSet(state, resolved)) state.asInstanceOf[Link[T]].get() else this + next.unlink(resolved) + } else tryComplete0(state, resolved) + } + + @throws[IOException] + private def writeObject(out: ObjectOutputStream): Unit = + throw new NotSerializableException("Promises and Futures cannot be serialized") + + @throws[IOException] + @throws[ClassNotFoundException] + private def readObject(in: ObjectInputStream): Unit = + throw new NotSerializableException("Promises and Futures cannot be deserialized") + } + + // Constant byte tags for unpacking transformation function inputs or outputs + // These need to be Ints to get compiled into constants. + final val Xform_noop = 0 + final val Xform_map = 1 + final val Xform_flatMap = 2 + final val Xform_transform = 3 + final val Xform_transformWith = 4 + final val Xform_foreach = 5 + final val Xform_onComplete = 6 + final val Xform_recover = 7 + final val Xform_recoverWith = 8 + final val Xform_filter = 9 + final val Xform_collect = 10 + + /* Marker trait + */ + sealed trait Callbacks[-T] + + final class ManyCallbacks[-T](final val first: Transformation[T,_], final val rest: Callbacks[T]) extends Callbacks[T] { + override final def toString: String = "ManyCallbacks" + } + + private[this] final val Noop = new Transformation[Nothing, Nothing](Xform_noop, null, ExecutionContext.parasitic) + + /** + * A Transformation[F, T] receives an F (it is a Callback[F]) and applies a transformation function to that F, + * Producing a value of type T (it is a Promise[T]). + * In order to conserve allocations, indirections, and avoid introducing bi/mega-morphicity the transformation + * function's type parameters are erased, and the _xform tag will be used to reify them. + **/ + final class Transformation[-F, T] private[this] ( + private[this] final var _fun: Any => Any, + private[this] final var _ec: ExecutionContext, + private[this] final var _arg: Try[F], + private[this] final val _xform: Int + ) extends DefaultPromise[T]() with Callbacks[F] with Runnable with Batchable { + final def this(xform: Int, f: _ => _, ec: ExecutionContext) = + this(f.asInstanceOf[Any => Any], ec.prepare(): @nowarn("cat=deprecation"), null, xform) + + final def benefitsFromBatching: Boolean = _xform != Xform_onComplete && _xform != Xform_foreach + + // Gets invoked when a value is available, schedules it to be run():ed by the ExecutionContext + // submitWithValue *happens-before* run(), through ExecutionContext.execute. + // Invariant: _arg is `null`, _ec is non-null. `this` ne Noop. + // requireNonNull(resolved) will hold as guarded by `resolve` + final def submitWithValue(resolved: Try[F]): this.type = { + _arg = resolved + val e = _ec + try e.execute(this) /* Safe publication of _arg, _fun, _ec */ + catch { + case t: Throwable => + _fun = null // allow to GC + _arg = null // see above + _ec = null // see above again + handleFailure(t, e) + } + + this + } + + private[this] final def handleFailure(t: Throwable, e: ExecutionContext): Unit = { + val wasInterrupted = t.isInstanceOf[InterruptedException] + if (wasInterrupted || NonFatal(t)) { + val completed = tryComplete0(get(), resolve(Failure(t))) + if (completed && wasInterrupted) Thread.currentThread.interrupt() + + // Report or rethrow failures which are unlikely to otherwise be noticed + if (_xform == Xform_foreach || _xform == Xform_onComplete || !completed) + e.reportFailure(t) + } else throw t + } + + // Gets invoked by the ExecutionContext, when we have a value to transform. + override final def run(): Unit = { + val v = _arg + val fun = _fun + val ec = _ec + _fun = null // allow to GC + _arg = null // see above + _ec = null // see above + try { + val resolvedResult: Try[_] = + (_xform: @switch) match { + case Xform_noop => + null + case Xform_map => + if (v.isInstanceOf[Success[F]]) Success(fun(v.get)) else v // Faster than `resolve(v map fun)` + case Xform_flatMap => + if (v.isInstanceOf[Success[F]]) { + val f = fun(v.get) + if (f.isInstanceOf[DefaultPromise[_]]) f.asInstanceOf[DefaultPromise[T]].linkRootOf(this, null) else completeWith(f.asInstanceOf[Future[T]]) + null + } else v + case Xform_transform => + resolve(fun(v).asInstanceOf[Try[T]]) + case Xform_transformWith => + val f = fun(v) + if (f.isInstanceOf[DefaultPromise[_]]) f.asInstanceOf[DefaultPromise[T]].linkRootOf(this, null) else completeWith(f.asInstanceOf[Future[T]]) + null + case Xform_foreach => + v.foreach(fun) + null + case Xform_onComplete => + fun(v) + null + case Xform_recover => + if (v.isInstanceOf[Failure[_]]) resolve(v.recover(fun.asInstanceOf[PartialFunction[Throwable, F]])) else v //recover F=:=T + case Xform_recoverWith => + if (v.isInstanceOf[Failure[F]]) { + val f = fun.asInstanceOf[PartialFunction[Throwable, Future[T]]].applyOrElse(v.asInstanceOf[Failure[F]].exception, Future.recoverWithFailed) + if (f ne Future.recoverWithFailedMarker) { + if (f.isInstanceOf[DefaultPromise[T]]) f.asInstanceOf[DefaultPromise[T]].linkRootOf(this, null) else completeWith(f.asInstanceOf[Future[T]]) + null + } else v + } else v + case Xform_filter => + if (v.isInstanceOf[Failure[F]] || fun.asInstanceOf[F => Boolean](v.get)) v else Future.filterFailure + case Xform_collect => + if (v.isInstanceOf[Success[F]]) Success(fun.asInstanceOf[PartialFunction[F, T]].applyOrElse(v.get, Future.collectFailed)) else v + case _ => + Failure(new IllegalStateException("BUG: encountered transformation promise with illegal type: " + _xform)) // Safe not to `resolve` + } + if (resolvedResult ne null) + tryComplete0(get(), resolvedResult.asInstanceOf[Try[T]]) // T is erased anyway so we won't have any use for it above + } catch { + case t: Throwable => handleFailure(t, ec) + } + } + } +} diff --git a/library/src/scala/concurrent/package.scala b/library/src/scala/concurrent/package.scala new file mode 100644 index 000000000000..d648a1c90a15 --- /dev/null +++ b/library/src/scala/concurrent/package.scala @@ -0,0 +1,204 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.concurrent.duration.Duration +import scala.annotation.implicitNotFound + +/** This package object contains primitives for concurrent and parallel programming. + * + * == Guide == + * + * A more detailed guide to Futures and Promises, including discussion and examples + * can be found at + * [[https://docs.scala-lang.org/overviews/core/futures.html]]. + * + * == Common Imports == + * + * When working with Futures, you will often find that importing the whole concurrent + * package is convenient: + * + * {{{ + * import scala.concurrent._ + * }}} + * + * When using things like `Future`s, it is often required to have an implicit `ExecutionContext` + * in scope. The general advice for these implicits are as follows. + * + * If the code in question is a class or method definition, and no `ExecutionContext` is available, + * request one from the caller by adding an implicit parameter list: + * + * {{{ + * def myMethod(myParam: MyType)(implicit ec: ExecutionContext) = … + * //Or + * class MyClass(myParam: MyType)(implicit ec: ExecutionContext) { … } + * }}} + * + * This allows the caller of the method, or creator of the instance of the class, to decide which + * `ExecutionContext` should be used. + * + * For typical REPL usage and experimentation, importing the global `ExecutionContext` is often desired. + * + * {{{ + * import scala.concurrent.ExcutionContext.Implicits.global + * }}} + * + * == Specifying Durations == + * + * Operations often require a duration to be specified. A duration DSL is available + * to make defining these easier: + * + * {{{ + * import scala.concurrent.duration._ + * val d: Duration = 10.seconds + * }}} + * + * == Using Futures For Non-blocking Computation == + * + * Basic use of futures is easy with the factory method on Future, which executes a + * provided function asynchronously, handing you back a future result of that function + * without blocking the current thread. In order to create the Future you will need + * either an implicit or explicit ExecutionContext to be provided: + * + * {{{ + * import scala.concurrent._ + * import ExecutionContext.Implicits.global // implicit execution context + * + * val firstZebra: Future[Int] = Future { + * val words = Files.readAllLines("/etc/dictionaries-common/words").asScala + * words.indexOfSlice("zebra") + * } + * }}} + * + * == Avoid Blocking == + * + * Although blocking is possible in order to await results (with a mandatory timeout duration): + * + * {{{ + * import scala.concurrent.duration._ + * Await.result(firstZebra, 10.seconds) + * }}} + * + * and although this is sometimes necessary to do, in particular for testing purposes, blocking + * in general is discouraged when working with Futures and concurrency in order to avoid + * potential deadlocks and improve performance. Instead, use callbacks or combinators to + * remain in the future domain: + * + * {{{ + * val animalRange: Future[Int] = for { + * aardvark <- firstAardvark + * zebra <- firstZebra + * } yield zebra - aardvark + * + * animalRange.onSuccess { + * case x if x > 500000 => println("It's a long way from Aardvark to Zebra") + * } + * }}} + */ +package object concurrent { + type ExecutionException = java.util.concurrent.ExecutionException + type CancellationException = java.util.concurrent.CancellationException + type TimeoutException = java.util.concurrent.TimeoutException + + /** Used to designate a piece of code which potentially blocks, allowing the current [[BlockContext]] to adjust + * the runtime's behavior. + * Properly marking blocking code may improve performance or avoid deadlocks. + * + * Blocking on an [[Awaitable]] should be done using [[Await.result]] instead of `blocking`. + * + * @param body A piece of code which contains potentially blocking or long running calls. + * @throws CancellationException if the computation was cancelled + * @throws InterruptedException in the case that a wait within the blocking `body` was interrupted + */ + @throws(classOf[Exception]) + final def blocking[T](body: => T): T = BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission) +} + +package concurrent { + /** + * This marker trait is used by [[Await]] to ensure that [[Awaitable.ready]] and [[Awaitable.result]] + * are not directly called by user code. An implicit instance of this trait is only available when + * user code is currently calling the methods on [[Await]]. + */ + @implicitNotFound("Don't call `Awaitable` methods directly, use the `Await` object.") + sealed trait CanAwait + + /** + * Internal usage only, implementation detail. + */ + private[concurrent] object AwaitPermission extends CanAwait + + /** + * `Await` is what is used to ensure proper handling of blocking for `Awaitable` instances. + * + * While occasionally useful, e.g. for testing, it is recommended that you avoid Await whenever possible— + * instead favoring combinators and/or callbacks. + * Await's `result` and `ready` methods will block the calling thread's execution until they return, + * which will cause performance degradation, and possibly, deadlock issues. + */ + object Await { + /** + * Await the "completed" state of an `Awaitable`. + * + * Although this method is blocking, the internal use of [[scala.concurrent.blocking blocking]] ensures that + * the underlying [[ExecutionContext]] is given an opportunity to properly manage the blocking. + * + * WARNING: It is strongly discouraged to supply lengthy timeouts since the progress of the calling thread will be + * suspended—blocked—until either the `Awaitable` becomes ready or the timeout expires. + * + * @param awaitable + * the `Awaitable` to be awaited + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[scala.concurrent.duration.Duration.Inf Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return the `awaitable` + * @throws InterruptedException if the current thread is interrupted while waiting + * @throws TimeoutException if after waiting for the specified time this `Awaitable` is still not ready + * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]] + */ + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) + final def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = awaitable match { + case f: Future[T] if f.isCompleted => awaitable.ready(atMost)(AwaitPermission) + case _ => blocking(awaitable.ready(atMost)(AwaitPermission)) + } + + /** + * Await and return the result (of type `T`) of an `Awaitable`. + * + * Although this method is blocking, the internal use of [[scala.concurrent.blocking blocking]] ensures that + * the underlying [[ExecutionContext]] is given an opportunity to properly manage the blocking. + * + * WARNING: It is strongly discouraged to supply lengthy timeouts since the progress of the calling thread will be + * suspended—blocked—until either the `Awaitable` has a result or the timeout expires. + * + * @param awaitable + * the `Awaitable` to be awaited + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[scala.concurrent.duration.Duration.Inf Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return the result value if `awaitable` is completed within the specific maximum wait time + * @throws InterruptedException if the current thread is interrupted while waiting + * @throws TimeoutException if after waiting for the specified time `awaitable` is still not ready + * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]] + */ + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) + final def result[T](awaitable: Awaitable[T], atMost: Duration): T = awaitable match { + case f: Future[T] if f.isCompleted => f.result(atMost)(AwaitPermission) + case _ => blocking(awaitable.result(atMost)(AwaitPermission)) + } + } +} From d99325ca3b1dd5218b9a38150ce493af953652cf Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:35:28 +0100 Subject: [PATCH 10/28] chore: add all the files in scala.io --- library/src/scala/io/AnsiColor.scala | 176 ++++++++++ library/src/scala/io/BufferedSource.scala | 114 +++++++ library/src/scala/io/Codec.scala | 136 ++++++++ library/src/scala/io/Position.scala | 84 +++++ library/src/scala/io/Source.scala | 381 ++++++++++++++++++++++ library/src/scala/io/StdIn.scala | 241 ++++++++++++++ 6 files changed, 1132 insertions(+) create mode 100644 library/src/scala/io/AnsiColor.scala create mode 100644 library/src/scala/io/BufferedSource.scala create mode 100644 library/src/scala/io/Codec.scala create mode 100644 library/src/scala/io/Position.scala create mode 100644 library/src/scala/io/Source.scala create mode 100644 library/src/scala/io/StdIn.scala diff --git a/library/src/scala/io/AnsiColor.scala b/library/src/scala/io/AnsiColor.scala new file mode 100644 index 000000000000..ca27dac45a6b --- /dev/null +++ b/library/src/scala/io/AnsiColor.scala @@ -0,0 +1,176 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package io + +/** ANSI escape codes providing control over text formatting and color on supporting text terminals. + * + * ==ANSI Style and Control Codes== + * + * This group of escape codes provides control over text styling. For example, to turn on reverse video with bold and + * then turn off all styling embed these codes, + * + * {{{ + * import io.AnsiColor._ + * + * object ColorDemo extends App { + * + * println(s"\${REVERSED}\${BOLD}Hello 1979!\${RESET}") + * } + * }}} + * + * ==Foreground and Background Colors== + * + * Embedding ANSI color codes in text output will control the text foreground and background colors. + * + * + * + * + * + * + * + * + * + * + * + *
ForegroundBackground
BLACK BLACK_B
RED RED_B
GREEN GREEN_B
YELLOW YELLOW_B
BLUE BLUE_B
MAGENTAMAGENTA_B
CYAN CYAN_B
WHITE WHITE_B
+ * + * @groupname style-control ANSI Style and Control Codes + * @groupprio style-control 101 + * + * @groupname color-black ANSI Black + * @groupdesc color-black
 
+ * @groupprio color-black 110 + * + * @groupname color-red ANSI Red + * @groupdesc color-red
 
+ * @groupprio color-red 120 + * + * @groupname color-green ANSI Green + * @groupdesc color-green
 
+ * @groupprio color-green 130 + * + * @groupname color-yellow ANSI Yellow + * @groupdesc color-yellow
 
+ * @groupprio color-yellow 140 + * + * @groupname color-blue ANSI Blue + * @groupdesc color-blue
 
+ * @groupprio color-blue 150 + * + * @groupname color-magenta ANSI Magenta + * @groupdesc color-magenta
 
+ * @groupprio color-magenta 160 + * + * @groupname color-cyan ANSI Cyan + * @groupdesc color-cyan
 
+ * @groupprio color-cyan 170 + * + * @groupname color-white ANSI White + * @groupdesc color-white
 
+ * @groupprio color-white 180 + */ +trait AnsiColor { + /** Foreground color for ANSI black + * @group color-black + */ + final val BLACK = "\u001b[30m" + /** Foreground color for ANSI red + * @group color-red + */ + final val RED = "\u001b[31m" + /** Foreground color for ANSI green + * @group color-green + */ + final val GREEN = "\u001b[32m" + /** Foreground color for ANSI yellow + * @group color-yellow + */ + final val YELLOW = "\u001b[33m" + /** Foreground color for ANSI blue + * @group color-blue + */ + final val BLUE = "\u001b[34m" + /** Foreground color for ANSI magenta + * @group color-magenta + */ + final val MAGENTA = "\u001b[35m" + /** Foreground color for ANSI cyan + * @group color-cyan + */ + final val CYAN = "\u001b[36m" + /** Foreground color for ANSI white + * @group color-white + */ + final val WHITE = "\u001b[37m" + + /** Background color for ANSI black + * @group color-black + */ + final val BLACK_B = "\u001b[40m" + /** Background color for ANSI red + * @group color-red + */ + final val RED_B = "\u001b[41m" + /** Background color for ANSI green + * @group color-green + */ + final val GREEN_B = "\u001b[42m" + /** Background color for ANSI yellow + * @group color-yellow + */ + final val YELLOW_B = "\u001b[43m" + /** Background color for ANSI blue + * @group color-blue + */ + final val BLUE_B = "\u001b[44m" + /** Background color for ANSI magenta + * @group color-magenta + */ + final val MAGENTA_B = "\u001b[45m" + /** Background color for ANSI cyan + * @group color-cyan + */ + final val CYAN_B = "\u001b[46m" + /** Background color for ANSI white + * @group color-white + */ + final val WHITE_B = "\u001b[47m" + + /** Reset ANSI styles + * @group style-control + */ + final val RESET = "\u001b[0m" + /** ANSI bold + * @group style-control + */ + final val BOLD = "\u001b[1m" + /** ANSI underlines + * @group style-control + */ + final val UNDERLINED = "\u001b[4m" + /** ANSI blink + * @group style-control + */ + final val BLINK = "\u001b[5m" + /** ANSI reversed + * @group style-control + */ + final val REVERSED = "\u001b[7m" + /** ANSI invisible + * @group style-control + */ + final val INVISIBLE = "\u001b[8m" +} + +object AnsiColor extends AnsiColor { } diff --git a/library/src/scala/io/BufferedSource.scala b/library/src/scala/io/BufferedSource.scala new file mode 100644 index 000000000000..2369b528f8f7 --- /dev/null +++ b/library/src/scala/io/BufferedSource.scala @@ -0,0 +1,114 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.io + +import java.io.{ InputStream, BufferedReader, InputStreamReader, PushbackReader } +import Source.DefaultBufSize +import scala.collection.{ Iterator, AbstractIterator } +import scala.collection.mutable.StringBuilder + +/** This object provides convenience methods to create an iterable + * representation of a source file. + */ +class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val codec: Codec) extends Source { + def this(inputStream: InputStream)(implicit codec: Codec) = this(inputStream, DefaultBufSize)(codec) + def reader() = new InputStreamReader(inputStream, codec.decoder) + def bufferedReader() = new BufferedReader(reader(), bufferSize) + + // The same reader has to be shared between the iterators produced + // by iter and getLines. This is because calling hasNext can cause a + // block of data to be read from the stream, which will then be lost + // to getLines if it creates a new reader, even though next() was + // never called on the original. + private[this] var charReaderCreated = false + private[this] lazy val charReader = { + charReaderCreated = true + bufferedReader() + } + + override val iter = ( + Iterator + continually (codec wrap charReader.read()) + takeWhile (_ != -1) + map (_.toChar) + ) + + private def decachedReader: BufferedReader = { + // Don't want to lose a buffered char sitting in iter either. Yes, + // this is ridiculous, but if I can't get rid of Source, and all the + // Iterator bits are designed into Source, and people create Sources + // in the repl, and the repl calls toString for the result line, and + // that calls hasNext to find out if they're empty, and that leads + // to chars being buffered, and no, I don't work here, they left a + // door unlocked. + // To avoid inflicting this silliness indiscriminately, we can + // skip it if the char reader was never created: and almost always + // it will not have been created, since getLines will be called + // immediately on the source. + if (charReaderCreated && iter.hasNext) { + val pb = new PushbackReader(charReader) + pb unread iter.next().toInt + new BufferedReader(pb, bufferSize) + } + else charReader + } + + + class BufferedLineIterator extends AbstractIterator[String] with Iterator[String] { + private[this] val lineReader = decachedReader + var nextLine: String = null + + override def hasNext = { + if (nextLine == null) + nextLine = lineReader.readLine + + nextLine != null + } + override def next(): String = { + val result = { + if (nextLine == null) lineReader.readLine + else try nextLine finally nextLine = null + } + if (result == null) Iterator.empty.next() + else result + } + } + + override def getLines(): Iterator[String] = new BufferedLineIterator + + /** Efficiently appends the entire remaining input. + * + * Note: This function may temporarily load the entire buffer into + * memory. + */ + override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = + if (sep.isEmpty) { + val allReader = decachedReader + val buf = new Array[Char](bufferSize) + val jsb = sb.underlying + + if (start.length != 0) jsb.append(start) + var n = allReader.read(buf) + while (n != -1) { + jsb.append(buf, 0, n) + n = allReader.read(buf) + } + if (end.length != 0) jsb.append(end) + sb + // This case is expected to be uncommon, so we're reusing code at + // the cost of temporary memory allocations. + // mkString will callback into BufferedSource.addString to read + // the Buffer into a String, and then we use StringOps.addString + // for the interspersing of sep. + } else mkString.addString(sb, start, sep, end) +} diff --git a/library/src/scala/io/Codec.scala b/library/src/scala/io/Codec.scala new file mode 100644 index 000000000000..a6eeab50b299 --- /dev/null +++ b/library/src/scala/io/Codec.scala @@ -0,0 +1,136 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package io + +import java.nio.charset.{CharacterCodingException, Charset, CharsetDecoder, CharsetEncoder, CodingErrorAction => Action} +import java.nio.charset.StandardCharsets.{ISO_8859_1, UTF_8} +import scala.annotation.migration +import scala.language.implicitConversions + +// Some notes about encodings for use in refining this implementation. +// +// Emails: encoding recorded in header, e.g. Content-Type: charset= "iso-8859-1" +// HTML: optional content-type meta tag. +// +// XML: optional encoding parameter. +// +// +// MacRoman vs. UTF-8: see https://groups.google.com/d/msg/jruby-developers/-qtwRhoE1WM/whSPVpTNV28J +// -Dfile.encoding: see https://bugs.java.com/view_bug.do?bug_id=4375816 + +/** A class for character encoding/decoding preferences. + * + */ +class Codec(val charSet: Charset) { + type Configure[T] = (T => T, Boolean) + type Handler = CharacterCodingException => Int + + // these variables allow configuring the Codec object, and then + // all decoders and encoders retrieved from it will use these settings. + private[this] var _onMalformedInput: Action = null + private[this] var _onUnmappableCharacter: Action = null + private[this] var _encodingReplacement: Array[Byte] = null + private[this] var _decodingReplacement: String = null + private[this] var _onCodingException: Handler = e => throw e + + /** The name of the Codec. */ + override def toString = name + + // these methods can be chained to configure the variables above + def onMalformedInput(newAction: Action): this.type = { _onMalformedInput = newAction ; this } + def onUnmappableCharacter(newAction: Action): this.type = { _onUnmappableCharacter = newAction ; this } + def decodingReplaceWith(newReplacement: String): this.type = { _decodingReplacement = newReplacement ; this } + def encodingReplaceWith(newReplacement: Array[Byte]): this.type = { _encodingReplacement = newReplacement ; this } + def onCodingException(handler: Handler): this.type = { _onCodingException = handler ; this } + + def name = charSet.name + def encoder: CharsetEncoder = { + val enc = charSet.newEncoder() + if (_onMalformedInput ne null) enc onMalformedInput _onMalformedInput + if (_onUnmappableCharacter ne null) enc onUnmappableCharacter _onUnmappableCharacter + if (_encodingReplacement ne null) enc replaceWith _encodingReplacement + enc + } + def decoder: CharsetDecoder = { + val dec = charSet.newDecoder() + if (_onMalformedInput ne null) dec onMalformedInput _onMalformedInput + if (_onUnmappableCharacter ne null) dec onUnmappableCharacter _onUnmappableCharacter + if (_decodingReplacement ne null) dec replaceWith _decodingReplacement + dec + } + + def wrap(body: => Int): Int = + try body catch { case e: CharacterCodingException => _onCodingException(e) } +} + +trait LowPriorityCodecImplicits { + self: Codec.type => + + /** The Codec of Last Resort. */ + implicit lazy val fallbackSystemCodec: Codec = defaultCharsetCodec +} + +object Codec extends LowPriorityCodecImplicits { + final val ISO8859: Codec = Codec(ISO_8859_1) + final val UTF8: Codec = Codec(UTF_8) + + /** Optimistically these two possible defaults will be the same thing. + * In practice this is not necessarily true, and in fact Sun classifies + * the fact that you can influence anything at all via -Dfile.encoding + * as an accident, with any anomalies considered "not a bug". + */ + def defaultCharsetCodec: Codec = apply(Charset.defaultCharset) + def fileEncodingCodec: Codec = apply(scala.util.Properties.encodingString) + def default: Codec = defaultCharsetCodec + + def apply(encoding: String): Codec = new Codec(Charset forName encoding) + def apply(charSet: Charset): Codec = new Codec(charSet) + def apply(decoder: CharsetDecoder): Codec = { + val _decoder = decoder + new Codec(decoder.charset()) { override def decoder = _decoder } + } + + @migration("This method was previously misnamed `toUTF8`. Converts from Array[Byte] to Array[Char].", "2.9.0") + def fromUTF8(bytes: Array[Byte]): Array[Char] = fromUTF8(bytes, 0, bytes.length) + def fromUTF8(bytes: Array[Byte], offset: Int, len: Int): Array[Char] = { + val bbuffer = java.nio.ByteBuffer.wrap(bytes, offset, len) + val cbuffer = UTF8.charSet decode bbuffer + val chars = new Array[Char](cbuffer.remaining()) + cbuffer get chars + + chars + } + + @migration("This method was previously misnamed `fromUTF8`. Converts from character sequence to Array[Byte].", "2.9.0") + def toUTF8(cs: CharSequence): Array[Byte] = { + val cbuffer = java.nio.CharBuffer.wrap(cs, 0, cs.length) + val bbuffer = UTF8.charSet encode cbuffer + val bytes = new Array[Byte](bbuffer.remaining()) + bbuffer get bytes + + bytes + } + def toUTF8(chars: Array[Char], offset: Int, len: Int): Array[Byte] = { + val cbuffer = java.nio.CharBuffer.wrap(chars, offset, len) + val bbuffer = UTF8.charSet encode cbuffer + val bytes = new Array[Byte](bbuffer.remaining()) + bbuffer get bytes + + bytes + } + + implicit def string2codec(s: String): Codec = apply(s) + implicit def charset2codec(c: Charset): Codec = apply(c) + implicit def decoder2codec(cd: CharsetDecoder): Codec = apply(cd) +} diff --git a/library/src/scala/io/Position.scala b/library/src/scala/io/Position.scala new file mode 100644 index 000000000000..5a0a4aecdc31 --- /dev/null +++ b/library/src/scala/io/Position.scala @@ -0,0 +1,84 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package io + +import annotation.nowarn + +/** The object Position provides convenience methods to encode + * line and column number in one single integer. The encoded line + * (column) numbers range from 0 to `LINE_MASK` (`COLUMN_MASK`), + * where `0` indicates that the line (column) is undefined and + * `1` represents the first line (column). + * + * Line (Column) numbers greater than `LINE_MASK` (`COLUMN_MASK`) are + * replaced by `LINE_MASK` (`COLUMN_MASK`). Furthermore, if the encoded + * line number is `LINE_MASK`, the column number is always set to 0. + * + * The following properties hold: + * + * the undefined position is 0: `encode(0,0) == 0` + * encodings are non-negative : `encode(line,column) >= 0` + * position order is preserved: + * {{{ + * (line1 <= line2) || (line1 == line2 && column1 <= column2) + * }}} + * implies + * {{{ + * encode(line1,column1) <= encode(line2,column2) + * }}} + */ +@deprecated("this class will be removed", "2.10.0") +private[scala] abstract class Position { + /** Definable behavior for overflow conditions. + */ + def checkInput(line: Int, column: Int): Unit + + /** Number of bits used to encode the line number */ + final val LINE_BITS = 20 + /** Number of bits used to encode the column number */ + final val COLUMN_BITS = 31 - LINE_BITS // no negatives => 31 + /** Mask to decode the line number */ + final val LINE_MASK = (1 << LINE_BITS) - 1 + /** Mask to decode the column number */ + final val COLUMN_MASK = (1 << COLUMN_BITS) - 1 + + /** Encodes a position into a single integer. */ + final def encode(line: Int, column: Int): Int = { + checkInput(line, column) + + if (line >= LINE_MASK) + LINE_MASK << COLUMN_BITS + else + (line << COLUMN_BITS) | scala.math.min(COLUMN_MASK, column) + } + + /** Returns the line number of the encoded position. */ + final def line(pos: Int): Int = (pos >> COLUMN_BITS) & LINE_MASK + + /** Returns the column number of the encoded position. */ + final def column(pos: Int): Int = pos & COLUMN_MASK + + /** Returns a string representation of the encoded position. */ + def toString(pos: Int): String = line(pos) + ":" + column(pos) +} + +@nowarn +private[scala] object Position extends Position { + def checkInput(line: Int, column: Int): Unit = { + if (line < 0) + throw new IllegalArgumentException(s"$line < 0") + if (line == 0 && column != 0 || column < 0) + throw new IllegalArgumentException(s"$line,$column not allowed") + } +} diff --git a/library/src/scala/io/Source.scala b/library/src/scala/io/Source.scala new file mode 100644 index 000000000000..360c9fe0cf6d --- /dev/null +++ b/library/src/scala/io/Source.scala @@ -0,0 +1,381 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package io + +import scala.collection.{AbstractIterator, BufferedIterator} +import java.io.{Closeable, FileInputStream, FileNotFoundException, InputStream, PrintStream, File => JFile} +import java.net.{URI, URL} + +import scala.annotation.nowarn + +/** This object provides convenience methods to create an iterable + * representation of a source file. + */ +object Source { + val DefaultBufSize = 2048 + + /** Creates a `Source` from System.in. + */ + def stdin = fromInputStream(System.in) + + /** Creates a Source from an Iterable. + * + * @param iterable the Iterable + * @return the Source + */ + def fromIterable(iterable: Iterable[Char]): Source = new Source { + val iter = iterable.iterator + } withReset(() => fromIterable(iterable)) + + /** Creates a Source instance from a single character. + */ + def fromChar(c: Char): Source = fromIterable(Array(c)) + + /** creates Source from array of characters, with empty description. + */ + def fromChars(chars: Array[Char]): Source = fromIterable(chars) + + /** creates Source from a String, with no description. + */ + def fromString(s: String): Source = fromIterable(s) + + /** creates Source from file with given name, setting its description to + * filename. + */ + def fromFile(name: String)(implicit codec: Codec): BufferedSource = + fromFile(new JFile(name))(codec) + + /** creates Source from file with given name, using given encoding, setting + * its description to filename. + */ + def fromFile(name: String, enc: String): BufferedSource = + fromFile(name)(Codec(enc)) + + /** creates `source` from file with given file `URI`. + */ + def fromFile(uri: URI)(implicit codec: Codec): BufferedSource = + fromFile(new JFile(uri))(codec) + + /** creates Source from file with given file: URI + */ + def fromFile(uri: URI, enc: String): BufferedSource = + fromFile(uri)(Codec(enc)) + + /** creates Source from file, using default character encoding, setting its + * description to filename. + */ + def fromFile(file: JFile)(implicit codec: Codec): BufferedSource = + fromFile(file, Source.DefaultBufSize)(codec) + + /** same as fromFile(file, enc, Source.DefaultBufSize) + */ + def fromFile(file: JFile, enc: String): BufferedSource = + fromFile(file)(Codec(enc)) + + def fromFile(file: JFile, enc: String, bufferSize: Int): BufferedSource = + fromFile(file, bufferSize)(Codec(enc)) + + /** Creates Source from `file`, using given character encoding, setting + * its description to filename. Input is buffered in a buffer of size + * `bufferSize`. + */ + def fromFile(file: JFile, bufferSize: Int)(implicit codec: Codec): BufferedSource = { + val inputStream = new FileInputStream(file) + + createBufferedSource( + inputStream, + bufferSize, + () => fromFile(file, bufferSize)(codec), + () => inputStream.close() + )(codec) withDescription s"file:${file.getAbsolutePath}" + } + + /** Create a `Source` from array of bytes, decoding + * the bytes according to codec. + * + * @return the created `Source` instance. + */ + def fromBytes(bytes: Array[Byte])(implicit codec: Codec): Source = + fromString(new String(bytes, codec.name)) + + def fromBytes(bytes: Array[Byte], enc: String): Source = + fromBytes(bytes)(Codec(enc)) + + /** Create a `Source` from array of bytes, assuming + * one byte per character (ISO-8859-1 encoding.) + */ + @deprecated("Use `fromBytes` and specify an encoding", since="2.13.9") + def fromRawBytes(bytes: Array[Byte]): Source = + fromString(new String(bytes, Codec.ISO8859.charSet)) + + /** creates `Source` from file with given file: URI + */ + def fromURI(uri: URI)(implicit codec: Codec): BufferedSource = + fromFile(new JFile(uri))(codec) + + /** same as fromURL(new URL(s))(Codec(enc)) + */ + def fromURL(s: String, enc: String): BufferedSource = + fromURL(s)(Codec(enc)) + + /** same as fromURL(new URL(s)) + */ + def fromURL(s: String)(implicit codec: Codec): BufferedSource = + fromURL(new URI(s).toURL)(codec) + + /** same as fromInputStream(url.openStream())(Codec(enc)) + */ + def fromURL(url: URL, enc: String): BufferedSource = + fromURL(url)(Codec(enc)) + + /** same as fromInputStream(url.openStream())(codec) + */ + def fromURL(url: URL)(implicit codec: Codec): BufferedSource = + fromInputStream(url.openStream())(codec) + + /** Reads data from inputStream with a buffered reader, using the encoding + * in implicit parameter codec. + * + * @param inputStream the input stream from which to read + * @param bufferSize buffer size (defaults to Source.DefaultBufSize) + * @param reset a () => Source which resets the stream (if unset, reset() will throw an Exception) + * @param close a () => Unit method which closes the stream (if unset, close() will do nothing) + * @param codec (implicit) a scala.io.Codec specifying behavior (defaults to Codec.default) + * @return the buffered source + */ + def createBufferedSource( + inputStream: InputStream, + bufferSize: Int = DefaultBufSize, + reset: () => Source = null, + close: () => Unit = null + )(implicit codec: Codec): BufferedSource = { + // workaround for default arguments being unable to refer to other parameters + val resetFn = if (reset == null) () => createBufferedSource(inputStream, bufferSize, reset, close)(codec) else reset + + new BufferedSource(inputStream, bufferSize)(codec) withReset resetFn withClose close + } + + def fromInputStream(is: InputStream, enc: String): BufferedSource = + fromInputStream(is)(Codec(enc)) + + def fromInputStream(is: InputStream)(implicit codec: Codec): BufferedSource = + createBufferedSource(is, reset = () => fromInputStream(is)(codec), close = () => is.close())(codec) + + /** Reads data from a classpath resource, using either a context classloader (default) or a passed one. + * + * @param resource name of the resource to load from the classpath + * @param classLoader classloader to be used, or context classloader if not specified + * @return the buffered source + */ + def fromResource(resource: String, classLoader: ClassLoader = Thread.currentThread().getContextClassLoader())(implicit codec: Codec): BufferedSource = + Option(classLoader.getResourceAsStream(resource)) match { + case Some(in) => fromInputStream(in) + case None => throw new FileNotFoundException(s"resource '$resource' was not found in the classpath from the given classloader.") + } + +} + +/** An iterable representation of source data. + * It may be reset with the optional [[reset]] method. + * + * Subclasses must supply [[scala.io.Source.iter the underlying iterator]]. + * + * Error handling may be customized by overriding the [[scala.io.Source.report report]] method. + * + * The [[scala.io.Source.ch current input]] and [[scala.io.Source.pos position]], + * as well as the [[scala.io.Source.next next character]] methods delegate to + * [[scala.io.Source#Positioner the positioner]]. + * + * The default positioner encodes line and column numbers in the position passed to [[report]]. + * This behavior can be changed by supplying a + * [[scala.io.Source.withPositioning(pos:* custom positioner]]. + * + */ +abstract class Source extends Iterator[Char] with Closeable { + /** the actual iterator */ + protected val iter: Iterator[Char] + + // ------ public values + + /** description of this source, default empty */ + var descr: String = "" + var nerrors = 0 + var nwarnings = 0 + + private def lineNum(line: Int): String = (getLines() drop (line - 1) take 1).mkString + + class LineIterator extends AbstractIterator[String] with Iterator[String] { + private[this] val sb = new StringBuilder + + lazy val iter: BufferedIterator[Char] = Source.this.iter.buffered + def isNewline(ch: Char): Boolean = ch == '\r' || ch == '\n' + def getc(): Boolean = iter.hasNext && { + val ch = iter.next() + if (ch == '\n') false + else if (ch == '\r') { + if (iter.hasNext && iter.head == '\n') + iter.next() + + false + } + else { + sb append ch + true + } + } + def hasNext: Boolean = iter.hasNext + def next(): String = { + sb.clear() + while (getc()) { } + sb.toString + } + } + + /** Returns an iterator who returns lines (NOT including newline character(s)). + * It will treat any of \r\n, \r, or \n as a line separator (longest match) - if + * you need more refined behavior you can subclass Source#LineIterator directly. + */ + def getLines(): Iterator[String] = new LineIterator() + + /** Returns `'''true'''` if this source has more characters. + */ + def hasNext: Boolean = iter.hasNext + + /** Returns next character. + */ + def next(): Char = positioner.next() + + @nowarn("cat=deprecation") + class Positioner(encoder: Position) { + def this() = this(RelaxedPosition) + /** the last character returned by next. */ + var ch: Char = _ + + /** position of last character returned by next */ + var pos = 0 + + /** current line and column */ + var cline = 1 + var ccol = 1 + + /** default col increment for tabs '\t', set to 4 initially */ + var tabinc = 4 + + def next(): Char = { + ch = iter.next() + pos = encoder.encode(cline, ccol) + ch match { + case '\n' => + ccol = 1 + cline += 1 + case '\t' => + ccol += tabinc + case _ => + ccol += 1 + } + ch + } + } + /** A Position implementation which ignores errors in + * the positions. + */ + @nowarn("cat=deprecation") + object RelaxedPosition extends Position { + def checkInput(line: Int, column: Int): Unit = () + } + object RelaxedPositioner extends Positioner(RelaxedPosition) { } + object NoPositioner extends Positioner(Position) { + override def next(): Char = iter.next() + } + def ch: Char = positioner.ch + def pos: Int = positioner.pos + + /** Reports an error message to the output stream `out`. + * + * @param pos the source position (line/column) + * @param msg the error message to report + * @param out PrintStream to use (optional: defaults to `Console.err`) + */ + def reportError( + pos: Int, + msg: String, + out: PrintStream = Console.err): Unit = + { + nerrors += 1 + report(pos, msg, out) + } + + private def spaces(n: Int) = List.fill(n)(' ').mkString + /** + * @param pos the source position (line/column) + * @param msg the error message to report + * @param out PrintStream to use + */ + def report(pos: Int, msg: String, out: PrintStream): Unit = { + val line = Position line pos + val col = Position column pos + + out println "%s:%d:%d: %s%s%s^".format(descr, line, col, msg, lineNum(line), spaces(col - 1)) + } + + /** + * @param pos the source position (line/column) + * @param msg the warning message to report + * @param out PrintStream to use (optional: defaults to `Console.out`) + */ + def reportWarning( + pos: Int, + msg: String, + out: PrintStream = Console.out): Unit = + { + nwarnings += 1 + report(pos, "warning! " + msg, out) + } + + private[this] var resetFunction: () => Source = null + private[this] var closeFunction: () => Unit = null + private[this] var positioner: Positioner = RelaxedPositioner + + def withReset(f: () => Source): this.type = { + resetFunction = f + this + } + def withClose(f: () => Unit): this.type = { + closeFunction = f + this + } + def withDescription(text: String): this.type = { + descr = text + this + } + /** Change or disable the positioner. */ + def withPositioning(on: Boolean): this.type = { + positioner = if (on) RelaxedPositioner else NoPositioner + this + } + def withPositioning(pos: Positioner): this.type = { + positioner = pos + this + } + + /** The close() method closes the underlying resource. */ + def close(): Unit = { + if (closeFunction != null) closeFunction() + } + + /** The reset() method creates a fresh copy of this Source. */ + def reset(): Source = + if (resetFunction != null) resetFunction() + else throw new UnsupportedOperationException("Source's reset() method was not set.") +} diff --git a/library/src/scala/io/StdIn.scala b/library/src/scala/io/StdIn.scala new file mode 100644 index 000000000000..a39f99b4d689 --- /dev/null +++ b/library/src/scala/io/StdIn.scala @@ -0,0 +1,241 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package io + +import java.text.MessageFormat + +/** private[scala] because this is not functionality we should be providing + * in the standard library, at least not in this idiosyncratic form. + * Factored into trait because it is better code structure regardless. + */ +private[scala] trait StdIn { + import scala.Console._ + + /** Read a full line from the default input. Returns `null` if the end of the + * input stream has been reached. + * + * @return the string read from the terminal or null if the end of stream was reached. + */ + def readLine(): String = in.readLine() + + /** Print and flush formatted text to the default output, and read a full line from the default input. + * Returns `null` if the end of the input stream has been reached. + * + * @param text the format of the text to print out, as in `printf`. + * @param args the parameters used to instantiate the format, as in `printf`. + * @return the string read from the default input + */ + def readLine(text: String, args: Any*): String = { + printf(text, args: _*) + out.flush() + readLine() + } + + /** Reads a boolean value from an entire line of the default input. + * Has a fairly liberal interpretation of the input. + * + * @return the boolean value read, or false if it couldn't be converted to a boolean + * @throws java.io.EOFException if the end of the input stream has been reached. + */ + def readBoolean(): Boolean = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s.toLowerCase() match { + case "true" => true + case "t" => true + case "yes" => true + case "y" => true + case _ => false + } + } + + /** Reads a byte value from an entire line of the default input. + * + * @return the Byte that was read + * @throws java.io.EOFException if the end of the + * input stream has been reached. + * @throws java.lang.NumberFormatException if the value couldn't be converted to a Byte + */ + def readByte(): Byte = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s.toByte + } + + /** Reads a short value from an entire line of the default input. + * + * @return the short that was read + * @throws java.io.EOFException if the end of the + * input stream has been reached. + * @throws java.lang.NumberFormatException if the value couldn't be converted to a Short + */ + def readShort(): Short = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s.toShort + } + + /** Reads a char value from an entire line of the default input. + * + * @return the Char that was read + * @throws java.io.EOFException if the end of the + * input stream has been reached. + * @throws java.lang.StringIndexOutOfBoundsException if the line read from default input was empty + */ + def readChar(): Char = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s charAt 0 + } + + /** Reads an int value from an entire line of the default input. + * + * @return the Int that was read + * @throws java.io.EOFException if the end of the + * input stream has been reached. + * @throws java.lang.NumberFormatException if the value couldn't be converted to an Int + */ + def readInt(): Int = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s.toInt + } + + /** Reads an long value from an entire line of the default input. + * + * @return the Long that was read + * @throws java.io.EOFException if the end of the + * input stream has been reached. + * @throws java.lang.NumberFormatException if the value couldn't be converted to a Long + */ + def readLong(): Long = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s.toLong + } + + /** Reads a float value from an entire line of the default input. + * @return the Float that was read. + * @throws java.io.EOFException if the end of the + * input stream has been reached. + * @throws java.lang.NumberFormatException if the value couldn't be converted to a Float + * + */ + def readFloat(): Float = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s.toFloat + } + + /** Reads a double value from an entire line of the default input. + * + * @return the Double that was read. + * @throws java.io.EOFException if the end of the + * input stream has been reached. + * @throws java.lang.NumberFormatException if the value couldn't be converted to a Float + */ + def readDouble(): Double = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + s.toDouble + } + + /** Reads in some structured input (from the default input), specified by + * a format specifier. See class `java.text.MessageFormat` for details of + * the format specification. + * + * @param format the format of the input. + * @return a list of all extracted values. + * @throws java.io.EOFException if the end of the input stream has been + * reached. + */ + def readf(format: String): List[Any] = { + val s = readLine() + if (s == null) + throw new java.io.EOFException("Console has reached end of input") + else + textComponents(new MessageFormat(format).parse(s)) + } + + /** Reads in some structured input (from the default input), specified by + * a format specifier, returning only the first value extracted, according + * to the format specification. + * + * @param format format string, as accepted by `readf`. + * @return The first value that was extracted from the input + */ + def readf1(format: String): Any = readf(format).head + + /** Reads in some structured input (from the default input), specified + * by a format specifier, returning only the first two values extracted, + * according to the format specification. + * + * @param format format string, as accepted by `readf`. + * @return A [[scala.Tuple2]] containing the first two values extracted + */ + def readf2(format: String): (Any, Any) = { + val res = readf(format) + (res.head, res.tail.head) + } + + /** Reads in some structured input (from the default input), specified + * by a format specifier, returning only the first three values extracted, + * according to the format specification. + * + * @param format format string, as accepted by `readf`. + * @return A [[scala.Tuple3]] containing the first three values extracted + */ + def readf3(format: String): (Any, Any, Any) = { + val res = readf(format) + (res.head, res.tail.head, res.tail.tail.head) + } + + private def textComponents(a: Array[AnyRef]): List[Any] = { + var i: Int = a.length - 1 + var res: List[Any] = Nil + while (i >= 0) { + res = (a(i) match { + case x: java.lang.Boolean => x.booleanValue() + case x: java.lang.Byte => x.byteValue() + case x: java.lang.Short => x.shortValue() + case x: java.lang.Character => x.charValue() + case x: java.lang.Integer => x.intValue() + case x: java.lang.Long => x.longValue() + case x: java.lang.Float => x.floatValue() + case x: java.lang.Double => x.doubleValue() + case x => x + }) :: res + i -= 1 + } + res + } +} + +object StdIn extends StdIn From fe299a8fc7dd8c2d2eb62547b0269603fcdb1797 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:35:51 +0100 Subject: [PATCH 11/28] chore: add all the files in scala.jdk --- library/src/scala/jdk/Accumulator.scala | 404 ++++++ library/src/scala/jdk/AnyAccumulator.scala | 380 ++++++ .../src/scala/jdk/CollectionConverters.scala | 95 ++ library/src/scala/jdk/DoubleAccumulator.scala | 486 ++++++++ .../src/scala/jdk/DurationConverters.scala | 34 + .../src/scala/jdk/FunctionConverters.scala | 52 + .../src/scala/jdk/FunctionExtensions.scala | 220 ++++ library/src/scala/jdk/FunctionWrappers.scala | 1090 +++++++++++++++++ library/src/scala/jdk/FutureConverters.scala | 43 + library/src/scala/jdk/IntAccumulator.scala | 491 ++++++++ library/src/scala/jdk/LongAccumulator.scala | 486 ++++++++ library/src/scala/jdk/OptionConverters.scala | 111 ++ library/src/scala/jdk/OptionShape.scala | 67 + library/src/scala/jdk/StreamConverters.scala | 92 ++ .../jdk/javaapi/CollectionConverters.scala | 77 ++ .../jdk/javaapi/DurationConverters.scala | 73 ++ .../jdk/javaapi/FunctionConverters.scala | 956 +++++++++++++++ .../scala/jdk/javaapi/FutureConverters.scala | 89 ++ .../scala/jdk/javaapi/OptionConverters.scala | 84 ++ .../scala/jdk/javaapi/StreamConverters.scala | 356 ++++++ library/src/scala/jdk/package.scala | 46 + 21 files changed, 5732 insertions(+) create mode 100644 library/src/scala/jdk/Accumulator.scala create mode 100644 library/src/scala/jdk/AnyAccumulator.scala create mode 100644 library/src/scala/jdk/CollectionConverters.scala create mode 100644 library/src/scala/jdk/DoubleAccumulator.scala create mode 100644 library/src/scala/jdk/DurationConverters.scala create mode 100644 library/src/scala/jdk/FunctionConverters.scala create mode 100644 library/src/scala/jdk/FunctionExtensions.scala create mode 100644 library/src/scala/jdk/FunctionWrappers.scala create mode 100644 library/src/scala/jdk/FutureConverters.scala create mode 100644 library/src/scala/jdk/IntAccumulator.scala create mode 100644 library/src/scala/jdk/LongAccumulator.scala create mode 100644 library/src/scala/jdk/OptionConverters.scala create mode 100644 library/src/scala/jdk/OptionShape.scala create mode 100644 library/src/scala/jdk/StreamConverters.scala create mode 100644 library/src/scala/jdk/javaapi/CollectionConverters.scala create mode 100644 library/src/scala/jdk/javaapi/DurationConverters.scala create mode 100644 library/src/scala/jdk/javaapi/FunctionConverters.scala create mode 100644 library/src/scala/jdk/javaapi/FutureConverters.scala create mode 100644 library/src/scala/jdk/javaapi/OptionConverters.scala create mode 100644 library/src/scala/jdk/javaapi/StreamConverters.scala create mode 100644 library/src/scala/jdk/package.scala diff --git a/library/src/scala/jdk/Accumulator.scala b/library/src/scala/jdk/Accumulator.scala new file mode 100644 index 000000000000..0398a204b9a0 --- /dev/null +++ b/library/src/scala/jdk/Accumulator.scala @@ -0,0 +1,404 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.{lang => jl} + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{Stepper, StepperShape, mutable} +import scala.language.implicitConversions + +/** Accumulators are mutable sequences with two distinct features: + * - An accumulator can be appended efficiently to another + * - There are manually specialized Accumulators for `Int`, `Long` and `Double` that don't box + * the elements + * + * These two features make Accumulators a good candidate to collect the results of a parallel Java + * stream pipeline into a Scala collection. The + * [[scala.collection.convert.StreamExtensions.StreamHasToScala.toScala]] extension method on Java + * streams (available by importing + * [[scala.jdk.StreamConverters `scala.jdk.StreamConverters._`]]) is specialized for + * Accumulators: they are built in parallel, the parts are merged efficiently. + * + * Building specialized Accumulators is handled transparently. As a user, using the + * [[Accumulator]] object as a factory automatically creates an [[IntAccumulator]], + * [[LongAccumulator]], [[DoubleAccumulator]] or [[AnyAccumulator]] depending on the element type. + * + * Note: to run the example, start the Scala REPL with `scala -Yrepl-class-based` to avoid + * deadlocks, see [[https://github.com/scala/bug/issues/9076]]. + * + * {{{ + * scala> import scala.jdk.StreamConverters._ + * import scala.jdk.StreamConverters._ + * + * scala> def isPrime(n: Int): Boolean = !(2 +: (3 to Math.sqrt(n).toInt by 2) exists (n % _ == 0)) + * isPrime: (n: Int)Boolean + * + * scala> val intAcc = (1 to 10000).asJavaParStream.filter(isPrime).toScala(scala.jdk.Accumulator) + * intAcc: scala.jdk.IntAccumulator = IntAccumulator(1, 3, 5, 7, 11, 13, 17, 19, ... + * + * scala> val stringAcc = (1 to 100).asJavaParStream.mapToObj("<>" * _).toScala(Accumulator) + * stringAcc: scala.jdk.AnyAccumulator[String] = AnyAccumulator(<>, <><>, <><><>, ... + * }}} + * + * There are two possibilities to process elements of a primitive Accumulator without boxing: + * specialized operations of the Accumulator, or the Stepper interface. The most common collection + * operations are overloaded or overridden in the primitive Accumulator classes, for example + * [[IntAccumulator.map(f:Int=>Int)* IntAccumulator.map]] or [[IntAccumulator.exists]]. + * Thanks to Scala's function specialization, + * `intAcc.exists(x => testOn(x))` does not incur boxing. + * + * The [[scala.collection.Stepper]] interface provides iterator-like `hasStep` and `nextStep` methods, and is + * specialized for `Int`, `Long` and `Double`. The `intAccumulator.stepper` method creates an + * [[scala.collection.IntStepper]] that yields the elements of the accumulator without boxing. + * + * Accumulators can hold more than `Int.MaxValue` elements. They have a [[sizeLong]] method that + * returns the size as a `Long`. Note that certain operations defined in [[scala.collection.Seq]] + * are implemented using [[length]], so they will not work correctly for large accumulators. + * + * The [[Accumulator]] class is a base class to share code between [[AnyAccumulator]] (for + * reference types) and the manual specializations [[IntAccumulator]], [[LongAccumulator]] and + * [[DoubleAccumulator]]. + */ +abstract class Accumulator[@specialized(Double, Int, Long) A, +CC[X] <: mutable.Seq[X], +C <: mutable.Seq[A]] + extends mutable.Seq[A] + with mutable.Builder[A, C] { + + /** + * Implementation Details + * + * Every subclass has two arrays + * - `current: Array[A]` + * - `history: Array[Array[A]]` + * + * Elements are added to `current` at [[index]] until it's full, then `current` is added to `history` at [[hIndex]]. + * [[nextBlockSize]] defines the size of the next `current`. See also [[cumulative]]. + */ + private[jdk] var index: Int = 0 + private[jdk] var hIndex: Int = 0 + private[jdk] var totalSize: Long = 0L + + /** + * The total number of elements stored in the history up to `history(i)` (where `0 <= i < hIndex`). + * This method is constant-time, the cumulative lengths are stored. + * - [[AnyAccumulator]] keeps a separate array to store the cumulative lengths. + * - [[LongAccumulator]] and [[DoubleAccumulator]] store the cumulative length at the last slot in every + * array in the history. Every array is allocated with 1 extra slot for this purpose. [[DoubleAccumulator]] + * converts the length to double for storing and back to long, which is correct for lengths that fit in the + * double's 52 fraction bits (so any collection that fits in memory). + * - [[IntAccumulator]] uses the last two slots in every array to store the cumulative length, every array is + * allocated with 1 extra slot. So `history(0)` has 17 slots of which the first 15 store elements. + */ + private[jdk] def cumulative(i: Int): Long + + private[jdk] def nextBlockSize: Int = { + if (totalSize < 32) 16 + else if (totalSize <= Int.MaxValue) { + val bit = 64 - jl.Long.numberOfLeadingZeros(totalSize) + 1 << (bit - (bit >> 2)) + } + else 1 << 24 + } + + protected def efficientStepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit + + final override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = + efficientStepper(shape) + + final override def length: Int = + if (sizeLong < Int.MaxValue) sizeLong.toInt + else throw new IllegalArgumentException(s"Size too large for an Int: $sizeLong") + + final override def knownSize: Int = if (sizeLong < Int.MaxValue) size else -1 + + /** Size of the accumulated collection, as a `Long` */ + final def sizeLong: Long = totalSize + + /** Remove all accumulated elements from this accumulator. */ + def clear(): Unit = { + index = 0 + hIndex = 0 + totalSize = 0L + } + + private[jdk] def seekSlot(ix: Long): Long = { + var lo = -1 + var hi = hIndex + while (lo + 1 < hi) { + val m = (lo + hi) >>> 1 // Shift allows division-as-unsigned, prevents overflow + if (cumulative(m) > ix) hi = m + else lo = m + } + (hi.toLong << 32) | (if (hi==0) ix else ix - cumulative(hi-1)).toInt + } +} + +/** Contains factory methods to build Accumulators. + * + * Note that the `Accumulator` object itself is not a factory, but it is implicitly convert to + * a factory according to the element type, see [[Accumulator.toFactory]]. + * + * This allows passing the `Accumulator` object as argument when a [[collection.Factory]], and + * the implicit [[Accumulator.AccumulatorFactoryShape]] instance is used to build a specialized + * Accumulator according to the element type: + * + * {{{ + * scala> val intAcc = Accumulator(1,2,3) + * intAcc: scala.collection.convert.IntAccumulator = IntAccumulator(1, 2, 3) + * + * scala> val anyAccc = Accumulator("K") + * anyAccc: scala.collection.convert.AnyAccumulator[String] = AnyAccumulator(K) + * + * scala> val intAcc2 = List(1,2,3).to(Accumulator) + * intAcc2: scala.jdk.IntAccumulator = IntAccumulator(1, 2, 3) + * + * scala> val anyAcc2 = List("K").to(Accumulator) + * anyAcc2: scala.jdk.AnyAccumulator[String] = AnyAccumulator(K) + * }}} + * + * @define coll Accumulator + * @define Coll `Accumulator` + */ +object Accumulator { + implicit def toFactory[A, C](sa: Accumulator.type)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): collection.Factory[A, C] = canAccumulate.factory + + /** Creates a target $coll from an existing source collection + * + * @param source Source collection + * @tparam A the type of the ${coll}’s elements + * @tparam C the (inferred) specific type of the $coll + * @return a new $coll with the elements of `source` + */ + def from[A, C](source: IterableOnce[A])(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + source.iterator.to(canAccumulate.factory) + + /** An empty collection + * @tparam A the type of the ${coll}'s elements + */ + def empty[A, C](implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + canAccumulate.empty + + /** Creates an $coll with the specified elements. + * @tparam A the type of the ${coll}'s elements + * @tparam C the (inferred) specific type of the $coll + * @param elems the elements of the created $coll + * @return a new $coll with elements `elems` + */ + def apply[A, C](elems: A*)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + canAccumulate.factory.fromSpecific(elems) + + /** Produces an $coll containing repeated applications of a function to a start value. + * + * @param start the start value of the $coll + * @param len the number of elements contained in the $coll + * @param f the function that's repeatedly applied + * @return an $coll with `len` values in the sequence `start, f(start), f(f(start)), ...` + */ + def iterate[A, C](start: A, len: Int)(f: A => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + from(new collection.View.Iterate(start, len)(f)) + + /** Produces an $coll that uses a function `f` to produce elements of type `A` + * and update an internal state of type `S`. + * + * @param init State initial value + * @param f Computes the next element (or returns `None` to signal + * the end of the collection) + * @tparam A Type of the elements + * @tparam S Type of the internal state + * @tparam C Type (usually inferred) of the $coll + * @return an $coll that produces elements using `f` until `f` returns `None` + */ + def unfold[A, S, C](init: S)(f: S => Option[(A, S)])(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + from(new collection.View.Unfold(init)(f)) + + /** Produces an $coll containing a sequence of increasing of integers. + * + * @param start the first element of the $coll + * @param end the end value of the $coll (the first value NOT contained) + * @return an $coll with values `start, start + 1, ..., end - 1` + */ + def range[A: Integral, C](start: A, end: A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + from(collection.immutable.NumericRange(start, end, implicitly[Integral[A]].one)) + + /** Produces an $coll containing equally spaced values in some integer interval. + * @param start the start value of the $coll + * @param end the end value of the $coll (the first value NOT contained) + * @param step the difference between successive elements of the $coll (must be positive or negative) + * @return an $coll with values `start, start + step, ...` up to, but excluding `end` + */ + def range[A: Integral, C](start: A, end: A, step: A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + from(collection.immutable.NumericRange(start, end, step)) + + /** + * @return A builder for $Coll objects. + * @tparam A the type of the ${coll}’s elements + * @tparam C the specific type of the $coll + */ + def newBuilder[A, C](implicit canAccumulate: AccumulatorFactoryShape[A, C]): collection.mutable.Builder[A, C] = + canAccumulate.factory.newBuilder + + /** Produces an $coll containing the results of some element computation a number of times. + * @param n the number of elements contained in the $coll. + * @param elem the element computation + * @return An $coll that contains the results of `n` evaluations of `elem`. + */ + def fill[A, C](n: Int)(elem: => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + from(new collection.View.Fill(n)(elem)) + + /** Produces a two-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param elem the element computation + * @return An $coll that contains the results of `n1 x n2` evaluations of `elem`. + */ + def fill[A, C](n1: Int, n2: Int)(elem: => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[C] = + fill(n1)(fill(n2)(elem)(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[C]) + + /** Produces a three-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param elem the element computation + * @return An $coll that contains the results of `n1 x n2 x n3` evaluations of `elem`. + */ + def fill[A, C](n1: Int, n2: Int, n3: Int)(elem: => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[AnyAccumulator[C]] = + fill(n1)(fill(n2, n3)(elem)(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[AnyAccumulator[C]]) + + /** Produces a four-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param elem the element computation + * @return An $coll that contains the results of `n1 x n2 x n3 x n4` evaluations of `elem`. + */ + def fill[A, C](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[AnyAccumulator[AnyAccumulator[C]]] = + fill(n1)(fill(n2, n3, n4)(elem)(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[AnyAccumulator[AnyAccumulator[C]]]) + + /** Produces a five-dimensional $coll containing the results of some element computation a number of times. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param elem the element computation + * @return An $coll that contains the results of `n1 x n2 x n3 x n4 x n5` evaluations of `elem`. + */ + def fill[A, C](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[AnyAccumulator[AnyAccumulator[AnyAccumulator[C]]]] = + fill(n1)(fill(n2, n3, n4, n5)(elem)(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[AnyAccumulator[AnyAccumulator[AnyAccumulator[C]]]]) + + /** Produces an $coll containing values of a given function over a range of integer values starting from 0. + * @param n The number of elements in the $coll + * @param f The function computing element values + * @return An $coll consisting of elements `f(0), ..., f(n -1)` + */ + def tabulate[A, C](n: Int)(f: Int => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + from(new collection.View.Tabulate(n)(f)) + + /** Produces a two-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param f The function computing element values + * @return An $coll consisting of elements `f(i1, i2)` + * for `0 <= i1 < n1` and `0 <= i2 < n2`. + */ + def tabulate[A, C](n1: Int, n2: Int)(f: (Int, Int) => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[C] = + tabulate(n1)(i1 => tabulate(n2)(f(i1, _))(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[C]) + + /** Produces a three-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param f The function computing element values + * @return An $coll consisting of elements `f(i1, i2, i3)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, and `0 <= i3 < n3`. + */ + def tabulate[A, C](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[AnyAccumulator[C]] = + tabulate(n1)(i1 => tabulate(n2, n3)(f(i1, _, _))(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[AnyAccumulator[C]]) + + /** Produces a four-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param f The function computing element values + * @return An $coll consisting of elements `f(i1, i2, i3, i4)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, and `0 <= i4 < n4`. + */ + def tabulate[A, C](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[AnyAccumulator[AnyAccumulator[C]]] = + tabulate(n1)(i1 => tabulate(n2, n3, n4)(f(i1, _, _, _))(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[AnyAccumulator[AnyAccumulator[C]]]) + + /** Produces a five-dimensional $coll containing values of a given function over ranges of integer values starting from 0. + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param f The function computing element values + * @return An $coll consisting of elements `f(i1, i2, i3, i4, i5)` + * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, `0 <= i4 < n4`, and `0 <= i5 < n5`. + */ + def tabulate[A, C](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => A)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): AnyAccumulator[AnyAccumulator[AnyAccumulator[AnyAccumulator[C]]]] = + tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _))(canAccumulate))(AccumulatorFactoryShape.anyAccumulatorFactoryShape[AnyAccumulator[AnyAccumulator[AnyAccumulator[C]]]]) + + /** Concatenates all argument collections into a single $coll. + * + * @param xss the collections that are to be concatenated. + * @return the concatenation of all the collections. + */ + def concat[A, C](xss: Iterable[A]*)(implicit canAccumulate: AccumulatorFactoryShape[A, C]): C = + if (xss.isEmpty) canAccumulate.empty + else { + val b = canAccumulate.factory.newBuilder + xss.foreach(b ++= _) + b.result() + } + + /** An implicit `AccumulatorFactoryShape` is used in Accumulator factory method to return + * specialized variants according to the element type. + */ + sealed trait AccumulatorFactoryShape[A, C] { + def factory: collection.Factory[A, C] + def empty: C + } + + object AccumulatorFactoryShape extends LowPriorityAccumulatorFactoryShape { + implicit val doubleAccumulatorFactoryShape: AccumulatorFactoryShape[Double, DoubleAccumulator] = new AccumulatorFactoryShape[Double, DoubleAccumulator] { + def factory: collection.Factory[Double, DoubleAccumulator] = DoubleAccumulator + def empty: DoubleAccumulator = DoubleAccumulator.empty + } + + implicit val intAccumulatorFactoryShape: AccumulatorFactoryShape[Int, IntAccumulator] = new AccumulatorFactoryShape[Int, IntAccumulator] { + def factory: collection.Factory[Int, IntAccumulator] = IntAccumulator + def empty: IntAccumulator = IntAccumulator.empty + } + + implicit val longAccumulatorFactoryShape: AccumulatorFactoryShape[Long, LongAccumulator] = new AccumulatorFactoryShape[Long, LongAccumulator] { + def factory: collection.Factory[Long, LongAccumulator] = LongAccumulator + def empty: LongAccumulator = LongAccumulator.empty + } + + implicit val jDoubleAccumulatorFactoryShape: AccumulatorFactoryShape[jl.Double, DoubleAccumulator] = doubleAccumulatorFactoryShape.asInstanceOf[AccumulatorFactoryShape[jl.Double, DoubleAccumulator]] + implicit val jIntegerAccumulatorFactoryShape: AccumulatorFactoryShape[jl.Integer, IntAccumulator] = intAccumulatorFactoryShape.asInstanceOf[AccumulatorFactoryShape[jl.Integer, IntAccumulator]] + implicit val jLongAccumulatorFactoryShape: AccumulatorFactoryShape[jl.Long, LongAccumulator] = longAccumulatorFactoryShape.asInstanceOf[AccumulatorFactoryShape[jl.Long, LongAccumulator]] + } + + sealed trait LowPriorityAccumulatorFactoryShape { + implicit def anyAccumulatorFactoryShape[A]: AccumulatorFactoryShape[A, AnyAccumulator[A]] = anyAccumulatorFactoryShapePrototype.asInstanceOf[AccumulatorFactoryShape[A, AnyAccumulator[A]]] + + private val anyAccumulatorFactoryShapePrototype = new AccumulatorFactoryShape[AnyRef, AnyAccumulator[AnyRef]] { + def factory: collection.Factory[AnyRef, AnyAccumulator[AnyRef]] = collection.IterableFactory.toFactory(AnyAccumulator) + def empty: AnyAccumulator[AnyRef] = AnyAccumulator.empty[AnyRef] + } + } +} diff --git a/library/src/scala/jdk/AnyAccumulator.scala b/library/src/scala/jdk/AnyAccumulator.scala new file mode 100644 index 000000000000..fa952105fcca --- /dev/null +++ b/library/src/scala/jdk/AnyAccumulator.scala @@ -0,0 +1,380 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.io.{ObjectInputStream, ObjectOutputStream} +import java.util.Spliterator +import java.util.function.Consumer + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{AnyStepper, Factory, IterableFactoryDefaults, SeqFactory, Stepper, StepperShape, mutable} +import scala.reflect.ClassTag + +/** An Accumulator for arbitrary element types, see [[Accumulator]]. */ +final class AnyAccumulator[A] + extends Accumulator[A, AnyAccumulator, AnyAccumulator[A]] + with mutable.SeqOps[A, AnyAccumulator, AnyAccumulator[A]] + with IterableFactoryDefaults[A, AnyAccumulator] + with Serializable { + private[jdk] var current: Array[AnyRef] = AnyAccumulator.emptyAnyRefArray + private[jdk] var history: Array[Array[AnyRef]] = AnyAccumulator.emptyAnyRefArrayArray + private[jdk] var cumul: Array[Long] = AnyAccumulator.emptyLongArray + + private[jdk] def cumulative(i: Int): Long = cumul(i) + + override protected[this] def className: String = "AnyAccumulator" + + def efficientStepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = + shape.parUnbox(new AnyAccumulatorStepper[A](this.asInstanceOf[AnyAccumulator[A]])) + + private def expand(): Unit = { + if (index > 0) { + if (hIndex >= history.length) hExpand() + history(hIndex) = current + cumul(hIndex) = (if (hIndex > 0) cumulative(hIndex-1) else 0) + index + hIndex += 1 + } + current = new Array[AnyRef](nextBlockSize) + index = 0 + } + + private def hExpand(): Unit = { + if (hIndex == 0) { + history = new Array[Array[AnyRef]](4) + cumul = new Array[Long](4) + } + else { + history = java.util.Arrays.copyOf(history, history.length << 1) + cumul = java.util.Arrays.copyOf(cumul, cumul.length << 1) + } + } + + /** Appends an element to this `AnyAccumulator`. */ + def addOne(a: A): this.type = { + totalSize += 1 + if (index >= current.length) expand() + current(index) = a.asInstanceOf[AnyRef] + index += 1 + this + } + + /** Result collection consisting of all elements appended so far. */ + override def result(): AnyAccumulator[A] = this + + /** Removes all elements from `that` and appends them to this `AnyAccumulator`. */ + def drain[A1 <: A](that: AnyAccumulator[A1]): Unit = { + var h = 0 + var prev = 0L + var more = true + while (more && h < that.hIndex) { + val n = (that.cumulative(h) - prev).toInt + if (current.length - index >= n) { + System.arraycopy(that.history(h), 0, current, index, n) + prev = that.cumulative(h) + index += n + h += 1 + } + else more = false + } + if (h >= that.hIndex && current.length - index >= that.index) { + if (that.index > 0) System.arraycopy(that.current, 0, current, index, that.index) + index += that.index + } + else { + val slots = (if (index > 0) 1 else 0) + that.hIndex - h + if (hIndex + slots > history.length) { + val n = math.max(4, 1 << (32 - java.lang.Integer.numberOfLeadingZeros(1 + hIndex + slots))) + history = java.util.Arrays.copyOf(history, n) + cumul = java.util.Arrays.copyOf(cumul, n) + } + var pv = if (hIndex > 0) cumulative(hIndex-1) else 0L + if (index > 0) { + pv += index + cumul(hIndex) = pv + history(hIndex) = if (index < (current.length >>> 3) && current.length > 32) java.util.Arrays.copyOf(current, index) else current + hIndex += 1 + } + while (h < that.hIndex) { + pv += that.cumulative(h) - prev + prev = that.cumulative(h) + cumul(hIndex) = pv + history(hIndex) = that.history(h) + h += 1 + hIndex += 1 + } + index = that.index + current = that.current + } + totalSize += that.totalSize + that.clear() + } + + override def clear(): Unit = { + super.clear() + current = AnyAccumulator.emptyAnyRefArray + history = AnyAccumulator.emptyAnyRefArrayArray + cumul = AnyAccumulator.emptyLongArray + } + + /** Retrieves the `ix`th element. */ + def apply(ix: Long): A = { + if (totalSize - ix <= index || hIndex == 0) current((ix - (totalSize - index)).toInt).asInstanceOf[A] + else { + val w = seekSlot(ix) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt).asInstanceOf[A] + } + } + + /** Retrieves the `ix`th element, using an `Int` index. */ + def apply(i: Int): A = apply(i.toLong) + + def update(idx: Long, elem: A): Unit = { + if (totalSize - idx <= index || hIndex == 0) current((idx - (totalSize - index)).toInt) = elem.asInstanceOf[AnyRef] + else { + val w = seekSlot(idx) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt) = elem.asInstanceOf[AnyRef] + } + } + + def update(idx: Int, elem: A): Unit = update(idx.toLong, elem) + + /** Returns an `Iterator` over the contents of this `AnyAccumulator`. */ + def iterator: Iterator[A] = stepper.iterator + + def countLong(p: A => Boolean): Long = { + var r = 0L + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) r += 1 + r + } + + /** Copy the elements in this `AnyAccumulator` into an `Array` */ + override def toArray[B >: A : ClassTag]: Array[B] = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for an array: "+totalSize.toString) + val a = new Array[B](totalSize.toInt) + var j = 0 + var h = 0 + var pv = 0L + while (h < hIndex) { + val x = history(h) + val n = cumulative(h) - pv + pv = cumulative(h) + var i = 0 + while (i < n) { + a(j) = x(i).asInstanceOf[B] + i += 1 + j += 1 + } + h += 1 + } + var i = 0 + while (i < index) { + a(j) = current(i).asInstanceOf[B] + i += 1 + j += 1 + } + a + } + + /** Copies the elements in this `AnyAccumulator` to a `List` */ + override def toList: List[A] = { + var ans: List[A] = Nil + var i = index - 1 + while (i >= 0) { + ans = current(i).asInstanceOf[A] :: ans + i -= 1 + } + var h = hIndex - 1 + while (h >= 0) { + val a = history(h) + i = (cumulative(h) - (if (h == 0) 0L else cumulative(h-1))).toInt - 1 + while (i >= 0) { + ans = a(i).asInstanceOf[A] :: ans + i -= 1 + } + h -= 1 + } + ans + } + + /** + * Copy the elements in this `AnyAccumulator` to a specified collection. Example use: + * `acc.to(Vector)`. + */ + override def to[C1](factory: Factory[A, C1]): C1 = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for a Scala collection: "+totalSize.toString) + factory.fromSpecific(iterator) + } + + override def iterableFactory: SeqFactory[AnyAccumulator] = AnyAccumulator + + private def writeReplace(): AnyRef = new AnyAccumulator.SerializationProxy(this) +} + +object AnyAccumulator extends collection.SeqFactory[AnyAccumulator] { + private val emptyAnyRefArray = new Array[AnyRef](0) + private val emptyAnyRefArrayArray = new Array[Array[AnyRef]](0) + private val emptyLongArray = new Array[Long](0) + + import java.util.{function => jf} + + /** A `Supplier` of `AnyAccumulator`s, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def supplier[A]: jf.Supplier[AnyAccumulator[A]] = () => new AnyAccumulator[A] + + /** A `BiConsumer` that adds an element to an `AnyAccumulator`, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def adder[A]: jf.BiConsumer[AnyAccumulator[A], A] = (ac: AnyAccumulator[A], a: A) => ac addOne a + + /** A `BiConsumer` that adds an `Int` to an `AnyAccumulator`, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def unboxedIntAdder: jf.ObjIntConsumer[AnyAccumulator[Int]] = (ac: AnyAccumulator[Int], a: Int) => ac addOne a + + /** A `BiConsumer` that adds a `Long` to an `AnyAccumulator`, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def unboxedLongAdder: jf.ObjLongConsumer[AnyAccumulator[Long]] = (ac: AnyAccumulator[Long], a: Long) => ac addOne a + + /** A `BiConsumer` that adds a `Double` to an `AnyAccumulator`, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def unboxedDoubleAdder: jf.ObjDoubleConsumer[AnyAccumulator[Double]] = (ac: AnyAccumulator[Double], a: Double) => ac addOne a + + /** A `BiConsumer` that merges `AnyAccumulator`s, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def merger[A]: jf.BiConsumer[AnyAccumulator[A], AnyAccumulator[A]] = (a1: AnyAccumulator[A], a2: AnyAccumulator[A]) => a1 drain a2 + + def from[A](source: IterableOnce[A]): AnyAccumulator[A] = source match { + case acc: AnyAccumulator[A] => acc + case _ => new AnyAccumulator[A].addAll(source) + } + + def empty[A]: AnyAccumulator[A] = new AnyAccumulator[A] + + def newBuilder[A]: mutable.Builder[A, AnyAccumulator[A]] = new AnyAccumulator[A] + + class SerializationProxy[A](@transient private val acc: AnyAccumulator[A]) extends Serializable { + @transient private var result: AnyAccumulator[AnyRef] = _ + + private def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + val size = acc.sizeLong + out.writeLong(size) + val st = acc.stepper + while (st.hasStep) + out.writeObject(st.nextStep()) + } + + private def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val res = new AnyAccumulator[AnyRef]() + var elems = in.readLong() + while (elems > 0) { + res += in.readObject() + elems -= 1L + } + result = res + } + + private def readResolve(): AnyRef = result + } +} + +private[jdk] class AnyAccumulatorStepper[A](private[this] val acc: AnyAccumulator[A]) extends AnyStepper[A] with EfficientSplit { + import java.util.Spliterator._ + + private var h: Int = 0 + private var i: Int = 0 + private var a: Array[AnyRef] = if (acc.hIndex > 0) acc.history(0) else acc.current + private var n: Long = if (acc.hIndex > 0) acc.cumulative(0) else acc.index + private var N: Long = acc.totalSize + + private def duplicateSelf(limit: Long): AnyAccumulatorStepper[A] = { + val ans = new AnyAccumulatorStepper(acc) + ans.h = h + ans.i = i + ans.a = a + ans.n = n + ans.N = limit + ans + } + + private def loadMore(): Unit = { + h += 1 + if (h < acc.hIndex) { a = acc.history(h); n = acc.cumulative(h) - acc.cumulative(h-1) } + else { a = acc.current; n = acc.index } + i = 0 + } + + def characteristics: Int = ORDERED | SIZED | SUBSIZED + + def estimateSize: Long = N + + def hasStep: Boolean = N > 0 + + def nextStep(): A = + if (N <= 0) throw new NoSuchElementException("Next in empty Stepper") + else { + if (i >= n) loadMore() + val ans = a(i).asInstanceOf[A] + i += 1 + N -= 1 + ans + } + + def trySplit(): AnyStepper[A] = + if (N <= 1) null + else { + val half = N >> 1 + val M = (if (h <= 0) 0L else acc.cumulative(h-1)) + i + val R = M + half + val ans = duplicateSelf(half) + if (h < acc.hIndex) { + val w = acc.seekSlot(R) + h = (w >>> 32).toInt + if (h < acc.hIndex) { + a = acc.history(h) + n = acc.cumulative(h) - (if (h > 0) acc.cumulative(h-1) else 0) + } + else { + a = acc.current + n = acc.index + } + i = (w & 0xFFFFFFFFL).toInt + } + else i += half.toInt + N -= half + ans + } + + override def spliterator[B >: A]: Spliterator[B] = new AnyStepper.AnyStepperSpliterator[B](this) { + // Overridden for efficiency + override def tryAdvance(c: Consumer[_ >: B]): Boolean = { + if (N <= 0) false + else { + if (i >= n) loadMore() + c.accept(a(i).asInstanceOf[B]) + i += 1 + N -= 1 + true + } + } + + // Overridden for efficiency + override def forEachRemaining(f: java.util.function.Consumer[_ >: B]): Unit = { + while (N > 0) { + if (i >= n) loadMore() + val i0 = i + if ((n-i) > N) n = i + N.toInt + while (i < n) { + f.accept(a(i).asInstanceOf[B]) + i += 1 + } + N -= (n - i0) + } + } + } +} diff --git a/library/src/scala/jdk/CollectionConverters.scala b/library/src/scala/jdk/CollectionConverters.scala new file mode 100644 index 000000000000..a76dc2d5d010 --- /dev/null +++ b/library/src/scala/jdk/CollectionConverters.scala @@ -0,0 +1,95 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import scala.collection.convert.{AsJavaExtensions, AsScalaExtensions} + +/** This object provides extension methods that convert between Scala and Java collections. + * + * When writing Java code, use the explicit conversion methods defined in + * [[javaapi.CollectionConverters]] instead. + * + * Note: to create [[java.util.stream.Stream Java Streams]] that operate on Scala collections + * (sequentially or in parallel), use [[StreamConverters]]. + * + * {{{ + * import scala.jdk.CollectionConverters._ + * val s: java.util.Set[String] = Set("one", "two").asJava + * }}} + * + * The conversions return adapters for the corresponding API, i.e., the collections are wrapped, + * not converted. Changes to the original collection are reflected in the view, and vice versa: + * + * {{{ + * scala> import scala.jdk.CollectionConverters._ + * + * scala> val s = collection.mutable.Set("one") + * s: scala.collection.mutable.Set[String] = HashSet(one) + * + * scala> val js = s.asJava + * js: java.util.Set[String] = [one] + * + * scala> js.add("two") + * + * scala> s + * res2: scala.collection.mutable.Set[String] = HashSet(two, one) + * }}} + * + * The following conversions are supported via `asScala` and `asJava`: + * + * {{{ + * scala.collection.Iterable <=> java.lang.Iterable + * scala.collection.Iterator <=> java.util.Iterator + * scala.collection.mutable.Buffer <=> java.util.List + * scala.collection.mutable.Set <=> java.util.Set + * scala.collection.mutable.Map <=> java.util.Map + * scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap + * }}} + * + * The following conversions are supported via `asScala` and through + * specially-named extension methods to convert to Java collections, as shown: + * + * {{{ + * scala.collection.Iterable <=> java.util.Collection (via asJavaCollection) + * scala.collection.Iterator <=> java.util.Enumeration (via asJavaEnumeration) + * scala.collection.mutable.Map <=> java.util.Dictionary (via asJavaDictionary) + * }}} + * + * In addition, the following one-way conversions are provided via `asJava`: + * + * {{{ + * scala.collection.Seq => java.util.List + * scala.collection.mutable.Seq => java.util.List + * scala.collection.Set => java.util.Set + * scala.collection.Map => java.util.Map + * }}} + * + * The following one way conversion is provided via `asScala`: + * + * {{{ + * java.util.Properties => scala.collection.mutable.Map + * }}} + * + * In all cases, converting from a source type to a target type and back + * again will return the original source object. For example: + * + * {{{ + * import scala.jdk.CollectionConverters._ + * + * val source = new scala.collection.mutable.ListBuffer[Int] + * val target: java.util.List[Int] = source.asJava + * val other: scala.collection.mutable.Buffer[Int] = target.asScala + * assert(source eq other) + * }}} + */ +object CollectionConverters extends AsJavaExtensions with AsScalaExtensions diff --git a/library/src/scala/jdk/DoubleAccumulator.scala b/library/src/scala/jdk/DoubleAccumulator.scala new file mode 100644 index 000000000000..9f2d81c3282d --- /dev/null +++ b/library/src/scala/jdk/DoubleAccumulator.scala @@ -0,0 +1,486 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.io.{ObjectInputStream, ObjectOutputStream} +import java.util.Spliterator +import java.util.function.{Consumer, DoubleConsumer} +import java.{lang => jl} + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{AnyStepper, DoubleStepper, Factory, SeqFactory, Stepper, StepperShape, mutable} +import scala.language.implicitConversions + +/** A specialized Accumulator that holds `Double`s without boxing, see [[Accumulator]]. */ +final class DoubleAccumulator + extends Accumulator[Double, AnyAccumulator, DoubleAccumulator] + with mutable.SeqOps[Double, AnyAccumulator, DoubleAccumulator] + with Serializable { + private[jdk] var current: Array[Double] = DoubleAccumulator.emptyDoubleArray + private[jdk] var history: Array[Array[Double]] = DoubleAccumulator.emptyDoubleArrayArray + + private[jdk] def cumulative(i: Int) = { val x = history(i); x(x.length-1).toLong } + + override protected[this] def className: String = "DoubleAccumulator" + + def efficientStepper[S <: Stepper[_]](implicit shape: StepperShape[Double, S]): S with EfficientSplit = { + val st = new DoubleAccumulatorStepper(this) + val r = + if (shape.shape == StepperShape.DoubleShape) st + else { + assert(shape.shape == StepperShape.ReferenceShape, s"unexpected StepperShape: $shape") + AnyStepper.ofParDoubleStepper(st) + } + r.asInstanceOf[S with EfficientSplit] + } + + private def expand(): Unit = { + if (index > 0) { + current(current.length-1) = (if (hIndex > 0) { val x = history(hIndex-1); x(x.length-1) } else 0) + index + if (hIndex >= history.length) hExpand() + history(hIndex) = current + hIndex += 1 + } + current = new Array[Double](nextBlockSize+1) + index = 0 + } + + private def hExpand(): Unit = { + if (hIndex == 0) history = new Array[Array[Double]](4) + else history = java.util.Arrays.copyOf(history, history.length << 1) + } + + /** Appends an element to this `DoubleAccumulator`. */ + def addOne(a: Double): this.type = { + totalSize += 1 + if (index+1 >= current.length) expand() + current(index) = a + index += 1 + this + } + + /** Result collection consisting of all elements appended so far. */ + override def result(): DoubleAccumulator = this + + /** Removes all elements from `that` and appends them to this `DoubleAccumulator`. */ + def drain(that: DoubleAccumulator): Unit = { + var h = 0 + var prev = 0L + var more = true + while (more && h < that.hIndex) { + val cuml = that.cumulative(h) + val n = (cuml - prev).toInt + if (current.length - index - 1 >= n) { + System.arraycopy(that.history(h), 0, current, index, n) + prev = cuml + index += n + h += 1 + } + else more = false + } + if (h >= that.hIndex && current.length - index - 1>= that.index) { + if (that.index > 0) System.arraycopy(that.current, 0, current, index, that.index) + index += that.index + } + else { + val slots = (if (index > 0) 1 else 0) + that.hIndex - h + if (hIndex + slots > history.length) { + val n = math.max(4, 1 << (32 - jl.Integer.numberOfLeadingZeros(1 + hIndex + slots))) + history = java.util.Arrays.copyOf(history, n) + } + var pv = if (hIndex > 0) cumulative(hIndex-1) else 0L + if (index > 0) { + val x = + if (index < (current.length >>> 3) && current.length - 1 > 32) { + val ans = java.util.Arrays.copyOf(current, index + 1) + ans(ans.length - 1) = current(current.length - 1) + ans + } + else current + pv = pv + index + x(x.length - 1) = pv.toDouble // see comment on Accumulator.cumulative + history(hIndex) = x + hIndex += 1 + } + while (h < that.hIndex) { + val cuml = that.cumulative(h) + pv = pv + cuml - prev + prev = cuml + val x = that.history(h) + x(x.length - 1) = pv.toDouble // see comment on Accumulator.cumulative + history(hIndex) = x + h += 1 + hIndex += 1 + } + index = that.index + current = that.current + } + totalSize += that.totalSize + that.clear() + } + + override def clear(): Unit = { + super.clear() + current = DoubleAccumulator.emptyDoubleArray + history = DoubleAccumulator.emptyDoubleArrayArray + } + + /** Retrieves the `ix`th element. */ + def apply(ix: Long): Double = { + if (totalSize - ix <= index || hIndex == 0) current((ix - (totalSize - index)).toInt) + else { + val w = seekSlot(ix) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt) + } + } + + /** Retrieves the `ix`th element, using an `Int` index. */ + def apply(i: Int): Double = apply(i.toLong) + + def update(idx: Long, elem: Double): Unit = { + if (totalSize - idx <= index || hIndex == 0) current((idx - (totalSize - index)).toInt) = elem + else { + val w = seekSlot(idx) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt) = elem + } + } + + def update(idx: Int, elem: Double): Unit = update(idx.toLong, elem) + + /** Returns an `Iterator` over the contents of this `DoubleAccumulator`. The `Iterator` is not specialized. */ + def iterator: Iterator[Double] = stepper.iterator + + override def foreach[U](f: Double => U): Unit = { + val s = stepper + while (s.hasStep) f(s.nextStep()) + } + + def map(f: Double => Double): DoubleAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) + b.addOne(f(s.nextStep())) + b.result() + } + + def flatMap(f: Double => IterableOnce[Double]): DoubleAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) + b.addAll(f(s.nextStep())) + b.result() + } + + def collect(pf: PartialFunction[Double, Double]): DoubleAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) { + val n = s.nextStep() + pf.runWith(b.addOne)(n) + } + b.result() + } + + private def filterAccImpl(pred: Double => Boolean, not: Boolean): DoubleAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) { + val n = s.nextStep() + if (pred(n) != not) b.addOne(n) + } + b.result() + } + + override def filter(pred: Double => Boolean): DoubleAccumulator = filterAccImpl(pred, not = false) + + override def filterNot(pred: Double => Boolean): DoubleAccumulator = filterAccImpl(pred, not = true) + + override def forall(p: Double => Boolean): Boolean = { + val s = stepper + while (s.hasStep) + if (!p(s.nextStep())) return false + true + } + + override def exists(p: Double => Boolean): Boolean = { + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) return true + false + } + + override def count(p: Double => Boolean): Int = { + var r = 0 + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) r += 1 + r + } + + def countLong(p: Double => Boolean): Long = { + var r = 0L + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) r += 1 + r + } + + /** Copies the elements in this `DoubleAccumulator` into an `Array[Double]` */ + def toArray: Array[Double] = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for an array: "+totalSize.toString) + val a = new Array[Double](totalSize.toInt) + var j = 0 + var h = 0 + var pv = 0L + while (h < hIndex) { + val x = history(h) + val cuml = x(x.length-1).toLong + val n = (cuml - pv).toInt + pv = cuml + System.arraycopy(x, 0, a, j, n) + j += n + h += 1 + } + System.arraycopy(current, 0, a, j, index) + j += index + a + } + + /** Copies the elements in this `DoubleAccumulator` to a `List` */ + override def toList: List[Double] = { + var ans: List[Double] = Nil + var i = index - 1 + while (i >= 0) { + ans = current(i) :: ans + i -= 1 + } + var h = hIndex - 1 + while (h >= 0) { + val a = history(h) + i = (cumulative(h) - (if (h == 0) 0L else cumulative(h-1))).toInt - 1 + while (i >= 0) { + ans = a(i) :: ans + i -= 1 + } + h -= 1 + } + ans + } + + /** + * Copy the elements in this `DoubleAccumulator` to a specified collection. + * Note that the target collection is not specialized. + * Usage example: `acc.to(Vector)` + */ + override def to[C1](factory: Factory[Double, C1]): C1 = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for a Scala collection: "+totalSize.toString) + factory.fromSpecific(iterator) + } + + override protected def fromSpecific(coll: IterableOnce[Double]): DoubleAccumulator = DoubleAccumulator.fromSpecific(coll) + override protected def newSpecificBuilder: DoubleAccumulator = DoubleAccumulator.newBuilder + override def iterableFactory: SeqFactory[AnyAccumulator] = AnyAccumulator + + override def empty: DoubleAccumulator = DoubleAccumulator.empty + + private def writeReplace(): AnyRef = new DoubleAccumulator.SerializationProxy(this) +} + +object DoubleAccumulator extends collection.SpecificIterableFactory[Double, DoubleAccumulator] { + private val emptyDoubleArray = new Array[Double](0) + private val emptyDoubleArrayArray = new Array[Array[Double]](0) + + implicit def toJavaDoubleAccumulator(ia: DoubleAccumulator.type): collection.SpecificIterableFactory[jl.Double, DoubleAccumulator] = DoubleAccumulator.asInstanceOf[collection.SpecificIterableFactory[jl.Double, DoubleAccumulator]] + + import java.util.{function => jf} + + /** A `Supplier` of `DoubleAccumulator`s, suitable for use with `java.util.stream.DoubleStream`'s `collect` method. Suitable for `Stream[Double]` also. */ + def supplier: jf.Supplier[DoubleAccumulator] = () => new DoubleAccumulator + + /** A `BiConsumer` that adds an element to an `DoubleAccumulator`, suitable for use with `java.util.stream.DoubleStream`'s `collect` method. */ + def adder: jf.ObjDoubleConsumer[DoubleAccumulator] = (ac: DoubleAccumulator, a: Double) => ac addOne a + + /** A `BiConsumer` that adds a boxed `Double` to an `DoubleAccumulator`, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def boxedAdder: jf.BiConsumer[DoubleAccumulator, Double] = (ac: DoubleAccumulator, a: Double) => ac addOne a + + /** A `BiConsumer` that merges `DoubleAccumulator`s, suitable for use with `java.util.stream.DoubleStream`'s `collect` method. Suitable for `Stream[Double]` also. */ + def merger: jf.BiConsumer[DoubleAccumulator, DoubleAccumulator] = (a1: DoubleAccumulator, a2: DoubleAccumulator) => a1 drain a2 + + private def fromArray(a: Array[Double]): DoubleAccumulator = { + val r = new DoubleAccumulator + var i = 0 + while (i < a.length) { r addOne a(i); i += 1 } + r + } + + override def fromSpecific(it: IterableOnce[Double]): DoubleAccumulator = it match { + case acc: DoubleAccumulator => acc + case as: collection.immutable.ArraySeq.ofDouble => fromArray(as.unsafeArray) + case as: collection.mutable.ArraySeq.ofDouble => fromArray(as.array) // this case ensures Array(1).to(Accumulator) doesn't box + case _ => (new DoubleAccumulator).addAll(it) + } + + override def empty: DoubleAccumulator = new DoubleAccumulator + + override def newBuilder: DoubleAccumulator = new DoubleAccumulator + + class SerializationProxy[A](@transient private val acc: DoubleAccumulator) extends Serializable { + @transient private var result: DoubleAccumulator = _ + + private def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + val size = acc.sizeLong + out.writeLong(size) + val st = acc.stepper + while (st.hasStep) + out.writeDouble(st.nextStep()) + } + + private def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val res = new DoubleAccumulator() + var elems = in.readLong() + while (elems > 0) { + res += in.readDouble() + elems -= 1L + } + result = res + } + + private def readResolve(): AnyRef = result + } +} + +private[jdk] class DoubleAccumulatorStepper(private val acc: DoubleAccumulator) extends DoubleStepper with EfficientSplit { + import java.util.Spliterator._ + + private var h: Int = 0 + private var i: Int = 0 + private var a: Array[Double] = if (acc.hIndex > 0) acc.history(0) else acc.current + private var n: Long = if (acc.hIndex > 0) acc.cumulative(0) else acc.index + private var N: Long = acc.totalSize + + private def duplicateSelf(limit: Long): DoubleAccumulatorStepper = { + val ans = new DoubleAccumulatorStepper(acc) + ans.h = h + ans.i = i + ans.a = a + ans.n = n + ans.N = limit + ans + } + + private def loadMore(): Unit = { + h += 1 + if (h < acc.hIndex) { a = acc.history(h); n = acc.cumulative(h) - acc.cumulative(h-1) } + else { a = acc.current; n = acc.index } + i = 0 + } + + def characteristics: Int = ORDERED | SIZED | SUBSIZED | NONNULL + + def estimateSize: Long = N + + def hasStep: Boolean = N > 0 + + def nextStep(): Double = + if (n <= 0) throw new NoSuchElementException("next on empty Stepper") + else { + if (i >= n) loadMore() + val ans = a(i) + i += 1 + N -= 1 + ans + } + + def trySplit(): DoubleStepper = + if (N <= 1) null + else { + val half = N >> 1 + val M = (if (h <= 0) 0L else acc.cumulative(h-1)) + i + val R = M + half + val ans = duplicateSelf(half) + if (h < acc.hIndex) { + val w = acc.seekSlot(R) + h = (w >>> 32).toInt + if (h < acc.hIndex) { + a = acc.history(h) + n = acc.cumulative(h) - (if (h > 0) acc.cumulative(h-1) else 0) + } + else { + a = acc.current + n = acc.index + } + i = (w & 0xFFFFFFFFL).toInt + } + else i += half.toInt + N -= half + ans + } + + override def spliterator[B >: Double]: Spliterator.OfDouble = new DoubleStepper.DoubleStepperSpliterator(this) { + // Overridden for efficiency + override def tryAdvance(c: DoubleConsumer): Boolean = + if (N <= 0) false + else { + if (i >= n) loadMore() + c.accept(a(i)) + i += 1 + N -= 1 + true + } + + // Overridden for efficiency + override def tryAdvance(c: Consumer[_ >: jl.Double]): Boolean = (c: AnyRef) match { + case ic: DoubleConsumer => tryAdvance(ic) + case _ => + if (N <= 0) false + else { + if (i >= n) loadMore() + c.accept(a(i)) + i += 1 + N -= 1 + true + } + } + + // Overridden for efficiency + override def forEachRemaining(c: DoubleConsumer): Unit = + while (N > 0) { + if (i >= n) loadMore() + val i0 = i + if ((n-i) > N) n = i + N.toInt + while (i < n) { + c.accept(a(i)) + i += 1 + } + N -= (n - i0) + } + + // Overridden for efficiency + override def forEachRemaining(c: Consumer[_ >: jl.Double]): Unit = (c: AnyRef) match { + case ic: DoubleConsumer => forEachRemaining(ic) + case _ => + while (N > 0) { + if (i >= n) loadMore() + val i0 = i + if ((n-i) > N) n = i + N.toInt + while (i < n) { + c.accept(a(i)) + i += 1 + } + N -= (n - i0) + } + } + } +} diff --git a/library/src/scala/jdk/DurationConverters.scala b/library/src/scala/jdk/DurationConverters.scala new file mode 100644 index 000000000000..a98cd5b709d6 --- /dev/null +++ b/library/src/scala/jdk/DurationConverters.scala @@ -0,0 +1,34 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.time.{Duration => JDuration} + +import scala.concurrent.duration.FiniteDuration + +/** This object provides extension methods that convert between Scala and Java duration types. + * + * When writing Java code, use the explicit conversion methods defined in + * [[javaapi.DurationConverters]] instead. + */ +object DurationConverters { + implicit class JavaDurationOps(private val duration: JDuration) extends AnyVal { + /** Convert a Java duration to a Scala duration, see [[javaapi.DurationConverters.toScala]]. */ + def toScala: FiniteDuration = javaapi.DurationConverters.toScala(duration) + } + + implicit final class ScalaDurationOps(private val duration: FiniteDuration) extends AnyVal { + /** Convert a Scala duration to a Java duration, see [[javaapi.DurationConverters.toJava]]. */ + def toJava: JDuration = javaapi.DurationConverters.toJava(duration) + } +} diff --git a/library/src/scala/jdk/FunctionConverters.scala b/library/src/scala/jdk/FunctionConverters.scala new file mode 100644 index 000000000000..3c2d42564df0 --- /dev/null +++ b/library/src/scala/jdk/FunctionConverters.scala @@ -0,0 +1,52 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. + + +package scala.jdk + +/** This object provides extension methods that convert between Scala and Java function types. + * + * When writing Java code, use the explicit conversion methods defined in + * [[javaapi.FunctionConverters]] instead. + * + * Using the `.asJava` extension method on a Scala function produces the most specific possible + * Java function type: + * + * {{{ + * scala> import scala.jdk.FunctionConverters._ + * scala> val f = (x: Int) => x + 1 + * + * scala> val jf1 = f.asJava + * jf1: java.util.function.IntUnaryOperator = ... + * }}} + * + * More generic Java function types can be created using the corresponding `asJavaXYZ` extension + * method: + * + * {{{ + * scala> val jf2 = f.asJavaFunction + * jf2: java.util.function.Function[Int,Int] = ... + * + * scala> val jf3 = f.asJavaUnaryOperator + * jf3: java.util.function.UnaryOperator[Int] = ... + * }}} + * + * Converting a Java function to Scala is done using the `asScala` extension method: + * + * {{{ + * scala> List(1,2,3).map(jf2.asScala) + * res1: List[Int] = List(2, 3, 4) + * }}} + */ +object FunctionConverters extends Priority0FunctionExtensions diff --git a/library/src/scala/jdk/FunctionExtensions.scala b/library/src/scala/jdk/FunctionExtensions.scala new file mode 100644 index 000000000000..e932609e7af5 --- /dev/null +++ b/library/src/scala/jdk/FunctionExtensions.scala @@ -0,0 +1,220 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. + + +package scala.jdk + +import language.implicitConversions + +trait Priority3FunctionExtensions { + import FunctionWrappers._ + + @inline implicit def enrichAsJavaBiFunction[T, U, R](sf: scala.Function2[T, U, R]): RichFunction2AsBiFunction[T, U, R] = new RichFunction2AsBiFunction[T, U, R](sf) +} + + + +import language.implicitConversions + +trait Priority2FunctionExtensions extends Priority3FunctionExtensions { + import FunctionWrappers._ + + @inline implicit def enrichAsJavaBiConsumer[T, U](sf: scala.Function2[T, U, Unit]): RichFunction2AsBiConsumer[T, U] = new RichFunction2AsBiConsumer[T, U](sf) + + @inline implicit def enrichAsJavaBiPredicate[T, U](sf: scala.Function2[T, U, Boolean]): RichFunction2AsBiPredicate[T, U] = new RichFunction2AsBiPredicate[T, U](sf) + + @inline implicit def enrichAsJavaFunction[T, R](sf: scala.Function1[T, R]): RichFunction1AsFunction[T, R] = new RichFunction1AsFunction[T, R](sf) + + @inline implicit def enrichAsJavaToDoubleBiFunction[T, U](sf: scala.Function2[T, U, Double]): RichFunction2AsToDoubleBiFunction[T, U] = new RichFunction2AsToDoubleBiFunction[T, U](sf) + + @inline implicit def enrichAsJavaToIntBiFunction[T, U](sf: scala.Function2[T, U, Int]): RichFunction2AsToIntBiFunction[T, U] = new RichFunction2AsToIntBiFunction[T, U](sf) + + @inline implicit def enrichAsJavaToLongBiFunction[T, U](sf: scala.Function2[T, U, Long]): RichFunction2AsToLongBiFunction[T, U] = new RichFunction2AsToLongBiFunction[T, U](sf) +} + + + +import language.implicitConversions + +trait Priority1FunctionExtensions extends Priority2FunctionExtensions { + import FunctionWrappers._ + + @inline implicit def enrichAsJavaBinaryOperator[T, A1, A2](sf: scala.Function2[T, A1, A2])(implicit evA1: =:=[A1, T], evA2: =:=[A2, T]): RichFunction2AsBinaryOperator[T] = new RichFunction2AsBinaryOperator[T](sf.asInstanceOf[scala.Function2[T, T, T]]) + + @inline implicit def enrichAsJavaConsumer[T](sf: scala.Function1[T, Unit]): RichFunction1AsConsumer[T] = new RichFunction1AsConsumer[T](sf) + + @inline implicit def enrichAsJavaDoubleFunction[A0, R](sf: scala.Function1[A0, R])(implicit evA0: =:=[A0, Double]): RichFunction1AsDoubleFunction[R] = new RichFunction1AsDoubleFunction[R](sf.asInstanceOf[scala.Function1[Double, R]]) + + @inline implicit def enrichAsJavaIntFunction[A0, R](sf: scala.Function1[A0, R])(implicit evA0: =:=[A0, Int]): RichFunction1AsIntFunction[R] = new RichFunction1AsIntFunction[R](sf.asInstanceOf[scala.Function1[Int, R]]) + + @inline implicit def enrichAsJavaLongFunction[A0, R](sf: scala.Function1[A0, R])(implicit evA0: =:=[A0, Long]): RichFunction1AsLongFunction[R] = new RichFunction1AsLongFunction[R](sf.asInstanceOf[scala.Function1[Long, R]]) + + @inline implicit def enrichAsJavaObjDoubleConsumer[T, A1](sf: scala.Function2[T, A1, Unit])(implicit evA1: =:=[A1, Double]): RichFunction2AsObjDoubleConsumer[T] = new RichFunction2AsObjDoubleConsumer[T](sf.asInstanceOf[scala.Function2[T, Double, Unit]]) + + @inline implicit def enrichAsJavaObjIntConsumer[T, A1](sf: scala.Function2[T, A1, Unit])(implicit evA1: =:=[A1, Int]): RichFunction2AsObjIntConsumer[T] = new RichFunction2AsObjIntConsumer[T](sf.asInstanceOf[scala.Function2[T, Int, Unit]]) + + @inline implicit def enrichAsJavaObjLongConsumer[T, A1](sf: scala.Function2[T, A1, Unit])(implicit evA1: =:=[A1, Long]): RichFunction2AsObjLongConsumer[T] = new RichFunction2AsObjLongConsumer[T](sf.asInstanceOf[scala.Function2[T, Long, Unit]]) + + @inline implicit def enrichAsJavaPredicate[T](sf: scala.Function1[T, Boolean]): RichFunction1AsPredicate[T] = new RichFunction1AsPredicate[T](sf) + + @inline implicit def enrichAsJavaSupplier[T](sf: scala.Function0[T]): RichFunction0AsSupplier[T] = new RichFunction0AsSupplier[T](sf) + + @inline implicit def enrichAsJavaToDoubleFunction[T](sf: scala.Function1[T, Double]): RichFunction1AsToDoubleFunction[T] = new RichFunction1AsToDoubleFunction[T](sf) + + @inline implicit def enrichAsJavaToIntFunction[T](sf: scala.Function1[T, Int]): RichFunction1AsToIntFunction[T] = new RichFunction1AsToIntFunction[T](sf) + + @inline implicit def enrichAsJavaToLongFunction[T](sf: scala.Function1[T, Long]): RichFunction1AsToLongFunction[T] = new RichFunction1AsToLongFunction[T](sf) + + @inline implicit def enrichAsJavaUnaryOperator[T, A1](sf: scala.Function1[T, A1])(implicit evA1: =:=[A1, T]): RichFunction1AsUnaryOperator[T] = new RichFunction1AsUnaryOperator[T](sf.asInstanceOf[scala.Function1[T, T]]) +} + + + +import language.implicitConversions + +trait Priority0FunctionExtensions extends Priority1FunctionExtensions { + import FunctionWrappers._ + + @inline implicit def enrichAsJavaBooleanSupplier(sf: scala.Function0[Boolean]): RichFunction0AsBooleanSupplier = new RichFunction0AsBooleanSupplier(sf) + + @inline implicit def enrichAsJavaDoubleBinaryOperator[A0, A1](sf: scala.Function2[A0, A1, Double])(implicit evA0: =:=[A0, Double], evA1: =:=[A1, Double]): RichFunction2AsDoubleBinaryOperator = new RichFunction2AsDoubleBinaryOperator(sf.asInstanceOf[scala.Function2[Double, Double, Double]]) + + @inline implicit def enrichAsJavaDoubleConsumer[A0](sf: scala.Function1[A0, Unit])(implicit evA0: =:=[A0, Double]): RichFunction1AsDoubleConsumer = new RichFunction1AsDoubleConsumer(sf.asInstanceOf[scala.Function1[Double, Unit]]) + + @inline implicit def enrichAsJavaDoublePredicate[A0](sf: scala.Function1[A0, Boolean])(implicit evA0: =:=[A0, Double]): RichFunction1AsDoublePredicate = new RichFunction1AsDoublePredicate(sf.asInstanceOf[scala.Function1[Double, Boolean]]) + + @inline implicit def enrichAsJavaDoubleSupplier(sf: scala.Function0[Double]): RichFunction0AsDoubleSupplier = new RichFunction0AsDoubleSupplier(sf) + + @inline implicit def enrichAsJavaDoubleToIntFunction[A0](sf: scala.Function1[A0, Int])(implicit evA0: =:=[A0, Double]): RichFunction1AsDoubleToIntFunction = new RichFunction1AsDoubleToIntFunction(sf.asInstanceOf[scala.Function1[Double, Int]]) + + @inline implicit def enrichAsJavaDoubleToLongFunction[A0](sf: scala.Function1[A0, Long])(implicit evA0: =:=[A0, Double]): RichFunction1AsDoubleToLongFunction = new RichFunction1AsDoubleToLongFunction(sf.asInstanceOf[scala.Function1[Double, Long]]) + + @inline implicit def enrichAsJavaDoubleUnaryOperator[A0](sf: scala.Function1[A0, Double])(implicit evA0: =:=[A0, Double]): RichFunction1AsDoubleUnaryOperator = new RichFunction1AsDoubleUnaryOperator(sf.asInstanceOf[scala.Function1[Double, Double]]) + + @inline implicit def enrichAsJavaIntBinaryOperator[A0, A1](sf: scala.Function2[A0, A1, Int])(implicit evA0: =:=[A0, Int], evA1: =:=[A1, Int]): RichFunction2AsIntBinaryOperator = new RichFunction2AsIntBinaryOperator(sf.asInstanceOf[scala.Function2[Int, Int, Int]]) + + @inline implicit def enrichAsJavaIntConsumer[A0](sf: scala.Function1[A0, Unit])(implicit evA0: =:=[A0, Int]): RichFunction1AsIntConsumer = new RichFunction1AsIntConsumer(sf.asInstanceOf[scala.Function1[Int, Unit]]) + + @inline implicit def enrichAsJavaIntPredicate[A0](sf: scala.Function1[A0, Boolean])(implicit evA0: =:=[A0, Int]): RichFunction1AsIntPredicate = new RichFunction1AsIntPredicate(sf.asInstanceOf[scala.Function1[Int, Boolean]]) + + @inline implicit def enrichAsJavaIntSupplier(sf: scala.Function0[Int]): RichFunction0AsIntSupplier = new RichFunction0AsIntSupplier(sf) + + @inline implicit def enrichAsJavaIntToDoubleFunction[A0](sf: scala.Function1[A0, Double])(implicit evA0: =:=[A0, Int]): RichFunction1AsIntToDoubleFunction = new RichFunction1AsIntToDoubleFunction(sf.asInstanceOf[scala.Function1[Int, Double]]) + + @inline implicit def enrichAsJavaIntToLongFunction[A0](sf: scala.Function1[A0, Long])(implicit evA0: =:=[A0, Int]): RichFunction1AsIntToLongFunction = new RichFunction1AsIntToLongFunction(sf.asInstanceOf[scala.Function1[Int, Long]]) + + @inline implicit def enrichAsJavaIntUnaryOperator[A0](sf: scala.Function1[A0, Int])(implicit evA0: =:=[A0, Int]): RichFunction1AsIntUnaryOperator = new RichFunction1AsIntUnaryOperator(sf.asInstanceOf[scala.Function1[Int, Int]]) + + @inline implicit def enrichAsJavaLongBinaryOperator[A0, A1](sf: scala.Function2[A0, A1, Long])(implicit evA0: =:=[A0, Long], evA1: =:=[A1, Long]): RichFunction2AsLongBinaryOperator = new RichFunction2AsLongBinaryOperator(sf.asInstanceOf[scala.Function2[Long, Long, Long]]) + + @inline implicit def enrichAsJavaLongConsumer[A0](sf: scala.Function1[A0, Unit])(implicit evA0: =:=[A0, Long]): RichFunction1AsLongConsumer = new RichFunction1AsLongConsumer(sf.asInstanceOf[scala.Function1[Long, Unit]]) + + @inline implicit def enrichAsJavaLongPredicate[A0](sf: scala.Function1[A0, Boolean])(implicit evA0: =:=[A0, Long]): RichFunction1AsLongPredicate = new RichFunction1AsLongPredicate(sf.asInstanceOf[scala.Function1[Long, Boolean]]) + + @inline implicit def enrichAsJavaLongSupplier(sf: scala.Function0[Long]): RichFunction0AsLongSupplier = new RichFunction0AsLongSupplier(sf) + + @inline implicit def enrichAsJavaLongToDoubleFunction[A0](sf: scala.Function1[A0, Double])(implicit evA0: =:=[A0, Long]): RichFunction1AsLongToDoubleFunction = new RichFunction1AsLongToDoubleFunction(sf.asInstanceOf[scala.Function1[Long, Double]]) + + @inline implicit def enrichAsJavaLongToIntFunction[A0](sf: scala.Function1[A0, Int])(implicit evA0: =:=[A0, Long]): RichFunction1AsLongToIntFunction = new RichFunction1AsLongToIntFunction(sf.asInstanceOf[scala.Function1[Long, Int]]) + + @inline implicit def enrichAsJavaLongUnaryOperator[A0](sf: scala.Function1[A0, Long])(implicit evA0: =:=[A0, Long]): RichFunction1AsLongUnaryOperator = new RichFunction1AsLongUnaryOperator(sf.asInstanceOf[scala.Function1[Long, Long]]) + + + + @inline implicit def enrichAsScalaFromBiConsumer[T, U](jf: java.util.function.BiConsumer[T, U]): RichBiConsumerAsFunction2[T, U] = new RichBiConsumerAsFunction2[T, U](jf) + + @inline implicit def enrichAsScalaFromBiFunction[T, U, R](jf: java.util.function.BiFunction[T, U, R]): RichBiFunctionAsFunction2[T, U, R] = new RichBiFunctionAsFunction2[T, U, R](jf) + + @inline implicit def enrichAsScalaFromBiPredicate[T, U](jf: java.util.function.BiPredicate[T, U]): RichBiPredicateAsFunction2[T, U] = new RichBiPredicateAsFunction2[T, U](jf) + + @inline implicit def enrichAsScalaFromBinaryOperator[T](jf: java.util.function.BinaryOperator[T]): RichBinaryOperatorAsFunction2[T] = new RichBinaryOperatorAsFunction2[T](jf) + + @inline implicit def enrichAsScalaFromBooleanSupplier(jf: java.util.function.BooleanSupplier): RichBooleanSupplierAsFunction0 = new RichBooleanSupplierAsFunction0(jf) + + @inline implicit def enrichAsScalaFromConsumer[T](jf: java.util.function.Consumer[T]): RichConsumerAsFunction1[T] = new RichConsumerAsFunction1[T](jf) + + @inline implicit def enrichAsScalaFromDoubleBinaryOperator(jf: java.util.function.DoubleBinaryOperator): RichDoubleBinaryOperatorAsFunction2 = new RichDoubleBinaryOperatorAsFunction2(jf) + + @inline implicit def enrichAsScalaFromDoubleConsumer(jf: java.util.function.DoubleConsumer): RichDoubleConsumerAsFunction1 = new RichDoubleConsumerAsFunction1(jf) + + @inline implicit def enrichAsScalaFromDoubleFunction[R](jf: java.util.function.DoubleFunction[R]): RichDoubleFunctionAsFunction1[R] = new RichDoubleFunctionAsFunction1[R](jf) + + @inline implicit def enrichAsScalaFromDoublePredicate(jf: java.util.function.DoublePredicate): RichDoublePredicateAsFunction1 = new RichDoublePredicateAsFunction1(jf) + + @inline implicit def enrichAsScalaFromDoubleSupplier(jf: java.util.function.DoubleSupplier): RichDoubleSupplierAsFunction0 = new RichDoubleSupplierAsFunction0(jf) + + @inline implicit def enrichAsScalaFromDoubleToIntFunction(jf: java.util.function.DoubleToIntFunction): RichDoubleToIntFunctionAsFunction1 = new RichDoubleToIntFunctionAsFunction1(jf) + + @inline implicit def enrichAsScalaFromDoubleToLongFunction(jf: java.util.function.DoubleToLongFunction): RichDoubleToLongFunctionAsFunction1 = new RichDoubleToLongFunctionAsFunction1(jf) + + @inline implicit def enrichAsScalaFromDoubleUnaryOperator(jf: java.util.function.DoubleUnaryOperator): RichDoubleUnaryOperatorAsFunction1 = new RichDoubleUnaryOperatorAsFunction1(jf) + + @inline implicit def enrichAsScalaFromFunction[T, R](jf: java.util.function.Function[T, R]): RichFunctionAsFunction1[T, R] = new RichFunctionAsFunction1[T, R](jf) + + @inline implicit def enrichAsScalaFromIntBinaryOperator(jf: java.util.function.IntBinaryOperator): RichIntBinaryOperatorAsFunction2 = new RichIntBinaryOperatorAsFunction2(jf) + + @inline implicit def enrichAsScalaFromIntConsumer(jf: java.util.function.IntConsumer): RichIntConsumerAsFunction1 = new RichIntConsumerAsFunction1(jf) + + @inline implicit def enrichAsScalaFromIntFunction[R](jf: java.util.function.IntFunction[R]): RichIntFunctionAsFunction1[R] = new RichIntFunctionAsFunction1[R](jf) + + @inline implicit def enrichAsScalaFromIntPredicate(jf: java.util.function.IntPredicate): RichIntPredicateAsFunction1 = new RichIntPredicateAsFunction1(jf) + + @inline implicit def enrichAsScalaFromIntSupplier(jf: java.util.function.IntSupplier): RichIntSupplierAsFunction0 = new RichIntSupplierAsFunction0(jf) + + @inline implicit def enrichAsScalaFromIntToDoubleFunction(jf: java.util.function.IntToDoubleFunction): RichIntToDoubleFunctionAsFunction1 = new RichIntToDoubleFunctionAsFunction1(jf) + + @inline implicit def enrichAsScalaFromIntToLongFunction(jf: java.util.function.IntToLongFunction): RichIntToLongFunctionAsFunction1 = new RichIntToLongFunctionAsFunction1(jf) + + @inline implicit def enrichAsScalaFromIntUnaryOperator(jf: java.util.function.IntUnaryOperator): RichIntUnaryOperatorAsFunction1 = new RichIntUnaryOperatorAsFunction1(jf) + + @inline implicit def enrichAsScalaFromLongBinaryOperator(jf: java.util.function.LongBinaryOperator): RichLongBinaryOperatorAsFunction2 = new RichLongBinaryOperatorAsFunction2(jf) + + @inline implicit def enrichAsScalaFromLongConsumer(jf: java.util.function.LongConsumer): RichLongConsumerAsFunction1 = new RichLongConsumerAsFunction1(jf) + + @inline implicit def enrichAsScalaFromLongFunction[R](jf: java.util.function.LongFunction[R]): RichLongFunctionAsFunction1[R] = new RichLongFunctionAsFunction1[R](jf) + + @inline implicit def enrichAsScalaFromLongPredicate(jf: java.util.function.LongPredicate): RichLongPredicateAsFunction1 = new RichLongPredicateAsFunction1(jf) + + @inline implicit def enrichAsScalaFromLongSupplier(jf: java.util.function.LongSupplier): RichLongSupplierAsFunction0 = new RichLongSupplierAsFunction0(jf) + + @inline implicit def enrichAsScalaFromLongToDoubleFunction(jf: java.util.function.LongToDoubleFunction): RichLongToDoubleFunctionAsFunction1 = new RichLongToDoubleFunctionAsFunction1(jf) + + @inline implicit def enrichAsScalaFromLongToIntFunction(jf: java.util.function.LongToIntFunction): RichLongToIntFunctionAsFunction1 = new RichLongToIntFunctionAsFunction1(jf) + + @inline implicit def enrichAsScalaFromLongUnaryOperator(jf: java.util.function.LongUnaryOperator): RichLongUnaryOperatorAsFunction1 = new RichLongUnaryOperatorAsFunction1(jf) + + @inline implicit def enrichAsScalaFromObjDoubleConsumer[T](jf: java.util.function.ObjDoubleConsumer[T]): RichObjDoubleConsumerAsFunction2[T] = new RichObjDoubleConsumerAsFunction2[T](jf) + + @inline implicit def enrichAsScalaFromObjIntConsumer[T](jf: java.util.function.ObjIntConsumer[T]): RichObjIntConsumerAsFunction2[T] = new RichObjIntConsumerAsFunction2[T](jf) + + @inline implicit def enrichAsScalaFromObjLongConsumer[T](jf: java.util.function.ObjLongConsumer[T]): RichObjLongConsumerAsFunction2[T] = new RichObjLongConsumerAsFunction2[T](jf) + + @inline implicit def enrichAsScalaFromPredicate[T](jf: java.util.function.Predicate[T]): RichPredicateAsFunction1[T] = new RichPredicateAsFunction1[T](jf) + + @inline implicit def enrichAsScalaFromSupplier[T](jf: java.util.function.Supplier[T]): RichSupplierAsFunction0[T] = new RichSupplierAsFunction0[T](jf) + + @inline implicit def enrichAsScalaFromToDoubleBiFunction[T, U](jf: java.util.function.ToDoubleBiFunction[T, U]): RichToDoubleBiFunctionAsFunction2[T, U] = new RichToDoubleBiFunctionAsFunction2[T, U](jf) + + @inline implicit def enrichAsScalaFromToDoubleFunction[T](jf: java.util.function.ToDoubleFunction[T]): RichToDoubleFunctionAsFunction1[T] = new RichToDoubleFunctionAsFunction1[T](jf) + + @inline implicit def enrichAsScalaFromToIntBiFunction[T, U](jf: java.util.function.ToIntBiFunction[T, U]): RichToIntBiFunctionAsFunction2[T, U] = new RichToIntBiFunctionAsFunction2[T, U](jf) + + @inline implicit def enrichAsScalaFromToIntFunction[T](jf: java.util.function.ToIntFunction[T]): RichToIntFunctionAsFunction1[T] = new RichToIntFunctionAsFunction1[T](jf) + + @inline implicit def enrichAsScalaFromToLongBiFunction[T, U](jf: java.util.function.ToLongBiFunction[T, U]): RichToLongBiFunctionAsFunction2[T, U] = new RichToLongBiFunctionAsFunction2[T, U](jf) + + @inline implicit def enrichAsScalaFromToLongFunction[T](jf: java.util.function.ToLongFunction[T]): RichToLongFunctionAsFunction1[T] = new RichToLongFunctionAsFunction1[T](jf) + + @inline implicit def enrichAsScalaFromUnaryOperator[T](jf: java.util.function.UnaryOperator[T]): RichUnaryOperatorAsFunction1[T] = new RichUnaryOperatorAsFunction1[T](jf) +} diff --git a/library/src/scala/jdk/FunctionWrappers.scala b/library/src/scala/jdk/FunctionWrappers.scala new file mode 100644 index 000000000000..27153ffed820 --- /dev/null +++ b/library/src/scala/jdk/FunctionWrappers.scala @@ -0,0 +1,1090 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. + + +package scala.jdk + +object FunctionWrappers { + case class FromJavaBiConsumer[T, U](jf: java.util.function.BiConsumer[T, U]) extends scala.Function2[T, U, Unit] { + def apply(x1: T, x2: U) = jf.accept(x1, x2) + } + + class RichBiConsumerAsFunction2[T, U](private val underlying: java.util.function.BiConsumer[T, U]) extends AnyVal { + @inline def asScala: scala.Function2[T, U, Unit] = underlying match { + case AsJavaBiConsumer((sf @ _)) => sf.asInstanceOf[scala.Function2[T, U, Unit]] + case _ => new FromJavaBiConsumer[T, U](underlying) + } + } + + case class AsJavaBiConsumer[T, U](sf: scala.Function2[T, U, Unit]) extends java.util.function.BiConsumer[T, U] { + def accept(x1: T, x2: U) = sf.apply(x1, x2) + } + + class RichFunction2AsBiConsumer[T, U](private val underlying: scala.Function2[T, U, Unit]) extends AnyVal { + @inline def asJava: java.util.function.BiConsumer[T, U] = underlying match { + case FromJavaBiConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.BiConsumer[T, U]] + case _ => new AsJavaBiConsumer[T, U](underlying) + }; + @inline def asJavaBiConsumer: java.util.function.BiConsumer[T, U] = underlying match { + case FromJavaBiConsumer((sf @ _)) => sf.asInstanceOf[java.util.function.BiConsumer[T, U]] + case _ => new AsJavaBiConsumer[T, U](underlying) + } + } + + + case class FromJavaBiFunction[T, U, R](jf: java.util.function.BiFunction[T, U, R]) extends scala.Function2[T, U, R] { + def apply(x1: T, x2: U) = jf.apply(x1, x2) + } + + class RichBiFunctionAsFunction2[T, U, R](private val underlying: java.util.function.BiFunction[T, U, R]) extends AnyVal { + @inline def asScala: scala.Function2[T, U, R] = underlying match { + case AsJavaBiFunction((sf @ _)) => sf.asInstanceOf[scala.Function2[T, U, R]] + case _ => new FromJavaBiFunction[T, U, R](underlying) + } + } + + case class AsJavaBiFunction[T, U, R](sf: scala.Function2[T, U, R]) extends java.util.function.BiFunction[T, U, R] { + def apply(x1: T, x2: U) = sf.apply(x1, x2) + } + + class RichFunction2AsBiFunction[T, U, R](private val underlying: scala.Function2[T, U, R]) extends AnyVal { + @inline def asJava: java.util.function.BiFunction[T, U, R] = underlying match { + case FromJavaBiFunction((jf @ _)) => jf.asInstanceOf[java.util.function.BiFunction[T, U, R]] + case _ => new AsJavaBiFunction[T, U, R](underlying) + }; + @inline def asJavaBiFunction: java.util.function.BiFunction[T, U, R] = underlying match { + case FromJavaBiFunction((sf @ _)) => sf.asInstanceOf[java.util.function.BiFunction[T, U, R]] + case _ => new AsJavaBiFunction[T, U, R](underlying) + } + } + + + case class FromJavaBiPredicate[T, U](jf: java.util.function.BiPredicate[T, U]) extends scala.Function2[T, U, Boolean] { + def apply(x1: T, x2: U) = jf.test(x1, x2) + } + + class RichBiPredicateAsFunction2[T, U](private val underlying: java.util.function.BiPredicate[T, U]) extends AnyVal { + @inline def asScala: scala.Function2[T, U, Boolean] = underlying match { + case AsJavaBiPredicate((sf @ _)) => sf.asInstanceOf[scala.Function2[T, U, Boolean]] + case _ => new FromJavaBiPredicate[T, U](underlying) + } + } + + case class AsJavaBiPredicate[T, U](sf: scala.Function2[T, U, Boolean]) extends java.util.function.BiPredicate[T, U] { + def test(x1: T, x2: U) = sf.apply(x1, x2) + } + + class RichFunction2AsBiPredicate[T, U](private val underlying: scala.Function2[T, U, Boolean]) extends AnyVal { + @inline def asJava: java.util.function.BiPredicate[T, U] = underlying match { + case FromJavaBiPredicate((jf @ _)) => jf.asInstanceOf[java.util.function.BiPredicate[T, U]] + case _ => new AsJavaBiPredicate[T, U](underlying) + }; + @inline def asJavaBiPredicate: java.util.function.BiPredicate[T, U] = underlying match { + case FromJavaBiPredicate((sf @ _)) => sf.asInstanceOf[java.util.function.BiPredicate[T, U]] + case _ => new AsJavaBiPredicate[T, U](underlying) + } + } + + + case class FromJavaBinaryOperator[T](jf: java.util.function.BinaryOperator[T]) extends scala.Function2[T, T, T] { + def apply(x1: T, x2: T) = jf.apply(x1, x2) + } + + class RichBinaryOperatorAsFunction2[T](private val underlying: java.util.function.BinaryOperator[T]) extends AnyVal { + @inline def asScala: scala.Function2[T, T, T] = underlying match { + case AsJavaBinaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function2[T, T, T]] + case _ => new FromJavaBinaryOperator[T](underlying) + } + } + + case class AsJavaBinaryOperator[T](sf: scala.Function2[T, T, T]) extends java.util.function.BinaryOperator[T] { + def apply(x1: T, x2: T) = sf.apply(x1, x2) + } + + class RichFunction2AsBinaryOperator[T](private val underlying: scala.Function2[T, T, T]) extends AnyVal { + @inline def asJava: java.util.function.BinaryOperator[T] = underlying match { + case FromJavaBinaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.BinaryOperator[T]] + case _ => new AsJavaBinaryOperator[T](underlying) + }; + @inline def asJavaBinaryOperator: java.util.function.BinaryOperator[T] = underlying match { + case FromJavaBinaryOperator((sf @ _)) => sf.asInstanceOf[java.util.function.BinaryOperator[T]] + case _ => new AsJavaBinaryOperator[T](underlying) + } + } + + + case class FromJavaBooleanSupplier(jf: java.util.function.BooleanSupplier) extends scala.Function0[Boolean] { + def apply() = jf.getAsBoolean() + } + + class RichBooleanSupplierAsFunction0(private val underlying: java.util.function.BooleanSupplier) extends AnyVal { + @inline def asScala: scala.Function0[Boolean] = underlying match { + case AsJavaBooleanSupplier((sf @ _)) => sf.asInstanceOf[scala.Function0[Boolean]] + case _ => new FromJavaBooleanSupplier(underlying) + } + } + + case class AsJavaBooleanSupplier(sf: scala.Function0[Boolean]) extends java.util.function.BooleanSupplier { + def getAsBoolean() = sf.apply() + } + + class RichFunction0AsBooleanSupplier(private val underlying: scala.Function0[Boolean]) extends AnyVal { + @inline def asJava: java.util.function.BooleanSupplier = underlying match { + case FromJavaBooleanSupplier((jf @ _)) => jf.asInstanceOf[java.util.function.BooleanSupplier] + case _ => new AsJavaBooleanSupplier(underlying) + } + } + + + case class FromJavaConsumer[T](jf: java.util.function.Consumer[T]) extends scala.Function1[T, Unit] { + def apply(x1: T) = jf.accept(x1) + } + + class RichConsumerAsFunction1[T](private val underlying: java.util.function.Consumer[T]) extends AnyVal { + @inline def asScala: scala.Function1[T, Unit] = underlying match { + case AsJavaConsumer((sf @ _)) => sf.asInstanceOf[scala.Function1[T, Unit]] + case _ => new FromJavaConsumer[T](underlying) + } + } + + case class AsJavaConsumer[T](sf: scala.Function1[T, Unit]) extends java.util.function.Consumer[T] { + def accept(x1: T) = sf.apply(x1) + } + + class RichFunction1AsConsumer[T](private val underlying: scala.Function1[T, Unit]) extends AnyVal { + @inline def asJava: java.util.function.Consumer[T] = underlying match { + case FromJavaConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.Consumer[T]] + case _ => new AsJavaConsumer[T](underlying) + }; + @inline def asJavaConsumer: java.util.function.Consumer[T] = underlying match { + case FromJavaConsumer((sf @ _)) => sf.asInstanceOf[java.util.function.Consumer[T]] + case _ => new AsJavaConsumer[T](underlying) + } + } + + + case class FromJavaDoubleBinaryOperator(jf: java.util.function.DoubleBinaryOperator) extends scala.Function2[Double, Double, Double] { + def apply(x1: scala.Double, x2: scala.Double) = jf.applyAsDouble(x1, x2) + } + + class RichDoubleBinaryOperatorAsFunction2(private val underlying: java.util.function.DoubleBinaryOperator) extends AnyVal { + @inline def asScala: scala.Function2[Double, Double, Double] = underlying match { + case AsJavaDoubleBinaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function2[Double, Double, Double]] + case _ => new FromJavaDoubleBinaryOperator(underlying) + } + } + + case class AsJavaDoubleBinaryOperator(sf: scala.Function2[Double, Double, Double]) extends java.util.function.DoubleBinaryOperator { + def applyAsDouble(x1: scala.Double, x2: scala.Double) = sf.apply(x1, x2) + } + + class RichFunction2AsDoubleBinaryOperator(private val underlying: scala.Function2[Double, Double, Double]) extends AnyVal { + @inline def asJava: java.util.function.DoubleBinaryOperator = underlying match { + case FromJavaDoubleBinaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.DoubleBinaryOperator] + case _ => new AsJavaDoubleBinaryOperator(underlying) + } + } + + + case class FromJavaDoubleConsumer(jf: java.util.function.DoubleConsumer) extends scala.Function1[Double, Unit] { + def apply(x1: scala.Double) = jf.accept(x1) + } + + class RichDoubleConsumerAsFunction1(private val underlying: java.util.function.DoubleConsumer) extends AnyVal { + @inline def asScala: scala.Function1[Double, Unit] = underlying match { + case AsJavaDoubleConsumer((sf @ _)) => sf.asInstanceOf[scala.Function1[Double, Unit]] + case _ => new FromJavaDoubleConsumer(underlying) + } + } + + case class AsJavaDoubleConsumer(sf: scala.Function1[Double, Unit]) extends java.util.function.DoubleConsumer { + def accept(x1: scala.Double) = sf.apply(x1) + } + + class RichFunction1AsDoubleConsumer(private val underlying: scala.Function1[Double, Unit]) extends AnyVal { + @inline def asJava: java.util.function.DoubleConsumer = underlying match { + case FromJavaDoubleConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.DoubleConsumer] + case _ => new AsJavaDoubleConsumer(underlying) + } + } + + + case class FromJavaDoubleFunction[R](jf: java.util.function.DoubleFunction[R]) extends scala.Function1[Double, R] { + def apply(x1: scala.Double) = jf.apply(x1) + } + + class RichDoubleFunctionAsFunction1[R](private val underlying: java.util.function.DoubleFunction[R]) extends AnyVal { + @inline def asScala: scala.Function1[Double, R] = underlying match { + case AsJavaDoubleFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Double, R]] + case _ => new FromJavaDoubleFunction[R](underlying) + } + } + + case class AsJavaDoubleFunction[R](sf: scala.Function1[Double, R]) extends java.util.function.DoubleFunction[R] { + def apply(x1: scala.Double) = sf.apply(x1) + } + + class RichFunction1AsDoubleFunction[R](private val underlying: scala.Function1[Double, R]) extends AnyVal { + @inline def asJava: java.util.function.DoubleFunction[R] = underlying match { + case FromJavaDoubleFunction((jf @ _)) => jf.asInstanceOf[java.util.function.DoubleFunction[R]] + case _ => new AsJavaDoubleFunction[R](underlying) + }; + @inline def asJavaDoubleFunction: java.util.function.DoubleFunction[R] = underlying match { + case FromJavaDoubleFunction((sf @ _)) => sf.asInstanceOf[java.util.function.DoubleFunction[R]] + case _ => new AsJavaDoubleFunction[R](underlying) + } + } + + + case class FromJavaDoublePredicate(jf: java.util.function.DoublePredicate) extends scala.Function1[Double, Boolean] { + def apply(x1: scala.Double) = jf.test(x1) + } + + class RichDoublePredicateAsFunction1(private val underlying: java.util.function.DoublePredicate) extends AnyVal { + @inline def asScala: scala.Function1[Double, Boolean] = underlying match { + case AsJavaDoublePredicate((sf @ _)) => sf.asInstanceOf[scala.Function1[Double, Boolean]] + case _ => new FromJavaDoublePredicate(underlying) + } + } + + case class AsJavaDoublePredicate(sf: scala.Function1[Double, Boolean]) extends java.util.function.DoublePredicate { + def test(x1: scala.Double) = sf.apply(x1) + } + + class RichFunction1AsDoublePredicate(private val underlying: scala.Function1[Double, Boolean]) extends AnyVal { + @inline def asJava: java.util.function.DoublePredicate = underlying match { + case FromJavaDoublePredicate((jf @ _)) => jf.asInstanceOf[java.util.function.DoublePredicate] + case _ => new AsJavaDoublePredicate(underlying) + } + } + + + case class FromJavaDoubleSupplier(jf: java.util.function.DoubleSupplier) extends scala.Function0[Double] { + def apply() = jf.getAsDouble() + } + + class RichDoubleSupplierAsFunction0(private val underlying: java.util.function.DoubleSupplier) extends AnyVal { + @inline def asScala: scala.Function0[Double] = underlying match { + case AsJavaDoubleSupplier((sf @ _)) => sf.asInstanceOf[scala.Function0[Double]] + case _ => new FromJavaDoubleSupplier(underlying) + } + } + + case class AsJavaDoubleSupplier(sf: scala.Function0[Double]) extends java.util.function.DoubleSupplier { + def getAsDouble() = sf.apply() + } + + class RichFunction0AsDoubleSupplier(private val underlying: scala.Function0[Double]) extends AnyVal { + @inline def asJava: java.util.function.DoubleSupplier = underlying match { + case FromJavaDoubleSupplier((jf @ _)) => jf.asInstanceOf[java.util.function.DoubleSupplier] + case _ => new AsJavaDoubleSupplier(underlying) + } + } + + + case class FromJavaDoubleToIntFunction(jf: java.util.function.DoubleToIntFunction) extends scala.Function1[Double, Int] { + def apply(x1: scala.Double) = jf.applyAsInt(x1) + } + + class RichDoubleToIntFunctionAsFunction1(private val underlying: java.util.function.DoubleToIntFunction) extends AnyVal { + @inline def asScala: scala.Function1[Double, Int] = underlying match { + case AsJavaDoubleToIntFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Double, Int]] + case _ => new FromJavaDoubleToIntFunction(underlying) + } + } + + case class AsJavaDoubleToIntFunction(sf: scala.Function1[Double, Int]) extends java.util.function.DoubleToIntFunction { + def applyAsInt(x1: scala.Double) = sf.apply(x1) + } + + class RichFunction1AsDoubleToIntFunction(private val underlying: scala.Function1[Double, Int]) extends AnyVal { + @inline def asJava: java.util.function.DoubleToIntFunction = underlying match { + case FromJavaDoubleToIntFunction((jf @ _)) => jf.asInstanceOf[java.util.function.DoubleToIntFunction] + case _ => new AsJavaDoubleToIntFunction(underlying) + } + } + + + case class FromJavaDoubleToLongFunction(jf: java.util.function.DoubleToLongFunction) extends scala.Function1[Double, Long] { + def apply(x1: scala.Double) = jf.applyAsLong(x1) + } + + class RichDoubleToLongFunctionAsFunction1(private val underlying: java.util.function.DoubleToLongFunction) extends AnyVal { + @inline def asScala: scala.Function1[Double, Long] = underlying match { + case AsJavaDoubleToLongFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Double, Long]] + case _ => new FromJavaDoubleToLongFunction(underlying) + } + } + + case class AsJavaDoubleToLongFunction(sf: scala.Function1[Double, Long]) extends java.util.function.DoubleToLongFunction { + def applyAsLong(x1: scala.Double) = sf.apply(x1) + } + + class RichFunction1AsDoubleToLongFunction(private val underlying: scala.Function1[Double, Long]) extends AnyVal { + @inline def asJava: java.util.function.DoubleToLongFunction = underlying match { + case FromJavaDoubleToLongFunction((jf @ _)) => jf.asInstanceOf[java.util.function.DoubleToLongFunction] + case _ => new AsJavaDoubleToLongFunction(underlying) + } + } + + + case class FromJavaDoubleUnaryOperator(jf: java.util.function.DoubleUnaryOperator) extends scala.Function1[Double, Double] { + def apply(x1: scala.Double) = jf.applyAsDouble(x1) + } + + class RichDoubleUnaryOperatorAsFunction1(private val underlying: java.util.function.DoubleUnaryOperator) extends AnyVal { + @inline def asScala: scala.Function1[Double, Double] = underlying match { + case AsJavaDoubleUnaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function1[Double, Double]] + case _ => new FromJavaDoubleUnaryOperator(underlying) + } + } + + case class AsJavaDoubleUnaryOperator(sf: scala.Function1[Double, Double]) extends java.util.function.DoubleUnaryOperator { + def applyAsDouble(x1: scala.Double) = sf.apply(x1) + } + + class RichFunction1AsDoubleUnaryOperator(private val underlying: scala.Function1[Double, Double]) extends AnyVal { + @inline def asJava: java.util.function.DoubleUnaryOperator = underlying match { + case FromJavaDoubleUnaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.DoubleUnaryOperator] + case _ => new AsJavaDoubleUnaryOperator(underlying) + } + } + + + case class FromJavaFunction[T, R](jf: java.util.function.Function[T, R]) extends scala.Function1[T, R] { + def apply(x1: T) = jf.apply(x1) + } + + class RichFunctionAsFunction1[T, R](private val underlying: java.util.function.Function[T, R]) extends AnyVal { + @inline def asScala: scala.Function1[T, R] = underlying match { + case AsJavaFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[T, R]] + case _ => new FromJavaFunction[T, R](underlying) + } + } + + case class AsJavaFunction[T, R](sf: scala.Function1[T, R]) extends java.util.function.Function[T, R] { + def apply(x1: T) = sf.apply(x1) + } + + class RichFunction1AsFunction[T, R](private val underlying: scala.Function1[T, R]) extends AnyVal { + @inline def asJava: java.util.function.Function[T, R] = underlying match { + case FromJavaFunction((jf @ _)) => jf.asInstanceOf[java.util.function.Function[T, R]] + case _ => new AsJavaFunction[T, R](underlying) + }; + @inline def asJavaFunction: java.util.function.Function[T, R] = underlying match { + case FromJavaFunction((sf @ _)) => sf.asInstanceOf[java.util.function.Function[T, R]] + case _ => new AsJavaFunction[T, R](underlying) + } + } + + + case class FromJavaIntBinaryOperator(jf: java.util.function.IntBinaryOperator) extends scala.Function2[Int, Int, Int] { + def apply(x1: scala.Int, x2: scala.Int) = jf.applyAsInt(x1, x2) + } + + class RichIntBinaryOperatorAsFunction2(private val underlying: java.util.function.IntBinaryOperator) extends AnyVal { + @inline def asScala: scala.Function2[Int, Int, Int] = underlying match { + case AsJavaIntBinaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function2[Int, Int, Int]] + case _ => new FromJavaIntBinaryOperator(underlying) + } + } + + case class AsJavaIntBinaryOperator(sf: scala.Function2[Int, Int, Int]) extends java.util.function.IntBinaryOperator { + def applyAsInt(x1: scala.Int, x2: scala.Int) = sf.apply(x1, x2) + } + + class RichFunction2AsIntBinaryOperator(private val underlying: scala.Function2[Int, Int, Int]) extends AnyVal { + @inline def asJava: java.util.function.IntBinaryOperator = underlying match { + case FromJavaIntBinaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.IntBinaryOperator] + case _ => new AsJavaIntBinaryOperator(underlying) + } + } + + + case class FromJavaIntConsumer(jf: java.util.function.IntConsumer) extends scala.Function1[Int, Unit] { + def apply(x1: scala.Int) = jf.accept(x1) + } + + class RichIntConsumerAsFunction1(private val underlying: java.util.function.IntConsumer) extends AnyVal { + @inline def asScala: scala.Function1[Int, Unit] = underlying match { + case AsJavaIntConsumer((sf @ _)) => sf.asInstanceOf[scala.Function1[Int, Unit]] + case _ => new FromJavaIntConsumer(underlying) + } + } + + case class AsJavaIntConsumer(sf: scala.Function1[Int, Unit]) extends java.util.function.IntConsumer { + def accept(x1: scala.Int) = sf.apply(x1) + } + + class RichFunction1AsIntConsumer(private val underlying: scala.Function1[Int, Unit]) extends AnyVal { + @inline def asJava: java.util.function.IntConsumer = underlying match { + case FromJavaIntConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.IntConsumer] + case _ => new AsJavaIntConsumer(underlying) + } + } + + + case class FromJavaIntFunction[R](jf: java.util.function.IntFunction[R]) extends scala.Function1[Int, R] { + def apply(x1: scala.Int) = jf.apply(x1) + } + + class RichIntFunctionAsFunction1[R](private val underlying: java.util.function.IntFunction[R]) extends AnyVal { + @inline def asScala: scala.Function1[Int, R] = underlying match { + case AsJavaIntFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Int, R]] + case _ => new FromJavaIntFunction[R](underlying) + } + } + + case class AsJavaIntFunction[R](sf: scala.Function1[Int, R]) extends java.util.function.IntFunction[R] { + def apply(x1: scala.Int) = sf.apply(x1) + } + + class RichFunction1AsIntFunction[R](private val underlying: scala.Function1[Int, R]) extends AnyVal { + @inline def asJava: java.util.function.IntFunction[R] = underlying match { + case FromJavaIntFunction((jf @ _)) => jf.asInstanceOf[java.util.function.IntFunction[R]] + case _ => new AsJavaIntFunction[R](underlying) + }; + @inline def asJavaIntFunction: java.util.function.IntFunction[R] = underlying match { + case FromJavaIntFunction((sf @ _)) => sf.asInstanceOf[java.util.function.IntFunction[R]] + case _ => new AsJavaIntFunction[R](underlying) + } + } + + + case class FromJavaIntPredicate(jf: java.util.function.IntPredicate) extends scala.Function1[Int, Boolean] { + def apply(x1: scala.Int) = jf.test(x1) + } + + class RichIntPredicateAsFunction1(private val underlying: java.util.function.IntPredicate) extends AnyVal { + @inline def asScala: scala.Function1[Int, Boolean] = underlying match { + case AsJavaIntPredicate((sf @ _)) => sf.asInstanceOf[scala.Function1[Int, Boolean]] + case _ => new FromJavaIntPredicate(underlying) + } + } + + case class AsJavaIntPredicate(sf: scala.Function1[Int, Boolean]) extends java.util.function.IntPredicate { + def test(x1: scala.Int) = sf.apply(x1) + } + + class RichFunction1AsIntPredicate(private val underlying: scala.Function1[Int, Boolean]) extends AnyVal { + @inline def asJava: java.util.function.IntPredicate = underlying match { + case FromJavaIntPredicate((jf @ _)) => jf.asInstanceOf[java.util.function.IntPredicate] + case _ => new AsJavaIntPredicate(underlying) + } + } + + + case class FromJavaIntSupplier(jf: java.util.function.IntSupplier) extends scala.Function0[Int] { + def apply() = jf.getAsInt() + } + + class RichIntSupplierAsFunction0(private val underlying: java.util.function.IntSupplier) extends AnyVal { + @inline def asScala: scala.Function0[Int] = underlying match { + case AsJavaIntSupplier((sf @ _)) => sf.asInstanceOf[scala.Function0[Int]] + case _ => new FromJavaIntSupplier(underlying) + } + } + + case class AsJavaIntSupplier(sf: scala.Function0[Int]) extends java.util.function.IntSupplier { + def getAsInt() = sf.apply() + } + + class RichFunction0AsIntSupplier(private val underlying: scala.Function0[Int]) extends AnyVal { + @inline def asJava: java.util.function.IntSupplier = underlying match { + case FromJavaIntSupplier((jf @ _)) => jf.asInstanceOf[java.util.function.IntSupplier] + case _ => new AsJavaIntSupplier(underlying) + } + } + + + case class FromJavaIntToDoubleFunction(jf: java.util.function.IntToDoubleFunction) extends scala.Function1[Int, Double] { + def apply(x1: scala.Int) = jf.applyAsDouble(x1) + } + + class RichIntToDoubleFunctionAsFunction1(private val underlying: java.util.function.IntToDoubleFunction) extends AnyVal { + @inline def asScala: scala.Function1[Int, Double] = underlying match { + case AsJavaIntToDoubleFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Int, Double]] + case _ => new FromJavaIntToDoubleFunction(underlying) + } + } + + case class AsJavaIntToDoubleFunction(sf: scala.Function1[Int, Double]) extends java.util.function.IntToDoubleFunction { + def applyAsDouble(x1: scala.Int) = sf.apply(x1) + } + + class RichFunction1AsIntToDoubleFunction(private val underlying: scala.Function1[Int, Double]) extends AnyVal { + @inline def asJava: java.util.function.IntToDoubleFunction = underlying match { + case FromJavaIntToDoubleFunction((jf @ _)) => jf.asInstanceOf[java.util.function.IntToDoubleFunction] + case _ => new AsJavaIntToDoubleFunction(underlying) + } + } + + + case class FromJavaIntToLongFunction(jf: java.util.function.IntToLongFunction) extends scala.Function1[Int, Long] { + def apply(x1: scala.Int) = jf.applyAsLong(x1) + } + + class RichIntToLongFunctionAsFunction1(private val underlying: java.util.function.IntToLongFunction) extends AnyVal { + @inline def asScala: scala.Function1[Int, Long] = underlying match { + case AsJavaIntToLongFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Int, Long]] + case _ => new FromJavaIntToLongFunction(underlying) + } + } + + case class AsJavaIntToLongFunction(sf: scala.Function1[Int, Long]) extends java.util.function.IntToLongFunction { + def applyAsLong(x1: scala.Int) = sf.apply(x1) + } + + class RichFunction1AsIntToLongFunction(private val underlying: scala.Function1[Int, Long]) extends AnyVal { + @inline def asJava: java.util.function.IntToLongFunction = underlying match { + case FromJavaIntToLongFunction((jf @ _)) => jf.asInstanceOf[java.util.function.IntToLongFunction] + case _ => new AsJavaIntToLongFunction(underlying) + } + } + + + case class FromJavaIntUnaryOperator(jf: java.util.function.IntUnaryOperator) extends scala.Function1[Int, Int] { + def apply(x1: scala.Int) = jf.applyAsInt(x1) + } + + class RichIntUnaryOperatorAsFunction1(private val underlying: java.util.function.IntUnaryOperator) extends AnyVal { + @inline def asScala: scala.Function1[Int, Int] = underlying match { + case AsJavaIntUnaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function1[Int, Int]] + case _ => new FromJavaIntUnaryOperator(underlying) + } + } + + case class AsJavaIntUnaryOperator(sf: scala.Function1[Int, Int]) extends java.util.function.IntUnaryOperator { + def applyAsInt(x1: scala.Int) = sf.apply(x1) + } + + class RichFunction1AsIntUnaryOperator(private val underlying: scala.Function1[Int, Int]) extends AnyVal { + @inline def asJava: java.util.function.IntUnaryOperator = underlying match { + case FromJavaIntUnaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.IntUnaryOperator] + case _ => new AsJavaIntUnaryOperator(underlying) + } + } + + + case class FromJavaLongBinaryOperator(jf: java.util.function.LongBinaryOperator) extends scala.Function2[Long, Long, Long] { + def apply(x1: scala.Long, x2: scala.Long) = jf.applyAsLong(x1, x2) + } + + class RichLongBinaryOperatorAsFunction2(private val underlying: java.util.function.LongBinaryOperator) extends AnyVal { + @inline def asScala: scala.Function2[Long, Long, Long] = underlying match { + case AsJavaLongBinaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function2[Long, Long, Long]] + case _ => new FromJavaLongBinaryOperator(underlying) + } + } + + case class AsJavaLongBinaryOperator(sf: scala.Function2[Long, Long, Long]) extends java.util.function.LongBinaryOperator { + def applyAsLong(x1: scala.Long, x2: scala.Long) = sf.apply(x1, x2) + } + + class RichFunction2AsLongBinaryOperator(private val underlying: scala.Function2[Long, Long, Long]) extends AnyVal { + @inline def asJava: java.util.function.LongBinaryOperator = underlying match { + case FromJavaLongBinaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.LongBinaryOperator] + case _ => new AsJavaLongBinaryOperator(underlying) + } + } + + + case class FromJavaLongConsumer(jf: java.util.function.LongConsumer) extends scala.Function1[Long, Unit] { + def apply(x1: scala.Long) = jf.accept(x1) + } + + class RichLongConsumerAsFunction1(private val underlying: java.util.function.LongConsumer) extends AnyVal { + @inline def asScala: scala.Function1[Long, Unit] = underlying match { + case AsJavaLongConsumer((sf @ _)) => sf.asInstanceOf[scala.Function1[Long, Unit]] + case _ => new FromJavaLongConsumer(underlying) + } + } + + case class AsJavaLongConsumer(sf: scala.Function1[Long, Unit]) extends java.util.function.LongConsumer { + def accept(x1: scala.Long) = sf.apply(x1) + } + + class RichFunction1AsLongConsumer(private val underlying: scala.Function1[Long, Unit]) extends AnyVal { + @inline def asJava: java.util.function.LongConsumer = underlying match { + case FromJavaLongConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.LongConsumer] + case _ => new AsJavaLongConsumer(underlying) + } + } + + + case class FromJavaLongFunction[R](jf: java.util.function.LongFunction[R]) extends scala.Function1[Long, R] { + def apply(x1: scala.Long) = jf.apply(x1) + } + + class RichLongFunctionAsFunction1[R](private val underlying: java.util.function.LongFunction[R]) extends AnyVal { + @inline def asScala: scala.Function1[Long, R] = underlying match { + case AsJavaLongFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Long, R]] + case _ => new FromJavaLongFunction[R](underlying) + } + } + + case class AsJavaLongFunction[R](sf: scala.Function1[Long, R]) extends java.util.function.LongFunction[R] { + def apply(x1: scala.Long) = sf.apply(x1) + } + + class RichFunction1AsLongFunction[R](private val underlying: scala.Function1[Long, R]) extends AnyVal { + @inline def asJava: java.util.function.LongFunction[R] = underlying match { + case FromJavaLongFunction((jf @ _)) => jf.asInstanceOf[java.util.function.LongFunction[R]] + case _ => new AsJavaLongFunction[R](underlying) + }; + @inline def asJavaLongFunction: java.util.function.LongFunction[R] = underlying match { + case FromJavaLongFunction((sf @ _)) => sf.asInstanceOf[java.util.function.LongFunction[R]] + case _ => new AsJavaLongFunction[R](underlying) + } + } + + + case class FromJavaLongPredicate(jf: java.util.function.LongPredicate) extends scala.Function1[Long, Boolean] { + def apply(x1: scala.Long) = jf.test(x1) + } + + class RichLongPredicateAsFunction1(private val underlying: java.util.function.LongPredicate) extends AnyVal { + @inline def asScala: scala.Function1[Long, Boolean] = underlying match { + case AsJavaLongPredicate((sf @ _)) => sf.asInstanceOf[scala.Function1[Long, Boolean]] + case _ => new FromJavaLongPredicate(underlying) + } + } + + case class AsJavaLongPredicate(sf: scala.Function1[Long, Boolean]) extends java.util.function.LongPredicate { + def test(x1: scala.Long) = sf.apply(x1) + } + + class RichFunction1AsLongPredicate(private val underlying: scala.Function1[Long, Boolean]) extends AnyVal { + @inline def asJava: java.util.function.LongPredicate = underlying match { + case FromJavaLongPredicate((jf @ _)) => jf.asInstanceOf[java.util.function.LongPredicate] + case _ => new AsJavaLongPredicate(underlying) + } + } + + + case class FromJavaLongSupplier(jf: java.util.function.LongSupplier) extends scala.Function0[Long] { + def apply() = jf.getAsLong() + } + + class RichLongSupplierAsFunction0(private val underlying: java.util.function.LongSupplier) extends AnyVal { + @inline def asScala: scala.Function0[Long] = underlying match { + case AsJavaLongSupplier((sf @ _)) => sf.asInstanceOf[scala.Function0[Long]] + case _ => new FromJavaLongSupplier(underlying) + } + } + + case class AsJavaLongSupplier(sf: scala.Function0[Long]) extends java.util.function.LongSupplier { + def getAsLong() = sf.apply() + } + + class RichFunction0AsLongSupplier(private val underlying: scala.Function0[Long]) extends AnyVal { + @inline def asJava: java.util.function.LongSupplier = underlying match { + case FromJavaLongSupplier((jf @ _)) => jf.asInstanceOf[java.util.function.LongSupplier] + case _ => new AsJavaLongSupplier(underlying) + } + } + + + case class FromJavaLongToDoubleFunction(jf: java.util.function.LongToDoubleFunction) extends scala.Function1[Long, Double] { + def apply(x1: scala.Long) = jf.applyAsDouble(x1) + } + + class RichLongToDoubleFunctionAsFunction1(private val underlying: java.util.function.LongToDoubleFunction) extends AnyVal { + @inline def asScala: scala.Function1[Long, Double] = underlying match { + case AsJavaLongToDoubleFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Long, Double]] + case _ => new FromJavaLongToDoubleFunction(underlying) + } + } + + case class AsJavaLongToDoubleFunction(sf: scala.Function1[Long, Double]) extends java.util.function.LongToDoubleFunction { + def applyAsDouble(x1: scala.Long) = sf.apply(x1) + } + + class RichFunction1AsLongToDoubleFunction(private val underlying: scala.Function1[Long, Double]) extends AnyVal { + @inline def asJava: java.util.function.LongToDoubleFunction = underlying match { + case FromJavaLongToDoubleFunction((jf @ _)) => jf.asInstanceOf[java.util.function.LongToDoubleFunction] + case _ => new AsJavaLongToDoubleFunction(underlying) + } + } + + + case class FromJavaLongToIntFunction(jf: java.util.function.LongToIntFunction) extends scala.Function1[Long, Int] { + def apply(x1: scala.Long) = jf.applyAsInt(x1) + } + + class RichLongToIntFunctionAsFunction1(private val underlying: java.util.function.LongToIntFunction) extends AnyVal { + @inline def asScala: scala.Function1[Long, Int] = underlying match { + case AsJavaLongToIntFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[Long, Int]] + case _ => new FromJavaLongToIntFunction(underlying) + } + } + + case class AsJavaLongToIntFunction(sf: scala.Function1[Long, Int]) extends java.util.function.LongToIntFunction { + def applyAsInt(x1: scala.Long) = sf.apply(x1) + } + + class RichFunction1AsLongToIntFunction(private val underlying: scala.Function1[Long, Int]) extends AnyVal { + @inline def asJava: java.util.function.LongToIntFunction = underlying match { + case FromJavaLongToIntFunction((jf @ _)) => jf.asInstanceOf[java.util.function.LongToIntFunction] + case _ => new AsJavaLongToIntFunction(underlying) + } + } + + + case class FromJavaLongUnaryOperator(jf: java.util.function.LongUnaryOperator) extends scala.Function1[Long, Long] { + def apply(x1: scala.Long) = jf.applyAsLong(x1) + } + + class RichLongUnaryOperatorAsFunction1(private val underlying: java.util.function.LongUnaryOperator) extends AnyVal { + @inline def asScala: scala.Function1[Long, Long] = underlying match { + case AsJavaLongUnaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function1[Long, Long]] + case _ => new FromJavaLongUnaryOperator(underlying) + } + } + + case class AsJavaLongUnaryOperator(sf: scala.Function1[Long, Long]) extends java.util.function.LongUnaryOperator { + def applyAsLong(x1: scala.Long) = sf.apply(x1) + } + + class RichFunction1AsLongUnaryOperator(private val underlying: scala.Function1[Long, Long]) extends AnyVal { + @inline def asJava: java.util.function.LongUnaryOperator = underlying match { + case FromJavaLongUnaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.LongUnaryOperator] + case _ => new AsJavaLongUnaryOperator(underlying) + } + } + + + case class FromJavaObjDoubleConsumer[T](jf: java.util.function.ObjDoubleConsumer[T]) extends scala.Function2[T, Double, Unit] { + def apply(x1: T, x2: scala.Double) = jf.accept(x1, x2) + } + + class RichObjDoubleConsumerAsFunction2[T](private val underlying: java.util.function.ObjDoubleConsumer[T]) extends AnyVal { + @inline def asScala: scala.Function2[T, Double, Unit] = underlying match { + case AsJavaObjDoubleConsumer((sf @ _)) => sf.asInstanceOf[scala.Function2[T, Double, Unit]] + case _ => new FromJavaObjDoubleConsumer[T](underlying) + } + } + + case class AsJavaObjDoubleConsumer[T](sf: scala.Function2[T, Double, Unit]) extends java.util.function.ObjDoubleConsumer[T] { + def accept(x1: T, x2: scala.Double) = sf.apply(x1, x2) + } + + class RichFunction2AsObjDoubleConsumer[T](private val underlying: scala.Function2[T, Double, Unit]) extends AnyVal { + @inline def asJava: java.util.function.ObjDoubleConsumer[T] = underlying match { + case FromJavaObjDoubleConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.ObjDoubleConsumer[T]] + case _ => new AsJavaObjDoubleConsumer[T](underlying) + }; + @inline def asJavaObjDoubleConsumer: java.util.function.ObjDoubleConsumer[T] = underlying match { + case FromJavaObjDoubleConsumer((sf @ _)) => sf.asInstanceOf[java.util.function.ObjDoubleConsumer[T]] + case _ => new AsJavaObjDoubleConsumer[T](underlying) + } + } + + + case class FromJavaObjIntConsumer[T](jf: java.util.function.ObjIntConsumer[T]) extends scala.Function2[T, Int, Unit] { + def apply(x1: T, x2: scala.Int) = jf.accept(x1, x2) + } + + class RichObjIntConsumerAsFunction2[T](private val underlying: java.util.function.ObjIntConsumer[T]) extends AnyVal { + @inline def asScala: scala.Function2[T, Int, Unit] = underlying match { + case AsJavaObjIntConsumer((sf @ _)) => sf.asInstanceOf[scala.Function2[T, Int, Unit]] + case _ => new FromJavaObjIntConsumer[T](underlying) + } + } + + case class AsJavaObjIntConsumer[T](sf: scala.Function2[T, Int, Unit]) extends java.util.function.ObjIntConsumer[T] { + def accept(x1: T, x2: scala.Int) = sf.apply(x1, x2) + } + + class RichFunction2AsObjIntConsumer[T](private val underlying: scala.Function2[T, Int, Unit]) extends AnyVal { + @inline def asJava: java.util.function.ObjIntConsumer[T] = underlying match { + case FromJavaObjIntConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.ObjIntConsumer[T]] + case _ => new AsJavaObjIntConsumer[T](underlying) + }; + @inline def asJavaObjIntConsumer: java.util.function.ObjIntConsumer[T] = underlying match { + case FromJavaObjIntConsumer((sf @ _)) => sf.asInstanceOf[java.util.function.ObjIntConsumer[T]] + case _ => new AsJavaObjIntConsumer[T](underlying) + } + } + + + case class FromJavaObjLongConsumer[T](jf: java.util.function.ObjLongConsumer[T]) extends scala.Function2[T, Long, Unit] { + def apply(x1: T, x2: scala.Long) = jf.accept(x1, x2) + } + + class RichObjLongConsumerAsFunction2[T](private val underlying: java.util.function.ObjLongConsumer[T]) extends AnyVal { + @inline def asScala: scala.Function2[T, Long, Unit] = underlying match { + case AsJavaObjLongConsumer((sf @ _)) => sf.asInstanceOf[scala.Function2[T, Long, Unit]] + case _ => new FromJavaObjLongConsumer[T](underlying) + } + } + + case class AsJavaObjLongConsumer[T](sf: scala.Function2[T, Long, Unit]) extends java.util.function.ObjLongConsumer[T] { + def accept(x1: T, x2: scala.Long) = sf.apply(x1, x2) + } + + class RichFunction2AsObjLongConsumer[T](private val underlying: scala.Function2[T, Long, Unit]) extends AnyVal { + @inline def asJava: java.util.function.ObjLongConsumer[T] = underlying match { + case FromJavaObjLongConsumer((jf @ _)) => jf.asInstanceOf[java.util.function.ObjLongConsumer[T]] + case _ => new AsJavaObjLongConsumer[T](underlying) + }; + @inline def asJavaObjLongConsumer: java.util.function.ObjLongConsumer[T] = underlying match { + case FromJavaObjLongConsumer((sf @ _)) => sf.asInstanceOf[java.util.function.ObjLongConsumer[T]] + case _ => new AsJavaObjLongConsumer[T](underlying) + } + } + + + case class FromJavaPredicate[T](jf: java.util.function.Predicate[T]) extends scala.Function1[T, Boolean] { + def apply(x1: T) = jf.test(x1) + } + + class RichPredicateAsFunction1[T](private val underlying: java.util.function.Predicate[T]) extends AnyVal { + @inline def asScala: scala.Function1[T, Boolean] = underlying match { + case AsJavaPredicate((sf @ _)) => sf.asInstanceOf[scala.Function1[T, Boolean]] + case _ => new FromJavaPredicate[T](underlying) + } + } + + case class AsJavaPredicate[T](sf: scala.Function1[T, Boolean]) extends java.util.function.Predicate[T] { + def test(x1: T) = sf.apply(x1) + } + + class RichFunction1AsPredicate[T](private val underlying: scala.Function1[T, Boolean]) extends AnyVal { + @inline def asJava: java.util.function.Predicate[T] = underlying match { + case FromJavaPredicate((jf @ _)) => jf.asInstanceOf[java.util.function.Predicate[T]] + case _ => new AsJavaPredicate[T](underlying) + }; + @inline def asJavaPredicate: java.util.function.Predicate[T] = underlying match { + case FromJavaPredicate((sf @ _)) => sf.asInstanceOf[java.util.function.Predicate[T]] + case _ => new AsJavaPredicate[T](underlying) + } + } + + + case class FromJavaSupplier[T](jf: java.util.function.Supplier[T]) extends scala.Function0[T] { + def apply() = jf.get() + } + + class RichSupplierAsFunction0[T](private val underlying: java.util.function.Supplier[T]) extends AnyVal { + @inline def asScala: scala.Function0[T] = underlying match { + case AsJavaSupplier((sf @ _)) => sf.asInstanceOf[scala.Function0[T]] + case _ => new FromJavaSupplier[T](underlying) + } + } + + case class AsJavaSupplier[T](sf: scala.Function0[T]) extends java.util.function.Supplier[T] { + def get() = sf.apply() + } + + class RichFunction0AsSupplier[T](private val underlying: scala.Function0[T]) extends AnyVal { + @inline def asJava: java.util.function.Supplier[T] = underlying match { + case FromJavaSupplier((jf @ _)) => jf.asInstanceOf[java.util.function.Supplier[T]] + case _ => new AsJavaSupplier[T](underlying) + }; + @inline def asJavaSupplier: java.util.function.Supplier[T] = underlying match { + case FromJavaSupplier((sf @ _)) => sf.asInstanceOf[java.util.function.Supplier[T]] + case _ => new AsJavaSupplier[T](underlying) + } + } + + + case class FromJavaToDoubleBiFunction[T, U](jf: java.util.function.ToDoubleBiFunction[T, U]) extends scala.Function2[T, U, Double] { + def apply(x1: T, x2: U) = jf.applyAsDouble(x1, x2) + } + + class RichToDoubleBiFunctionAsFunction2[T, U](private val underlying: java.util.function.ToDoubleBiFunction[T, U]) extends AnyVal { + @inline def asScala: scala.Function2[T, U, Double] = underlying match { + case AsJavaToDoubleBiFunction((sf @ _)) => sf.asInstanceOf[scala.Function2[T, U, Double]] + case _ => new FromJavaToDoubleBiFunction[T, U](underlying) + } + } + + case class AsJavaToDoubleBiFunction[T, U](sf: scala.Function2[T, U, Double]) extends java.util.function.ToDoubleBiFunction[T, U] { + def applyAsDouble(x1: T, x2: U) = sf.apply(x1, x2) + } + + class RichFunction2AsToDoubleBiFunction[T, U](private val underlying: scala.Function2[T, U, Double]) extends AnyVal { + @inline def asJava: java.util.function.ToDoubleBiFunction[T, U] = underlying match { + case FromJavaToDoubleBiFunction((jf @ _)) => jf.asInstanceOf[java.util.function.ToDoubleBiFunction[T, U]] + case _ => new AsJavaToDoubleBiFunction[T, U](underlying) + }; + @inline def asJavaToDoubleBiFunction: java.util.function.ToDoubleBiFunction[T, U] = underlying match { + case FromJavaToDoubleBiFunction((sf @ _)) => sf.asInstanceOf[java.util.function.ToDoubleBiFunction[T, U]] + case _ => new AsJavaToDoubleBiFunction[T, U](underlying) + } + } + + + case class FromJavaToDoubleFunction[T](jf: java.util.function.ToDoubleFunction[T]) extends scala.Function1[T, Double] { + def apply(x1: T) = jf.applyAsDouble(x1) + } + + class RichToDoubleFunctionAsFunction1[T](private val underlying: java.util.function.ToDoubleFunction[T]) extends AnyVal { + @inline def asScala: scala.Function1[T, Double] = underlying match { + case AsJavaToDoubleFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[T, Double]] + case _ => new FromJavaToDoubleFunction[T](underlying) + } + } + + case class AsJavaToDoubleFunction[T](sf: scala.Function1[T, Double]) extends java.util.function.ToDoubleFunction[T] { + def applyAsDouble(x1: T) = sf.apply(x1) + } + + class RichFunction1AsToDoubleFunction[T](private val underlying: scala.Function1[T, Double]) extends AnyVal { + @inline def asJava: java.util.function.ToDoubleFunction[T] = underlying match { + case FromJavaToDoubleFunction((jf @ _)) => jf.asInstanceOf[java.util.function.ToDoubleFunction[T]] + case _ => new AsJavaToDoubleFunction[T](underlying) + }; + @inline def asJavaToDoubleFunction: java.util.function.ToDoubleFunction[T] = underlying match { + case FromJavaToDoubleFunction((sf @ _)) => sf.asInstanceOf[java.util.function.ToDoubleFunction[T]] + case _ => new AsJavaToDoubleFunction[T](underlying) + } + } + + + case class FromJavaToIntBiFunction[T, U](jf: java.util.function.ToIntBiFunction[T, U]) extends scala.Function2[T, U, Int] { + def apply(x1: T, x2: U) = jf.applyAsInt(x1, x2) + } + + class RichToIntBiFunctionAsFunction2[T, U](private val underlying: java.util.function.ToIntBiFunction[T, U]) extends AnyVal { + @inline def asScala: scala.Function2[T, U, Int] = underlying match { + case AsJavaToIntBiFunction((sf @ _)) => sf.asInstanceOf[scala.Function2[T, U, Int]] + case _ => new FromJavaToIntBiFunction[T, U](underlying) + } + } + + case class AsJavaToIntBiFunction[T, U](sf: scala.Function2[T, U, Int]) extends java.util.function.ToIntBiFunction[T, U] { + def applyAsInt(x1: T, x2: U) = sf.apply(x1, x2) + } + + class RichFunction2AsToIntBiFunction[T, U](private val underlying: scala.Function2[T, U, Int]) extends AnyVal { + @inline def asJava: java.util.function.ToIntBiFunction[T, U] = underlying match { + case FromJavaToIntBiFunction((jf @ _)) => jf.asInstanceOf[java.util.function.ToIntBiFunction[T, U]] + case _ => new AsJavaToIntBiFunction[T, U](underlying) + }; + @inline def asJavaToIntBiFunction: java.util.function.ToIntBiFunction[T, U] = underlying match { + case FromJavaToIntBiFunction((sf @ _)) => sf.asInstanceOf[java.util.function.ToIntBiFunction[T, U]] + case _ => new AsJavaToIntBiFunction[T, U](underlying) + } + } + + + case class FromJavaToIntFunction[T](jf: java.util.function.ToIntFunction[T]) extends scala.Function1[T, Int] { + def apply(x1: T) = jf.applyAsInt(x1) + } + + class RichToIntFunctionAsFunction1[T](private val underlying: java.util.function.ToIntFunction[T]) extends AnyVal { + @inline def asScala: scala.Function1[T, Int] = underlying match { + case AsJavaToIntFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[T, Int]] + case _ => new FromJavaToIntFunction[T](underlying) + } + } + + case class AsJavaToIntFunction[T](sf: scala.Function1[T, Int]) extends java.util.function.ToIntFunction[T] { + def applyAsInt(x1: T) = sf.apply(x1) + } + + class RichFunction1AsToIntFunction[T](private val underlying: scala.Function1[T, Int]) extends AnyVal { + @inline def asJava: java.util.function.ToIntFunction[T] = underlying match { + case FromJavaToIntFunction((jf @ _)) => jf.asInstanceOf[java.util.function.ToIntFunction[T]] + case _ => new AsJavaToIntFunction[T](underlying) + }; + @inline def asJavaToIntFunction: java.util.function.ToIntFunction[T] = underlying match { + case FromJavaToIntFunction((sf @ _)) => sf.asInstanceOf[java.util.function.ToIntFunction[T]] + case _ => new AsJavaToIntFunction[T](underlying) + } + } + + + case class FromJavaToLongBiFunction[T, U](jf: java.util.function.ToLongBiFunction[T, U]) extends scala.Function2[T, U, Long] { + def apply(x1: T, x2: U) = jf.applyAsLong(x1, x2) + } + + class RichToLongBiFunctionAsFunction2[T, U](private val underlying: java.util.function.ToLongBiFunction[T, U]) extends AnyVal { + @inline def asScala: scala.Function2[T, U, Long] = underlying match { + case AsJavaToLongBiFunction((sf @ _)) => sf.asInstanceOf[scala.Function2[T, U, Long]] + case _ => new FromJavaToLongBiFunction[T, U](underlying) + } + } + + case class AsJavaToLongBiFunction[T, U](sf: scala.Function2[T, U, Long]) extends java.util.function.ToLongBiFunction[T, U] { + def applyAsLong(x1: T, x2: U) = sf.apply(x1, x2) + } + + class RichFunction2AsToLongBiFunction[T, U](private val underlying: scala.Function2[T, U, Long]) extends AnyVal { + @inline def asJava: java.util.function.ToLongBiFunction[T, U] = underlying match { + case FromJavaToLongBiFunction((jf @ _)) => jf.asInstanceOf[java.util.function.ToLongBiFunction[T, U]] + case _ => new AsJavaToLongBiFunction[T, U](underlying) + }; + @inline def asJavaToLongBiFunction: java.util.function.ToLongBiFunction[T, U] = underlying match { + case FromJavaToLongBiFunction((sf @ _)) => sf.asInstanceOf[java.util.function.ToLongBiFunction[T, U]] + case _ => new AsJavaToLongBiFunction[T, U](underlying) + } + } + + + case class FromJavaToLongFunction[T](jf: java.util.function.ToLongFunction[T]) extends scala.Function1[T, Long] { + def apply(x1: T) = jf.applyAsLong(x1) + } + + class RichToLongFunctionAsFunction1[T](private val underlying: java.util.function.ToLongFunction[T]) extends AnyVal { + @inline def asScala: scala.Function1[T, Long] = underlying match { + case AsJavaToLongFunction((sf @ _)) => sf.asInstanceOf[scala.Function1[T, Long]] + case _ => new FromJavaToLongFunction[T](underlying) + } + } + + case class AsJavaToLongFunction[T](sf: scala.Function1[T, Long]) extends java.util.function.ToLongFunction[T] { + def applyAsLong(x1: T) = sf.apply(x1) + } + + class RichFunction1AsToLongFunction[T](private val underlying: scala.Function1[T, Long]) extends AnyVal { + @inline def asJava: java.util.function.ToLongFunction[T] = underlying match { + case FromJavaToLongFunction((jf @ _)) => jf.asInstanceOf[java.util.function.ToLongFunction[T]] + case _ => new AsJavaToLongFunction[T](underlying) + }; + @inline def asJavaToLongFunction: java.util.function.ToLongFunction[T] = underlying match { + case FromJavaToLongFunction((sf @ _)) => sf.asInstanceOf[java.util.function.ToLongFunction[T]] + case _ => new AsJavaToLongFunction[T](underlying) + } + } + + + case class FromJavaUnaryOperator[T](jf: java.util.function.UnaryOperator[T]) extends scala.Function1[T, T] { + def apply(x1: T) = jf.apply(x1) + } + + class RichUnaryOperatorAsFunction1[T](private val underlying: java.util.function.UnaryOperator[T]) extends AnyVal { + @inline def asScala: scala.Function1[T, T] = underlying match { + case AsJavaUnaryOperator((sf @ _)) => sf.asInstanceOf[scala.Function1[T, T]] + case _ => new FromJavaUnaryOperator[T](underlying) + } + } + + case class AsJavaUnaryOperator[T](sf: scala.Function1[T, T]) extends java.util.function.UnaryOperator[T] { + def apply(x1: T) = sf.apply(x1) + } + + class RichFunction1AsUnaryOperator[T](private val underlying: scala.Function1[T, T]) extends AnyVal { + @inline def asJava: java.util.function.UnaryOperator[T] = underlying match { + case FromJavaUnaryOperator((jf @ _)) => jf.asInstanceOf[java.util.function.UnaryOperator[T]] + case _ => new AsJavaUnaryOperator[T](underlying) + }; + @inline def asJavaUnaryOperator: java.util.function.UnaryOperator[T] = underlying match { + case FromJavaUnaryOperator((sf @ _)) => sf.asInstanceOf[java.util.function.UnaryOperator[T]] + case _ => new AsJavaUnaryOperator[T](underlying) + } + } +} diff --git a/library/src/scala/jdk/FutureConverters.scala b/library/src/scala/jdk/FutureConverters.scala new file mode 100644 index 000000000000..9b9b6ad3c8b6 --- /dev/null +++ b/library/src/scala/jdk/FutureConverters.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.util.concurrent.CompletionStage + +import scala.concurrent.Future + +/** This object provides extension methods that convert between Scala [[scala.concurrent.Future]] and Java + * [[java.util.concurrent.CompletionStage]] + * + * When writing Java code, use the explicit conversion methods defined in + * [[javaapi.FutureConverters]] instead. + * + * Note that the bridge is implemented at the read-only side of asynchronous handles, namely + * [[scala.concurrent.Future]] (instead of [[scala.concurrent.Promise]]) and [[java.util.concurrent.CompletionStage]] (instead of + * [[java.util.concurrent.CompletableFuture]]). This is intentional, as the semantics of bridging + * the write-handles would be prone to race conditions; if both ends (`CompletableFuture` and + * `Promise`) are completed independently at the same time, they may contain different values + * afterwards. For this reason, `toCompletableFuture` is not supported on the created + * `CompletionStage`s. + */ +object FutureConverters { + implicit class FutureOps[T](private val f: Future[T]) extends AnyVal { + /** Convert a Scala Future to a Java CompletionStage, see [[javaapi.FutureConverters.asJava]]. */ + def asJava: CompletionStage[T] = javaapi.FutureConverters.asJava(f) + } + + implicit class CompletionStageOps[T](private val cs: CompletionStage[T]) extends AnyVal { + /** Convert a Java CompletionStage to a Scala Future, see [[javaapi.FutureConverters.asScala]]. */ + def asScala: Future[T] = javaapi.FutureConverters.asScala(cs) + } +} diff --git a/library/src/scala/jdk/IntAccumulator.scala b/library/src/scala/jdk/IntAccumulator.scala new file mode 100644 index 000000000000..e407f438520a --- /dev/null +++ b/library/src/scala/jdk/IntAccumulator.scala @@ -0,0 +1,491 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.io.{ObjectInputStream, ObjectOutputStream} +import java.util.Spliterator +import java.util.function.{Consumer, IntConsumer} +import java.{lang => jl} + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{AnyStepper, Factory, IntStepper, SeqFactory, Stepper, StepperShape, mutable} +import scala.language.implicitConversions + +/** A specialized Accumulator that holds `Int`s without boxing, see [[Accumulator]]. */ +final class IntAccumulator + extends Accumulator[Int, AnyAccumulator, IntAccumulator] + with mutable.SeqOps[Int, AnyAccumulator, IntAccumulator] + with Serializable { + private[jdk] var current: Array[Int] = IntAccumulator.emptyIntArray + private[jdk] var history: Array[Array[Int]] = IntAccumulator.emptyIntArrayArray + + private[jdk] def cumulative(i: Int) = { val x = history(i); x(x.length-2).toLong << 32 | (x(x.length-1)&0xFFFFFFFFL) } + + override protected[this] def className: String = "IntAccumulator" + + def efficientStepper[S <: Stepper[_]](implicit shape: StepperShape[Int, S]): S with EfficientSplit = { + val st = new IntAccumulatorStepper(this) + val r = + if (shape.shape == StepperShape.IntShape) st + else { + assert(shape.shape == StepperShape.ReferenceShape, s"unexpected StepperShape: $shape") + AnyStepper.ofParIntStepper(st) + } + r.asInstanceOf[S with EfficientSplit] + } + + private def expand(): Unit = { + if (index > 0) { + val cuml = (if (hIndex > 0) cumulative(hIndex-1) else 0) + index + current(current.length-2) = (cuml >>> 32).toInt + current(current.length-1) = (cuml & 0xFFFFFFFFL).toInt + if (hIndex >= history.length) hExpand() + history(hIndex) = current + hIndex += 1 + } + current = new Array[Int](nextBlockSize+1) + index = 0 + } + + private def hExpand(): Unit = { + if (hIndex == 0) history = new Array[Array[Int]](4) + else history = java.util.Arrays.copyOf(history, history.length << 1) + } + + /** Appends an element to this `IntAccumulator`. */ + def addOne(a: Int): this.type = { + totalSize += 1 + if (index+2 >= current.length) expand() + current(index) = a + index += 1 + this + } + + /** Result collection consisting of all elements appended so far. */ + override def result(): IntAccumulator = this + + /** Removes all elements from `that` and appends them to this `IntAccumulator`. */ + def drain(that: IntAccumulator): Unit = { + var h = 0 + var prev = 0L + var more = true + while (more && h < that.hIndex) { + val cuml = that.cumulative(h) + val n = (cuml - prev).toInt + if (current.length - index - 2 >= n) { + System.arraycopy(that.history(h), 0, current, index, n) + prev = cuml + index += n + h += 1 + } + else more = false + } + if (h >= that.hIndex && current.length - index - 2 >= that.index) { + if (that.index > 0) System.arraycopy(that.current, 0, current, index, that.index) + index += that.index + } + else { + val slots = (if (index > 0) 1 else 0) + that.hIndex - h + if (hIndex + slots > history.length) { + val n = math.max(4, 1 << (32 - jl.Integer.numberOfLeadingZeros(1 + hIndex + slots))) + history = java.util.Arrays.copyOf(history, n) + } + var pv = if (hIndex > 0) cumulative(hIndex-1) else 0L + if (index > 0) { + val x = + if (index < (current.length >>> 3) && current.length - 1 > 32) { + val ans = java.util.Arrays.copyOf(current, index + 2) + ans(ans.length - 2) = current(current.length - 2) + ans(ans.length - 1) = current(current.length - 1) + ans + } + else current + pv = pv + index + x(x.length - 2) = (pv >>> 32).toInt + x(x.length - 1) = (pv & 0xFFFFFFFFL).toInt + history(hIndex) = x + hIndex += 1 + } + while (h < that.hIndex) { + val cuml = that.cumulative(h) + pv = pv + cuml - prev + prev = cuml + val x = that.history(h) + x(x.length - 2) = (pv >>> 32).toInt + x(x.length - 1) = (pv & 0xFFFFFFFFL).toInt + history(hIndex) = x + h += 1 + hIndex += 1 + } + index = that.index + current = that.current + } + totalSize += that.totalSize + that.clear() + } + + override def clear(): Unit = { + super.clear() + current = IntAccumulator.emptyIntArray + history = IntAccumulator.emptyIntArrayArray + } + + /** Retrieves the `ix`th element. */ + def apply(ix: Long): Int = { + if (totalSize - ix <= index || hIndex == 0) current((ix - (totalSize - index)).toInt) + else { + val w = seekSlot(ix) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt) + } + } + + /** Retrieves the `ix`th element, using an `Int` index. */ + def apply(i: Int): Int = apply(i.toLong) + + def update(idx: Long, elem: Int): Unit = { + if (totalSize - idx <= index || hIndex == 0) current((idx - (totalSize - index)).toInt) = elem + else { + val w = seekSlot(idx) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt) = elem + } + } + + def update(idx: Int, elem: Int): Unit = update(idx.toLong, elem) + + /** Returns an `Iterator` over the contents of this `IntAccumulator`. The `Iterator` is not specialized. */ + def iterator: Iterator[Int] = stepper.iterator + + override def foreach[U](f: Int => U): Unit = { + val s = stepper + while (s.hasStep) f(s.nextStep()) + } + + def map(f: Int => Int): IntAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) + b.addOne(f(s.nextStep())) + b.result() + } + + def flatMap(f: Int => IterableOnce[Int]): IntAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) + b.addAll(f(s.nextStep())) + b.result() + } + + def collect(pf: PartialFunction[Int, Int]): IntAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) { + val n = s.nextStep() + pf.runWith(b.addOne)(n) + } + b.result() + } + + private def filterAccImpl(pred: Int => Boolean, not: Boolean): IntAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) { + val n = s.nextStep() + if (pred(n) != not) b.addOne(n) + } + b.result() + } + + override def filter(pred: Int => Boolean): IntAccumulator = filterAccImpl(pred, not = false) + + override def filterNot(pred: Int => Boolean): IntAccumulator = filterAccImpl(pred, not = true) + + override def forall(p: Int => Boolean): Boolean = { + val s = stepper + while (s.hasStep) + if (!p(s.nextStep())) return false + true + } + + override def exists(p: Int => Boolean): Boolean = { + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) return true + false + } + + override def count(p: Int => Boolean): Int = { + var r = 0 + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) r += 1 + r + } + + def countLong(p: Int => Boolean): Long = { + var r = 0L + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) r += 1 + r + } + + /** Copies the elements in this `IntAccumulator` into an `Array[Int]` */ + def toArray: Array[Int] = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for an array: "+totalSize.toString) + val a = new Array[Int](totalSize.toInt) + var j = 0 + var h = 0 + var pv = 0L + while (h < hIndex) { + val x = history(h) + val cuml = cumulative(h) + val n = (cuml - pv).toInt + pv = cuml + System.arraycopy(x, 0, a, j, n) + j += n + h += 1 + } + System.arraycopy(current, 0, a, j, index) + j += index + a + } + + /** Copies the elements in this `IntAccumulator` to a `List` */ + override def toList: List[Int] = { + var ans: List[Int] = Nil + var i = index - 1 + while (i >= 0) { + ans = current(i) :: ans + i -= 1 + } + var h = hIndex - 1 + while (h >= 0) { + val a = history(h) + i = (cumulative(h) - (if (h == 0) 0L else cumulative(h-1))).toInt - 1 + while (i >= 0) { + ans = a(i) :: ans + i -= 1 + } + h -= 1 + } + ans + } + + /** + * Copy the elements in this `IntAccumulator` to a specified collection. + * Note that the target collection is not specialized. + * Usage example: `acc.to(Vector)` + */ + override def to[C1](factory: Factory[Int, C1]): C1 = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for a Scala collection: "+totalSize.toString) + factory.fromSpecific(iterator) + } + + override protected def fromSpecific(coll: IterableOnce[Int]): IntAccumulator = IntAccumulator.fromSpecific(coll) + override protected def newSpecificBuilder: IntAccumulator = IntAccumulator.newBuilder + override def iterableFactory: SeqFactory[AnyAccumulator] = AnyAccumulator + + override def empty: IntAccumulator = IntAccumulator.empty + + private def writeReplace(): AnyRef = new IntAccumulator.SerializationProxy(this) +} + +object IntAccumulator extends collection.SpecificIterableFactory[Int, IntAccumulator] { + private val emptyIntArray = new Array[Int](0) + private val emptyIntArrayArray = new Array[Array[Int]](0) + + implicit def toJavaIntegerAccumulator(ia: IntAccumulator.type): collection.SpecificIterableFactory[jl.Integer, IntAccumulator] = IntAccumulator.asInstanceOf[collection.SpecificIterableFactory[jl.Integer, IntAccumulator]] + + import java.util.{function => jf} + + /** A `Supplier` of `IntAccumulator`s, suitable for use with `java.util.stream.IntStream`'s `collect` method. Suitable for `Stream[Int]` also. */ + def supplier: jf.Supplier[IntAccumulator] = () => new IntAccumulator + + /** A `BiConsumer` that adds an element to an `IntAccumulator`, suitable for use with `java.util.stream.IntStream`'s `collect` method. */ + def adder: jf.ObjIntConsumer[IntAccumulator] = (ac: IntAccumulator, a: Int) => ac addOne a + + /** A `BiConsumer` that adds a boxed `Int` to an `IntAccumulator`, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def boxedAdder: jf.BiConsumer[IntAccumulator, Int] = (ac: IntAccumulator, a: Int) => ac addOne a + + /** A `BiConsumer` that merges `IntAccumulator`s, suitable for use with `java.util.stream.IntStream`'s `collect` method. Suitable for `Stream[Int]` also. */ + def merger: jf.BiConsumer[IntAccumulator, IntAccumulator] = (a1: IntAccumulator, a2: IntAccumulator) => a1 drain a2 + + private def fromArray(a: Array[Int]): IntAccumulator = { + val r = new IntAccumulator + var i = 0 + while (i < a.length) { r addOne a(i); i += 1 } + r + } + + override def fromSpecific(it: IterableOnce[Int]): IntAccumulator = it match { + case acc: IntAccumulator => acc + case as: collection.immutable.ArraySeq.ofInt => fromArray(as.unsafeArray) + case as: collection.mutable.ArraySeq.ofInt => fromArray(as.array) // this case ensures Array(1).to(Accumulator) doesn't box + case _ => (new IntAccumulator).addAll(it) + } + + override def empty: IntAccumulator = new IntAccumulator + + override def newBuilder: IntAccumulator = new IntAccumulator + + class SerializationProxy[A](@transient private val acc: IntAccumulator) extends Serializable { + @transient private var result: IntAccumulator = _ + + private def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + val size = acc.sizeLong + out.writeLong(size) + val st = acc.stepper + while (st.hasStep) + out.writeInt(st.nextStep()) + } + + private def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val res = new IntAccumulator() + var elems = in.readLong() + while (elems > 0) { + res += in.readInt() + elems -= 1L + } + result = res + } + + private def readResolve(): AnyRef = result + } +} + +private[jdk] class IntAccumulatorStepper(private val acc: IntAccumulator) extends IntStepper with EfficientSplit { + import java.util.Spliterator._ + + private var h: Int = 0 + private var i: Int = 0 + private var a: Array[Int] = if (acc.hIndex > 0) acc.history(0) else acc.current + private var n: Long = if (acc.hIndex > 0) acc.cumulative(0) else acc.index + private var N: Long = acc.totalSize + + private def duplicateSelf(limit: Long): IntAccumulatorStepper = { + val ans = new IntAccumulatorStepper(acc) + ans.h = h + ans.i = i + ans.a = a + ans.n = n + ans.N = limit + ans + } + + private def loadMore(): Unit = { + h += 1 + if (h < acc.hIndex) { a = acc.history(h); n = acc.cumulative(h) - acc.cumulative(h-1) } + else { a = acc.current; n = acc.index } + i = 0 + } + + def characteristics: Int = ORDERED | SIZED | SUBSIZED | NONNULL + + def estimateSize: Long = N + + def hasStep: Boolean = N > 0 + + def nextStep(): Int = + if (N <= 0) throw new NoSuchElementException("next on empty Stepper") + else { + if (i >= n) loadMore() + val ans = a(i) + i += 1 + N -= 1 + ans + } + + def trySplit(): IntStepper = + if (N <= 1) null + else { + val half = N >> 1 + val M = (if (h <= 0) 0L else acc.cumulative(h-1)) + i + val R = M + half + val ans = duplicateSelf(half) + if (h < acc.hIndex) { + val w = acc.seekSlot(R) + h = (w >>> 32).toInt + if (h < acc.hIndex) { + a = acc.history(h) + n = acc.cumulative(h) - (if (h > 0) acc.cumulative(h-1) else 0) + } + else { + a = acc.current + n = acc.index + } + i = (w & 0xFFFFFFFFL).toInt + } + else i += half.toInt + N -= half + ans + } + + override def spliterator[B >: Int]: Spliterator.OfInt = new IntStepper.IntStepperSpliterator(this) { + // Overridden for efficiency + override def tryAdvance(c: IntConsumer): Boolean = + if (N <= 0) false + else { + if (i >= n) loadMore() + c.accept(a(i)) + i += 1 + N -= 1 + true + } + + // Overridden for efficiency + override def tryAdvance(c: Consumer[_ >: jl.Integer]): Boolean = (c: AnyRef) match { + case ic: IntConsumer => tryAdvance(ic) + case _ => + if (N <= 0) false + else { + if (i >= n) loadMore() + c.accept(a(i)) + i += 1 + N -= 1 + true + } + } + + // Overridden for efficiency + override def forEachRemaining(c: IntConsumer): Unit = + while (N > 0) { + if (i >= n) loadMore() + val i0 = i + if ((n-i) > N) n = i + N.toInt + while (i < n) { + c.accept(a(i)) + i += 1 + } + N -= (n - i0) + } + + // Overridden for efficiency + override def forEachRemaining(c: Consumer[_ >: jl.Integer]): Unit = (c: AnyRef) match { + case ic: IntConsumer => forEachRemaining(ic) + case _ => + while (N > 0) { + if (i >= n) loadMore() + val i0 = i + if ((n-i) > N) n = i + N.toInt + while (i < n) { + c.accept(a(i)) + i += 1 + } + N -= (n - i0) + } + } + } +} diff --git a/library/src/scala/jdk/LongAccumulator.scala b/library/src/scala/jdk/LongAccumulator.scala new file mode 100644 index 000000000000..8c538533a923 --- /dev/null +++ b/library/src/scala/jdk/LongAccumulator.scala @@ -0,0 +1,486 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.io.{ObjectInputStream, ObjectOutputStream} +import java.util.Spliterator +import java.util.function.{Consumer, LongConsumer} +import java.{lang => jl} + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{AnyStepper, Factory, LongStepper, SeqFactory, Stepper, StepperShape, mutable} +import scala.language.implicitConversions + +/** A specialized Accumulator that holds `Long`s without boxing, see [[Accumulator]]. */ +final class LongAccumulator + extends Accumulator[Long, AnyAccumulator, LongAccumulator] + with mutable.SeqOps[Long, AnyAccumulator, LongAccumulator] + with Serializable { + private[jdk] var current: Array[Long] = LongAccumulator.emptyLongArray + private[jdk] var history: Array[Array[Long]] = LongAccumulator.emptyLongArrayArray + + private[jdk] def cumulative(i: Int) = { val x = history(i); x(x.length-1) } + + override protected[this] def className: String = "LongAccumulator" + + def efficientStepper[S <: Stepper[_]](implicit shape: StepperShape[Long, S]): S with EfficientSplit = { + val st = new LongAccumulatorStepper(this) + val r = + if (shape.shape == StepperShape.LongShape) st + else { + assert(shape.shape == StepperShape.ReferenceShape, s"unexpected StepperShape: $shape") + AnyStepper.ofParLongStepper(st) + } + r.asInstanceOf[S with EfficientSplit] + } + + private def expand(): Unit = { + if (index > 0) { + current(current.length-1) = (if (hIndex > 0) { val x = history(hIndex-1); x(x.length-1) } else 0) + index + if (hIndex >= history.length) hExpand() + history(hIndex) = current + hIndex += 1 + } + current = new Array[Long](nextBlockSize+1) + index = 0 + } + + private def hExpand(): Unit = { + if (hIndex == 0) history = new Array[Array[Long]](4) + else history = java.util.Arrays.copyOf(history, history.length << 1) + } + + /** Appends an element to this `LongAccumulator`. */ + def addOne(a: Long): this.type = { + totalSize += 1 + if (index+1 >= current.length) expand() + current(index) = a + index += 1 + this + } + + /** Result collection consisting of all elements appended so far. */ + override def result(): LongAccumulator = this + + /** Removes all elements from `that` and appends them to this `LongAccumulator`. */ + def drain(that: LongAccumulator): Unit = { + var h = 0 + var prev = 0L + var more = true + while (more && h < that.hIndex) { + val cuml = that.cumulative(h) + val n = (cuml - prev).toInt + if (current.length - index - 1 >= n) { + System.arraycopy(that.history(h), 0, current, index, n) + prev = cuml + index += n + h += 1 + } + else more = false + } + if (h >= that.hIndex && current.length - index - 1>= that.index) { + if (that.index > 0) System.arraycopy(that.current, 0, current, index, that.index) + index += that.index + } + else { + val slots = (if (index > 0) 1 else 0) + that.hIndex - h + if (hIndex + slots > history.length) { + val n = math.max(4, 1 << (32 - jl.Integer.numberOfLeadingZeros(1 + hIndex + slots))) + history = java.util.Arrays.copyOf(history, n) + } + var pv = if (hIndex > 0) cumulative(hIndex-1) else 0L + if (index > 0) { + val x = + if (index < (current.length >>> 3) && current.length - 1 > 32) { + val ans = java.util.Arrays.copyOf(current, index + 1) + ans(ans.length - 1) = current(current.length - 1) + ans + } + else current + pv = pv + index + x(x.length - 1) = pv + history(hIndex) = x + hIndex += 1 + } + while (h < that.hIndex) { + val cuml = that.cumulative(h) + pv = pv + cuml - prev + prev = cuml + val x = that.history(h) + x(x.length - 1) = pv + history(hIndex) = x + h += 1 + hIndex += 1 + } + index = that.index + current = that.current + } + totalSize += that.totalSize + that.clear() + } + + override def clear(): Unit = { + super.clear() + current = LongAccumulator.emptyLongArray + history = LongAccumulator.emptyLongArrayArray + } + + /** Retrieves the `ix`th element. */ + def apply(ix: Long): Long = { + if (totalSize - ix <= index || hIndex == 0) current((ix - (totalSize - index)).toInt) + else { + val w = seekSlot(ix) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt) + } + } + + /** Retrieves the `ix`th element, using an `Int` index. */ + def apply(i: Int): Long = apply(i.toLong) + + def update(idx: Long, elem: Long): Unit = { + if (totalSize - idx <= index || hIndex == 0) current((idx - (totalSize - index)).toInt) = elem + else { + val w = seekSlot(idx) + history((w >>> 32).toInt)((w & 0xFFFFFFFFL).toInt) = elem + } + } + + def update(idx: Int, elem: Long): Unit = update(idx.toLong, elem) + + /** Returns an `Iterator` over the contents of this `LongAccumulator`. The `Iterator` is not specialized. */ + def iterator: Iterator[Long] = stepper.iterator + + override def foreach[U](f: Long => U): Unit = { + val s = stepper + while (s.hasStep) f(s.nextStep()) + } + + def map(f: Long => Long): LongAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) + b.addOne(f(s.nextStep())) + b.result() + } + + def flatMap(f: Long => IterableOnce[Long]): LongAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) + b.addAll(f(s.nextStep())) + b.result() + } + + def collect(pf: PartialFunction[Long, Long]): LongAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) { + val n = s.nextStep() + pf.runWith(b.addOne)(n) + } + b.result() + } + + private def filterAccImpl(pred: Long => Boolean, not: Boolean): LongAccumulator = { + val b = newSpecificBuilder + val s = stepper + while (s.hasStep) { + val n = s.nextStep() + if (pred(n) != not) b.addOne(n) + } + b.result() + } + + override def filter(pred: Long => Boolean): LongAccumulator = filterAccImpl(pred, not = false) + + override def filterNot(pred: Long => Boolean): LongAccumulator = filterAccImpl(pred, not = true) + + override def forall(p: Long => Boolean): Boolean = { + val s = stepper + while (s.hasStep) + if (!p(s.nextStep())) return false + true + } + + override def exists(p: Long => Boolean): Boolean = { + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) return true + false + } + + override def count(p: Long => Boolean): Int = { + var r = 0 + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) r += 1 + r + } + + def countLong(p: Long => Boolean): Long = { + var r = 0L + val s = stepper + while (s.hasStep) + if (p(s.nextStep())) r += 1 + r + } + + /** Copies the elements in this `LongAccumulator` into an `Array[Long]` */ + def toArray: Array[Long] = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for an array: "+totalSize.toString) + val a = new Array[Long](totalSize.toInt) + var j = 0 + var h = 0 + var pv = 0L + while (h < hIndex) { + val x = history(h) + val cuml = x(x.length-1) + val n = (cuml - pv).toInt + pv = cuml + System.arraycopy(x, 0, a, j, n) + j += n + h += 1 + } + System.arraycopy(current, 0, a, j, index) + j += index + a + } + + /** Copies the elements in this `LongAccumulator` to a `List` */ + override def toList: List[Long] = { + var ans: List[Long] = Nil + var i = index - 1 + while (i >= 0) { + ans = current(i) :: ans + i -= 1 + } + var h = hIndex - 1 + while (h >= 0) { + val a = history(h) + i = (cumulative(h) - (if (h == 0) 0L else cumulative(h-1))).toInt - 1 + while (i >= 0) { + ans = a(i) :: ans + i -= 1 + } + h -= 1 + } + ans + } + + /** + * Copy the elements in this `LongAccumulator` to a specified collection. + * Note that the target collection is not specialized. + * Usage example: `acc.to(Vector)` + */ + override def to[C1](factory: Factory[Long, C1]): C1 = { + if (totalSize > Int.MaxValue) throw new IllegalArgumentException("Too many elements accumulated for a Scala collection: "+totalSize.toString) + factory.fromSpecific(iterator) + } + + override protected def fromSpecific(coll: IterableOnce[Long]): LongAccumulator = LongAccumulator.fromSpecific(coll) + override protected def newSpecificBuilder: LongAccumulator = LongAccumulator.newBuilder + override def iterableFactory: SeqFactory[AnyAccumulator] = AnyAccumulator + + override def empty: LongAccumulator = LongAccumulator.empty + + private def writeReplace(): AnyRef = new LongAccumulator.SerializationProxy(this) +} + +object LongAccumulator extends collection.SpecificIterableFactory[Long, LongAccumulator] { + private val emptyLongArray = new Array[Long](0) + private val emptyLongArrayArray = new Array[Array[Long]](0) + + implicit def toJavaLongAccumulator(ia: LongAccumulator.type): collection.SpecificIterableFactory[jl.Long, LongAccumulator] = LongAccumulator.asInstanceOf[collection.SpecificIterableFactory[jl.Long, LongAccumulator]] + + import java.util.{function => jf} + + /** A `Supplier` of `LongAccumulator`s, suitable for use with `java.util.stream.LongStream`'s `collect` method. Suitable for `Stream[Long]` also. */ + def supplier: jf.Supplier[LongAccumulator] = () => new LongAccumulator + + /** A `BiConsumer` that adds an element to an `LongAccumulator`, suitable for use with `java.util.stream.LongStream`'s `collect` method. */ + def adder: jf.ObjLongConsumer[LongAccumulator] = (ac: LongAccumulator, a: Long) => ac addOne a + + /** A `BiConsumer` that adds a boxed `Long` to an `LongAccumulator`, suitable for use with `java.util.stream.Stream`'s `collect` method. */ + def boxedAdder: jf.BiConsumer[LongAccumulator, Long] = (ac: LongAccumulator, a: Long) => ac addOne a + + /** A `BiConsumer` that merges `LongAccumulator`s, suitable for use with `java.util.stream.LongStream`'s `collect` method. Suitable for `Stream[Long]` also. */ + def merger: jf.BiConsumer[LongAccumulator, LongAccumulator] = (a1: LongAccumulator, a2: LongAccumulator) => a1 drain a2 + + private def fromArray(a: Array[Long]): LongAccumulator = { + val r = new LongAccumulator + var i = 0 + while (i < a.length) { r addOne a(i); i += 1 } + r + } + + override def fromSpecific(it: IterableOnce[Long]): LongAccumulator = it match { + case acc: LongAccumulator => acc + case as: collection.immutable.ArraySeq.ofLong => fromArray(as.unsafeArray) + case as: collection.mutable.ArraySeq.ofLong => fromArray(as.array) // this case ensures Array(1).to(Accumulator) doesn't box + case _ => (new LongAccumulator).addAll(it) + } + + override def empty: LongAccumulator = new LongAccumulator + + override def newBuilder: LongAccumulator = new LongAccumulator + + class SerializationProxy[A](@transient private val acc: LongAccumulator) extends Serializable { + @transient private var result: LongAccumulator = _ + + private def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + val size = acc.sizeLong + out.writeLong(size) + val st = acc.stepper + while (st.hasStep) + out.writeLong(st.nextStep()) + } + + private def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val res = new LongAccumulator() + var elems = in.readLong() + while (elems > 0) { + res += in.readLong() + elems -= 1L + } + result = res + } + + private def readResolve(): AnyRef = result + } +} + +private[jdk] class LongAccumulatorStepper(private val acc: LongAccumulator) extends LongStepper with EfficientSplit { + import java.util.Spliterator._ + + private var h: Int = 0 + private var i: Int = 0 + private var a: Array[Long] = if (acc.hIndex > 0) acc.history(0) else acc.current + private var n: Long = if (acc.hIndex > 0) acc.cumulative(0) else acc.index + private var N: Long = acc.totalSize + + private def duplicateSelf(limit: Long): LongAccumulatorStepper = { + val ans = new LongAccumulatorStepper(acc) + ans.h = h + ans.i = i + ans.a = a + ans.n = n + ans.N = limit + ans + } + + private def loadMore(): Unit = { + h += 1 + if (h < acc.hIndex) { a = acc.history(h); n = acc.cumulative(h) - acc.cumulative(h-1) } + else { a = acc.current; n = acc.index } + i = 0 + } + + def characteristics: Int = ORDERED | SIZED | SUBSIZED | NONNULL + + def estimateSize: Long = N + + def hasStep: Boolean = N > 0 + + def nextStep(): Long = + if (n <= 0) throw new NoSuchElementException("next on empty Stepper") + else { + if (i >= n) loadMore() + val ans = a(i) + i += 1 + N -= 1 + ans + } + + def trySplit(): LongStepper = + if (N <= 1) null + else { + val half = N >> 1 + val M = (if (h <= 0) 0L else acc.cumulative(h-1)) + i + val R = M + half + val ans = duplicateSelf(half) + if (h < acc.hIndex) { + val w = acc.seekSlot(R) + h = (w >>> 32).toInt + if (h < acc.hIndex) { + a = acc.history(h) + n = acc.cumulative(h) - (if (h > 0) acc.cumulative(h-1) else 0) + } + else { + a = acc.current + n = acc.index + } + i = (w & 0xFFFFFFFFL).toInt + } + else i += half.toInt + N -= half + ans + } + + override def spliterator[B >: Long]: Spliterator.OfLong = new LongStepper.LongStepperSpliterator(this) { + // Overridden for efficiency + override def tryAdvance(c: LongConsumer): Boolean = + if (N <= 0) false + else { + if (i >= n) loadMore() + c.accept(a(i)) + i += 1 + N -= 1 + true + } + + // Overridden for efficiency + override def tryAdvance(c: Consumer[_ >: jl.Long]): Boolean = (c: AnyRef) match { + case ic: LongConsumer => tryAdvance(ic) + case _ => + if (N <= 0) false + else { + if (i >= n) loadMore() + c.accept(a(i)) + i += 1 + N -= 1 + true + } + } + + // Overridden for efficiency + override def forEachRemaining(c: LongConsumer): Unit = + while (N > 0) { + if (i >= n) loadMore() + val i0 = i + if ((n-i) > N) n = i + N.toInt + while (i < n) { + c.accept(a(i)) + i += 1 + } + N -= (n - i0) + } + + // Overridden for efficiency + override def forEachRemaining(c: Consumer[_ >: jl.Long]): Unit = (c: AnyRef) match { + case ic: LongConsumer => forEachRemaining(ic) + case _ => + while (N > 0) { + if (i >= n) loadMore() + val i0 = i + if ((n-i) > N) n = i + N.toInt + while (i < n) { + c.accept(a(i)) + i += 1 + } + N -= (n - i0) + } + } + } +} diff --git a/library/src/scala/jdk/OptionConverters.scala b/library/src/scala/jdk/OptionConverters.scala new file mode 100644 index 000000000000..5fbfef206394 --- /dev/null +++ b/library/src/scala/jdk/OptionConverters.scala @@ -0,0 +1,111 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.util.{Optional, OptionalDouble, OptionalInt, OptionalLong} + +/** This object provides extension methods that convert between Scala `Option` and Java `Optional` + * types. + * + * When writing Java code, use the explicit conversion methods defined in + * [[javaapi.OptionConverters]] instead. + * + * Scala `Option` is extended with a `toJava` method that creates a corresponding `Optional`, and + * a `toJavaPrimitive` method that creates a specialized variant (e.g., `OptionalInt`) if + * applicable. + * + * Java `Optional` is extended with a `toScala` method and a `toJavaPrimitive` method. + * + * Finally, specialized `Optional` types are extended with `toScala` and `toJavaGeneric` methods. + * + * Example usage: + * + * {{{ + * import scala.jdk.OptionConverters._ + * val a = Option("example").toJava // Creates java.util.Optional[String] containing "example" + * val b = (None: Option[String]).toJava // Creates an empty java.util.Optional[String] + * val c = a.toScala // Back to Option("example") + * val d = b.toScala // Back to None typed as Option[String] + * val e = Option(2.7).toJava // java.util.Optional[Double] containing boxed 2.7 + * val f = Option(2.7).toJavaPrimitive // java.util.OptionalDouble containing 2.7 (not boxed) + * val g = f.toScala // Back to Option(2.7) + * val h = f.toJavaGeneric // Same as e + * val i = e.toJavaPrimitive // Same as f + * }}} + */ +object OptionConverters { + /** Provides conversions from Java `Optional` to Scala `Option` and specialized `Optional` types */ + implicit class RichOptional[A](private val o: java.util.Optional[A]) extends AnyVal { + /** Convert a Java `Optional` to a Scala `Option` */ + def toScala: Option[A] = if (o.isPresent) Some(o.get) else None + + /** Convert a Java `Optional` to a Scala `Option` */ + @deprecated("Use `toScala` instead", "2.13.0") + def asScala: Option[A] = if (o.isPresent) Some(o.get) else None + + /** Convert a generic Java `Optional` to a specialized variant */ + def toJavaPrimitive[O](implicit shape: OptionShape[A, O]): O = shape.fromJava(o) + } + + /** Provides conversions from Scala `Option` to Java `Optional` types */ + implicit class RichOption[A](private val o: Option[A]) extends AnyVal { + /** Convert a Scala `Option` to a generic Java `Optional` */ + def toJava: Optional[A] = o match { case Some(a) => Optional.ofNullable(a); case _ => Optional.empty[A] } + + /** Convert a Scala `Option` to a generic Java `Optional` */ + @deprecated("Use `toJava` instead", "2.13.0") + def asJava: Optional[A] = o match { case Some(a) => Optional.ofNullable(a); case _ => Optional.empty[A] } + + /** Convert a Scala `Option` to a specialized Java `Optional` */ + def toJavaPrimitive[O](implicit shape: OptionShape[A, O]): O = shape.fromScala(o) + } + + /** Provides conversions from `OptionalDouble` to Scala `Option` and the generic `Optional` */ + implicit class RichOptionalDouble(private val o: OptionalDouble) extends AnyVal { + /** Convert a Java `OptionalDouble` to a Scala `Option` */ + def toScala: Option[Double] = if (o.isPresent) Some(o.getAsDouble) else None + + /** Convert a Java `OptionalDouble` to a Scala `Option` */ + @deprecated("Use `toScala` instead", "2.13.0") + def asScala: Option[Double] = if (o.isPresent) Some(o.getAsDouble) else None + + /** Convert a Java `OptionalDouble` to a generic Java `Optional` */ + def toJavaGeneric: Optional[Double] = if (o.isPresent) Optional.of(o.getAsDouble) else Optional.empty[Double] + } + + /** Provides conversions from `OptionalInt` to Scala `Option` and the generic `Optional` */ + implicit class RichOptionalInt(private val o: OptionalInt) extends AnyVal { + /** Convert a Java `OptionalInt` to a Scala `Option` */ + def toScala: Option[Int] = if (o.isPresent) Some(o.getAsInt) else None + + /** Convert a Java `OptionalInt` to a Scala `Option` */ + @deprecated("Use `toScala` instead", "2.13.0") + def asScala: Option[Int] = if (o.isPresent) Some(o.getAsInt) else None + + /** Convert a Java `OptionalInt` to a generic Java `Optional` */ + def toJavaGeneric: Optional[Int] = if (o.isPresent) Optional.of(o.getAsInt) else Optional.empty[Int] + } + + /** Provides conversions from `OptionalLong` to Scala `Option` and the generic `Optional` */ + implicit class RichOptionalLong(private val o: OptionalLong) extends AnyVal { + /** Convert a Java `OptionalLong` to a Scala `Option` */ + def toScala: Option[Long] = if (o.isPresent) Some(o.getAsLong) else None + + /** Convert a Java `OptionalLong` to a Scala `Option` */ + @deprecated("Use `toScala` instead", "2.13.0") + def asScala: Option[Long] = if (o.isPresent) Some(o.getAsLong) else None + + /** Convert a Java `OptionalLong` to a generic Java `Optional` */ + def toJavaGeneric: Optional[Long] = if (o.isPresent) Optional.of(o.getAsLong) else Optional.empty[Long] + } +} diff --git a/library/src/scala/jdk/OptionShape.scala b/library/src/scala/jdk/OptionShape.scala new file mode 100644 index 000000000000..e56b3296e439 --- /dev/null +++ b/library/src/scala/jdk/OptionShape.scala @@ -0,0 +1,67 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import java.util.{Optional, OptionalDouble, OptionalInt, OptionalLong} +import java.{lang => jl} + +import scala.annotation.implicitNotFound + +/** A type class implementing conversions from a generic Scala `Option` or Java `Optional` to + * a specialized Java variant (for `Double`, `Int` and `Long`). + * + * @tparam A the primitive type wrapped in an option + * @tparam O the specialized Java `Optional` wrapping an element of type `A` + */ +@implicitNotFound("No specialized Optional type exists for elements of type ${A}") +sealed abstract class OptionShape[A, O] { + /** Converts from `Optional` to the specialized variant `O` */ + def fromJava(o: Optional[A]): O + /** Converts from `Option` to the specialized variant `O` */ + def fromScala(o: Option[A]): O +} + +object OptionShape { + implicit val doubleOptionShape: OptionShape[Double, OptionalDouble] = new OptionShape[Double, OptionalDouble] { + def fromJava(o: Optional[Double]): OptionalDouble = + if (o.isPresent) OptionalDouble.of(o.get) else OptionalDouble.empty + + def fromScala(o: Option[Double]): OptionalDouble = o match { + case Some(d) => OptionalDouble.of(d) + case _ => OptionalDouble.empty + } + } + implicit val jDoubleOptionShape: OptionShape[jl.Double, OptionalDouble] = doubleOptionShape.asInstanceOf[OptionShape[jl.Double, OptionalDouble]] + + implicit val intOptionShape: OptionShape[Int, OptionalInt] = new OptionShape[Int, OptionalInt] { + def fromJava(o: Optional[Int]): OptionalInt = + if (o.isPresent) OptionalInt.of(o.get) else OptionalInt.empty + + def fromScala(o: Option[Int]): OptionalInt = o match { + case Some(d) => OptionalInt.of(d) + case _ => OptionalInt.empty + } + } + implicit val jIntegerOptionShape: OptionShape[jl.Integer, OptionalInt] = intOptionShape.asInstanceOf[OptionShape[jl.Integer, OptionalInt]] + + implicit val longOptionShape: OptionShape[Long, OptionalLong] = new OptionShape[Long, OptionalLong] { + def fromJava(o: Optional[Long]): OptionalLong = + if (o.isPresent) OptionalLong.of(o.get) else OptionalLong.empty + + def fromScala(o: Option[Long]): OptionalLong = o match { + case Some(d) => OptionalLong.of(d) + case _ => OptionalLong.empty + } + } + implicit val jLongOptionShape: OptionShape[jl.Long, OptionalLong] = longOptionShape.asInstanceOf[OptionShape[jl.Long, OptionalLong]] +} diff --git a/library/src/scala/jdk/StreamConverters.scala b/library/src/scala/jdk/StreamConverters.scala new file mode 100644 index 000000000000..e3338bdba011 --- /dev/null +++ b/library/src/scala/jdk/StreamConverters.scala @@ -0,0 +1,92 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk + +import scala.collection.convert.StreamExtensions + +/** This object provides extension methods to create [[java.util.stream.Stream Java Streams]] that + * operate on Scala collections (sequentially or in parallel). For more information on Java + * streams, consult the documentation + * ([[https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html]]). + * + * When writing Java code, use the explicit conversion methods defined in + * [[javaapi.StreamConverters]] instead. + * + * The methods `asJavaSeqStream` and `asJavaParStream` convert a collection to a Java Stream: + * + * {{{ + * scala> import scala.jdk.StreamConverters._ + * + * scala> val s = (1 to 10).toList.asJavaSeqStream + * s: java.util.stream.IntStream = java.util.stream.IntPipeline\$Head@7b1e5e55 + * + * scala> s.map(_ * 2).filter(_ > 5).toScala(List) + * res1: List[Int] = List(6, 8, 10, 12, 14, 16, 18, 20) + * }}} + * + * Note: using parallel streams in the Scala REPL causes deadlocks, see + * [[https://github.com/scala/bug/issues/9076]]. As a workaround, use `scala -Yrepl-class-based`. + * + * {{{ + * scala> def isPrime(n: Int): Boolean = !(2 +: (3 to Math.sqrt(n).toInt by 2) exists (n % _ == 0)) + * isPrime: (n: Int)Boolean + * + * scala> (10000 to 1000000).asJavaParStream.filter(isPrime).toScala(Vector) + * res6: scala.collection.immutable.Vector[Int] = Vector(10007, 10009, 10037, 10039, ... + * }}} + * + * A Java [[Stream]] provides operations on a sequence of elements. Streams are created from + * [[java.util.Spliterator Spliterators]], which are similar to Iterators with the additional + * capability to partition off some of their elements. This partitioning, if supported by the + * Spliterator, is used for parallelizing Stream operations. + * + * Scala collections have a method [[scala.collection.IterableOnce.stepper `stepper`]] that + * returns a [[scala.collection.Stepper]] for the collection, which in turn can be converted to a + * Spliterator for creating a Java Stream. + * + * The `asJavaSeqStream ` extension method is available on any Scala collection. The + * `asJavaParStream` extension method can only be invoked on collections where the return type of + * the [[scala.collection.IterableOnce.stepper `stepper`]] method is marked with the + * [[scala.collection.Stepper.EfficientSplit]] marker trait. This trait is added to steppers that + * support partitioning, and therefore efficient parallel processing. + * + * The following extension methods are available: + * + * | Collection Type | Extension Methods | + * | --- | --- | + * | `IterableOnce` | `asJavaSeqStream` | + * | `IndexedSeq`, Arrays, `BitSet`, `Accumulator`, `HashMap`, `HashSet`, `Range`, `TreeMap`, `TreeSet`, `Vector`, Strings | `asJavaParStream` | + * | `Map` | `asJavaSeqKeyStream`, `asJavaSeqValueStream` | + * | `HashMap`, `TreeMap` | `asJavaParKeyStream`, `asJavaParValueStream` | + * | `Stepper` | `asJavaSeqStream` | + * | `Stepper with EfficientSplit` | `asJavaParStream` | + * | Strings | `asJavaSeqStream`, `asJavaParStream`, `asJavaSeqCharStream`, `asJavaParCharStream`, `asJavaSeqCodePointStream`, `asJavaParCodePointStream` | + * | Java streams | `toScala`, `asJavaPrimitiveStream` | + * + * The `asJavaPrimitiveStream` method converts a `Stream[Int]` to an `IntStream`. It is the dual + * of the `boxed` method defined on primitive streams (e.g., `IntStream.boxed` is a + * `Stream[Integer]`). + * + * The `toScala` extension methods on Java streams collects the result of a stream pipeline into a + * Scala collection, for example `stream.toScala(List)`, `stream.toScala(Vector)`. Note that + * transformation operations on streams are lazy (also called "intermediate"), terminal operations + * such as `forEach`, `count` or `toScala` trigger the evaluation. + * + * Collecting a parallel stream to a collection can be performed in parallel. This is beneficial if + * the target collection supports efficient merging of the segments that are built in parallel. + * To support this use case, the Scala standard library provides the [[Accumulator]] collection. + * This collection supports efficient parallel construction, and it has specialized subtypes for + * `Int`, `Long` and `Double` so that primitive Java streams can be collected to a Scala collection + * without boxing the elements. + */ +object StreamConverters extends StreamExtensions diff --git a/library/src/scala/jdk/javaapi/CollectionConverters.scala b/library/src/scala/jdk/javaapi/CollectionConverters.scala new file mode 100644 index 000000000000..80c2d7c0b8a9 --- /dev/null +++ b/library/src/scala/jdk/javaapi/CollectionConverters.scala @@ -0,0 +1,77 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk.javaapi + +import scala.collection.convert.{AsJavaConverters, AsScalaConverters} + +/** This object contains methods that convert between Scala and Java collections. + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in + * [[scala.jdk.CollectionConverters]]. + * + * Note: to create [[java.util.stream.Stream Java Streams]] that operate on Scala collections + * (sequentially or in parallel), use [[StreamConverters]]. + * + * {{{ + * // Java Code + * import scala.jdk.javaapi.CollectionConverters; + * public class A { + * public void t(scala.collection.immutable.List l) { + * java.util.List jl = CollectionConverters.asJava(l); + * } + * } + * }}} + * + * The conversions return adapters for the corresponding API, i.e., the collections are wrapped, + * not converted. Changes to the original collection are reflected in the view, and vice versa. + * + * The following conversions are supported via `asScala` and `asJava`: + * + * {{{ + * scala.collection.Iterable <=> java.lang.Iterable + * scala.collection.Iterator <=> java.util.Iterator + * scala.collection.mutable.Buffer <=> java.util.List + * scala.collection.mutable.Set <=> java.util.Set + * scala.collection.mutable.Map <=> java.util.Map + * scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap + * }}} + * + * The following conversions are supported via `asScala` and through + * specially-named methods to convert to Java collections, as shown: + * + * {{{ + * scala.collection.Iterable <=> java.util.Collection (via asJavaCollection) + * scala.collection.Iterator <=> java.util.Enumeration (via asJavaEnumeration) + * scala.collection.mutable.Map <=> java.util.Dictionary (via asJavaDictionary) + * }}} + * + * In addition, the following one-way conversions are provided via `asJava`: + * + * {{{ + * scala.collection.Seq => java.util.List + * scala.collection.mutable.Seq => java.util.List + * scala.collection.Set => java.util.Set + * scala.collection.Map => java.util.Map + * }}} + * + * The following one way conversion is provided via `asScala`: + * + * {{{ + * java.util.Properties => scala.collection.mutable.Map + * }}} + * + * In all cases, converting from a source type to a target type and back + * again will return the original source object. + */ +object CollectionConverters extends AsJavaConverters with AsScalaConverters diff --git a/library/src/scala/jdk/javaapi/DurationConverters.scala b/library/src/scala/jdk/javaapi/DurationConverters.scala new file mode 100644 index 000000000000..00285a11c41a --- /dev/null +++ b/library/src/scala/jdk/javaapi/DurationConverters.scala @@ -0,0 +1,73 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk.javaapi + +import java.time.temporal.ChronoUnit +import java.time.{Duration => JDuration} +import java.util.concurrent.TimeUnit + +import scala.concurrent.duration.{Duration, FiniteDuration} + +/** This object contains methods that convert between Scala and Java duration types. + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in [[scala.jdk.DurationConverters]]. + */ +object DurationConverters { + /** Convert a Java duration to a Scala duration. If the nanosecond part of the Java duration is + * zero, the returned duration will have a time unit of seconds. If there is a nanoseconds part, + * the Scala duration will have a time unit of nanoseconds. + * + * @throws IllegalArgumentException If the given Java Duration is out of bounds of what can be + * expressed by [[scala.concurrent.duration.FiniteDuration]]. + */ + def toScala(duration: JDuration): FiniteDuration = { + val originalSeconds = duration.getSeconds + val originalNanos = duration.getNano + if (originalNanos == 0) { + if (originalSeconds == 0) Duration.Zero + else FiniteDuration(originalSeconds, TimeUnit.SECONDS) + } else if (originalSeconds == 0) { + FiniteDuration(originalNanos, TimeUnit.NANOSECONDS) + } else { + try { + val secondsAsNanos = Math.multiplyExact(originalSeconds, 1000000000) + val totalNanos = secondsAsNanos + originalNanos + if ((totalNanos < 0 && secondsAsNanos < 0) || (totalNanos > 0 && secondsAsNanos > 0)) + FiniteDuration(totalNanos, TimeUnit.NANOSECONDS) + else + throw new ArithmeticException() + } catch { + case _: ArithmeticException => + throw new IllegalArgumentException(s"Java duration $duration cannot be expressed as a Scala duration") + } + } + } + + /** Convert a Scala `FiniteDuration` to a Java duration. Note that the Scala duration keeps the + * time unit it was created with, while a Java duration always is a pair of seconds and nanos, + * so the unit it lost. + */ + def toJava(duration: FiniteDuration): JDuration = { + if (duration.length == 0) JDuration.ZERO + else duration.unit match { + case TimeUnit.NANOSECONDS => JDuration.ofNanos(duration.length) + case TimeUnit.MICROSECONDS => JDuration.of(duration.length, ChronoUnit.MICROS) + case TimeUnit.MILLISECONDS => JDuration.ofMillis(duration.length) + case TimeUnit.SECONDS => JDuration.ofSeconds(duration.length) + case TimeUnit.MINUTES => JDuration.ofMinutes(duration.length) + case TimeUnit.HOURS => JDuration.ofHours(duration.length) + case TimeUnit.DAYS => JDuration.ofDays(duration.length) + } + } +} diff --git a/library/src/scala/jdk/javaapi/FunctionConverters.scala b/library/src/scala/jdk/javaapi/FunctionConverters.scala new file mode 100644 index 000000000000..22c9769bbf42 --- /dev/null +++ b/library/src/scala/jdk/javaapi/FunctionConverters.scala @@ -0,0 +1,956 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. + + +package scala.jdk.javaapi + +/** This object contains methods that convert between Scala and Java function types. + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in [[scala.jdk.FunctionConverters]]. + * + * For details how the function converters work, see [[scala.jdk.FunctionConverters]]. + * + */ +object FunctionConverters { + import scala.jdk.FunctionWrappers._ + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromBiConsumer[T, U](jf: java.util.function.BiConsumer[T, U]): scala.Function2[T, U, scala.runtime.BoxedUnit] = jf match { + case AsJavaBiConsumer((f @ _)) => f.asInstanceOf[scala.Function2[T, U, scala.runtime.BoxedUnit]] + case _ => new FromJavaBiConsumer[T, U](jf).asInstanceOf[scala.Function2[T, U, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaBiConsumer[T, U](sf: scala.Function2[T, U, scala.runtime.BoxedUnit]): java.util.function.BiConsumer[T, U] = ((sf): AnyRef) match { + case FromJavaBiConsumer((f @ _)) => f.asInstanceOf[java.util.function.BiConsumer[T, U]] + case _ => new AsJavaBiConsumer[T, U](sf.asInstanceOf[scala.Function2[T, U, Unit]]) + } + + + @inline def asScalaFromBiFunction[T, U, R](jf: java.util.function.BiFunction[T, U, R]): scala.Function2[T, U, R] = jf match { + case AsJavaBiFunction((f @ _)) => f.asInstanceOf[scala.Function2[T, U, R]] + case _ => new FromJavaBiFunction[T, U, R](jf).asInstanceOf[scala.Function2[T, U, R]] + } + + @inline def asJavaBiFunction[T, U, R](sf: scala.Function2[T, U, R]): java.util.function.BiFunction[T, U, R] = ((sf): AnyRef) match { + case FromJavaBiFunction((f @ _)) => f.asInstanceOf[java.util.function.BiFunction[T, U, R]] + case _ => new AsJavaBiFunction[T, U, R](sf.asInstanceOf[scala.Function2[T, U, R]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromBiPredicate[T, U](jf: java.util.function.BiPredicate[T, U]): scala.Function2[T, U, java.lang.Boolean] = jf match { + case AsJavaBiPredicate((f @ _)) => f.asInstanceOf[scala.Function2[T, U, java.lang.Boolean]] + case _ => new FromJavaBiPredicate[T, U](jf).asInstanceOf[scala.Function2[T, U, java.lang.Boolean]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaBiPredicate[T, U](sf: scala.Function2[T, U, java.lang.Boolean]): java.util.function.BiPredicate[T, U] = ((sf): AnyRef) match { + case FromJavaBiPredicate((f @ _)) => f.asInstanceOf[java.util.function.BiPredicate[T, U]] + case _ => new AsJavaBiPredicate[T, U](sf.asInstanceOf[scala.Function2[T, U, Boolean]]) + } + + + @inline def asScalaFromBinaryOperator[T](jf: java.util.function.BinaryOperator[T]): scala.Function2[T, T, T] = jf match { + case AsJavaBinaryOperator((f @ _)) => f.asInstanceOf[scala.Function2[T, T, T]] + case _ => new FromJavaBinaryOperator[T](jf).asInstanceOf[scala.Function2[T, T, T]] + } + + @inline def asJavaBinaryOperator[T](sf: scala.Function2[T, T, T]): java.util.function.BinaryOperator[T] = ((sf): AnyRef) match { + case FromJavaBinaryOperator((f @ _)) => f.asInstanceOf[java.util.function.BinaryOperator[T]] + case _ => new AsJavaBinaryOperator[T](sf.asInstanceOf[scala.Function2[T, T, T]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromBooleanSupplier(jf: java.util.function.BooleanSupplier): scala.Function0[java.lang.Boolean] = jf match { + case AsJavaBooleanSupplier((f @ _)) => f.asInstanceOf[scala.Function0[java.lang.Boolean]] + case _ => new FromJavaBooleanSupplier(jf).asInstanceOf[scala.Function0[java.lang.Boolean]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaBooleanSupplier(sf: scala.Function0[java.lang.Boolean]): java.util.function.BooleanSupplier = ((sf): AnyRef) match { + case FromJavaBooleanSupplier((f @ _)) => f.asInstanceOf[java.util.function.BooleanSupplier] + case _ => new AsJavaBooleanSupplier(sf.asInstanceOf[scala.Function0[Boolean]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromConsumer[T](jf: java.util.function.Consumer[T]): scala.Function1[T, scala.runtime.BoxedUnit] = jf match { + case AsJavaConsumer((f @ _)) => f.asInstanceOf[scala.Function1[T, scala.runtime.BoxedUnit]] + case _ => new FromJavaConsumer[T](jf).asInstanceOf[scala.Function1[T, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaConsumer[T](sf: scala.Function1[T, scala.runtime.BoxedUnit]): java.util.function.Consumer[T] = ((sf): AnyRef) match { + case FromJavaConsumer((f @ _)) => f.asInstanceOf[java.util.function.Consumer[T]] + case _ => new AsJavaConsumer[T](sf.asInstanceOf[scala.Function1[T, Unit]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoubleBinaryOperator(jf: java.util.function.DoubleBinaryOperator): scala.Function2[java.lang.Double, java.lang.Double, java.lang.Double] = jf match { + case AsJavaDoubleBinaryOperator((f @ _)) => f.asInstanceOf[scala.Function2[java.lang.Double, java.lang.Double, java.lang.Double]] + case _ => new FromJavaDoubleBinaryOperator(jf).asInstanceOf[scala.Function2[java.lang.Double, java.lang.Double, java.lang.Double]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoubleBinaryOperator(sf: scala.Function2[java.lang.Double, java.lang.Double, java.lang.Double]): java.util.function.DoubleBinaryOperator = ((sf): AnyRef) match { + case FromJavaDoubleBinaryOperator((f @ _)) => f.asInstanceOf[java.util.function.DoubleBinaryOperator] + case _ => new AsJavaDoubleBinaryOperator(sf.asInstanceOf[scala.Function2[Double, Double, Double]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoubleConsumer(jf: java.util.function.DoubleConsumer): scala.Function1[java.lang.Double, scala.runtime.BoxedUnit] = jf match { + case AsJavaDoubleConsumer((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Double, scala.runtime.BoxedUnit]] + case _ => new FromJavaDoubleConsumer(jf).asInstanceOf[scala.Function1[java.lang.Double, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoubleConsumer(sf: scala.Function1[java.lang.Double, scala.runtime.BoxedUnit]): java.util.function.DoubleConsumer = ((sf): AnyRef) match { + case FromJavaDoubleConsumer((f @ _)) => f.asInstanceOf[java.util.function.DoubleConsumer] + case _ => new AsJavaDoubleConsumer(sf.asInstanceOf[scala.Function1[Double, Unit]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoubleFunction[R](jf: java.util.function.DoubleFunction[R]): scala.Function1[java.lang.Double, R] = jf match { + case AsJavaDoubleFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Double, R]] + case _ => new FromJavaDoubleFunction[R](jf).asInstanceOf[scala.Function1[java.lang.Double, R]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoubleFunction[R](sf: scala.Function1[java.lang.Double, R]): java.util.function.DoubleFunction[R] = ((sf): AnyRef) match { + case FromJavaDoubleFunction((f @ _)) => f.asInstanceOf[java.util.function.DoubleFunction[R]] + case _ => new AsJavaDoubleFunction[R](sf.asInstanceOf[scala.Function1[Double, R]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoublePredicate(jf: java.util.function.DoublePredicate): scala.Function1[java.lang.Double, java.lang.Boolean] = jf match { + case AsJavaDoublePredicate((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Double, java.lang.Boolean]] + case _ => new FromJavaDoublePredicate(jf).asInstanceOf[scala.Function1[java.lang.Double, java.lang.Boolean]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoublePredicate(sf: scala.Function1[java.lang.Double, java.lang.Boolean]): java.util.function.DoublePredicate = ((sf): AnyRef) match { + case FromJavaDoublePredicate((f @ _)) => f.asInstanceOf[java.util.function.DoublePredicate] + case _ => new AsJavaDoublePredicate(sf.asInstanceOf[scala.Function1[Double, Boolean]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoubleSupplier(jf: java.util.function.DoubleSupplier): scala.Function0[java.lang.Double] = jf match { + case AsJavaDoubleSupplier((f @ _)) => f.asInstanceOf[scala.Function0[java.lang.Double]] + case _ => new FromJavaDoubleSupplier(jf).asInstanceOf[scala.Function0[java.lang.Double]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoubleSupplier(sf: scala.Function0[java.lang.Double]): java.util.function.DoubleSupplier = ((sf): AnyRef) match { + case FromJavaDoubleSupplier((f @ _)) => f.asInstanceOf[java.util.function.DoubleSupplier] + case _ => new AsJavaDoubleSupplier(sf.asInstanceOf[scala.Function0[Double]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoubleToIntFunction(jf: java.util.function.DoubleToIntFunction): scala.Function1[java.lang.Double, java.lang.Integer] = jf match { + case AsJavaDoubleToIntFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Double, java.lang.Integer]] + case _ => new FromJavaDoubleToIntFunction(jf).asInstanceOf[scala.Function1[java.lang.Double, java.lang.Integer]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoubleToIntFunction(sf: scala.Function1[java.lang.Double, java.lang.Integer]): java.util.function.DoubleToIntFunction = ((sf): AnyRef) match { + case FromJavaDoubleToIntFunction((f @ _)) => f.asInstanceOf[java.util.function.DoubleToIntFunction] + case _ => new AsJavaDoubleToIntFunction(sf.asInstanceOf[scala.Function1[Double, Int]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoubleToLongFunction(jf: java.util.function.DoubleToLongFunction): scala.Function1[java.lang.Double, java.lang.Long] = jf match { + case AsJavaDoubleToLongFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Double, java.lang.Long]] + case _ => new FromJavaDoubleToLongFunction(jf).asInstanceOf[scala.Function1[java.lang.Double, java.lang.Long]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoubleToLongFunction(sf: scala.Function1[java.lang.Double, java.lang.Long]): java.util.function.DoubleToLongFunction = ((sf): AnyRef) match { + case FromJavaDoubleToLongFunction((f @ _)) => f.asInstanceOf[java.util.function.DoubleToLongFunction] + case _ => new AsJavaDoubleToLongFunction(sf.asInstanceOf[scala.Function1[Double, Long]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromDoubleUnaryOperator(jf: java.util.function.DoubleUnaryOperator): scala.Function1[java.lang.Double, java.lang.Double] = jf match { + case AsJavaDoubleUnaryOperator((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Double, java.lang.Double]] + case _ => new FromJavaDoubleUnaryOperator(jf).asInstanceOf[scala.Function1[java.lang.Double, java.lang.Double]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaDoubleUnaryOperator(sf: scala.Function1[java.lang.Double, java.lang.Double]): java.util.function.DoubleUnaryOperator = ((sf): AnyRef) match { + case FromJavaDoubleUnaryOperator((f @ _)) => f.asInstanceOf[java.util.function.DoubleUnaryOperator] + case _ => new AsJavaDoubleUnaryOperator(sf.asInstanceOf[scala.Function1[Double, Double]]) + } + + + @inline def asScalaFromFunction[T, R](jf: java.util.function.Function[T, R]): scala.Function1[T, R] = jf match { + case AsJavaFunction((f @ _)) => f.asInstanceOf[scala.Function1[T, R]] + case _ => new FromJavaFunction[T, R](jf).asInstanceOf[scala.Function1[T, R]] + } + + @inline def asJavaFunction[T, R](sf: scala.Function1[T, R]): java.util.function.Function[T, R] = ((sf): AnyRef) match { + case FromJavaFunction((f @ _)) => f.asInstanceOf[java.util.function.Function[T, R]] + case _ => new AsJavaFunction[T, R](sf.asInstanceOf[scala.Function1[T, R]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntBinaryOperator(jf: java.util.function.IntBinaryOperator): scala.Function2[java.lang.Integer, java.lang.Integer, java.lang.Integer] = jf match { + case AsJavaIntBinaryOperator((f @ _)) => f.asInstanceOf[scala.Function2[java.lang.Integer, java.lang.Integer, java.lang.Integer]] + case _ => new FromJavaIntBinaryOperator(jf).asInstanceOf[scala.Function2[java.lang.Integer, java.lang.Integer, java.lang.Integer]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntBinaryOperator(sf: scala.Function2[java.lang.Integer, java.lang.Integer, java.lang.Integer]): java.util.function.IntBinaryOperator = ((sf): AnyRef) match { + case FromJavaIntBinaryOperator((f @ _)) => f.asInstanceOf[java.util.function.IntBinaryOperator] + case _ => new AsJavaIntBinaryOperator(sf.asInstanceOf[scala.Function2[Int, Int, Int]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntConsumer(jf: java.util.function.IntConsumer): scala.Function1[java.lang.Integer, scala.runtime.BoxedUnit] = jf match { + case AsJavaIntConsumer((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Integer, scala.runtime.BoxedUnit]] + case _ => new FromJavaIntConsumer(jf).asInstanceOf[scala.Function1[java.lang.Integer, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntConsumer(sf: scala.Function1[java.lang.Integer, scala.runtime.BoxedUnit]): java.util.function.IntConsumer = ((sf): AnyRef) match { + case FromJavaIntConsumer((f @ _)) => f.asInstanceOf[java.util.function.IntConsumer] + case _ => new AsJavaIntConsumer(sf.asInstanceOf[scala.Function1[Int, Unit]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntFunction[R](jf: java.util.function.IntFunction[R]): scala.Function1[java.lang.Integer, R] = jf match { + case AsJavaIntFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Integer, R]] + case _ => new FromJavaIntFunction[R](jf).asInstanceOf[scala.Function1[java.lang.Integer, R]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntFunction[R](sf: scala.Function1[java.lang.Integer, R]): java.util.function.IntFunction[R] = ((sf): AnyRef) match { + case FromJavaIntFunction((f @ _)) => f.asInstanceOf[java.util.function.IntFunction[R]] + case _ => new AsJavaIntFunction[R](sf.asInstanceOf[scala.Function1[Int, R]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntPredicate(jf: java.util.function.IntPredicate): scala.Function1[java.lang.Integer, java.lang.Boolean] = jf match { + case AsJavaIntPredicate((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Boolean]] + case _ => new FromJavaIntPredicate(jf).asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Boolean]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntPredicate(sf: scala.Function1[java.lang.Integer, java.lang.Boolean]): java.util.function.IntPredicate = ((sf): AnyRef) match { + case FromJavaIntPredicate((f @ _)) => f.asInstanceOf[java.util.function.IntPredicate] + case _ => new AsJavaIntPredicate(sf.asInstanceOf[scala.Function1[Int, Boolean]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntSupplier(jf: java.util.function.IntSupplier): scala.Function0[java.lang.Integer] = jf match { + case AsJavaIntSupplier((f @ _)) => f.asInstanceOf[scala.Function0[java.lang.Integer]] + case _ => new FromJavaIntSupplier(jf).asInstanceOf[scala.Function0[java.lang.Integer]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntSupplier(sf: scala.Function0[java.lang.Integer]): java.util.function.IntSupplier = ((sf): AnyRef) match { + case FromJavaIntSupplier((f @ _)) => f.asInstanceOf[java.util.function.IntSupplier] + case _ => new AsJavaIntSupplier(sf.asInstanceOf[scala.Function0[Int]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntToDoubleFunction(jf: java.util.function.IntToDoubleFunction): scala.Function1[java.lang.Integer, java.lang.Double] = jf match { + case AsJavaIntToDoubleFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Double]] + case _ => new FromJavaIntToDoubleFunction(jf).asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Double]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntToDoubleFunction(sf: scala.Function1[java.lang.Integer, java.lang.Double]): java.util.function.IntToDoubleFunction = ((sf): AnyRef) match { + case FromJavaIntToDoubleFunction((f @ _)) => f.asInstanceOf[java.util.function.IntToDoubleFunction] + case _ => new AsJavaIntToDoubleFunction(sf.asInstanceOf[scala.Function1[Int, Double]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntToLongFunction(jf: java.util.function.IntToLongFunction): scala.Function1[java.lang.Integer, java.lang.Long] = jf match { + case AsJavaIntToLongFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Long]] + case _ => new FromJavaIntToLongFunction(jf).asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Long]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntToLongFunction(sf: scala.Function1[java.lang.Integer, java.lang.Long]): java.util.function.IntToLongFunction = ((sf): AnyRef) match { + case FromJavaIntToLongFunction((f @ _)) => f.asInstanceOf[java.util.function.IntToLongFunction] + case _ => new AsJavaIntToLongFunction(sf.asInstanceOf[scala.Function1[Int, Long]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromIntUnaryOperator(jf: java.util.function.IntUnaryOperator): scala.Function1[java.lang.Integer, java.lang.Integer] = jf match { + case AsJavaIntUnaryOperator((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Integer]] + case _ => new FromJavaIntUnaryOperator(jf).asInstanceOf[scala.Function1[java.lang.Integer, java.lang.Integer]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaIntUnaryOperator(sf: scala.Function1[java.lang.Integer, java.lang.Integer]): java.util.function.IntUnaryOperator = ((sf): AnyRef) match { + case FromJavaIntUnaryOperator((f @ _)) => f.asInstanceOf[java.util.function.IntUnaryOperator] + case _ => new AsJavaIntUnaryOperator(sf.asInstanceOf[scala.Function1[Int, Int]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongBinaryOperator(jf: java.util.function.LongBinaryOperator): scala.Function2[java.lang.Long, java.lang.Long, java.lang.Long] = jf match { + case AsJavaLongBinaryOperator((f @ _)) => f.asInstanceOf[scala.Function2[java.lang.Long, java.lang.Long, java.lang.Long]] + case _ => new FromJavaLongBinaryOperator(jf).asInstanceOf[scala.Function2[java.lang.Long, java.lang.Long, java.lang.Long]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongBinaryOperator(sf: scala.Function2[java.lang.Long, java.lang.Long, java.lang.Long]): java.util.function.LongBinaryOperator = ((sf): AnyRef) match { + case FromJavaLongBinaryOperator((f @ _)) => f.asInstanceOf[java.util.function.LongBinaryOperator] + case _ => new AsJavaLongBinaryOperator(sf.asInstanceOf[scala.Function2[Long, Long, Long]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongConsumer(jf: java.util.function.LongConsumer): scala.Function1[java.lang.Long, scala.runtime.BoxedUnit] = jf match { + case AsJavaLongConsumer((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Long, scala.runtime.BoxedUnit]] + case _ => new FromJavaLongConsumer(jf).asInstanceOf[scala.Function1[java.lang.Long, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongConsumer(sf: scala.Function1[java.lang.Long, scala.runtime.BoxedUnit]): java.util.function.LongConsumer = ((sf): AnyRef) match { + case FromJavaLongConsumer((f @ _)) => f.asInstanceOf[java.util.function.LongConsumer] + case _ => new AsJavaLongConsumer(sf.asInstanceOf[scala.Function1[Long, Unit]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongFunction[R](jf: java.util.function.LongFunction[R]): scala.Function1[java.lang.Long, R] = jf match { + case AsJavaLongFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Long, R]] + case _ => new FromJavaLongFunction[R](jf).asInstanceOf[scala.Function1[java.lang.Long, R]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongFunction[R](sf: scala.Function1[java.lang.Long, R]): java.util.function.LongFunction[R] = ((sf): AnyRef) match { + case FromJavaLongFunction((f @ _)) => f.asInstanceOf[java.util.function.LongFunction[R]] + case _ => new AsJavaLongFunction[R](sf.asInstanceOf[scala.Function1[Long, R]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongPredicate(jf: java.util.function.LongPredicate): scala.Function1[java.lang.Long, java.lang.Boolean] = jf match { + case AsJavaLongPredicate((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Long, java.lang.Boolean]] + case _ => new FromJavaLongPredicate(jf).asInstanceOf[scala.Function1[java.lang.Long, java.lang.Boolean]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongPredicate(sf: scala.Function1[java.lang.Long, java.lang.Boolean]): java.util.function.LongPredicate = ((sf): AnyRef) match { + case FromJavaLongPredicate((f @ _)) => f.asInstanceOf[java.util.function.LongPredicate] + case _ => new AsJavaLongPredicate(sf.asInstanceOf[scala.Function1[Long, Boolean]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongSupplier(jf: java.util.function.LongSupplier): scala.Function0[java.lang.Long] = jf match { + case AsJavaLongSupplier((f @ _)) => f.asInstanceOf[scala.Function0[java.lang.Long]] + case _ => new FromJavaLongSupplier(jf).asInstanceOf[scala.Function0[java.lang.Long]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongSupplier(sf: scala.Function0[java.lang.Long]): java.util.function.LongSupplier = ((sf): AnyRef) match { + case FromJavaLongSupplier((f @ _)) => f.asInstanceOf[java.util.function.LongSupplier] + case _ => new AsJavaLongSupplier(sf.asInstanceOf[scala.Function0[Long]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongToDoubleFunction(jf: java.util.function.LongToDoubleFunction): scala.Function1[java.lang.Long, java.lang.Double] = jf match { + case AsJavaLongToDoubleFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Long, java.lang.Double]] + case _ => new FromJavaLongToDoubleFunction(jf).asInstanceOf[scala.Function1[java.lang.Long, java.lang.Double]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongToDoubleFunction(sf: scala.Function1[java.lang.Long, java.lang.Double]): java.util.function.LongToDoubleFunction = ((sf): AnyRef) match { + case FromJavaLongToDoubleFunction((f @ _)) => f.asInstanceOf[java.util.function.LongToDoubleFunction] + case _ => new AsJavaLongToDoubleFunction(sf.asInstanceOf[scala.Function1[Long, Double]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongToIntFunction(jf: java.util.function.LongToIntFunction): scala.Function1[java.lang.Long, java.lang.Integer] = jf match { + case AsJavaLongToIntFunction((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Long, java.lang.Integer]] + case _ => new FromJavaLongToIntFunction(jf).asInstanceOf[scala.Function1[java.lang.Long, java.lang.Integer]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongToIntFunction(sf: scala.Function1[java.lang.Long, java.lang.Integer]): java.util.function.LongToIntFunction = ((sf): AnyRef) match { + case FromJavaLongToIntFunction((f @ _)) => f.asInstanceOf[java.util.function.LongToIntFunction] + case _ => new AsJavaLongToIntFunction(sf.asInstanceOf[scala.Function1[Long, Int]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromLongUnaryOperator(jf: java.util.function.LongUnaryOperator): scala.Function1[java.lang.Long, java.lang.Long] = jf match { + case AsJavaLongUnaryOperator((f @ _)) => f.asInstanceOf[scala.Function1[java.lang.Long, java.lang.Long]] + case _ => new FromJavaLongUnaryOperator(jf).asInstanceOf[scala.Function1[java.lang.Long, java.lang.Long]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaLongUnaryOperator(sf: scala.Function1[java.lang.Long, java.lang.Long]): java.util.function.LongUnaryOperator = ((sf): AnyRef) match { + case FromJavaLongUnaryOperator((f @ _)) => f.asInstanceOf[java.util.function.LongUnaryOperator] + case _ => new AsJavaLongUnaryOperator(sf.asInstanceOf[scala.Function1[Long, Long]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromObjDoubleConsumer[T](jf: java.util.function.ObjDoubleConsumer[T]): scala.Function2[T, java.lang.Double, scala.runtime.BoxedUnit] = jf match { + case AsJavaObjDoubleConsumer((f @ _)) => f.asInstanceOf[scala.Function2[T, java.lang.Double, scala.runtime.BoxedUnit]] + case _ => new FromJavaObjDoubleConsumer[T](jf).asInstanceOf[scala.Function2[T, java.lang.Double, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaObjDoubleConsumer[T](sf: scala.Function2[T, java.lang.Double, scala.runtime.BoxedUnit]): java.util.function.ObjDoubleConsumer[T] = ((sf): AnyRef) match { + case FromJavaObjDoubleConsumer((f @ _)) => f.asInstanceOf[java.util.function.ObjDoubleConsumer[T]] + case _ => new AsJavaObjDoubleConsumer[T](sf.asInstanceOf[scala.Function2[T, Double, Unit]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromObjIntConsumer[T](jf: java.util.function.ObjIntConsumer[T]): scala.Function2[T, java.lang.Integer, scala.runtime.BoxedUnit] = jf match { + case AsJavaObjIntConsumer((f @ _)) => f.asInstanceOf[scala.Function2[T, java.lang.Integer, scala.runtime.BoxedUnit]] + case _ => new FromJavaObjIntConsumer[T](jf).asInstanceOf[scala.Function2[T, java.lang.Integer, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaObjIntConsumer[T](sf: scala.Function2[T, java.lang.Integer, scala.runtime.BoxedUnit]): java.util.function.ObjIntConsumer[T] = ((sf): AnyRef) match { + case FromJavaObjIntConsumer((f @ _)) => f.asInstanceOf[java.util.function.ObjIntConsumer[T]] + case _ => new AsJavaObjIntConsumer[T](sf.asInstanceOf[scala.Function2[T, Int, Unit]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromObjLongConsumer[T](jf: java.util.function.ObjLongConsumer[T]): scala.Function2[T, java.lang.Long, scala.runtime.BoxedUnit] = jf match { + case AsJavaObjLongConsumer((f @ _)) => f.asInstanceOf[scala.Function2[T, java.lang.Long, scala.runtime.BoxedUnit]] + case _ => new FromJavaObjLongConsumer[T](jf).asInstanceOf[scala.Function2[T, java.lang.Long, scala.runtime.BoxedUnit]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaObjLongConsumer[T](sf: scala.Function2[T, java.lang.Long, scala.runtime.BoxedUnit]): java.util.function.ObjLongConsumer[T] = ((sf): AnyRef) match { + case FromJavaObjLongConsumer((f @ _)) => f.asInstanceOf[java.util.function.ObjLongConsumer[T]] + case _ => new AsJavaObjLongConsumer[T](sf.asInstanceOf[scala.Function2[T, Long, Unit]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromPredicate[T](jf: java.util.function.Predicate[T]): scala.Function1[T, java.lang.Boolean] = jf match { + case AsJavaPredicate((f @ _)) => f.asInstanceOf[scala.Function1[T, java.lang.Boolean]] + case _ => new FromJavaPredicate[T](jf).asInstanceOf[scala.Function1[T, java.lang.Boolean]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaPredicate[T](sf: scala.Function1[T, java.lang.Boolean]): java.util.function.Predicate[T] = ((sf): AnyRef) match { + case FromJavaPredicate((f @ _)) => f.asInstanceOf[java.util.function.Predicate[T]] + case _ => new AsJavaPredicate[T](sf.asInstanceOf[scala.Function1[T, Boolean]]) + } + + + @inline def asScalaFromSupplier[T](jf: java.util.function.Supplier[T]): scala.Function0[T] = jf match { + case AsJavaSupplier((f @ _)) => f.asInstanceOf[scala.Function0[T]] + case _ => new FromJavaSupplier[T](jf).asInstanceOf[scala.Function0[T]] + } + + @inline def asJavaSupplier[T](sf: scala.Function0[T]): java.util.function.Supplier[T] = ((sf): AnyRef) match { + case FromJavaSupplier((f @ _)) => f.asInstanceOf[java.util.function.Supplier[T]] + case _ => new AsJavaSupplier[T](sf.asInstanceOf[scala.Function0[T]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromToDoubleBiFunction[T, U](jf: java.util.function.ToDoubleBiFunction[T, U]): scala.Function2[T, U, java.lang.Double] = jf match { + case AsJavaToDoubleBiFunction((f @ _)) => f.asInstanceOf[scala.Function2[T, U, java.lang.Double]] + case _ => new FromJavaToDoubleBiFunction[T, U](jf).asInstanceOf[scala.Function2[T, U, java.lang.Double]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaToDoubleBiFunction[T, U](sf: scala.Function2[T, U, java.lang.Double]): java.util.function.ToDoubleBiFunction[T, U] = ((sf): AnyRef) match { + case FromJavaToDoubleBiFunction((f @ _)) => f.asInstanceOf[java.util.function.ToDoubleBiFunction[T, U]] + case _ => new AsJavaToDoubleBiFunction[T, U](sf.asInstanceOf[scala.Function2[T, U, Double]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromToDoubleFunction[T](jf: java.util.function.ToDoubleFunction[T]): scala.Function1[T, java.lang.Double] = jf match { + case AsJavaToDoubleFunction((f @ _)) => f.asInstanceOf[scala.Function1[T, java.lang.Double]] + case _ => new FromJavaToDoubleFunction[T](jf).asInstanceOf[scala.Function1[T, java.lang.Double]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaToDoubleFunction[T](sf: scala.Function1[T, java.lang.Double]): java.util.function.ToDoubleFunction[T] = ((sf): AnyRef) match { + case FromJavaToDoubleFunction((f @ _)) => f.asInstanceOf[java.util.function.ToDoubleFunction[T]] + case _ => new AsJavaToDoubleFunction[T](sf.asInstanceOf[scala.Function1[T, Double]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromToIntBiFunction[T, U](jf: java.util.function.ToIntBiFunction[T, U]): scala.Function2[T, U, java.lang.Integer] = jf match { + case AsJavaToIntBiFunction((f @ _)) => f.asInstanceOf[scala.Function2[T, U, java.lang.Integer]] + case _ => new FromJavaToIntBiFunction[T, U](jf).asInstanceOf[scala.Function2[T, U, java.lang.Integer]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaToIntBiFunction[T, U](sf: scala.Function2[T, U, java.lang.Integer]): java.util.function.ToIntBiFunction[T, U] = ((sf): AnyRef) match { + case FromJavaToIntBiFunction((f @ _)) => f.asInstanceOf[java.util.function.ToIntBiFunction[T, U]] + case _ => new AsJavaToIntBiFunction[T, U](sf.asInstanceOf[scala.Function2[T, U, Int]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromToIntFunction[T](jf: java.util.function.ToIntFunction[T]): scala.Function1[T, java.lang.Integer] = jf match { + case AsJavaToIntFunction((f @ _)) => f.asInstanceOf[scala.Function1[T, java.lang.Integer]] + case _ => new FromJavaToIntFunction[T](jf).asInstanceOf[scala.Function1[T, java.lang.Integer]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaToIntFunction[T](sf: scala.Function1[T, java.lang.Integer]): java.util.function.ToIntFunction[T] = ((sf): AnyRef) match { + case FromJavaToIntFunction((f @ _)) => f.asInstanceOf[java.util.function.ToIntFunction[T]] + case _ => new AsJavaToIntFunction[T](sf.asInstanceOf[scala.Function1[T, Int]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromToLongBiFunction[T, U](jf: java.util.function.ToLongBiFunction[T, U]): scala.Function2[T, U, java.lang.Long] = jf match { + case AsJavaToLongBiFunction((f @ _)) => f.asInstanceOf[scala.Function2[T, U, java.lang.Long]] + case _ => new FromJavaToLongBiFunction[T, U](jf).asInstanceOf[scala.Function2[T, U, java.lang.Long]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaToLongBiFunction[T, U](sf: scala.Function2[T, U, java.lang.Long]): java.util.function.ToLongBiFunction[T, U] = ((sf): AnyRef) match { + case FromJavaToLongBiFunction((f @ _)) => f.asInstanceOf[java.util.function.ToLongBiFunction[T, U]] + case _ => new AsJavaToLongBiFunction[T, U](sf.asInstanceOf[scala.Function2[T, U, Long]]) + } + + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asScalaFromToLongFunction[T](jf: java.util.function.ToLongFunction[T]): scala.Function1[T, java.lang.Long] = jf match { + case AsJavaToLongFunction((f @ _)) => f.asInstanceOf[scala.Function1[T, java.lang.Long]] + case _ => new FromJavaToLongFunction[T](jf).asInstanceOf[scala.Function1[T, java.lang.Long]] + } + + /** Note: this method uses the boxed type `java.lang.X` (or `BoxedUnit`) instead of the + * primitive type `scala.X` to improve compatibility when using it in Java code (the + * Scala compiler emits `C[Int]` as `C[Object]` in bytecode due to + * [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In Scala code, add + * `import scala.jdk.FunctionConverters._` and use the extension methods instead. + */ + @inline def asJavaToLongFunction[T](sf: scala.Function1[T, java.lang.Long]): java.util.function.ToLongFunction[T] = ((sf): AnyRef) match { + case FromJavaToLongFunction((f @ _)) => f.asInstanceOf[java.util.function.ToLongFunction[T]] + case _ => new AsJavaToLongFunction[T](sf.asInstanceOf[scala.Function1[T, Long]]) + } + + + @inline def asScalaFromUnaryOperator[T](jf: java.util.function.UnaryOperator[T]): scala.Function1[T, T] = jf match { + case AsJavaUnaryOperator((f @ _)) => f.asInstanceOf[scala.Function1[T, T]] + case _ => new FromJavaUnaryOperator[T](jf).asInstanceOf[scala.Function1[T, T]] + } + + @inline def asJavaUnaryOperator[T](sf: scala.Function1[T, T]): java.util.function.UnaryOperator[T] = ((sf): AnyRef) match { + case FromJavaUnaryOperator((f @ _)) => f.asInstanceOf[java.util.function.UnaryOperator[T]] + case _ => new AsJavaUnaryOperator[T](sf.asInstanceOf[scala.Function1[T, T]]) + } +} diff --git a/library/src/scala/jdk/javaapi/FutureConverters.scala b/library/src/scala/jdk/javaapi/FutureConverters.scala new file mode 100644 index 000000000000..d28a8da8a92e --- /dev/null +++ b/library/src/scala/jdk/javaapi/FutureConverters.scala @@ -0,0 +1,89 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk.javaapi + +import java.util.concurrent.{CompletableFuture, CompletionStage} +import scala.concurrent.impl.FutureConvertersImpl.{CF, P} +import scala.concurrent.{ExecutionContext, Future} +import scala.util.Success + +/** This object contains methods that convert between Scala [[scala.concurrent.Future]] and Java [[java.util.concurrent.CompletionStage]]. + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in [[scala.jdk.FutureConverters]]. + * + * Note that the bridge is implemented at the read-only side of asynchronous handles, namely + * [[scala.concurrent.Future]] (instead of [[scala.concurrent.Promise]]) and [[java.util.concurrent.CompletionStage]] (instead of + * [[java.util.concurrent.CompletableFuture]]). This is intentional, as the semantics of bridging + * the write-handles would be prone to race conditions; if both ends (`CompletableFuture` and + * `Promise`) are completed independently at the same time, they may contain different values + * afterwards. For this reason, `toCompletableFuture` is not supported on the created + * `CompletionStage`s. + */ +object FutureConverters { + /** Returns a [[java.util.concurrent.CompletionStage]] that will be completed with the same value or exception as the + * given Scala [[scala.concurrent.Future]] when that completes. Since the Future is a read-only representation, + * this CompletionStage does not support the `toCompletableFuture` method. + * + * The semantics of Scala Future demand that all callbacks are invoked asynchronously by default, + * therefore the returned CompletionStage routes all calls to synchronous transformations to + * their asynchronous counterparts, i.e., `thenRun` will internally call `thenRunAsync`. + * + * @param f The Scala Future which may eventually supply the completion for the returned + * CompletionStage + * @return a CompletionStage that runs all callbacks asynchronously and does not support the + * CompletableFuture interface + */ + def asJava[T](f: Future[T]): CompletionStage[T] = { + f match { + case p: P[T] => p.wrapped + // in theory not safe (could be `class C extends Future[A] with CompletionStage[B]`): + case c: CompletionStage[T @unchecked] => c + case _ => + val cf = new CF[T](f) + f.onComplete(cf)(ExecutionContext.parasitic) + cf + } + } + + /** Returns a Scala [[scala.concurrent.Future]] that will be completed with the same value or exception as the + * given [[java.util.concurrent.CompletionStage]] when that completes. Transformations of the returned Future are + * executed asynchronously as specified by the ExecutionContext that is given to the combinator + * methods. + * + * @param cs The CompletionStage which may eventually supply the completion for the returned + * Scala Future + * @return a Scala Future that represents the CompletionStage's completion + */ + def asScala[T](cs: CompletionStage[T]): Future[T] = { + cs match { + case cf: CF[T] => cf.wrapped + // in theory not safe (could be `class C extends Future[A] with CompletionStage[B]`): + case f: Future[T @unchecked] => f + case _ => + val p = new P[T](cs) + val completedCF = cs match { + case cf0: CompletableFuture[T @unchecked] => + // drop `MinimalStage` (scala/bug#12918) + val cf = cf0.toCompletableFuture + if (cf.isDone && !cf.isCompletedExceptionally) cf else null + case _ => null + } + if (completedCF != null) + p.tryComplete(Success(completedCF.join())) + else + cs.handle(p) + p.future + } + } +} diff --git a/library/src/scala/jdk/javaapi/OptionConverters.scala b/library/src/scala/jdk/javaapi/OptionConverters.scala new file mode 100644 index 000000000000..27ae7b4e6060 --- /dev/null +++ b/library/src/scala/jdk/javaapi/OptionConverters.scala @@ -0,0 +1,84 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk.javaapi + +import java.util.{Optional, OptionalDouble, OptionalInt, OptionalLong} +import java.{lang => jl} + +/** This object contains methods that convert between Scala `Option` and Java `Optional` types. + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in [[scala.jdk.OptionConverters]]. + * + * @define primitiveNote Note: this method uses the boxed type `java.lang.X` instead of the + * primitive type `scala.X` to improve compatibility when using it in + * Java code (the Scala compiler emits `C[Int]` as `C[Object]` in bytecode + * due to [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In + * Scala code, add `import scala.jdk.OptionConverters._` and use the + * extension methods instead. + */ +object OptionConverters { + /** Convert a Scala `Option` to a Java `Optional` */ + def toJava[A](o: Option[A]): Optional[A] = o match { + case Some(a) => Optional.ofNullable(a) + case _ => Optional.empty[A] + } + + /** Convert a Scala `Option[java.lang.Double]` to a Java `OptionalDouble` + * + * $primitiveNote + */ + def toJavaOptionalDouble(o: Option[jl.Double]): OptionalDouble = o match { + case Some(a) => OptionalDouble.of(a) + case _ => OptionalDouble.empty + } + + /** Convert a Scala `Option[java.lang.Integer]` to a Java `OptionalInt` + * + * $primitiveNote + */ + def toJavaOptionalInt(o: Option[jl.Integer]): OptionalInt = o match { + case Some(a) => OptionalInt.of(a) + case _ => OptionalInt.empty + } + + /** Convert a Scala `Option[java.lang.Long]` to a Java `OptionalLong` + * + * $primitiveNote + */ + def toJavaOptionalLong(o: Option[jl.Long]): OptionalLong = o match { + case Some(a) => OptionalLong.of(a) + case _ => OptionalLong.empty + } + + /** Convert a Java `Optional` to a Scala `Option` */ + def toScala[A](o: Optional[A]): Option[A] = if (o.isPresent) Some(o.get) else None + + /** Convert a Java `OptionalDouble` to a Scala `Option[java.lang.Double]` + * + * $primitiveNote + */ + def toScala(o: OptionalDouble): Option[jl.Double] = if (o.isPresent) Some(o.getAsDouble) else None + + /** Convert a Java `OptionalInt` to a Scala `Option[java.lang.Integer]` + * + * $primitiveNote + */ + def toScala(o: OptionalInt): Option[jl.Integer] = if (o.isPresent) Some(o.getAsInt) else None + + /** Convert a Java `OptionalLong` to a Scala `Option[java.lang.Long]` + * + * $primitiveNote + */ + def toScala(o: OptionalLong): Option[jl.Long] = if (o.isPresent) Some(o.getAsLong) else None +} diff --git a/library/src/scala/jdk/javaapi/StreamConverters.scala b/library/src/scala/jdk/javaapi/StreamConverters.scala new file mode 100644 index 000000000000..d5adeb84ab71 --- /dev/null +++ b/library/src/scala/jdk/javaapi/StreamConverters.scala @@ -0,0 +1,356 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.jdk.javaapi + +import java.util.stream.{DoubleStream, IntStream, LongStream, Stream, StreamSupport} +import java.{lang => jl} + +/** This object contains methods to create Java Streams that operate on Scala collections + * (sequentially or in parallel). For more information on Java streams, consult the documentation + * ([[https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html]]). + * + * The explicit conversion methods defined here are intended to be used in Java code. For Scala + * code, it is recommended to use the extension methods defined in [[scala.jdk.StreamConverters]]. + * + * Note: to convert between Scala collections and classic Java collections, use + * [[CollectionConverters]]. + * + * For details how the stream converters work, see [[scala.jdk.StreamConverters]]. + * + * @define parNote Note: parallel processing is only efficient for collections that have a + * [[scala.collection.Stepper]] implementation which supports efficient splitting. For collections + * where this is the case, the [[scala.collection.IterableOnce.stepper `stepper`]] + * method has a return type marked `with EfficientSplit`. + * + * @define primitiveNote Note: this method uses the boxed type `java.lang.X` instead of the + * primitive type `scala.X` to improve compatibility when using it in + * Java code (the Scala compiler emits `C[Int]` as `C[Object]` in bytecode + * due to [[https://github.com/scala/bug/issues/4214 scala/bug#4214]]). In + * Scala code, add `import scala.jdk.StreamConverters._` and use the + * extension methods instead. + */ +object StreamConverters { + ///////////////////////////////////// + // sequential streams for collections + ///////////////////////////////////// + + /** Create a sequential [[java.util.stream.Stream Java Stream]] for a Scala collection. */ + def asJavaSeqStream[A](cc: IterableOnce[A]): Stream[A] = StreamSupport.stream(cc.stepper.spliterator, false) + + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $primitiveNote + */ + def asJavaSeqIntStream (cc: IterableOnce[jl.Integer]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $primitiveNote + */ + def asJavaSeqIntStreamFromByte (cc: IterableOnce[jl.Byte]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $primitiveNote + */ + def asJavaSeqIntStreamFromShort(cc: IterableOnce[jl.Short]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $primitiveNote + */ + def asJavaSeqIntStreamFromChar (cc: IterableOnce[jl.Character]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, false) + + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for a Scala collection. + * + * $primitiveNote + */ + def asJavaSeqDoubleStream (cc: IterableOnce[jl.Double]): DoubleStream = StreamSupport.doubleStream(cc.stepper.spliterator, false) + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for a Scala collection. + * + * $primitiveNote + */ + def asJavaSeqDoubleStreamFromFloat(cc: IterableOnce[jl.Float]): DoubleStream = StreamSupport.doubleStream(cc.stepper.spliterator, false) + + /** Create a sequential [[java.util.stream.LongStream Java LongStream]] for a Scala collection. + * + * $primitiveNote + */ + def asJavaSeqLongStream(cc: IterableOnce[jl.Long]): LongStream = StreamSupport.longStream(cc.stepper.spliterator, false) + + // Map Key Streams + + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the keys of a Scala Map. */ + def asJavaSeqKeyStream[K, V](m: collection.Map[K, V]): Stream[K] = StreamSupport.stream(m.keyStepper.spliterator, false) + + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $primitiveNote + */ + def asJavaSeqKeyIntStream [V](m: collection.Map[jl.Integer, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $primitiveNote + */ + def asJavaSeqKeyIntStreamFromByte [V](m: collection.Map[jl.Byte, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $primitiveNote + */ + def asJavaSeqKeyIntStreamFromShort[V](m: collection.Map[jl.Short, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $primitiveNote + */ + def asJavaSeqKeyIntStreamFromChar [V](m: collection.Map[jl.Character, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, false) + + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for the keys of a Scala Map. + * + * $primitiveNote + */ + def asJavaSeqKeyDoubleStream [V](m: collection.Map[jl.Double, V]): DoubleStream = StreamSupport.doubleStream(m.keyStepper.spliterator, false) + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for the keys of a Scala Map. + * + * $primitiveNote + */ + def asJavaSeqKeyDoubleStreamFromFloat[V](m: collection.Map[jl.Float, V]): DoubleStream = StreamSupport.doubleStream(m.keyStepper.spliterator, false) + + /** Create a sequential [[java.util.stream.LongStream Java LongStream]] for the keys of a Scala Map. + * + * $primitiveNote + */ + def asJavaSeqKeyLongStream[V](m: collection.Map[jl.Long, V]): LongStream = StreamSupport.longStream(m.keyStepper.spliterator, false) + + // Map Value Streams + + /** Create a sequential [[java.util.stream.Stream Java Stream]] for the values of a Scala Map. */ + def asJavaSeqValueStream[K, V](m: collection.Map[K, V]): Stream[V] = StreamSupport.stream(m.valueStepper.spliterator, false) + + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the values of a + * + * $primitiveNote + */ + def asJavaSeqValueIntStream [K](m: collection.Map[K, jl.Integer]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the values of a + * + * $primitiveNote + */ + def asJavaSeqValueIntStreamFromByte [K](m: collection.Map[K, jl.Byte]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the values of a + * + * $primitiveNote + */ + def asJavaSeqValueIntStreamFromShort[K](m: collection.Map[K, jl.Short]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, false) + /** Create a sequential [[java.util.stream.IntStream Java IntStream]] for the values of a + * + * $primitiveNote + */ + def asJavaSeqValueIntStreamFromChar [K](m: collection.Map[K, jl.Character]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, false) + + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for the values of a + * + * $primitiveNote + */ + def asJavaSeqValueDoubleStream [K](m: collection.Map[K, jl.Double]): DoubleStream = StreamSupport.doubleStream(m.valueStepper.spliterator, false) + /** Create a sequential [[java.util.stream.DoubleStream Java DoubleStream]] for the values of a + * + * $primitiveNote + */ + def asJavaSeqValueDoubleStreamFromFloat[K](m: collection.Map[K, jl.Float]): DoubleStream = StreamSupport.doubleStream(m.valueStepper.spliterator, false) + + /** Create a sequential [[java.util.stream.LongStream Java LongStream]] for the values of a + * + * $primitiveNote + */ + def asJavaSeqValueLongStream[K](m: collection.Map[K, jl.Long]): LongStream = StreamSupport.longStream(m.valueStepper.spliterator, false) + + /////////////////////////////////// + // parallel streams for collections + /////////////////////////////////// + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for a Scala collection. + * + * $parNote + */ + def asJavaParStream[A](cc: IterableOnce[A]): Stream[A] = StreamSupport.stream(cc.stepper.spliterator, true) + + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParIntStream (cc: IterableOnce[jl.Integer]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParIntStreamFromByte (cc: IterableOnce[jl.Byte]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParIntStreamFromShort(cc: IterableOnce[jl.Short]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for a Scala collection. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParIntStreamFromChar (cc: IterableOnce[jl.Character]): IntStream = StreamSupport.intStream(cc.stepper.spliterator, true) + + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for a Scala collection. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParDoubleStream (cc: IterableOnce[jl.Double]): DoubleStream = StreamSupport.doubleStream(cc.stepper.spliterator, true) + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for a Scala collection. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParDoubleStreamFromFloat(cc: IterableOnce[jl.Float]): DoubleStream = StreamSupport.doubleStream(cc.stepper.spliterator, true) + + /** Create a parallel [[java.util.stream.LongStream Java LongStream]] for a Scala collection. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParLongStream(cc: IterableOnce[jl.Long]): LongStream = StreamSupport.longStream(cc.stepper.spliterator, true) + + + // Map Key Streams + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the keys of a Scala Map. + * + * $parNote + */ + def asJavaParKeyStream[K, V](m: collection.Map[K, V]): Stream[K] = StreamSupport.stream(m.keyStepper.spliterator, true) + + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParKeyIntStream [V](m: collection.Map[jl.Integer, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParKeyIntStreamFromByte [V](m: collection.Map[jl.Byte, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParKeyIntStreamFromShort[V](m: collection.Map[jl.Short, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the keys of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParKeyIntStreamFromChar [V](m: collection.Map[jl.Character, V]): IntStream = StreamSupport.intStream(m.keyStepper.spliterator, true) + + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for the keys of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParKeyDoubleStream [V](m: collection.Map[jl.Double, V]): DoubleStream = StreamSupport.doubleStream(m.keyStepper.spliterator, true) + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for the keys of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParKeyDoubleStreamFromFloat[V](m: collection.Map[jl.Float, V]): DoubleStream = StreamSupport.doubleStream(m.keyStepper.spliterator, true) + + /** Create a parallel [[java.util.stream.LongStream Java LongStream]] for the keys of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParKeyLongStream[V](m: collection.Map[jl.Long, V]): LongStream = StreamSupport.longStream(m.keyStepper.spliterator, true) + + // Map Value Streams + + /** Create a parallel [[java.util.stream.Stream Java Stream]] for the values of a Scala Map. + * + * $parNote + */ + def asJavaParValueStream[K, V](m: collection.Map[K, V]): Stream[V] = StreamSupport.stream(m.valueStepper.spliterator, true) + + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the values of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParValueIntStream [K](m: collection.Map[K, jl.Integer]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the values of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParValueIntStreamFromByte [K](m: collection.Map[K, jl.Byte]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the values of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParValueIntStreamFromShort[K](m: collection.Map[K, jl.Short]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, true) + /** Create a parallel [[java.util.stream.IntStream Java IntStream]] for the values of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParValueIntStreamFromChar [K](m: collection.Map[K, jl.Character]): IntStream = StreamSupport.intStream(m.valueStepper.spliterator, true) + + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for the values of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParValueDoubleStream [K](m: collection.Map[K, jl.Double]): DoubleStream = StreamSupport.doubleStream(m.valueStepper.spliterator, true) + /** Create a parallel [[java.util.stream.DoubleStream Java DoubleStream]] for the values of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParValueDoubleStreamFromFloat[K](m: collection.Map[K, jl.Float]): DoubleStream = StreamSupport.doubleStream(m.valueStepper.spliterator, true) + + /** Create a parallel [[java.util.stream.LongStream Java LongStream]] for the values of a Scala Map. + * + * $parNote + * + * $primitiveNote + */ + def asJavaParValueLongStream[K](m: collection.Map[K, jl.Long]): LongStream = StreamSupport.longStream(m.valueStepper.spliterator, true) +} diff --git a/library/src/scala/jdk/package.scala b/library/src/scala/jdk/package.scala new file mode 100644 index 000000000000..386a6886cefd --- /dev/null +++ b/library/src/scala/jdk/package.scala @@ -0,0 +1,46 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** The jdk package contains utilities to interact with JDK classes. + * + * This packages offers a number of converters, that are able to wrap or copy + * types from the scala library to equivalent types in the JDK class library + * and vice versa: + * + * - [[CollectionConverters]], converting collections like [[scala.collection.Seq]], + * [[scala.collection.Map]], [[scala.collection.Set]], + * [[scala.collection.mutable.Buffer]], [[scala.collection.Iterator]] + * and [[scala.collection.Iterable]] to their JDK counterparts + * - [[OptionConverters]], converting between [[Option]] and + * [[java.util.Optional]] and primitive variations + * - [[StreamConverters]], to create JDK Streams from scala collections + * - [[DurationConverters]], for conversions between scala + * [[scala.concurrent.duration.FiniteDuration]] and [[java.time.Duration]] + * - [[FunctionConverters]], from scala Functions to java + * [[java.util.function.Function]], [[java.util.function.UnaryOperator]], + * [[java.util.function.Consumer]] and [[java.util.function.Predicate]], as + * well as primitive variations and Bi-variations. + * + * By convention, converters that wrap an object to provide a different + * interface to the same underlying data structure use .asScala and .asJava + * extension methods, whereas converters that copy the underlying data structure + * use .toScala and .toJava. + * + * In the [[javaapi]] package, the same converters can be found with a + * java-friendly interface that don't rely on implicit enrichments. + * + * Additionally, this package offers [[Accumulator]]s, capable of efficiently + * traversing JDK Streams. + **/ +package object jdk From 30678b7dd3311556a81599bb6fd5139f3cda6c01 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:36:12 +0100 Subject: [PATCH 12/28] chore: add all the files in scala.math --- library/src/scala/math/BigDecimal.scala | 721 ++++++++++++++ library/src/scala/math/BigInt.scala | 638 ++++++++++++ library/src/scala/math/Equiv.scala | 507 ++++++++++ library/src/scala/math/Fractional.scala | 35 + library/src/scala/math/Integral.scala | 41 + library/src/scala/math/Numeric.scala | 261 +++++ library/src/scala/math/Ordered.scala | 101 ++ library/src/scala/math/Ordering.scala | 928 ++++++++++++++++++ library/src/scala/math/PartialOrdering.scala | 88 ++ library/src/scala/math/PartiallyOrdered.scala | 54 + library/src/scala/math/ScalaNumber.java | 20 + .../scala/math/ScalaNumericConversions.scala | 122 +++ library/src/scala/math/package.scala | 438 +++++++++ 13 files changed, 3954 insertions(+) create mode 100644 library/src/scala/math/BigDecimal.scala create mode 100644 library/src/scala/math/BigInt.scala create mode 100644 library/src/scala/math/Equiv.scala create mode 100644 library/src/scala/math/Fractional.scala create mode 100644 library/src/scala/math/Integral.scala create mode 100644 library/src/scala/math/Numeric.scala create mode 100644 library/src/scala/math/Ordered.scala create mode 100644 library/src/scala/math/Ordering.scala create mode 100644 library/src/scala/math/PartialOrdering.scala create mode 100644 library/src/scala/math/PartiallyOrdered.scala create mode 100644 library/src/scala/math/ScalaNumber.java create mode 100644 library/src/scala/math/ScalaNumericConversions.scala create mode 100644 library/src/scala/math/package.scala diff --git a/library/src/scala/math/BigDecimal.scala b/library/src/scala/math/BigDecimal.scala new file mode 100644 index 000000000000..e70cdbab41e4 --- /dev/null +++ b/library/src/scala/math/BigDecimal.scala @@ -0,0 +1,721 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import scala.language.implicitConversions + +import java.math.{ + BigDecimal => BigDec, + MathContext, + RoundingMode => JRM, +} +import scala.collection.immutable.NumericRange + +object BigDecimal { + private final val maximumHashScale = 4934 // Quit maintaining hash identity with BigInt beyond this scale + private final val hashCodeNotComputed = 0x5D50690F // Magic value (happens to be "BigDecimal" old MurmurHash3 value) + private final val deci2binary = 3.3219280948873626 // Ratio of log(10) to log(2) + private[this] val minCached = -512 + private[this] val maxCached = 512 + val defaultMathContext = MathContext.DECIMAL128 + + /** Cache only for defaultMathContext using BigDecimals in a small range. */ + private[this] lazy val cache = new Array[BigDecimal](maxCached - minCached + 1) + + object RoundingMode extends Enumeration { + // Annoying boilerplate to ensure consistency with java.math.RoundingMode + type RoundingMode = Value + val UP = Value(JRM.UP.ordinal) + val DOWN = Value(JRM.DOWN.ordinal) + val CEILING = Value(JRM.CEILING.ordinal) + val FLOOR = Value(JRM.FLOOR.ordinal) + val HALF_UP = Value(JRM.HALF_UP.ordinal) + val HALF_DOWN = Value(JRM.HALF_DOWN.ordinal) + val HALF_EVEN = Value(JRM.HALF_EVEN.ordinal) + val UNNECESSARY = Value(JRM.UNNECESSARY.ordinal) + } + + /** Constructs a `BigDecimal` using the decimal text representation of `Double` value `d`, rounding if necessary. */ + def decimal(d: Double, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(java.lang.Double.toString(d), mc), mc) + + /** Constructs a `BigDecimal` using the decimal text representation of `Double` value `d`. */ + def decimal(d: Double): BigDecimal = decimal(d, defaultMathContext) + + /** Constructs a `BigDecimal` using the decimal text representation of `Float` value `f`, rounding if necessary. + * Note that `BigDecimal.decimal(0.1f) != 0.1f` since equality agrees with the `Double` representation, and + * `0.1 != 0.1f`. + */ + def decimal(f: Float, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(java.lang.Float.toString(f), mc), mc) + + /** Constructs a `BigDecimal` using the decimal text representation of `Float` value `f`. + * Note that `BigDecimal.decimal(0.1f) != 0.1f` since equality agrees with the `Double` representation, and + * `0.1 != 0.1f`. + */ + def decimal(f: Float): BigDecimal = decimal(f, defaultMathContext) + + // This exists solely to avoid conversion from Int/Long to Float, screwing everything up. + /** Constructs a `BigDecimal` from a `Long`, rounding if necessary. This is identical to `BigDecimal(l, mc)`. */ + def decimal(l: Long, mc: MathContext): BigDecimal = apply(l, mc) + + // This exists solely to avoid conversion from Int/Long to Float, screwing everything up. + /** Constructs a `BigDecimal` from a `Long`. This is identical to `BigDecimal(l)`. */ + def decimal(l: Long): BigDecimal = apply(l) + + /** Constructs a `BigDecimal` using a `java.math.BigDecimal`, rounding if necessary. */ + def decimal(bd: BigDec, mc: MathContext): BigDecimal = new BigDecimal(bd.round(mc), mc) + + /** Constructs a `BigDecimal` by expanding the binary fraction + * contained by `Double` value `d` into a decimal representation, + * rounding if necessary. When a `Float` is converted to a + * `Double`, the binary fraction is preserved, so this method + * also works for converted `Float`s. + */ + def binary(d: Double, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(d, mc), mc) + + /** Constructs a `BigDecimal` by expanding the binary fraction + * contained by `Double` value `d` into a decimal representation. + * Note: this also works correctly on converted `Float`s. + */ + def binary(d: Double): BigDecimal = binary(d, defaultMathContext) + + /** Constructs a `BigDecimal` from a `java.math.BigDecimal`. The + * precision is the default for `BigDecimal` or enough to represent + * the `java.math.BigDecimal` exactly, whichever is greater. + */ + def exact(repr: BigDec): BigDecimal = { + val mc = + if (repr.precision <= defaultMathContext.getPrecision) defaultMathContext + else new MathContext(repr.precision, java.math.RoundingMode.HALF_EVEN) + new BigDecimal(repr, mc) + } + + /** Constructs a `BigDecimal` by fully expanding the binary fraction + * contained by `Double` value `d`, adjusting the precision as + * necessary. Note: this works correctly on converted `Float`s also. + */ + def exact(d: Double): BigDecimal = exact(new BigDec(d)) + + /** Constructs a `BigDecimal` that exactly represents a `BigInt`. + */ + def exact(bi: BigInt): BigDecimal = exact(new BigDec(bi.bigInteger)) + + /** Constructs a `BigDecimal` that exactly represents a `Long`. Note that + * all creation methods for `BigDecimal` that do not take a `MathContext` + * represent a `Long`; this is equivalent to `apply`, `valueOf`, etc.. + */ + def exact(l: Long): BigDecimal = apply(l) + + /** Constructs a `BigDecimal` that exactly represents the number + * specified in a `String`. + */ + def exact(s: String): BigDecimal = exact(new BigDec(s)) + + /** Constructs a `BigDecimal` that exactly represents the number + * specified in base 10 in a character array. + */ + def exact(cs: Array[Char]): BigDecimal = exact(new BigDec(cs)) + + + /** Constructs a `BigDecimal` using the java BigDecimal static + * valueOf constructor. Equivalent to `BigDecimal.decimal`. + * + * @param d the specified double value + * @return the constructed `BigDecimal` + */ + def valueOf(d: Double): BigDecimal = apply(BigDec valueOf d) + + /** Constructs a `BigDecimal` using the java BigDecimal static + * valueOf constructor. + * + * @param x the specified `Long` value + * @return the constructed `BigDecimal` + */ + def valueOf(x: Long): BigDecimal = apply(x) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified `Integer` value. + * + * @param i the specified integer value + * @return the constructed `BigDecimal` + */ + def apply(i: Int): BigDecimal = apply(i, defaultMathContext) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified `Integer` value, rounding if necessary. + * + * @param i the specified integer value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ + def apply(i: Int, mc: MathContext): BigDecimal = + if (mc == defaultMathContext && minCached <= i && i <= maxCached) { + val offset = i - minCached + var n = cache(offset) + if (n eq null) { n = new BigDecimal(BigDec.valueOf(i.toLong), mc); cache(offset) = n } + n + } + else apply(i.toLong, mc) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified long value. + * + * @param l the specified long value + * @return the constructed `BigDecimal` + */ + def apply(l: Long): BigDecimal = + if (minCached <= l && l <= maxCached) apply(l.toInt) + else new BigDecimal(BigDec.valueOf(l), defaultMathContext) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified long value, but rounded if necessary. + * + * @param l the specified long value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ + def apply(l: Long, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(l, mc), mc) + + /** Constructs a `BigDecimal` whose unscaled value is equal to that + * of the specified long value. + * + * @param unscaledVal the value + * @param scale the scale + * @return the constructed `BigDecimal` + */ + def apply(unscaledVal: Long, scale: Int): BigDecimal = + apply(BigInt(unscaledVal), scale) + + /** Constructs a `BigDecimal` whose unscaled value is equal to that + * of the specified long value, but rounded if necessary. + * + * @param unscaledVal the value + * @param scale the scale + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ + def apply(unscaledVal: Long, scale: Int, mc: MathContext): BigDecimal = + apply(BigInt(unscaledVal), scale, mc) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified double value. Equivalent to `BigDecimal.decimal`. + * + * @param d the specified `Double` value + * @return the constructed `BigDecimal` + */ + def apply(d: Double): BigDecimal = decimal(d, defaultMathContext) + + // note we don't use the static valueOf because it doesn't let us supply + // a MathContext, but we should be duplicating its logic, modulo caching. + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified double value, but rounded if necessary. Equivalent to + * `BigDecimal.decimal`. + * + * @param d the specified `Double` value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ + def apply(d: Double, mc: MathContext): BigDecimal = decimal(d, mc) + + /** Translates a character array representation of a `BigDecimal` + * into a `BigDecimal`. + */ + def apply(x: Array[Char]): BigDecimal = exact(x) + + /** Translates a character array representation of a `BigDecimal` + * into a `BigDecimal`, rounding if necessary. + */ + def apply(x: Array[Char], mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(x, mc), mc) + + /** Translates the decimal String representation of a `BigDecimal` + * into a `BigDecimal`. + */ + def apply(x: String): BigDecimal = exact(x) + + /** Translates the decimal String representation of a `BigDecimal` + * into a `BigDecimal`, rounding if necessary. + */ + def apply(x: String, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(x, mc), mc) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified `BigInt` value. + * + * @param x the specified `BigInt` value + * @return the constructed `BigDecimal` + */ + def apply(x: BigInt): BigDecimal = exact(x) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified `BigInt` value, rounding if necessary. + * + * @param x the specified `BigInt` value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ + def apply(x: BigInt, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(x.bigInteger, mc), mc) + + /** Constructs a `BigDecimal` whose unscaled value is equal to that + * of the specified `BigInt` value. + * + * @param unscaledVal the specified `BigInt` value + * @param scale the scale + * @return the constructed `BigDecimal` + */ + def apply(unscaledVal: BigInt, scale: Int): BigDecimal = + exact(new BigDec(unscaledVal.bigInteger, scale)) + + /** Constructs a `BigDecimal` whose unscaled value is equal to that + * of the specified `BigInt` value. + * + * @param unscaledVal the specified `BigInt` value + * @param scale the scale + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ + def apply(unscaledVal: BigInt, scale: Int, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(unscaledVal.bigInteger, scale, mc), mc) + + /** Constructs a `BigDecimal` from a `java.math.BigDecimal`. */ + def apply(bd: BigDec): BigDecimal = new BigDecimal(bd, defaultMathContext) + + /** Implicit conversion from `Int` to `BigDecimal`. */ + implicit def int2bigDecimal(i: Int): BigDecimal = apply(i) + + /** Implicit conversion from `Long` to `BigDecimal`. */ + implicit def long2bigDecimal(l: Long): BigDecimal = apply(l) + + /** Implicit conversion from `Double` to `BigDecimal`. */ + implicit def double2bigDecimal(d: Double): BigDecimal = decimal(d) + + /** Implicit conversion from `java.math.BigDecimal` to `scala.BigDecimal`. */ + implicit def javaBigDecimal2bigDecimal(x: BigDec): BigDecimal = if (x == null) null else apply(x) +} + +/** + * `BigDecimal` represents decimal floating-point numbers of arbitrary precision. + * By default, the precision approximately matches that of IEEE 128-bit floating + * point numbers (34 decimal digits, `HALF_EVEN` rounding mode). Within the range + * of IEEE binary128 numbers, `BigDecimal` will agree with `BigInt` for both + * equality and hash codes (and will agree with primitive types as well). Beyond + * that range--numbers with more than 4934 digits when written out in full--the + * `hashCode` of `BigInt` and `BigDecimal` is allowed to diverge due to difficulty + * in efficiently computing both the decimal representation in `BigDecimal` and the + * binary representation in `BigInt`. + * + * When creating a `BigDecimal` from a `Double` or `Float`, care must be taken as + * the binary fraction representation of `Double` and `Float` does not easily + * convert into a decimal representation. Three explicit schemes are available + * for conversion. `BigDecimal.decimal` will convert the floating-point number + * to a decimal text representation, and build a `BigDecimal` based on that. + * `BigDecimal.binary` will expand the binary fraction to the requested or default + * precision. `BigDecimal.exact` will expand the binary fraction to the + * full number of digits, thus producing the exact decimal value corresponding to + * the binary fraction of that floating-point number. `BigDecimal` equality + * matches the decimal expansion of `Double`: `BigDecimal.decimal(0.1) == 0.1`. + * Note that since `0.1f != 0.1`, the same is not true for `Float`. Instead, + * `0.1f == BigDecimal.decimal((0.1f).toDouble)`. + * + * To test whether a `BigDecimal` number can be converted to a `Double` or + * `Float` and then back without loss of information by using one of these + * methods, test with `isDecimalDouble`, `isBinaryDouble`, or `isExactDouble` + * or the corresponding `Float` versions. Note that `BigInt`'s `isValidDouble` + * will agree with `isExactDouble`, not the `isDecimalDouble` used by default. + * + * `BigDecimal` uses the decimal representation of binary floating-point numbers + * to determine equality and hash codes. This yields different answers than + * conversion between `Long` and `Double` values, where the exact form is used. + * As always, since floating-point is a lossy representation, it is advisable to + * take care when assuming identity will be maintained across multiple conversions. + * + * `BigDecimal` maintains a `MathContext` that determines the rounding that + * is applied to certain calculations. In most cases, the value of the + * `BigDecimal` is also rounded to the precision specified by the `MathContext`. + * To create a `BigDecimal` with a different precision than its `MathContext`, + * use `new BigDecimal(new java.math.BigDecimal(...), mc)`. Rounding will + * be applied on those mathematical operations that can dramatically change the + * number of digits in a full representation, namely multiplication, division, + * and powers. The left-hand argument's `MathContext` always determines the + * degree of rounding, if any, and is the one propagated through arithmetic + * operations that do not apply rounding themselves. + */ +final class BigDecimal(val bigDecimal: BigDec, val mc: MathContext) +extends ScalaNumber with ScalaNumericConversions with Serializable with Ordered[BigDecimal] { + def this(bigDecimal: BigDec) = this(bigDecimal, BigDecimal.defaultMathContext) + import BigDecimal.RoundingMode._ + import BigDecimal.{decimal, binary, exact} + + if (bigDecimal eq null) throw new IllegalArgumentException("null value for BigDecimal") + if (mc eq null) throw new IllegalArgumentException("null MathContext for BigDecimal") + + // There was an implicit to cut down on the wrapper noise for BigDec -> BigDecimal. + // However, this may mask introduction of surprising behavior (e.g. lack of rounding + // where one might expect it). Wrappers should be applied explicitly with an + // eye to correctness. + + // Sane hash code computation (which is surprisingly hard). + // Note--not lazy val because we can't afford the extra space. + private final var computedHashCode: Int = BigDecimal.hashCodeNotComputed + private final def computeHashCode(): Unit = { + computedHashCode = + if (isWhole && (precision - scale) < BigDecimal.maximumHashScale) toBigInt.hashCode + else if (isDecimalDouble) doubleValue.## + else { + val temp = bigDecimal.stripTrailingZeros + scala.util.hashing.MurmurHash3.mixLast( temp.scaleByPowerOfTen(temp.scale).toBigInteger.hashCode, temp.scale ) + } + } + + /** Returns the hash code for this BigDecimal. + * Note that this does not merely use the underlying java object's + * `hashCode` because we compare `BigDecimal`s with `compareTo` + * which deems 2 == 2.00, whereas in java these are unequal + * with unequal `hashCode`s. These hash codes agree with `BigInt` + * for whole numbers up ~4934 digits (the range of IEEE 128 bit floating + * point). Beyond this, hash codes will disagree; this prevents the + * explicit representation of the `BigInt` form for `BigDecimal` values + * with large exponents. + */ + override def hashCode(): Int = { + if (computedHashCode == BigDecimal.hashCodeNotComputed) computeHashCode() + computedHashCode + } + + /** Compares this BigDecimal with the specified value for equality. Where `Float` and `Double` + * disagree, `BigDecimal` will agree with the `Double` value + */ + override def equals (that: Any): Boolean = that match { + case that: BigDecimal => this equals that + case that: BigInt => + that.bitLength > (precision-scale-2)*BigDecimal.deci2binary && + this.toBigIntExact.exists(that equals _) + case that: Double => + !that.isInfinity && { + val d = toDouble + !d.isInfinity && d == that && equals(decimal(d)) + } + case that: Float => + !that.isInfinity && { + val f = toFloat + !f.isInfinity && f == that && equals(decimal(f.toDouble)) + } + case _ => isValidLong && unifiedPrimitiveEquals(that) + } + override def isValidByte = noArithmeticException(toByteExact) + override def isValidShort = noArithmeticException(toShortExact) + override def isValidChar = isValidInt && toIntExact >= Char.MinValue && toIntExact <= Char.MaxValue + override def isValidInt = noArithmeticException(toIntExact) + def isValidLong = noArithmeticException(toLongExact) + + /** Tests whether this `BigDecimal` holds the decimal representation of a `Double`. */ + def isDecimalDouble = { + val d = toDouble + !d.isInfinity && equals(decimal(d)) + } + + /** Tests whether this `BigDecimal` holds the decimal representation of a `Float`. */ + def isDecimalFloat = { + val f = toFloat + !f.isInfinity && equals(decimal(f)) + } + + /** Tests whether this `BigDecimal` holds, to within precision, the binary representation of a `Double`. */ + def isBinaryDouble = { + val d = toDouble + !d.isInfinity && equals(binary(d,mc)) + } + + /** Tests whether this `BigDecimal` holds, to within precision, the binary representation of a `Float`. */ + def isBinaryFloat = { + val f = toFloat + !f.isInfinity && equals(binary(f,mc)) + } + + /** Tests whether this `BigDecimal` holds the exact expansion of a `Double`'s binary fractional form into base 10. */ + def isExactDouble = { + val d = toDouble + !d.isInfinity && equals(exact(d)) + } + + /** Tests whether this `BigDecimal` holds the exact expansion of a `Float`'s binary fractional form into base 10. */ + def isExactFloat = { + val f = toFloat + !f.isInfinity && equals(exact(f.toDouble)) + } + + + private def noArithmeticException(body: => Unit): Boolean = { + try { body ; true } + catch { case _: ArithmeticException => false } + } + + def isWhole = scale <= 0 || bigDecimal.stripTrailingZeros.scale <= 0 + + def underlying: java.math.BigDecimal = bigDecimal + + + /** Compares this BigDecimal with the specified BigDecimal for equality. + */ + def equals (that: BigDecimal): Boolean = compare(that) == 0 + + /** Compares this BigDecimal with the specified BigDecimal + */ + def compare (that: BigDecimal): Int = this.bigDecimal compareTo that.bigDecimal + + /** Addition of BigDecimals + */ + def + (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal.add(that.bigDecimal, mc), mc) + + /** Subtraction of BigDecimals + */ + def - (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal.subtract(that.bigDecimal, mc), mc) + + /** Multiplication of BigDecimals + */ + def * (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal.multiply(that.bigDecimal, mc), mc) + + /** Division of BigDecimals + */ + def / (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal.divide(that.bigDecimal, mc), mc) + + /** Division and Remainder - returns tuple containing the result of + * divideToIntegralValue and the remainder. The computation is exact: no rounding is applied. + */ + def /% (that: BigDecimal): (BigDecimal, BigDecimal) = { + val qr = this.bigDecimal.divideAndRemainder(that.bigDecimal, mc) + (new BigDecimal(qr(0), mc), new BigDecimal(qr(1), mc)) + } + + /** Divide to Integral value. + */ + def quot (that: BigDecimal): BigDecimal = + new BigDecimal(this.bigDecimal.divideToIntegralValue(that.bigDecimal, mc), mc) + + /** Returns the minimum of this and that, or this if the two are equal + */ + def min (that: BigDecimal): BigDecimal = (this compare that) match { + case x if x <= 0 => this + case _ => that + } + + /** Returns the maximum of this and that, or this if the two are equal + */ + def max (that: BigDecimal): BigDecimal = (this compare that) match { + case x if x >= 0 => this + case _ => that + } + + /** Remainder after dividing this by that. + */ + def remainder (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal.remainder(that.bigDecimal, mc), mc) + + /** Remainder after dividing this by that. + */ + def % (that: BigDecimal): BigDecimal = this.remainder(that) + + /** Returns a BigDecimal whose value is this ** n. + */ + def pow (n: Int): BigDecimal = new BigDecimal(this.bigDecimal.pow(n, mc), mc) + + /** Returns a BigDecimal whose value is the negation of this BigDecimal + */ + def unary_- : BigDecimal = new BigDecimal(this.bigDecimal.negate(mc), mc) + + /** Returns the absolute value of this BigDecimal + */ + def abs: BigDecimal = if (signum < 0) unary_- else this + + /** Returns the sign of this BigDecimal; + * -1 if it is less than 0, + * +1 if it is greater than 0, + * 0 if it is equal to 0. + */ + def signum: Int = this.bigDecimal.signum() + + /** Returns the sign of this BigDecimal; + * -1 if it is less than 0, + * +1 if it is greater than 0, + * 0 if it is equal to 0. + */ + def sign: BigDecimal = signum + + /** Returns the precision of this `BigDecimal`. + */ + def precision: Int = this.bigDecimal.precision + + /** Returns a BigDecimal rounded according to the supplied MathContext settings, but + * preserving its own MathContext for future operations. + */ + def round(mc: MathContext): BigDecimal = { + val r = this.bigDecimal round mc + if (r eq bigDecimal) this else new BigDecimal(r, this.mc) + } + + /** Returns a `BigDecimal` rounded according to its own `MathContext` */ + def rounded: BigDecimal = { + val r = bigDecimal round mc + if (r eq bigDecimal) this else new BigDecimal(r, mc) + } + + /** Returns the scale of this `BigDecimal`. + */ + def scale: Int = this.bigDecimal.scale + + /** Returns the size of an ulp, a unit in the last place, of this BigDecimal. + */ + def ulp: BigDecimal = new BigDecimal(this.bigDecimal.ulp, mc) + + /** Returns a new BigDecimal based on the supplied MathContext, rounded as needed. + */ + def apply(mc: MathContext): BigDecimal = new BigDecimal(this.bigDecimal round mc, mc) + + /** Returns a `BigDecimal` whose scale is the specified value, and whose value is + * numerically equal to this BigDecimal's. + */ + def setScale(scale: Int): BigDecimal = + if (this.scale == scale) this + else new BigDecimal(this.bigDecimal.setScale(scale), mc) + + def setScale(scale: Int, mode: RoundingMode): BigDecimal = + if (this.scale == scale) this + else new BigDecimal(this.bigDecimal.setScale(scale, JRM.valueOf(mode.id)), mc) + + /** Converts this BigDecimal to a Byte. + * If the BigDecimal is too big to fit in a Byte, only the low-order 8 bits are returned. + * Note that this conversion can lose information about the overall magnitude of the + * BigDecimal value as well as return a result with the opposite sign. + */ + override def byteValue = intValue.toByte + + /** Converts this BigDecimal to a Short. + * If the BigDecimal is too big to fit in a Short, only the low-order 16 bits are returned. + * Note that this conversion can lose information about the overall magnitude of the + * BigDecimal value as well as return a result with the opposite sign. + */ + override def shortValue = intValue.toShort + + /** Converts this BigDecimal to a Char. + * If the BigDecimal is too big to fit in a Char, only the low-order 16 bits are returned. + * Note that this conversion can lose information about the overall magnitude of the + * BigDecimal value and that it always returns a positive result. + */ + def charValue = intValue.toChar + + /** Converts this BigDecimal to an Int. + * If the BigDecimal is too big to fit in an Int, only the low-order 32 bits + * are returned. Note that this conversion can lose information about the + * overall magnitude of the BigDecimal value as well as return a result with + * the opposite sign. + */ + def intValue = this.bigDecimal.intValue + + /** Converts this BigDecimal to a Long. + * If the BigDecimal is too big to fit in a Long, only the low-order 64 bits + * are returned. Note that this conversion can lose information about the + * overall magnitude of the BigDecimal value as well as return a result with + * the opposite sign. + */ + def longValue = this.bigDecimal.longValue + + /** Converts this BigDecimal to a Float. + * if this BigDecimal has too great a magnitude to represent as a float, + * it will be converted to `Float.NEGATIVE_INFINITY` or + * `Float.POSITIVE_INFINITY` as appropriate. + */ + def floatValue = this.bigDecimal.floatValue + + /** Converts this BigDecimal to a Double. + * if this BigDecimal has too great a magnitude to represent as a double, + * it will be converted to `Double.NEGATIVE_INFINITY` or + * `Double.POSITIVE_INFINITY` as appropriate. + */ + def doubleValue = this.bigDecimal.doubleValue + + /** Converts this `BigDecimal` to a [[scala.Byte]], checking for lost information. + * If this `BigDecimal` has a nonzero fractional part, or is out of the possible + * range for a [[scala.Byte]] result, then a `java.lang.ArithmeticException` is + * thrown. + */ + def toByteExact = bigDecimal.byteValueExact + + /** Converts this `BigDecimal` to a [[scala.Short]], checking for lost information. + * If this `BigDecimal` has a nonzero fractional part, or is out of the possible + * range for a [[scala.Short]] result, then a `java.lang.ArithmeticException` is + * thrown. + */ + def toShortExact = bigDecimal.shortValueExact + + /** Converts this `BigDecimal` to a [[scala.Int]], checking for lost information. + * If this `BigDecimal` has a nonzero fractional part, or is out of the possible + * range for an [[scala.Int]] result, then a `java.lang.ArithmeticException` is + * thrown. + */ + def toIntExact = bigDecimal.intValueExact + + /** Converts this `BigDecimal` to a [[scala.Long]], checking for lost information. + * If this `BigDecimal` has a nonzero fractional part, or is out of the possible + * range for a [[scala.Long]] result, then a `java.lang.ArithmeticException` is + * thrown. + */ + def toLongExact = bigDecimal.longValueExact + + /** Creates a partially constructed NumericRange[BigDecimal] in range + * `[start;end)`, where start is the target BigDecimal. The step + * must be supplied via the "by" method of the returned object in order + * to receive the fully constructed range. For example: + * {{{ + * val partial = BigDecimal(1.0) to 2.0 // not usable yet + * val range = partial by 0.01 // now a NumericRange + * val range2 = BigDecimal(0) to 1.0 by 0.01 // all at once of course is fine too + * }}} + * + * @param end the end value of the range (exclusive) + * @return the partially constructed NumericRange + */ + def until(end: BigDecimal): Range.Partial[BigDecimal, NumericRange.Exclusive[BigDecimal]] = + new Range.Partial(until(end, _)) + + /** Same as the one-argument `until`, but creates the range immediately. */ + def until(end: BigDecimal, step: BigDecimal): NumericRange.Exclusive[BigDecimal] = Range.BigDecimal(this, end, step) + + /** Like `until`, but inclusive of the end value. */ + def to(end: BigDecimal): Range.Partial[BigDecimal, NumericRange.Inclusive[BigDecimal]] = + new Range.Partial(to(end, _)) + + /** Like `until`, but inclusive of the end value. */ + def to(end: BigDecimal, step: BigDecimal) = Range.BigDecimal.inclusive(this, end, step) + + /** Converts this `BigDecimal` to a scala.BigInt. + */ + def toBigInt: BigInt = new BigInt(this.bigDecimal.toBigInteger) + + /** Converts this `BigDecimal` to a scala.BigInt if it + * can be done losslessly, returning Some(BigInt) or None. + */ + def toBigIntExact: Option[BigInt] = + if (isWhole) { + try Some(new BigInt(this.bigDecimal.toBigIntegerExact)) + catch { case _: ArithmeticException => None } + } + else None + + /** Returns the decimal String representation of this BigDecimal. + */ + override def toString: String = this.bigDecimal.toString + +} diff --git a/library/src/scala/math/BigInt.scala b/library/src/scala/math/BigInt.scala new file mode 100644 index 000000000000..9a17ee02a51a --- /dev/null +++ b/library/src/scala/math/BigInt.scala @@ -0,0 +1,638 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import java.math.BigInteger + +import scala.annotation.nowarn +import scala.language.implicitConversions +import scala.collection.immutable.NumericRange + +object BigInt { + + private val longMinValueBigInteger = BigInteger.valueOf(Long.MinValue) + private val longMinValue = new BigInt(longMinValueBigInteger, Long.MinValue) + + private[this] val minCached = -1024 + private[this] val maxCached = 1024 + private[this] val cache = new Array[BigInt](maxCached - minCached + 1) + + private[this] def getCached(i: Int): BigInt = { + val offset = i - minCached + var n = cache(offset) + if (n eq null) { + n = new BigInt(null, i.toLong) + cache(offset) = n + } + n + } + + private val minusOne = BigInteger.valueOf(-1) + + /** Constructs a `BigInt` whose value is equal to that of the + * specified integer value. + * + * @param i the specified integer value + * @return the constructed `BigInt` + */ + def apply(i: Int): BigInt = + if (minCached <= i && i <= maxCached) getCached(i) else apply(i: Long) + + /** Constructs a `BigInt` whose value is equal to that of the + * specified long value. + * + * @param l the specified long value + * @return the constructed `BigInt` + */ + def apply(l: Long): BigInt = + if (minCached <= l && l <= maxCached) getCached(l.toInt) + else if (l == Long.MinValue) longMinValue + else new BigInt(null, l) + + /** Translates a byte array containing the two's-complement binary + * representation of a BigInt into a BigInt. + */ + def apply(x: Array[Byte]): BigInt = + apply(new BigInteger(x)) + + /** Translates the sign-magnitude representation of a BigInt into a BigInt. + * + * @param signum signum of the number (-1 for negative, 0 for zero, 1 + * for positive). + * @param magnitude big-endian binary representation of the magnitude of + * the number. + */ + def apply(signum: Int, magnitude: Array[Byte]): BigInt = + apply(new BigInteger(signum, magnitude)) + + /** Constructs a randomly generated positive BigInt that is probably prime, + * with the specified bitLength. + */ + def apply(bitlength: Int, certainty: Int, rnd: scala.util.Random): BigInt = + apply(new BigInteger(bitlength, certainty, rnd.self)) + + /** Constructs a randomly generated BigInt, uniformly distributed over the + * range `0` to `(2 ^ numBits - 1)`, inclusive. + */ + def apply(numbits: Int, rnd: scala.util.Random): BigInt = + apply(new BigInteger(numbits, rnd.self)) + + /** Translates the decimal String representation of a BigInt into a BigInt. + */ + def apply(x: String): BigInt = + apply(new BigInteger(x)) + + /** Translates the string representation of a `BigInt` in the + * specified `radix` into a BigInt. + */ + def apply(x: String, radix: Int): BigInt = + apply(new BigInteger(x, radix)) + + /** Translates a `java.math.BigInteger` into a BigInt. + */ + def apply(x: BigInteger): BigInt = { + if (x.bitLength <= 63) { + val l = x.longValue + if (minCached <= l && l <= maxCached) getCached(l.toInt) else new BigInt(x, l) + } else new BigInt(x, Long.MinValue) + } + + /** Returns a positive BigInt that is probably prime, with the specified bitLength. + */ + def probablePrime(bitLength: Int, rnd: scala.util.Random): BigInt = + apply(BigInteger.probablePrime(bitLength, rnd.self)) + + /** Implicit conversion from `Int` to `BigInt`. + */ + implicit def int2bigInt(i: Int): BigInt = apply(i) + + /** Implicit conversion from `Long` to `BigInt`. + */ + implicit def long2bigInt(l: Long): BigInt = apply(l) + + /** Implicit conversion from `java.math.BigInteger` to `scala.BigInt`. + */ + implicit def javaBigInteger2bigInt(x: BigInteger): BigInt = if (x eq null) null else apply(x) + + // this method is adapted from Google Guava's version at + // https://github.com/google/guava/blob/master/guava/src/com/google/common/math/LongMath.java + // that code carries the following notice: + // * Copyright (C) 2011 The Guava Authors + // * + // * Licensed under the Apache License, Version 2.0 (the "License") + /** + * Returns the greatest common divisor of a and b. Returns 0 if a == 0 && b == 0. + */ + private def longGcd(a: Long, b: Long): Long = { + // both a and b must be >= 0 + if (a == 0) { // 0 % b == 0, so b divides a, but the converse doesn't hold. + // BigInteger.gcd is consistent with this decision. + return b + } + else if (b == 0) return a // similar logic + /* + * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm. This is + * >60% faster than the Euclidean algorithm in benchmarks. + */ + val aTwos = java.lang.Long.numberOfTrailingZeros(a) + var a1 = a >> aTwos // divide out all 2s + + val bTwos = java.lang.Long.numberOfTrailingZeros(b) + var b1 = b >> bTwos + while (a1 != b1) { // both a, b are odd + // The key to the binary GCD algorithm is as follows: + // Both a1 and b1 are odd. Assume a1 > b1; then gcd(a1 - b1, b1) = gcd(a1, b1). + // But in gcd(a1 - b1, b1), a1 - b1 is even and b1 is odd, so we can divide out powers of two. + // We bend over backwards to avoid branching, adapting a technique from + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax + val delta = a1 - b1 // can't overflow, since a1 and b1 are nonnegative + val minDeltaOrZero = delta & (delta >> (java.lang.Long.SIZE - 1)) + // equivalent to Math.min(delta, 0) + a1 = delta - minDeltaOrZero - minDeltaOrZero // sets a to Math.abs(a - b) + + // a is now nonnegative and even + b1 += minDeltaOrZero // sets b to min(old a, b) + + a1 >>= java.lang.Long.numberOfTrailingZeros(a1) // divide out all 2s, since 2 doesn't divide b + + } + a1 << scala.math.min(aTwos, bTwos) + } + +} + +/** A type with efficient encoding of arbitrary integers. + * + * It wraps `java.math.BigInteger`, with optimization for small values that can be encoded in a `Long`. + */ +final class BigInt private (private var _bigInteger: BigInteger, private val _long: Long) + extends ScalaNumber + with ScalaNumericConversions + with Serializable + with Ordered[BigInt] +{ + // The class has a special encoding for integer that fit in a Long *and* are not equal to Long.MinValue. + // + // The Long value Long.MinValue is a tag specifying that the integer is encoded in the BigInteger field. + // + // There are three possible states for the class fields (_bigInteger, _long) + // 1. (null, l) where l != Long.MinValue, encodes the integer "l" + // 2. (b, l) where l != Long.MinValue; then b is a BigInteger with value l, encodes "l" == "b" + // 3a. (b, Long.MinValue) where b == Long.MinValue, encodes Long.MinValue + // 3b. (b, Long.MinValue) where b does not fit in a Long, encodes "b" + // + // There is only one possible transition 1. -> 2., when the method .bigInteger is called, then the field + // _bigInteger caches the result. + // + // The case 3a. is the only one where the BigInteger could actually fit in a Long, but as its value is used as a + // tag, we'll take the slow path instead. + // + // Additionally, we know that if this.isValidLong is true, then _long is the encoded value. + + /** Public constructor present for compatibility. Use the BigInt.apply companion object method instead. */ + def this(bigInteger: BigInteger) = this( + bigInteger, // even if it is a short BigInteger, we cache the instance + if (bigInteger.bitLength <= 63) + bigInteger.longValue // if _bigInteger is actually equal to Long.MinValue, no big deal, its value acts as a tag + else Long.MinValue + ) + + /** Returns whether the integer is encoded in the Long. Returns true for all values fitting in a Long except + * Long.MinValue. */ + private def longEncoding: Boolean = _long != Long.MinValue + + def bigInteger: BigInteger = { + val read = _bigInteger + if (read ne null) read else { + val write = BigInteger.valueOf(_long) + _bigInteger = write // reference assignment is atomic; this is multi-thread safe (if possibly wasteful) + write + } + } + + /** Returns the hash code for this BigInt. */ + override def hashCode(): Int = + if (isValidLong) unifiedPrimitiveHashcode + else bigInteger.## + + /** Compares this BigInt with the specified value for equality. */ + @nowarn("cat=other-non-cooperative-equals") + override def equals(that: Any): Boolean = that match { + case that: BigInt => this equals that + case that: BigDecimal => that equals this + case that: Double => isValidDouble && toDouble == that + case that: Float => isValidFloat && toFloat == that + case x => isValidLong && unifiedPrimitiveEquals(x) + } + + override def isValidByte: Boolean = _long >= Byte.MinValue && _long <= Byte.MaxValue /* && longEncoding */ + override def isValidShort: Boolean = _long >= Short.MinValue && _long <= Short.MaxValue /* && longEncoding */ + override def isValidChar: Boolean = _long >= Char.MinValue && _long <= Char.MaxValue /* && longEncoding */ + override def isValidInt: Boolean = _long >= Int.MinValue && _long <= Int.MaxValue /* && longEncoding */ + def isValidLong: Boolean = longEncoding || _bigInteger == BigInt.longMinValueBigInteger // rhs of || tests == Long.MinValue + + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + */ + def isValidFloat: Boolean = { + val bitLen = bitLength + (bitLen <= 24 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Float.MAX_EXPONENT + 1 && // exclude this < -2^128 && this >= 2^128 + lowest >= bitLen - 24 && + lowest < java.lang.Float.MAX_EXPONENT + 1 // exclude this == -2^128 + } + ) && !bitLengthOverflow + } + /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + */ + def isValidDouble: Boolean = { + val bitLen = bitLength + (bitLen <= 53 || + { + val lowest = lowestSetBit + bitLen <= java.lang.Double.MAX_EXPONENT + 1 && // exclude this < -2^1024 && this >= 2^1024 + lowest >= bitLen - 53 && + lowest < java.lang.Double.MAX_EXPONENT + 1 // exclude this == -2^1024 + } + ) && !bitLengthOverflow + } + /** Some implementations of java.math.BigInteger allow huge values with bit length greater than Int.MaxValue. + * The BigInteger.bitLength method returns truncated bit length in this case. + * This method tests if result of bitLength is valid. + * This method will become unnecessary if BigInt constructors reject huge BigIntegers. + */ + private def bitLengthOverflow = { + val shifted = bigInteger.shiftRight(Int.MaxValue) + (shifted.signum != 0) && !(shifted equals BigInt.minusOne) + } + + @deprecated("isWhole on an integer type is always true", "2.12.15") + def isWhole: Boolean = true + def underlying: BigInteger = bigInteger + + /** Compares this BigInt with the specified BigInt for equality. + */ + def equals(that: BigInt): Boolean = + if (this.longEncoding) + that.longEncoding && (this._long == that._long) + else + !that.longEncoding && (this._bigInteger == that._bigInteger) + + /** Compares this BigInt with the specified BigInt + */ + def compare(that: BigInt): Int = + if (this.longEncoding) { + if (that.longEncoding) java.lang.Long.compare(this._long, that._long) else -that._bigInteger.signum() + } else { + if (that.longEncoding) _bigInteger.signum() else this._bigInteger.compareTo(that._bigInteger) + } + + /** Addition of BigInts + */ + def +(that: BigInt): BigInt = { + if (this.longEncoding && that.longEncoding) { // fast path + val x = this._long + val y = that._long + val z = x + y + if ((~(x ^ y) & (x ^ z)) >= 0L) return BigInt(z) + } + BigInt(this.bigInteger.add(that.bigInteger)) + } + + /** Subtraction of BigInts + */ + def -(that: BigInt): BigInt = { + if (this.longEncoding && that.longEncoding) { // fast path + val x = this._long + val y = that._long + val z = x - y + if (((x ^ y) & (x ^ z)) >= 0L) return BigInt(z) + } + BigInt(this.bigInteger.subtract(that.bigInteger)) + } + + /** Multiplication of BigInts + */ + def *(that: BigInt): BigInt = { + if (this.longEncoding && that.longEncoding) { // fast path + val x = this._long + val y = that._long + val z = x * y + // original code checks the y != Long.MinValue, but when longEncoding is true, that is never the case + // if (x == 0 || (y == z / x && !(x == -1 && y == Long.MinValue))) return BigInt(z) + if (x == 0 || y == z / x) return BigInt(z) + } + BigInt(this.bigInteger.multiply(that.bigInteger)) + } + + /** Division of BigInts + */ + def /(that: BigInt): BigInt = + // in the fast path, note that the original code avoided storing -Long.MinValue in a long: + // if (this._long != Long.MinValue || that._long != -1) return BigInt(this._long / that._long) + // but we know this._long cannot be Long.MinValue, because Long.MinValue is the tag for bigger integers + if (this.longEncoding && that.longEncoding) BigInt(this._long / that._long) + else BigInt(this.bigInteger.divide(that.bigInteger)) + + /** Remainder of BigInts + */ + def %(that: BigInt): BigInt = + // see / for the original logic regarding Long.MinValue + if (this.longEncoding && that.longEncoding) BigInt(this._long % that._long) + else BigInt(this.bigInteger.remainder(that.bigInteger)) + + /** Returns a pair of two BigInts containing (this / that) and (this % that). + */ + def /%(that: BigInt): (BigInt, BigInt) = + if (this.longEncoding && that.longEncoding) { + val x = this._long + val y = that._long + // original line: if (x != Long.MinValue || y != -1) return (BigInt(x / y), BigInt(x % y)) + (BigInt(x / y), BigInt(x % y)) + } else { + val dr = this.bigInteger.divideAndRemainder(that.bigInteger) + (BigInt(dr(0)), BigInt(dr(1))) + } + + /** Leftshift of BigInt + */ + def <<(n: Int): BigInt = + if (longEncoding && n <= 0) (this >> (-n)) else BigInt(this.bigInteger.shiftLeft(n)) + + /** (Signed) rightshift of BigInt + */ + def >>(n: Int): BigInt = + if (longEncoding && n >= 0) { + if (n < 64) BigInt(_long >> n) + else if (_long < 0) BigInt(-1) + else BigInt(0) // for _long >= 0 + } else BigInt(this.bigInteger.shiftRight(n)) + + /** Bitwise and of BigInts + */ + def &(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long & that._long) + else BigInt(this.bigInteger.and(that.bigInteger)) + + /** Bitwise or of BigInts + */ + def |(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long | that._long) + else BigInt(this.bigInteger.or(that.bigInteger)) + + /** Bitwise exclusive-or of BigInts + */ + def ^(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long ^ that._long) + else BigInt(this.bigInteger.xor(that.bigInteger)) + + /** Bitwise and-not of BigInts. Returns a BigInt whose value is (this & ~that). + */ + def &~(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long & ~that._long) + else BigInt(this.bigInteger.andNot(that.bigInteger)) + + /** Returns the greatest common divisor of abs(this) and abs(that) + */ + def gcd(that: BigInt): BigInt = + if (this.longEncoding) { + if (this._long == 0) return that.abs + // if (this._long == Long.MinValue) return (-this) gcd that + // this != 0 && this != Long.MinValue + if (that.longEncoding) { + if (that._long == 0) return this.abs + // if (that._long == Long.MinValue) return this gcd (-that) + BigInt(BigInt.longGcd(this._long.abs, that._long.abs)) + } else that gcd this // force the BigInteger on the left + } else { + // this is not a valid long + if (that.longEncoding) { + if (that._long == 0) return this.abs + // if (that._long == Long.MinValue) return this gcd (-that) + val red = (this._bigInteger mod BigInteger.valueOf(that._long.abs)).longValue() + if (red == 0) return that.abs + BigInt(BigInt.longGcd(that._long.abs, red)) + } else BigInt(this.bigInteger.gcd(that.bigInteger)) + } + + + /** Returns a BigInt whose value is (this mod that). + * This method differs from `%` in that it always returns a non-negative BigInt. + * @param that A positive number + */ + def mod(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding && that._long > 0) { + val res = this._long % that._long + if (res >= 0) BigInt(res) else BigInt(res + that._long) + } else BigInt(this.bigInteger.mod(that.bigInteger)) + + /** Returns the minimum of this and that + */ + def min(that: BigInt): BigInt = + if (this <= that) this else that + + /** Returns the maximum of this and that + */ + def max(that: BigInt): BigInt = + if (this >= that) this else that + + /** Returns a BigInt whose value is (this raised to the power of exp). + */ + def pow(exp: Int): BigInt = BigInt(this.bigInteger.pow(exp)) + + /** Returns a BigInt whose value is + * (this raised to the power of exp modulo m). + */ + def modPow(exp: BigInt, m: BigInt): BigInt = BigInt(this.bigInteger.modPow(exp.bigInteger, m.bigInteger)) + + /** Returns a BigInt whose value is (the inverse of this modulo m). + */ + def modInverse(m: BigInt): BigInt = BigInt(this.bigInteger.modInverse(m.bigInteger)) + + /** Returns a BigInt whose value is the negation of this BigInt + */ + def unary_- : BigInt = if (longEncoding) BigInt(-_long) else BigInt(this.bigInteger.negate()) + + /** Returns the absolute value of this BigInt + */ + def abs: BigInt = if (signum < 0) -this else this + + /** Returns the sign of this BigInt; + * -1 if it is less than 0, + * +1 if it is greater than 0, + * 0 if it is equal to 0. + */ + def signum: Int = if (longEncoding) java.lang.Long.signum(_long) else _bigInteger.signum() + + /** Returns the sign of this BigInt; + * -1 if it is less than 0, + * +1 if it is greater than 0, + * 0 if it is equal to 0. + */ + def sign: BigInt = BigInt(signum) + + /** Returns the bitwise complement of this BigInt + */ + def unary_~ : BigInt = + // it is equal to -(this + 1) + if (longEncoding && _long != Long.MaxValue) BigInt(-(_long + 1)) else BigInt(this.bigInteger.not()) + + /** Returns true if and only if the designated bit is set. + */ + def testBit(n: Int): Boolean = + if (longEncoding && n >= 0) { + if (n <= 63) + (_long & (1L << n)) != 0 + else + _long < 0 // give the sign bit + } else this.bigInteger.testBit(n) + + /** Returns a BigInt whose value is equivalent to this BigInt with the designated bit set. + */ + def setBit(n: Int): BigInt = // note that we do not operate on the Long sign bit #63 + if (longEncoding && n <= 62 && n >= 0) BigInt(_long | (1L << n)) else BigInt(this.bigInteger.setBit(n)) + + /** Returns a BigInt whose value is equivalent to this BigInt with the designated bit cleared. + */ + def clearBit(n: Int): BigInt = // note that we do not operate on the Long sign bit #63 + if (longEncoding && n <= 62 && n >= 0) BigInt(_long & ~(1L << n)) else BigInt(this.bigInteger.clearBit(n)) + + /** Returns a BigInt whose value is equivalent to this BigInt with the designated bit flipped. + */ + def flipBit(n: Int): BigInt = // note that we do not operate on the Long sign bit #63 + if (longEncoding && n <= 62 && n >= 0) BigInt(_long ^ (1L << n)) else BigInt(this.bigInteger.flipBit(n)) + + /** Returns the index of the rightmost (lowest-order) one bit in this BigInt + * (the number of zero bits to the right of the rightmost one bit). + */ + def lowestSetBit: Int = + if (longEncoding) { + if (_long == 0) -1 else java.lang.Long.numberOfTrailingZeros(_long) + } else this.bigInteger.getLowestSetBit() + + /** Returns the number of bits in the minimal two's-complement representation of this BigInt, + * excluding a sign bit. + */ + def bitLength: Int = + // bitLength is defined as ceil(log2(this < 0 ? -this : this + 1))) + // where ceil(log2(x)) = 64 - numberOfLeadingZeros(x - 1) + if (longEncoding) { + if (_long < 0) 64 - java.lang.Long.numberOfLeadingZeros(-(_long + 1)) // takes care of Long.MinValue + else 64 - java.lang.Long.numberOfLeadingZeros(_long) + } else _bigInteger.bitLength() + + /** Returns the number of bits in the two's complement representation of this BigInt + * that differ from its sign bit. + */ + def bitCount: Int = + if (longEncoding) { + if (_long < 0) java.lang.Long.bitCount(-(_long + 1)) else java.lang.Long.bitCount(_long) + } else this.bigInteger.bitCount() + + /** Returns true if this BigInt is probably prime, false if it's definitely composite. + * @param certainty a measure of the uncertainty that the caller is willing to tolerate: + * if the call returns true the probability that this BigInt is prime + * exceeds (1 - 1/2 ^ certainty). + * The execution time of this method is proportional to the value of + * this parameter. + */ + def isProbablePrime(certainty: Int): Boolean = this.bigInteger.isProbablePrime(certainty) + + /** Converts this BigInt to a byte. + * If the BigInt is too big to fit in a byte, only the low-order 8 bits are returned. + * Note that this conversion can lose information about the overall magnitude of the + * BigInt value as well as return a result with the opposite sign. + */ + override def byteValue: Byte = intValue.toByte + + /** Converts this BigInt to a short. + * If the BigInt is too big to fit in a short, only the low-order 16 bits are returned. + * Note that this conversion can lose information about the overall magnitude of the + * BigInt value as well as return a result with the opposite sign. + */ + override def shortValue: Short = intValue.toShort + + /** Converts this BigInt to a char. + * If the BigInt is too big to fit in a char, only the low-order 16 bits are returned. + * Note that this conversion can lose information about the overall magnitude of the + * BigInt value and that it always returns a positive result. + */ + def charValue: Char = intValue.toChar + + /** Converts this BigInt to an int. + * If the BigInt is too big to fit in an int, only the low-order 32 bits + * are returned. Note that this conversion can lose information about the + * overall magnitude of the BigInt value as well as return a result with + * the opposite sign. + */ + def intValue: Int = if (longEncoding) _long.toInt else this.bigInteger.intValue + + /** Converts this BigInt to a long. + * If the BigInt is too big to fit in a long, only the low-order 64 bits + * are returned. Note that this conversion can lose information about the + * overall magnitude of the BigInt value as well as return a result with + * the opposite sign. + */ + def longValue: Long = if (longEncoding) _long else _bigInteger.longValue + + /** Converts this `BigInt` to a `float`. + * If this `BigInt` has too great a magnitude to represent as a float, + * it will be converted to `Float.NEGATIVE_INFINITY` or + * `Float.POSITIVE_INFINITY` as appropriate. + */ + def floatValue: Float = this.bigInteger.floatValue + + /** Converts this `BigInt` to a `double`. + * if this `BigInt` has too great a magnitude to represent as a double, + * it will be converted to `Double.NEGATIVE_INFINITY` or + * `Double.POSITIVE_INFINITY` as appropriate. + */ + def doubleValue: Double = + if (isValidLong && (-(1L << 53) <= _long && _long <= (1L << 53))) _long.toDouble + else this.bigInteger.doubleValue + + /** Create a `NumericRange[BigInt]` in range `[start;end)` + * with the specified step, where start is the target BigInt. + * + * @param end the end value of the range (exclusive) + * @param step the distance between elements (defaults to 1) + * @return the range + */ + def until(end: BigInt, step: BigInt = BigInt(1)): NumericRange.Exclusive[BigInt] = Range.BigInt(this, end, step) + + /** Like until, but inclusive of the end value. + */ + def to(end: BigInt, step: BigInt = BigInt(1)): NumericRange.Inclusive[BigInt] = Range.BigInt.inclusive(this, end, step) + + /** Returns the decimal String representation of this BigInt. + */ + override def toString(): String = if (longEncoding) _long.toString() else _bigInteger.toString() + + /** Returns the String representation in the specified radix of this BigInt. + */ + def toString(radix: Int): String = this.bigInteger.toString(radix) + + /** Returns a byte array containing the two's-complement representation of + * this BigInt. The byte array will be in big-endian byte-order: the most + * significant byte is in the zeroth element. The array will contain the + * minimum number of bytes required to represent this BigInt, including at + * least one sign bit. + */ + def toByteArray: Array[Byte] = this.bigInteger.toByteArray() +} diff --git a/library/src/scala/math/Equiv.scala b/library/src/scala/math/Equiv.scala new file mode 100644 index 000000000000..b5d01b362901 --- /dev/null +++ b/library/src/scala/math/Equiv.scala @@ -0,0 +1,507 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import java.util.Comparator +import scala.annotation.migration + +/** A trait for representing equivalence relations. It is important to + * distinguish between a type that can be compared for equality or + * equivalence and a representation of equivalence on some type. This + * trait is for representing the latter. + * + * An [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]] + * is a binary relation on a type. This relation is exposed as + * the `equiv` method of the `Equiv` trait. The relation must be: + * + * 1. reflexive: `equiv(x, x) == true` for any x of type `T`. + * 1. symmetric: `equiv(x, y) == equiv(y, x)` for any `x` and `y` of type `T`. + * 1. transitive: if `equiv(x, y) == true` and `equiv(y, z) == true`, then + * `equiv(x, z) == true` for any `x`, `y`, and `z` of type `T`. + */ + +trait Equiv[T] extends Any with Serializable { + /** Returns `true` iff `x` is equivalent to `y`. + */ + def equiv(x: T, y: T): Boolean +} + +trait LowPriorityEquiv { + self: Equiv.type => + + /** + * @deprecated This implicit universal `Equiv` instance allows accidentally + * comparing instances of types for which equality isn't well-defined or implemented. + * (For example, it does not make sense to compare two `Function1` instances.) + * + * Use `Equiv.universal` explicitly instead. If you really want an implicit universal `Equiv` instance + * despite the potential problems, consider `implicit def universalEquiv[T]: Equiv[T] = universal[T]`. + */ + @deprecated("Use explicit Equiv.universal instead. See Scaladoc entry for more information: " + + "https://www.scala-lang.org/api/current/scala/math/Equiv$.html#universalEquiv[T]:scala.math.Equiv[T]", + since = "2.13.0") + implicit def universalEquiv[T]: Equiv[T] = universal[T] +} + +object Equiv extends LowPriorityEquiv { + def reference[T <: AnyRef]: Equiv[T] = { _ eq _ } + def universal[T]: Equiv[T] = { _ == _ } + def fromComparator[T](cmp: Comparator[T]): Equiv[T] = { + (x, y) => cmp.compare(x, y) == 0 + } + def fromFunction[T](cmp: (T, T) => Boolean): Equiv[T] = { + (x, y) => cmp(x, y) + } + def by[T, S: Equiv](f: T => S): Equiv[T] = + ((x, y) => implicitly[Equiv[S]].equiv(f(x), f(y))) + + @inline def apply[T: Equiv]: Equiv[T] = implicitly[Equiv[T]] + + /* copied from Ordering */ + + private final val optionSeed = 43 + private final val iterableSeed = 47 + + private final class IterableEquiv[CC[X] <: Iterable[X], T](private val eqv: Equiv[T]) extends Equiv[CC[T]] { + def equiv(x: CC[T], y: CC[T]): Boolean = { + val xe = x.iterator + val ye = y.iterator + + while (xe.hasNext && ye.hasNext) { + if (!eqv.equiv(xe.next(), ye.next())) return false + } + + xe.hasNext == ye.hasNext + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: IterableEquiv[_, _] => this.eqv == that.eqv + case _ => false + } + override def hashCode(): Int = eqv.hashCode() * iterableSeed + } + + trait ExtraImplicits { + /** Not in the standard scope due to the potential for divergence: + * For instance `implicitly[Equiv[Any]]` diverges in its presence. + */ + implicit def seqEquiv[CC[X] <: scala.collection.Seq[X], T](implicit eqv: Equiv[T]): Equiv[CC[T]] = + new IterableEquiv[CC, T](eqv) + + implicit def sortedSetEquiv[CC[X] <: scala.collection.SortedSet[X], T](implicit eqv: Equiv[T]): Equiv[CC[T]] = + new IterableEquiv[CC, T](eqv) + } + + /** An object containing implicits which are not in the default scope. */ + object Implicits extends ExtraImplicits { } + + implicit object Unit extends Equiv[Unit] { + def equiv(x: Unit, y: Unit): Boolean = true + } + + implicit object Boolean extends Equiv[Boolean] { + def equiv(x: Boolean, y: Boolean): Boolean = x == y + } + + implicit object Byte extends Equiv[Byte] { + def equiv(x: Byte, y: Byte): Boolean = x == y + } + + implicit object Char extends Equiv[Char] { + def equiv(x: Char, y: Char): Boolean = x == y + } + + implicit object Short extends Equiv[Short] { + def equiv(x: Short, y: Short): Boolean = x == y + } + + implicit object Int extends Equiv[Int] { + def equiv(x: Int, y: Int): Boolean = x == y + } + + implicit object Long extends Equiv[Long] { + def equiv(x: Long, y: Long): Boolean = x == y + } + + /** `Equiv`s for `Float`s. + * + * @define floatEquiv Because the behaviour of `Float`s specified by IEEE is + * not consistent with behaviors required of an equivalence + * relation for `NaN` (it is not reflexive), there are two + * equivalences defined for `Float`: `StrictEquiv`, which + * is reflexive, and `IeeeEquiv`, which is consistent + * with IEEE spec and floating point operations defined in + * [[scala.math]]. + */ + object Float { + /** An equivalence for `Float`s which is reflexive (treats all `NaN`s + * as equivalent), and treats `-0.0` and `0.0` as not equivalent; it + * behaves the same as [[java.lang.Float.compare]]. + * + * $floatEquiv + * + * This equivalence may be preferable for collections. + * + * @see [[IeeeEquiv]] + */ + trait StrictEquiv extends Equiv[Float] { + def equiv(x: Float, y: Float): Boolean = java.lang.Float.compare(x, y) == 0 + } + implicit object StrictEquiv extends StrictEquiv + + /** An equivalence for `Float`s which is consistent with IEEE specifications. + * + * $floatEquiv + * + * This equivalence may be preferable for numeric contexts. + * + * @see [[StrictEquiv]] + */ + trait IeeeEquiv extends Equiv[Float] { + override def equiv(x: Float, y: Float): Boolean = x == y + } + implicit object IeeeEquiv extends IeeeEquiv + } + + @migration( + " The default implicit equivalence for floats no longer conforms to\n" + + " to IEEE 754's behavior for -0.0F and NaN.\n" + + " Import `Equiv.Float.IeeeEquiv` to recover the previous behavior.\n" + + " See also https://www.scala-lang.org/api/current/scala/math/Equiv$$Float$.html.", "2.13.2") + implicit object DeprecatedFloatEquiv extends Float.StrictEquiv + + /** `Equiv`s for `Double`s. + * + * @define doubleEquiv Because the behaviour of `Double`s specified by IEEE is + * not consistent with behaviors required of an equivalence + * relation for `NaN` (it is not reflexive), there are two + * equivalences defined for `Double`: `StrictEquiv`, which + * is reflexive, and `IeeeEquiv`, which is consistent + * with IEEE spec and floating point operations defined in + * [[scala.math]]. + */ + object Double { + /** An equivalence for `Double`s which is reflexive (treats all `NaN`s + * as equivalent), and treats `-0.0` and `0.0` as not equivalent; it + * behaves the same as [[java.lang.Double.compare]]. + * + * $doubleEquiv + * + * This equivalence may be preferable for collections. + * + * @see [[IeeeEquiv]] + */ + trait StrictEquiv extends Equiv[Double] { + def equiv(x: Double, y: Double): Boolean = java.lang.Double.compare(x, y) == 0 + } + implicit object StrictEquiv extends StrictEquiv + + /** An equivalence for `Double`s which is consistent with IEEE specifications. + * + * $doubleEquiv + * + * This equivalence may be preferable for numeric contexts. + * + * @see [[StrictEquiv]] + */ + trait IeeeEquiv extends Equiv[Double] { + def equiv(x: Double, y: Double): Boolean = x == y + } + implicit object IeeeEquiv extends IeeeEquiv + } + @migration( + " The default implicit equivalence for doubles no longer conforms to\n" + + " to IEEE 754's behavior for -0.0D and NaN.\n" + + " Import `Equiv.Double.IeeeEquiv` to recover the previous behavior.\n" + + " See also https://www.scala-lang.org/api/current/scala/math/Equiv$$Double$.html.", "2.13.2") + implicit object DeprecatedDoubleEquiv extends Double.StrictEquiv + + implicit object BigInt extends Equiv[BigInt] { + def equiv(x: BigInt, y: BigInt): Boolean = x == y + } + + implicit object BigDecimal extends Equiv[BigDecimal] { + def equiv(x: BigDecimal, y: BigDecimal): Boolean = x == y + } + + implicit object String extends Equiv[String] { + def equiv(x: String, y: String): Boolean = x == y + } + + implicit object Symbol extends Equiv[Symbol] { + def equiv(x: Symbol, y: Symbol): Boolean = x == y + } + + implicit def Option[T](implicit eqv: Equiv[T]): Equiv[Option[T]] = + new OptionEquiv[T](eqv) + + private[this] final class OptionEquiv[T](private val eqv: Equiv[T]) extends Equiv[Option[T]] { + def equiv(x: Option[T], y: Option[T]): Boolean = (x, y) match { + case (None, None) => true + case (Some(x), Some(y)) => eqv.equiv(x, y) + case _ => false + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: OptionEquiv[_] => this.eqv == that.eqv + case _ => false + } + override def hashCode(): Int = eqv.hashCode() * optionSeed + } + + implicit def Tuple2[T1, T2](implicit eqv1: Equiv[T1], eqv2: Equiv[T2]): Equiv[(T1, T2)] = + new Tuple2Equiv(eqv1, eqv2) + + private[this] final class Tuple2Equiv[T1, T2](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2]) extends Equiv[(T1, T2)] { + def equiv(x: (T1, T2), y: (T1, T2)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple2Equiv[_, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2).hashCode() + } + + implicit def Tuple3[T1, T2, T3](implicit eqv1: Equiv[T1], eqv2: Equiv[T2], eqv3: Equiv[T3]) : Equiv[(T1, T2, T3)] = + new Tuple3Equiv(eqv1, eqv2, eqv3) + + private[this] final class Tuple3Equiv[T1, T2, T3](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2], + private val eqv3: Equiv[T3]) extends Equiv[(T1, T2, T3)] { + def equiv(x: (T1, T2, T3), y: (T1, T2, T3)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) && + eqv3.equiv(x._3, y._3) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple3Equiv[_, _, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 && + this.eqv3 == that.eqv3 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2, eqv3).hashCode() + } + + implicit def Tuple4[T1, T2, T3, T4](implicit eqv1: Equiv[T1], eqv2: Equiv[T2], eqv3: Equiv[T3], eqv4: Equiv[T4]) : Equiv[(T1, T2, T3, T4)] = + new Tuple4Equiv(eqv1, eqv2, eqv3, eqv4) + + private[this] final class Tuple4Equiv[T1, T2, T3, T4](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2], + private val eqv3: Equiv[T3], + private val eqv4: Equiv[T4]) + extends Equiv[(T1, T2, T3, T4)] { + def equiv(x: (T1, T2, T3, T4), y: (T1, T2, T3, T4)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) && + eqv3.equiv(x._3, y._3) && + eqv4.equiv(x._4, y._4) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple4Equiv[_, _, _, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 && + this.eqv3 == that.eqv3 && + this.eqv4 == that.eqv4 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2, eqv3, eqv4).hashCode() + } + + implicit def Tuple5[T1, T2, T3, T4, T5](implicit eqv1: Equiv[T1], eqv2: Equiv[T2], eqv3: Equiv[T3], eqv4: Equiv[T4], eqv5: Equiv[T5]): Equiv[(T1, T2, T3, T4, T5)] = + new Tuple5Equiv(eqv1, eqv2, eqv3, eqv4, eqv5) + + private[this] final class Tuple5Equiv[T1, T2, T3, T4, T5](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2], + private val eqv3: Equiv[T3], + private val eqv4: Equiv[T4], + private val eqv5: Equiv[T5]) + extends Equiv[(T1, T2, T3, T4, T5)] { + def equiv(x: (T1, T2, T3, T4, T5), y: (T1, T2, T3, T4, T5)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) && + eqv3.equiv(x._3, y._3) && + eqv4.equiv(x._4, y._4) && + eqv5.equiv(x._5, y._5) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple5Equiv[_, _, _, _, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 && + this.eqv3 == that.eqv3 && + this.eqv4 == that.eqv4 && + this.eqv5 == that.eqv5 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2, eqv3, eqv4, eqv5).hashCode() + } + + implicit def Tuple6[T1, T2, T3, T4, T5, T6](implicit eqv1: Equiv[T1], eqv2: Equiv[T2], eqv3: Equiv[T3], eqv4: Equiv[T4], eqv5: Equiv[T5], eqv6: Equiv[T6]): Equiv[(T1, T2, T3, T4, T5, T6)] = + new Tuple6Equiv(eqv1, eqv2, eqv3, eqv4, eqv5, eqv6) + + private[this] final class Tuple6Equiv[T1, T2, T3, T4, T5, T6](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2], + private val eqv3: Equiv[T3], + private val eqv4: Equiv[T4], + private val eqv5: Equiv[T5], + private val eqv6: Equiv[T6]) + extends Equiv[(T1, T2, T3, T4, T5, T6)] { + def equiv(x: (T1, T2, T3, T4, T5, T6), y: (T1, T2, T3, T4, T5, T6)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) && + eqv3.equiv(x._3, y._3) && + eqv4.equiv(x._4, y._4) && + eqv5.equiv(x._5, y._5) && + eqv6.equiv(x._6, y._6) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple6Equiv[_, _, _, _, _, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 && + this.eqv3 == that.eqv3 && + this.eqv4 == that.eqv4 && + this.eqv5 == that.eqv5 && + this.eqv6 == that.eqv6 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2, eqv3, eqv4, eqv5, eqv6).hashCode() + } + + implicit def Tuple7[T1, T2, T3, T4, T5, T6, T7](implicit eqv1: Equiv[T1], eqv2: Equiv[T2], eqv3: Equiv[T3], eqv4: Equiv[T4], eqv5: Equiv[T5], eqv6: Equiv[T6], eqv7: Equiv[T7]): Equiv[(T1, T2, T3, T4, T5, T6, T7)] = + new Tuple7Equiv(eqv1, eqv2, eqv3, eqv4, eqv5, eqv6, eqv7) + + private[this] final class Tuple7Equiv[T1, T2, T3, T4, T5, T6, T7](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2], + private val eqv3: Equiv[T3], + private val eqv4: Equiv[T4], + private val eqv5: Equiv[T5], + private val eqv6: Equiv[T6], + private val eqv7: Equiv[T7]) + extends Equiv[(T1, T2, T3, T4, T5, T6, T7)] { + def equiv(x: (T1, T2, T3, T4, T5, T6, T7), y: (T1, T2, T3, T4, T5, T6, T7)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) && + eqv3.equiv(x._3, y._3) && + eqv4.equiv(x._4, y._4) && + eqv5.equiv(x._5, y._5) && + eqv6.equiv(x._6, y._6) && + eqv7.equiv(x._7, y._7) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple7Equiv[_, _, _, _, _, _, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 && + this.eqv3 == that.eqv3 && + this.eqv4 == that.eqv4 && + this.eqv5 == that.eqv5 && + this.eqv6 == that.eqv6 && + this.eqv7 == that.eqv7 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2, eqv3, eqv4, eqv5, eqv6, eqv7).hashCode() + } + + implicit def Tuple8[T1, T2, T3, T4, T5, T6, T7, T8](implicit eqv1: Equiv[T1], eqv2: Equiv[T2], eqv3: Equiv[T3], eqv4: Equiv[T4], eqv5: Equiv[T5], eqv6: Equiv[T6], eqv7: Equiv[T7], eqv8: Equiv[T8]): Equiv[(T1, T2, T3, T4, T5, T6, T7, T8)] = + new Tuple8Equiv(eqv1, eqv2, eqv3, eqv4, eqv5, eqv6, eqv7, eqv8) + + private[this] final class Tuple8Equiv[T1, T2, T3, T4, T5, T6, T7, T8](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2], + private val eqv3: Equiv[T3], + private val eqv4: Equiv[T4], + private val eqv5: Equiv[T5], + private val eqv6: Equiv[T6], + private val eqv7: Equiv[T7], + private val eqv8: Equiv[T8]) + extends Equiv[(T1, T2, T3, T4, T5, T6, T7, T8)] { + def equiv(x: (T1, T2, T3, T4, T5, T6, T7, T8), y: (T1, T2, T3, T4, T5, T6, T7, T8)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) && + eqv3.equiv(x._3, y._3) && + eqv4.equiv(x._4, y._4) && + eqv5.equiv(x._5, y._5) && + eqv6.equiv(x._6, y._6) && + eqv7.equiv(x._7, y._7) && + eqv8.equiv(x._8, y._8) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple8Equiv[_, _, _, _, _, _, _, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 && + this.eqv3 == that.eqv3 && + this.eqv4 == that.eqv4 && + this.eqv5 == that.eqv5 && + this.eqv6 == that.eqv6 && + this.eqv7 == that.eqv7 && + this.eqv8 == that.eqv8 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2, eqv3, eqv4, eqv5, eqv6, eqv7, eqv8).hashCode() + } + + implicit def Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9](implicit eqv1: Equiv[T1], eqv2: Equiv[T2], eqv3: Equiv[T3], eqv4: Equiv[T4], eqv5: Equiv[T5], eqv6: Equiv[T6], eqv7: Equiv[T7], eqv8 : Equiv[T8], eqv9: Equiv[T9]): Equiv[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] = + new Tuple9Equiv(eqv1, eqv2, eqv3, eqv4, eqv5, eqv6, eqv7, eqv8, eqv9) + + private[this] final class Tuple9Equiv[T1, T2, T3, T4, T5, T6, T7, T8, T9](private val eqv1: Equiv[T1], + private val eqv2: Equiv[T2], + private val eqv3: Equiv[T3], + private val eqv4: Equiv[T4], + private val eqv5: Equiv[T5], + private val eqv6: Equiv[T6], + private val eqv7: Equiv[T7], + private val eqv8: Equiv[T8], + private val eqv9: Equiv[T9]) + extends Equiv[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] { + def equiv(x: (T1, T2, T3, T4, T5, T6, T7, T8, T9), y: (T1, T2, T3, T4, T5, T6, T7, T8, T9)): Boolean = + eqv1.equiv(x._1, y._1) && + eqv2.equiv(x._2, y._2) && + eqv3.equiv(x._3, y._3) && + eqv4.equiv(x._4, y._4) && + eqv5.equiv(x._5, y._5) && + eqv6.equiv(x._6, y._6) && + eqv7.equiv(x._7, y._7) && + eqv8.equiv(x._8, y._8) && + eqv9.equiv(x._9, y._9) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple9Equiv[_, _, _, _, _, _, _, _, _] => + this.eqv1 == that.eqv1 && + this.eqv2 == that.eqv2 && + this.eqv3 == that.eqv3 && + this.eqv4 == that.eqv4 && + this.eqv5 == that.eqv5 && + this.eqv6 == that.eqv6 && + this.eqv7 == that.eqv7 && + this.eqv8 == that.eqv8 && + this.eqv9 == that.eqv9 + case _ => false + } + override def hashCode(): Int = (eqv1, eqv2, eqv3, eqv4, eqv5, eqv6, eqv7, eqv8, eqv9).hashCode() + } + +} diff --git a/library/src/scala/math/Fractional.scala b/library/src/scala/math/Fractional.scala new file mode 100644 index 000000000000..2066cc65d8ac --- /dev/null +++ b/library/src/scala/math/Fractional.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import scala.language.implicitConversions + +trait Fractional[T] extends Numeric[T] { + def div(x: T, y: T): T + + class FractionalOps(lhs: T) extends NumericOps(lhs) { + def /(rhs: T) = div(lhs, rhs) + } + override implicit def mkNumericOps(lhs: T): FractionalOps = + new FractionalOps(lhs) +} + +object Fractional { + @inline def apply[T](implicit frac: Fractional[T]): Fractional[T] = frac + + trait ExtraImplicits { + implicit def infixFractionalOps[T](x: T)(implicit num: Fractional[T]): Fractional[T]#FractionalOps = new num.FractionalOps(x) + } + object Implicits extends ExtraImplicits +} diff --git a/library/src/scala/math/Integral.scala b/library/src/scala/math/Integral.scala new file mode 100644 index 000000000000..d5dd189d64fe --- /dev/null +++ b/library/src/scala/math/Integral.scala @@ -0,0 +1,41 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import scala.language.implicitConversions + +trait Integral[T] extends Numeric[T] { + def quot(x: T, y: T): T + def rem(x: T, y: T): T + + class IntegralOps(lhs: T) extends NumericOps(lhs) { + def /(rhs: T) = quot(lhs, rhs) + def %(rhs: T) = rem(lhs, rhs) + def /%(rhs: T) = (quot(lhs, rhs), rem(lhs, rhs)) + } + override implicit def mkNumericOps(lhs: T): IntegralOps = new IntegralOps(lhs) +} + +object Integral { + @inline def apply[T](implicit int: Integral[T]): Integral[T] = int + + trait ExtraImplicits { + /** The regrettable design of Numeric/Integral/Fractional has them all + * bumping into one another when searching for this implicit, so they + * are exiled into their own companions. + */ + implicit def infixIntegralOps[T](x: T)(implicit num: Integral[T]): Integral[T]#IntegralOps = new num.IntegralOps(x) + } + object Implicits extends ExtraImplicits +} diff --git a/library/src/scala/math/Numeric.scala b/library/src/scala/math/Numeric.scala new file mode 100644 index 000000000000..84028f13f833 --- /dev/null +++ b/library/src/scala/math/Numeric.scala @@ -0,0 +1,261 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import scala.collection.StringParsers +import scala.language.implicitConversions +import scala.util.Try + +object Numeric { + @inline def apply[T](implicit num: Numeric[T]): Numeric[T] = num + + trait ExtraImplicits { + /** These implicits create conversions from a value for which an implicit Numeric + * exists to the inner class which creates infix operations. Once imported, you + * can write methods as follows: + * {{{ + * def plus[T: Numeric](x: T, y: T) = x + y + * }}} + */ + implicit def infixNumericOps[T](x: T)(implicit num: Numeric[T]): Numeric[T]#NumericOps = new num.NumericOps(x) + } + object Implicits extends ExtraImplicits { } + + trait BigIntIsIntegral extends Integral[BigInt] { + def plus(x: BigInt, y: BigInt): BigInt = x + y + def minus(x: BigInt, y: BigInt): BigInt = x - y + def times(x: BigInt, y: BigInt): BigInt = x * y + def quot(x: BigInt, y: BigInt): BigInt = x / y + def rem(x: BigInt, y: BigInt): BigInt = x % y + def negate(x: BigInt): BigInt = -x + def fromInt(x: Int): BigInt = BigInt(x) + def parseString(str: String): Option[BigInt] = Try(BigInt(str)).toOption + def toInt(x: BigInt): Int = x.intValue + def toLong(x: BigInt): Long = x.longValue + def toFloat(x: BigInt): Float = x.floatValue + def toDouble(x: BigInt): Double = x.doubleValue + } + implicit object BigIntIsIntegral extends BigIntIsIntegral with Ordering.BigIntOrdering + + trait IntIsIntegral extends Integral[Int] { + def plus(x: Int, y: Int): Int = x + y + def minus(x: Int, y: Int): Int = x - y + def times(x: Int, y: Int): Int = x * y + def quot(x: Int, y: Int): Int = x / y + def rem(x: Int, y: Int): Int = x % y + def negate(x: Int): Int = -x + def fromInt(x: Int): Int = x + def parseString(str: String): Option[Int] = StringParsers.parseInt(str) + def toInt(x: Int): Int = x + def toLong(x: Int): Long = x.toLong + def toFloat(x: Int): Float = x.toFloat + def toDouble(x: Int): Double = x.toDouble + override def signum(x: Int): Int = math.signum(x) + override def sign(x: Int): Int = math.signum(x) + } + implicit object IntIsIntegral extends IntIsIntegral with Ordering.IntOrdering + + trait ShortIsIntegral extends Integral[Short] { + def plus(x: Short, y: Short): Short = (x + y).toShort + def minus(x: Short, y: Short): Short = (x - y).toShort + def times(x: Short, y: Short): Short = (x * y).toShort + def quot(x: Short, y: Short): Short = (x / y).toShort + def rem(x: Short, y: Short): Short = (x % y).toShort + def negate(x: Short): Short = (-x).toShort + def fromInt(x: Int): Short = x.toShort + def parseString(str: String): Option[Short] = StringParsers.parseShort(str) + def toInt(x: Short): Int = x.toInt + def toLong(x: Short): Long = x.toLong + def toFloat(x: Short): Float = x.toFloat + def toDouble(x: Short): Double = x.toDouble + override def signum(x: Short): Int = math.signum(x.toInt) + override def sign(x: Short): Short = math.signum(x.toInt).toShort + } + implicit object ShortIsIntegral extends ShortIsIntegral with Ordering.ShortOrdering + + trait ByteIsIntegral extends Integral[Byte] { + def plus(x: Byte, y: Byte): Byte = (x + y).toByte + def minus(x: Byte, y: Byte): Byte = (x - y).toByte + def times(x: Byte, y: Byte): Byte = (x * y).toByte + def quot(x: Byte, y: Byte): Byte = (x / y).toByte + def rem(x: Byte, y: Byte): Byte = (x % y).toByte + def negate(x: Byte): Byte = (-x).toByte + def fromInt(x: Int): Byte = x.toByte + def parseString(str: String): Option[Byte] = StringParsers.parseByte(str) + def toInt(x: Byte): Int = x.toInt + def toLong(x: Byte): Long = x.toLong + def toFloat(x: Byte): Float = x.toFloat + def toDouble(x: Byte): Double = x.toDouble + override def signum(x: Byte): Int = math.signum(x.toInt) + override def sign(x: Byte): Byte = math.signum(x.toInt).toByte + } + implicit object ByteIsIntegral extends ByteIsIntegral with Ordering.ByteOrdering + + trait CharIsIntegral extends Integral[Char] { + def plus(x: Char, y: Char): Char = (x + y).toChar + def minus(x: Char, y: Char): Char = (x - y).toChar + def times(x: Char, y: Char): Char = (x * y).toChar + def quot(x: Char, y: Char): Char = (x / y).toChar + def rem(x: Char, y: Char): Char = (x % y).toChar + def negate(x: Char): Char = (-x).toChar + def fromInt(x: Int): Char = x.toChar + def parseString(str: String): Option[Char] = Try(str.toInt.toChar).toOption + def toInt(x: Char): Int = x.toInt + def toLong(x: Char): Long = x.toLong + def toFloat(x: Char): Float = x.toFloat + def toDouble(x: Char): Double = x.toDouble + override def signum(x: Char): Int = math.signum(x.toInt) + override def sign(x: Char): Char = math.signum(x.toInt).toChar + } + implicit object CharIsIntegral extends CharIsIntegral with Ordering.CharOrdering + + trait LongIsIntegral extends Integral[Long] { + def plus(x: Long, y: Long): Long = x + y + def minus(x: Long, y: Long): Long = x - y + def times(x: Long, y: Long): Long = x * y + def quot(x: Long, y: Long): Long = x / y + def rem(x: Long, y: Long): Long = x % y + def negate(x: Long): Long = -x + def fromInt(x: Int): Long = x.toLong + def parseString(str: String): Option[Long] = StringParsers.parseLong(str) + def toInt(x: Long): Int = x.toInt + def toLong(x: Long): Long = x + def toFloat(x: Long): Float = x.toFloat + def toDouble(x: Long): Double = x.toDouble + override def signum(x: Long): Int = math.signum(x).toInt + override def sign(x: Long): Long = math.signum(x) + } + implicit object LongIsIntegral extends LongIsIntegral with Ordering.LongOrdering + + trait FloatIsFractional extends Fractional[Float] { + def plus(x: Float, y: Float): Float = x + y + def minus(x: Float, y: Float): Float = x - y + def times(x: Float, y: Float): Float = x * y + def negate(x: Float): Float = -x + def fromInt(x: Int): Float = x.toFloat + def parseString(str: String): Option[Float] = StringParsers.parseFloat(str) + def toInt(x: Float): Int = x.toInt + def toLong(x: Float): Long = x.toLong + def toFloat(x: Float): Float = x + def toDouble(x: Float): Double = x.toDouble + def div(x: Float, y: Float): Float = x / y + // logic in Numeric base trait mishandles abs(-0.0f) + override def abs(x: Float): Float = math.abs(x) + // logic in Numeric base trait mishandles sign(-0.0f) and sign(Float.NaN) + override def sign(x: Float): Float = math.signum(x) + } + implicit object FloatIsFractional extends FloatIsFractional with Ordering.Float.IeeeOrdering + + trait DoubleIsFractional extends Fractional[Double] { + def plus(x: Double, y: Double): Double = x + y + def minus(x: Double, y: Double): Double = x - y + def times(x: Double, y: Double): Double = x * y + def negate(x: Double): Double = -x + def fromInt(x: Int): Double = x.toDouble + def parseString(str: String): Option[Double] = StringParsers.parseDouble(str) + def toInt(x: Double): Int = x.toInt + def toLong(x: Double): Long = x.toLong + def toFloat(x: Double): Float = x.toFloat + def toDouble(x: Double): Double = x + def div(x: Double, y: Double): Double = x / y + // logic in Numeric base trait mishandles abs(-0.0) + override def abs(x: Double): Double = math.abs(x) + // logic in Numeric base trait mishandles sign(-0.0) and sign(Double.NaN) + override def sign(x: Double): Double = math.signum(x) + } + implicit object DoubleIsFractional extends DoubleIsFractional with Ordering.Double.IeeeOrdering + + trait BigDecimalIsConflicted extends Numeric[BigDecimal] { + // works around pollution of math context by ignoring identity element + def plus(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._0 + if (x eq _0) y else x + y + } + def minus(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._0 + if (x eq _0) -y else x - y + } + // works around pollution of math context by ignoring identity element + def times(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._1 + if (x eq _1) y else x * y + } + def negate(x: BigDecimal): BigDecimal = -x + def fromInt(x: Int): BigDecimal = BigDecimal(x) + def parseString(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption + def toInt(x: BigDecimal): Int = x.intValue + def toLong(x: BigDecimal): Long = x.longValue + def toFloat(x: BigDecimal): Float = x.floatValue + def toDouble(x: BigDecimal): Double = x.doubleValue + } + private object BigDecimalIsConflicted { + private val _0 = BigDecimal(0) // cached zero is ordinarily cached for default math context + private val _1 = BigDecimal(1) // cached one is ordinarily cached for default math context + } + + trait BigDecimalIsFractional extends BigDecimalIsConflicted with Fractional[BigDecimal] { + def div(x: BigDecimal, y: BigDecimal): BigDecimal = x / y + } + trait BigDecimalAsIfIntegral extends BigDecimalIsConflicted with Integral[BigDecimal] { + def quot(x: BigDecimal, y: BigDecimal): BigDecimal = x quot y + def rem(x: BigDecimal, y: BigDecimal): BigDecimal = x remainder y + } + + // For BigDecimal we offer an implicit Fractional object, but also one + // which acts like an Integral type, which is useful in NumericRange. + implicit object BigDecimalIsFractional extends BigDecimalIsFractional with Ordering.BigDecimalOrdering + object BigDecimalAsIfIntegral extends BigDecimalAsIfIntegral with Ordering.BigDecimalOrdering +} + +trait Numeric[T] extends Ordering[T] { + def plus(x: T, y: T): T + def minus(x: T, y: T): T + def times(x: T, y: T): T + def negate(x: T): T + def fromInt(x: Int): T + def parseString(str: String): Option[T] + def toInt(x: T): Int + def toLong(x: T): Long + def toFloat(x: T): Float + def toDouble(x: T): Double + + def zero = fromInt(0) + def one = fromInt(1) + + def abs(x: T): T = if (lt(x, zero)) negate(x) else x + + @deprecated("use `sign` method instead", since = "2.13.0") def signum(x: T): Int = + if (lt(x, zero)) -1 + else if (gt(x, zero)) 1 + else 0 + def sign(x: T): T = + if (lt(x, zero)) negate(one) + else if (gt(x, zero)) one + else zero + + class NumericOps(lhs: T) { + def +(rhs: T) = plus(lhs, rhs) + def -(rhs: T) = minus(lhs, rhs) + def *(rhs: T) = times(lhs, rhs) + def unary_- = negate(lhs) + def abs: T = Numeric.this.abs(lhs) + @deprecated("use `sign` method instead", since = "2.13.0") def signum: Int = Numeric.this.signum(lhs) + def sign: T = Numeric.this.sign(lhs) + def toInt: Int = Numeric.this.toInt(lhs) + def toLong: Long = Numeric.this.toLong(lhs) + def toFloat: Float = Numeric.this.toFloat(lhs) + def toDouble: Double = Numeric.this.toDouble(lhs) + } + implicit def mkNumericOps(lhs: T): NumericOps = new NumericOps(lhs) +} diff --git a/library/src/scala/math/Ordered.scala b/library/src/scala/math/Ordered.scala new file mode 100644 index 000000000000..fe3ed90453e0 --- /dev/null +++ b/library/src/scala/math/Ordered.scala @@ -0,0 +1,101 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import scala.language.implicitConversions + +/** A trait for data that have a single, natural ordering. See + * [[scala.math.Ordering]] before using this trait for + * more information about whether to use [[scala.math.Ordering]] instead. + * + * Classes that implement this trait can be sorted with + * [[scala.util.Sorting]] and can be compared with standard comparison operators + * (e.g. > and <). + * + * Ordered should be used for data with a single, natural ordering (like + * integers) while Ordering allows for multiple ordering implementations. + * An Ordering instance will be implicitly created if necessary. + * + * [[scala.math.Ordering]] is an alternative to this trait that allows multiple orderings to be + * defined for the same type. + * + * [[scala.math.PartiallyOrdered]] is an alternative to this trait for partially ordered data. + * + * For example, create a simple class that implements `Ordered` and then sort it with [[scala.util.Sorting]]: + * {{{ + * case class OrderedClass(n:Int) extends Ordered[OrderedClass] { + * def compare(that: OrderedClass) = this.n - that.n + * } + * + * val x = Array(OrderedClass(1), OrderedClass(5), OrderedClass(3)) + * scala.util.Sorting.quickSort(x) + * x + * }}} + * + * It is important that the `equals` method for an instance of `Ordered[A]` be consistent with the + * compare method. However, due to limitations inherent in the type erasure semantics, there is no + * reasonable way to provide a default implementation of equality for instances of `Ordered[A]`. + * Therefore, if you need to be able to use equality on an instance of `Ordered[A]` you must + * provide it yourself either when inheriting or instantiating. + * + * It is important that the `hashCode` method for an instance of `Ordered[A]` be consistent with + * the `compare` method. However, it is not possible to provide a sensible default implementation. + * Therefore, if you need to be able compute the hash of an instance of `Ordered[A]` you must + * provide it yourself either when inheriting or instantiating. + * + * @see [[scala.math.Ordering]], [[scala.math.PartiallyOrdered]] + */ +trait Ordered[A] extends Any with java.lang.Comparable[A] { + + /** Result of comparing `this` with operand `that`. + * + * Implement this method to determine how instances of A will be sorted. + * + * Returns `x` where: + * + * - `x < 0` when `this < that` + * + * - `x == 0` when `this == that` + * + * - `x > 0` when `this > that` + * + */ + def compare(that: A): Int + + /** Returns true if `this` is less than `that` + */ + def < (that: A): Boolean = (this compare that) < 0 + + /** Returns true if `this` is greater than `that`. + */ + def > (that: A): Boolean = (this compare that) > 0 + + /** Returns true if `this` is less than or equal to `that`. + */ + def <= (that: A): Boolean = (this compare that) <= 0 + + /** Returns true if `this` is greater than or equal to `that`. + */ + def >= (that: A): Boolean = (this compare that) >= 0 + + /** Result of comparing `this` with operand `that`. + */ + def compareTo(that: A): Int = compare(that) +} + +object Ordered { + /** Lens from `Ordering[T]` to `Ordered[T]` */ + implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] = + new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) } +} diff --git a/library/src/scala/math/Ordering.scala b/library/src/scala/math/Ordering.scala new file mode 100644 index 000000000000..5a34f1fe91a9 --- /dev/null +++ b/library/src/scala/math/Ordering.scala @@ -0,0 +1,928 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +import java.util.Comparator + +import scala.language.implicitConversions +import scala.annotation.migration + +/** Ordering is a trait whose instances each represent a strategy for sorting + * instances of a type. + * + * Ordering's companion object defines many implicit objects to deal with + * subtypes of [[AnyVal]] (e.g. `Int`, `Double`), `String`, and others. + * + * To sort instances by one or more member variables, you can take advantage + * of these built-in orderings using [[Ordering.by]] and [[Ordering.on]]: + * + * {{{ + * import scala.util.Sorting + * val pairs = Array(("a", 5, 2), ("c", 3, 1), ("b", 1, 3)) + * + * // sort by 2nd element + * Sorting.quickSort(pairs)(Ordering.by[(String, Int, Int), Int](_._2)) + * + * // sort by the 3rd element, then 1st + * Sorting.quickSort(pairs)(Ordering[(Int, String)].on(x => (x._3, x._1))) + * }}} + * + * An `Ordering[T]` is implemented by specifying the [[compare]] method, + * `compare(a: T, b: T): Int`, which decides how to order two instances + * `a` and `b`. Instances of `Ordering[T]` can be used by things like + * `scala.util.Sorting` to sort collections like `Array[T]`. + * + * For example: + * + * {{{ + * import scala.util.Sorting + * + * case class Person(name:String, age:Int) + * val people = Array(Person("bob", 30), Person("ann", 32), Person("carl", 19)) + * + * // sort by age + * object AgeOrdering extends Ordering[Person] { + * def compare(a:Person, b:Person) = a.age.compare(b.age) + * } + * Sorting.quickSort(people)(AgeOrdering) + * }}} + * + * This trait and [[scala.math.Ordered]] both provide this same functionality, but + * in different ways. A type `T` can be given a single way to order itself by + * extending `Ordered`. Using `Ordering`, this same type may be sorted in many + * other ways. `Ordered` and `Ordering` both provide implicits allowing them to be + * used interchangeably. + * + * You can `import scala.math.Ordering.Implicits._` to gain access to other + * implicit orderings. + * + * @see [[scala.math.Ordered]], [[scala.util.Sorting]], [[scala.math.Ordering.Implicits]] + */ +trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable { + outer => + + /** Returns whether a comparison between `x` and `y` is defined, and if so + * the result of `compare(x, y)`. + */ + def tryCompare(x: T, y: T): Some[Int] = Some(compare(x, y)) + + /** Returns an integer whose sign communicates how x compares to y. + * + * The result sign has the following meaning: + * + * - negative if x < y + * - positive if x > y + * - zero otherwise (if x == y) + */ + def compare(x: T, y: T): Int + + /** Return true if `x` <= `y` in the ordering. */ + override def lteq(x: T, y: T): Boolean = compare(x, y) <= 0 + + /** Return true if `x` >= `y` in the ordering. */ + override def gteq(x: T, y: T): Boolean = compare(x, y) >= 0 + + /** Return true if `x` < `y` in the ordering. */ + override def lt(x: T, y: T): Boolean = compare(x, y) < 0 + + /** Return true if `x` > `y` in the ordering. */ + override def gt(x: T, y: T): Boolean = compare(x, y) > 0 + + /** Return true if `x` == `y` in the ordering. */ + override def equiv(x: T, y: T): Boolean = compare(x, y) == 0 + + /** Return `x` if `x` >= `y`, otherwise `y`. */ + def max[U <: T](x: U, y: U): U = if (gteq(x, y)) x else y + + /** Return `x` if `x` <= `y`, otherwise `y`. */ + def min[U <: T](x: U, y: U): U = if (lteq(x, y)) x else y + + /** Return the opposite ordering of this one. + * + * Implementations overriding this method MUST override [[isReverseOf]] + * as well if they change the behavior at all (for example, caching does + * not require overriding it). + */ + override def reverse: Ordering[T] = new Ordering.Reverse[T](this) + + /** Returns whether or not the other ordering is the opposite + * ordering of this one. + * + * Equivalent to `other == this.reverse`. + * + * Implementations should only override this method if they are overriding + * [[reverse]] as well. + */ + def isReverseOf(other: Ordering[_]): Boolean = other match { + case that: Ordering.Reverse[_] => that.outer == this + case _ => false + } + + /** Given f, a function from U into T, creates an Ordering[U] whose compare + * function is equivalent to: + * + * {{{ + * def compare(x:U, y:U) = Ordering[T].compare(f(x), f(y)) + * }}} + */ + def on[U](f: U => T): Ordering[U] = new Ordering[U] { + def compare(x: U, y: U) = outer.compare(f(x), f(y)) + } + + /** Creates an Ordering[T] whose compare function returns the + * result of this Ordering's compare function, if it is non-zero, + * or else the result of `other`s compare function. + * + * @example + * {{{ + * case class Pair(a: Int, b: Int) + * + * val pairOrdering = Ordering.by[Pair, Int](_.a) + * .orElse(Ordering.by[Pair, Int](_.b)) + * }}} + * + * @param other an Ordering to use if this Ordering returns zero + */ + def orElse(other: Ordering[T]): Ordering[T] = (x, y) => { + val res1 = outer.compare(x, y) + if (res1 != 0) res1 else other.compare(x, y) + } + + /** Given f, a function from T into S, creates an Ordering[T] whose compare + * function returns the result of this Ordering's compare function, + * if it is non-zero, or else a result equivalent to: + * + * {{{ + * Ordering[S].compare(f(x), f(y)) + * }}} + * + * This function is equivalent to passing the result of `Ordering.by(f)` + * to `orElse`. + * + * @example + * {{{ + * case class Pair(a: Int, b: Int) + * + * val pairOrdering = Ordering.by[Pair, Int](_.a) + * .orElseBy[Int](_.b) + * }}} + */ + def orElseBy[S](f: T => S)(implicit ord: Ordering[S]): Ordering[T] = (x, y) => { + val res1 = outer.compare(x, y) + if (res1 != 0) res1 else ord.compare(f(x), f(y)) + } + + /** This inner class defines comparison operators available for `T`. + * + * It can't extend `AnyVal` because it is not a top-level class + * or a member of a statically accessible object. + */ + class OrderingOps(lhs: T) { + def <(rhs: T): Boolean = lt(lhs, rhs) + def <=(rhs: T): Boolean = lteq(lhs, rhs) + def >(rhs: T): Boolean = gt(lhs, rhs) + def >=(rhs: T): Boolean = gteq(lhs, rhs) + def equiv(rhs: T): Boolean = Ordering.this.equiv(lhs, rhs) + def max(rhs: T): T = Ordering.this.max(lhs, rhs) + def min(rhs: T): T = Ordering.this.min(lhs, rhs) + } + + /** This implicit method augments `T` with the comparison operators defined + * in `scala.math.Ordering.Ops`. + */ + implicit def mkOrderingOps(lhs: T): OrderingOps = new OrderingOps(lhs) +} + +trait LowPriorityOrderingImplicits { + + type AsComparable[A] = A => Comparable[_ >: A] + + /** This would conflict with all the nice implicit Orderings + * available, but thanks to the magic of prioritized implicits + * via subclassing we can make `Ordered[A] => Ordering[A]` only + * turn up if nothing else works. Since `Ordered[A]` extends + * `Comparable[A]` anyway, we can throw in some Java interop too. + */ + implicit def ordered[A](implicit asComparable: AsComparable[A]): Ordering[A] = new Ordering[A] { + def compare(x: A, y: A): Int = asComparable(x).compareTo(y) + } + + implicit def comparatorToOrdering[A](implicit cmp: Comparator[A]): Ordering[A] = new Ordering[A] { + def compare(x: A, y: A) = cmp.compare(x, y) + } +} + +/** This is the companion object for the [[scala.math.Ordering]] trait. + * + * It contains many implicit orderings as well as well as methods to construct + * new orderings. + */ +object Ordering extends LowPriorityOrderingImplicits { + private final val reverseSeed = 41 + private final val optionSeed = 43 + private final val iterableSeed = 47 + + @inline def apply[T](implicit ord: Ordering[T]) = ord + + /** An ordering which caches the value of its reverse. */ + sealed trait CachedReverse[T] extends Ordering[T] { + private[this] val _reverse = super.reverse + override final def reverse: Ordering[T] = _reverse + override final def isReverseOf(other: Ordering[_]): Boolean = other eq _reverse + } + + /** A reverse ordering */ + private final class Reverse[T](private[Ordering] val outer: Ordering[T]) extends Ordering[T] { + override def reverse: Ordering[T] = outer + override def isReverseOf(other: Ordering[_]): Boolean = other == outer + + def compare(x: T, y: T): Int = outer.compare(y, x) + override def lteq(x: T, y: T): Boolean = outer.lteq(y, x) + override def gteq(x: T, y: T): Boolean = outer.gteq(y, x) + override def lt(x: T, y: T): Boolean = outer.lt(y, x) + override def gt(x: T, y: T): Boolean = outer.gt(y, x) + override def equiv(x: T, y: T): Boolean = outer.equiv(y, x) + override def max[U <: T](x: U, y: U): U = outer.min(x, y) + override def min[U <: T](x: U, y: U): U = outer.max(x, y) + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Reverse[_] => this.outer == that.outer + case _ => false + } + override def hashCode(): Int = outer.hashCode() * reverseSeed + } + + @SerialVersionUID(-2996748994664583574L) + private final class IterableOrdering[CC[X] <: Iterable[X], T](private val ord: Ordering[T]) extends Ordering[CC[T]] { + def compare(x: CC[T], y: CC[T]): Int = { + val xe = x.iterator + val ye = y.iterator + + while (xe.hasNext && ye.hasNext) { + val res = ord.compare(xe.next(), ye.next()) + if (res != 0) return res + } + + Boolean.compare(xe.hasNext, ye.hasNext) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: IterableOrdering[_, _] => this.ord == that.ord + case _ => false + } + override def hashCode(): Int = ord.hashCode() * iterableSeed + } + + trait ExtraImplicits { + /** Not in the standard scope due to the potential for divergence: + * For instance `implicitly[Ordering[Any]]` diverges in its presence. + */ + implicit def seqOrdering[CC[X] <: scala.collection.Seq[X], T](implicit ord: Ordering[T]): Ordering[CC[T]] = + new IterableOrdering[CC, T](ord) + + implicit def sortedSetOrdering[CC[X] <: scala.collection.SortedSet[X], T](implicit ord: Ordering[T]): Ordering[CC[T]] = + new IterableOrdering[CC, T](ord) + + /** This implicit creates a conversion from any value for which an + * implicit `Ordering` exists to the class which creates infix operations. + * With it imported, you can write methods as follows: + * + * {{{ + * def lessThan[T: Ordering](x: T, y: T) = x < y + * }}} + */ + implicit def infixOrderingOps[T](x: T)(implicit ord: Ordering[T]): Ordering[T]#OrderingOps = new ord.OrderingOps(x) + } + + /** An object containing implicits which are not in the default scope. */ + object Implicits extends ExtraImplicits { } + + /** Construct an Ordering[T] given a function `lt`. */ + def fromLessThan[T](cmp: (T, T) => Boolean): Ordering[T] = new Ordering[T] { + def compare(x: T, y: T) = if (cmp(x, y)) -1 else if (cmp(y, x)) 1 else 0 + // overrides to avoid multiple comparisons + override def lt(x: T, y: T): Boolean = cmp(x, y) + override def gt(x: T, y: T): Boolean = cmp(y, x) + override def gteq(x: T, y: T): Boolean = !cmp(x, y) + override def lteq(x: T, y: T): Boolean = !cmp(y, x) + } + + /** Given f, a function from T into S, creates an Ordering[T] whose compare + * function is equivalent to: + * + * {{{ + * def compare(x:T, y:T) = Ordering[S].compare(f(x), f(y)) + * }}} + * + * This function is an analogue to Ordering.on where the Ordering[S] + * parameter is passed implicitly. + */ + def by[T, S](f: T => S)(implicit ord: Ordering[S]): Ordering[T] = new Ordering[T] { + def compare(x: T, y: T) = ord.compare(f(x), f(y)) + override def lt(x: T, y: T): Boolean = ord.lt(f(x), f(y)) + override def gt(x: T, y: T): Boolean = ord.gt(f(x), f(y)) + override def gteq(x: T, y: T): Boolean = ord.gteq(f(x), f(y)) + override def lteq(x: T, y: T): Boolean = ord.lteq(f(x), f(y)) + } + + trait UnitOrdering extends Ordering[Unit] { + def compare(x: Unit, y: Unit) = 0 + } + @SerialVersionUID(4089257611611206746L) + implicit object Unit extends UnitOrdering + + trait BooleanOrdering extends Ordering[Boolean] { + def compare(x: Boolean, y: Boolean): Int = java.lang.Boolean.compare(x, y) + } + @SerialVersionUID(-94703182178890445L) + implicit object Boolean extends BooleanOrdering + + trait ByteOrdering extends Ordering[Byte] { + def compare(x: Byte, y: Byte): Int = java.lang.Byte.compare(x, y) + } + @SerialVersionUID(-2268545360148786406L) + implicit object Byte extends ByteOrdering + + trait CharOrdering extends Ordering[Char] { + def compare(x: Char, y: Char): Int = java.lang.Character.compare(x, y) + } + @SerialVersionUID(2588141633104296698L) + implicit object Char extends CharOrdering + + trait ShortOrdering extends Ordering[Short] { + def compare(x: Short, y: Short): Int = java.lang.Short.compare(x, y) + } + @SerialVersionUID(4919657051864630912L) + implicit object Short extends ShortOrdering + + trait IntOrdering extends Ordering[Int] { + def compare(x: Int, y: Int): Int = java.lang.Integer.compare(x, y) + } + @SerialVersionUID(-8412871093094815037L) + implicit object Int extends IntOrdering with CachedReverse[Int] + + trait LongOrdering extends Ordering[Long] { + def compare(x: Long, y: Long): Int = java.lang.Long.compare(x, y) + } + @SerialVersionUID(-5231423581640563981L) + implicit object Long extends LongOrdering + + /** `Ordering`s for `Float`s. + * + * The default extends `Ordering.Float.TotalOrdering`. + * + * `Ordering.Float.TotalOrdering` uses the `java.lang.Float.compare` semantics for all operations. + * Scala also provides the `Ordering.Float.IeeeOrdering` semantics. Which uses the IEEE 754 semantics + * for float ordering. + * + * Historically: `IeeeOrdering` was used in Scala from 2.10.x through 2.12.x. This changed in 2.13.0 + * to `TotalOrdering`. + * + * Prior to Scala 2.10.0, the `Ordering` instance used semantics + * consistent with `java.lang.Float.compare`. + * + * Scala 2.10.0 changed the implementation of `lt`, `equiv`, `min`, etc., to be + * IEEE 754 compliant, while keeping the `compare` method NOT compliant, + * creating an internally inconsistent instance. IEEE 754 specifies that + * `0.0F == -0.0F`. In addition, it requires all comparisons with `Float.NaN` return + * `false` thus `0.0F < Float.NaN`, `0.0F > Float.NaN`, and + * `Float.NaN == Float.NaN` all yield `false`, analogous `None` in `flatMap`. + * + * + * {{{ + * List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).sorted // List(-Infinity, 0.0, 1.0, NaN) + * List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).min // -Infinity + * implicitly[Ordering[Float]].lt(0.0F, 0.0F / 0.0F) // true + * { + * import Ordering.Float.IeeeOrdering + * List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).sorted // List(-Infinity, 0.0, 1.0, NaN) + * List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).min // NaN + * implicitly[Ordering[Float]].lt(0.0F, 0.0F / 0.0F) // false + * } + * }}} + * + * @define floatOrdering Because the behavior of `Float`s specified by IEEE is + * not consistent with a total ordering when dealing with + * `NaN`, there are two orderings defined for `Float`: + * `TotalOrdering`, which is consistent with a total + * ordering, and `IeeeOrdering`, which is consistent + * as much as possible with IEEE spec and floating point + * operations defined in [[scala.math]]. + */ + object Float { + /** An ordering for `Float`s which is a fully consistent total ordering, + * and treats `NaN` as larger than all other `Float` values; it behaves + * the same as [[java.lang.Float.compare]]. + * + * $floatOrdering + * + * This ordering may be preferable for sorting collections. + * + * @see [[IeeeOrdering]] + */ + trait TotalOrdering extends Ordering[Float] { + def compare(x: Float, y: Float) = java.lang.Float.compare(x, y) + } + @SerialVersionUID(2951539161283192433L) + implicit object TotalOrdering extends TotalOrdering + + /** An ordering for `Float`s which is consistent with IEEE specifications + * whenever possible. + * + * - `lt`, `lteq`, `equiv`, `gteq` and `gt` are consistent with primitive + * comparison operations for `Float`s, and return `false` when called with + * `NaN`. + * - `min` and `max` are consistent with `math.min` and `math.max`, and + * return `NaN` when called with `NaN` as either argument. + * - `compare` behaves the same as [[java.lang.Float.compare]]. + * + * $floatOrdering + * + * This ordering may be preferable for numeric contexts. + * + * @see [[TotalOrdering]] + */ + trait IeeeOrdering extends Ordering[Float] { + def compare(x: Float, y: Float) = java.lang.Float.compare(x, y) + + override def lteq(x: Float, y: Float): Boolean = x <= y + override def gteq(x: Float, y: Float): Boolean = x >= y + override def lt(x: Float, y: Float): Boolean = x < y + override def gt(x: Float, y: Float): Boolean = x > y + override def equiv(x: Float, y: Float): Boolean = x == y + override def max[U <: Float](x: U, y: U): U = math.max(x, y).asInstanceOf[U] + override def min[U <: Float](x: U, y: U): U = math.min(x, y).asInstanceOf[U] + } + @SerialVersionUID(2142189527751553605L) + implicit object IeeeOrdering extends IeeeOrdering + } + @migration( + " The default implicit ordering for floats now maintains consistency\n" + + " between its `compare` method and its `lt`, `min`, `equiv`, etc., methods,\n" + + " which means nonconforming to IEEE 754's behavior for -0.0F and NaN.\n" + + " The sort order of floats remains the same, however, with NaN at the end.\n" + + " Import Ordering.Float.IeeeOrdering to recover the previous behavior.\n" + + " See also https://www.scala-lang.org/api/current/scala/math/Ordering$$Float$.html.", "2.13.0") + @SerialVersionUID(-8500693657289762132L) + implicit object DeprecatedFloatOrdering extends Float.TotalOrdering + + /** `Ordering`s for `Double`s. + * + * The behavior of the comparison operations provided by the default (implicit) + * ordering on `Double` changed in 2.10.0 and 2.13.0. + * Prior to Scala 2.10.0, the `Ordering` instance used semantics + * consistent with `java.lang.Double.compare`. + * + * Scala 2.10.0 changed the implementation of `lt`, `equiv`, `min`, etc., to be + * IEEE 754 compliant, while keeping the `compare` method NOT compliant, + * creating an internally inconsistent instance. IEEE 754 specifies that + * `0.0 == -0.0`. In addition, it requires all comparisons with `Double.NaN` return + * `false` thus `0.0 < Double.NaN`, `0.0 > Double.NaN`, and + * `Double.NaN == Double.NaN` all yield `false`, analogous `None` in `flatMap`. + * + * Recognizing the limitation of the IEEE 754 semantics in terms of ordering, + * Scala 2.13.0 created two instances: `Ordering.Double.IeeeOrdering`, which retains + * the IEEE 754 semantics from Scala 2.12.x, and `Ordering.Double.TotalOrdering`, + * which brings back the `java.lang.Double.compare` semantics for all operations. + * The default extends `TotalOrdering`. + * + * {{{ + * List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).sorted // List(-Infinity, 0.0, 1.0, NaN) + * List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).min // -Infinity + * implicitly[Ordering[Double]].lt(0.0, 0.0 / 0.0) // true + * { + * import Ordering.Double.IeeeOrdering + * List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).sorted // List(-Infinity, 0.0, 1.0, NaN) + * List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).min // NaN + * implicitly[Ordering[Double]].lt(0.0, 0.0 / 0.0) // false + * } + * }}} + * + * @define doubleOrdering Because the behavior of `Double`s specified by IEEE is + * not consistent with a total ordering when dealing with + * `NaN`, there are two orderings defined for `Double`: + * `TotalOrdering`, which is consistent with a total + * ordering, and `IeeeOrdering`, which is consistent + * as much as possible with IEEE spec and floating point + * operations defined in [[scala.math]]. + */ + object Double { + /** An ordering for `Double`s which is a fully consistent total ordering, + * and treats `NaN` as larger than all other `Double` values; it behaves + * the same as [[java.lang.Double.compare]]. + * + * $doubleOrdering + * + * This ordering may be preferable for sorting collections. + * + * @see [[IeeeOrdering]] + */ + trait TotalOrdering extends Ordering[Double] { + def compare(x: Double, y: Double) = java.lang.Double.compare(x, y) + } + @SerialVersionUID(-831119229746134011L) + implicit object TotalOrdering extends TotalOrdering + + /** An ordering for `Double`s which is consistent with IEEE specifications + * whenever possible. + * + * - `lt`, `lteq`, `equiv`, `gteq` and `gt` are consistent with primitive + * comparison operations for `Double`s, and return `false` when called with + * `NaN`. + * - `min` and `max` are consistent with `math.min` and `math.max`, and + * return `NaN` when called with `NaN` as either argument. + * - `compare` behaves the same as [[java.lang.Double.compare]]. + * + * $doubleOrdering + * + * This ordering may be preferable for numeric contexts. + * + * @see [[TotalOrdering]] + */ + trait IeeeOrdering extends Ordering[Double] { + def compare(x: Double, y: Double) = java.lang.Double.compare(x, y) + + override def lteq(x: Double, y: Double): Boolean = x <= y + override def gteq(x: Double, y: Double): Boolean = x >= y + override def lt(x: Double, y: Double): Boolean = x < y + override def gt(x: Double, y: Double): Boolean = x > y + override def equiv(x: Double, y: Double): Boolean = x == y + override def max[U <: Double](x: U, y: U): U = math.max(x, y).asInstanceOf[U] + override def min[U <: Double](x: U, y: U): U = math.min(x, y).asInstanceOf[U] + } + @SerialVersionUID(5722631152457877238L) + implicit object IeeeOrdering extends IeeeOrdering + } + @migration( + " The default implicit ordering for doubles now maintains consistency\n" + + " between its `compare` method and its `lt`, `min`, `equiv`, etc., methods,\n" + + " which means nonconforming to IEEE 754's behavior for -0.0 and NaN.\n" + + " The sort order of doubles remains the same, however, with NaN at the end.\n" + + " Import Ordering.Double.IeeeOrdering to recover the previous behavior.\n" + + " See also https://www.scala-lang.org/api/current/scala/math/Ordering$$Double$.html.", "2.13.0") + @SerialVersionUID(-7340686892557971538L) + implicit object DeprecatedDoubleOrdering extends Double.TotalOrdering + + trait BigIntOrdering extends Ordering[BigInt] { + def compare(x: BigInt, y: BigInt) = x.compare(y) + } + @SerialVersionUID(-3075297647817530785L) + implicit object BigInt extends BigIntOrdering + + trait BigDecimalOrdering extends Ordering[BigDecimal] { + def compare(x: BigDecimal, y: BigDecimal) = x.compare(y) + } + @SerialVersionUID(-833457937756812905L) + implicit object BigDecimal extends BigDecimalOrdering + + trait StringOrdering extends Ordering[String] { + def compare(x: String, y: String) = x.compareTo(y) + } + @SerialVersionUID(1302240016074071079L) + implicit object String extends StringOrdering + + trait SymbolOrdering extends Ordering[Symbol] { + def compare(x: Symbol, y: Symbol): Int = x.name.compareTo(y.name) + } + @SerialVersionUID(1996702162912307637L) + implicit object Symbol extends SymbolOrdering + + trait OptionOrdering[T] extends Ordering[Option[T]] { + def optionOrdering: Ordering[T] + def compare(x: Option[T], y: Option[T]) = (x, y) match { + case (None, None) => 0 + case (None, _) => -1 + case (_, None) => 1 + case (Some(x), Some(y)) => optionOrdering.compare(x, y) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: OptionOrdering[_] => this.optionOrdering == that.optionOrdering + case _ => false + } + override def hashCode(): Int = optionOrdering.hashCode() * optionSeed + } + implicit def Option[T](implicit ord: Ordering[T]): Ordering[Option[T]] = { + @SerialVersionUID(6958068162830323876L) + class O extends OptionOrdering[T] { val optionOrdering = ord } + new O() + } + + /** @deprecated Iterables are not guaranteed to have a consistent order, so the `Ordering` + * returned by this method may not be stable or meaningful. If you are using a type + * with a consistent order (such as `Seq`), use its `Ordering` (found in the + * [[Implicits]] object) instead. + */ + @deprecated("Iterables are not guaranteed to have a consistent order; if using a type with a " + + "consistent order (e.g. Seq), use its Ordering (found in the Ordering.Implicits object)", since = "2.13.0") + implicit def Iterable[T](implicit ord: Ordering[T]): Ordering[Iterable[T]] = + new IterableOrdering[Iterable, T](ord) + + implicit def Tuple2[T1, T2](implicit ord1: Ordering[T1], ord2: Ordering[T2]): Ordering[(T1, T2)] = + new Tuple2Ordering(ord1, ord2) + + @SerialVersionUID(4945084135299531202L) + private[this] final class Tuple2Ordering[T1, T2](private val ord1: Ordering[T1], + private val ord2: Ordering[T2]) extends Ordering[(T1, T2)] { + def compare(x: (T1, T2), y: (T1, T2)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + ord2.compare(x._2, y._2) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple2Ordering[_, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 + case _ => false + } + override def hashCode(): Int = (ord1, ord2).hashCode() + } + + implicit def Tuple3[T1, T2, T3](implicit ord1: Ordering[T1], ord2: Ordering[T2], ord3: Ordering[T3]) : Ordering[(T1, T2, T3)] = + new Tuple3Ordering(ord1, ord2, ord3) + + @SerialVersionUID(-5367223704121832335L) + private[this] final class Tuple3Ordering[T1, T2, T3](private val ord1: Ordering[T1], + private val ord2: Ordering[T2], + private val ord3: Ordering[T3]) extends Ordering[(T1, T2, T3)] { + def compare(x: (T1, T2, T3), y: (T1, T2, T3)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + val compare2 = ord2.compare(x._2, y._2) + if (compare2 != 0) return compare2 + ord3.compare(x._3, y._3) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple3Ordering[_, _, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 && + this.ord3 == that.ord3 + case _ => false + } + override def hashCode(): Int = (ord1, ord2, ord3).hashCode() + } + + implicit def Tuple4[T1, T2, T3, T4](implicit ord1: Ordering[T1], ord2: Ordering[T2], ord3: Ordering[T3], ord4: Ordering[T4]) : Ordering[(T1, T2, T3, T4)] = + new Tuple4Ordering(ord1, ord2, ord3, ord4) + + @SerialVersionUID(-6055313861145218178L) + private[this] final class Tuple4Ordering[T1, T2, T3, T4](private val ord1: Ordering[T1], + private val ord2: Ordering[T2], + private val ord3: Ordering[T3], + private val ord4: Ordering[T4]) + extends Ordering[(T1, T2, T3, T4)] { + def compare(x: (T1, T2, T3, T4), y: (T1, T2, T3, T4)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + val compare2 = ord2.compare(x._2, y._2) + if (compare2 != 0) return compare2 + val compare3 = ord3.compare(x._3, y._3) + if (compare3 != 0) return compare3 + ord4.compare(x._4, y._4) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple4Ordering[_, _, _, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 && + this.ord3 == that.ord3 && + this.ord4 == that.ord4 + case _ => false + } + override def hashCode(): Int = (ord1, ord2, ord3, ord4).hashCode() + } + + implicit def Tuple5[T1, T2, T3, T4, T5](implicit ord1: Ordering[T1], ord2: Ordering[T2], ord3: Ordering[T3], ord4: Ordering[T4], ord5: Ordering[T5]): Ordering[(T1, T2, T3, T4, T5)] = + new Tuple5Ordering(ord1, ord2, ord3, ord4, ord5) + + @SerialVersionUID(-5517329921227646061L) + private[this] final class Tuple5Ordering[T1, T2, T3, T4, T5](private val ord1: Ordering[T1], + private val ord2: Ordering[T2], + private val ord3: Ordering[T3], + private val ord4: Ordering[T4], + private val ord5: Ordering[T5]) + extends Ordering[(T1, T2, T3, T4, T5)] { + def compare(x: (T1, T2, T3, T4, T5), y: (T1, T2, T3, T4, T5)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + val compare2 = ord2.compare(x._2, y._2) + if (compare2 != 0) return compare2 + val compare3 = ord3.compare(x._3, y._3) + if (compare3 != 0) return compare3 + val compare4 = ord4.compare(x._4, y._4) + if (compare4 != 0) return compare4 + ord5.compare(x._5, y._5) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple5Ordering[_, _, _, _, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 && + this.ord3 == that.ord3 && + this.ord4 == that.ord4 && + this.ord5 == that.ord5 + case _ => false + } + override def hashCode(): Int = (ord1, ord2, ord3, ord4, ord5).hashCode() + } + + @SerialVersionUID(3045467524192969060L) + implicit def Tuple6[T1, T2, T3, T4, T5, T6](implicit ord1: Ordering[T1], ord2: Ordering[T2], ord3: Ordering[T3], ord4: Ordering[T4], ord5: Ordering[T5], ord6: Ordering[T6]): Ordering[(T1, T2, T3, T4, T5, T6)] = + new Tuple6Ordering(ord1, ord2, ord3, ord4, ord5, ord6) + + private[this] final class Tuple6Ordering[T1, T2, T3, T4, T5, T6](private val ord1: Ordering[T1], + private val ord2: Ordering[T2], + private val ord3: Ordering[T3], + private val ord4: Ordering[T4], + private val ord5: Ordering[T5], + private val ord6: Ordering[T6]) + extends Ordering[(T1, T2, T3, T4, T5, T6)] { + def compare(x: (T1, T2, T3, T4, T5, T6), y: (T1, T2, T3, T4, T5, T6)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + val compare2 = ord2.compare(x._2, y._2) + if (compare2 != 0) return compare2 + val compare3 = ord3.compare(x._3, y._3) + if (compare3 != 0) return compare3 + val compare4 = ord4.compare(x._4, y._4) + if (compare4 != 0) return compare4 + val compare5 = ord5.compare(x._5, y._5) + if (compare5 != 0) return compare5 + ord6.compare(x._6, y._6) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple6Ordering[_, _, _, _, _, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 && + this.ord3 == that.ord3 && + this.ord4 == that.ord4 && + this.ord5 == that.ord5 && + this.ord6 == that.ord6 + case _ => false + } + override def hashCode(): Int = (ord1, ord2, ord3, ord4, ord5, ord6).hashCode() + } + + implicit def Tuple7[T1, T2, T3, T4, T5, T6, T7](implicit ord1: Ordering[T1], ord2: Ordering[T2], ord3: Ordering[T3], ord4: Ordering[T4], ord5: Ordering[T5], ord6: Ordering[T6], ord7: Ordering[T7]): Ordering[(T1, T2, T3, T4, T5, T6, T7)] = + new Tuple7Ordering(ord1, ord2, ord3, ord4, ord5, ord6, ord7) + + @SerialVersionUID(1253188205893682451L) + private[this] final class Tuple7Ordering[T1, T2, T3, T4, T5, T6, T7](private val ord1: Ordering[T1], + private val ord2: Ordering[T2], + private val ord3: Ordering[T3], + private val ord4: Ordering[T4], + private val ord5: Ordering[T5], + private val ord6: Ordering[T6], + private val ord7: Ordering[T7]) + extends Ordering[(T1, T2, T3, T4, T5, T6, T7)] { + def compare(x: (T1, T2, T3, T4, T5, T6, T7), y: (T1, T2, T3, T4, T5, T6, T7)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + val compare2 = ord2.compare(x._2, y._2) + if (compare2 != 0) return compare2 + val compare3 = ord3.compare(x._3, y._3) + if (compare3 != 0) return compare3 + val compare4 = ord4.compare(x._4, y._4) + if (compare4 != 0) return compare4 + val compare5 = ord5.compare(x._5, y._5) + if (compare5 != 0) return compare5 + val compare6 = ord6.compare(x._6, y._6) + if (compare6 != 0) return compare6 + ord7.compare(x._7, y._7) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple7Ordering[_, _, _, _, _, _, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 && + this.ord3 == that.ord3 && + this.ord4 == that.ord4 && + this.ord5 == that.ord5 && + this.ord6 == that.ord6 && + this.ord7 == that.ord7 + case _ => false + } + override def hashCode(): Int = (ord1, ord2, ord3, ord4, ord5, ord6, ord7).hashCode() + } + + @SerialVersionUID(4003095353309354068L) + implicit def Tuple8[T1, T2, T3, T4, T5, T6, T7, T8](implicit ord1: Ordering[T1], ord2: Ordering[T2], ord3: Ordering[T3], ord4: Ordering[T4], ord5: Ordering[T5], ord6: Ordering[T6], ord7: Ordering[T7], ord8: Ordering[T8]): Ordering[(T1, T2, T3, T4, T5, T6, T7, T8)] = + new Tuple8Ordering(ord1, ord2, ord3, ord4, ord5, ord6, ord7, ord8) + + private[this] final class Tuple8Ordering[T1, T2, T3, T4, T5, T6, T7, T8](private val ord1: Ordering[T1], + private val ord2: Ordering[T2], + private val ord3: Ordering[T3], + private val ord4: Ordering[T4], + private val ord5: Ordering[T5], + private val ord6: Ordering[T6], + private val ord7: Ordering[T7], + private val ord8: Ordering[T8]) + extends Ordering[(T1, T2, T3, T4, T5, T6, T7, T8)] { + def compare(x: (T1, T2, T3, T4, T5, T6, T7, T8), y: (T1, T2, T3, T4, T5, T6, T7, T8)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + val compare2 = ord2.compare(x._2, y._2) + if (compare2 != 0) return compare2 + val compare3 = ord3.compare(x._3, y._3) + if (compare3 != 0) return compare3 + val compare4 = ord4.compare(x._4, y._4) + if (compare4 != 0) return compare4 + val compare5 = ord5.compare(x._5, y._5) + if (compare5 != 0) return compare5 + val compare6 = ord6.compare(x._6, y._6) + if (compare6 != 0) return compare6 + val compare7 = ord7.compare(x._7, y._7) + if (compare7 != 0) return compare7 + ord8.compare(x._8, y._8) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple8Ordering[_, _, _, _, _, _, _, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 && + this.ord3 == that.ord3 && + this.ord4 == that.ord4 && + this.ord5 == that.ord5 && + this.ord6 == that.ord6 && + this.ord7 == that.ord7 && + this.ord8 == that.ord8 + case _ => false + } + override def hashCode(): Int = (ord1, ord2, ord3, ord4, ord5, ord6, ord7, ord8).hashCode() + } + + @SerialVersionUID(8185342054829975001L) + implicit def Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9](implicit ord1: Ordering[T1], ord2: Ordering[T2], ord3: Ordering[T3], ord4: Ordering[T4], ord5: Ordering[T5], ord6: Ordering[T6], ord7: Ordering[T7], ord8 : Ordering[T8], ord9: Ordering[T9]): Ordering[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] = + new Tuple9Ordering(ord1, ord2, ord3, ord4, ord5, ord6, ord7, ord8, ord9) + + private[this] final class Tuple9Ordering[T1, T2, T3, T4, T5, T6, T7, T8, T9](private val ord1: Ordering[T1], + private val ord2: Ordering[T2], + private val ord3: Ordering[T3], + private val ord4: Ordering[T4], + private val ord5: Ordering[T5], + private val ord6: Ordering[T6], + private val ord7: Ordering[T7], + private val ord8: Ordering[T8], + private val ord9: Ordering[T9]) + extends Ordering[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] { + def compare(x: (T1, T2, T3, T4, T5, T6, T7, T8, T9), y: (T1, T2, T3, T4, T5, T6, T7, T8, T9)): Int = { + val compare1 = ord1.compare(x._1, y._1) + if (compare1 != 0) return compare1 + val compare2 = ord2.compare(x._2, y._2) + if (compare2 != 0) return compare2 + val compare3 = ord3.compare(x._3, y._3) + if (compare3 != 0) return compare3 + val compare4 = ord4.compare(x._4, y._4) + if (compare4 != 0) return compare4 + val compare5 = ord5.compare(x._5, y._5) + if (compare5 != 0) return compare5 + val compare6 = ord6.compare(x._6, y._6) + if (compare6 != 0) return compare6 + val compare7 = ord7.compare(x._7, y._7) + if (compare7 != 0) return compare7 + val compare8 = ord8.compare(x._8, y._8) + if (compare8 != 0) return compare8 + ord9.compare(x._9, y._9) + } + + override def equals(obj: scala.Any): Boolean = obj match { + case that: AnyRef if this eq that => true + case that: Tuple9Ordering[_, _, _, _, _, _, _, _, _] => + this.ord1 == that.ord1 && + this.ord2 == that.ord2 && + this.ord3 == that.ord3 && + this.ord4 == that.ord4 && + this.ord5 == that.ord5 && + this.ord6 == that.ord6 && + this.ord7 == that.ord7 && + this.ord8 == that.ord8 && + this.ord9 == that.ord9 + case _ => false + } + override def hashCode(): Int = (ord1, ord2, ord3, ord4, ord5, ord6, ord7, ord8, ord9).hashCode() + } +} diff --git a/library/src/scala/math/PartialOrdering.scala b/library/src/scala/math/PartialOrdering.scala new file mode 100644 index 000000000000..e8ea9d355344 --- /dev/null +++ b/library/src/scala/math/PartialOrdering.scala @@ -0,0 +1,88 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +/** A trait for representing partial orderings. It is important to + * distinguish between a type that has a partial order and a representation + * of partial ordering on some type. This trait is for representing the + * latter. + * + * A [[https://en.wikipedia.org/wiki/Partially_ordered_set partial ordering]] is a + * binary relation on a type `T`, exposed as the `lteq` method of this trait. + * This relation must be: + * + * - reflexive: `lteq(x, x) == '''true'''`, for any `x` of type `T`. + * - anti-symmetric: if `lteq(x, y) == '''true'''` and + * `lteq(y, x) == '''true'''` + * then `equiv(x, y) == '''true'''`, for any `x` and `y` of type `T`. + * - transitive: if `lteq(x, y) == '''true'''` and + * `lteq(y, z) == '''true'''` then `lteq(x, z) == '''true'''`, + * for any `x`, `y`, and `z` of type `T`. + * + * Additionally, a partial ordering induces an + * [[https://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]] + * on a type `T`: `x` and `y` of type `T` are equivalent if and only if + * `lteq(x, y) && lteq(y, x) == '''true'''`. This equivalence relation is + * exposed as the `equiv` method, inherited from the + * [[scala.math.Equiv Equiv]] trait. + */ + +trait PartialOrdering[T] extends Equiv[T] { + outer => + + /** Result of comparing `x` with operand `y`. + * Returns `None` if operands are not comparable. + * If operands are comparable, returns `Some(r)` where + * - `r < 0` iff `x < y` + * - `r == 0` iff `x == y` + * - `r > 0` iff `x > y` + */ + def tryCompare(x: T, y: T): Option[Int] + + /** Returns `'''true'''` iff `x` comes before `y` in the ordering. + */ + def lteq(x: T, y: T): Boolean + + /** Returns `'''true'''` iff `y` comes before `x` in the ordering. + */ + def gteq(x: T, y: T): Boolean = lteq(y, x) + + /** Returns `'''true'''` iff `x` comes before `y` in the ordering + * and is not the same as `y`. + */ + def lt(x: T, y: T): Boolean = lteq(x, y) && !equiv(x, y) + + /** Returns `'''true'''` iff `y` comes before `x` in the ordering + * and is not the same as `x`. + */ + def gt(x: T, y: T): Boolean = gteq(x, y) && !equiv(x, y) + + /** Returns `'''true'''` iff `x` is equivalent to `y` in the ordering. + */ + def equiv(x: T, y: T): Boolean = lteq(x,y) && lteq(y,x) + + def reverse : PartialOrdering[T] = new PartialOrdering[T] { + override def reverse = outer + def tryCompare(x: T, y: T) = outer.tryCompare(y, x) + def lteq(x: T, y: T) = outer.lteq(y, x) + override def gteq(x: T, y: T) = outer.gteq(y, x) + override def lt(x: T, y: T) = outer.lt(y, x) + override def gt(x: T, y: T) = outer.gt(y, x) + override def equiv(x: T, y: T) = outer.equiv(y, x) + } +} + +object PartialOrdering { + @inline def apply[T](implicit ev: PartialOrdering[T]): PartialOrdering[T] = ev +} diff --git a/library/src/scala/math/PartiallyOrdered.scala b/library/src/scala/math/PartiallyOrdered.scala new file mode 100644 index 000000000000..b955879ae0f1 --- /dev/null +++ b/library/src/scala/math/PartiallyOrdered.scala @@ -0,0 +1,54 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +/** A class for partially ordered data. + */ +trait PartiallyOrdered[+A] extends Any { + + type AsPartiallyOrdered[B] = B => PartiallyOrdered[B] + + /** Result of comparing `'''this'''` with operand `that`. + * Returns `None` if operands are not comparable. + * If operands are comparable, returns `Some(x)` where + * - `x < 0` iff `'''this''' < that` + * - `x == 0` iff `'''this''' == that` + * - `x > 0` iff `'''this''' > that` + */ + def tryCompareTo [B >: A: AsPartiallyOrdered](that: B): Option[Int] + + def < [B >: A: AsPartiallyOrdered](that: B): Boolean = + (this tryCompareTo that) match { + case Some(x) if x < 0 => true + case _ => false + } + + def > [B >: A: AsPartiallyOrdered](that: B): Boolean = + (this tryCompareTo that) match { + case Some(x) if x > 0 => true + case _ => false + } + + def <= [B >: A: AsPartiallyOrdered](that: B): Boolean = + (this tryCompareTo that) match { + case Some(x) if x <= 0 => true + case _ => false + } + + def >= [B >: A: AsPartiallyOrdered](that: B): Boolean = + (this tryCompareTo that) match { + case Some(x) if x >= 0 => true + case _ => false + } +} diff --git a/library/src/scala/math/ScalaNumber.java b/library/src/scala/math/ScalaNumber.java new file mode 100644 index 000000000000..5ed76ec3fb22 --- /dev/null +++ b/library/src/scala/math/ScalaNumber.java @@ -0,0 +1,20 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.math; + +/** A marker class for Number types introduced by Scala + */ +public abstract class ScalaNumber extends java.lang.Number { + protected abstract boolean isWhole(); + public abstract Object underlying(); +} diff --git a/library/src/scala/math/ScalaNumericConversions.scala b/library/src/scala/math/ScalaNumericConversions.scala new file mode 100644 index 000000000000..a3fa90c98c9d --- /dev/null +++ b/library/src/scala/math/ScalaNumericConversions.scala @@ -0,0 +1,122 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package math + +/** A slightly more specific conversion trait for classes which + * extend ScalaNumber (which excludes value classes.) + */ +trait ScalaNumericConversions extends ScalaNumber with ScalaNumericAnyConversions { + def underlying: Object +} + +/** Conversions which present a consistent conversion interface + * across all the numeric types, suitable for use in value classes. + */ +trait ScalaNumericAnyConversions extends Any { + /** @return `'''true'''` if this number has no decimal component, `'''false'''` otherwise. */ + def isWhole: Boolean + + def byteValue: Byte + def shortValue: Short + def intValue: Int + def longValue: Long + def floatValue: Float + def doubleValue: Double + + /** Returns the value of this as a [[scala.Char]]. This may involve + * rounding or truncation. + */ + def toChar = intValue.toChar + + /** Returns the value of this as a [[scala.Byte]]. This may involve + * rounding or truncation. + */ + def toByte = byteValue + + /** Returns the value of this as a [[scala.Short]]. This may involve + * rounding or truncation. + */ + def toShort = shortValue + + /** Returns the value of this as an [[scala.Int]]. This may involve + * rounding or truncation. + */ + def toInt = intValue + + /** Returns the value of this as a [[scala.Long]]. This may involve + * rounding or truncation. + */ + def toLong = longValue + + /** Returns the value of this as a [[scala.Float]]. This may involve + * rounding or truncation. + */ + def toFloat = floatValue + + /** Returns the value of this as a [[scala.Double]]. This may involve + * rounding or truncation. + */ + def toDouble = doubleValue + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Byte]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidByte = isWhole && (toInt == toByte) + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Short]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidShort = isWhole && (toInt == toShort) + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Int]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidInt = isWhole && (toLong == toInt) + + /** Returns `true` iff this has a zero fractional part, and is within the + * range of [[scala.Char]] MinValue and MaxValue; otherwise returns `false`. + */ + def isValidChar = isWhole && (toInt >= Char.MinValue && toInt <= Char.MaxValue) + + protected def unifiedPrimitiveHashcode = { + val lv = toLong + if (lv >= Int.MinValue && lv <= Int.MaxValue) lv.toInt + else lv.## + } + + /** Should only be called after all known non-primitive + * types have been excluded. This method won't dispatch + * anywhere else after checking against the primitives + * to avoid infinite recursion between equals and this on + * unknown "Number" variants. + * + * Additionally, this should only be called if the numeric + * type is happy to be converted to Long, Float, and Double. + * If for instance a BigInt much larger than the Long range is + * sent here, it will claim equality with whatever Long is left + * in its lower 64 bits. Or a BigDecimal with more precision + * than Double can hold: same thing. There's no way given the + * interface available here to prevent this error. + */ + protected def unifiedPrimitiveEquals(x: Any) = x match { + case x: Char => isValidChar && (toInt == x.toInt) + case x: Byte => isValidByte && (toByte == x) + case x: Short => isValidShort && (toShort == x) + case x: Int => isValidInt && (toInt == x) + case x: Long => toLong == x + case x: Float => toFloat == x + case x: Double => toDouble == x + case _ => false + } +} diff --git a/library/src/scala/math/package.scala b/library/src/scala/math/package.scala new file mode 100644 index 000000000000..dbfde894aa0c --- /dev/null +++ b/library/src/scala/math/package.scala @@ -0,0 +1,438 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** The package object `scala.math` contains methods for performing basic + * numeric operations such as elementary exponential, logarithmic, root and + * trigonometric functions. + * + * All methods forward to [[java.lang.Math]] unless otherwise noted. + * + * @see [[java.lang.Math]] + * + * @groupname math-const Mathematical Constants + * @groupprio math-const 10 + * + * @groupname minmax Minimum and Maximum + * @groupdesc minmax Find the min or max of two numbers. Note: [[scala.collection.IterableOnceOps]] has + * min and max methods which determine the min or max of a collection. + * @groupprio minmax 20 + * + * @groupname rounding Rounding + * @groupprio rounding 30 + * + * @groupname scaling Scaling + * @groupdesc scaling Scaling with rounding guarantees + * @groupprio scaling 40 + * + * @groupname explog Exponential and Logarithmic + * @groupprio explog 50 + * + * @groupname trig Trigonometric + * @groupdesc trig Arguments in radians + * @groupprio trig 60 + * + * @groupname angle-conversion Angular Measurement Conversion + * @groupprio angle-conversion 70 + * + * @groupname hyperbolic Hyperbolic + * @groupprio hyperbolic 80 + * + * @groupname abs Absolute Values + * @groupdesc abs Determine the magnitude of a value by discarding the sign. Results are >= 0. + * @groupprio abs 90 + * + * @groupname signs Signs + * @groupdesc signs For `signum` extract the sign of a value. Results are -1, 0 or 1. + * Note the `signum` methods are not pure forwarders to the Java versions. + * In particular, the return type of `java.lang.Long.signum` is `Int`, + * but here it is widened to `Long` so that each overloaded variant + * will return the same numeric type it is passed. + * @groupprio signs 100 + * + * @groupname root-extraction Root Extraction + * @groupprio root-extraction 110 + * + * @groupname polar-coords Polar Coordinates + * @groupprio polar-coords 120 + * + * @groupname ulp Unit of Least Precision + * @groupprio ulp 130 + * + * @groupname randomisation Pseudo Random Number Generation + * @groupprio randomisation 140 + * + * @groupname exact Exact Arithmetic + * @groupdesc exact Integral addition, multiplication, stepping and conversion throwing ArithmeticException instead of underflowing or overflowing + * @groupprio exact 150 + * + * @groupname modquo Modulus and Quotient + * @groupdesc modquo Calculate quotient values by rounding to negative infinity + * @groupprio modquo 160 + * + * @groupname adjacent-float Adjacent Floats + * @groupprio adjacent-float 170 + */ +package object math { + /** The `Double` value that is closer than any other to `e`, the base of + * the natural logarithms. + * @group math-const + */ + @inline final val E = java.lang.Math.E + + /** The `Double` value that is closer than any other to `pi`, the ratio of + * the circumference of a circle to its diameter. + * @group math-const + */ + @inline final val Pi = java.lang.Math.PI + + /** Returns a `Double` value with a positive sign, greater than or equal + * to `0.0` and less than `1.0`. + * + * @group randomisation + */ + def random(): Double = java.lang.Math.random() + + /** @group trig */ + def sin(x: Double): Double = java.lang.Math.sin(x) + /** @group trig */ + def cos(x: Double): Double = java.lang.Math.cos(x) + /** @group trig */ + def tan(x: Double): Double = java.lang.Math.tan(x) + /** @group trig */ + def asin(x: Double): Double = java.lang.Math.asin(x) + /** @group trig */ + def acos(x: Double): Double = java.lang.Math.acos(x) + /** @group trig */ + def atan(x: Double): Double = java.lang.Math.atan(x) + + /** Converts an angle measured in degrees to an approximately equivalent + * angle measured in radians. + * + * @param x an angle, in degrees + * @return the measurement of the angle `x` in radians. + * @group angle-conversion + */ + def toRadians(x: Double): Double = java.lang.Math.toRadians(x) + + /** Converts an angle measured in radians to an approximately equivalent + * angle measured in degrees. + * + * @param x angle, in radians + * @return the measurement of the angle `x` in degrees. + * @group angle-conversion + */ + def toDegrees(x: Double): Double = java.lang.Math.toDegrees(x) + + /** Converts rectangular coordinates `(x, y)` to polar `(r, theta)`. + * + * @param x the ordinate coordinate + * @param y the abscissa coordinate + * @return the ''theta'' component of the point `(r, theta)` in polar + * coordinates that corresponds to the point `(x, y)` in + * Cartesian coordinates. + * @group polar-coords + */ + def atan2(y: Double, x: Double): Double = java.lang.Math.atan2(y, x) + + /** Returns the square root of the sum of the squares of both given `Double` + * values without intermediate underflow or overflow. + * + * The ''r'' component of the point `(r, theta)` in polar + * coordinates that corresponds to the point `(x, y)` in + * Cartesian coordinates. + * @group polar-coords + */ + def hypot(x: Double, y: Double): Double = java.lang.Math.hypot(x, y) + + // ----------------------------------------------------------------------- + // rounding functions + // ----------------------------------------------------------------------- + + /** @group rounding */ + def ceil(x: Double): Double = java.lang.Math.ceil(x) + /** @group rounding */ + def floor(x: Double): Double = java.lang.Math.floor(x) + + /** Returns the `Double` value that is closest in value to the + * argument and is equal to a mathematical integer. + * + * @param x a `Double` value + * @return the closest floating-point value to a that is equal to a + * mathematical integer. + * @group rounding + */ + def rint(x: Double): Double = java.lang.Math.rint(x) + + /** There is no reason to round a `Long`, but this method prevents unintended conversion to `Float` followed by rounding to `Int`. + * + * @note Does not forward to [[java.lang.Math]] + * @group rounding + */ + @deprecated("This is an integer type; there is no reason to round it. Perhaps you meant to call this with a floating-point value?", "2.11.0") + def round(x: Long): Long = x + + /** Returns the closest `Int` to the argument. + * + * @param x a floating-point value to be rounded to a `Int`. + * @return the value of the argument rounded to the nearest `Int` value. + * @group rounding + */ + def round(x: Float): Int = java.lang.Math.round(x) + + /** Returns the closest `Long` to the argument. + * + * @param x a floating-point value to be rounded to a `Long`. + * @return the value of the argument rounded to the nearest`long` value. + * @group rounding + */ + def round(x: Double): Long = java.lang.Math.round(x) + + /** @group abs */ + def abs(x: Int): Int = java.lang.Math.abs(x) + /** @group abs */ + def abs(x: Long): Long = java.lang.Math.abs(x) + /** @group abs */ + def abs(x: Float): Float = java.lang.Math.abs(x) + /** @group abs */ + def abs(x: Double): Double = java.lang.Math.abs(x) + + /** @group minmax */ + def max(x: Int, y: Int): Int = java.lang.Math.max(x, y) + /** @group minmax */ + def max(x: Long, y: Long): Long = java.lang.Math.max(x, y) + /** @group minmax */ + def max(x: Float, y: Float): Float = java.lang.Math.max(x, y) + /** @group minmax */ + def max(x: Double, y: Double): Double = java.lang.Math.max(x, y) + + /** @group minmax */ + def min(x: Int, y: Int): Int = java.lang.Math.min(x, y) + /** @group minmax */ + def min(x: Long, y: Long): Long = java.lang.Math.min(x, y) + /** @group minmax */ + def min(x: Float, y: Float): Float = java.lang.Math.min(x, y) + /** @group minmax */ + def min(x: Double, y: Double): Double = java.lang.Math.min(x, y) + + /** @group signs + * @note Forwards to [[java.lang.Integer]] + */ + def signum(x: Int): Int = java.lang.Integer.signum(x) + /** @group signs + * @note Forwards to [[java.lang.Long]] + */ + def signum(x: Long): Long = java.lang.Long.signum(x) + /** @group signs */ + def signum(x: Float): Float = java.lang.Math.signum(x) + /** @group signs */ + def signum(x: Double): Double = java.lang.Math.signum(x) + + /** @group modquo */ + def floorDiv(x: Int, y: Int): Int = java.lang.Math.floorDiv(x, y) + + /** @group modquo */ + def floorDiv(x: Long, y: Long): Long = java.lang.Math.floorDiv(x, y) + + /** @group modquo */ + def floorMod(x: Int, y: Int): Int = java.lang.Math.floorMod(x, y) + + /** @group modquo */ + def floorMod(x: Long, y: Long): Long = java.lang.Math.floorMod(x, y) + + /** @group signs */ + def copySign(magnitude: Double, sign: Double): Double = java.lang.Math.copySign(magnitude, sign) + + /** @group signs */ + def copySign(magnitude: Float, sign: Float): Float = java.lang.Math.copySign(magnitude, sign) + + /** @group adjacent-float */ + def nextAfter(start: Double, direction: Double): Double = java.lang.Math.nextAfter(start, direction) + + /** @group adjacent-float */ + def nextAfter(start: Float, direction: Double): Float = java.lang.Math.nextAfter(start, direction) + + /** @group adjacent-float */ + def nextUp(d: Double): Double = java.lang.Math.nextUp(d) + + /** @group adjacent-float */ + def nextUp(f: Float): Float = java.lang.Math.nextUp(f) + + /** @group adjacent-float */ + def nextDown(d: Double): Double = java.lang.Math.nextDown(d) + + /** @group adjacent-float */ + def nextDown(f: Float): Float = java.lang.Math.nextDown(f) + + /** @group scaling */ + def scalb(d: Double, scaleFactor: Int): Double = java.lang.Math.scalb(d, scaleFactor) + + /** @group scaling */ + def scalb(f: Float, scaleFactor: Int): Float = java.lang.Math.scalb(f, scaleFactor) + + // ----------------------------------------------------------------------- + // root functions + // ----------------------------------------------------------------------- + + /** Returns the square root of a `Double` value. + * + * @param x the number to take the square root of + * @return the value √x + * @group root-extraction + */ + def sqrt(x: Double): Double = java.lang.Math.sqrt(x) + + /** Returns the cube root of the given `Double` value. + * + * @param x the number to take the cube root of + * @return the value ∛x + * @group root-extraction + */ + def cbrt(x: Double): Double = java.lang.Math.cbrt(x) + + // ----------------------------------------------------------------------- + // exponential functions + // ----------------------------------------------------------------------- + + /** Returns the value of the first argument raised to the power of the + * second argument. + * + * @param x the base. + * @param y the exponent. + * @return the value `x^y^`. + * @group explog + */ + def pow(x: Double, y: Double): Double = java.lang.Math.pow(x, y) + + /** Returns Euler's number `e` raised to the power of a `Double` value. + * + * @param x the exponent to raise `e` to. + * @return the value `e^a^`, where `e` is the base of the natural + * logarithms. + * @group explog + */ + def exp(x: Double): Double = java.lang.Math.exp(x) + + /** Returns `exp(x) - 1`. + * @group explog + */ + def expm1(x: Double): Double = java.lang.Math.expm1(x) + + /** @group explog */ + def getExponent(f: Float): Int = java.lang.Math.getExponent(f) + + /** @group explog */ + def getExponent(d: Double): Int = java.lang.Math.getExponent(d) + + // ----------------------------------------------------------------------- + // logarithmic functions + // ----------------------------------------------------------------------- + + /** Returns the natural logarithm of a `Double` value. + * + * @param x the number to take the natural logarithm of + * @return the value `logₑ(x)` where `e` is Eulers number + * @group explog + */ + def log(x: Double): Double = java.lang.Math.log(x) + + /** Returns the natural logarithm of the sum of the given `Double` value and 1. + * @group explog + */ + def log1p(x: Double): Double = java.lang.Math.log1p(x) + + /** Returns the base 10 logarithm of the given `Double` value. + * @group explog + */ + def log10(x: Double): Double = java.lang.Math.log10(x) + + // ----------------------------------------------------------------------- + // trigonometric functions + // ----------------------------------------------------------------------- + + /** Returns the hyperbolic sine of the given `Double` value. + * @group hyperbolic + */ + def sinh(x: Double): Double = java.lang.Math.sinh(x) + + /** Returns the hyperbolic cosine of the given `Double` value. + * @group hyperbolic + */ + def cosh(x: Double): Double = java.lang.Math.cosh(x) + + /** Returns the hyperbolic tangent of the given `Double` value. + * @group hyperbolic + */ + def tanh(x: Double):Double = java.lang.Math.tanh(x) + + // ----------------------------------------------------------------------- + // miscellaneous functions + // ----------------------------------------------------------------------- + + /** Returns the size of an ulp of the given `Double` value. + * @group ulp + */ + def ulp(x: Double): Double = java.lang.Math.ulp(x) + + /** Returns the size of an ulp of the given `Float` value. + * @group ulp + */ + def ulp(x: Float): Float = java.lang.Math.ulp(x) + + /** @group exact */ + def IEEEremainder(x: Double, y: Double): Double = java.lang.Math.IEEEremainder(x, y) + + // ----------------------------------------------------------------------- + // exact functions + // ----------------------------------------------------------------------- + + /** @group exact */ + def addExact(x: Int, y: Int): Int = java.lang.Math.addExact(x, y) + + /** @group exact */ + def addExact(x: Long, y: Long): Long = java.lang.Math.addExact(x, y) + + /** @group exact */ + def subtractExact(x: Int, y: Int): Int = java.lang.Math.subtractExact(x, y) + + /** @group exact */ + def subtractExact(x: Long, y: Long): Long = java.lang.Math.subtractExact(x, y) + + /** @group exact */ + def multiplyExact(x: Int, y: Int): Int = java.lang.Math.multiplyExact(x, y) + + /** @group exact */ + def multiplyExact(x: Long, y: Long): Long = java.lang.Math.multiplyExact(x, y) + + /** @group exact */ + def incrementExact(x: Int): Int = java.lang.Math.incrementExact(x) + + /** @group exact */ + def incrementExact(x: Long) = java.lang.Math.incrementExact(x) + + /** @group exact */ + def decrementExact(x: Int) = java.lang.Math.decrementExact(x) + + /** @group exact */ + def decrementExact(x: Long) = java.lang.Math.decrementExact(x) + + /** @group exact */ + def negateExact(x: Int) = java.lang.Math.negateExact(x) + + /** @group exact */ + def negateExact(x: Long) = java.lang.Math.negateExact(x) + + /** @group exact */ + def toIntExact(x: Long): Int = java.lang.Math.toIntExact(x) + +} From bfbe6b976afef8b724d8b97acab2615ba7eefd0b Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:36:31 +0100 Subject: [PATCH 13/28] chore: add all the files in scala.ref --- library/src/scala/ref/PhantomReference.scala | 21 +++++++++++ library/src/scala/ref/Reference.scala | 27 ++++++++++++++ library/src/scala/ref/ReferenceQueue.scala | 30 ++++++++++++++++ library/src/scala/ref/ReferenceWrapper.scala | 34 ++++++++++++++++++ library/src/scala/ref/SoftReference.scala | 35 ++++++++++++++++++ library/src/scala/ref/WeakReference.scala | 37 ++++++++++++++++++++ 6 files changed, 184 insertions(+) create mode 100644 library/src/scala/ref/PhantomReference.scala create mode 100644 library/src/scala/ref/Reference.scala create mode 100644 library/src/scala/ref/ReferenceQueue.scala create mode 100644 library/src/scala/ref/ReferenceWrapper.scala create mode 100644 library/src/scala/ref/SoftReference.scala create mode 100644 library/src/scala/ref/WeakReference.scala diff --git a/library/src/scala/ref/PhantomReference.scala b/library/src/scala/ref/PhantomReference.scala new file mode 100644 index 000000000000..0790f539d03d --- /dev/null +++ b/library/src/scala/ref/PhantomReference.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.ref + +class PhantomReference[+T <: AnyRef](value: T, queue: ReferenceQueue[T]) extends ReferenceWrapper[T] { + val underlying: java.lang.ref.PhantomReference[_ <: T] = + new PhantomReferenceWithWrapper[T](value, queue, this) +} + +private class PhantomReferenceWithWrapper[T <: AnyRef](value: T, queue: ReferenceQueue[T], val wrapper: PhantomReference[T]) + extends java.lang.ref.PhantomReference[T](value, queue.underlying.asInstanceOf[java.lang.ref.ReferenceQueue[T]]) with ReferenceWithWrapper[T] diff --git a/library/src/scala/ref/Reference.scala b/library/src/scala/ref/Reference.scala new file mode 100644 index 000000000000..02e673fa4184 --- /dev/null +++ b/library/src/scala/ref/Reference.scala @@ -0,0 +1,27 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.ref + +/** + * @see `java.lang.ref.Reference` + */ +trait Reference[+T <: AnyRef] extends Function0[T] { + /** return the underlying value */ + def apply(): T + /** return `Some` underlying if it hasn't been collected, otherwise `None` */ + def get: Option[T] + override def toString: String = get.map(_.toString).getOrElse("") + def clear(): Unit + def enqueue(): Boolean + def isEnqueued: Boolean +} diff --git a/library/src/scala/ref/ReferenceQueue.scala b/library/src/scala/ref/ReferenceQueue.scala new file mode 100644 index 000000000000..70743708c732 --- /dev/null +++ b/library/src/scala/ref/ReferenceQueue.scala @@ -0,0 +1,30 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.ref + +class ReferenceQueue[+T <: AnyRef] { + + private[ref] val underlying: java.lang.ref.ReferenceQueue[_ <: T] = new java.lang.ref.ReferenceQueue[T] + override def toString: String = underlying.toString + + protected def Wrapper(jref: java.lang.ref.Reference[_]): Option[Reference[T]] = + jref match { + case null => None + case ref => Some(ref.asInstanceOf[ReferenceWithWrapper[T]].wrapper) + } + + def poll: Option[Reference[T]] = Wrapper(underlying.poll) + def remove: Option[Reference[T]] = Wrapper(underlying.remove) + def remove(timeout: Long): Option[Reference[T]] = Wrapper(underlying.remove(timeout)) + +} diff --git a/library/src/scala/ref/ReferenceWrapper.scala b/library/src/scala/ref/ReferenceWrapper.scala new file mode 100644 index 000000000000..4e681ed18570 --- /dev/null +++ b/library/src/scala/ref/ReferenceWrapper.scala @@ -0,0 +1,34 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.ref + +import scala.annotation.nowarn + +@nowarn("cat=deprecation") +trait ReferenceWrapper[+T <: AnyRef] extends Reference[T] with Proxy { + val underlying: java.lang.ref.Reference[_ <: T] + override def get = Option(underlying.get) + def apply() = { + val ret = underlying.get + if (ret eq null) throw new NoSuchElementException + ret + } + def clear(): Unit = underlying.clear() + def enqueue(): Boolean = underlying.enqueue() + def isEnqueued: Boolean = underlying.isEnqueued + def self: java.lang.ref.Reference[_ <: T] = underlying +} + +private trait ReferenceWithWrapper[T <: AnyRef] { + val wrapper: ReferenceWrapper[T] +} diff --git a/library/src/scala/ref/SoftReference.scala b/library/src/scala/ref/SoftReference.scala new file mode 100644 index 000000000000..859eef5e7fef --- /dev/null +++ b/library/src/scala/ref/SoftReference.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.ref + +class SoftReference[+T <: AnyRef](value : T, queue : ReferenceQueue[T]) extends ReferenceWrapper[T] { + def this(value : T) = this(value, null) + + val underlying: java.lang.ref.SoftReference[_ <: T] = + new SoftReferenceWithWrapper[T](value, queue, this) +} + +/** + * A companion object that implements an extractor for `SoftReference` values + */ +object SoftReference { + + /** Creates a `SoftReference` pointing to `value` */ + def apply[T <: AnyRef](value: T): SoftReference[T] = new SoftReference(value) + + /** Optionally returns the referenced value, or `None` if that value no longer exists */ + def unapply[T <: AnyRef](sr: SoftReference[T]): Option[T] = Option(sr.underlying.get) +} + +private class SoftReferenceWithWrapper[T <: AnyRef](value: T, queue: ReferenceQueue[T], val wrapper: SoftReference[T]) + extends java.lang.ref.SoftReference[T](value, if (queue == null) null else queue.underlying.asInstanceOf[java.lang.ref.ReferenceQueue[T]]) with ReferenceWithWrapper[T] diff --git a/library/src/scala/ref/WeakReference.scala b/library/src/scala/ref/WeakReference.scala new file mode 100644 index 000000000000..5ca06063590b --- /dev/null +++ b/library/src/scala/ref/WeakReference.scala @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.ref + +/** + * A wrapper class for java.lang.ref.WeakReference + * The new functionality is (1) results are Option values, instead of using null. + * (2) There is an extractor that maps the weak reference itself into an option. + */ +class WeakReference[+T <: AnyRef](value: T, queue: ReferenceQueue[T]) extends ReferenceWrapper[T] { + def this(value: T) = this(value, null) + val underlying: java.lang.ref.WeakReference[_ <: T] = + new WeakReferenceWithWrapper[T](value, queue, this) +} + +/** An extractor for weak reference values */ +object WeakReference { + + /** Creates a weak reference pointing to `value` */ + def apply[T <: AnyRef](value: T): WeakReference[T] = new WeakReference(value) + + /** Optionally returns the referenced value, or `None` if that value no longer exists */ + def unapply[T <: AnyRef](wr: WeakReference[T]): Option[T] = Option(wr.underlying.get) +} + +private class WeakReferenceWithWrapper[T <: AnyRef](value: T, queue: ReferenceQueue[T], val wrapper: WeakReference[T]) + extends java.lang.ref.WeakReference[T](value, if (queue == null) null else queue.underlying.asInstanceOf[java.lang.ref.ReferenceQueue[T]]) with ReferenceWithWrapper[T] From a00daaf318acec01007649f4852ac31f0a312152 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:37:09 +0100 Subject: [PATCH 14/28] chore: add all the files in scala.reflect --- .../reflect/ClassManifestDeprecatedApis.scala | 248 ++++++++++ library/src/scala/reflect/ClassTag.scala | 168 +++++++ library/src/scala/reflect/Manifest.scala | 461 ++++++++++++++++++ .../src/scala/reflect/NameTransformer.scala | 167 +++++++ library/src/scala/reflect/NoManifest.scala | 22 + library/src/scala/reflect/OptManifest.scala | 22 + .../src/scala/reflect/ScalaLongSignature.java | 24 + library/src/scala/reflect/ScalaSignature.java | 24 + library/src/scala/reflect/package.scala | 76 +++ 9 files changed, 1212 insertions(+) create mode 100644 library/src/scala/reflect/ClassManifestDeprecatedApis.scala create mode 100644 library/src/scala/reflect/ClassTag.scala create mode 100644 library/src/scala/reflect/Manifest.scala create mode 100644 library/src/scala/reflect/NameTransformer.scala create mode 100644 library/src/scala/reflect/NoManifest.scala create mode 100644 library/src/scala/reflect/OptManifest.scala create mode 100644 library/src/scala/reflect/ScalaLongSignature.java create mode 100644 library/src/scala/reflect/ScalaSignature.java create mode 100644 library/src/scala/reflect/package.scala diff --git a/library/src/scala/reflect/ClassManifestDeprecatedApis.scala b/library/src/scala/reflect/ClassManifestDeprecatedApis.scala new file mode 100644 index 000000000000..cc8d0a457c2a --- /dev/null +++ b/library/src/scala/reflect/ClassManifestDeprecatedApis.scala @@ -0,0 +1,248 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +import scala.collection.mutable.{ArrayBuilder, ArraySeq} +import java.lang.{Class => jClass} + +import scala.annotation.{nowarn, tailrec} + +@deprecated("use scala.reflect.ClassTag instead", "2.10.0") +trait ClassManifestDeprecatedApis[T] extends OptManifest[T] { + self: ClassManifest[T] => + + // Still in use in target test.junit.comp. + @deprecated("use runtimeClass instead", "2.10.0") + def erasure: jClass[_] = runtimeClass + + private def subtype(sub: jClass[_], sup: jClass[_]): Boolean = { + @tailrec + def loop(left: Set[jClass[_]], seen: Set[jClass[_]]): Boolean = { + left.nonEmpty && { + val next = left.head + val supers = next.getInterfaces.toSet ++ Option(next.getSuperclass) + supers(sup) || { + val xs = left ++ supers filterNot seen + loop(xs - next, seen + next) + } + } + } + loop(Set(sub), Set()) + } + + private def subargs(args1: List[OptManifest[_]], args2: List[OptManifest[_]]) = (args1 corresponds args2) { + // !!! [Martin] this is wrong, need to take variance into account + case (x: ClassManifest[_], y: ClassManifest[_]) => x <:< y + case (x, y) => (x eq NoManifest) && (y eq NoManifest) + } + + /** Tests whether the type represented by this manifest is a subtype + * of the type represented by `that` manifest, subject to the limitations + * described in the header. + */ + @deprecated("use scala.reflect.runtime.universe.TypeTag for subtype checking instead", "2.10.0") + def <:<(that: ClassManifest[_]): Boolean = { + // All types which could conform to these types will override <:<. + def cannotMatch = { + import Manifest._ + that.isInstanceOf[AnyValManifest[_]] || (that eq AnyVal) || (that eq Nothing) || (that eq Null) + } + + // This is wrong, and I don't know how it can be made right + // without more development of Manifests, due to arity-defying + // relationships like: + // + // List[String] <: AnyRef + // Map[Int, Int] <: Iterable[(Int, Int)] + // + // Given the manifest for Map[K, V] how do I determine that a + // supertype has single type argument (K, V) ? I don't see how we + // can say whether X <:< Y when type arguments are involved except + // when the erasure is the same, even before considering variance. + !cannotMatch && { + // this part is wrong for not considering variance + if (this.runtimeClass == that.runtimeClass) + subargs(this.typeArguments, that.typeArguments) + // this part is wrong for punting unless the rhs has no type + // arguments, but it's better than a blindfolded pinata swing. + else + that.typeArguments.isEmpty && subtype(this.runtimeClass, that.runtimeClass) + } + } + + /** Tests whether the type represented by this manifest is a supertype + * of the type represented by `that` manifest, subject to the limitations + * described in the header. + */ + @deprecated("use scala.reflect.runtime.universe.TypeTag for subtype checking instead", "2.10.0") + def >:>(that: ClassManifest[_]): Boolean = + that <:< this + + override def canEqual(other: Any) = other match { + case _: ClassManifest[_] => true + case _ => false + } + + protected def arrayClass[A](tp: jClass[_]): jClass[Array[A]] = + java.lang.reflect.Array.newInstance(tp, 0).getClass.asInstanceOf[jClass[Array[A]]] + + @deprecated("use wrap instead", "2.10.0") + def arrayManifest: ClassManifest[Array[T]] = + ClassManifest.classType[Array[T]](arrayClass[T](runtimeClass), this) + + @deprecated("use wrap.newArray instead", "2.10.0") + def newArray2(len: Int): Array[Array[T]] = + java.lang.reflect.Array.newInstance(arrayClass[T](runtimeClass), len) + .asInstanceOf[Array[Array[T]]] + + @deprecated("use wrap.wrap.newArray instead", "2.10.0") + def newArray3(len: Int): Array[Array[Array[T]]] = + java.lang.reflect.Array.newInstance(arrayClass[Array[T]](arrayClass[T](runtimeClass)), len) + .asInstanceOf[Array[Array[Array[T]]]] + + @deprecated("use wrap.wrap.wrap.newArray instead", "2.10.0") + def newArray4(len: Int): Array[Array[Array[Array[T]]]] = + java.lang.reflect.Array.newInstance(arrayClass[Array[Array[T]]](arrayClass[Array[T]](arrayClass[T](runtimeClass))), len) + .asInstanceOf[Array[Array[Array[Array[T]]]]] + + @deprecated("use wrap.wrap.wrap.wrap.newArray instead", "2.10.0") + def newArray5(len: Int): Array[Array[Array[Array[Array[T]]]]] = + java.lang.reflect.Array.newInstance(arrayClass[Array[Array[Array[T]]]](arrayClass[Array[Array[T]]](arrayClass[Array[T]](arrayClass[T](runtimeClass)))), len) + .asInstanceOf[Array[Array[Array[Array[Array[T]]]]]] + + @deprecated("create WrappedArray directly instead", "2.10.0") + def newWrappedArray(len: Int): ArraySeq[T] = + // it's safe to assume T <: AnyRef here because the method is overridden for all value type manifests + new ArraySeq.ofRef[T with AnyRef](newArray(len).asInstanceOf[Array[T with AnyRef]]).asInstanceOf[ArraySeq[T]] + + @deprecated("use ArrayBuilder.make(this) instead", "2.10.0") + def newArrayBuilder(): ArrayBuilder[T] = + // it's safe to assume T <: AnyRef here because the method is overridden for all value type manifests + new ArrayBuilder.ofRef[T with AnyRef]()(this.asInstanceOf[ClassManifest[T with AnyRef]]).asInstanceOf[ArrayBuilder[T]] + + @deprecated("use scala.reflect.runtime.universe.TypeTag to capture type structure instead", "2.10.0") + def typeArguments: List[OptManifest[_]] = List() + + protected def argString = + if (typeArguments.nonEmpty) typeArguments.mkString("[", ", ", "]") + else if (runtimeClass.isArray) "["+ClassManifest.fromClass(runtimeClass.getComponentType)+"]" + else "" +} + +/** `ClassManifestFactory` defines factory methods for manifests. + * It is intended for use by the compiler and should not be used in client code. + * + * Unlike `ClassManifest`, this factory isn't annotated with a deprecation warning. + * This is done to prevent avalanches of deprecation warnings in the code that calls methods with manifests. + * + * In a perfect world, we would just remove the @deprecated annotation from `ClassManifest` the object + * and then delete it in 2.11. After all, that object is explicitly marked as internal, so no one should use it. + * However a lot of existing libraries disregarded the Scaladoc that comes with `ClassManifest`, + * so we need to somehow nudge them into migrating prior to removing stuff out of the blue. + * Hence we've introduced this design decision as the lesser of two evils. + */ +@nowarn("""cat=deprecation&origin=scala\.reflect\.ClassManifest.*""") +object ClassManifestFactory { + val Byte = ManifestFactory.Byte + val Short = ManifestFactory.Short + val Char = ManifestFactory.Char + val Int = ManifestFactory.Int + val Long = ManifestFactory.Long + val Float = ManifestFactory.Float + val Double = ManifestFactory.Double + val Boolean = ManifestFactory.Boolean + val Unit = ManifestFactory.Unit + val Any = ManifestFactory.Any + val Object = ManifestFactory.Object + val AnyVal = ManifestFactory.AnyVal + val Nothing = ManifestFactory.Nothing + val Null = ManifestFactory.Null + + def fromClass[T](clazz: jClass[T]): ClassManifest[T] = clazz match { + case java.lang.Byte.TYPE => Byte.asInstanceOf[ClassManifest[T]] + case java.lang.Short.TYPE => Short.asInstanceOf[ClassManifest[T]] + case java.lang.Character.TYPE => Char.asInstanceOf[ClassManifest[T]] + case java.lang.Integer.TYPE => Int.asInstanceOf[ClassManifest[T]] + case java.lang.Long.TYPE => Long.asInstanceOf[ClassManifest[T]] + case java.lang.Float.TYPE => Float.asInstanceOf[ClassManifest[T]] + case java.lang.Double.TYPE => Double.asInstanceOf[ClassManifest[T]] + case java.lang.Boolean.TYPE => Boolean.asInstanceOf[ClassManifest[T]] + case java.lang.Void.TYPE => Unit.asInstanceOf[ClassManifest[T]] + case _ => classType[T with AnyRef](clazz).asInstanceOf[ClassManifest[T]] + } + + def singleType[T <: AnyRef](value: AnyRef): Manifest[T] = Manifest.singleType(value) + + /** ClassManifest for the class type `clazz`, where `clazz` is + * a top-level or static class. + * @note This no-prefix, no-arguments case is separate because we + * it's called from ScalaRunTime.boxArray itself. If we + * pass varargs as arrays into this, we get an infinitely recursive call + * to boxArray. (Besides, having a separate case is more efficient) + */ + def classType[T](clazz: jClass[_]): ClassManifest[T] = + new ClassTypeManifest[T](None, clazz, Nil) + + /** ClassManifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class and `args` are its type arguments */ + def classType[T](clazz: jClass[_], arg1: OptManifest[_], args: OptManifest[_]*): ClassManifest[T] = + new ClassTypeManifest[T](None, clazz, arg1 :: args.toList) + + /** ClassManifest for the class type `clazz[args]`, where `clazz` is + * a class with non-package prefix type `prefix` and type arguments `args`. + */ + def classType[T](prefix: OptManifest[_], clazz: jClass[_], args: OptManifest[_]*): ClassManifest[T] = + new ClassTypeManifest[T](Some(prefix), clazz, args.toList) + + def arrayType[T](arg: OptManifest[_]): ClassManifest[Array[T]] = (arg: @unchecked) match { + case NoManifest => Object.asInstanceOf[ClassManifest[Array[T]]] + case m: ClassManifest[_] => m.asInstanceOf[ClassManifest[T]].arrayManifest + } + + @SerialVersionUID(1L) + private class AbstractTypeClassManifest[T](prefix: OptManifest[_], name: String, clazz: jClass[_], args: OptManifest[_]*) extends ClassManifest[T] { + override def runtimeClass = clazz + override val typeArguments = args.toList + override def toString = prefix.toString+"#"+name+argString + } + + /** ClassManifest for the abstract type `prefix # name`. `upperBound` is not + * strictly necessary as it could be obtained by reflection. It was + * added so that erasure can be calculated without reflection. */ + def abstractType[T](prefix: OptManifest[_], name: String, clazz: jClass[_], args: OptManifest[_]*): ClassManifest[T] = + new AbstractTypeClassManifest(prefix, name, clazz) + + /** ClassManifest for the abstract type `prefix # name`. `upperBound` is not + * strictly necessary as it could be obtained by reflection. It was + * added so that erasure can be calculated without reflection. + * todo: remove after next bootstrap + */ + def abstractType[T](prefix: OptManifest[_], name: String, upperbound: ClassManifest[_], args: OptManifest[_]*): ClassManifest[T] = + new AbstractTypeClassManifest(prefix, name, upperbound.runtimeClass) +} + +/** Manifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class */ +@nowarn("""cat=deprecation&origin=scala\.reflect\.ClassManifest""") +@SerialVersionUID(1L) +private class ClassTypeManifest[T]( + prefix: Option[OptManifest[_]], + val runtimeClass: jClass[_], + override val typeArguments: List[OptManifest[_]]) extends ClassManifest[T] +{ + override def toString = + (if (prefix.isEmpty) "" else prefix.get.toString+"#") + + (if (runtimeClass.isArray) "Array" else runtimeClass.getName) + + argString +} diff --git a/library/src/scala/reflect/ClassTag.scala b/library/src/scala/reflect/ClassTag.scala new file mode 100644 index 000000000000..5ebd3f1506e1 --- /dev/null +++ b/library/src/scala/reflect/ClassTag.scala @@ -0,0 +1,168 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +import java.lang.{Class => jClass} +import java.lang.ref.{WeakReference => jWeakReference} +import scala.annotation.{implicitNotFound, nowarn} +import scala.runtime.ClassValueCompat + +/** + * + * A `ClassTag[T]` stores the erased class of a given type `T`, accessible via the `runtimeClass` + * field. This is particularly useful for instantiating `Array`s whose element types are unknown + * at compile time. + * + * `ClassTag`s are a weaker special case of [[scala.reflect.api.TypeTags.TypeTag]]s, in that they + * wrap only the runtime class of a given type, whereas a `TypeTag` contains all static type + * information. That is, `ClassTag`s are constructed from knowing only the top-level class of a + * type, without necessarily knowing all of its argument types. This runtime information is enough + * for runtime `Array` creation. + * + * For example: + * {{{ + * scala> def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*) + * mkArray: [T](elems: T*)(implicit evidence\$1: scala.reflect.ClassTag[T])Array[T] + * + * scala> mkArray(42, 13) + * res0: Array[Int] = Array(42, 13) + * + * scala> mkArray("Japan","Brazil","Germany") + * res1: Array[String] = Array(Japan, Brazil, Germany) + * }}} + * + * See [[scala.reflect.api.TypeTags]] for more examples, or the + * [[https://docs.scala-lang.org/overviews/reflection/typetags-manifests.html Reflection Guide: TypeTags]] + * for more details. + * + */ +@nowarn("""cat=deprecation&origin=scala\.reflect\.ClassManifestDeprecatedApis""") +@implicitNotFound(msg = "No ClassTag available for ${T}") +trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serializable { + // please, don't add any APIs here, like it was with `newWrappedArray` and `newArrayBuilder` + // class tags, and all tags in general, should be as minimalistic as possible + + /** A class representing the type `U` to which `T` would be erased. + * Note that there is no subtyping relationship between `T` and `U`. + */ + def runtimeClass: jClass[_] + + /** Produces a `ClassTag` that knows how to instantiate an `Array[Array[T]]` */ + def wrap: ClassTag[Array[T]] = ClassTag[Array[T]](arrayClass(runtimeClass)) + + /** Produces a new array with element type `T` and length `len` */ + def newArray(len: Int): Array[T] = + java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]] + + /** A ClassTag[T] can serve as an extractor that matches only objects of type T. + * + * The compiler tries to turn unchecked type tests in pattern matches into checked ones + * by wrapping a `(_: T)` type pattern as `ct(_: T)`, where `ct` is the `ClassTag[T]` instance. + * Type tests necessary before calling other extractors are treated similarly. + * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)` + * is uncheckable, but we have an instance of `ClassTag[T]`. + */ + def unapply(x: Any): Option[T] = + if (runtimeClass.isInstance(x)) Some(x.asInstanceOf[T]) + else None + + // case class accessories + override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] + override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && this.runtimeClass == x.asInstanceOf[ClassTag[_]].runtimeClass + override def hashCode = runtimeClass.## + override def toString = { + def prettyprint(clazz: jClass[_]): String = + if (clazz.isArray) s"Array[${prettyprint(clazz.getComponentType)}]" else + clazz.getName + prettyprint(runtimeClass) + } +} + +/** + * Class tags corresponding to primitive types and constructor/extractor for ClassTags. + */ +object ClassTag { + private[this] val ObjectTYPE = classOf[java.lang.Object] + private[this] val NothingTYPE = classOf[scala.runtime.Nothing$] + private[this] val NullTYPE = classOf[scala.runtime.Null$] + + import ManifestFactory._ + + val Byte : ByteManifest = Manifest.Byte + val Short : ShortManifest = Manifest.Short + val Char : CharManifest = Manifest.Char + val Int : IntManifest = Manifest.Int + val Long : LongManifest = Manifest.Long + val Float : FloatManifest = Manifest.Float + val Double : DoubleManifest = Manifest.Double + val Boolean : BooleanManifest = Manifest.Boolean + val Unit : UnitManifest = Manifest.Unit + val Any : ClassTag[scala.Any] = Manifest.Any + val Object : ClassTag[java.lang.Object] = Manifest.Object + val AnyVal : ClassTag[scala.AnyVal] = Manifest.AnyVal + val AnyRef : ClassTag[scala.AnyRef] = Manifest.AnyRef + val Nothing : ClassTag[scala.Nothing] = Manifest.Nothing + val Null : ClassTag[scala.Null] = Manifest.Null + + private val cacheDisabled = java.lang.Boolean.getBoolean("scala.reflect.classtag.cache.disable") + private[this] object cache extends ClassValueCompat[jWeakReference[ClassTag[_]]] { + override def computeValue(runtimeClass: jClass[_]): jWeakReference[ClassTag[_]] = + new jWeakReference(computeTag(runtimeClass)) + + def computeTag(runtimeClass: jClass[_]): ClassTag[_] = + runtimeClass match { + case x if x.isPrimitive => primitiveClassTag(runtimeClass) + case ObjectTYPE => ClassTag.Object + case NothingTYPE => ClassTag.Nothing + case NullTYPE => ClassTag.Null + case _ => new GenericClassTag[AnyRef](runtimeClass) + } + + private def primitiveClassTag[T](runtimeClass: Class[_]): ClassTag[_] = + (runtimeClass: @unchecked) match { + case java.lang.Byte.TYPE => ClassTag.Byte + case java.lang.Short.TYPE => ClassTag.Short + case java.lang.Character.TYPE => ClassTag.Char + case java.lang.Integer.TYPE => ClassTag.Int + case java.lang.Long.TYPE => ClassTag.Long + case java.lang.Float.TYPE => ClassTag.Float + case java.lang.Double.TYPE => ClassTag.Double + case java.lang.Boolean.TYPE => ClassTag.Boolean + case java.lang.Void.TYPE => ClassTag.Unit + } + } + + @SerialVersionUID(1L) + private class GenericClassTag[T](val runtimeClass: jClass[_]) extends ClassTag[T] { + override def newArray(len: Int): Array[T] = { + java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]] + } + } + + def apply[T](runtimeClass1: jClass[_]): ClassTag[T] = { + if (cacheDisabled) { + cache.computeTag(runtimeClass1).asInstanceOf[ClassTag[T]] + } else { + val ref = cache.get(runtimeClass1).asInstanceOf[jWeakReference[ClassTag[T]]] + var tag = ref.get + if (tag == null) { + cache.remove(runtimeClass1) + tag = cache.computeTag(runtimeClass1).asInstanceOf[ClassTag[T]] + } + tag + } + } + + def unapply[T](ctag: ClassTag[T]): Option[Class[_]] = Some(ctag.runtimeClass) +} diff --git a/library/src/scala/reflect/Manifest.scala b/library/src/scala/reflect/Manifest.scala new file mode 100644 index 000000000000..9f382fdd800e --- /dev/null +++ b/library/src/scala/reflect/Manifest.scala @@ -0,0 +1,461 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +import scala.annotation.{implicitNotFound, nowarn} +import scala.collection.mutable.{ArrayBuilder, ArraySeq} + +/** A `Manifest[T]` is an opaque descriptor for type T. Its supported use + * is to give access to the erasure of the type as a `Class` instance, as + * is necessary for the creation of native `Arrays` if the class is not + * known at compile time. + * + * The type-relation operators `<:<` and `=:=` should be considered + * approximations only, as there are numerous aspects of type conformance + * which are not yet adequately represented in manifests. + * + * Example usages: + * {{{ + * def arr[T] = new Array[T](0) // does not compile + * def arr[T](implicit m: Manifest[T]) = new Array[T](0) // compiles + * def arr[T: Manifest] = new Array[T](0) // shorthand for the preceding + * + * // Methods manifest and optManifest are in [[scala.Predef]]. + * def isApproxSubType[T: Manifest, U: Manifest] = manifest[T] <:< manifest[U] + * isApproxSubType[List[String], List[AnyRef]] // true + * isApproxSubType[List[String], List[Int]] // false + * + * def methods[T: Manifest] = manifest[T].runtimeClass.getMethods + * def retType[T: Manifest](name: String) = + * methods[T] find (_.getName == name) map (_.getGenericReturnType) + * + * retType[Map[_, _]]("values") // Some(scala.collection.Iterable) + * }}} + */ +@nowarn("""cat=deprecation&origin=scala\.reflect\.ClassManifest(DeprecatedApis.*)?""") +@implicitNotFound(msg = "No Manifest available for ${T}.") +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("use scala.reflect.ClassTag (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") +trait Manifest[T] extends ClassManifest[T] with Equals { + override def typeArguments: List[Manifest[_]] = Nil + + override def arrayManifest: Manifest[Array[T]] = + Manifest.classType[Array[T]](arrayClass[T](runtimeClass), this) + + override def canEqual(that: Any): Boolean = that match { + case _: Manifest[_] => true + case _ => false + } + /** Note: testing for erasure here is important, as it is many times + * faster than <:< and rules out most comparisons. + */ + override def equals(that: Any): Boolean = that match { + case m: Manifest[_] => (m canEqual this) && (this.runtimeClass == m.runtimeClass) && (this <:< m) && (m <:< this) + case _ => false + } + override def hashCode = this.runtimeClass.## +} + +/** The object `Manifest` defines factory methods for manifests. + * It is intended for use by the compiler and should not be used in client code. + */ +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("use scala.reflect.ClassTag (to capture erasures), scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") +object Manifest { + /* Forward all the public members of ManifestFactory, since this object used + * to be a `private val Manifest = ManifestFactory` in the package object. It + * was moved here because it needs to be in the same file as `trait Manifest` + * defined above. + */ + + def valueManifests: List[AnyValManifest[_]] = + ManifestFactory.valueManifests + + val Byte: ManifestFactory.ByteManifest = ManifestFactory.Byte + val Short: ManifestFactory.ShortManifest = ManifestFactory.Short + val Char: ManifestFactory.CharManifest = ManifestFactory.Char + val Int: ManifestFactory.IntManifest = ManifestFactory.Int + val Long: ManifestFactory.LongManifest = ManifestFactory.Long + val Float: ManifestFactory.FloatManifest = ManifestFactory.Float + val Double: ManifestFactory.DoubleManifest = ManifestFactory.Double + val Boolean: ManifestFactory.BooleanManifest = ManifestFactory.Boolean + val Unit: ManifestFactory.UnitManifest = ManifestFactory.Unit + + val Any: Manifest[scala.Any] = ManifestFactory.Any + val Object: Manifest[java.lang.Object] = ManifestFactory.Object + val AnyRef: Manifest[scala.AnyRef] = ManifestFactory.AnyRef + val AnyVal: Manifest[scala.AnyVal] = ManifestFactory.AnyVal + val Null: Manifest[scala.Null] = ManifestFactory.Null + val Nothing: Manifest[scala.Nothing] = ManifestFactory.Nothing + + /** Manifest for the singleton type `value.type`. */ + def singleType[T <: AnyRef](value: AnyRef): Manifest[T] = + ManifestFactory.singleType[T](value) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class. + * @note This no-prefix, no-arguments case is separate because we + * it's called from ScalaRunTime.boxArray itself. If we + * pass varargs as arrays into this, we get an infinitely recursive call + * to boxArray. (Besides, having a separate case is more efficient) + */ + def classType[T](clazz: Predef.Class[_]): Manifest[T] = + ManifestFactory.classType[T](clazz) + + /** Manifest for the class type `clazz`, where `clazz` is + * a top-level or static class and args are its type arguments. */ + def classType[T](clazz: Predef.Class[T], arg1: Manifest[_], args: Manifest[_]*): Manifest[T] = + ManifestFactory.classType[T](clazz, arg1, args: _*) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a class with non-package prefix type `prefix` and type arguments `args`. + */ + def classType[T](prefix: Manifest[_], clazz: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + ManifestFactory.classType[T](prefix, clazz, args: _*) + + def arrayType[T](arg: Manifest[_]): Manifest[Array[T]] = + ManifestFactory.arrayType[T](arg) + + /** Manifest for the abstract type `prefix # name`. `upperBound` is not + * strictly necessary as it could be obtained by reflection. It was + * added so that erasure can be calculated without reflection. */ + def abstractType[T](prefix: Manifest[_], name: String, upperBound: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + ManifestFactory.abstractType[T](prefix, name, upperBound, args: _*) + + /** Manifest for the unknown type `_ >: L <: U` in an existential. */ + def wildcardType[T](lowerBound: Manifest[_], upperBound: Manifest[_]): Manifest[T] = + ManifestFactory.wildcardType[T](lowerBound, upperBound) + + /** Manifest for the intersection type `parents_0 with ... with parents_n`. */ + def intersectionType[T](parents: Manifest[_]*): Manifest[T] = + ManifestFactory.intersectionType[T](parents: _*) + +} + +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("use type tags and manually check the corresponding class or type instead", "2.10.0") +@nowarn("""cat=deprecation&origin=scala\.reflect\.ClassManifest(DeprecatedApis.*)?""") +@SerialVersionUID(1L) +abstract class AnyValManifest[T <: AnyVal](override val toString: String) extends Manifest[T] with Equals { + override def <:<(that: ClassManifest[_]): Boolean = + (that eq this) || (that eq Manifest.Any) || (that eq Manifest.AnyVal) + override def canEqual(other: Any) = other match { + case _: AnyValManifest[_] => true + case _ => false + } + override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef] + @transient + override val hashCode = System.identityHashCode(this) +} + +/** `ManifestFactory` defines factory methods for manifests. + * It is intended for use by the compiler and should not be used in client code. + * + * Unlike `Manifest`, this factory isn't annotated with a deprecation warning. + * This is done to prevent avalanches of deprecation warnings in the code that calls methods with manifests. + * Why so complicated? Read up the comments for `ClassManifestFactory`. + */ +@nowarn("""cat=deprecation&origin=scala\.reflect\.ClassManifest(DeprecatedApis.*)?""") +object ManifestFactory { + def valueManifests: List[AnyValManifest[_]] = + List(Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit) + + @SerialVersionUID(1L) + final private[reflect] class ByteManifest extends AnyValManifest[scala.Byte]("Byte") { + def runtimeClass: Class[java.lang.Byte] = java.lang.Byte.TYPE + @inline override def newArray(len: Int): Array[Byte] = new Array[Byte](len) + override def newWrappedArray(len: Int): ArraySeq[Byte] = new ArraySeq.ofByte(new Array[Byte](len)) + override def newArrayBuilder(): ArrayBuilder[Byte] = new ArrayBuilder.ofByte() + override def unapply(x: Any): Option[Byte] = { + x match { + case d: Byte => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Byte + } + val Byte: ByteManifest = new ByteManifest + + @SerialVersionUID(1L) + final private[reflect] class ShortManifest extends AnyValManifest[scala.Short]("Short") { + def runtimeClass: Class[java.lang.Short] = java.lang.Short.TYPE + @inline override def newArray(len: Int): Array[Short] = new Array[Short](len) + override def newWrappedArray(len: Int): ArraySeq[Short] = new ArraySeq.ofShort(new Array[Short](len)) + override def newArrayBuilder(): ArrayBuilder[Short] = new ArrayBuilder.ofShort() + override def unapply(x: Any): Option[Short] = { + x match { + case d: Short => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Short + } + val Short: ShortManifest = new ShortManifest + + @SerialVersionUID(1L) + final private[reflect] class CharManifest extends AnyValManifest[scala.Char]("Char") { + def runtimeClass: Class[java.lang.Character] = java.lang.Character.TYPE + @inline override def newArray(len: Int): Array[Char] = new Array[Char](len) + override def newWrappedArray(len: Int): ArraySeq[Char] = new ArraySeq.ofChar(new Array[Char](len)) + override def newArrayBuilder(): ArrayBuilder[Char] = new ArrayBuilder.ofChar() + override def unapply(x: Any): Option[Char] = { + x match { + case d: Char => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Char + } + val Char: CharManifest = new CharManifest + + @SerialVersionUID(1L) + final private[reflect] class IntManifest extends AnyValManifest[scala.Int]("Int") { + def runtimeClass: Class[java.lang.Integer] = java.lang.Integer.TYPE + @inline override def newArray(len: Int): Array[Int] = new Array[Int](len) + override def newWrappedArray(len: Int): ArraySeq[Int] = new ArraySeq.ofInt(new Array[Int](len)) + override def newArrayBuilder(): ArrayBuilder[Int] = new ArrayBuilder.ofInt() + override def unapply(x: Any): Option[Int] = { + x match { + case d: Int => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Int + } + val Int: IntManifest = new IntManifest + + @SerialVersionUID(1L) + final private[reflect] class LongManifest extends AnyValManifest[scala.Long]("Long") { + def runtimeClass: Class[java.lang.Long] = java.lang.Long.TYPE + @inline override def newArray(len: Int): Array[Long] = new Array[Long](len) + override def newWrappedArray(len: Int): ArraySeq[Long] = new ArraySeq.ofLong(new Array[Long](len)) + override def newArrayBuilder(): ArrayBuilder[Long] = new ArrayBuilder.ofLong() + override def unapply(x: Any): Option[Long] = { + x match { + case d: Long => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Long + } + val Long: LongManifest = new LongManifest + + @SerialVersionUID(1L) + final private[reflect] class FloatManifest extends AnyValManifest[scala.Float]("Float") { + def runtimeClass: Class[java.lang.Float] = java.lang.Float.TYPE + @inline override def newArray(len: Int): Array[Float] = new Array[Float](len) + override def newWrappedArray(len: Int): ArraySeq[Float] = new ArraySeq.ofFloat(new Array[Float](len)) + override def newArrayBuilder(): ArrayBuilder[Float] = new ArrayBuilder.ofFloat() + override def unapply(x: Any): Option[Float] = { + x match { + case d: Float => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Float + } + val Float: FloatManifest = new FloatManifest + + @SerialVersionUID(1L) + final private[reflect] class DoubleManifest extends AnyValManifest[scala.Double]("Double") { + def runtimeClass: Class[java.lang.Double] = java.lang.Double.TYPE + @inline override def newArray(len: Int): Array[Double] = new Array[Double](len) + override def newWrappedArray(len: Int): ArraySeq[Double] = new ArraySeq.ofDouble(new Array[Double](len)) + override def newArrayBuilder(): ArrayBuilder[Double] = new ArrayBuilder.ofDouble() + + override def unapply(x: Any): Option[Double] = { + x match { + case d: Double => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Double + } + val Double: DoubleManifest = new DoubleManifest + + @SerialVersionUID(1L) + final private[reflect] class BooleanManifest extends AnyValManifest[scala.Boolean]("Boolean") { + def runtimeClass: Class[java.lang.Boolean] = java.lang.Boolean.TYPE + @inline override def newArray(len: Int): Array[Boolean] = new Array[Boolean](len) + override def newWrappedArray(len: Int): ArraySeq[Boolean] = new ArraySeq.ofBoolean(new Array[Boolean](len)) + override def newArrayBuilder(): ArrayBuilder[Boolean] = new ArrayBuilder.ofBoolean() + override def unapply(x: Any): Option[Boolean] = { + x match { + case d: Boolean => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Boolean + } + val Boolean: BooleanManifest = new BooleanManifest + + @SerialVersionUID(1L) + final private[reflect] class UnitManifest extends AnyValManifest[scala.Unit]("Unit") { + def runtimeClass: Class[java.lang.Void] = java.lang.Void.TYPE + @inline override def newArray(len: Int): Array[Unit] = new Array[Unit](len) + override def newWrappedArray(len: Int): ArraySeq[Unit] = new ArraySeq.ofUnit(new Array[Unit](len)) + override def newArrayBuilder(): ArrayBuilder[Unit] = new ArrayBuilder.ofUnit() + override protected def arrayClass[T](tp: Class[_]): Class[Array[T]] = + if (tp eq runtimeClass) classOf[Array[scala.runtime.BoxedUnit]].asInstanceOf[Class[Array[T]]] + else super.arrayClass(tp) + override def unapply(x: Any): Option[Unit] = { + x match { + case d: Unit => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Unit + } + val Unit: UnitManifest = new UnitManifest + + private[this] val ObjectTYPE = classOf[java.lang.Object] + private[this] val NothingTYPE = classOf[scala.runtime.Nothing$] + private[this] val NullTYPE = classOf[scala.runtime.Null$] + + @SerialVersionUID(1L) + final private class AnyManifest extends PhantomManifest[scala.Any](ObjectTYPE, "Any") { + override def newArray(len: Int) = new Array[scala.Any](len) + override def <:<(that: ClassManifest[_]): Boolean = (that eq this) + private def readResolve(): Any = Manifest.Any + } + val Any: Manifest[scala.Any] = new AnyManifest + + @SerialVersionUID(1L) + final private class ObjectManifest extends PhantomManifest[java.lang.Object](ObjectTYPE, "Object") { + override def newArray(len: Int) = new Array[java.lang.Object](len) + override def <:<(that: ClassManifest[_]): Boolean = (that eq this) || (that eq Any) + private def readResolve(): Any = Manifest.Object + } + val Object: Manifest[java.lang.Object] = new ObjectManifest + + val AnyRef: Manifest[scala.AnyRef] = Object.asInstanceOf[Manifest[scala.AnyRef]] + + @SerialVersionUID(1L) + final private class AnyValPhantomManifest extends PhantomManifest[scala.AnyVal](ObjectTYPE, "AnyVal") { + override def newArray(len: Int) = new Array[scala.AnyVal](len) + override def <:<(that: ClassManifest[_]): Boolean = (that eq this) || (that eq Any) + private def readResolve(): Any = Manifest.AnyVal + } + val AnyVal: Manifest[scala.AnyVal] = new AnyValPhantomManifest + + @SerialVersionUID(1L) + final private class NullManifest extends PhantomManifest[scala.Null](NullTYPE, "Null") { + override def newArray(len: Int) = new Array[scala.Null](len) + override def <:<(that: ClassManifest[_]): Boolean = + (that ne null) && (that ne Nothing) && !(that <:< AnyVal) + private def readResolve(): Any = Manifest.Null + } + val Null: Manifest[scala.Null] = new NullManifest + + @SerialVersionUID(1L) + final private class NothingManifest extends PhantomManifest[scala.Nothing](NothingTYPE, "Nothing") { + override def newArray(len: Int) = new Array[scala.Nothing](len) + override def <:<(that: ClassManifest[_]): Boolean = (that ne null) + private def readResolve(): Any = Manifest.Nothing + } + val Nothing: Manifest[scala.Nothing] = new NothingManifest + + @SerialVersionUID(1L) + final private class SingletonTypeManifest[T <: AnyRef](value: AnyRef) extends Manifest[T] { + lazy val runtimeClass: Class[_ <: AnyRef] = value.getClass + override lazy val toString = value.toString + ".type" + } + + /** Manifest for the singleton type `value.type`. */ + def singleType[T <: AnyRef](value: AnyRef): Manifest[T] = + new SingletonTypeManifest[T](value) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class. + * @note This no-prefix, no-arguments case is separate because we + * it's called from ScalaRunTime.boxArray itself. If we + * pass varargs as arrays into this, we get an infinitely recursive call + * to boxArray. (Besides, having a separate case is more efficient) + */ + def classType[T](clazz: Predef.Class[_]): Manifest[T] = + new ClassTypeManifest[T](None, clazz, Nil) + + /** Manifest for the class type `clazz`, where `clazz` is + * a top-level or static class and args are its type arguments. */ + def classType[T](clazz: Predef.Class[T], arg1: Manifest[_], args: Manifest[_]*): Manifest[T] = + new ClassTypeManifest[T](None, clazz, arg1 :: args.toList) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a class with non-package prefix type `prefix` and type arguments `args`. + */ + def classType[T](prefix: Manifest[_], clazz: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + new ClassTypeManifest[T](Some(prefix), clazz, args.toList) + + @SerialVersionUID(1L) + private abstract class PhantomManifest[T](_runtimeClass: Predef.Class[_], + override val toString: String) extends ClassTypeManifest[T](None, _runtimeClass, Nil) { + override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef] + @transient + override val hashCode = System.identityHashCode(this) + } + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class. */ + @SerialVersionUID(1L) + private class ClassTypeManifest[T](prefix: Option[Manifest[_]], + val runtimeClass: Predef.Class[_], + override val typeArguments: List[Manifest[_]]) extends Manifest[T] { + override def toString = + (if (prefix.isEmpty) "" else prefix.get.toString+"#") + + (if (runtimeClass.isArray) "Array" else runtimeClass.getName) + + argString + } + + def arrayType[T](arg: Manifest[_]): Manifest[Array[T]] = + arg.asInstanceOf[Manifest[T]].arrayManifest + + @SerialVersionUID(1L) + private class AbstractTypeManifest[T](prefix: Manifest[_], name: String, upperBound: Predef.Class[_], args: scala.collection.Seq[Manifest[_]]) extends Manifest[T] { + def runtimeClass = upperBound + override val typeArguments = args.toList + override def toString = prefix.toString+"#"+name+argString + } + + /** Manifest for the abstract type `prefix # name`. `upperBound` is not + * strictly necessary as it could be obtained by reflection. It was + * added so that erasure can be calculated without reflection. */ + def abstractType[T](prefix: Manifest[_], name: String, upperBound: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + new AbstractTypeManifest[T](prefix, name, upperBound, args) + + @SerialVersionUID(1L) + private class WildcardManifest[T](lowerBound: Manifest[_], upperBound: Manifest[_]) extends Manifest[T] { + def runtimeClass = upperBound.runtimeClass + override def toString = + "_" + + (if (lowerBound eq Nothing) "" else " >: "+lowerBound) + + (if (upperBound eq Nothing) "" else " <: "+upperBound) + } + + /** Manifest for the unknown type `_ >: L <: U` in an existential. + */ + def wildcardType[T](lowerBound: Manifest[_], upperBound: Manifest[_]): Manifest[T] = + new WildcardManifest[T](lowerBound, upperBound) + + @SerialVersionUID(1L) + private class IntersectionTypeManifest[T](parents: Array[Manifest[_]]) extends Manifest[T] { + // We use an `Array` instead of a `Seq` for `parents` to avoid cyclic dependencies during deserialization + // which can cause serialization proxies to leak and cause a ClassCastException. + def runtimeClass = parents(0).runtimeClass + override def toString = parents.mkString(" with ") + } + + /** Manifest for the intersection type `parents_0 with ... with parents_n`. */ + def intersectionType[T](parents: Manifest[_]*): Manifest[T] = + new IntersectionTypeManifest[T](parents.toArray) +} diff --git a/library/src/scala/reflect/NameTransformer.scala b/library/src/scala/reflect/NameTransformer.scala new file mode 100644 index 000000000000..4980ed5bd6b4 --- /dev/null +++ b/library/src/scala/reflect/NameTransformer.scala @@ -0,0 +1,167 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +/** Provides functions to encode and decode Scala symbolic names. + * Also provides some constants. + */ +object NameTransformer { + // TODO: reduce duplication with and in StdNames + // I made these constants because we cannot change them without bumping our major version anyway. + final val NAME_JOIN_STRING = "$" + final val MODULE_SUFFIX_STRING = "$" + final val MODULE_INSTANCE_NAME = "MODULE$" + final val LOCAL_SUFFIX_STRING = " " + final val LAZY_LOCAL_SUFFIX_STRING = "$lzy" + final val MODULE_VAR_SUFFIX_STRING = "$module" + final val SETTER_SUFFIX_STRING = "_$eq" + final val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" + + private[this] val nops = 128 + private[this] val ncodes = 26 * 26 + + private class OpCodes(val op: Char, val code: String, val next: OpCodes) + + private[this] val op2code = new Array[String](nops) + private[this] val code2op = new Array[OpCodes](ncodes) + private def enterOp(op: Char, code: String) = { + op2code(op.toInt) = code + val c = (code.charAt(1) - 'a') * 26 + code.charAt(2) - 'a' + code2op(c.toInt) = new OpCodes(op, code, code2op(c)) + } + + /* Note: decoding assumes opcodes are only ever lowercase. */ + enterOp('~', "$tilde") + enterOp('=', "$eq") + enterOp('<', "$less") + enterOp('>', "$greater") + enterOp('!', "$bang") + enterOp('#', "$hash") + enterOp('%', "$percent") + enterOp('^', "$up") + enterOp('&', "$amp") + enterOp('|', "$bar") + enterOp('*', "$times") + enterOp('/', "$div") + enterOp('+', "$plus") + enterOp('-', "$minus") + enterOp(':', "$colon") + enterOp('\\', "$bslash") + enterOp('?', "$qmark") + enterOp('@', "$at") + + /** Replace operator symbols by corresponding `\$opname`. + * + * @param name the string to encode + * @return the string with all recognized opchars replaced with their encoding + */ + def encode(name: String): String = { + var buf: StringBuilder = null + val len = name.length() + var i = 0 + while (i < len) { + val c = name charAt i + if (c < nops && (op2code(c.toInt) ne null)) { + if (buf eq null) { + buf = new StringBuilder() + buf.append(name.substring(0, i)) + } + buf.append(op2code(c.toInt)) + /* Handle glyphs that are not valid Java/JVM identifiers */ + } + else if (!Character.isJavaIdentifierPart(c)) { + if (buf eq null) { + buf = new StringBuilder() + buf.append(name.substring(0, i)) + } + buf.append("$u%04X".format(c.toInt)) + } + else if (buf ne null) { + buf.append(c) + } + i += 1 + } + if (buf eq null) name else buf.toString() + } + + /** Replace `\$opname` by corresponding operator symbol. + * + * @param name0 the string to decode + * @return the string with all recognized operator symbol encodings replaced with their name + */ + def decode(name0: String): String = { + //System.out.println("decode: " + name);//DEBUG + val name = if (name0.endsWith("")) name0.stripSuffix("") + "this" + else name0 + var buf: StringBuilder = null + val len = name.length() + var i = 0 + while (i < len) { + var ops: OpCodes = null + var unicode = false + val c = name charAt i + if (c == '$' && i + 2 < len) { + val ch1 = name.charAt(i+1) + if ('a' <= ch1 && ch1 <= 'z') { + val ch2 = name.charAt(i+2) + if ('a' <= ch2 && ch2 <= 'z') { + ops = code2op((ch1 - 'a') * 26 + ch2 - 'a') + while ((ops ne null) && !name.startsWith(ops.code, i)) ops = ops.next + if (ops ne null) { + if (buf eq null) { + buf = new StringBuilder() + buf.append(name.substring(0, i)) + } + buf.append(ops.op) + i += ops.code.length() + } + /* Handle the decoding of Unicode glyphs that are + * not valid Java/JVM identifiers */ + } else if ((len - i) >= 6 && // Check that there are enough characters left + ch1 == 'u' && + ((Character.isDigit(ch2)) || + ('A' <= ch2 && ch2 <= 'F'))) { + /* Skip past "$u", next four should be hexadecimal */ + val hex = name.substring(i+2, i+6) + try { + val str = Integer.parseInt(hex, 16).toChar + if (buf eq null) { + buf = new StringBuilder() + buf.append(name.substring(0, i)) + } + buf.append(str) + /* 2 for "$u", 4 for hexadecimal number */ + i += 6 + unicode = true + } catch { + case _:NumberFormatException => + /* `hex` did not decode to a hexadecimal number, so + * do nothing. */ + } + } + } + } + /* If we didn't see an opcode or encoded Unicode glyph, and the + buffer is non-empty, write the current character and advance + one */ + if ((ops eq null) && !unicode) { + if (buf ne null) + buf.append(c) + i += 1 + } + } + //System.out.println("= " + (if (buf == null) name else buf.toString()));//DEBUG + if (buf eq null) name else buf.toString() + } +} diff --git a/library/src/scala/reflect/NoManifest.scala b/library/src/scala/reflect/NoManifest.scala new file mode 100644 index 000000000000..819ffede46d3 --- /dev/null +++ b/library/src/scala/reflect/NoManifest.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +/** One of the branches of an [[scala.reflect.OptManifest]]. + */ +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("This notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") +object NoManifest extends OptManifest[Nothing] with Serializable { + override def toString = "" +} diff --git a/library/src/scala/reflect/OptManifest.scala b/library/src/scala/reflect/OptManifest.scala new file mode 100644 index 000000000000..f3b3c3117236 --- /dev/null +++ b/library/src/scala/reflect/OptManifest.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +/** A `OptManifest[T]` is an optional [[scala.reflect.Manifest]]. + * + * It is either a `Manifest` or the value `NoManifest`. + */ +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("This notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") +trait OptManifest[+T] extends Serializable diff --git a/library/src/scala/reflect/ScalaLongSignature.java b/library/src/scala/reflect/ScalaLongSignature.java new file mode 100644 index 000000000000..29a77dc2f352 --- /dev/null +++ b/library/src/scala/reflect/ScalaLongSignature.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.reflect; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ScalaLongSignature { + String[] bytes(); +} diff --git a/library/src/scala/reflect/ScalaSignature.java b/library/src/scala/reflect/ScalaSignature.java new file mode 100644 index 000000000000..dbd5a46bfd10 --- /dev/null +++ b/library/src/scala/reflect/ScalaSignature.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.reflect; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ScalaSignature { + String bytes(); +} diff --git a/library/src/scala/reflect/package.scala b/library/src/scala/reflect/package.scala new file mode 100644 index 000000000000..caf79866c71e --- /dev/null +++ b/library/src/scala/reflect/package.scala @@ -0,0 +1,76 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import java.lang.reflect.{AccessibleObject => jAccessibleObject} +import scala.annotation.nowarn + +package object reflect { + + // in the new scheme of things ClassManifests are aliased to ClassTags + // this is done because we want `toArray` in collections work with ClassTags + // but changing it to use the ClassTag context bound without aliasing ClassManifest + // will break everyone who subclasses and overrides `toArray` + // luckily for us, aliasing doesn't hamper backward compatibility, so it's ideal in this situation + // I wish we could do the same for Manifests and TypeTags though + + // note, by the way, that we don't touch ClassManifest the object + // because its Byte, Short and so on factory fields are incompatible with ClassTag's + + /** A `ClassManifest[T]` is an opaque descriptor for type `T`. + * It is used by the compiler to preserve information necessary + * for instantiating `Arrays` in those cases where the element type + * is unknown at compile time. + * + * The type-relation operators make an effort to present a more accurate + * picture than can be realized with erased types, but they should not be + * relied upon to give correct answers. In particular they are likely to + * be wrong when variance is involved or when a subtype has a different + * number of type arguments than a supertype. + */ + @deprecated("use scala.reflect.ClassTag instead", "2.10.0") + @annotation.implicitNotFound(msg = "No ClassManifest available for ${T}.") + type ClassManifest[T] = scala.reflect.ClassTag[T] + + /** The object `ClassManifest` defines factory methods for manifests. + * It is intended for use by the compiler and should not be used in client code. + */ + @deprecated("use scala.reflect.ClassTag instead", "2.10.0") + val ClassManifest = ClassManifestFactory + + def classTag[T](implicit ctag: ClassTag[T]) = ctag + + /** Make a java reflection object accessible, if it is not already + * and it is possible to do so. If a SecurityException is thrown in the + * attempt, it is caught and discarded. + */ + def ensureAccessible[T <: jAccessibleObject](m: T): T = { + // This calls `setAccessible` unnecessarily, because `isAccessible` is only `true` if `setAccessible(true)` + // was called before, not if the reflected object is inherently accessible. + // TODO: replace by `canAccess` once we're on JDK 9+ + if (!m.isAccessible: @nowarn("cat=deprecation")) { + try m setAccessible true + catch { case _: SecurityException => } // does nothing + } + m + } + + // anchor for the class tag materialization macro emitted during tag materialization in Implicits.scala + // implementation is hardwired into `scala.reflect.reify.Taggers` + // using the mechanism implemented in `scala.tools.reflect.FastTrack` + // todo. once we have implicit macros for tag generation, we can remove this anchor + private[scala] def materializeClassTag[T](): ClassTag[T] = macro ??? +} + +/** An exception that indicates an error during Scala reflection */ +case class ScalaReflectionException(msg: String) extends Exception(msg) From 48cc50e957267d50750d6cba5fb051169ef07d9e Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:37:53 +0100 Subject: [PATCH 15/28] chore: add all the files in scala.reflect.macros --- .../reflect/macros/internal/macroImpl.scala | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 library/src/scala/reflect/macros/internal/macroImpl.scala diff --git a/library/src/scala/reflect/macros/internal/macroImpl.scala b/library/src/scala/reflect/macros/internal/macroImpl.scala new file mode 100644 index 000000000000..c26426a079f2 --- /dev/null +++ b/library/src/scala/reflect/macros/internal/macroImpl.scala @@ -0,0 +1,30 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.reflect.macros +package internal + +/** Links macro definitions with their implementation. + * This is necessary to preserve macro def -> macro impl links between compilation runs. + * + * More precisely, after typechecking right-hand side of a macro def + * `typedMacroBody` slaps `macroImpl` annotation onto the macro def + * with the result of typechecking as a sole parameter. + * + * As an unfortunate consequence, this annotation must be defined in scala-library.jar, + * because anyone (even those programmers who compile their programs with only scala-library on classpath) + * must be able to define macros. + * + * To lessen the weirdness we define this annotation as `private[scala]`. + * It will not prevent pickling, but it will prevent application developers (and scaladocs) from seeing the annotation. + */ +private[scala] final class macroImpl(val referenceToMacroImpl: Any) extends scala.annotation.StaticAnnotation From 725f72a4b35c9ea0cee3b3164a1ac91c2b822d13 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:39:13 +0100 Subject: [PATCH 16/28] chore: add all the files in scala.runtime.java8 --- .../runtime/java8/JFunction0$mcB$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction0$mcC$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction0$mcD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction0$mcF$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction0$mcI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction0$mcJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction0$mcS$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction0$mcV$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction0$mcZ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcDD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcDF$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcDI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcDJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcFD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcFF$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcFI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcFJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcID$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcIF$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcII$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcIJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcJD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcJF$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcJI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcJJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcVD$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction1$mcVF$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction1$mcVI$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction1$mcVJ$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction1$mcZD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcZF$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcZI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction1$mcZJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDDD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDDI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDDJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDID$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDII$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDIJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDJD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDJI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcDJJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFDD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFDI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFDJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFID$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFII$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFIJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFJD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFJI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcFJJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIDD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIDI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIDJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIID$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIII$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIIJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIJD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIJI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcIJJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJDD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJDI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJDJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJID$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJII$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJIJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJJD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJJI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcJJJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcVDD$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVDI$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVDJ$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVID$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVII$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVIJ$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVJD$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVJI$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcVJJ$sp.scala | 21 +++++++++++++++++++ .../runtime/java8/JFunction2$mcZDD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZDI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZDJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZID$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZII$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZIJ$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZJD$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZJI$sp.scala | 18 ++++++++++++++++ .../runtime/java8/JFunction2$mcZJJ$sp.scala | 18 ++++++++++++++++ 87 files changed, 1608 insertions(+) create mode 100644 library/src/scala/runtime/java8/JFunction0$mcB$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcC$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcF$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcS$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcV$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction0$mcZ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcDD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcDF$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcDI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcDJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcFD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcFF$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcFI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcFJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcID$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcIF$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcII$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcIJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcJD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcJF$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcJI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcJJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcVD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcVF$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcVI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcVJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcZD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcZF$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcZI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction1$mcZJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDDD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDDI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDDJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDID$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDII$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDIJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDJD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDJI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcDJJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFDD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFDI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFDJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFID$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFII$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFIJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFJD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFJI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcFJJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIDD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIDI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIDJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIID$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIII$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIIJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIJD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIJI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcIJJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJDD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJDI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJDJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJID$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJII$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJIJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJJD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJJI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcJJJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVDD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVDI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVDJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVID$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVII$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVIJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVJD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVJI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcVJJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZDD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZDI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZDJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZID$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZII$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZIJ$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZJD$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZJI$sp.scala create mode 100644 library/src/scala/runtime/java8/JFunction2$mcZJJ$sp.scala diff --git a/library/src/scala/runtime/java8/JFunction0$mcB$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcB$sp.scala new file mode 100644 index 000000000000..cccb1a1a9430 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcB$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcB$sp extends Function0[Any] with Serializable { + def apply$mcB$sp(): Byte + override def apply(): Any = scala.runtime.BoxesRunTime.boxToByte(apply$mcB$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcC$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcC$sp.scala new file mode 100644 index 000000000000..c4e0ed82bd2f --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcC$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcC$sp extends Function0[Any] with Serializable { + def apply$mcC$sp(): Char + override def apply(): Any = scala.runtime.BoxesRunTime.boxToCharacter(apply$mcC$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcD$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcD$sp.scala new file mode 100644 index 000000000000..21b9e0152222 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcD$sp extends Function0[Any] with Serializable { + def apply$mcD$sp(): Double + override def apply(): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcD$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcF$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcF$sp.scala new file mode 100644 index 000000000000..b90637f54c60 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcF$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcF$sp extends Function0[Any] with Serializable { + def apply$mcF$sp(): Float + override def apply(): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcF$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcI$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcI$sp.scala new file mode 100644 index 000000000000..dd4e0738f985 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcI$sp extends Function0[Any] with Serializable { + def apply$mcI$sp(): Int + override def apply(): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcI$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcJ$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcJ$sp.scala new file mode 100644 index 000000000000..25d340fa9aae --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcJ$sp extends Function0[Any] with Serializable { + def apply$mcJ$sp(): Long + override def apply(): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJ$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcS$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcS$sp.scala new file mode 100644 index 000000000000..44d2a6ae934f --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcS$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcS$sp extends Function0[Any] with Serializable { + def apply$mcS$sp(): Short + override def apply(): Any = scala.runtime.BoxesRunTime.boxToShort(apply$mcS$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcV$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcV$sp.scala new file mode 100644 index 000000000000..867d5cfe1052 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcV$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcV$sp extends Function0[Any] with Serializable { + def apply$mcV$sp(): Unit + override def apply(): Any = { + apply$mcV$sp() + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction0$mcZ$sp.scala b/library/src/scala/runtime/java8/JFunction0$mcZ$sp.scala new file mode 100644 index 000000000000..9f9389aa4445 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction0$mcZ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction0$mcZ$sp extends Function0[Any] with Serializable { + def apply$mcZ$sp(): Boolean + override def apply(): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZ$sp()) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcDD$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcDD$sp.scala new file mode 100644 index 000000000000..98491234c57c --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcDD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcDD$sp extends Function1[Any, Any] with Serializable { + def apply$mcDD$sp(v1: Double): Double + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDD$sp(scala.runtime.BoxesRunTime.unboxToDouble(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcDF$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcDF$sp.scala new file mode 100644 index 000000000000..702e7201ac7a --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcDF$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcDF$sp extends Function1[Any, Any] with Serializable { + def apply$mcDF$sp(v1: Float): Double + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDF$sp(scala.runtime.BoxesRunTime.unboxToFloat(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcDI$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcDI$sp.scala new file mode 100644 index 000000000000..3d1badf17393 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcDI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcDI$sp extends Function1[Any, Any] with Serializable { + def apply$mcDI$sp(v1: Int): Double + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDI$sp(scala.runtime.BoxesRunTime.unboxToInt(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcDJ$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcDJ$sp.scala new file mode 100644 index 000000000000..ce0c27f8f279 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcDJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcDJ$sp extends Function1[Any, Any] with Serializable { + def apply$mcDJ$sp(v1: Long): Double + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDJ$sp(scala.runtime.BoxesRunTime.unboxToLong(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcFD$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcFD$sp.scala new file mode 100644 index 000000000000..eb436da193bf --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcFD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcFD$sp extends Function1[Any, Any] with Serializable { + def apply$mcFD$sp(v1: Double): Float + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFD$sp(scala.runtime.BoxesRunTime.unboxToDouble(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcFF$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcFF$sp.scala new file mode 100644 index 000000000000..c7a253449554 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcFF$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcFF$sp extends Function1[Any, Any] with Serializable { + def apply$mcFF$sp(v1: Float): Float + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFF$sp(scala.runtime.BoxesRunTime.unboxToFloat(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcFI$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcFI$sp.scala new file mode 100644 index 000000000000..c77a5272ab66 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcFI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcFI$sp extends Function1[Any, Any] with Serializable { + def apply$mcFI$sp(v1: Int): Float + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFI$sp(scala.runtime.BoxesRunTime.unboxToInt(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcFJ$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcFJ$sp.scala new file mode 100644 index 000000000000..c0ceaae6856a --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcFJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcFJ$sp extends Function1[Any, Any] with Serializable { + def apply$mcFJ$sp(v1: Long): Float + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFJ$sp(scala.runtime.BoxesRunTime.unboxToLong(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcID$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcID$sp.scala new file mode 100644 index 000000000000..742b46d23602 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcID$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcID$sp extends Function1[Any, Any] with Serializable { + def apply$mcID$sp(v1: Double): Int + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcID$sp(scala.runtime.BoxesRunTime.unboxToDouble(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcIF$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcIF$sp.scala new file mode 100644 index 000000000000..d5dc57ea6d49 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcIF$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcIF$sp extends Function1[Any, Any] with Serializable { + def apply$mcIF$sp(v1: Float): Int + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIF$sp(scala.runtime.BoxesRunTime.unboxToFloat(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcII$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcII$sp.scala new file mode 100644 index 000000000000..1c84c49dccd9 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcII$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcII$sp extends Function1[Any, Any] with Serializable { + def apply$mcII$sp(v1: Int): Int + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcII$sp(scala.runtime.BoxesRunTime.unboxToInt(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcIJ$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcIJ$sp.scala new file mode 100644 index 000000000000..298afef64518 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcIJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcIJ$sp extends Function1[Any, Any] with Serializable { + def apply$mcIJ$sp(v1: Long): Int + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIJ$sp(scala.runtime.BoxesRunTime.unboxToLong(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcJD$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcJD$sp.scala new file mode 100644 index 000000000000..a315b0ca7e5c --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcJD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcJD$sp extends Function1[Any, Any] with Serializable { + def apply$mcJD$sp(v1: Double): Long + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJD$sp(scala.runtime.BoxesRunTime.unboxToDouble(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcJF$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcJF$sp.scala new file mode 100644 index 000000000000..b810c01df2be --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcJF$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcJF$sp extends Function1[Any, Any] with Serializable { + def apply$mcJF$sp(v1: Float): Long + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJF$sp(scala.runtime.BoxesRunTime.unboxToFloat(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcJI$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcJI$sp.scala new file mode 100644 index 000000000000..11a0e2c9513b --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcJI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcJI$sp extends Function1[Any, Any] with Serializable { + def apply$mcJI$sp(v1: Int): Long + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJI$sp(scala.runtime.BoxesRunTime.unboxToInt(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcJJ$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcJJ$sp.scala new file mode 100644 index 000000000000..e6388f802798 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcJJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcJJ$sp extends Function1[Any, Any] with Serializable { + def apply$mcJJ$sp(v1: Long): Long + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJJ$sp(scala.runtime.BoxesRunTime.unboxToLong(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcVD$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcVD$sp.scala new file mode 100644 index 000000000000..51b919af7bd6 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcVD$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcVD$sp extends Function1[Any, Any] with Serializable { + def apply$mcVD$sp(v1: Double): Unit + override def apply(t: Any): Any = { + apply$mcVD$sp(scala.runtime.BoxesRunTime.unboxToDouble(t)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcVF$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcVF$sp.scala new file mode 100644 index 000000000000..7c032068bd06 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcVF$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcVF$sp extends Function1[Any, Any] with Serializable { + def apply$mcVF$sp(v1: Float): Unit + override def apply(t: Any): Any = { + apply$mcVF$sp(scala.runtime.BoxesRunTime.unboxToFloat(t)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcVI$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcVI$sp.scala new file mode 100644 index 000000000000..06c78e4d4074 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcVI$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcVI$sp extends Function1[Any, Any] with Serializable { + def apply$mcVI$sp(v1: Int): Unit + override def apply(t: Any): Any = { + apply$mcVI$sp(scala.runtime.BoxesRunTime.unboxToInt(t)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcVJ$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcVJ$sp.scala new file mode 100644 index 000000000000..ade57aaad9ea --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcVJ$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcVJ$sp extends Function1[Any, Any] with Serializable { + def apply$mcVJ$sp(v1: Long): Unit + override def apply(t: Any): Any = { + apply$mcVJ$sp(scala.runtime.BoxesRunTime.unboxToLong(t)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcZD$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcZD$sp.scala new file mode 100644 index 000000000000..070c0fed73ee --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcZD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcZD$sp extends Function1[Any, Any] with Serializable { + def apply$mcZD$sp(v1: Double): Boolean + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZD$sp(scala.runtime.BoxesRunTime.unboxToDouble(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcZF$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcZF$sp.scala new file mode 100644 index 000000000000..af1cab6c44d4 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcZF$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcZF$sp extends Function1[Any, Any] with Serializable { + def apply$mcZF$sp(v1: Float): Boolean + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZF$sp(scala.runtime.BoxesRunTime.unboxToFloat(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcZI$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcZI$sp.scala new file mode 100644 index 000000000000..d9d5f5417b7b --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcZI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcZI$sp extends Function1[Any, Any] with Serializable { + def apply$mcZI$sp(v1: Int): Boolean + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZI$sp(scala.runtime.BoxesRunTime.unboxToInt(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction1$mcZJ$sp.scala b/library/src/scala/runtime/java8/JFunction1$mcZJ$sp.scala new file mode 100644 index 000000000000..b1ac4f5fb0c1 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction1$mcZJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction1$mcZJ$sp extends Function1[Any, Any] with Serializable { + def apply$mcZJ$sp(v1: Long): Boolean + override def apply(t: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZJ$sp(scala.runtime.BoxesRunTime.unboxToLong(t))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDDD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDDD$sp.scala new file mode 100644 index 000000000000..a7dba65aa372 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDDD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDDD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDDD$sp(v1: Double, v2: Double): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDDD$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDDI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDDI$sp.scala new file mode 100644 index 000000000000..f4dd19493eaa --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDDI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDDI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDDI$sp(v1: Double, v2: Int): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDDI$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDDJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDDJ$sp.scala new file mode 100644 index 000000000000..5ded95bd8910 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDDJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDDJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDDJ$sp(v1: Double, v2: Long): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDDJ$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDID$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDID$sp.scala new file mode 100644 index 000000000000..2aabd59d8c8e --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDID$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDID$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDID$sp(v1: Int, v2: Double): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDID$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDII$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDII$sp.scala new file mode 100644 index 000000000000..ad4467dbe07e --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDII$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDII$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDII$sp(v1: Int, v2: Int): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDII$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDIJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDIJ$sp.scala new file mode 100644 index 000000000000..4bc84d0b9a51 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDIJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDIJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDIJ$sp(v1: Int, v2: Long): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDIJ$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDJD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDJD$sp.scala new file mode 100644 index 000000000000..bc8f02173ad1 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDJD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDJD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDJD$sp(v1: Long, v2: Double): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDJD$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDJI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDJI$sp.scala new file mode 100644 index 000000000000..f139ad404716 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDJI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDJI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDJI$sp(v1: Long, v2: Int): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDJI$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcDJJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcDJJ$sp.scala new file mode 100644 index 000000000000..8d3b45df6e42 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcDJJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcDJJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcDJJ$sp(v1: Long, v2: Long): Double + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToDouble(apply$mcDJJ$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFDD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFDD$sp.scala new file mode 100644 index 000000000000..4381735c8973 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFDD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFDD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFDD$sp(v1: Double, v2: Double): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFDD$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFDI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFDI$sp.scala new file mode 100644 index 000000000000..e3e5b9a9026d --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFDI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFDI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFDI$sp(v1: Double, v2: Int): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFDI$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFDJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFDJ$sp.scala new file mode 100644 index 000000000000..1a26782f59dc --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFDJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFDJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFDJ$sp(v1: Double, v2: Long): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFDJ$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFID$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFID$sp.scala new file mode 100644 index 000000000000..08f8ac2872d6 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFID$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFID$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFID$sp(v1: Int, v2: Double): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFID$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFII$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFII$sp.scala new file mode 100644 index 000000000000..8a482dfa722f --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFII$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFII$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFII$sp(v1: Int, v2: Int): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFII$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFIJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFIJ$sp.scala new file mode 100644 index 000000000000..a3f02eb64c01 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFIJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFIJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFIJ$sp(v1: Int, v2: Long): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFIJ$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFJD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFJD$sp.scala new file mode 100644 index 000000000000..a78fed85f23d --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFJD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFJD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFJD$sp(v1: Long, v2: Double): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFJD$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFJI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFJI$sp.scala new file mode 100644 index 000000000000..52dd6c11fe40 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFJI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFJI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFJI$sp(v1: Long, v2: Int): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFJI$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcFJJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcFJJ$sp.scala new file mode 100644 index 000000000000..15e91de115c1 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcFJJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcFJJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcFJJ$sp(v1: Long, v2: Long): Float + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToFloat(apply$mcFJJ$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIDD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIDD$sp.scala new file mode 100644 index 000000000000..09f7188d5447 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIDD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIDD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIDD$sp(v1: Double, v2: Double): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIDD$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIDI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIDI$sp.scala new file mode 100644 index 000000000000..d53a99a61972 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIDI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIDI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIDI$sp(v1: Double, v2: Int): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIDI$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIDJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIDJ$sp.scala new file mode 100644 index 000000000000..eedd1db3df17 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIDJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIDJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIDJ$sp(v1: Double, v2: Long): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIDJ$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIID$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIID$sp.scala new file mode 100644 index 000000000000..067044482034 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIID$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIID$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIID$sp(v1: Int, v2: Double): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIID$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIII$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIII$sp.scala new file mode 100644 index 000000000000..c868fad4e8f5 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIII$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIII$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIII$sp(v1: Int, v2: Int): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIII$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIIJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIIJ$sp.scala new file mode 100644 index 000000000000..c23e514092aa --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIIJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIIJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIIJ$sp(v1: Int, v2: Long): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIIJ$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIJD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIJD$sp.scala new file mode 100644 index 000000000000..48f58e3d3859 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIJD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIJD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIJD$sp(v1: Long, v2: Double): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIJD$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIJI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIJI$sp.scala new file mode 100644 index 000000000000..c9ce9c1186d3 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIJI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIJI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIJI$sp(v1: Long, v2: Int): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIJI$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcIJJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcIJJ$sp.scala new file mode 100644 index 000000000000..7749b84ebefe --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcIJJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcIJJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcIJJ$sp(v1: Long, v2: Long): Int + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToInteger(apply$mcIJJ$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJDD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJDD$sp.scala new file mode 100644 index 000000000000..4e2885c0c8b1 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJDD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJDD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJDD$sp(v1: Double, v2: Double): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJDD$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJDI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJDI$sp.scala new file mode 100644 index 000000000000..29e102ae3e01 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJDI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJDI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJDI$sp(v1: Double, v2: Int): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJDI$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJDJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJDJ$sp.scala new file mode 100644 index 000000000000..537d83a4e9d5 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJDJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJDJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJDJ$sp(v1: Double, v2: Long): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJDJ$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJID$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJID$sp.scala new file mode 100644 index 000000000000..c943f1bbcd1e --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJID$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJID$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJID$sp(v1: Int, v2: Double): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJID$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJII$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJII$sp.scala new file mode 100644 index 000000000000..387d6424c8d1 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJII$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJII$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJII$sp(v1: Int, v2: Int): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJII$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJIJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJIJ$sp.scala new file mode 100644 index 000000000000..ef33074c5b5e --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJIJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJIJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJIJ$sp(v1: Int, v2: Long): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJIJ$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJJD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJJD$sp.scala new file mode 100644 index 000000000000..314930812281 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJJD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJJD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJJD$sp(v1: Long, v2: Double): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJJD$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJJI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJJI$sp.scala new file mode 100644 index 000000000000..87bc0de39256 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJJI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJJI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJJI$sp(v1: Long, v2: Int): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJJI$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcJJJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcJJJ$sp.scala new file mode 100644 index 000000000000..391582994fc6 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcJJJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcJJJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcJJJ$sp(v1: Long, v2: Long): Long + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToLong(apply$mcJJJ$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVDD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVDD$sp.scala new file mode 100644 index 000000000000..056fee1df387 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVDD$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVDD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVDD$sp(v1: Double, v2: Double): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVDD$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVDI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVDI$sp.scala new file mode 100644 index 000000000000..0683881a2923 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVDI$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVDI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVDI$sp(v1: Double, v2: Int): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVDI$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToInt(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVDJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVDJ$sp.scala new file mode 100644 index 000000000000..9a50555c1921 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVDJ$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVDJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVDJ$sp(v1: Double, v2: Long): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVDJ$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToLong(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVID$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVID$sp.scala new file mode 100644 index 000000000000..b0df076be14f --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVID$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVID$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVID$sp(v1: Int, v2: Double): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVID$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVII$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVII$sp.scala new file mode 100644 index 000000000000..822fa89df106 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVII$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVII$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVII$sp(v1: Int, v2: Int): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVII$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToInt(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVIJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVIJ$sp.scala new file mode 100644 index 000000000000..b922343162e6 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVIJ$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVIJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVIJ$sp(v1: Int, v2: Long): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVIJ$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToLong(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVJD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVJD$sp.scala new file mode 100644 index 000000000000..bb514d145017 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVJD$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVJD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVJD$sp(v1: Long, v2: Double): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVJD$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVJI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVJI$sp.scala new file mode 100644 index 000000000000..079e48276a7d --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVJI$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVJI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVJI$sp(v1: Long, v2: Int): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVJI$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToInt(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcVJJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcVJJ$sp.scala new file mode 100644 index 000000000000..4b80f04c9dba --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcVJJ$sp.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcVJJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcVJJ$sp(v1: Long, v2: Long): Unit + override def apply(v1: Any, v2: Any): Any = { + apply$mcVJJ$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToLong(v2)) + scala.runtime.BoxedUnit.UNIT + } +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZDD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZDD$sp.scala new file mode 100644 index 000000000000..0918660b802d --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZDD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZDD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZDD$sp(v1: Double, v2: Double): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZDD$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZDI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZDI$sp.scala new file mode 100644 index 000000000000..4514d78b8f3a --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZDI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZDI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZDI$sp(v1: Double, v2: Int): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZDI$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZDJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZDJ$sp.scala new file mode 100644 index 000000000000..daebfd82f041 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZDJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZDJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZDJ$sp(v1: Double, v2: Long): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZDJ$sp(scala.runtime.BoxesRunTime.unboxToDouble(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZID$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZID$sp.scala new file mode 100644 index 000000000000..c0a06d1c9373 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZID$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZID$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZID$sp(v1: Int, v2: Double): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZID$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZII$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZII$sp.scala new file mode 100644 index 000000000000..c49fea6a2543 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZII$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZII$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZII$sp(v1: Int, v2: Int): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZII$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZIJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZIJ$sp.scala new file mode 100644 index 000000000000..bbf003ebd17b --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZIJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZIJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZIJ$sp(v1: Int, v2: Long): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZIJ$sp(scala.runtime.BoxesRunTime.unboxToInt(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZJD$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZJD$sp.scala new file mode 100644 index 000000000000..ab08030714e2 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZJD$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZJD$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZJD$sp(v1: Long, v2: Double): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZJD$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToDouble(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZJI$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZJI$sp.scala new file mode 100644 index 000000000000..5f32af16f201 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZJI$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZJI$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZJI$sp(v1: Long, v2: Int): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZJI$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToInt(v2))) +} diff --git a/library/src/scala/runtime/java8/JFunction2$mcZJJ$sp.scala b/library/src/scala/runtime/java8/JFunction2$mcZJJ$sp.scala new file mode 100644 index 000000000000..b588644fb0c4 --- /dev/null +++ b/library/src/scala/runtime/java8/JFunction2$mcZJJ$sp.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime.java8 + +@FunctionalInterface trait JFunction2$mcZJJ$sp extends Function2[Any, Any, Any] with Serializable { + def apply$mcZJJ$sp(v1: Long, v2: Long): Boolean + override def apply(v1: Any, v2: Any): Any = scala.runtime.BoxesRunTime.boxToBoolean(apply$mcZJJ$sp(scala.runtime.BoxesRunTime.unboxToLong(v1), scala.runtime.BoxesRunTime.unboxToLong(v2))) +} From 2abbfacf6a97485287a6d0ec965641feceeb7dcb Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:40:26 +0100 Subject: [PATCH 17/28] chore: add all the files in scala.runtime --- .../src/scala/runtime/AbstractFunction0.scala | 19 + .../src/scala/runtime/AbstractFunction1.scala | 19 + .../scala/runtime/AbstractFunction10.scala | 19 + .../scala/runtime/AbstractFunction11.scala | 19 + .../scala/runtime/AbstractFunction12.scala | 19 + .../scala/runtime/AbstractFunction13.scala | 19 + .../scala/runtime/AbstractFunction14.scala | 19 + .../scala/runtime/AbstractFunction15.scala | 19 + .../scala/runtime/AbstractFunction16.scala | 19 + .../scala/runtime/AbstractFunction17.scala | 19 + .../scala/runtime/AbstractFunction18.scala | 19 + .../scala/runtime/AbstractFunction19.scala | 19 + .../src/scala/runtime/AbstractFunction2.scala | 19 + .../scala/runtime/AbstractFunction20.scala | 19 + .../scala/runtime/AbstractFunction21.scala | 19 + .../scala/runtime/AbstractFunction22.scala | 19 + .../src/scala/runtime/AbstractFunction3.scala | 19 + .../src/scala/runtime/AbstractFunction4.scala | 19 + .../src/scala/runtime/AbstractFunction5.scala | 19 + .../src/scala/runtime/AbstractFunction6.scala | 19 + .../src/scala/runtime/AbstractFunction7.scala | 19 + .../src/scala/runtime/AbstractFunction8.scala | 19 + .../src/scala/runtime/AbstractFunction9.scala | 19 + .../runtime/AbstractPartialFunction.scala | 36 + .../src/scala/runtime/ArrayCharSequence.scala | 47 ++ library/src/scala/runtime/BooleanRef.java | 24 + library/src/scala/runtime/BoxedUnit.java | 38 + library/src/scala/runtime/BoxesRunTime.java | 760 ++++++++++++++++++ library/src/scala/runtime/ByteRef.java | 24 + library/src/scala/runtime/CharRef.java | 24 + .../src/scala/runtime/ClassValueCompat.scala | 53 ++ library/src/scala/runtime/DoubleRef.java | 24 + library/src/scala/runtime/FloatRef.java | 24 + library/src/scala/runtime/IntRef.java | 24 + .../src/scala/runtime/LambdaDeserialize.scala | 44 + .../scala/runtime/LambdaDeserializer.scala | 129 +++ library/src/scala/runtime/LazyRef.scala | 171 ++++ library/src/scala/runtime/LongRef.java | 24 + library/src/scala/runtime/MethodCache.scala | 87 ++ .../scala/runtime/MethodHandleConstants.java | 35 + .../runtime/ModuleSerializationProxy.scala | 43 + .../scala/runtime/NonLocalReturnControl.scala | 21 + library/src/scala/runtime/Nothing$.scala | 22 + library/src/scala/runtime/Null$.scala | 22 + library/src/scala/runtime/ObjectRef.java | 25 + library/src/scala/runtime/PStatics.scala | 21 + library/src/scala/runtime/RichBoolean.scala | 19 + library/src/scala/runtime/RichByte.scala | 35 + library/src/scala/runtime/RichChar.scala | 70 ++ library/src/scala/runtime/RichDouble.scala | 70 ++ library/src/scala/runtime/RichFloat.scala | 71 ++ library/src/scala/runtime/RichInt.scala | 87 ++ library/src/scala/runtime/RichLong.scala | 49 ++ library/src/scala/runtime/RichShort.scala | 35 + .../src/scala/runtime/ScalaNumberProxy.scala | 87 ++ library/src/scala/runtime/ScalaRunTime.scala | 295 +++++++ library/src/scala/runtime/ShortRef.java | 24 + library/src/scala/runtime/Static.java | 25 + library/src/scala/runtime/Statics.java | 199 +++++ .../scala/runtime/StructuralCallSite.scala | 46 ++ library/src/scala/runtime/SymbolLiteral.java | 31 + library/src/scala/runtime/Tuple2Zipped.scala | 144 ++++ library/src/scala/runtime/Tuple3Zipped.scala | 156 ++++ .../src/scala/runtime/VolatileBooleanRef.java | 24 + .../src/scala/runtime/VolatileByteRef.java | 24 + .../src/scala/runtime/VolatileCharRef.java | 24 + .../src/scala/runtime/VolatileDoubleRef.java | 24 + .../src/scala/runtime/VolatileFloatRef.java | 24 + library/src/scala/runtime/VolatileIntRef.java | 24 + .../src/scala/runtime/VolatileLongRef.java | 24 + .../src/scala/runtime/VolatileObjectRef.java | 25 + .../src/scala/runtime/VolatileShortRef.java | 24 + 72 files changed, 3819 insertions(+) create mode 100644 library/src/scala/runtime/AbstractFunction0.scala create mode 100644 library/src/scala/runtime/AbstractFunction1.scala create mode 100644 library/src/scala/runtime/AbstractFunction10.scala create mode 100644 library/src/scala/runtime/AbstractFunction11.scala create mode 100644 library/src/scala/runtime/AbstractFunction12.scala create mode 100644 library/src/scala/runtime/AbstractFunction13.scala create mode 100644 library/src/scala/runtime/AbstractFunction14.scala create mode 100644 library/src/scala/runtime/AbstractFunction15.scala create mode 100644 library/src/scala/runtime/AbstractFunction16.scala create mode 100644 library/src/scala/runtime/AbstractFunction17.scala create mode 100644 library/src/scala/runtime/AbstractFunction18.scala create mode 100644 library/src/scala/runtime/AbstractFunction19.scala create mode 100644 library/src/scala/runtime/AbstractFunction2.scala create mode 100644 library/src/scala/runtime/AbstractFunction20.scala create mode 100644 library/src/scala/runtime/AbstractFunction21.scala create mode 100644 library/src/scala/runtime/AbstractFunction22.scala create mode 100644 library/src/scala/runtime/AbstractFunction3.scala create mode 100644 library/src/scala/runtime/AbstractFunction4.scala create mode 100644 library/src/scala/runtime/AbstractFunction5.scala create mode 100644 library/src/scala/runtime/AbstractFunction6.scala create mode 100644 library/src/scala/runtime/AbstractFunction7.scala create mode 100644 library/src/scala/runtime/AbstractFunction8.scala create mode 100644 library/src/scala/runtime/AbstractFunction9.scala create mode 100644 library/src/scala/runtime/AbstractPartialFunction.scala create mode 100644 library/src/scala/runtime/ArrayCharSequence.scala create mode 100644 library/src/scala/runtime/BooleanRef.java create mode 100644 library/src/scala/runtime/BoxedUnit.java create mode 100644 library/src/scala/runtime/BoxesRunTime.java create mode 100644 library/src/scala/runtime/ByteRef.java create mode 100644 library/src/scala/runtime/CharRef.java create mode 100644 library/src/scala/runtime/ClassValueCompat.scala create mode 100644 library/src/scala/runtime/DoubleRef.java create mode 100644 library/src/scala/runtime/FloatRef.java create mode 100644 library/src/scala/runtime/IntRef.java create mode 100644 library/src/scala/runtime/LambdaDeserialize.scala create mode 100644 library/src/scala/runtime/LambdaDeserializer.scala create mode 100644 library/src/scala/runtime/LazyRef.scala create mode 100644 library/src/scala/runtime/LongRef.java create mode 100644 library/src/scala/runtime/MethodCache.scala create mode 100644 library/src/scala/runtime/MethodHandleConstants.java create mode 100644 library/src/scala/runtime/ModuleSerializationProxy.scala create mode 100644 library/src/scala/runtime/NonLocalReturnControl.scala create mode 100644 library/src/scala/runtime/Nothing$.scala create mode 100644 library/src/scala/runtime/Null$.scala create mode 100644 library/src/scala/runtime/ObjectRef.java create mode 100644 library/src/scala/runtime/PStatics.scala create mode 100644 library/src/scala/runtime/RichBoolean.scala create mode 100644 library/src/scala/runtime/RichByte.scala create mode 100644 library/src/scala/runtime/RichChar.scala create mode 100644 library/src/scala/runtime/RichDouble.scala create mode 100644 library/src/scala/runtime/RichFloat.scala create mode 100644 library/src/scala/runtime/RichInt.scala create mode 100644 library/src/scala/runtime/RichLong.scala create mode 100644 library/src/scala/runtime/RichShort.scala create mode 100644 library/src/scala/runtime/ScalaNumberProxy.scala create mode 100644 library/src/scala/runtime/ScalaRunTime.scala create mode 100644 library/src/scala/runtime/ShortRef.java create mode 100644 library/src/scala/runtime/Static.java create mode 100644 library/src/scala/runtime/Statics.java create mode 100644 library/src/scala/runtime/StructuralCallSite.scala create mode 100644 library/src/scala/runtime/SymbolLiteral.java create mode 100644 library/src/scala/runtime/Tuple2Zipped.scala create mode 100644 library/src/scala/runtime/Tuple3Zipped.scala create mode 100644 library/src/scala/runtime/VolatileBooleanRef.java create mode 100644 library/src/scala/runtime/VolatileByteRef.java create mode 100644 library/src/scala/runtime/VolatileCharRef.java create mode 100644 library/src/scala/runtime/VolatileDoubleRef.java create mode 100644 library/src/scala/runtime/VolatileFloatRef.java create mode 100644 library/src/scala/runtime/VolatileIntRef.java create mode 100644 library/src/scala/runtime/VolatileLongRef.java create mode 100644 library/src/scala/runtime/VolatileObjectRef.java create mode 100644 library/src/scala/runtime/VolatileShortRef.java diff --git a/library/src/scala/runtime/AbstractFunction0.scala b/library/src/scala/runtime/AbstractFunction0.scala new file mode 100644 index 000000000000..c322efcd6281 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction0.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction0[@specialized(Specializable.Primitives) +R] extends Function0[R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction1.scala b/library/src/scala/runtime/AbstractFunction1.scala new file mode 100644 index 000000000000..49977d8a1393 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction1.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction1[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends Function1[T1, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction10.scala b/library/src/scala/runtime/AbstractFunction10.scala new file mode 100644 index 000000000000..121c3cc0d53b --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction10.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction11.scala b/library/src/scala/runtime/AbstractFunction11.scala new file mode 100644 index 000000000000..c4321c10f142 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction11.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] extends Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction12.scala b/library/src/scala/runtime/AbstractFunction12.scala new file mode 100644 index 000000000000..3f6d666f9c42 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction12.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R] extends Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction13.scala b/library/src/scala/runtime/AbstractFunction13.scala new file mode 100644 index 000000000000..264de0f87296 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction13.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R] extends Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction14.scala b/library/src/scala/runtime/AbstractFunction14.scala new file mode 100644 index 000000000000..1e92d1e04bae --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction14.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R] extends Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction15.scala b/library/src/scala/runtime/AbstractFunction15.scala new file mode 100644 index 000000000000..ed9b6b187e39 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction15.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R] extends Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction16.scala b/library/src/scala/runtime/AbstractFunction16.scala new file mode 100644 index 000000000000..839efed863b6 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction16.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R] extends Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction17.scala b/library/src/scala/runtime/AbstractFunction17.scala new file mode 100644 index 000000000000..ee91b466ea5b --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction17.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R] extends Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction18.scala b/library/src/scala/runtime/AbstractFunction18.scala new file mode 100644 index 000000000000..83aaf6b10c44 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction18.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R] extends Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction19.scala b/library/src/scala/runtime/AbstractFunction19.scala new file mode 100644 index 000000000000..93741656a585 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction19.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R] extends Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction2.scala b/library/src/scala/runtime/AbstractFunction2.scala new file mode 100644 index 000000000000..7c8d1628e545 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction2.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction2[@specialized(Specializable.Args) -T1, @specialized(Specializable.Args) -T2, @specialized(Specializable.Return) +R] extends Function2[T1, T2, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction20.scala b/library/src/scala/runtime/AbstractFunction20.scala new file mode 100644 index 000000000000..b2858b27c125 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction20.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R] extends Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction21.scala b/library/src/scala/runtime/AbstractFunction21.scala new file mode 100644 index 000000000000..e36e6b043959 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction21.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R] extends Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction22.scala b/library/src/scala/runtime/AbstractFunction22.scala new file mode 100644 index 000000000000..f9cf63a9542d --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction22.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R] extends Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction3.scala b/library/src/scala/runtime/AbstractFunction3.scala new file mode 100644 index 000000000000..fbeb3e7b1cf8 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction3.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction3[-T1, -T2, -T3, +R] extends Function3[T1, T2, T3, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction4.scala b/library/src/scala/runtime/AbstractFunction4.scala new file mode 100644 index 000000000000..9a91280eea52 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction4.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction4[-T1, -T2, -T3, -T4, +R] extends Function4[T1, T2, T3, T4, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction5.scala b/library/src/scala/runtime/AbstractFunction5.scala new file mode 100644 index 000000000000..a7880cdb0a14 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction5.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction5[-T1, -T2, -T3, -T4, -T5, +R] extends Function5[T1, T2, T3, T4, T5, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction6.scala b/library/src/scala/runtime/AbstractFunction6.scala new file mode 100644 index 000000000000..0a8c4eeacc70 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction6.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction6[-T1, -T2, -T3, -T4, -T5, -T6, +R] extends Function6[T1, T2, T3, T4, T5, T6, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction7.scala b/library/src/scala/runtime/AbstractFunction7.scala new file mode 100644 index 000000000000..d0f18b0dcbd4 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction7.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends Function7[T1, T2, T3, T4, T5, T6, T7, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction8.scala b/library/src/scala/runtime/AbstractFunction8.scala new file mode 100644 index 000000000000..4f1a528a4ed9 --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction8.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends Function8[T1, T2, T3, T4, T5, T6, T7, T8, R] { + +} diff --git a/library/src/scala/runtime/AbstractFunction9.scala b/library/src/scala/runtime/AbstractFunction9.scala new file mode 100644 index 000000000000..f4ebb395bd5a --- /dev/null +++ b/library/src/scala/runtime/AbstractFunction9.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala.runtime + +abstract class AbstractFunction9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] { + +} diff --git a/library/src/scala/runtime/AbstractPartialFunction.scala b/library/src/scala/runtime/AbstractPartialFunction.scala new file mode 100644 index 000000000000..f4e8ae1b7818 --- /dev/null +++ b/library/src/scala/runtime/AbstractPartialFunction.scala @@ -0,0 +1,36 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +/** `AbstractPartialFunction` reformulates all operations of its supertrait `PartialFunction` + * in terms of `isDefinedAt` and `applyOrElse`. + * + * This allows more efficient implementations in many cases: + * - optimized `orElse` method supports chained `orElse` in linear time, + * and with no slow-down if the `orElse` part is not needed. + * - optimized `lift` method helps to avoid double evaluation of pattern matchers & guards + * of partial function literals. + * + * This trait is used as a basis for implementation of all partial function literals. + */ +abstract class AbstractPartialFunction[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends Function1[T1, R] with PartialFunction[T1, R] { self => + // this method must be overridden for better performance, + // for backwards compatibility, fall back to the one inherited from PartialFunction + // this assumes the old-school partial functions override the apply method, though + // override def applyOrElse[A1 <: T1, B1 >: R](x: A1, default: A1 => B1): B1 = ??? + + // probably okay to make final since classes compiled before have overridden against the old version of AbstractPartialFunction + // let's not make it final so as not to confuse anyone + /*final*/ def apply(x: T1): R = applyOrElse(x, PartialFunction.empty) +} diff --git a/library/src/scala/runtime/ArrayCharSequence.scala b/library/src/scala/runtime/ArrayCharSequence.scala new file mode 100644 index 000000000000..971b0ac24c0d --- /dev/null +++ b/library/src/scala/runtime/ArrayCharSequence.scala @@ -0,0 +1,47 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +// Still need this one since the implicit class ArrayCharSequence only converts +// a single argument. +final class ArrayCharSequence(val xs: Array[Char], start: Int, end: Int) extends CharSequence { + // yikes + // java.lang.VerifyError: (class: scala/runtime/ArrayCharSequence, method: signature: ([C)V) + // Constructor must call super() or this() + // + // def this(xs: Array[Char]) = this(xs, 0, xs.length) + + def length: Int = math.max(0, end - start) + def charAt(index: Int): Char = { + if (0 <= index && index < length) + xs(start + index) + else throw new ArrayIndexOutOfBoundsException(s"$index is out of bounds (min 0, max ${xs.length - 1})") + } + def subSequence(start0: Int, end0: Int): CharSequence = { + if (start0 < 0) throw new ArrayIndexOutOfBoundsException(s"$start0 is out of bounds (min 0, max ${length -1})") + else if (end0 > length) throw new ArrayIndexOutOfBoundsException(s"$end0 is out of bounds (min 0, max ${xs.length -1})") + else if (end0 <= start0) new ArrayCharSequence(xs, 0, 0) + else { + val newlen = end0 - start0 + val start1 = start + start0 + new ArrayCharSequence(xs, start1, start1 + newlen) + } + } + override def toString = { + val start = math.max(this.start, 0) + val end = math.min(xs.length, start + length) + + if (start >= end) "" else new String(xs, start, end - start) + } +} diff --git a/library/src/scala/runtime/BooleanRef.java b/library/src/scala/runtime/BooleanRef.java new file mode 100644 index 000000000000..2c43fd719366 --- /dev/null +++ b/library/src/scala/runtime/BooleanRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class BooleanRef implements java.io.Serializable { + private static final long serialVersionUID = -5730524563015615974L; + + public boolean elem; + public BooleanRef(boolean elem) { this.elem = elem; } + public String toString() { return String.valueOf(elem); } + + public static BooleanRef create(boolean e) { return new BooleanRef(e); } + public static BooleanRef zero() { return new BooleanRef(false); } +} diff --git a/library/src/scala/runtime/BoxedUnit.java b/library/src/scala/runtime/BoxedUnit.java new file mode 100644 index 000000000000..aaa986f87f1a --- /dev/null +++ b/library/src/scala/runtime/BoxedUnit.java @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + + +public final class BoxedUnit implements java.io.Serializable { + private static final long serialVersionUID = 8405543498931817370L; + + public final static BoxedUnit UNIT = new BoxedUnit(); + + public final static Class TYPE = java.lang.Void.TYPE; + + private Object readResolve() { return UNIT; } + + private BoxedUnit() { } + + public boolean equals(java.lang.Object other) { + return this == other; + } + + public int hashCode() { + return 0; + } + + public String toString() { + return "()"; + } +} diff --git a/library/src/scala/runtime/BoxesRunTime.java b/library/src/scala/runtime/BoxesRunTime.java new file mode 100644 index 000000000000..3ddc2516fbb9 --- /dev/null +++ b/library/src/scala/runtime/BoxesRunTime.java @@ -0,0 +1,760 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +import scala.math.ScalaNumber; + +/** An object (static class) that defines methods used for creating, + * reverting, and calculating with, boxed values. There are four classes + * of methods in this object: + * - Convenience boxing methods which call the static valueOf method + * on the boxed class, thus utilizing the JVM boxing cache. + * - Convenience unboxing methods returning default value on null. + * - The generalised comparison method to be used when an object may + * be a boxed value. + * - Standard value operators for boxed number and quasi-number values. + */ +public final class BoxesRunTime +{ + private static final int CHAR = 0, /* BYTE = 1, SHORT = 2, */ INT = 3, LONG = 4, FLOAT = 5, DOUBLE = 6, OTHER = 7; + + /** We don't need to return BYTE and SHORT, as everything which might + * care widens to INT. + */ + private static int typeCode(Object a) { + if (a instanceof java.lang.Integer) return INT; + if (a instanceof java.lang.Double) return DOUBLE; + if (a instanceof java.lang.Long) return LONG; + if (a instanceof java.lang.Character) return CHAR; + if (a instanceof java.lang.Float) return FLOAT; + if ((a instanceof java.lang.Byte) || (a instanceof java.lang.Short)) return INT; + return OTHER; + } + +/* BOXING ... BOXING ... BOXING ... BOXING ... BOXING ... BOXING ... BOXING ... BOXING */ + + public static java.lang.Boolean boxToBoolean(boolean b) { + return java.lang.Boolean.valueOf(b); + } + + public static java.lang.Character boxToCharacter(char c) { + return java.lang.Character.valueOf(c); + } + + public static java.lang.Byte boxToByte(byte b) { + return java.lang.Byte.valueOf(b); + } + + public static java.lang.Short boxToShort(short s) { + return java.lang.Short.valueOf(s); + } + + public static java.lang.Integer boxToInteger(int i) { + return java.lang.Integer.valueOf(i); + } + + public static java.lang.Long boxToLong(long l) { + return java.lang.Long.valueOf(l); + } + + public static java.lang.Float boxToFloat(float f) { + return java.lang.Float.valueOf(f); + } + + public static java.lang.Double boxToDouble(double d) { + // System.out.println("box " + d); + // (new Throwable()).printStackTrace(); + return java.lang.Double.valueOf(d); + } + +/* UNBOXING ... UNBOXING ... UNBOXING ... UNBOXING ... UNBOXING ... UNBOXING ... UNBOXING */ + + public static boolean unboxToBoolean(Object b) { + return b == null ? false : ((java.lang.Boolean)b).booleanValue(); + } + + public static char unboxToChar(Object c) { + return c == null ? 0 : ((java.lang.Character)c).charValue(); + } + + public static byte unboxToByte(Object b) { + return b == null ? 0 : ((java.lang.Byte)b).byteValue(); + } + + public static short unboxToShort(Object s) { + return s == null ? 0 : ((java.lang.Short)s).shortValue(); + } + + public static int unboxToInt(Object i) { + return i == null ? 0 : ((java.lang.Integer)i).intValue(); + } + + public static long unboxToLong(Object l) { + return l == null ? 0 : ((java.lang.Long)l).longValue(); + } + + public static float unboxToFloat(Object f) { + return f == null ? 0.0f : ((java.lang.Float)f).floatValue(); + } + + public static double unboxToDouble(Object d) { + // System.out.println("unbox " + d); + return d == null ? 0.0d : ((java.lang.Double)d).doubleValue(); + } + + /* COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON */ + + public static boolean equals(Object x, Object y) { + if (x == y) return true; + return equals2(x, y); + } + + /** Since all applicable logic has to be present in the equals method of a ScalaNumber + * in any case, we dispatch to it as soon as we spot one on either side. + */ + public static boolean equals2(Object x, Object y) { + if (x instanceof java.lang.Number) + return equalsNumObject((java.lang.Number)x, y); + if (x instanceof java.lang.Character) + return equalsCharObject((java.lang.Character)x, y); + if (x == null) + return y == null; + + return x.equals(y); + } + + public static boolean equalsNumObject(java.lang.Number xn, Object y) { + if (y instanceof java.lang.Number) + return equalsNumNum(xn, (java.lang.Number)y); + if (y instanceof java.lang.Character) + return equalsNumChar(xn, (java.lang.Character)y); + if (xn == null) + return y == null; + + return xn.equals(y); + } + + public static boolean equalsNumNum(java.lang.Number xn, java.lang.Number yn) { + int xcode = typeCode(xn); + int ycode = typeCode(yn); + switch (ycode > xcode ? ycode : xcode) { + case INT: + return xn.intValue() == yn.intValue(); + case LONG: + return xn.longValue() == yn.longValue(); + case FLOAT: + return xn.floatValue() == yn.floatValue(); + case DOUBLE: + return xn.doubleValue() == yn.doubleValue(); + default: + if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber)) + return yn.equals(xn); + } + if (xn == null) + return yn == null; + + return xn.equals(yn); + } + + public static boolean equalsCharObject(java.lang.Character xc, Object y) { + if (y instanceof java.lang.Character) + return xc.charValue() == ((java.lang.Character)y).charValue(); + if (y instanceof java.lang.Number) + return equalsNumChar((java.lang.Number)y, xc); + if (xc == null) + return y == null; + + return xc.equals(y); + } + + public static boolean equalsNumChar(java.lang.Number xn, java.lang.Character yc) { + if (yc == null) + return xn == null; + + char ch = yc.charValue(); + switch (typeCode(xn)) { + case INT: + return xn.intValue() == ch; + case LONG: + return xn.longValue() == ch; + case FLOAT: + return xn.floatValue() == ch; + case DOUBLE: + return xn.doubleValue() == ch; + default: + return xn.equals(yc); + } + } + + private static int unboxCharOrInt(Object arg1, int code) { + if (code == CHAR) + return ((java.lang.Character) arg1).charValue(); + else + return ((java.lang.Number) arg1).intValue(); + } + private static long unboxCharOrLong(Object arg1, int code) { + if (code == CHAR) + return ((java.lang.Character) arg1).charValue(); + else + return ((java.lang.Number) arg1).longValue(); + } + private static float unboxCharOrFloat(Object arg1, int code) { + if (code == CHAR) + return ((java.lang.Character) arg1).charValue(); + else + return ((java.lang.Number) arg1).floatValue(); + } + private static double unboxCharOrDouble(Object arg1, int code) { + if (code == CHAR) + return ((java.lang.Character) arg1).charValue(); + else + return ((java.lang.Number) arg1).doubleValue(); + } + +/* OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS */ + + /** arg1 + arg2 */ + public static Object add(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + if (maxcode <= INT) { + return boxToInteger(unboxCharOrInt(arg1, code1) + unboxCharOrInt(arg2, code2)); + } + if (maxcode <= LONG) { + return boxToLong(unboxCharOrLong(arg1, code1) + unboxCharOrLong(arg2, code2)); + } + if (maxcode <= FLOAT) { + return boxToFloat(unboxCharOrFloat(arg1, code1) + unboxCharOrFloat(arg2, code2)); + } + if (maxcode <= DOUBLE) { + return boxToDouble(unboxCharOrDouble(arg1, code1) + unboxCharOrDouble(arg2, code2)); + } + throw new NoSuchMethodException(); + } + + /** arg1 - arg2 */ + public static Object subtract(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + if (maxcode <= INT) { + return boxToInteger(unboxCharOrInt(arg1, code1) - unboxCharOrInt(arg2, code2)); + } + if (maxcode <= LONG) { + return boxToLong(unboxCharOrLong(arg1, code1) - unboxCharOrLong(arg2, code2)); + } + if (maxcode <= FLOAT) { + return boxToFloat(unboxCharOrFloat(arg1, code1) - unboxCharOrFloat(arg2, code2)); + } + if (maxcode <= DOUBLE) { + return boxToDouble(unboxCharOrDouble(arg1, code1) - unboxCharOrDouble(arg2, code2)); + } + throw new NoSuchMethodException(); + } + + /** arg1 * arg2 */ + public static Object multiply(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + if (maxcode <= INT) { + return boxToInteger(unboxCharOrInt(arg1, code1) * unboxCharOrInt(arg2, code2)); + } + if (maxcode <= LONG) { + return boxToLong(unboxCharOrLong(arg1, code1) * unboxCharOrLong(arg2, code2)); + } + if (maxcode <= FLOAT) { + return boxToFloat(unboxCharOrFloat(arg1, code1) * unboxCharOrFloat(arg2, code2)); + } + if (maxcode <= DOUBLE) { + return boxToDouble(unboxCharOrDouble(arg1, code1) * unboxCharOrDouble(arg2, code2)); + } + throw new NoSuchMethodException(); + } + + /** arg1 / arg2 */ + public static Object divide(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + + if (maxcode <= INT) + return boxToInteger(unboxCharOrInt(arg1, code1) / unboxCharOrInt(arg2, code2)); + if (maxcode <= LONG) + return boxToLong(unboxCharOrLong(arg1, code1) / unboxCharOrLong(arg2, code2)); + if (maxcode <= FLOAT) + return boxToFloat(unboxCharOrFloat(arg1, code1) / unboxCharOrFloat(arg2, code2)); + if (maxcode <= DOUBLE) + return boxToDouble(unboxCharOrDouble(arg1, code1) / unboxCharOrDouble(arg2, code2)); + + throw new NoSuchMethodException(); + } + + /** arg1 % arg2 */ + public static Object takeModulo(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + + if (maxcode <= INT) + return boxToInteger(unboxCharOrInt(arg1, code1) % unboxCharOrInt(arg2, code2)); + if (maxcode <= LONG) + return boxToLong(unboxCharOrLong(arg1, code1) % unboxCharOrLong(arg2, code2)); + if (maxcode <= FLOAT) + return boxToFloat(unboxCharOrFloat(arg1, code1) % unboxCharOrFloat(arg2, code2)); + if (maxcode <= DOUBLE) + return boxToDouble(unboxCharOrDouble(arg1, code1) % unboxCharOrDouble(arg2, code2)); + + throw new NoSuchMethodException(); + } + + /** arg1 >> arg2 */ + public static Object shiftSignedRight(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + if (code1 <= INT) { + int val1 = unboxCharOrInt(arg1, code1); + if (code2 <= INT) { + int val2 = unboxCharOrInt(arg2, code2); + return boxToInteger(val1 >> val2); + } + if (code2 <= LONG) { + long val2 = unboxCharOrLong(arg2, code2); + return boxToInteger(val1 >> val2); + } + } + if (code1 <= LONG) { + long val1 = unboxCharOrLong(arg1, code1); + if (code2 <= INT) { + int val2 = unboxCharOrInt(arg2, code2); + return boxToLong(val1 >> val2); + } + if (code2 <= LONG) { + long val2 = unboxCharOrLong(arg2, code2); + return boxToLong(val1 >> val2); + } + } + throw new NoSuchMethodException(); + } + + /** arg1 << arg2 */ + public static Object shiftSignedLeft(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + if (code1 <= INT) { + int val1 = unboxCharOrInt(arg1, code1); + if (code2 <= INT) { + int val2 = unboxCharOrInt(arg2, code2); + return boxToInteger(val1 << val2); + } + if (code2 <= LONG) { + long val2 = unboxCharOrLong(arg2, code2); + return boxToInteger(val1 << val2); + } + } + if (code1 <= LONG) { + long val1 = unboxCharOrLong(arg1, code1); + if (code2 <= INT) { + int val2 = unboxCharOrInt(arg2, code2); + return boxToLong(val1 << val2); + } + if (code2 <= LONG) { + long val2 = unboxCharOrLong(arg2, code2); + return boxToLong(val1 << val2); + } + } + throw new NoSuchMethodException(); + } + + /** arg1 >>> arg2 */ + public static Object shiftLogicalRight(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + if (code1 <= INT) { + int val1 = unboxCharOrInt(arg1, code1); + if (code2 <= INT) { + int val2 = unboxCharOrInt(arg2, code2); + return boxToInteger(val1 >>> val2); + } + if (code2 <= LONG) { + long val2 = unboxCharOrLong(arg2, code2); + return boxToInteger(val1 >>> val2); + } + } + if (code1 <= LONG) { + long val1 = unboxCharOrLong(arg1, code1); + if (code2 <= INT) { + int val2 = unboxCharOrInt(arg2, code2); + return boxToLong(val1 >>> val2); + } + if (code2 <= LONG) { + long val2 = unboxCharOrLong(arg2, code2); + return boxToLong(val1 >>> val2); + } + } + throw new NoSuchMethodException(); + } + + /** -arg */ + public static Object negate(Object arg) throws NoSuchMethodException { + int code = typeCode(arg); + if (code <= INT) { + int val = unboxCharOrInt(arg, code); + return boxToInteger(-val); + } + if (code <= LONG) { + long val = unboxCharOrLong(arg, code); + return boxToLong(-val); + } + if (code <= FLOAT) { + float val = unboxCharOrFloat(arg, code); + return boxToFloat(-val); + } + if (code <= DOUBLE) { + double val = unboxCharOrDouble(arg, code); + return boxToDouble(-val); + } + throw new NoSuchMethodException(); + } + + /** +arg */ + public static Object positive(Object arg) throws NoSuchMethodException { + int code = typeCode(arg); + if (code <= INT) { + return boxToInteger(+unboxCharOrInt(arg, code)); + } + if (code <= LONG) { + return boxToLong(+unboxCharOrLong(arg, code)); + } + if (code <= FLOAT) { + return boxToFloat(+unboxCharOrFloat(arg, code)); + } + if (code <= DOUBLE) { + return boxToDouble(+unboxCharOrDouble(arg, code)); + } + throw new NoSuchMethodException(); + } + + /** arg1 & arg2 */ + public static Object takeAnd(Object arg1, Object arg2) throws NoSuchMethodException { + if ((arg1 instanceof Boolean) || (arg2 instanceof Boolean)) { + if ((arg1 instanceof Boolean) && (arg2 instanceof Boolean)) + return boxToBoolean(((java.lang.Boolean) arg1).booleanValue() & ((java.lang.Boolean) arg2).booleanValue()); + else + throw new NoSuchMethodException(); + } + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + + if (maxcode <= INT) + return boxToInteger(unboxCharOrInt(arg1, code1) & unboxCharOrInt(arg2, code2)); + if (maxcode <= LONG) + return boxToLong(unboxCharOrLong(arg1, code1) & unboxCharOrLong(arg2, code2)); + + throw new NoSuchMethodException(); + } + + /** arg1 | arg2 */ + public static Object takeOr(Object arg1, Object arg2) throws NoSuchMethodException { + if ((arg1 instanceof Boolean) || (arg2 instanceof Boolean)) { + if ((arg1 instanceof Boolean) && (arg2 instanceof Boolean)) + return boxToBoolean(((java.lang.Boolean) arg1).booleanValue() | ((java.lang.Boolean) arg2).booleanValue()); + else + throw new NoSuchMethodException(); + } + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + + if (maxcode <= INT) + return boxToInteger(unboxCharOrInt(arg1, code1) | unboxCharOrInt(arg2, code2)); + if (maxcode <= LONG) + return boxToLong(unboxCharOrLong(arg1, code1) | unboxCharOrLong(arg2, code2)); + + throw new NoSuchMethodException(); + } + + /** arg1 ^ arg2 */ + public static Object takeXor(Object arg1, Object arg2) throws NoSuchMethodException { + if ((arg1 instanceof Boolean) || (arg2 instanceof Boolean)) { + if ((arg1 instanceof Boolean) && (arg2 instanceof Boolean)) + return boxToBoolean(((java.lang.Boolean) arg1).booleanValue() ^ ((java.lang.Boolean) arg2).booleanValue()); + else + throw new NoSuchMethodException(); + } + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + + if (maxcode <= INT) + return boxToInteger(unboxCharOrInt(arg1, code1) ^ unboxCharOrInt(arg2, code2)); + if (maxcode <= LONG) + return boxToLong(unboxCharOrLong(arg1, code1) ^ unboxCharOrLong(arg2, code2)); + + throw new NoSuchMethodException(); + } + + /** arg1 && arg2 */ + public static Object takeConditionalAnd(Object arg1, Object arg2) throws NoSuchMethodException { + if ((arg1 instanceof Boolean) && (arg2 instanceof Boolean)) { + return boxToBoolean(((java.lang.Boolean) arg1).booleanValue() && ((java.lang.Boolean) arg2).booleanValue()); + } + throw new NoSuchMethodException(); + } + + /** arg1 || arg2 */ + public static Object takeConditionalOr(Object arg1, Object arg2) throws NoSuchMethodException { + if ((arg1 instanceof Boolean) && (arg2 instanceof Boolean)) { + return boxToBoolean(((java.lang.Boolean) arg1).booleanValue() || ((java.lang.Boolean) arg2).booleanValue()); + } + throw new NoSuchMethodException(); + } + + /** ~arg */ + public static Object complement(Object arg) throws NoSuchMethodException { + int code = typeCode(arg); + if (code <= INT) { + return boxToInteger(~unboxCharOrInt(arg, code)); + } + if (code <= LONG) { + return boxToLong(~unboxCharOrLong(arg, code)); + } + throw new NoSuchMethodException(); + } + + /** !arg */ + public static Object takeNot(Object arg) throws NoSuchMethodException { + if (arg instanceof Boolean) { + return boxToBoolean(!((java.lang.Boolean) arg).booleanValue()); + } + throw new NoSuchMethodException(); + } + + public static Object testEqual(Object arg1, Object arg2) throws NoSuchMethodException { + return boxToBoolean(arg1 == arg2); + } + + public static Object testNotEqual(Object arg1, Object arg2) throws NoSuchMethodException { + return boxToBoolean(arg1 != arg2); + } + + public static Object testLessThan(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + if (maxcode <= INT) { + int val1 = unboxCharOrInt(arg1, code1); + int val2 = unboxCharOrInt(arg2, code2); + return boxToBoolean(val1 < val2); + } + if (maxcode <= LONG) { + long val1 = unboxCharOrLong(arg1, code1); + long val2 = unboxCharOrLong(arg2, code2); + return boxToBoolean(val1 < val2); + } + if (maxcode <= FLOAT) { + float val1 = unboxCharOrFloat(arg1, code1); + float val2 = unboxCharOrFloat(arg2, code2); + return boxToBoolean(val1 < val2); + } + if (maxcode <= DOUBLE) { + double val1 = unboxCharOrDouble(arg1, code1); + double val2 = unboxCharOrDouble(arg2, code2); + return boxToBoolean(val1 < val2); + } + throw new NoSuchMethodException(); + } + + public static Object testLessOrEqualThan(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + if (maxcode <= INT) { + int val1 = unboxCharOrInt(arg1, code1); + int val2 = unboxCharOrInt(arg2, code2); + return boxToBoolean(val1 <= val2); + } + if (maxcode <= LONG) { + long val1 = unboxCharOrLong(arg1, code1); + long val2 = unboxCharOrLong(arg2, code2); + return boxToBoolean(val1 <= val2); + } + if (maxcode <= FLOAT) { + float val1 = unboxCharOrFloat(arg1, code1); + float val2 = unboxCharOrFloat(arg2, code2); + return boxToBoolean(val1 <= val2); + } + if (maxcode <= DOUBLE) { + double val1 = unboxCharOrDouble(arg1, code1); + double val2 = unboxCharOrDouble(arg2, code2); + return boxToBoolean(val1 <= val2); + } + throw new NoSuchMethodException(); + } + + public static Object testGreaterOrEqualThan(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + if (maxcode <= INT) { + int val1 = unboxCharOrInt(arg1, code1); + int val2 = unboxCharOrInt(arg2, code2); + return boxToBoolean(val1 >= val2); + } + if (maxcode <= LONG) { + long val1 = unboxCharOrLong(arg1, code1); + long val2 = unboxCharOrLong(arg2, code2); + return boxToBoolean(val1 >= val2); + } + if (maxcode <= FLOAT) { + float val1 = unboxCharOrFloat(arg1, code1); + float val2 = unboxCharOrFloat(arg2, code2); + return boxToBoolean(val1 >= val2); + } + if (maxcode <= DOUBLE) { + double val1 = unboxCharOrDouble(arg1, code1); + double val2 = unboxCharOrDouble(arg2, code2); + return boxToBoolean(val1 >= val2); + } + throw new NoSuchMethodException(); + } + + public static Object testGreaterThan(Object arg1, Object arg2) throws NoSuchMethodException { + int code1 = typeCode(arg1); + int code2 = typeCode(arg2); + int maxcode = (code1 < code2) ? code2 : code1; + if (maxcode <= INT) { + int val1 = unboxCharOrInt(arg1, code1); + int val2 = unboxCharOrInt(arg2, code2); + return boxToBoolean(val1 > val2); + } + if (maxcode <= LONG) { + long val1 = unboxCharOrLong(arg1, code1); + long val2 = unboxCharOrLong(arg2, code2); + return boxToBoolean(val1 > val2); + } + if (maxcode <= FLOAT) { + float val1 = unboxCharOrFloat(arg1, code1); + float val2 = unboxCharOrFloat(arg2, code2); + return boxToBoolean(val1 > val2); + } + if (maxcode <= DOUBLE) { + double val1 = unboxCharOrDouble(arg1, code1); + double val2 = unboxCharOrDouble(arg2, code2); + return boxToBoolean(val1 > val2); + } + throw new NoSuchMethodException(); + } + + public static boolean isBoxedNumberOrBoolean(Object arg) { + return (arg instanceof java.lang.Boolean) || isBoxedNumber(arg); + } + public static boolean isBoxedNumber(Object arg) { + return ( + (arg instanceof java.lang.Integer) + || (arg instanceof java.lang.Long) + || (arg instanceof java.lang.Double) + || (arg instanceof java.lang.Float) + || (arg instanceof java.lang.Short) + || (arg instanceof java.lang.Character) + || (arg instanceof java.lang.Byte) + ); + } + + /** arg.toChar */ + public static java.lang.Character toCharacter(Object arg) throws NoSuchMethodException { + if (arg instanceof java.lang.Integer) return boxToCharacter((char)unboxToInt(arg)); + if (arg instanceof java.lang.Short) return boxToCharacter((char)unboxToShort(arg)); + if (arg instanceof java.lang.Character) return (java.lang.Character)arg; + if (arg instanceof java.lang.Long) return boxToCharacter((char)unboxToLong(arg)); + if (arg instanceof java.lang.Byte) return boxToCharacter((char)unboxToByte(arg)); + if (arg instanceof java.lang.Float) return boxToCharacter((char)unboxToFloat(arg)); + if (arg instanceof java.lang.Double) return boxToCharacter((char)unboxToDouble(arg)); + throw new NoSuchMethodException(); + } + + /** arg.toByte */ + public static java.lang.Byte toByte(Object arg) throws NoSuchMethodException { + if (arg instanceof java.lang.Integer) return boxToByte((byte)unboxToInt(arg)); + if (arg instanceof java.lang.Character) return boxToByte((byte)unboxToChar(arg)); + if (arg instanceof java.lang.Byte) return (java.lang.Byte)arg; + if (arg instanceof java.lang.Long) return boxToByte((byte)unboxToLong(arg)); + if (arg instanceof java.lang.Short) return boxToByte((byte)unboxToShort(arg)); + if (arg instanceof java.lang.Float) return boxToByte((byte)unboxToFloat(arg)); + if (arg instanceof java.lang.Double) return boxToByte((byte)unboxToDouble(arg)); + throw new NoSuchMethodException(); + } + + /** arg.toShort */ + public static java.lang.Short toShort(Object arg) throws NoSuchMethodException { + if (arg instanceof java.lang.Integer) return boxToShort((short)unboxToInt(arg)); + if (arg instanceof java.lang.Long) return boxToShort((short)unboxToLong(arg)); + if (arg instanceof java.lang.Character) return boxToShort((short)unboxToChar(arg)); + if (arg instanceof java.lang.Byte) return boxToShort((short)unboxToByte(arg)); + if (arg instanceof java.lang.Short) return (java.lang.Short)arg; + if (arg instanceof java.lang.Float) return boxToShort((short)unboxToFloat(arg)); + if (arg instanceof java.lang.Double) return boxToShort((short)unboxToDouble(arg)); + throw new NoSuchMethodException(); + } + + /** arg.toInt */ + public static java.lang.Integer toInteger(Object arg) throws NoSuchMethodException { + if (arg instanceof java.lang.Integer) return (java.lang.Integer)arg; + if (arg instanceof java.lang.Long) return boxToInteger((int)unboxToLong(arg)); + if (arg instanceof java.lang.Double) return boxToInteger((int)unboxToDouble(arg)); + if (arg instanceof java.lang.Float) return boxToInteger((int)unboxToFloat(arg)); + if (arg instanceof java.lang.Character) return boxToInteger((int)unboxToChar(arg)); + if (arg instanceof java.lang.Byte) return boxToInteger((int)unboxToByte(arg)); + if (arg instanceof java.lang.Short) return boxToInteger((int)unboxToShort(arg)); + throw new NoSuchMethodException(); + } + + /** arg.toLong */ + public static java.lang.Long toLong(Object arg) throws NoSuchMethodException { + if (arg instanceof java.lang.Integer) return boxToLong((long)unboxToInt(arg)); + if (arg instanceof java.lang.Double) return boxToLong((long)unboxToDouble(arg)); + if (arg instanceof java.lang.Float) return boxToLong((long)unboxToFloat(arg)); + if (arg instanceof java.lang.Long) return (java.lang.Long)arg; + if (arg instanceof java.lang.Character) return boxToLong((long)unboxToChar(arg)); + if (arg instanceof java.lang.Byte) return boxToLong((long)unboxToByte(arg)); + if (arg instanceof java.lang.Short) return boxToLong((long)unboxToShort(arg)); + throw new NoSuchMethodException(); + } + + /** arg.toFloat */ + public static java.lang.Float toFloat(Object arg) throws NoSuchMethodException { + if (arg instanceof java.lang.Integer) return boxToFloat((float)unboxToInt(arg)); + if (arg instanceof java.lang.Long) return boxToFloat((float)unboxToLong(arg)); + if (arg instanceof java.lang.Float) return (java.lang.Float)arg; + if (arg instanceof java.lang.Double) return boxToFloat((float)unboxToDouble(arg)); + if (arg instanceof java.lang.Character) return boxToFloat((float)unboxToChar(arg)); + if (arg instanceof java.lang.Byte) return boxToFloat((float)unboxToByte(arg)); + if (arg instanceof java.lang.Short) return boxToFloat((float)unboxToShort(arg)); + throw new NoSuchMethodException(); + } + + /** arg.toDouble */ + public static java.lang.Double toDouble(Object arg) throws NoSuchMethodException { + if (arg instanceof java.lang.Integer) return boxToDouble((double)unboxToInt(arg)); + if (arg instanceof java.lang.Float) return boxToDouble((double)unboxToFloat(arg)); + if (arg instanceof java.lang.Double) return (java.lang.Double)arg; + if (arg instanceof java.lang.Long) return boxToDouble((double)unboxToLong(arg)); + if (arg instanceof java.lang.Character) return boxToDouble((double)unboxToChar(arg)); + if (arg instanceof java.lang.Byte) return boxToDouble((double)unboxToByte(arg)); + if (arg instanceof java.lang.Short) return boxToDouble((double)unboxToShort(arg)); + throw new NoSuchMethodException(); + } + +} diff --git a/library/src/scala/runtime/ByteRef.java b/library/src/scala/runtime/ByteRef.java new file mode 100644 index 000000000000..4630440fd7a7 --- /dev/null +++ b/library/src/scala/runtime/ByteRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class ByteRef implements java.io.Serializable { + private static final long serialVersionUID = -100666928446877072L; + + public byte elem; + public ByteRef(byte elem) { this.elem = elem; } + public String toString() { return java.lang.Byte.toString(elem); } + + public static ByteRef create(byte e) { return new ByteRef(e); } + public static ByteRef zero() { return new ByteRef((byte)0); } +} diff --git a/library/src/scala/runtime/CharRef.java b/library/src/scala/runtime/CharRef.java new file mode 100644 index 000000000000..05e8fa55c982 --- /dev/null +++ b/library/src/scala/runtime/CharRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class CharRef implements java.io.Serializable { + private static final long serialVersionUID = 6537214938268005702L; + + public char elem; + public CharRef(char elem) { this.elem = elem; } + public String toString() { return java.lang.Character.toString(elem); } + + public static CharRef create(char e) { return new CharRef(e); } + public static CharRef zero() { return new CharRef((char)0); } +} diff --git a/library/src/scala/runtime/ClassValueCompat.scala b/library/src/scala/runtime/ClassValueCompat.scala new file mode 100644 index 000000000000..09a619f7a5f5 --- /dev/null +++ b/library/src/scala/runtime/ClassValueCompat.scala @@ -0,0 +1,53 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + + +import scala.runtime.ClassValueCompat._ + +private[scala] abstract class ClassValueCompat[T] extends ClassValueInterface[T] { self => + private val instance: ClassValueInterface[T] = + if (classValueAvailable) new JavaClassValue() + else new FallbackClassValue() + + private class JavaClassValue extends ClassValue[T] with ClassValueInterface[T] { + override def computeValue(cls: Class[_]): T = self.computeValue(cls) + } + + private class FallbackClassValue extends ClassValueInterface[T] { + override def get(cls: Class[_]): T = self.computeValue(cls) + + override def remove(cls: Class[_]): Unit = {} + } + + def get(cls: Class[_]): T = instance.get(cls) + + def remove(cls: Class[_]): Unit = instance.remove(cls) + + protected def computeValue(cls: Class[_]): T +} + +private[scala] object ClassValueCompat { + trait ClassValueInterface[T] { + def get(cls: Class[_]): T + + def remove(cls: Class[_]): Unit + } + + private val classValueAvailable: Boolean = try { + Class.forName("java.lang.ClassValue", false, classOf[Object].getClassLoader) + true + } catch { + case _: ClassNotFoundException => false + } +} diff --git a/library/src/scala/runtime/DoubleRef.java b/library/src/scala/runtime/DoubleRef.java new file mode 100644 index 000000000000..52b40cde396e --- /dev/null +++ b/library/src/scala/runtime/DoubleRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class DoubleRef implements java.io.Serializable { + private static final long serialVersionUID = 8304402127373655534L; + + public double elem; + public DoubleRef(double elem) { this.elem = elem; } + public String toString() { return java.lang.Double.toString(elem); } + + public static DoubleRef create(double e) { return new DoubleRef(e); } + public static DoubleRef zero() { return new DoubleRef(0); } +} diff --git a/library/src/scala/runtime/FloatRef.java b/library/src/scala/runtime/FloatRef.java new file mode 100644 index 000000000000..d28d62a0ccc2 --- /dev/null +++ b/library/src/scala/runtime/FloatRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class FloatRef implements java.io.Serializable { + private static final long serialVersionUID = -5793980990371366933L; + + public float elem; + public FloatRef(float elem) { this.elem = elem; } + public String toString() { return java.lang.Float.toString(elem); } + + public static FloatRef create(float e) { return new FloatRef(e); } + public static FloatRef zero() { return new FloatRef(0); } +} diff --git a/library/src/scala/runtime/IntRef.java b/library/src/scala/runtime/IntRef.java new file mode 100644 index 000000000000..d456c3a750b3 --- /dev/null +++ b/library/src/scala/runtime/IntRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class IntRef implements java.io.Serializable { + private static final long serialVersionUID = 1488197132022872888L; + + public int elem; + public IntRef(int elem) { this.elem = elem; } + public String toString() { return java.lang.Integer.toString(elem); } + + public static IntRef create(int e) { return new IntRef(e); } + public static IntRef zero() { return new IntRef(0); } +} diff --git a/library/src/scala/runtime/LambdaDeserialize.scala b/library/src/scala/runtime/LambdaDeserialize.scala new file mode 100644 index 000000000000..b4270d63e643 --- /dev/null +++ b/library/src/scala/runtime/LambdaDeserialize.scala @@ -0,0 +1,44 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +import java.lang.invoke._ +import java.util + +import scala.annotation.varargs +import scala.collection.immutable + +final class LambdaDeserialize private (lookup: MethodHandles.Lookup, targetMethods: Array[MethodHandle]) { + private val targetMethodMap: util.HashMap[String, MethodHandle] = new util.HashMap[String, MethodHandle](targetMethods.length) + + for (targetMethod <- targetMethods) { + val info = lookup.revealDirect(targetMethod) + val key = LambdaDeserialize.nameAndDescriptorKey(info.getName, info.getMethodType.toMethodDescriptorString) + targetMethodMap.put(key, targetMethod) + } + + private val cache = new util.HashMap[String, MethodHandle] + + def deserializeLambda(serialized: SerializedLambda): AnyRef = LambdaDeserializer.deserializeLambda(lookup, cache, targetMethodMap, serialized) +} + +object LambdaDeserialize { + @varargs @throws[Throwable] + def bootstrap(lookup: MethodHandles.Lookup, invokedName: String, invokedType: MethodType, targetMethods: MethodHandle*): CallSite = { + val targetMethodsArray = targetMethods.asInstanceOf[immutable.ArraySeq[_]].unsafeArray.asInstanceOf[Array[MethodHandle]] + val exact = MethodHandleConstants.LAMBDA_DESERIALIZE_DESERIALIZE_LAMBDA.bindTo(new LambdaDeserialize(lookup, targetMethodsArray)).asType(invokedType) + new ConstantCallSite(exact) + } + + def nameAndDescriptorKey(name: String, descriptor: String): String = name + descriptor +} diff --git a/library/src/scala/runtime/LambdaDeserializer.scala b/library/src/scala/runtime/LambdaDeserializer.scala new file mode 100644 index 000000000000..76fc5d778bec --- /dev/null +++ b/library/src/scala/runtime/LambdaDeserializer.scala @@ -0,0 +1,129 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +import java.lang.invoke._ + +/** + * This class is only intended to be called by synthetic `$deserializeLambda$` method that the Scala 2.12 + * compiler will add to classes hosting lambdas. + * + * It is not intended to be consumed directly. + */ +object LambdaDeserializer { + /** + * Deserialize a lambda by calling `LambdaMetafactory.altMetafactory` to spin up a lambda class + * and instantiating this class with the captured arguments. + * + * A cache may be provided to ensure that subsequent deserialization of the same lambda expression + * is cheap, it amounts to a reflective call to the constructor of the previously created class. + * However, deserialization of the same lambda expression is not guaranteed to use the same class, + * concurrent deserialization of the same lambda expression may spin up more than one class. + * + * Assumptions: + * - No additional marker interfaces are required beyond `java.io.Serializable`. These are + * not stored in `SerializedLambda`, so we can't reconstitute them. + * - No additional bridge methods are passed to `altMetafactory`. Again, these are not stored. + * + * @param lookup The factory for method handles. Must have access to the implementation method, the + * functional interface class, and `java.io.Serializable`. + * @param cache A cache used to avoid spinning up a class for each deserialization of a given lambda. May be `null` + * @param serialized The lambda to deserialize. Note that this is typically created by the `readResolve` + * member of the anonymous class created by `LambdaMetaFactory`. + * @return An instance of the functional interface + */ + def deserializeLambda(lookup: MethodHandles.Lookup, cache: java.util.Map[String, MethodHandle], + targetMethodMap: java.util.Map[String, MethodHandle], serialized: SerializedLambda): AnyRef = { + val result = deserializeLambdaOrNull(lookup, cache, targetMethodMap, serialized) + if (result == null) throw new IllegalArgumentException("Illegal lambda deserialization") + else result + } + + def deserializeLambdaOrNull(lookup: MethodHandles.Lookup, cache: java.util.Map[String, MethodHandle], + targetMethodMap: java.util.Map[String, MethodHandle], serialized: SerializedLambda): AnyRef = { + assert(targetMethodMap != null) + def slashDot(name: String) = name.replaceAll("/", ".") + val loader = lookup.lookupClass().getClassLoader + val implClass = loader.loadClass(slashDot(serialized.getImplClass)) + val key = LambdaDeserialize.nameAndDescriptorKey(serialized.getImplMethodName, serialized.getImplMethodSignature) + + def makeCallSite: CallSite = { + import serialized._ + def parseDescriptor(s: String) = + MethodType.fromMethodDescriptorString(s, loader) + + val funcInterfaceSignature = parseDescriptor(getFunctionalInterfaceMethodSignature) + val instantiated = parseDescriptor(getInstantiatedMethodType) + val functionalInterfaceClass = loader.loadClass(slashDot(getFunctionalInterfaceClass)) + + val implMethodSig = parseDescriptor(getImplMethodSignature) + // Construct the invoked type from the impl method type. This is the type of a factory + // that will be generated by the meta-factory. It is a method type, with param types + // coming form the types of the captures, and return type being the functional interface. + val invokedType: MethodType = { + // 1. Add receiver for non-static impl methods + val withReceiver = getImplMethodKind match { + case MethodHandleInfo.REF_invokeStatic | MethodHandleInfo.REF_newInvokeSpecial => + implMethodSig + case _ => + implMethodSig.insertParameterTypes(0, implClass) + } + // 2. Remove lambda parameters, leaving only captures. Note: the receiver may be a lambda parameter, + // such as in `Function s = Object::toString` + val lambdaArity = funcInterfaceSignature.parameterCount() + val from = withReceiver.parameterCount() - lambdaArity + val to = withReceiver.parameterCount() + + // 3. Drop the lambda return type and replace with the functional interface. + withReceiver.dropParameterTypes(from, to).changeReturnType(functionalInterfaceClass) + } + + // Lookup the implementation method + val implMethod: MethodHandle = if (targetMethodMap.containsKey(key)) { + targetMethodMap.get(key) + } else { + return null + } + + val flags: Int = LambdaMetafactory.FLAG_SERIALIZABLE + + LambdaMetafactory.altMetafactory( + lookup, getFunctionalInterfaceMethodName, invokedType, + + /* samMethodType = */ funcInterfaceSignature, + /* implMethod = */ implMethod, + /* instantiatedMethodType = */ instantiated, + /* flags = */ flags.asInstanceOf[AnyRef] + ) + } + + val factory: MethodHandle = if (cache == null) { + val callSite = makeCallSite + if (callSite == null) return null + callSite.getTarget + } else cache.synchronized{ + cache.get(key) match { + case null => + val callSite = makeCallSite + if (callSite == null) return null + val temp = callSite.getTarget + cache.put(key, temp) + temp + case target => target + } + } + + val captures = Array.tabulate(serialized.getCapturedArgCount)(n => serialized.getCapturedArg(n)) + factory.invokeWithArguments(captures: _*) + } +} diff --git a/library/src/scala/runtime/LazyRef.scala b/library/src/scala/runtime/LazyRef.scala new file mode 100644 index 000000000000..ee0364cfab94 --- /dev/null +++ b/library/src/scala/runtime/LazyRef.scala @@ -0,0 +1,171 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +/** Classes used as holders for lazy vals defined in methods. */ + +@SerialVersionUID(1L) +class LazyRef[T] extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: T = _ + def value: T = _value + def initialize(value: T): T = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyRef ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyBoolean extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Boolean = _ + def value: Boolean = _value + def initialize(value: Boolean): Boolean = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyBoolean ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyByte extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Byte = _ + + def value: Byte = _value + + def initialize(value: Byte): Byte = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyByte ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyChar extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Char = _ + def value: Char = _value + def initialize(value: Char): Char = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyChar ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyShort extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Short = _ + def value: Short = _value + def initialize(value: Short): Short = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyShort ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyInt extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Int = _ + def value: Int = _value + def initialize(value: Int): Int = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyInt ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyLong extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Long = _ + def value: Long = _value + def initialize(value: Long): Long = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyLong ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyFloat extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Float = _ + def value: Float = _value + def initialize(value: Float): Float = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyFloat ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyDouble extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Double = _ + def value: Double = _value + def initialize(value: Double): Double = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyDouble ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +@SerialVersionUID(1L) +class LazyUnit extends Serializable { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + def initialize(): Unit = _initialized = true + + override def toString = s"LazyUnit${if (_initialized) "" else " thunk"}" +} diff --git a/library/src/scala/runtime/LongRef.java b/library/src/scala/runtime/LongRef.java new file mode 100644 index 000000000000..9e189af0ef2b --- /dev/null +++ b/library/src/scala/runtime/LongRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class LongRef implements java.io.Serializable { + private static final long serialVersionUID = -3567869820105829499L; + + public long elem; + public LongRef(long elem) { this.elem = elem; } + public String toString() { return java.lang.Long.toString(elem); } + + public static LongRef create(long e) { return new LongRef(e); } + public static LongRef zero() { return new LongRef(0); } +} diff --git a/library/src/scala/runtime/MethodCache.scala b/library/src/scala/runtime/MethodCache.scala new file mode 100644 index 000000000000..2aa41c9e352a --- /dev/null +++ b/library/src/scala/runtime/MethodCache.scala @@ -0,0 +1,87 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + + +import java.lang.reflect.{ Method => JMethod } +import java.lang.{ Class => JClass } + +import scala.annotation.tailrec + +/** An element of a polymorphic object cache. + * This class is referred to by the `CleanUp` phase. Each `PolyMethodCache` chain + * must only relate to one method as `PolyMethodCache` does not identify + * the method name and argument types. In practice, one variable will be + * generated per call point, and will uniquely relate to the method called + * at that point, making the method name and argument types irrelevant. */ +/* TODO: if performance is acceptable, PolyMethodCache should be made generic on the method type */ +private[scala] sealed abstract class MethodCache { + /** Searches for a cached method in the `MethodCache` chain that + * is compatible with receiver class `forReceiver`. If none is cached, + * `null` is returned. If `null` is returned, find's caller should look- + * up the right method using whichever means it prefers, and add it to + * the cache for later use. */ + def find(forReceiver: JClass[_]): JMethod + def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache +} + +private[scala] final class EmptyMethodCache extends MethodCache { + + def find(forReceiver: JClass[_]): JMethod = null + + def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache = + new PolyMethodCache(this, forReceiver, forMethod, 1) + +} + +private[scala] final class MegaMethodCache( + private[this] val forName: String, + private[this] val forParameterTypes: Array[JClass[_]] +) extends MethodCache { + + def find(forReceiver: JClass[_]): JMethod = + forReceiver.getMethod(forName, forParameterTypes:_*) + + def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache = this + +} + +private[scala] final class PolyMethodCache( + private[this] val next: MethodCache, + private[this] val receiver: JClass[_], + private[this] val method: JMethod, + private[this] val complexity: Int +) extends MethodCache { + + /** To achieve tail recursion this must be a separate method + * from `find`, because the type of next is not `PolyMethodCache`. + */ + @tailrec private def findInternal(forReceiver: JClass[_]): JMethod = + if (forReceiver eq receiver) method + else next match { + case x: PolyMethodCache => x findInternal forReceiver + case _ => next find forReceiver + } + + def find(forReceiver: JClass[_]): JMethod = findInternal(forReceiver) + + // TODO: come up with a more realistic number + final private val MaxComplexity = 160 + + def add(forReceiver: JClass[_], forMethod: JMethod): MethodCache = + if (complexity < MaxComplexity) + new PolyMethodCache(this, forReceiver, forMethod, complexity + 1) + else + new MegaMethodCache(forMethod.getName, forMethod.getParameterTypes) +} diff --git a/library/src/scala/runtime/MethodHandleConstants.java b/library/src/scala/runtime/MethodHandleConstants.java new file mode 100644 index 000000000000..16773431f86a --- /dev/null +++ b/library/src/scala/runtime/MethodHandleConstants.java @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.SerializedLambda; + +class MethodHandleConstants { + // static final MethodHandles are optimized by the JIT (https://stackoverflow.com/a/14146641/248998) + static final MethodHandle LAMBDA_DESERIALIZE_DESERIALIZE_LAMBDA; + + static { + LAMBDA_DESERIALIZE_DESERIALIZE_LAMBDA = lookupDeserialize(); + } + + private static MethodHandle lookupDeserialize() { + try { + return MethodHandles.lookup().findVirtual(Class.forName("scala.runtime.LambdaDeserialize"), "deserializeLambda", MethodType.methodType(Object.class, SerializedLambda.class)); + } catch (NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) { + throw new ExceptionInInitializerError(e); + } + } +} diff --git a/library/src/scala/runtime/ModuleSerializationProxy.scala b/library/src/scala/runtime/ModuleSerializationProxy.scala new file mode 100644 index 000000000000..46c676ecc779 --- /dev/null +++ b/library/src/scala/runtime/ModuleSerializationProxy.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +import java.io.Serializable +import java.security.PrivilegedActionException +import java.security.PrivilegedExceptionAction +import scala.annotation.nowarn + +private[runtime] object ModuleSerializationProxy { + private val instances = new ClassValueCompat[Object] { + @nowarn("cat=deprecation") // AccessController is deprecated on JDK 17 + def getModule(cls: Class[_]): Object = + java.security.AccessController.doPrivileged( + (() => cls.getField("MODULE$").get(null)): PrivilegedExceptionAction[Object]) + override protected def computeValue(cls: Class[_]): Object = + try getModule(cls) + catch { + case e: PrivilegedActionException => + rethrowRuntime(e.getCause) + } + } + + private def rethrowRuntime(e: Throwable): Object = e match { + case re: RuntimeException => throw re + case _ => throw new RuntimeException(e) + } +} + +@SerialVersionUID(1L) +final class ModuleSerializationProxy(moduleClass: Class[_]) extends Serializable { + private def readResolve = ModuleSerializationProxy.instances.get(moduleClass) +} diff --git a/library/src/scala/runtime/NonLocalReturnControl.scala b/library/src/scala/runtime/NonLocalReturnControl.scala new file mode 100644 index 000000000000..8abd48501e3c --- /dev/null +++ b/library/src/scala/runtime/NonLocalReturnControl.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +import scala.util.control.ControlThrowable + +// remove Unit specialization when binary compatibility permits +@annotation.nowarn("cat=lint-unit-specialization") +class NonLocalReturnControl[@specialized(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit) T](val key: AnyRef, val value: T) extends ControlThrowable { + final override def fillInStackTrace(): Throwable = this +} diff --git a/library/src/scala/runtime/Nothing$.scala b/library/src/scala/runtime/Nothing$.scala new file mode 100644 index 000000000000..cb098a5507da --- /dev/null +++ b/library/src/scala/runtime/Nothing$.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + + +/** + * Dummy class which exist only to satisfy the JVM. It corresponds + * to `scala.Nothing`. If such type appears in method + * signatures, it is erased to this one. + */ +sealed abstract class Nothing$ extends Throwable diff --git a/library/src/scala/runtime/Null$.scala b/library/src/scala/runtime/Null$.scala new file mode 100644 index 000000000000..d279f861e94a --- /dev/null +++ b/library/src/scala/runtime/Null$.scala @@ -0,0 +1,22 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +/** + * Dummy class which exist only to satisfy the JVM. It corresponds to + * `scala.Null`. If such type appears in method signatures, it is erased + * to this one. A private constructor ensures that Java code can't create + * subclasses. The only value of type Null$ should be null + */ +sealed abstract class Null$ private () diff --git a/library/src/scala/runtime/ObjectRef.java b/library/src/scala/runtime/ObjectRef.java new file mode 100644 index 000000000000..04545449e9a1 --- /dev/null +++ b/library/src/scala/runtime/ObjectRef.java @@ -0,0 +1,25 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class ObjectRef implements java.io.Serializable { + private static final long serialVersionUID = -9055728157600312291L; + + public T elem; + public ObjectRef(T elem) { this.elem = elem; } + @Override + public String toString() { return String.valueOf(elem); } + + public static ObjectRef create(U e) { return new ObjectRef(e); } + public static ObjectRef zero() { return new ObjectRef(null); } +} diff --git a/library/src/scala/runtime/PStatics.scala b/library/src/scala/runtime/PStatics.scala new file mode 100644 index 000000000000..9196e21e75be --- /dev/null +++ b/library/src/scala/runtime/PStatics.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +// things that should be in `Statics`, but can't be yet for bincompat reasons +// TODO 3.T: move to `Statics` +private[scala] object PStatics { + // `Int.MaxValue - 8` traditional soft limit to maximize compatibility with diverse JVMs + // See https://stackoverflow.com/a/8381338 for example + final val VM_MaxArraySize = 2147483639 +} diff --git a/library/src/scala/runtime/RichBoolean.scala b/library/src/scala/runtime/RichBoolean.scala new file mode 100644 index 000000000000..8b1c1525cedf --- /dev/null +++ b/library/src/scala/runtime/RichBoolean.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + + +final class RichBoolean(val self: Boolean) extends AnyVal with OrderedProxy[Boolean] { + protected def ord: scala.math.Ordering.Boolean.type = scala.math.Ordering.Boolean +} diff --git a/library/src/scala/runtime/RichByte.scala b/library/src/scala/runtime/RichByte.scala new file mode 100644 index 000000000000..6060d9b75a79 --- /dev/null +++ b/library/src/scala/runtime/RichByte.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +final class RichByte(val self: Byte) extends AnyVal with ScalaWholeNumberProxy[Byte] { + protected def num: scala.math.Numeric.ByteIsIntegral.type = scala.math.Numeric.ByteIsIntegral + protected def ord: scala.math.Ordering.Byte.type = scala.math.Ordering.Byte + + override def doubleValue = self.toDouble + override def floatValue = self.toFloat + override def longValue = self.toLong + override def intValue = self.toInt + override def byteValue = self + override def shortValue = self.toShort + + override def isValidByte = true + + // These method are all overridden and redefined to call out to scala.math to avoid 3 allocations: + // the primitive boxing, the value class boxing and instantiation of the Numeric num. + // We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to. + override def abs: Byte = math.abs(self).toByte + override def max(that: Byte): Byte = math.max(self, that).toByte + override def min(that: Byte): Byte = math.min(self, that).toByte +} diff --git a/library/src/scala/runtime/RichChar.scala b/library/src/scala/runtime/RichChar.scala new file mode 100644 index 000000000000..2bdb80be96f4 --- /dev/null +++ b/library/src/scala/runtime/RichChar.scala @@ -0,0 +1,70 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +final class RichChar(val self: Char) extends AnyVal with IntegralProxy[Char] { + protected def num: scala.math.Numeric.CharIsIntegral.type = scala.math.Numeric.CharIsIntegral + protected def ord: scala.math.Ordering.Char.type = scala.math.Ordering.Char + + override def doubleValue = self.toDouble + override def floatValue = self.toFloat + override def longValue = self.toLong + override def intValue = self.toInt + override def byteValue = self.toByte + override def shortValue = self.toShort + + override def isValidChar = true + + // These method are all overridden and redefined to call out to scala.math to avoid 3 allocations: + // the primitive boxing, the value class boxing and instantiation of the Numeric num. + // We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to. + override def abs: Char = self + override def max(that: Char): Char = math.max(self.toInt, that.toInt).toChar + override def min(that: Char): Char = math.min(self.toInt, that.toInt).toChar + + def asDigit: Int = Character.digit(self, Character.MAX_RADIX) + + def isControl: Boolean = Character.isISOControl(self) + def isDigit: Boolean = Character.isDigit(self) + def isLetter: Boolean = Character.isLetter(self) + def isLetterOrDigit: Boolean = Character.isLetterOrDigit(self) + def isWhitespace: Boolean = Character.isWhitespace(self) + def isSpaceChar: Boolean = Character.isSpaceChar(self) + def isHighSurrogate: Boolean = Character.isHighSurrogate(self) + def isLowSurrogate: Boolean = Character.isLowSurrogate(self) + def isSurrogate: Boolean = isHighSurrogate || isLowSurrogate + def isUnicodeIdentifierStart: Boolean = Character.isUnicodeIdentifierStart(self) + def isUnicodeIdentifierPart: Boolean = Character.isUnicodeIdentifierPart(self) + def isIdentifierIgnorable: Boolean = Character.isIdentifierIgnorable(self) + def isMirrored: Boolean = Character.isMirrored(self) + + def isLower: Boolean = Character.isLowerCase(self) + def isUpper: Boolean = Character.isUpperCase(self) + def isTitleCase: Boolean = Character.isTitleCase(self) + + def toLower: Char = Character.toLowerCase(self) + def toUpper: Char = Character.toUpperCase(self) + def toTitleCase: Char = Character.toTitleCase(self) + + def getType: Int = Character.getType(self) + def getNumericValue: Int = Character.getNumericValue(self) + def getDirectionality: Byte = Character.getDirectionality(self) + def reverseBytes: Char = Character.reverseBytes(self) + + // Java 5 Character methods not added: + // + // public static boolean isDefined(char ch) + // public static boolean isJavaIdentifierStart(char ch) + // public static boolean isJavaIdentifierPart(char ch) +} diff --git a/library/src/scala/runtime/RichDouble.scala b/library/src/scala/runtime/RichDouble.scala new file mode 100644 index 000000000000..b453e69deee0 --- /dev/null +++ b/library/src/scala/runtime/RichDouble.scala @@ -0,0 +1,70 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +final class RichDouble(val self: Double) extends AnyVal with FractionalProxy[Double] { + protected def num: Fractional[Double] = scala.math.Numeric.DoubleIsFractional + protected def ord: Ordering[Double] = scala.math.Ordering.Double.TotalOrdering + + override def doubleValue = self + override def floatValue = self.toFloat + override def longValue = self.toLong + override def intValue = self.toInt + override def byteValue = self.toByte + override def shortValue = self.toShort + + override def isWhole = { + val l = self.toLong + l.toDouble == self || l == Long.MaxValue && self < Double.PositiveInfinity || l == Long.MinValue && self > Double.NegativeInfinity + } + override def isValidByte = self.toByte.toDouble == self + override def isValidShort = self.toShort.toDouble == self + override def isValidChar = self.toChar.toDouble == self + override def isValidInt = self.toInt.toDouble == self + // override def isValidLong = { val l = self.toLong; l.toDouble == self && l != Long.MaxValue } + // override def isValidFloat = self.toFloat.toDouble == self + // override def isValidDouble = !java.lang.Double.isNaN(self) + + def isNaN: Boolean = java.lang.Double.isNaN(self) + def isInfinity: Boolean = java.lang.Double.isInfinite(self) + def isFinite: Boolean = java.lang.Double.isFinite(self) + def isPosInfinity: Boolean = Double.PositiveInfinity == self + def isNegInfinity: Boolean = Double.NegativeInfinity == self + + // These method are all overridden and redefined to call out to scala.math to avoid 3 allocations: + // the primitive boxing, the value class boxing and instantiation of the Numeric num. + // We'd like to redefine sign too but forwards binary compatibility doesn't allow us to. + override def abs: Double = math.abs(self) + override def max(that: Double): Double = math.max(self, that) + override def min(that: Double): Double = math.min(self, that) + @deprecated("signum does not handle -0.0 or Double.NaN; use `sign` method instead", since = "2.13.0") + override def signum: Int = math.signum(self).toInt + + def round: Long = math.round(self) + def ceil: Double = math.ceil(self) + def floor: Double = math.floor(self) + + /** Converts an angle measured in degrees to an approximately equivalent + * angle measured in radians. + * + * @return the measurement of the angle x in radians. + */ + def toRadians: Double = math.toRadians(self) + + /** Converts an angle measured in radians to an approximately equivalent + * angle measured in degrees. + * @return the measurement of the angle x in degrees. + */ + def toDegrees: Double = math.toDegrees(self) +} diff --git a/library/src/scala/runtime/RichFloat.scala b/library/src/scala/runtime/RichFloat.scala new file mode 100644 index 000000000000..c6570ab10615 --- /dev/null +++ b/library/src/scala/runtime/RichFloat.scala @@ -0,0 +1,71 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +final class RichFloat(val self: Float) extends AnyVal with FractionalProxy[Float] { + protected def num: Fractional[Float] = scala.math.Numeric.FloatIsFractional + protected def ord: Ordering[Float] = scala.math.Ordering.Float.TotalOrdering + + override def doubleValue = self.toDouble + override def floatValue = self + override def longValue = self.toLong + override def intValue = self.toInt + override def byteValue = self.toByte + override def shortValue = self.toShort + + override def isWhole = { + val l = self.toLong + l.toFloat == self || l == Long.MaxValue && self < Float.PositiveInfinity || l == Long.MinValue && self > Float.NegativeInfinity + } + override def isValidByte = self.toByte.toFloat == self + override def isValidShort = self.toShort.toFloat == self + override def isValidChar = self.toChar.toFloat == self + override def isValidInt = { val i = self.toInt; i.toFloat == self && i != Int.MaxValue } + // override def isValidLong = { val l = self.toLong; l.toFloat == self && l != Long.MaxValue } + // override def isValidFloat = !java.lang.Float.isNaN(self) + // override def isValidDouble = !java.lang.Float.isNaN(self) + + def isNaN: Boolean = java.lang.Float.isNaN(self) + def isInfinity: Boolean = java.lang.Float.isInfinite(self) + def isFinite: Boolean = java.lang.Float.isFinite(self) + def isPosInfinity: Boolean = Float.PositiveInfinity == self + def isNegInfinity: Boolean = Float.NegativeInfinity == self + + // These method are all overridden and redefined to call out to scala.math to avoid 3 allocations: + // the primitive boxing, the value class boxing and instantiation of the Numeric num. + // We'd like to redefine sign too but forwards binary compatibility doesn't allow us to. + override def abs: Float = math.abs(self) + override def max(that: Float): Float = math.max(self, that) + override def min(that: Float): Float = math.min(self, that) + @deprecated("signum does not handle -0.0f or Float.NaN; use `sign` method instead", since = "2.13.0") + override def signum: Int = math.signum(self).toInt + + def round: Int = math.round(self) + def ceil: Float = math.ceil(self.toDouble).toFloat + def floor: Float = math.floor(self.toDouble).toFloat + + /** Converts an angle measured in degrees to an approximately equivalent + * angle measured in radians. + * + * @return the measurement of the angle `x` in radians. + */ + def toRadians: Float = math.toRadians(self.toDouble).toFloat + + /** Converts an angle measured in radians to an approximately equivalent + * angle measured in degrees. + * + * @return the measurement of the angle `x` in degrees. + */ + def toDegrees: Float = math.toDegrees(self.toDouble).toFloat +} diff --git a/library/src/scala/runtime/RichInt.scala b/library/src/scala/runtime/RichInt.scala new file mode 100644 index 000000000000..7499e5078006 --- /dev/null +++ b/library/src/scala/runtime/RichInt.scala @@ -0,0 +1,87 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +import scala.collection.immutable.Range + +// Note that this does not implement IntegralProxy[Int] so that it can return +// the Int-specific Range class from until/to. +final class RichInt(val self: Int) extends AnyVal with ScalaNumberProxy[Int] with RangedProxy[Int] { + protected def num: scala.math.Numeric.IntIsIntegral.type = scala.math.Numeric.IntIsIntegral + protected def ord: scala.math.Ordering.Int.type = scala.math.Ordering.Int + + override def doubleValue = self.toDouble + override def floatValue = self.toFloat + override def longValue = self.toLong + override def intValue = self + override def byteValue = self.toByte + override def shortValue = self.toShort + + /** Returns `'''true'''` if this number has no decimal component. + * Always `'''true'''` for `RichInt`. + */ + @deprecated("isWhole on an integer type is always true", "2.12.15") + def isWhole = true + + override def isValidInt = true + def isValidLong = true + + // These method are all overridden and redefined to call out to scala.math to avoid 3 allocations: + // the primitive boxing, the value class boxing and instantiation of the Numeric num. + // We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to. + override def abs: Int = math.abs(self) + override def max(that: Int): Int = math.max(self, that) + override def min(that: Int): Int = math.min(self, that) + + /** There is no reason to round an `Int`, but this method is provided to avoid accidental loss of precision from a detour through `Float`. */ + @deprecated("this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value?", "2.11.0") + def round: Int = self + + def toBinaryString: String = java.lang.Integer.toBinaryString(self) + def toHexString: String = java.lang.Integer.toHexString(self) + def toOctalString: String = java.lang.Integer.toOctalString(self) + + type ResultWithoutStep = Range + + /** + * @param end The final bound of the range to make. + * @return A [[scala.collection.immutable.Range]] from `this` up to but + * not including `end`. + */ + def until(end: Int): Range = Range(self, end) + + /** + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + * @return A [[scala.collection.immutable.Range]] from `this` up to but + * not including `end`. + */ + def until(end: Int, step: Int): Range = Range(self, end, step) + + /** like `until`, but includes the last index */ + /** + * @param end The final bound of the range to make. + * @return A [[scala.collection.immutable.Range]] from `'''this'''` up to + * and including `end`. + */ + def to(end: Int): Range.Inclusive = Range.inclusive(self, end) + + /** + * @param end The final bound of the range to make. + * @param step The number to increase by for each step of the range. + * @return A [[scala.collection.immutable.Range]] from `'''this'''` up to + * and including `end`. + */ + def to(end: Int, step: Int): Range.Inclusive = Range.inclusive(self, end, step) +} diff --git a/library/src/scala/runtime/RichLong.scala b/library/src/scala/runtime/RichLong.scala new file mode 100644 index 000000000000..1f5bc5d0da4b --- /dev/null +++ b/library/src/scala/runtime/RichLong.scala @@ -0,0 +1,49 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +final class RichLong(val self: Long) extends AnyVal with IntegralProxy[Long] { + protected def num: scala.math.Numeric.LongIsIntegral.type = scala.math.Numeric.LongIsIntegral + protected def ord: scala.math.Ordering.Long.type = scala.math.Ordering.Long + + override def doubleValue = self.toDouble + override def floatValue = self.toFloat + override def longValue = self + override def intValue = self.toInt + override def byteValue = self.toByte + override def shortValue = self.toShort + + override def isValidByte = self.toByte.toLong == self + override def isValidShort = self.toShort.toLong == self + override def isValidChar = self.toChar.toLong == self + override def isValidInt = self.toInt.toLong == self + def isValidLong = true + // override def isValidFloat = self.toFloat.toLong == self && self != Long.MaxValue + // override def isValidDouble = self.toDouble.toLong == self && self != Long.MaxValue + + // These method are all overridden and redefined to call out to scala.math to avoid 3 allocations: + // the primitive boxing, the value class boxing and instantiation of the Numeric num. + // We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to. + override def abs: Long = math.abs(self) + override def max(that: Long): Long = math.max(self, that) + override def min(that: Long): Long = math.min(self, that) + + /** There is no reason to round a `Long`, but this method is provided to avoid accidental conversion to `Int` through `Float`. */ + @deprecated("this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value?", "2.11.0") + def round: Long = self + + def toBinaryString: String = java.lang.Long.toBinaryString(self) + def toHexString: String = java.lang.Long.toHexString(self) + def toOctalString: String = java.lang.Long.toOctalString(self) +} diff --git a/library/src/scala/runtime/RichShort.scala b/library/src/scala/runtime/RichShort.scala new file mode 100644 index 000000000000..1f4ebfaf0b1a --- /dev/null +++ b/library/src/scala/runtime/RichShort.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +final class RichShort(val self: Short) extends AnyVal with ScalaWholeNumberProxy[Short] { + protected def num: scala.math.Numeric.ShortIsIntegral.type = scala.math.Numeric.ShortIsIntegral + protected def ord: scala.math.Ordering.Short.type = scala.math.Ordering.Short + + override def doubleValue = self.toDouble + override def floatValue = self.toFloat + override def longValue = self.toLong + override def intValue = self.toInt + override def byteValue = self.toByte + override def shortValue = self + + override def isValidShort = true + + // These method are all overridden and redefined to call out to scala.math to avoid 3 allocations: + // the primitive boxing, the value class boxing and instantiation of the Numeric num. + // We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to. + override def abs: Short = math.abs(self.toInt).toShort + override def max(that: Short): Short = math.max(self.toInt, that.toInt).toShort + override def min(that: Short): Short = math.min(self.toInt, that.toInt).toShort +} diff --git a/library/src/scala/runtime/ScalaNumberProxy.scala b/library/src/scala/runtime/ScalaNumberProxy.scala new file mode 100644 index 000000000000..a39c0e7854d3 --- /dev/null +++ b/library/src/scala/runtime/ScalaNumberProxy.scala @@ -0,0 +1,87 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +import scala.collection.immutable +import scala.math.ScalaNumericAnyConversions +import immutable.NumericRange +import Proxy.Typed +import scala.annotation.nowarn + +/** Base classes for the Rich* wrappers of the primitive types. + * As with all classes in scala.runtime.*, this is not a supported API. + */ +@nowarn("cat=deprecation") +trait ScalaNumberProxy[T] extends Any with ScalaNumericAnyConversions with Typed[T] with OrderedProxy[T] { + protected implicit def num: Numeric[T] + + def doubleValue = num.toDouble(self) + def floatValue = num.toFloat(self) + def longValue = num.toLong(self) + def intValue = num.toInt(self) + def byteValue = intValue.toByte + def shortValue = intValue.toShort + + /** Returns `'''this'''` if `'''this''' < that` or `that` otherwise. */ + def min(that: T): T = num.min(self, that) + /** Returns `'''this'''` if `'''this''' > that` or `that` otherwise. */ + def max(that: T): T = num.max(self, that) + /** Returns the absolute value of `'''this'''`. */ + def abs = num.abs(self) + /** + * Returns the sign of `'''this'''`. + * zero if the argument is zero, -zero if the argument is -zero, + * one if the argument is greater than zero, -one if the argument is less than zero, + * and NaN if the argument is NaN where applicable. + */ + def sign: T = num.sign(self) + /** Returns the signum of `'''this'''`. */ + @deprecated("use `sign` method instead", since = "2.13.0") def signum: Int = num.signum(self) +} +trait ScalaWholeNumberProxy[T] extends Any with ScalaNumberProxy[T] { + @deprecated("isWhole on an integer type is always true", "2.12.15") + def isWhole = true +} +trait IntegralProxy[T] extends Any with ScalaWholeNumberProxy[T] with RangedProxy[T] { + protected implicit def num: Integral[T] + type ResultWithoutStep = NumericRange[T] + + def until(end: T): NumericRange.Exclusive[T] = NumericRange(self, end, num.one) + def until(end: T, step: T): NumericRange.Exclusive[T] = NumericRange(self, end, step) + def to(end: T): NumericRange.Inclusive[T] = NumericRange.inclusive(self, end, num.one) + def to(end: T, step: T): NumericRange.Inclusive[T] = NumericRange.inclusive(self, end, step) +} +trait FractionalProxy[T] extends Any with ScalaNumberProxy[T] { + protected implicit def num: Fractional[T] + + def isWhole = false +} + +@nowarn("cat=deprecation") +trait OrderedProxy[T] extends Any with Ordered[T] with Typed[T] { + protected def ord: Ordering[T] + + def compare(y: T) = ord.compare(self, y) +} + +@nowarn("cat=deprecation") +trait RangedProxy[T] extends Any with Typed[T] { + type ResultWithoutStep + + def until(end: T): ResultWithoutStep + def until(end: T, step: T): immutable.IndexedSeq[T] + def to(end: T): ResultWithoutStep + def to(end: T, step: T): immutable.IndexedSeq[T] +} + diff --git a/library/src/scala/runtime/ScalaRunTime.scala b/library/src/scala/runtime/ScalaRunTime.scala new file mode 100644 index 000000000000..0e1884495cf1 --- /dev/null +++ b/library/src/scala/runtime/ScalaRunTime.scala @@ -0,0 +1,295 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +import scala.collection.{AbstractIterator, AnyConstr, SortedOps, StrictOptimizedIterableOps, StringOps, StringView, View} +import scala.collection.generic.IsIterable +import scala.collection.immutable.{ArraySeq, NumericRange} +import scala.collection.mutable.StringBuilder +import scala.math.min +import scala.reflect.{ClassTag, classTag} +import java.lang.{Class => jClass} +import java.lang.reflect.{Method => JMethod} + +/** The object ScalaRunTime provides support methods required by + * the scala runtime. All these methods should be considered + * outside the API and subject to change or removal without notice. + */ +object ScalaRunTime { + def isArray(x: Any, atLevel: Int = 1): Boolean = + x != null && isArrayClass(x.getClass, atLevel) + + private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = + clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) + + // A helper method to make my life in the pattern matcher a lot easier. + def drop[Repr](coll: Repr, num: Int)(implicit iterable: IsIterable[Repr] { type C <: Repr }): Repr = + iterable(coll) drop num + + /** Return the class object representing an array with element class `clazz`. + */ + def arrayClass(clazz: jClass[_]): jClass[_] = { + // newInstance throws an exception if the erasure is Void.TYPE. see scala/bug#5680 + if (clazz == java.lang.Void.TYPE) classOf[Array[Unit]] + else java.lang.reflect.Array.newInstance(clazz, 0).getClass + } + + /** Return the class object representing an unboxed value type, + * e.g., classOf[int], not classOf[java.lang.Integer]. The compiler + * rewrites expressions like 5.getClass to come here. + */ + def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = + classTag[T].runtimeClass.asInstanceOf[jClass[T]] + + /** Retrieve generic array element */ + def array_apply(xs: AnyRef, idx: Int): Any = { + (xs: @unchecked) match { + case x: Array[AnyRef] => x(idx).asInstanceOf[Any] + case x: Array[Int] => x(idx).asInstanceOf[Any] + case x: Array[Double] => x(idx).asInstanceOf[Any] + case x: Array[Long] => x(idx).asInstanceOf[Any] + case x: Array[Float] => x(idx).asInstanceOf[Any] + case x: Array[Char] => x(idx).asInstanceOf[Any] + case x: Array[Byte] => x(idx).asInstanceOf[Any] + case x: Array[Short] => x(idx).asInstanceOf[Any] + case x: Array[Boolean] => x(idx).asInstanceOf[Any] + case null => throw new NullPointerException + } + } + + /** update generic array element */ + def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { + (xs: @unchecked) match { + case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] + case x: Array[Int] => x(idx) = value.asInstanceOf[Int] + case x: Array[Double] => x(idx) = value.asInstanceOf[Double] + case x: Array[Long] => x(idx) = value.asInstanceOf[Long] + case x: Array[Float] => x(idx) = value.asInstanceOf[Float] + case x: Array[Char] => x(idx) = value.asInstanceOf[Char] + case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] + case x: Array[Short] => x(idx) = value.asInstanceOf[Short] + case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] + case null => throw new NullPointerException + } + } + + /** Get generic array length */ + @inline def array_length(xs: AnyRef): Int = java.lang.reflect.Array.getLength(xs) + + // TODO: bytecode Object.clone() will in fact work here and avoids + // the type switch. See Array_clone comment in BCodeBodyBuilder. + def array_clone(xs: AnyRef): AnyRef = (xs: @unchecked) match { + case x: Array[AnyRef] => x.clone() + case x: Array[Int] => x.clone() + case x: Array[Double] => x.clone() + case x: Array[Long] => x.clone() + case x: Array[Float] => x.clone() + case x: Array[Char] => x.clone() + case x: Array[Byte] => x.clone() + case x: Array[Short] => x.clone() + case x: Array[Boolean] => x.clone() + case null => throw new NullPointerException + } + + /** Convert an array to an object array. + * Needed to deal with vararg arguments of primitive types that are passed + * to a generic Java vararg parameter T ... + */ + def toObjectArray(src: AnyRef): Array[Object] = { + def copy[@specialized T <: AnyVal](src: Array[T]): Array[Object] = { + val length = src.length + if (length == 0) Array.emptyObjectArray + else { + val dest = new Array[Object](length) + var i = 0 + while (i < length) { + dest(i) = src(i).asInstanceOf[AnyRef] + i += 1 + } + dest + } + } + (src: @unchecked) match { + case x: Array[AnyRef] => x + case x: Array[Int] => copy(x) + case x: Array[Double] => copy(x) + case x: Array[Long] => copy(x) + case x: Array[Float] => copy(x) + case x: Array[Char] => copy(x) + case x: Array[Byte] => copy(x) + case x: Array[Short] => copy(x) + case x: Array[Boolean] => copy(x) + case null => throw new NullPointerException + } + } + + def toArray[T](xs: scala.collection.Seq[T]) = { + if (xs.isEmpty) Array.emptyObjectArray + else { + val arr = new Array[AnyRef](xs.length) + val it = xs.iterator + var i = 0 + while (it.hasNext) { + arr(i) = it.next().asInstanceOf[AnyRef] + i += 1 + } + arr + } + } + + // Java bug: https://bugs.java.com/view_bug.do?bug_id=4071957 + // More background at ticket #2318. + def ensureAccessible(m: JMethod): JMethod = scala.reflect.ensureAccessible(m) + + def _toString(x: Product): String = + x.productIterator.mkString(x.productPrefix + "(", ",", ")") + + def _hashCode(x: Product): Int = scala.util.hashing.MurmurHash3.productHash(x) + + /** A helper for case classes. */ + def typedProductIterator[T](x: Product): Iterator[T] = { + new AbstractIterator[T] { + private[this] var c: Int = 0 + private[this] val cmax = x.productArity + def hasNext = c < cmax + def next() = { + val result = x.productElement(c) + c += 1 + result.asInstanceOf[T] + } + } + } + + /** Given any Scala value, convert it to a String. + * + * The primary motivation for this method is to provide a means for + * correctly obtaining a String representation of a value, while + * avoiding the pitfalls of naively calling toString on said value. + * In particular, it addresses the fact that (a) toString cannot be + * called on null and (b) depending on the apparent type of an + * array, toString may or may not print it in a human-readable form. + * + * @param arg the value to stringify + * @return a string representation of arg. + */ + def stringOf(arg: Any): String = stringOf(arg, scala.Int.MaxValue) + def stringOf(arg: Any, maxElements: Int): String = { + def packageOf(x: AnyRef) = x.getClass.getPackage match { + case null => "" + case p => p.getName + } + def isScalaClass(x: AnyRef) = packageOf(x) startsWith "scala." + def isScalaCompilerClass(x: AnyRef) = packageOf(x) startsWith "scala.tools.nsc." + + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") + + // We use reflection because the scala.xml package might not be available + def isSubClassOf(potentialSubClass: Class[_], ofClass: String) = + try { + val classLoader = potentialSubClass.getClassLoader + val clazz = Class.forName(ofClass, /*initialize =*/ false, classLoader) + clazz.isAssignableFrom(potentialSubClass) + } catch { + case cnfe: ClassNotFoundException => false + } + def isXmlNode(potentialSubClass: Class[_]) = isSubClassOf(potentialSubClass, "scala.xml.Node") + def isXmlMetaData(potentialSubClass: Class[_]) = isSubClassOf(potentialSubClass, "scala.xml.MetaData") + + // When doing our own iteration is dangerous + def useOwnToString(x: Any) = x match { + // Range/NumericRange have a custom toString to avoid walking a gazillion elements + case _: Range | _: NumericRange[_] => true + // Sorted collections to the wrong thing (for us) on iteration - ticket #3493 + case _: SortedOps[_, _] => true + // StringBuilder(a, b, c) and similar not so attractive + case _: StringView | _: StringOps | _: StringBuilder => true + // Don't want to evaluate any elements in a view + case _: View[_] => true + // Node extends NodeSeq extends Seq[Node] and MetaData extends Iterable[MetaData] + // -> catch those by isXmlNode and isXmlMetaData. + // Don't want to a) traverse infinity or b) be overly helpful with peoples' custom + // collections which may have useful toString methods - ticket #3710 + // or c) print AbstractFiles which are somehow also Iterable[AbstractFile]s. + case x: Iterable[_] => (!x.isInstanceOf[StrictOptimizedIterableOps[_, AnyConstr, _]]) || !isScalaClass(x) || isScalaCompilerClass(x) || isXmlNode(x.getClass) || isXmlMetaData(x.getClass) + // Otherwise, nothing could possibly go wrong + case _ => false + } + + // A variation on inner for maps so they print -> instead of bare tuples + def mapInner(arg: Any): String = arg match { + case (k, v) => inner(k) + " -> " + inner(v) + case _ => inner(arg) + } + + // Special casing Unit arrays, the value class which uses a reference array type. + def arrayToString(x: AnyRef) = { + if (x.getClass.getComponentType == classOf[BoxedUnit]) + (0 until min(array_length(x), maxElements)).map(_ => "()").mkString("Array(", ", ", ")") + else + x.asInstanceOf[Array[_]].iterator.take(maxElements).map(inner).mkString("Array(", ", ", ")") + } + + // The recursively applied attempt to prettify Array printing. + // Note that iterator is used if possible and foreach is used as a + // last resort, because the parallel collections "foreach" in a + // random order even on sequences. + def inner(arg: Any): String = arg match { + case null => "null" + case "" => "\"\"" + case x: String => if (x.head.isWhitespace || x.last.isWhitespace) "\"" + x + "\"" else x + case x if useOwnToString(x) => x.toString + case x: AnyRef if isArray(x) => arrayToString(x) + case x: scala.collection.Map[_, _] => x.iterator.take(maxElements).map(mapInner).mkString(x.collectionClassName + "(", ", ", ")") + case x: Iterable[_] => x.iterator.take(maxElements).map(inner).mkString(x.collectionClassName + "(", ", ", ")") + case x: Product1[_] if isTuple(x) => "(" + inner(x._1) + ",)" // that special trailing comma + case x: Product if isTuple(x) => x.productIterator.map(inner).mkString("(", ",", ")") + case x => x.toString + } + + // The try/catch is defense against iterables which aren't actually designed + // to be iterated, such as some scala.tools.nsc.io.AbstractFile derived classes. + try inner(arg) + catch { + case _: UnsupportedOperationException | _: AssertionError => "" + arg + } + } + + /** stringOf formatted for use in a repl result. */ + def replStringOf(arg: Any, maxElements: Int): String = + stringOf(arg, maxElements) match { + case null => "null toString" + case s if s.indexOf('\n') >= 0 => "\n" + s + "\n" + case s => s + "\n" + } + + // Convert arrays to immutable.ArraySeq for use with Java varargs: + def genericWrapArray[T](xs: Array[T]): ArraySeq[T] = + if (xs eq null) null + else ArraySeq.unsafeWrapArray(xs) + def wrapRefArray[T <: AnyRef](xs: Array[T]): ArraySeq[T] = { + if (xs eq null) null + else if (xs.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq[T]] + else new ArraySeq.ofRef[T](xs) + } + def wrapIntArray(xs: Array[Int]): ArraySeq[Int] = if (xs ne null) new ArraySeq.ofInt(xs) else null + def wrapDoubleArray(xs: Array[Double]): ArraySeq[Double] = if (xs ne null) new ArraySeq.ofDouble(xs) else null + def wrapLongArray(xs: Array[Long]): ArraySeq[Long] = if (xs ne null) new ArraySeq.ofLong(xs) else null + def wrapFloatArray(xs: Array[Float]): ArraySeq[Float] = if (xs ne null) new ArraySeq.ofFloat(xs) else null + def wrapCharArray(xs: Array[Char]): ArraySeq[Char] = if (xs ne null) new ArraySeq.ofChar(xs) else null + def wrapByteArray(xs: Array[Byte]): ArraySeq[Byte] = if (xs ne null) new ArraySeq.ofByte(xs) else null + def wrapShortArray(xs: Array[Short]): ArraySeq[Short] = if (xs ne null) new ArraySeq.ofShort(xs) else null + def wrapBooleanArray(xs: Array[Boolean]): ArraySeq[Boolean] = if (xs ne null) new ArraySeq.ofBoolean(xs) else null + def wrapUnitArray(xs: Array[Unit]): ArraySeq[Unit] = if (xs ne null) new ArraySeq.ofUnit(xs) else null +} diff --git a/library/src/scala/runtime/ShortRef.java b/library/src/scala/runtime/ShortRef.java new file mode 100644 index 000000000000..11fd2aece720 --- /dev/null +++ b/library/src/scala/runtime/ShortRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class ShortRef implements java.io.Serializable { + private static final long serialVersionUID = 4218441291229072313L; + + public short elem; + public ShortRef(short elem) { this.elem = elem; } + public String toString() { return java.lang.Short.toString(elem); } + + public static ShortRef create(short e) { return new ShortRef(e); } + public static ShortRef zero() { return new ShortRef((short)0); } +} diff --git a/library/src/scala/runtime/Static.java b/library/src/scala/runtime/Static.java new file mode 100644 index 000000000000..1971fe3b463e --- /dev/null +++ b/library/src/scala/runtime/Static.java @@ -0,0 +1,25 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +import java.lang.invoke.*; + +public final class Static { + private Static() { + } + + public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName, MethodType invokedType, MethodHandle handle, Object... args) throws Throwable { + Object value = handle.invokeWithArguments(args); + return new ConstantCallSite(MethodHandles.constant(invokedType.returnType(), value)); + } +} diff --git a/library/src/scala/runtime/Statics.java b/library/src/scala/runtime/Statics.java new file mode 100644 index 000000000000..34dc1818065a --- /dev/null +++ b/library/src/scala/runtime/Statics.java @@ -0,0 +1,199 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; + +/** Not for public consumption. Usage by the runtime only. + */ + +public final class Statics { + public static int mix(int hash, int data) { + int h = mixLast(hash, data); + h = Integer.rotateLeft(h, 13); + return h * 5 + 0xe6546b64; + } + + public static int mixLast(int hash, int data) { + int k = data; + + k *= 0xcc9e2d51; + k = Integer.rotateLeft(k, 15); + k *= 0x1b873593; + + return hash ^ k; + } + + public static int finalizeHash(int hash, int length) { + return avalanche(hash ^ length); + } + + /** Force all bits of the hash to avalanche. Used for finalizing the hash. */ + public static int avalanche(int h) { + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + + return h; + } + + public static int longHash(long lv) { + int iv = (int)lv; + if (iv == lv) + return iv; + + return java.lang.Long.hashCode(lv); + } + + public static int doubleHash(double dv) { + int iv = (int)dv; + if (iv == dv) + return iv; + + long lv = (long)dv; + if (lv == dv) + return java.lang.Long.hashCode(lv); + + float fv = (float)dv; + if (fv == dv) + return java.lang.Float.hashCode(fv); + + return java.lang.Double.hashCode(dv); + } + + public static int floatHash(float fv) { + int iv = (int)fv; + if (iv == fv) + return iv; + + long lv = (long)fv; + if (lv == fv) + return java.lang.Long.hashCode(lv); + + return java.lang.Float.hashCode(fv); + } + + /** + * Hashcode algorithm is driven by the requirements imposed + * by primitive equality semantics, namely that equal objects + * have equal hashCodes. The first priority are the integral/char + * types, which already have the same hashCodes for the same + * values except for Long. So Long's hashCode is altered to + * conform to Int's for all values in Int's range. + * + * Float is problematic because it's far too small to hold + * all the Ints, so for instance Int.MaxValue.toFloat claims + * to be == to each of the largest 64 Ints. There is no way + * to preserve equals/hashCode alignment without compromising + * the hashCode distribution, so Floats are only guaranteed + * to have the same hashCode for whole Floats in the range + * Short.MinValue to Short.MaxValue (2^16 total.) + * + * Double has its hashCode altered to match the entire Int range, + * but is not guaranteed beyond that. (But could/should it be? + * The hashCode is only 32 bits so this is a more tractable + * issue than Float's, but it might be better simply to exclude it.) + * + * Note: BigInt and BigDecimal, being arbitrary precision, could + * be made consistent with all other types for the Int range, but + * as yet have not. + * + * Note: Among primitives, Float.NaN != Float.NaN, but the boxed + * versions are equal. This still needs reconciliation. + */ + public static int anyHash(Object x) { + if (x == null) + return 0; + + if (x instanceof java.lang.Number) { + return anyHashNumber((java.lang.Number) x); + } + + return x.hashCode(); + } + + private static int anyHashNumber(Number x) { + if (x instanceof java.lang.Long) + return longHash(((java.lang.Long)x).longValue()); + + if (x instanceof java.lang.Double) + return doubleHash(((java.lang.Double)x).doubleValue()); + + if (x instanceof java.lang.Float) + return floatHash(((java.lang.Float)x).floatValue()); + + return x.hashCode(); + } + + /** Used as a marker object to return from PartialFunctions */ + public static final Object pfMarker = new Object(); + + // @ForceInline would be nice here. + public static void releaseFence() throws Throwable { + VM.RELEASE_FENCE.invoke(); + } + + final static class VM { + static final MethodHandle RELEASE_FENCE; + + static { + RELEASE_FENCE = mkHandle(); + } + + private static MethodHandle mkHandle() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + return lookup.findStatic(Class.forName("java.lang.invoke.VarHandle"), "releaseFence", MethodType.methodType(Void.TYPE)); + } catch (NoSuchMethodException | ClassNotFoundException e) { + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + return lookup.findVirtual(unsafeClass, "storeFence", MethodType.methodType(void.class)).bindTo(findUnsafe(unsafeClass)); + } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException e1) { + ExceptionInInitializerError error = new ExceptionInInitializerError(e1); + error.addSuppressed(e); + throw error; + } + } catch (IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static Object findUnsafe(Class unsafeClass) throws IllegalAccessException { + Object found = null; + for (Field field : unsafeClass.getDeclaredFields()) { + if (field.getType() == unsafeClass) { + field.setAccessible(true); + found = field.get(null); + break; + } + } + if (found == null) throw new IllegalStateException("No instance of Unsafe found"); + return found; + } + } + + /** + * Just throws an exception. + * Used by the synthetic `productElement` and `productElementName` methods in case classes. + * Delegating the exception-throwing to this function reduces the bytecode size of the case class. + */ + public static final T ioobe(int n) throws IndexOutOfBoundsException { + throw new IndexOutOfBoundsException(String.valueOf(n)); + } + +} diff --git a/library/src/scala/runtime/StructuralCallSite.scala b/library/src/scala/runtime/StructuralCallSite.scala new file mode 100644 index 000000000000..8e245e6c99db --- /dev/null +++ b/library/src/scala/runtime/StructuralCallSite.scala @@ -0,0 +1,46 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +import java.lang.invoke._ +import java.lang.ref.SoftReference +import java.lang.reflect.Method + +final class StructuralCallSite private (callType: MethodType) { + private var cache: SoftReference[MethodCache] = new SoftReference(new EmptyMethodCache) + + val parameterTypes: Array[Class[_]] = callType.parameterArray + + def get: MethodCache = { + var cache = this.cache.get + if (cache == null) { + cache = new EmptyMethodCache + this.cache = new SoftReference(cache) + } + cache + } + + def find(receiver: Class[_]): Method = get.find(receiver) + + def add(receiver: Class[_], m: Method): Method = { + cache = new SoftReference(get.add(receiver, m)) + m + } +} + +object StructuralCallSite { + def bootstrap(lookup: MethodHandles.Lookup, invokedName: String, invokedType: MethodType, reflectiveCallType: MethodType): CallSite = { + val structuralCallSite = new StructuralCallSite(reflectiveCallType) + new ConstantCallSite(MethodHandles.constant(classOf[StructuralCallSite], structuralCallSite)) + } +} diff --git a/library/src/scala/runtime/SymbolLiteral.java b/library/src/scala/runtime/SymbolLiteral.java new file mode 100644 index 000000000000..67f59b15fbe2 --- /dev/null +++ b/library/src/scala/runtime/SymbolLiteral.java @@ -0,0 +1,31 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +import java.lang.invoke.*; + +public final class SymbolLiteral { + private SymbolLiteral() { + } + + public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName, + MethodType invokedType, + String value) throws Throwable { + ClassLoader classLoader = lookup.lookupClass().getClassLoader(); + MethodType type = MethodType.fromMethodDescriptorString("(Ljava/lang/String;)Lscala/Symbol;", classLoader); + Class symbolClass = Class.forName("scala.Symbol", false, classLoader); + MethodHandle factoryMethod = lookup.findStatic(symbolClass, "apply", type); + Object symbolValue = factoryMethod.invokeWithArguments(value); + return new ConstantCallSite(MethodHandles.constant(symbolClass, symbolValue)); + } +} diff --git a/library/src/scala/runtime/Tuple2Zipped.scala b/library/src/scala/runtime/Tuple2Zipped.scala new file mode 100644 index 000000000000..e49bf9e1aba6 --- /dev/null +++ b/library/src/scala/runtime/Tuple2Zipped.scala @@ -0,0 +1,144 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + + +import scala.collection.{BuildFrom, IterableOps} +import scala.language.implicitConversions + +/** This interface is intended as a minimal interface, not complicated + * by the requirement to resolve type constructors, for implicit search (which only + * needs to find an implicit conversion to Iterable for our purposes.) + * @define Coll `ZippedIterable2` + * @define coll collection + * @define collectExample + * @define willNotTerminateInf + */ +@deprecated("Use scala.collection.LazyZip2.", "2.13.0") +trait ZippedIterable2[+El1, +El2] extends Any { + def iterator: Iterator[(El1, El2)] + def isEmpty: Boolean +} +@deprecated("Use scala.collection.LazyZip2.", "2.13.0") +object ZippedIterable2 { + implicit def zippedIterable2ToIterable[El1, El2](zz: ZippedIterable2[El1, El2]): Iterable[(El1, El2)] = { + new scala.collection.AbstractIterable[(El1, El2)] { + def iterator: Iterator[(El1, El2)] = zz.iterator + override def isEmpty: Boolean = zz.isEmpty + } + } +} + +@deprecated("Use scala.collection.LazyZip2.", "2.13.0") +final class Tuple2Zipped[El1, It1 <: Iterable[El1], El2, It2 <: Iterable[El2]](private val colls: (It1, It2)) extends AnyVal with ZippedIterable2[El1, El2] { + private def coll1 = colls._1 + private def coll2 = colls._2 + + def map[B, To](f: (El1, El2) => B)(implicit bf: BuildFrom[It1, B, To]): To = { + val b = bf.newBuilder(coll1) + b.sizeHint(coll1, delta = 0) + val elems1 = coll1.iterator + val elems2 = coll2.iterator + + while (elems1.hasNext && elems2.hasNext) { + b += f(elems1.next(), elems2.next()) + } + + b.result() + } + + def flatMap[B, To](f: (El1, El2) => IterableOnce[B])(implicit bf: BuildFrom[It1, B, To]): To = { + val b = bf.newBuilder(coll1) + val elems1 = coll1.iterator + val elems2 = coll2.iterator + + while (elems1.hasNext && elems2.hasNext) { + b ++= f(elems1.next(), elems2.next()) + } + + b.result() + } + + def filter[To1, To2](f: (El1, El2) => Boolean)(implicit bf1: BuildFrom[It1, El1, To1], bf2: BuildFrom[It2, El2, To2]): (To1, To2) = { + val b1 = bf1.newBuilder(coll1) + val b2 = bf2.newBuilder(coll2) + val elems1 = coll1.iterator + val elems2 = coll2.iterator + + while (elems1.hasNext && elems2.hasNext) { + val el1 = elems1.next() + val el2 = elems2.next() + if (f(el1, el2)) { + b1 += el1 + b2 += el2 + } + } + + (b1.result(), b2.result()) + } + + def exists(p: (El1, El2) => Boolean): Boolean = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + + while (elems1.hasNext && elems2.hasNext) { + if (p(elems1.next(), elems2.next())) { + return true + } + } + false + } + + def forall(p: (El1, El2) => Boolean): Boolean = + !exists((x, y) => !p(x, y)) + + def iterator: Iterator[(El1, El2)] = coll1.iterator.zip(coll2.iterator) + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty + def foreach[U](f: (El1, El2) => U): Unit = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + + while (elems1.hasNext && elems2.hasNext) { + f(elems1.next(), elems2.next()) + } + } + + override def toString = s"($coll1, $coll2).zipped" +} + +@deprecated("Use scala.collection.LazyZip2.", since = "2.13.0") +object Tuple2Zipped { + final class Ops[T1, T2](private val x: (T1, T2)) extends AnyVal { + @deprecated("Use xs.lazyZip(yz).map((_, _))", since = "2.13.0") + def invert[El1, It1[a] <: Iterable[a], El2, It2[a] <: Iterable[a], That] + (implicit w1: T1 <:< It1[El1], + w2: T2 <:< It2[El2], + bf: BuildFrom[T1, (El1, El2), That] + ): That = { + val buf = bf.newBuilder(x._1) + val it1 = x._1.iterator + val it2 = x._2.iterator + while (it1.hasNext && it2.hasNext) + buf += ((it1.next(), it2.next())) + + buf.result() + } + + @deprecated("Use xs.lazyZip(ys)", since = "2.13.0") + def zipped[El1, It1 <: Iterable[El1], El2, It2 <: Iterable[El2]] + (implicit w1: T1 => IterableOps[El1, Iterable, It1] with It1, + w2: T2 => IterableOps[El2, Iterable, It2] with It2 + ): Tuple2Zipped[El1, It1, El2, It2] = new Tuple2Zipped((w1(x._1), w2(x._2))) + } +} diff --git a/library/src/scala/runtime/Tuple3Zipped.scala b/library/src/scala/runtime/Tuple3Zipped.scala new file mode 100644 index 000000000000..b1e8763f9891 --- /dev/null +++ b/library/src/scala/runtime/Tuple3Zipped.scala @@ -0,0 +1,156 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + + +import scala.collection.{BuildFrom, IterableOps} +import scala.language.implicitConversions + +/** See comment on ZippedIterable2 + * @define Coll `ZippedIterable3` + * @define coll collection + * @define collectExample + * @define willNotTerminateInf + */ +@deprecated("Use scala.collection.LazyZip3.", "2.13.0") +trait ZippedIterable3[+El1, +El2, +El3] extends Any { + def iterator: Iterator[(El1, El2, El3)] + def isEmpty: Boolean +} +@deprecated("Use scala.collection.LazyZip3.", "2.13.0") +object ZippedIterable3 { + implicit def zippedIterable3ToIterable[El1, El2, El3](zz: ZippedIterable3[El1, El2, El3]): Iterable[(El1, El2, El3)] = { + new scala.collection.AbstractIterable[(El1, El2, El3)] { + def iterator: Iterator[(El1, El2, El3)] = zz.iterator + override def isEmpty: Boolean = zz.isEmpty + } + } +} + +@deprecated("Use scala.collection.LazyZip3.", "2.13.0") +final class Tuple3Zipped[El1, It1 <: Iterable[El1], El2, It2 <: Iterable[El2], El3, It3 <: Iterable[El3]](private val colls: (It1, It2, It3)) + extends AnyVal with ZippedIterable3[El1, El2, El3] { + + private def coll1 = colls._1 + private def coll2 = colls._2 + private def coll3 = colls._3 + + def map[B, To](f: (El1, El2, El3) => B)(implicit bf: BuildFrom[It1, B, To]): To = { + val b = bf.newBuilder(coll1) + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + + while (elems1.hasNext && elems2.hasNext && elems3.hasNext) { + b += f(elems1.next(), elems2.next(), elems3.next()) + } + b.result() + } + + def flatMap[B, To](f: (El1, El2, El3) => IterableOnce[B])(implicit bf: BuildFrom[It1, B, To]): To = { + val b = bf.newBuilder(coll1) + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + + while (elems1.hasNext && elems2.hasNext && elems3.hasNext) { + b ++= f(elems1.next(), elems2.next(), elems3.next()) + } + b.result() + } + + def filter[To1, To2, To3](f: (El1, El2, El3) => Boolean)( + implicit bf1: BuildFrom[It1, El1, To1], + bf2: BuildFrom[It2, El2, To2], + bf3: BuildFrom[It3, El3, To3]): (To1, To2, To3) = { + val b1 = bf1.newBuilder(coll1) + val b2 = bf2.newBuilder(coll2) + val b3 = bf3.newBuilder(coll3) + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + + while (elems1.hasNext && elems2.hasNext && elems3.hasNext) { + val el1 = elems1.next() + val el2 = elems2.next() + val el3 = elems3.next() + + if (f(el1, el2, el3)) { + b1 += el1 + b2 += el2 + b3 += el3 + } + } + (b1.result(), b2.result(), b3.result()) + } + + def exists(p: (El1, El2, El3) => Boolean): Boolean = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + + while (elems1.hasNext && elems2.hasNext && elems3.hasNext) { + if (p(elems1.next(), elems2.next(), elems3.next())) { + return true + } + } + false + } + + def forall(p: (El1, El2, El3) => Boolean): Boolean = + !exists((x, y, z) => !p(x, y, z)) + + def iterator: Iterator[(El1, El2, El3)] = coll1.iterator.zip(coll2.iterator).zip(coll3.iterator).map { case ((a, b), c) => (a, b, c)} + override def isEmpty: Boolean = coll1.isEmpty || coll2.isEmpty || coll3.isEmpty + def foreach[U](f: (El1, El2, El3) => U): Unit = { + val elems1 = coll1.iterator + val elems2 = coll2.iterator + val elems3 = coll3.iterator + + while (elems1.hasNext && elems2.hasNext && elems3.hasNext) { + f(elems1.next(), elems2.next(), elems3.next()) + } + } + + override def toString = s"($coll1, $coll2, $coll3).zipped" +} + +@deprecated("Use scala.collection.LazyZip3.", since = "2.13.0") +object Tuple3Zipped { + final class Ops[T1, T2, T3](private val x: (T1, T2, T3)) extends AnyVal { + @deprecated("Use xs.lazyZip(yz).lazyZip(zs).map((_, _, _))", since = "2.13.0") + def invert[El1, It1[a] <: Iterable[a], El2, It2[a] <: Iterable[a], El3, It3[a] <: Iterable[a], That] + (implicit w1: T1 <:< It1[El1], + w2: T2 <:< It2[El2], + w3: T3 <:< It3[El3], + bf: BuildFrom[T1, (El1, El2, El3), That] + ): That = { + val buf = bf.newBuilder(x._1) + val it1 = x._1.iterator + val it2 = x._2.iterator + val it3 = x._3.iterator + while (it1.hasNext && it2.hasNext && it3.hasNext) + buf += ((it1.next(), it2.next(), it3.next())) + + buf.result() + } + + @deprecated("Use xs.lazyZip(ys).lazyZip(zs)", since = "2.13.0") + def zipped[El1, It1 <: Iterable[El1], El2, It2 <: Iterable[El2], El3, It3 <: Iterable[El3]] + (implicit w1: T1 => IterableOps[El1, Iterable, It1] with It1, + w2: T2 => IterableOps[El2, Iterable, It2] with It2, + w3: T3 => IterableOps[El3, Iterable, It3] with It3 + ): Tuple3Zipped[El1, It1, El2, It2, El3, It3] = new Tuple3Zipped((w1(x._1), w2(x._2), w3(x._3))) + } +} diff --git a/library/src/scala/runtime/VolatileBooleanRef.java b/library/src/scala/runtime/VolatileBooleanRef.java new file mode 100644 index 000000000000..0436cf5ee882 --- /dev/null +++ b/library/src/scala/runtime/VolatileBooleanRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileBooleanRef implements java.io.Serializable { + private static final long serialVersionUID = -5730524563015615974L; + + volatile public boolean elem; + public VolatileBooleanRef(boolean elem) { this.elem = elem; } + public String toString() { return String.valueOf(elem); } + + public static VolatileBooleanRef create(boolean e) { return new VolatileBooleanRef(e); } + public static VolatileBooleanRef zero() { return new VolatileBooleanRef(false); } +} diff --git a/library/src/scala/runtime/VolatileByteRef.java b/library/src/scala/runtime/VolatileByteRef.java new file mode 100644 index 000000000000..23ea7ce3d32e --- /dev/null +++ b/library/src/scala/runtime/VolatileByteRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileByteRef implements java.io.Serializable { + private static final long serialVersionUID = -100666928446877072L; + + volatile public byte elem; + public VolatileByteRef(byte elem) { this.elem = elem; } + public String toString() { return java.lang.Byte.toString(elem); } + + public static VolatileByteRef create(byte e) { return new VolatileByteRef(e); } + public static VolatileByteRef zero() { return new VolatileByteRef((byte)0); } +} diff --git a/library/src/scala/runtime/VolatileCharRef.java b/library/src/scala/runtime/VolatileCharRef.java new file mode 100644 index 000000000000..b8d11584556a --- /dev/null +++ b/library/src/scala/runtime/VolatileCharRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileCharRef implements java.io.Serializable { + private static final long serialVersionUID = 6537214938268005702L; + + volatile public char elem; + public VolatileCharRef(char elem) { this.elem = elem; } + public String toString() { return java.lang.Character.toString(elem); } + + public static VolatileCharRef create(char e) { return new VolatileCharRef(e); } + public static VolatileCharRef zero() { return new VolatileCharRef((char)0); } +} diff --git a/library/src/scala/runtime/VolatileDoubleRef.java b/library/src/scala/runtime/VolatileDoubleRef.java new file mode 100644 index 000000000000..809a27040540 --- /dev/null +++ b/library/src/scala/runtime/VolatileDoubleRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileDoubleRef implements java.io.Serializable { + private static final long serialVersionUID = 8304402127373655534L; + + volatile public double elem; + public VolatileDoubleRef(double elem) { this.elem = elem; } + public String toString() { return java.lang.Double.toString(elem); } + + public static VolatileDoubleRef create(double e) { return new VolatileDoubleRef(e); } + public static VolatileDoubleRef zero() { return new VolatileDoubleRef(0); } +} diff --git a/library/src/scala/runtime/VolatileFloatRef.java b/library/src/scala/runtime/VolatileFloatRef.java new file mode 100644 index 000000000000..954c7522c407 --- /dev/null +++ b/library/src/scala/runtime/VolatileFloatRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileFloatRef implements java.io.Serializable { + private static final long serialVersionUID = -5793980990371366933L; + + volatile public float elem; + public VolatileFloatRef(float elem) { this.elem = elem; } + public String toString() { return java.lang.Float.toString(elem); } + + public static VolatileFloatRef create(float e) { return new VolatileFloatRef(e); } + public static VolatileFloatRef zero() { return new VolatileFloatRef(0); } +} diff --git a/library/src/scala/runtime/VolatileIntRef.java b/library/src/scala/runtime/VolatileIntRef.java new file mode 100644 index 000000000000..a3d2c33eab71 --- /dev/null +++ b/library/src/scala/runtime/VolatileIntRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileIntRef implements java.io.Serializable { + private static final long serialVersionUID = 1488197132022872888L; + + volatile public int elem; + public VolatileIntRef(int elem) { this.elem = elem; } + public String toString() { return java.lang.Integer.toString(elem); } + + public static VolatileIntRef create(int e) { return new VolatileIntRef(e); } + public static VolatileIntRef zero() { return new VolatileIntRef(0); } +} diff --git a/library/src/scala/runtime/VolatileLongRef.java b/library/src/scala/runtime/VolatileLongRef.java new file mode 100644 index 000000000000..9e93e0b49e3b --- /dev/null +++ b/library/src/scala/runtime/VolatileLongRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileLongRef implements java.io.Serializable { + private static final long serialVersionUID = -3567869820105829499L; + + volatile public long elem; + public VolatileLongRef(long elem) { this.elem = elem; } + public String toString() { return java.lang.Long.toString(elem); } + + public static VolatileLongRef create(long e) { return new VolatileLongRef(e); } + public static VolatileLongRef zero() { return new VolatileLongRef(0); } +} diff --git a/library/src/scala/runtime/VolatileObjectRef.java b/library/src/scala/runtime/VolatileObjectRef.java new file mode 100644 index 000000000000..78aef1eaff26 --- /dev/null +++ b/library/src/scala/runtime/VolatileObjectRef.java @@ -0,0 +1,25 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileObjectRef implements java.io.Serializable { + private static final long serialVersionUID = -9055728157600312291L; + + volatile public T elem; + public VolatileObjectRef(T elem) { this.elem = elem; } + @Override + public String toString() { return String.valueOf(elem); } + + public static VolatileObjectRef create(U e) { return new VolatileObjectRef(e); } + public static VolatileObjectRef zero() { return new VolatileObjectRef(null); } +} diff --git a/library/src/scala/runtime/VolatileShortRef.java b/library/src/scala/runtime/VolatileShortRef.java new file mode 100644 index 000000000000..87a0c12dd7ed --- /dev/null +++ b/library/src/scala/runtime/VolatileShortRef.java @@ -0,0 +1,24 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime; + +public final class VolatileShortRef implements java.io.Serializable { + private static final long serialVersionUID = 4218441291229072313L; + + volatile public short elem; + public VolatileShortRef(short elem) { this.elem = elem; } + public String toString() { return java.lang.Short.toString(elem); } + + public static VolatileShortRef create(short e) { return new VolatileShortRef(e); } + public static VolatileShortRef zero() { return new VolatileShortRef((short)0); } +} From 03f378962c35a8cbf8513018ca6d517f0f4dd949 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:40:57 +0100 Subject: [PATCH 18/28] chore: add all the files in scala.sys --- library/src/scala/sys/BooleanProp.scala | 88 ++++ library/src/scala/sys/Prop.scala | 93 ++++ library/src/scala/sys/PropImpl.scala | 52 ++ .../src/scala/sys/ShutdownHookThread.scala | 37 ++ library/src/scala/sys/SystemProperties.scala | 92 ++++ library/src/scala/sys/package.scala | 97 ++++ library/src/scala/sys/process/BasicIO.scala | 274 ++++++++++ library/src/scala/sys/process/Parser.scala | 116 +++++ library/src/scala/sys/process/Process.scala | 222 ++++++++ .../scala/sys/process/ProcessBuilder.scala | 479 ++++++++++++++++++ .../sys/process/ProcessBuilderImpl.scala | 274 ++++++++++ library/src/scala/sys/process/ProcessIO.scala | 72 +++ .../src/scala/sys/process/ProcessImpl.scala | 287 +++++++++++ .../src/scala/sys/process/ProcessLogger.scala | 105 ++++ library/src/scala/sys/process/package.scala | 260 ++++++++++ 15 files changed, 2548 insertions(+) create mode 100644 library/src/scala/sys/BooleanProp.scala create mode 100644 library/src/scala/sys/Prop.scala create mode 100644 library/src/scala/sys/PropImpl.scala create mode 100644 library/src/scala/sys/ShutdownHookThread.scala create mode 100644 library/src/scala/sys/SystemProperties.scala create mode 100644 library/src/scala/sys/package.scala create mode 100644 library/src/scala/sys/process/BasicIO.scala create mode 100644 library/src/scala/sys/process/Parser.scala create mode 100644 library/src/scala/sys/process/Process.scala create mode 100644 library/src/scala/sys/process/ProcessBuilder.scala create mode 100644 library/src/scala/sys/process/ProcessBuilderImpl.scala create mode 100644 library/src/scala/sys/process/ProcessIO.scala create mode 100644 library/src/scala/sys/process/ProcessImpl.scala create mode 100644 library/src/scala/sys/process/ProcessLogger.scala create mode 100644 library/src/scala/sys/process/package.scala diff --git a/library/src/scala/sys/BooleanProp.scala b/library/src/scala/sys/BooleanProp.scala new file mode 100644 index 000000000000..70066c070c37 --- /dev/null +++ b/library/src/scala/sys/BooleanProp.scala @@ -0,0 +1,88 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys + +import scala.language.implicitConversions + +/** A few additional conveniences for Boolean properties. + */ +trait BooleanProp extends Prop[Boolean] { + /** The semantics of value are determined at Prop creation. See methods + * `valueIsTrue` and `keyExists` in object BooleanProp for examples. + * + * @return true if the current String is considered true, false otherwise + */ + def value: Boolean + + /** Alter this property so that `value` will be true. */ + def enable(): Unit + + /** Alter this property so that `value` will be false. */ + def disable(): Unit + + /** Toggle the property between enabled and disabled states. */ + def toggle(): Unit +} + +object BooleanProp { + private[sys] + class BooleanPropImpl(key: String, valueFn: String => Boolean) extends PropImpl(key, valueFn) with BooleanProp { + override def setValue[T1 >: Boolean](newValue: T1): Boolean = newValue match { + case x: Boolean if !x => val old = value ; clear() ; old + case x => super.setValue(newValue) + } + def enable() = this setValue true + def disable() = this.clear() + def toggle() = if (value) disable() else enable() + } + private[sys] + class ConstantImpl(val key: String, val value: Boolean) extends BooleanProp { + val isSet = value + def set(newValue: String) = "" + value + def setValue[T1 >: Boolean](newValue: T1): Boolean = value + def get: String = "" + value + def option = if (isSet) Some(value) else None + //def or[T1 >: Boolean](alt: => T1): T1 = if (value) true else alt + + def clear() = () + def enable() = () + def disable() = () + def toggle() = () + + protected def zero = false + } + + /** The java definition of property truth is that the key be in the map and + * the value be equal to the String "true", case insensitively. This method + * creates a BooleanProp instance which adheres to that definition. + * + * @return A BooleanProp which acts like java's Boolean.getBoolean + */ + def valueIsTrue[T](key: String): BooleanProp = new BooleanPropImpl(key, _.toLowerCase == "true") + + /** As an alternative, this method creates a BooleanProp which is true + * if the key exists in the map and is not assigned a value other than "true", + * compared case-insensitively, or the empty string. This way -Dmy.property + * results in a true-valued property, but -Dmy.property=false does not. + * + * @return A BooleanProp with a liberal truth policy + */ + def keyExists[T](key: String): BooleanProp = new BooleanPropImpl(key, s => s == "" || s.equalsIgnoreCase("true")) + + /** A constant true or false property which ignores all method calls. + */ + def constant(key: String, isOn: Boolean): BooleanProp = new ConstantImpl(key, isOn) + + implicit def booleanPropAsBoolean(b: BooleanProp): Boolean = b.value +} diff --git a/library/src/scala/sys/Prop.scala b/library/src/scala/sys/Prop.scala new file mode 100644 index 000000000000..7645e4ac8993 --- /dev/null +++ b/library/src/scala/sys/Prop.scala @@ -0,0 +1,93 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys + +/** A lightweight interface wrapping a property contained in some + * unspecified map. Generally it'll be the system properties but this + * is not a requirement. + * + * See `scala.sys.SystemProperties` for an example usage. + */ +trait Prop[+T] { + /** The full name of the property, e.g., "java.awt.headless". + */ + def key: String + + /** If the key exists in the properties map, converts the value + * to type `T` using valueFn. As yet no validation is performed: + * it will throw an exception on a failed conversion. + * @return the converted value, or `zero` if not in the map + */ + def value: T + + /** True if the key exists in the properties map. Note that this + * is not sufficient for a Boolean property to be considered true. + * @return whether the map contains the key + */ + def isSet: Boolean + + /** Sets the property. + * + * @param newValue the new string value + * @return the old value, or null if it was unset. + */ + def set(newValue: String): String + + /** Sets the property with a value of the represented type. + */ + def setValue[T1 >: T](value: T1): T + + /** Gets the current string value if any. Will not return null: use + * `isSet` to test for existence. + * @return the current string value if any, else the empty string + */ + def get: String + + /** Some(value) if the property is set, None otherwise. + */ + def option: Option[T] + + // Do not open until 2.12. + //** This value if the property is set, an alternative value otherwise. */ + //def or[T1 >: T](alt: => T1): T1 + + /** Removes the property from the underlying map. + */ + def clear(): Unit + + /** A value of type `T` for use when the property is unset. + * The default implementation delivers null for reference types + * and 0/0.0/false for non-reference types. + */ + protected def zero: T +} + +object Prop { + /** A creator of property instances. For any type `T`, if an implicit + * parameter of type Creator[T] is in scope, a Prop[T] can be created + * via this object's apply method. + */ + @annotation.implicitNotFound("No implicit property creator available for type ${T}.") + trait Creator[+T] { + /** Creates a Prop[T] of this type based on the given key. */ + def apply(key: String): Prop[T] + } + + implicit object FileProp extends CreatorImpl[java.io.File](s => new java.io.File(s)) + implicit object StringProp extends CreatorImpl[String](s => s) + implicit object IntProp extends CreatorImpl[Int](_.toInt) + implicit object DoubleProp extends CreatorImpl[Double](_.toDouble) + + def apply[T: Creator](key: String): Prop[T] = implicitly[Creator[T]] apply key +} diff --git a/library/src/scala/sys/PropImpl.scala b/library/src/scala/sys/PropImpl.scala new file mode 100644 index 000000000000..390c5c9c576d --- /dev/null +++ b/library/src/scala/sys/PropImpl.scala @@ -0,0 +1,52 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys + +import scala.collection.mutable + +/** The internal implementation of scala.sys.Prop. + */ +private[sys] class PropImpl[+T](val key: String, valueFn: String => T) extends Prop[T] { + def value: T = if (isSet) valueFn(get) else zero + def isSet = underlying contains key + def set(newValue: String): String = { + val old = if (isSet) get else null + underlying(key) = newValue + old + } + def setValue[T1 >: T](newValue: T1): T = { + val old = value + if (newValue == null) set(null) + else set("" + newValue) + old + } + def get: String = + if (isSet) underlying.getOrElse(key, "") + else "" + + def clear(): Unit = underlying -= key + def option: Option[T] = if (isSet) Some(value) else None + def or[T1 >: T](alt: => T1): T1 = if (isSet) value else alt + + /** The underlying property map, in our case always sys.props */ + protected def underlying: mutable.Map[String, String] = scala.sys.props + protected def zero: T = null.asInstanceOf[T] + private def getString = if (isSet) "currently: " + get else "unset" + override def toString = "%s (%s)".format(key, getString) +} + +private[sys] abstract class CreatorImpl[+T](f: String => T) extends Prop.Creator[T] { + def apply(key: String): Prop[T] = new PropImpl[T](key, f) +} + diff --git a/library/src/scala/sys/ShutdownHookThread.scala b/library/src/scala/sys/ShutdownHookThread.scala new file mode 100644 index 000000000000..d7a8ae5bb3ca --- /dev/null +++ b/library/src/scala/sys/ShutdownHookThread.scala @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys + +/** A minimal Thread wrapper to enhance shutdown hooks. It knows + * how to unregister itself. + */ +class ShutdownHookThread private (runnable: Runnable, name: String) extends Thread(runnable, name) { + def remove() = Runtime.getRuntime removeShutdownHook this +} + +object ShutdownHookThread { + private[this] var hookNameCount: Int = 0 + private def hookName(): String = synchronized { + hookNameCount += 1 + "shutdownHook" + hookNameCount + } + /** Creates, names, and registers a shutdown hook to run the + * given code. + */ + def apply(body: => Unit): ShutdownHookThread = { + val t = new ShutdownHookThread(() => body, hookName()) + Runtime.getRuntime addShutdownHook t + t + } +} diff --git a/library/src/scala/sys/SystemProperties.scala b/library/src/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..1f848a73358e --- /dev/null +++ b/library/src/scala/sys/SystemProperties.scala @@ -0,0 +1,92 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys + +import scala.collection.{mutable, Iterator} +import scala.jdk.CollectionConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + */ +class SystemProperties +extends mutable.AbstractMap[String, String] { + + override def empty: mutable.Map[String, String] = mutable.Map[String, String]() + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = wrapAccess { + val ps = System.getProperties() + names map (k => (k, ps getProperty k)) filter (_._2 ne null) + } getOrElse Iterator.empty + + override def isEmpty: Boolean = iterator.isEmpty + def names: Iterator[String] = wrapAccess ( + System.getProperties().stringPropertyNames().asScala.iterator + ) getOrElse Iterator.empty + + def get(key: String): Option[String] = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String): Boolean = + wrapAccess(super.contains(key)) exists (x => x) + + override def clear(): Unit = wrapAccess(System.getProperties().clear()) + def subtractOne (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def addOne (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + @annotation.nowarn("cat=deprecation") // AccessControlException is deprecated on JDK 17 + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T): T = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + + private final val HeadlessKey = "java.awt.headless" + private final val PreferIPv4StackKey = "java.net.preferIPv4Stack" + private final val PreferIPv6AddressesKey = "java.net.preferIPv6Addresses" + private final val NoTraceSuppressionKey = "scala.control.noTraceSuppression" + + def help(key: String): String = key match { + case HeadlessKey => "system should not utilize a display device" + case PreferIPv4StackKey => "system should prefer IPv4 sockets" + case PreferIPv6AddressesKey => "system should prefer IPv6 addresses" + case NoTraceSuppressionKey => "scala should not suppress any stack trace creation" + case _ => "" + } + + lazy val headless: BooleanProp = BooleanProp.keyExists(HeadlessKey) + lazy val preferIPv4Stack: BooleanProp = BooleanProp.keyExists(PreferIPv4StackKey) + lazy val preferIPv6Addresses: BooleanProp = BooleanProp.keyExists(PreferIPv6AddressesKey) + lazy val noTraceSuppression: BooleanProp = BooleanProp.valueIsTrue(NoTraceSuppressionKey) +} + diff --git a/library/src/scala/sys/package.scala b/library/src/scala/sys/package.scala new file mode 100644 index 000000000000..122f19d12c3a --- /dev/null +++ b/library/src/scala/sys/package.scala @@ -0,0 +1,97 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.collection.immutable.ArraySeq +import scala.jdk.CollectionConverters._ + +/** The package object `scala.sys` contains methods for reading + * and altering core aspects of the virtual machine as well as the + * world outside of it. + */ +package object sys { + /** Throw a new RuntimeException with the supplied message. + * + * @return Nothing. + */ + def error(message: String): Nothing = throw new RuntimeException(message) + + /** Exit the JVM with the default status code. + * + * @return Nothing. + */ + def exit(): Nothing = exit(0) + + /** Exit the JVM with the given status code. + * + * @return Nothing. + */ + def exit(status: Int): Nothing = { + java.lang.System.exit(status) + throw new Throwable() + } + + /** A convenience method to get the current Runtime instance. + * + * @return the result of `java.lang.Runtime.getRuntime()` + */ + def runtime: Runtime = Runtime.getRuntime + + /** A bidirectional, mutable Map representing the current system Properties. + * + * @return a SystemProperties. + * @see [[scala.sys.SystemProperties]] + */ + def props: SystemProperties = new SystemProperties + + // TODO: consider whether layering a Map on top of Java's properties is really needed -- we could simply provide: + // def prop(p: String) = Option(System.getProperty(p)) + + /** An immutable Map representing the current system environment. + * + * If lookup fails, use `System.getenv(_)` for case-insensitive lookup + * on a certain platform. If that also fails, throw `NoSuchElementException`. + * + * @return a Map containing the system environment variables. + */ + def env: Map[String, String] = Map.from(System.getenv().asScala).withDefault { v => + val s = System.getenv(v) + if (s == null) throw new NoSuchElementException(v) + s + } + + /** Register a shutdown hook to be run when the VM exits. + * The hook is automatically registered: the returned value can be ignored, + * but is available in case the Thread requires further modification. + * It can also be unregistered by calling ShutdownHookThread#remove(). + * + * Note that shutdown hooks are NOT guaranteed to be run. + * + * @param body the body of code to run at shutdown + * @return the Thread which will run the shutdown hook. + * @see [[scala.sys.ShutdownHookThread]] + */ + def addShutdownHook(body: => Unit): ShutdownHookThread = ShutdownHookThread(body) + + /** Returns all active thread in the current thread's thread group and subgroups. + * + * @return an IndexedSeq containing the threads. + */ + def allThreads(): IndexedSeq[Thread] = { + val num = Thread.activeCount() + val tarray = new Array[Thread](num) + val got = Thread.enumerate(tarray) + + ArraySeq.unsafeWrapArray(tarray).take(got) + } +} diff --git a/library/src/scala/sys/process/BasicIO.scala b/library/src/scala/sys/process/BasicIO.scala new file mode 100644 index 000000000000..a242fe312bbf --- /dev/null +++ b/library/src/scala/sys/process/BasicIO.scala @@ -0,0 +1,274 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys +package process + +import processInternal._ +import java.io.{ BufferedReader, InputStreamReader, FilterInputStream, FilterOutputStream } +import java.util.concurrent.LinkedBlockingQueue +import scala.annotation.tailrec + +/** + * This object contains factories for [[scala.sys.process.ProcessIO]], + * which can be used to control the I/O of a [[scala.sys.process.Process]] + * when a [[scala.sys.process.ProcessBuilder]] is started with the `run` + * command. + * + * It also contains some helper methods that can be used to in the creation of + * `ProcessIO`. + * + * It is used by other classes in the package in the implementation of various + * features, but can also be used by client code. + */ +object BasicIO { + /** Size of the buffer used in all the functions that copy data */ + final val BufferSize = 8192 + + /** Used to separate lines in the `processFully` function that takes `Appendable`. */ + final val Newline = System.lineSeparator + + private[process] final class LazilyListed[T]( + val process: T => Unit, + val done: Int => Unit, + val lazyList: LazyList[T] + ) + + private[process] object LazilyListed { + def apply[T](nonzeroException: Boolean, capacity: Integer): LazilyListed[T] = { + val queue = new LinkedBlockingQueue[Either[Int, T]](capacity) + val ll = LazyList.unfold(queue) { q => + q.take() match { + case Left(0) => None + case Left(code) => if (nonzeroException) scala.sys.error("Nonzero exit code: " + code) else None + case Right(s) => Some((s, q)) + } + } + new LazilyListed((s: T) => queue put Right(s), code => queue put Left(code), ll) + } + } + + @deprecated("internal", since = "2.13.4") + private[process] final class Streamed[T]( + val process: T => Unit, + val done: Int => Unit, + val stream: () => Stream[T] + ) + + @deprecated("internal", since = "2.13.4") + private[process] object Streamed { + def apply[T](nonzeroException: Boolean, capacity: Integer): Streamed[T] = { + val q = new LinkedBlockingQueue[Either[Int, T]](capacity) + def next(): Stream[T] = q.take() match { + case Left(0) => Stream.empty + case Left(code) => if (nonzeroException) scala.sys.error("Nonzero exit code: " + code) else Stream.empty + case Right(s) => Stream.cons(s, next()) + } + new Streamed((s: T) => q put Right(s), code => q put Left(code), () => next()) + } + } + + private[process] trait Uncloseable extends Closeable { + final override def close(): Unit = () + } + private[process] object Uncloseable { + def apply(in: InputStream): InputStream = new FilterInputStream(in) with Uncloseable { } + def apply(out: OutputStream): OutputStream = new FilterOutputStream(out) with Uncloseable { } + def protect(in: InputStream): InputStream = if (in eq stdin) Uncloseable(in) else in + def protect(out: OutputStream): OutputStream = if ((out eq stdout) || (out eq stderr)) Uncloseable(out) else out + } + + /** Creates a `ProcessIO` from a function `String => Unit`. It can attach the + * process input to stdin, and it will either send the error stream to + * stderr, or to a `ProcessLogger`. + * + * For example, the `ProcessIO` created below will print all normal output + * while ignoring all error output. No input will be provided. + * {{{ + * import scala.sys.process.BasicIO + * val errToDevNull = BasicIO(false, println(_), None) + * }}} + * + * @param withIn True if the process input should be attached to stdin. + * @param output A function that will be called with the process output. + * @param log An optional `ProcessLogger` to which the output should be + * sent. If `None`, output will be sent to stderr. + * @return A `ProcessIO` with the characteristics above. + */ + def apply(withIn: Boolean, output: String => Unit, log: Option[ProcessLogger]) = + new ProcessIO(input(withIn), processFully(output), getErr(log)) + + /** Creates a `ProcessIO` that appends its output to an `Appendable`. It can + * attach the process input to stdin, and it will either send the error + * stream to stderr, or to a `ProcessLogger`. + * + * For example, the `ProcessIO` created by the function below will store the + * normal output on the buffer provided, and print all error on stderr. The + * input will be read from stdin. + * {{{ + * import scala.sys.process.{BasicIO, ProcessLogger} + * val printer = ProcessLogger(println(_)) + * def appendToBuffer(b: StringBuffer) = BasicIO(true, b, Some(printer)) + * }}} + * + * @param withIn True if the process input should be attached to stdin. + * @param buffer An `Appendable` which will receive the process normal + * output. + * @param log An optional `ProcessLogger` to which the output should be + * sent. If `None`, output will be sent to stderr. + * @return A `ProcessIO` with the characteristics above. + */ + def apply(withIn: Boolean, buffer: Appendable, log: Option[ProcessLogger]) = + new ProcessIO(input(withIn), processFully(buffer), getErr(log)) + + /** Creates a `ProcessIO` from a `ProcessLogger` . It can attach the + * process input to stdin. + * + * @param withIn True if the process input should be attached to stdin. + * @param log A `ProcessLogger` to receive all output, normal and error. + * @return A `ProcessIO` with the characteristics above. + */ + def apply(withIn: Boolean, log: ProcessLogger) = + new ProcessIO(input(withIn), processOutFully(log), processErrFully(log)) + + /** Returns a function `InputStream => Unit` given an optional + * `ProcessLogger`. If no logger is passed, the function will send the output + * to stderr. This function can be used to create a + * [[scala.sys.process.ProcessIO]]. + * + * @param log An optional `ProcessLogger` to which the contents of + * the `InputStream` will be sent. + * @return A function `InputStream => Unit` (used by + * [[scala.sys.process.ProcessIO]]) which will send the data to + * either the provided `ProcessLogger` or, if `None`, to stderr. + */ + def getErr(log: Option[ProcessLogger]) = log match { + case Some(lg) => processErrFully(lg) + case None => toStdErr + } + + private def processErrFully(log: ProcessLogger) = processFully(log err _) + private def processOutFully(log: ProcessLogger) = processFully(log out _) + + /** Closes a `Closeable` without throwing an exception */ + def close(c: Closeable) = try c.close() catch { case _: IOException => () } + + /** Returns a function `InputStream => Unit` that appends all data read to the + * provided `Appendable`. This function can be used to create a + * [[scala.sys.process.ProcessIO]]. The buffer will be appended line by line. + * + * @param buffer An `Appendable` such as `StringBuilder` or `StringBuffer`. + * @return A function `InputStream => Unit` (used by + * [[scala.sys.process.ProcessIO]] which will append all data read + * from the stream to the buffer. + */ + def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer)) + + /** Returns a function `InputStream => Unit` that will call the passed + * function with all data read. This function can be used to create a + * [[scala.sys.process.ProcessIO]]. The `processLine` function will be called + * with each line read, and `Newline` will be appended after each line. + * + * @param processLine A function that will be called with all data read from + * the stream. + * @return A function `InputStream => Unit` (used by + * [[scala.sys.process.ProcessIO]] which will call `processLine` + * with all data read from the stream. + */ + def processFully(processLine: String => Unit): InputStream => Unit = in => { + val reader = new BufferedReader(new InputStreamReader(in)) + try processLinesFully(processLine)(() => reader.readLine()) + finally reader.close() + } + + /** Calls `processLine` with the result of `readLine` until the latter returns + * `null` or the current thread is interrupted. + */ + def processLinesFully(processLine: String => Unit)(readLine: () => String): Unit = { + def working = !Thread.currentThread.isInterrupted + def halting = { Thread.currentThread.interrupt(); null } + @tailrec + def readFully(): Unit = + if (working) { + val line = + try readLine() + catch { + case _: InterruptedException => halting + case _: IOException if !working => halting + } + if (line != null) { + processLine(line) + readFully() + } + } + readFully() + } + + /** Copy contents of stdin to the `OutputStream`. */ + def connectToIn(o: OutputStream): Unit = transferFully(Uncloseable protect stdin, o) + + /** Returns a function `OutputStream => Unit` that either reads the content + * from stdin or does nothing but close the stream. This function can be used by + * [[scala.sys.process.ProcessIO]]. + */ + def input(connect: Boolean): OutputStream => Unit = if (connect) connectToStdIn else connectNoOp + + /** A sentinel value telling ProcessBuilderImpl to redirect. */ + private[process] val connectToStdIn: OutputStream => Unit = _ => () + + /** A sentinel value telling ProcessBuilderImpl not to process. */ + private[process] val connectNoOp: OutputStream => Unit = _ => () + + /** Returns a `ProcessIO` connected to stdout and stderr, and, optionally, stdin. */ + def standard(connectInput: Boolean): ProcessIO = standard(input(connectInput)) + + /** Returns a `ProcessIO` connected to stdout, stderr and the provided `in` */ + def standard(in: OutputStream => Unit): ProcessIO = new ProcessIO(in, toStdOut, toStdErr) + + /** Send all the input from the stream to stderr, and closes the input stream + * afterwards. + */ + def toStdErr = (in: InputStream) => transferFully(in, stderr) + + /** Send all the input from the stream to stdout, and closes the input stream + * afterwards. + */ + def toStdOut = (in: InputStream) => transferFully(in, stdout) + + /** Copy all input from the input stream to the output stream. Closes the + * input stream once it's all read. + */ + def transferFully(in: InputStream, out: OutputStream): Unit = + try transferFullyImpl(in, out) + catch onIOInterrupt(()) + + private[this] def appendLine(buffer: Appendable): String => Unit = line => { + buffer append line + buffer append Newline + } + + private[this] def transferFullyImpl(in: InputStream, out: OutputStream): Unit = { + val buffer = new Array[Byte](BufferSize) + @tailrec def loop(): Unit = { + val byteCount = in.read(buffer) + if (byteCount > 0) { + out.write(buffer, 0, byteCount) + // flush() will throw an exception once the process has terminated + val available = try { out.flush(); true } catch { case _: IOException => false } + if (available) loop() + } + } + loop() + in.close() + } +} diff --git a/library/src/scala/sys/process/Parser.scala b/library/src/scala/sys/process/Parser.scala new file mode 100644 index 000000000000..e9a25a0f9366 --- /dev/null +++ b/library/src/scala/sys/process/Parser.scala @@ -0,0 +1,116 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.sys.process + +import scala.annotation.tailrec + +/** A simple enough command line parser using shell quote conventions. + */ +private[scala] object Parser { + private final val DQ = '"' + private final val SQ = '\'' + private final val EOF = -1 + + /** Split the line into tokens separated by whitespace or quotes. + * + * @return either an error message or reverse list of tokens + */ + def tokenize(line: String, errorFn: String => Unit): List[String] = { + import Character.isWhitespace + import java.lang.{StringBuilder => Builder} + import collection.mutable.ArrayBuffer + + var accum: List[String] = Nil + var pos = 0 + var start = 0 + val qpos = new ArrayBuffer[Int](16) // positions of paired quotes + + def cur: Int = if (done) EOF else line.charAt(pos) + def bump() = pos += 1 + def done = pos >= line.length + + // Skip to the next quote as given. + def skipToQuote(q: Int): Boolean = { + var escaped = false + def terminal: Boolean = cur match { + case _ if escaped => escaped = false ; false + case '\\' => escaped = true ; false + case `q` | EOF => true + case _ => false + } + while (!terminal) bump() + !done + } + // Skip to a word boundary, where words can be quoted and quotes can be escaped + def skipToDelim(): Boolean = { + var escaped = false + def quote() = { qpos += pos ; bump() } + @tailrec def advance(): Boolean = cur match { + case _ if escaped => escaped = false ; bump() ; advance() + case '\\' => escaped = true ; bump() ; advance() + case q @ (DQ | SQ) => { quote() ; skipToQuote(q) } && { quote() ; advance() } + case EOF => true + case c if isWhitespace(c) => true + case _ => bump(); advance() + } + advance() + } + def skipWhitespace() = while (isWhitespace(cur)) bump() + def copyText() = { + val buf = new Builder + var p = start + var i = 0 + while (p < pos) { + if (i >= qpos.size) { + buf.append(line, p, pos) + p = pos + } else if (p == qpos(i)) { + buf.append(line, qpos(i)+1, qpos(i+1)) + p = qpos(i+1)+1 + i += 2 + } else { + buf.append(line, p, qpos(i)) + p = qpos(i) + } + } + buf.toString + } + def text() = { + val res = + if (qpos.isEmpty) line.substring(start, pos) + else if (qpos(0) == start && qpos(1) == pos) line.substring(start+1, pos-1) + else copyText() + qpos.clear() + res + } + def badquote() = errorFn(s"Unmatched quote [${qpos.last}](${line.charAt(qpos.last)})") + def badescape() = errorFn("trailing backslash") + + @tailrec def loop(): List[String] = { + skipWhitespace() + start = pos + if (done) accum.reverse + else if (!skipToDelim()) { badquote(); Nil } + else if (pos > line.length) { badescape(); Nil } + else { + accum ::= text() + loop() + } + } + loop() + } + + class ParseException(msg: String) extends RuntimeException(msg) + + def tokenize(line: String): List[String] = tokenize(line, x => throw new ParseException(x)) +} diff --git a/library/src/scala/sys/process/Process.scala b/library/src/scala/sys/process/Process.scala new file mode 100644 index 000000000000..531971125e6d --- /dev/null +++ b/library/src/scala/sys/process/Process.scala @@ -0,0 +1,222 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys +package process + +import processInternal._ +import ProcessBuilder._ +import scala.language.implicitConversions + + +/** Represents a process that is running or has finished running. + * It may be a compound process with several underlying native processes (such as `a #&& b`). + * + * This trait is often not used directly, though its companion object contains + * factories for [[scala.sys.process.ProcessBuilder]], the main component of this + * package. + * + * It is used directly when calling the method `run` on a `ProcessBuilder`, + * which makes the process run in the background. The methods provided on `Process` + * make it possible for one to block until the process exits and get the exit value, + * or destroy the process altogether. + * + * @see [[scala.sys.process.ProcessBuilder]] + */ +trait Process { + /** Returns this process alive status */ + def isAlive(): Boolean + /** Blocks until this process exits and returns the exit code.*/ + def exitValue(): Int + /** Destroys this process. */ + def destroy(): Unit +} + +/** Methods for constructing simple commands that can then be combined. */ +object Process extends ProcessImpl with ProcessCreation { } + +/** Factories for creating [[scala.sys.process.ProcessBuilder]]. They can be + * found on and used through [[scala.sys.process.Process]]'s companion object. + */ +trait ProcessCreation { + /** Creates a [[scala.sys.process.ProcessBuilder]] from a `String`, including the + * parameters. + * + * @example {{{ apply("cat file.txt") }}} + */ + def apply(command: String): ProcessBuilder = apply(command, None) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a sequence of `String`, + * where the head is the command and each element of the tail is a parameter. + * + * @example {{{ apply("cat" :: files) }}} + */ + def apply(command: scala.collection.Seq[String]): ProcessBuilder = apply(command, None) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a command represented by a `String`, + * and a sequence of `String` representing the arguments. + * + * @example {{{ apply("cat", files) }}} + */ + def apply(command: String, arguments: scala.collection.Seq[String]): ProcessBuilder = apply(command +: arguments, None) + + /** Creates a [[scala.sys.process.ProcessBuilder]] with working dir set to `File` and extra + * environment variables. + * + * @example {{{ apply("java", new java.io.File("/opt/app"), "CLASSPATH" -> "library.jar") }}} + */ + def apply(command: String, cwd: File, extraEnv: (String, String)*): ProcessBuilder = + apply(command, Some(cwd), extraEnv: _*) + + /** Creates a [[scala.sys.process.ProcessBuilder]] with working dir set to `File` and extra + * environment variables. + * + * @example {{{ apply("java" :: javaArgs, new java.io.File("/opt/app"), "CLASSPATH" -> "library.jar") }}} + */ + def apply(command: scala.collection.Seq[String], cwd: File, extraEnv: (String, String)*): ProcessBuilder = + apply(command, Some(cwd), extraEnv: _*) + + /** Creates a [[scala.sys.process.ProcessBuilder]] with working dir optionally set to + * `File` and extra environment variables. + * + * @example {{{ apply("java", params.get("cwd"), "CLASSPATH" -> "library.jar") }}} + */ + def apply(command: String, cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = + apply(Parser.tokenize(command), cwd, extraEnv: _*) + + /** Creates a [[scala.sys.process.ProcessBuilder]] with working dir optionally set to + * `File` and extra environment variables. + * + * @example {{{ apply("java" :: javaArgs, params.get("cwd"), "CLASSPATH" -> "library.jar") }}} + */ + def apply(command: scala.collection.Seq[String], cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = { + val jpb = new JProcessBuilder(command.toArray: _*) + cwd foreach (jpb directory _) + extraEnv foreach { case (k, v) => jpb.environment.put(k, v) } + apply(jpb) + } + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a `java.lang.ProcessBuilder`. + * + * @example {{{ + * apply((new java.lang.ProcessBuilder("ls", "-l")) directory new java.io.File(System.getProperty("user.home"))) + * }}} + */ + def apply(builder: JProcessBuilder): ProcessBuilder = new Simple(builder) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a `java.io.File`. This + * `ProcessBuilder` can then be used as a `Source` or a `Sink`, so one can + * pipe things from and to it. + */ + def apply(file: File): FileBuilder = new FileImpl(file) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a `java.net.URL`. This + * `ProcessBuilder` can then be used as a `Source`, so that one can pipe things + * from it. + */ + def apply(url: URL): URLBuilder = new URLImpl(url) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a `Boolean`. This can be + * to force an exit value. + */ + def apply(value: Boolean): ProcessBuilder = apply(value.toString, if (value) 0 else 1) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a `String` name and a + * `Boolean`. This can be used to force an exit value, with the name being + * used for `toString`. + */ + def apply(name: String, exitValue: => Int): ProcessBuilder = new Dummy(name, exitValue) + + /** Creates a sequence of [[scala.sys.process.ProcessBuilder.Source]] from a sequence of + * something else for which there's an implicit conversion to `Source`. + */ + def applySeq[T](builders: scala.collection.Seq[T])(implicit convert: T => Source): scala.collection.Seq[Source] = builders.map(convert) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from one or more + * [[scala.sys.process.ProcessBuilder.Source]], which can then be + * piped to something else. + * + * This will concatenate the output of all sources. For example: + * + * {{{ + * import scala.sys.process._ + * import scala.sys.process.Process.cat + * import java.net.URL + * import java.io.File + * + * val spde = new URL("http://technically.us/spde.html") + * val dispatch = new URL("https://dispatchhttp.org/Dispatch.html") + * val build = new File("project/build.properties") + * cat(spde, dispatch, build) #| "grep -i scala" ! + * }}} + */ + def cat(file: Source, files: Source*): ProcessBuilder = cat(file +: files) + + /** Creates a [[scala.sys.process.ProcessBuilder]] from a non-empty sequence + * of [[scala.sys.process.ProcessBuilder.Source]], which can then be + * piped to something else. + * + * This will concatenate the output of all sources. + */ + def cat(files: scala.collection.Seq[Source]): ProcessBuilder = { + require(files.nonEmpty) + files.map(_.cat).reduceLeft(_ #&& _) + } +} + +/** Provide implicit conversions for the factories offered by [[scala.sys.process.Process]]'s + * companion object. These implicits can then be used to decrease the noise in a pipeline + * of commands, making it look more shell-like. They are available through the package object + * [[scala.sys.process]]. + */ +trait ProcessImplicits { + import Process._ + + /** Return a sequence of [[scala.sys.process.ProcessBuilder.Source]] from a sequence + * of values for which an implicit conversion to `Source` is available. + */ + implicit def buildersToProcess[T](builders: scala.collection.Seq[T])(implicit convert: T => Source): scala.collection.Seq[Source] = applySeq(builders) + + /** Implicitly convert a `java.lang.ProcessBuilder` into a Scala one. */ + implicit def builderToProcess(builder: JProcessBuilder): ProcessBuilder = apply(builder) + + /** Implicitly convert a `java.io.File` into a + * [[scala.sys.process.ProcessBuilder.FileBuilder]], which can be used as + * either input or output of a process. For example: + * {{{ + * import scala.sys.process._ + * "ls" #> new java.io.File("dirContents.txt") ! + * }}} + */ + implicit def fileToProcess(file: File): FileBuilder = apply(file) + + /** Implicitly convert a `java.net.URL` into a + * [[scala.sys.process.ProcessBuilder.URLBuilder]] , which can be used as + * input to a process. For example: + * {{{ + * import scala.sys.process._ + * Seq("xmllint", "--html", "-") #< new java.net.URL("https://www.scala-lang.org") #> new java.io.File("fixed.html") ! + * }}} + */ + implicit def urlToProcess(url: URL): URLBuilder = apply(url) + + /** Implicitly convert a `String` into a [[scala.sys.process.ProcessBuilder]]. */ + implicit def stringToProcess(command: String): ProcessBuilder = apply(command) + + /** Implicitly convert a sequence of `String` into a + * [[scala.sys.process.ProcessBuilder]]. The first argument will be taken to + * be the command to be executed, and the remaining will be its arguments. + * When using this, arguments may contain spaces. + */ + implicit def stringSeqToProcess(command: scala.collection.Seq[String]): ProcessBuilder = apply(command) +} diff --git a/library/src/scala/sys/process/ProcessBuilder.scala b/library/src/scala/sys/process/ProcessBuilder.scala new file mode 100644 index 000000000000..bf1c1507707d --- /dev/null +++ b/library/src/scala/sys/process/ProcessBuilder.scala @@ -0,0 +1,479 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys +package process + +import processInternal._ +import ProcessBuilder.{Sink, Source} + +/** Represents a sequence of one or more external processes that can be + * executed. A `ProcessBuilder` can be a single external process, or a + * combination of other `ProcessBuilder`. One can control where the + * output of an external process will go to, and where its input will come + * from, or leave that decision to whoever starts it. + * + * One creates a `ProcessBuilder` through factories provided in + * [[scala.sys.process.Process]]'s companion object, or implicit conversions + * based on these factories made available in the package object + * [[scala.sys.process]]. Here are some examples: + * {{{ + * import scala.sys.process._ + * + * // Executes "ls" and sends output to stdout + * "ls".! + * + * // Execute "ls" and assign a `LazyList[String]` of its output to "contents". + * val contents = Process("ls").lazyLines + * + * // Here we use a `Seq` to make the parameter whitespace-safe + * def contentsOf(dir: String): String = Seq("ls", dir).!! + * }}} + * + * The methods of `ProcessBuilder` are divided in three categories: the ones that + * combine two `ProcessBuilder` to create a third, the ones that redirect input + * or output of a `ProcessBuilder`, and the ones that execute + * the external processes associated with it. + * + * ==Combining `ProcessBuilder`== + * + * Two existing `ProcessBuilder` can be combined in the following ways: + * + * - They can be executed in parallel, with the output of the first being fed + * as input to the second, like Unix pipes. This is achieved with the `#|` + * method. + * - They can be executed in sequence, with the second starting as soon as + * the first ends. This is done by the `###` method. + * - The execution of the second one can be conditioned by the return code + * (exit status) of the first, either only when it's zero, or only when it's + * not zero. The methods `#&&` and `#||` accomplish these tasks. + * + * ==Redirecting Input/Output== + * + * Though control of input and output can be done when executing the process, + * there's a few methods that create a new `ProcessBuilder` with a + * pre-configured input or output. They are `#<`, `#>` and `#>>`, and may take + * as input either another `ProcessBuilder` (like the pipe described above), or + * something else such as a `java.io.File` or a `java.io.InputStream`. + * For example: + * {{{ + * new URL("https://databinder.net/dispatch/About") #> "grep JSON" #>> new File("About_JSON") ! + * }}} + * + * ==Starting Processes== + * + * To execute all external commands associated with a `ProcessBuilder`, one + * may use one of four groups of methods. Each of these methods have various + * overloads and variations to enable further control over the I/O. These + * methods are: + * + * - `run`: the most general method, it returns a + * [[scala.sys.process.Process]] immediately, and the external command + * executes concurrently. + * - `!`: blocks until all external commands exit, and returns the exit code + * of the last one in the chain of execution. + * - `!!`: blocks until all external commands exit, and returns a `String` + * with the output generated. + * - `lazyLines`: returns immediately like `run`, and the output being generated + * is provided through a `LazyList[String]`. Getting the next element of that + * `LazyList` may block until it becomes available. This method will throw an + * exception if the return code is different than zero -- if this is not + * desired, use the `lazyLines_!` method. + * + * ==Handling Input and Output== + * + * If not specified, the input of the external commands executed with `run` or + * `!` will not be tied to anything, and the output will be redirected to the + * stdout and stderr of the Scala process. For the methods `!!` and `lazyLines`, no + * input will be provided, and the output will be directed according to the + * semantics of these methods. + * + * Some methods will cause stdin to be used as input. Output can be controlled + * with a [[scala.sys.process.ProcessLogger]] -- `!!` and `lazyLines` will only + * redirect error output when passed a `ProcessLogger`. If one desires full + * control over input and output, then a [[scala.sys.process.ProcessIO]] can be + * used with `run`. + * + * For example, we could silence the error output from `lazyLines_!` like this: + * {{{ + * val etcFiles = "find /etc" lazyLines_! ProcessLogger(line => ()) + * }}} + * + * ==Extended Example== + * + * Let's examine in detail one example of usage: + * {{{ + * import scala.sys.process._ + * "find src -name *.scala -exec grep null {} ;" #| "xargs test -z" #&& "echo null-free" #|| "echo null detected" ! + * }}} + * Note that every `String` is implicitly converted into a `ProcessBuilder` + * through the implicits imported from [[scala.sys.process]]. These `ProcessBuilder` are then + * combined in three different ways. + * + * 1. `#|` pipes the output of the first command into the input of the second command. It + * mirrors a shell pipe (`|`). + * 1. `#&&` conditionally executes the second command if the previous one finished with + * exit value 0. It mirrors shell's `&&`. + * 1. `#||` conditionally executes the third command if the exit value of the previous + * command is different than zero. It mirrors shell's `||`. + * + * Finally, `!` at the end executes the commands, and returns the exit value. + * Whatever is printed will be sent to the Scala process standard output. If + * we wanted to capture it, we could run that with `!!` instead. + * + * Note: though it is not shown above, the equivalent of a shell's `;` would be + * `###`. The reason for this name is that `;` is a reserved token in Scala. + * + */ +trait ProcessBuilder extends Source with Sink { + /** Starts the process represented by this builder, blocks until it exits, and + * returns the output as a String. Standard error is sent to the console. If + * the exit code is non-zero, an exception is thrown. + */ + def !! : String + + /** Starts the process represented by this builder, blocks until it exits, and + * returns the output as a String. Standard error is sent to the provided + * ProcessLogger. If the exit code is non-zero, an exception is thrown. + */ + def !!(log: ProcessLogger): String + + /** Starts the process represented by this builder, blocks until it exits, and + * returns the output as a String. Standard error is sent to the console. If + * the exit code is non-zero, an exception is thrown. The newly started + * process reads from standard input of the current process. + */ + def !!< : String + + /** Starts the process represented by this builder, blocks until it exits, and + * returns the output as a String. Standard error is sent to the provided + * ProcessLogger. If the exit code is non-zero, an exception is thrown. The + * newly started process reads from standard input of the current process. + */ + def !!<(log: ProcessLogger): String + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. Standard error is sent to the console. If the process exits + * with a non-zero value, the LazyList will provide all lines up to termination + * and then throw an exception. + */ + def lazyLines: LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the LazyList. + * Standard error is sent to the console. If the process exits + * with a non-zero value, the LazyList will provide all lines up to termination + * and then throw an exception. + */ + def lazyLines(capacity: Integer): LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the LazyList will provide all lines up + * to termination and then throw an exception. + */ + def lazyLines(log: ProcessLogger): LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the LazyList. + * Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the LazyList will provide all lines up + * to termination and then throw an exception. + */ + def lazyLines(log: ProcessLogger, capacity: Integer): LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. Standard error is sent to the console. If the process exits + * with a non-zero value, the LazyList will provide all lines up to termination + * but will not throw an exception. + */ + def lazyLines_! : LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the stream. + * Standard error is sent to the console. If the process exits + * with a non-zero value, the LazyList will provide all lines up to termination + * but will not throw an exception. + */ + def lazyLines_!(capacity: Integer): LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the LazyList will provide all lines up + * to termination but will not throw an exception. + */ + def lazyLines_!(log: ProcessLogger): LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a LazyList that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the stream. + * Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the LazyList will provide all lines up + * to termination but will not throw an exception. + */ + def lazyLines_!(log: ProcessLogger, capacity: Integer): LazyList[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. Standard error is sent to the console. If the process exits + * with a non-zero value, the Stream will provide all lines up to termination + * and then throw an exception. + */ + @deprecated("use lazyLines", since = "2.13.0") + def lineStream: Stream[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the stream. + * Standard error is sent to the console. If the process exits + * with a non-zero value, the Stream will provide all lines up to termination + * and then throw an exception. + */ + @deprecated("use lazyLines", since = "2.13.0") + def lineStream(capacity: Integer): Stream[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the Stream will provide all lines up + * to termination and then throw an exception. + */ + @deprecated("use lazyLines", since = "2.13.0") + def lineStream(log: ProcessLogger): Stream[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the stream. + * Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the Stream will provide all lines up + * to termination and then throw an exception. + */ + @deprecated("use lazyLines", since = "2.13.0") + def lineStream(log: ProcessLogger, capacity: Integer): Stream[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. Standard error is sent to the console. If the process exits + * with a non-zero value, the Stream will provide all lines up to termination + * but will not throw an exception. + */ + @deprecated("use lazyLines_!", since = "2.13.0") + def lineStream_! : Stream[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the stream. + * Standard error is sent to the console. If the process exits + * with a non-zero value, the Stream will provide all lines up to termination + * but will not throw an exception. + */ + @deprecated("use lazyLines_!", since = "2.13.0") + def lineStream_!(capacity: Integer): Stream[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the Stream will provide all lines up + * to termination but will not throw an exception. + */ + @deprecated("use lazyLines_!", since = "2.13.0") + def lineStream_!(log: ProcessLogger): Stream[String] + + /** Starts the process represented by this builder. The output is returned as + * a Stream that blocks when lines are not available but the process has not + * completed. + * The producer process will block if the given capacity of lines if filled + * without being consumed from the stream. + * Standard error is sent to the provided ProcessLogger. If the + * process exits with a non-zero value, the Stream will provide all lines up + * to termination but will not throw an exception. + */ + @deprecated("use lazyLines_!", since = "2.13.0") + def lineStream_!(log: ProcessLogger, capacity: Integer): Stream[String] + + /** Starts the process represented by this builder, blocks until it exits, and + * returns the exit code. Standard output and error are sent to the console. + */ + def ! : Int + + /** Starts the process represented by this builder, blocks until it exits, and + * returns the exit code. Standard output and error are sent to the given + * ProcessLogger. + */ + def !(log: ProcessLogger): Int + + /** Starts the process represented by this builder, blocks until it exits, and + * returns the exit code. Standard output and error are sent to the console. + * The newly started process reads from standard input of the current process. + */ + def !< : Int + + /** Starts the process represented by this builder, blocks until it exits, and + * returns the exit code. Standard output and error are sent to the given + * ProcessLogger. The newly started process reads from standard input of the + * current process. + */ + def !<(log: ProcessLogger): Int + + /** Starts the process represented by this builder. Standard output and error + * are sent to the console.*/ + def run(): Process + + /** Starts the process represented by this builder. Standard output and error + * are sent to the given ProcessLogger. + */ + def run(log: ProcessLogger): Process + + /** Starts the process represented by this builder. I/O is handled by the + * given ProcessIO instance. + */ + def run(io: ProcessIO): Process + + /** Starts the process represented by this builder. Standard output and error + * are sent to the console. The newly started process reads from standard + * input of the current process if `connectInput` is true. + */ + def run(connectInput: Boolean): Process + + /** Starts the process represented by this builder. Standard output and error + * are sent to the given ProcessLogger. The newly started process reads from + * standard input of the current process if `connectInput` is true. + */ + def run(log: ProcessLogger, connectInput: Boolean): Process + + /** Constructs a command that runs this command first and then `other` if this + * command succeeds. + */ + def #&& (other: ProcessBuilder): ProcessBuilder + + /** Constructs a command that runs this command first and then `other` if this + * command does not succeed. + */ + def #|| (other: ProcessBuilder): ProcessBuilder + + /** Constructs a command that will run this command and pipes the output to + * `other`. `other` must be a simple command. + */ + def #| (other: ProcessBuilder): ProcessBuilder + + /** Constructs a command that will run this command and then `other`. The + * exit code will be the exit code of `other`. + */ + def ### (other: ProcessBuilder): ProcessBuilder + + + /** True if this command can be the target of a pipe. */ + def canPipeTo: Boolean + + /** True if this command has an exit code which should be propagated to the + * user. Given a pipe between A and B, if B.hasExitValue is true then the + * exit code will be the one from B; if it is false, the one from A. This + * exists to prevent output redirections (implemented as pipes) from masking + * useful process error codes. + */ + def hasExitValue: Boolean +} + +/** This object contains traits used to describe input and output sources. */ +object ProcessBuilder extends ProcessBuilderImpl { + /** Used when creating [[scala.sys.process.ProcessBuilder.Source]] from an URL. */ + trait URLBuilder extends Source { + + } + + /** Used when creating [[scala.sys.process.ProcessBuilder.Source]] and/or + * [[scala.sys.process.ProcessBuilder.Sink]] from a file. + */ + trait FileBuilder extends Sink with Source { + /** Append the contents of a `java.io.File` to this file */ + def #<<(f: File): ProcessBuilder + + /** Append the contents from a `java.net.URL` to this file */ + def #<<(u: URL): ProcessBuilder + + /** Append the contents of a `java.io.InputStream` to this file */ + def #<<(i: => InputStream): ProcessBuilder + + /** Append the contents of a [[scala.sys.process.ProcessBuilder]] to this file */ + def #<<(p: ProcessBuilder): ProcessBuilder + } + + /** Represents everything that can be used as an input to a + * [[scala.sys.process.ProcessBuilder]]. + */ + trait Source { + protected def toSource: ProcessBuilder + + /** Writes the output stream of this process to the given file. */ + def #> (f: File): ProcessBuilder = toFile(f, append = false) + + /** Appends the output stream of this process to the given file. */ + def #>> (f: File): ProcessBuilder = toFile(f, append = true) + + /** Writes the output stream of this process to the given OutputStream. The + * argument is call-by-name, so the stream is recreated, written, and closed each + * time this process is executed. + */ + def #>(out: => OutputStream): ProcessBuilder = #> (new OStreamBuilder(out, "")) + + /** Writes the output stream of this process to a [[scala.sys.process.ProcessBuilder]]. */ + def #>(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(toSource, b, toError = false) + + /** Returns a [[scala.sys.process.ProcessBuilder]] representing this `Source`. */ + def cat = toSource + private def toFile(f: File, append: Boolean) = #> (new FileOutput(f, append)) + } + + /** Represents everything that can receive an output from a + * [[scala.sys.process.ProcessBuilder]]. + */ + trait Sink { + protected def toSink: ProcessBuilder + + /** Reads the given file into the input stream of this process. */ + def #< (f: File): ProcessBuilder = #< (new FileInput(f)) + + /** Reads the given URL into the input stream of this process. */ + def #< (f: URL): ProcessBuilder = #< (new URLInput(f)) + + /** Reads the given InputStream into the input stream of this process. The + * argument is call-by-name, so the stream is recreated, read, and closed each + * time this process is executed. + */ + def #<(in: => InputStream): ProcessBuilder = #< (new IStreamBuilder(in, "")) + + /** Reads the output of a [[scala.sys.process.ProcessBuilder]] into the input stream of this process. */ + def #<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, toSink, toError = false) + } +} diff --git a/library/src/scala/sys/process/ProcessBuilderImpl.scala b/library/src/scala/sys/process/ProcessBuilderImpl.scala new file mode 100644 index 000000000000..186ad134a218 --- /dev/null +++ b/library/src/scala/sys/process/ProcessBuilderImpl.scala @@ -0,0 +1,274 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys +package process + +import processInternal._ +import Process._ +import BasicIO.{LazilyListed, Streamed, Uncloseable} +import Uncloseable.protect + +import java.io.{FileInputStream, FileOutputStream} +import java.util.concurrent.LinkedBlockingQueue + +import scala.util.control.NonFatal + +private[process] trait ProcessBuilderImpl { + self: ProcessBuilder.type => + + private[process] class DaemonBuilder(underlying: ProcessBuilder) extends AbstractBuilder { + final def run(io: ProcessIO): Process = underlying.run(io.daemonized()) + } + + private[process] class Dummy(override val toString: String, exitValue: => Int) extends AbstractBuilder { + override def run(io: ProcessIO): Process = new DummyProcess(exitValue) + override def canPipeTo = true + } + + private[process] class URLInput(url: URL) extends IStreamBuilder(url.openStream(), url.toString) + private[process] class FileInput(file: File) extends IStreamBuilder(new FileInputStream(file), file.getAbsolutePath) + private[process] class FileOutput(file: File, append: Boolean) extends OStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath) + + private[process] class OStreamBuilder( + stream: => OutputStream, + label: String + ) extends ThreadBuilder(label, _ writeInput protect(stream)) { + override def hasExitValue = false + } + + private[process] class IStreamBuilder( + stream: => InputStream, + label: String + ) extends ThreadBuilder(label, _ processOutput protect(stream)) { + override def hasExitValue = false + } + + private[process] abstract class ThreadBuilder( + override val toString: String, + runImpl: ProcessIO => Unit + ) extends AbstractBuilder { + + override def run(io: ProcessIO): Process = { + val success = new LinkedBlockingQueue[Boolean](1) + def go(): Unit = { + var ok = false + try { + runImpl(io) + ok = true + } finally success.put(ok) + } + val t = Spawn("ThreadProcess", io.daemonizeThreads)(go()) + new ThreadProcess(t, success) + } + } + + /** Represents a simple command without any redirection or combination. */ + private[process] class Simple(p: JProcessBuilder) extends AbstractBuilder { + override def run(io: ProcessIO): Process = { + import java.lang.ProcessBuilder.Redirect.{INHERIT => Inherit} + import io.{daemonizeThreads, processError, processOutput, writeInput} + + val inherit = writeInput eq BasicIO.connectToStdIn + if (inherit) p.redirectInput(Inherit) + + val process = p.start() // start the external process + + // spawn threads that process the input, output, and error streams using the functions defined in `io` + val inThread = + if (inherit || (writeInput eq BasicIO.connectNoOp)) null + else Spawn("Simple-input", daemon = true)(writeInput(process.getOutputStream)) + val outThread = Spawn("Simple-output", daemonizeThreads)(processOutput(process.getInputStream())) + val errorThread = + if (p.redirectErrorStream) Nil + else List(Spawn("Simple-error", daemonizeThreads)(processError(process.getErrorStream()))) + + new SimpleProcess(process, inThread, outThread :: errorThread) + } + override def toString = p.command.toString + override def canPipeTo = true + } + + private[scala] abstract class AbstractBuilder extends ProcessBuilder with Sink with Source { + protected def toSource: AbstractBuilder = this + protected def toSink: AbstractBuilder = this + + private[this] val defaultStreamCapacity = 4096 + + def #|(other: ProcessBuilder): ProcessBuilder = { + require(other.canPipeTo, "Piping to multiple processes is not supported.") + new PipedBuilder(this, other, toError = false) + } + def #||(other: ProcessBuilder): ProcessBuilder = new OrBuilder(this, other) + def #&&(other: ProcessBuilder): ProcessBuilder = new AndBuilder(this, other) + def ###(other: ProcessBuilder): ProcessBuilder = new SequenceBuilder(this, other) + + def run(): Process = run(connectInput = false) + def run(connectInput: Boolean): Process = run(BasicIO.standard(connectInput)) + def run(log: ProcessLogger): Process = run(log, connectInput = false) + def run(log: ProcessLogger, connectInput: Boolean): Process = run(BasicIO(connectInput, log)) + + def !! = slurp(None, withIn = false) + def !!(log: ProcessLogger) = slurp(Some(log), withIn = false) + def !!< = slurp(None, withIn = true) + def !!<(log: ProcessLogger) = slurp(Some(log), withIn = true) + + def lazyLines: LazyList[String] = lazyLines(withInput = false, nonZeroException = true, None, defaultStreamCapacity) + def lazyLines(log: ProcessLogger): LazyList[String] = lazyLines(withInput = false, nonZeroException = true, Some(log), defaultStreamCapacity) + def lazyLines_! : LazyList[String] = lazyLines(withInput = false, nonZeroException = false, None, defaultStreamCapacity) + def lazyLines_!(log: ProcessLogger): LazyList[String] = lazyLines(withInput = false, nonZeroException = false, Some(log), defaultStreamCapacity) + def lazyLines(capacity: Integer): LazyList[String] = lazyLines(withInput = false, nonZeroException = true, None, capacity) + def lazyLines(log: ProcessLogger, capacity: Integer): LazyList[String] = lazyLines(withInput = false, nonZeroException = true, Some(log), capacity) + def lazyLines_!(capacity: Integer) : LazyList[String] = lazyLines(withInput = false, nonZeroException = false, None, capacity) + def lazyLines_!(log: ProcessLogger, capacity: Integer): LazyList[String] = lazyLines(withInput = false, nonZeroException = false, Some(log), capacity) + + @deprecated("internal", since = "2.13.4") def lineStream: Stream[String] = lineStream(withInput = false, nonZeroException = true, None, defaultStreamCapacity) + @deprecated("internal", since = "2.13.4") def lineStream(log: ProcessLogger): Stream[String] = lineStream(withInput = false, nonZeroException = true, Some(log), defaultStreamCapacity) + @deprecated("internal", since = "2.13.4") def lineStream_! : Stream[String] = lineStream(withInput = false, nonZeroException = false, None, defaultStreamCapacity) + @deprecated("internal", since = "2.13.4") def lineStream_!(log: ProcessLogger): Stream[String] = lineStream(withInput = false, nonZeroException = false, Some(log), defaultStreamCapacity) + @deprecated("internal", since = "2.13.4") def lineStream(capacity: Integer): Stream[String] = lineStream(withInput = false, nonZeroException = true, None, capacity) + @deprecated("internal", since = "2.13.4") def lineStream(log: ProcessLogger, capacity: Integer): Stream[String] = lineStream(withInput = false, nonZeroException = true, Some(log), capacity) + @deprecated("internal", since = "2.13.4") def lineStream_!(capacity: Integer) : Stream[String] = lineStream(withInput = false, nonZeroException = false, None, capacity) + @deprecated("internal", since = "2.13.4") def lineStream_!(log: ProcessLogger, capacity: Integer): Stream[String] = lineStream(withInput = false, nonZeroException = false, Some(log), capacity) + + def ! = run(connectInput = false).exitValue() + def !(io: ProcessIO) = run(io).exitValue() + def !(log: ProcessLogger) = runBuffered(log, connectInput = false) + def !< = run(connectInput = true).exitValue() + def !<(log: ProcessLogger) = runBuffered(log, connectInput = true) + + /** Constructs a new builder which runs this command with all input/output threads marked + * as daemon threads. This allows the creation of a long running process while still + * allowing the JVM to exit normally. + * + * Note: not in the public API because it's not fully baked, but I need the capability + * for fsc. + */ + def daemonized(): ProcessBuilder = new DaemonBuilder(this) + + private[this] def slurp(log: Option[ProcessLogger], withIn: Boolean): String = { + val buffer = new StringBuffer + val code = this ! BasicIO(withIn, buffer, log) + + if (code == 0) buffer.toString + else scala.sys.error("Nonzero exit value: " + code) + } + + private[this] def lazyLines( + withInput: Boolean, + nonZeroException: Boolean, + log: Option[ProcessLogger], + capacity: Integer + ): LazyList[String] = { + val lazilyListed = LazilyListed[String](nonZeroException, capacity) + val process = run(BasicIO(withInput, lazilyListed.process, log)) + + // extract done from lazilyListed so that the anonymous function below closes over just the done and not the whole lazilyListed (see https://github.com/scala/bug/issues/12185) + val done = lazilyListed.done + + Spawn("LazyLines") { + done { + try process.exitValue() + catch { + case NonFatal(_) => -2 + } + } + } + lazilyListed.lazyList + } + + @deprecated("internal", since = "2.13.4") + private[this] def lineStream( + withInput: Boolean, + nonZeroException: Boolean, + log: Option[ProcessLogger], + capacity: Integer + ): Stream[String] = { + val streamed = Streamed[String](nonZeroException, capacity) + val process = run(BasicIO(withInput, streamed.process, log)) + + Spawn("LineStream")(streamed done process.exitValue()) + streamed.stream() + } + + private[this] def runBuffered(log: ProcessLogger, connectInput: Boolean) = + log buffer run(log, connectInput).exitValue() + + def canPipeTo = false + def hasExitValue = true + } + + private[process] class URLImpl(url: URL) extends URLBuilder with Source { + protected def toSource: URLInput = new URLInput(url) + } + private[process] class FileImpl(base: File) extends FileBuilder with Sink with Source { + protected def toSource: FileInput = new FileInput(base) + protected def toSink: FileOutput = new FileOutput(base, append = false) + + def #<<(f: File): ProcessBuilder = #<<(new FileInput(f)) + def #<<(u: URL): ProcessBuilder = #<<(new URLInput(u)) + def #<<(s: => InputStream): ProcessBuilder = #<<(new IStreamBuilder(s, "")) + def #<<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, new FileOutput(base, append = true), toError = false) + } + + private[process] abstract class BasicBuilder extends AbstractBuilder { + protected[this] def checkNotThis(a: ProcessBuilder) = require(a != this, "Compound process '" + a + "' cannot contain itself.") + final def run(io: ProcessIO): Process = { + val p = createProcess(io) + p.start() + p + } + protected[this] def createProcess(io: ProcessIO): BasicProcess + } + + private[process] abstract class SequentialBuilder( + a: ProcessBuilder, + b: ProcessBuilder, + operatorString: String + ) extends BasicBuilder { + + checkNotThis(a) + checkNotThis(b) + override def toString = " ( " + a + " " + operatorString + " " + b + " ) " + } + + private[process] class PipedBuilder( + first: ProcessBuilder, + second: ProcessBuilder, + toError: Boolean + ) extends SequentialBuilder(first, second, if (toError) "#|!" else "#|") { + + override def createProcess(io: ProcessIO): PipedProcesses = new PipedProcesses(first, second, io, toError) + } + + private[process] class AndBuilder( + first: ProcessBuilder, + second: ProcessBuilder + ) extends SequentialBuilder(first, second, "#&&") { + override def createProcess(io: ProcessIO): AndProcess = new AndProcess(first, second, io) + } + + private[process] class OrBuilder( + first: ProcessBuilder, + second: ProcessBuilder + ) extends SequentialBuilder(first, second, "#||") { + override def createProcess(io: ProcessIO): OrProcess = new OrProcess(first, second, io) + } + + private[process] class SequenceBuilder( + first: ProcessBuilder, + second: ProcessBuilder + ) extends SequentialBuilder(first, second, "###") { + override def createProcess(io: ProcessIO): ProcessSequence = new ProcessSequence(first, second, io) + } +} diff --git a/library/src/scala/sys/process/ProcessIO.scala b/library/src/scala/sys/process/ProcessIO.scala new file mode 100644 index 000000000000..a4898d2c9b1e --- /dev/null +++ b/library/src/scala/sys/process/ProcessIO.scala @@ -0,0 +1,72 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys +package process + +import processInternal._ + +/** This class is used to control the I/O of every + * [[scala.sys.process.Process]]. The functions used to create it will be + * called with the process streams once it has been started. It might not be + * necessary to use `ProcessIO` directly -- + * [[scala.sys.process.ProcessBuilder]] can return the process output to the + * caller, or use a [[scala.sys.process.ProcessLogger]] which avoids direct + * interaction with a stream. One can even use the factories at `BasicIO` to + * create a `ProcessIO`, or use its helper methods when creating one's own + * `ProcessIO`. + * + * When creating a `ProcessIO`, it is important to ''close all streams'' when + * finished, since the JVM might use system resources to capture the process + * input and output, and will not release them unless the streams are + * explicitly closed. + * + * `ProcessBuilder` will call `writeInput`, `processOutput` and `processError` + * in separate threads, and if daemonizeThreads is true, they will all be + * marked as daemon threads. + * + * @param writeInput Function that will be called with the `OutputStream` to + * which all input to the process must be written. This will + * be called in a newly spawned thread. + * @param processOutput Function that will be called with the `InputStream` + * from which all normal output of the process must be + * read from. This will be called in a newly spawned + * thread. + * @param processError Function that will be called with the `InputStream` from + * which all error output of the process must be read from. + * This will be called in a newly spawned thread. + * @param daemonizeThreads Indicates whether the newly spawned threads that + * will run `processOutput`, `processError` and + * `writeInput` should be marked as daemon threads. + * @note Failure to close the passed streams may result in resource leakage. + */ +final class ProcessIO( + val writeInput: OutputStream => Unit, + val processOutput: InputStream => Unit, + val processError: InputStream => Unit, + val daemonizeThreads: Boolean +) { + def this(in: OutputStream => Unit, out: InputStream => Unit, err: InputStream => Unit) = this(in, out, err, daemonizeThreads = false) + + /** Creates a new `ProcessIO` with a different handler for the process input. */ + def withInput(write: OutputStream => Unit): ProcessIO = new ProcessIO(write, processOutput, processError, daemonizeThreads) + + /** Creates a new `ProcessIO` with a different handler for the normal output. */ + def withOutput(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, process, processError, daemonizeThreads) + + /** Creates a new `ProcessIO` with a different handler for the error output. */ + def withError(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, processOutput, process, daemonizeThreads) + + /** Creates a new `ProcessIO`, with `daemonizeThreads` true. */ + def daemonized(): ProcessIO = new ProcessIO(writeInput, processOutput, processError, daemonizeThreads = true) +} diff --git a/library/src/scala/sys/process/ProcessImpl.scala b/library/src/scala/sys/process/ProcessImpl.scala new file mode 100644 index 000000000000..3ed0c5766412 --- /dev/null +++ b/library/src/scala/sys/process/ProcessImpl.scala @@ -0,0 +1,287 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.sys.process + +import processInternal._ + +import java.util.concurrent.LinkedBlockingQueue +import java.io.{PipedInputStream, PipedOutputStream} + +import scala.annotation.tailrec + +private[process] trait ProcessImpl { + self: Process.type => + + /** Runs provided code in a new Thread and returns the Thread instance. */ + private[process] object Spawn { + def apply(prefix: String, daemon: Boolean = false)(f: => Unit): Thread = { + val thread = new Thread() { override def run() = f } + thread.setName(prefix + "-spawn-" + thread.getName) + thread.setDaemon(daemon) + thread.start() + thread + } + } + private[process] object Future { + def apply[T](f: => T): (Thread, () => T) = { + val result = new LinkedBlockingQueue[Either[Throwable, T]](1) + def run(): Unit = { + val value = try Right(f) catch { case e: Exception => Left(e) } + result.put(value) + } + + val t = Spawn("Future")(run()) + + (t, () => result.take() match { + case Right(value) => value + case Left(exception) => throw exception + }) + } + } + + private[process] class AndProcess( + a: ProcessBuilder, + b: ProcessBuilder, + io: ProcessIO + ) extends SequentialProcess(a, b, io, _ == 0) + + private[process] class OrProcess( + a: ProcessBuilder, + b: ProcessBuilder, + io: ProcessIO + ) extends SequentialProcess(a, b, io, _ != 0) + + private[process] class ProcessSequence( + a: ProcessBuilder, + b: ProcessBuilder, + io: ProcessIO + ) extends SequentialProcess(a, b, io, _ => true) + + private[process] class SequentialProcess( + a: ProcessBuilder, + b: ProcessBuilder, + io: ProcessIO, + evaluateSecondProcess: Int => Boolean + ) extends CompoundProcess { + + protected[this] override def runAndExitValue() = { + val first = a.run(io) + runInterruptible(first.exitValue())(first.destroy()) flatMap { codeA => + if (evaluateSecondProcess(codeA)) { + val second = b.run(io) + runInterruptible(second.exitValue())(second.destroy()) + } + else Some(codeA) + } + } + } + + private[process] abstract class BasicProcess extends Process { + def start(): Unit + } + + private[process] abstract class CompoundProcess extends BasicProcess { + def isAlive() = processThread.isAlive() + def destroy() = destroyer() + def exitValue() = futureValue() getOrElse scala.sys.error("No exit code: process destroyed.") + def start() = { futureThread ;() } + + protected lazy val (processThread, (futureThread, futureValue), destroyer) = { + val code = new LinkedBlockingQueue[Option[Int]](1) + val thread = Spawn("CompoundProcess") { + var value: Option[Int] = None + try value = runAndExitValue() + catch { + case _: IndexOutOfBoundsException + | _: IOException + | _: NullPointerException + | _: SecurityException + | _: UnsupportedOperationException + => value = Some(-1) + } + finally code.put(value) + } + + ( + thread, + Future(code.take()), // thread.join() + () => thread.interrupt() + ) + } + + /** Start and block until the exit value is available and then return it in Some. Return None if destroyed (use 'run')*/ + protected[this] def runAndExitValue(): Option[Int] + + protected[this] def runInterruptible[T](action: => T)(destroyImpl: => Unit): Option[T] = { + try Some(action) + catch onInterrupt { destroyImpl; None } + } + } + + private[process] class PipedProcesses(a: ProcessBuilder, b: ProcessBuilder, defaultIO: ProcessIO, toError: Boolean) extends CompoundProcess { + protected def newSource: PipeSource = new PipeSource(a.toString) + protected def newSink: PipeSink = new PipeSink(b.toString) + protected[this] override def runAndExitValue() = runAndExitValue(newSource, newSink) + protected[this] def runAndExitValue(source: PipeSource, sink: PipeSink): Option[Int] = { + source connectOut sink + source.start() + sink.start() + + /* Release PipeSource, PipeSink and Process in the correct order. + * If once connect Process with Source or Sink, then the order of releasing them + * must be Source -> Sink -> Process, otherwise IOException will be thrown. + */ + def releaseResources(so: PipeSource, sk: PipeSink, ps: Process*) = { + so.release() + sk.release() + ps.foreach(_.destroy()) + } + + val firstIO = + if (toError) defaultIO.withError(source.connectIn) + else defaultIO.withOutput(source.connectIn) + val secondIO = defaultIO.withInput(sink.connectOut) + + val second = + try b.run(secondIO) + catch onError { err => + releaseResources(source, sink) + throw err + } + val first = + try a.run(firstIO) + catch onError { err => + releaseResources(source, sink, second) + throw err + } + runInterruptible { + val exit1 = first.exitValue() + source.done() + source.join() + val exit2 = second.exitValue() + sink.done() + // Since file redirection (e.g. #>) is implemented as a piped process, + // we ignore its exit value so cmd #> file doesn't always return 0. + if (b.hasExitValue) exit2 else exit1 + } { + releaseResources(source, sink, first, second) + } + } + } + + private[process] abstract class PipeThread(isSink: Boolean, labelFn: () => String) extends Thread { + def run(): Unit + + private[process] def runloop(src: InputStream, dst: OutputStream): Unit = { + try BasicIO.transferFully(src, dst) + catch ioFailure(ioHandler) + finally BasicIO close { + if (isSink) dst else src + } + } + private def ioHandler(e: IOException): Unit = e.printStackTrace() + } + + private[process] class PipeSource(label: => String) extends PipeThread(isSink = false, () => label) { + setName(s"PipeSource($label)-$getName") + protected[this] val pipe = new PipedOutputStream + protected[this] val source = new LinkedBlockingQueue[Option[InputStream]](1) + override final def run(): Unit = { + @tailrec def go(): Unit = + source.take() match { + case Some(in) => runloop(in, pipe) ; go() + case None => + } + try go() + catch onInterrupt(()) + finally BasicIO close pipe + } + def connectIn(in: InputStream): Unit = source.put(Some(in)) + def connectOut(sink: PipeSink): Unit = sink connectIn pipe + def release(): Unit = { + interrupt() + done() + join() + } + def done() = source.put(None) + } + private[process] class PipeSink(label: => String) extends PipeThread(isSink = true, () => label) { + setName(s"PipeSink($label)-$getName") + protected[this] val pipe = new PipedInputStream + protected[this] val sink = new LinkedBlockingQueue[Option[OutputStream]](1) + override def run(): Unit = { + @tailrec def go(): Unit = + sink.take() match { + case Some(out) => runloop(pipe, out) ; go() + case None => + } + try go() + catch onInterrupt(()) + finally BasicIO close pipe + } + def connectOut(out: OutputStream): Unit = sink.put(Some(out)) + def connectIn(pipeOut: PipedOutputStream): Unit = pipe connect pipeOut + def release(): Unit = { + interrupt() + done() + join() + } + def done() = sink.put(None) + } + + /** A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O. + * The implementation of `exitValue` waits until these threads die before returning. + */ + private[process] class DummyProcess(action: => Int) extends Process { + private[this] val (thread, value) = Future(action) + override def isAlive() = thread.isAlive() + override def exitValue() = value() + override def destroy(): Unit = { } + } + + /** A thin wrapper around a java.lang.Process. + * + * `outputThreads` are the Threads created to read from the + * output and error streams of the process. + * + * `inputThread` is the Thread created to write to the input stream of + * the process. It may be null if stdin was inherited. + * + * The implementation of `exitValue` interrupts `inputThread` + * and then waits until all I/O threads die before returning. + */ + private[process] class SimpleProcess(p: JProcess, inputThread: Thread, outputThreads: List[Thread]) extends Process { + override def isAlive() = p.isAlive() + override def exitValue() = { + try p.waitFor() // wait for the process to terminate + finally interrupt() + outputThreads foreach (_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this) + + p.exitValue() + } + override def destroy() = { + try { + outputThreads foreach (_.interrupt()) // on destroy, don't bother consuming any more output + p.destroy() + } + finally interrupt() + } + // we interrupt the input thread to notify it that it can terminate + private[this] def interrupt(): Unit = if (inputThread != null) inputThread.interrupt() + } + private[process] final class ThreadProcess(thread: Thread, success: LinkedBlockingQueue[Boolean]) extends Process { + override def isAlive() = thread.isAlive() + override def exitValue() = if (success.take()) 0 else 1 // thread.join() + override def destroy() = thread.interrupt() + } +} diff --git a/library/src/scala/sys/process/ProcessLogger.scala b/library/src/scala/sys/process/ProcessLogger.scala new file mode 100644 index 000000000000..e048ca5c9446 --- /dev/null +++ b/library/src/scala/sys/process/ProcessLogger.scala @@ -0,0 +1,105 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package sys +package process + +import java.io._ + +/** Encapsulates the output and error streams of a running process. This is used + * by [[scala.sys.process.ProcessBuilder]] when starting a process, as an + * alternative to [[scala.sys.process.ProcessIO]], which can be more difficult + * to use. Note that a `ProcessLogger` will be used to create a `ProcessIO` + * anyway. The object `BasicIO` has some functions to do that. + * + * Here is an example that counts the number of lines in the normal and error + * output of a process: + * {{{ + * import scala.sys.process._ + * + * var normalLines = 0 + * var errorLines = 0 + * val countLogger = ProcessLogger(line => normalLines += 1, + * line => errorLines += 1) + * "find /etc" ! countLogger + * }}} + * + * @see [[scala.sys.process.ProcessBuilder]] + */ +trait ProcessLogger { + /** Will be called with each line read from the process output stream. + */ + def out(s: => String): Unit + + /** Will be called with each line read from the process error stream. + */ + def err(s: => String): Unit + + /** If a process is begun with one of these `ProcessBuilder` methods: + * {{{ + * def !(log: ProcessLogger): Int + * def !<(log: ProcessLogger): Int + * }}} + * The run will be wrapped in a call to buffer. This gives the logger + * an opportunity to set up and tear down buffering. At present the + * library implementations of `ProcessLogger` simply execute the body + * unbuffered. + */ + def buffer[T](f: => T): T +} + +/** A [[scala.sys.process.ProcessLogger]] that writes output to a file. */ +class FileProcessLogger(file: File) extends ProcessLogger with Closeable with Flushable { + private[this] val writer = ( + new PrintWriter( + new BufferedWriter( + new OutputStreamWriter( + new FileOutputStream(file, true) + ) + ) + ) + ) + def out(s: => String): Unit = writer println s + def err(s: => String): Unit = writer println s + def buffer[T](f: => T): T = f + def close(): Unit = writer.close() + def flush(): Unit = writer.flush() +} + +/** Provides factories to create [[scala.sys.process.ProcessLogger]], which + * are used to capture output of [[scala.sys.process.ProcessBuilder]] commands + * when run. + */ +object ProcessLogger { + /** Creates a [[scala.sys.process.ProcessLogger]] that redirects output to a `java.io.File`. */ + def apply(file: File): FileProcessLogger = new FileProcessLogger(file) + + /** Creates a [[scala.sys.process.ProcessLogger]] that sends all output, standard and error, + * to the passed function. + */ + def apply(fn: String => Unit): ProcessLogger = apply(fn, fn) + + /** Creates a [[scala.sys.process.ProcessLogger]] that sends all output to the corresponding + * function. + * + * @param fout This function will receive standard output. + * + * @param ferr This function will receive standard error. + */ + def apply(fout: String => Unit, ferr: String => Unit): ProcessLogger = + new ProcessLogger { + def out(s: => String): Unit = fout(s) + def err(s: => String): Unit = ferr(s) + def buffer[T](f: => T): T = f + } +} diff --git a/library/src/scala/sys/process/package.scala b/library/src/scala/sys/process/package.scala new file mode 100644 index 000000000000..11e6640d8e21 --- /dev/null +++ b/library/src/scala/sys/process/package.scala @@ -0,0 +1,260 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// Developer note: +// scala -J-Dscala.process.debug +// for process debugging output. +// +package scala.sys +package process + /** This package handles the execution of external processes. The contents of + * this package can be divided in three groups, according to their + * responsibilities: + * + * - Indicating what to run and how to run it. + * - Handling a process input and output. + * - Running the process. + * + * For simple uses, the only group that matters is the first one. Running an + * external command can be as simple as `"ls".!`, or as complex as building a + * pipeline of commands such as this: + * + * {{{ + * import scala.sys.process._ + * "ls" #| "grep .scala" #&& Seq("sh", "-c", "scalac *.scala") #|| "echo nothing found" lazyLines + * }}} + * + * We describe below the general concepts and architecture of the package, + * and then take a closer look at each of the categories mentioned above. + * + * ==Concepts and Architecture== + * + * The underlying basis for the whole package is Java's `Process` and + * `ProcessBuilder` classes. While there's no need to use these Java classes, + * they impose boundaries on what is possible. One cannot, for instance, + * retrieve a ''process id'' for whatever is executing. + * + * When executing an external process, one can provide a command's name, + * arguments to it, the directory in which it will be executed and what + * environment variables will be set. For each executing process, one can + * feed its standard input through a `java.io.OutputStream`, and read from + * its standard output and standard error through a pair of + * `java.io.InputStream`. One can wait until a process finishes execution and + * then retrieve its return value, or one can kill an executing process. + * Everything else must be built on those features. + * + * This package provides a DSL for running and chaining such processes, + * mimicking Unix shells ability to pipe output from one process to the input + * of another, or control the execution of further processes based on the + * return status of the previous one. + * + * In addition to this DSL, this package also provides a few ways of + * controlling input and output of these processes, going from simple and + * easy to use to complex and flexible. + * + * When processes are composed, a new `ProcessBuilder` is created which, when + * run, will execute the `ProcessBuilder` instances it is composed of + * according to the manner of the composition. If piping one process to + * another, they'll be executed simultaneously, and each will be passed a + * `ProcessIO` that will copy the output of one to the input of the other. + * + * ==What to Run and How== + * + * The central component of the process execution DSL is the + * [[scala.sys.process.ProcessBuilder]] trait. It is `ProcessBuilder` that + * implements the process execution DSL, that creates the + * [[scala.sys.process.Process]] that will handle the execution, and return + * the results of such execution to the caller. We can see that DSL in the + * introductory example: `#|`, `#&&` and `#!!` are methods on + * `ProcessBuilder` used to create a new `ProcessBuilder` through + * composition. + * + * One creates a `ProcessBuilder` either through factories on the + * [[scala.sys.process.Process]]'s companion object, or through implicit + * conversions available in this package object itself. Implicitly, each + * process is created either out of a `String`, with arguments separated by + * spaces -- no escaping of spaces is possible -- or out of a + * [[scala.collection.Seq]], where the first element represents the command + * name, and the remaining elements are arguments to it. In this latter case, + * arguments may contain spaces. + * + * To further control what how the process will be run, such as specifying + * the directory in which it will be run, see the factories on + * [[scala.sys.process.Process]]'s companion object. + * + * Once the desired `ProcessBuilder` is available, it can be executed in + * different ways, depending on how one desires to control its I/O, and what + * kind of result one wishes for: + * + * - Return status of the process (`!` methods) + * - Output of the process as a `String` (`!!` methods) + * - Continuous output of the process as a `LazyList[String]` (`lazyLines` methods) + * - The `Process` representing it (`run` methods) + * + * Some simple examples of these methods: + * {{{ + * import scala.sys.process._ + * + * // This uses ! to get the exit code + * def fileExists(name: String) = Seq("test", "-f", name).! == 0 + * + * // This uses !! to get the whole result as a string + * val dirContents = "ls".!! + * + * // This "fire-and-forgets" the method, which can be lazily read through + * // a LazyList[String] + * def sourceFilesAt(baseDir: String): LazyList[String] = { + * val cmd = Seq("find", baseDir, "-name", "*.scala", "-type", "f") + * cmd.lazyLines + * } + * }}} + * + * We'll see more details about controlling I/O of the process in the next + * section. + * + * ==Handling Input and Output== + * + * In the underlying Java model, once a `Process` has been started, one can + * get `java.io.InputStream` and `java.io.OutputStream` representing its + * output and input respectively. That is, what one writes to an + * `OutputStream` is turned into input to the process, and the output of a + * process can be read from an `InputStream` -- of which there are two, one + * representing normal output, and the other representing error output. + * + * This model creates a difficulty, which is that the code responsible for + * actually running the external processes is the one that has to take + * decisions about how to handle its I/O. + * + * This package presents an alternative model: the I/O of a running process + * is controlled by a [[scala.sys.process.ProcessIO]] object, which can be + * passed _to_ the code that runs the external process. A `ProcessIO` will + * have direct access to the java streams associated with the process I/O. It + * must, however, close these streams afterwards. + * + * Simpler abstractions are available, however. The components of this + * package that handle I/O are: + * + * - [[scala.sys.process.ProcessIO]]: provides the low level abstraction. + * - [[scala.sys.process.ProcessLogger]]: provides a higher level abstraction + * for output, and can be created through its companion object. + * - [[scala.sys.process.BasicIO]]: a library of helper methods for the + * creation of `ProcessIO`. + * - This package object itself, with a few implicit conversions. + * + * Some examples of I/O handling: + * {{{ + * import scala.sys.process._ + * + * // An overly complex way of computing size of a compressed file + * def gzFileSize(name: String) = { + * val cat = Seq("zcat", name) + * var count = 0 + * def byteCounter(input: java.io.InputStream) = { + * while(input.read() != -1) count += 1 + * input.close() + * } + * val p = cat run new ProcessIO(_.close(), byteCounter, _.close()) + * p.exitValue() + * count + * } + * + * // This "fire-and-forgets" the method, which can be lazily read through + * // a LazyList[String], and accumulates all errors on a StringBuffer + * def sourceFilesAt(baseDir: String): (LazyList[String], StringBuffer) = { + * val buffer = new StringBuffer() + * val cmd = Seq("find", baseDir, "-name", "*.scala", "-type", "f") + * val lazyLines = cmd lazyLines_! ProcessLogger(buffer append _) + * (lazyLines, buffer) + * } + * }}} + * + * Instances of the java classes `java.io.File` and `java.net.URL` can both + * be used directly as input to other processes, and `java.io.File` can be + * used as output as well. One can even pipe one to the other directly + * without any intervening process, though that's not a design goal or + * recommended usage. For example, the following code will copy a web page to + * a file: + * {{{ + * import java.io.File + * import java.net.URL + * import scala.sys.process._ + * new URL("https://www.scala-lang.org/") #> new File("scala-lang.html") ! + * }}} + * + * More information about the other ways of controlling I/O can be found + * in the Scaladoc for the associated objects, traits and classes. + * + * ==Running the Process== + * + * Paradoxically, this is the simplest component of all, and the one least + * likely to be interacted with. It consists solely of + * [[scala.sys.process.Process]], and it provides only two methods: + * + * - `exitValue()`: blocks until the process exit, and then returns the exit + * value. This is what happens when one uses the `!` method of + * `ProcessBuilder`. + * - `destroy()`: this will kill the external process and close the streams + * associated with it. + */ + @annotation.nowarn("msg=package object inheritance") + object `package` extends ProcessImplicits { + /** The input stream of this process */ + def stdin = java.lang.System.in + /** The output stream of this process */ + def stdout = java.lang.System.out + /** The error stream of this process */ + def stderr = java.lang.System.err + } + // private val shell: String => Array[String] = + // if (isWin) Array("cmd.exe", "/C", _) + // else Array("sh", "-c", _) + + // These are in a nested object instead of at the package level + // due to the issues described in tickets #3160 and #3836. + private[process] object processInternal { + final val processDebug = props contains "scala.process.debug" + dbg("Initializing process package.") + + type =?>[-A, +B] = PartialFunction[A, B] + type Closeable = java.io.Closeable + type File = java.io.File + type IOException = java.io.IOException + type InterruptedIOException = java.io.InterruptedIOException + type InputStream = java.io.InputStream + type JProcess = java.lang.Process + type JProcessBuilder = java.lang.ProcessBuilder + type OutputStream = java.io.OutputStream + type URL = java.net.URL + + @deprecated("Use `java.util.concurrent.LinkedBlockingQueue with capacity 1` instead.", since = "2.13.4") + type SyncVar[T] = scala.concurrent.SyncVar[T] + + def onError[T](handler: Throwable => T): Throwable =?> T = { + case e @ _ => handler(e) + } + + def onIOInterrupt[T](handler: => T): Throwable =?> T = { + case _: InterruptedIOException => handler + } + + def onInterrupt[T](handler: => T): Throwable =?> T = { + case _: InterruptedException => handler + } + + def ioFailure[T](handler: IOException => T): Throwable =?> T = { + case e: IOException => handler(e) + } + + def dbg(msgs: Any*) = if (processDebug) { + Console.println("[process] " + (msgs mkString " ")) + } + } From e6a5ead0cfff9c6edc3d0a293b4615fd8b01f281 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:41:37 +0100 Subject: [PATCH 19/28] chore: add all the files in scala.util --- library/src/scala/util/ChainingOps.scala | 65 ++ library/src/scala/util/DynamicVariable.scala | 69 ++ library/src/scala/util/Either.scala | 838 +++++++++++++++++++ library/src/scala/util/Properties.scala | 232 +++++ library/src/scala/util/Random.scala | 264 ++++++ library/src/scala/util/Sorting.scala | 302 +++++++ library/src/scala/util/Try.scala | 290 +++++++ library/src/scala/util/Using.scala | 432 ++++++++++ library/src/scala/util/package.scala | 20 + 9 files changed, 2512 insertions(+) create mode 100644 library/src/scala/util/ChainingOps.scala create mode 100644 library/src/scala/util/DynamicVariable.scala create mode 100644 library/src/scala/util/Either.scala create mode 100644 library/src/scala/util/Properties.scala create mode 100644 library/src/scala/util/Random.scala create mode 100644 library/src/scala/util/Sorting.scala create mode 100644 library/src/scala/util/Try.scala create mode 100644 library/src/scala/util/Using.scala create mode 100644 library/src/scala/util/package.scala diff --git a/library/src/scala/util/ChainingOps.scala b/library/src/scala/util/ChainingOps.scala new file mode 100644 index 000000000000..4bfbdc82a833 --- /dev/null +++ b/library/src/scala/util/ChainingOps.scala @@ -0,0 +1,65 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +import scala.language.implicitConversions + +trait ChainingSyntax { + @inline implicit final def scalaUtilChainingOps[A](a: A): ChainingOps[A] = new ChainingOps(a) +} + +/** Adds chaining methods `tap` and `pipe` to every type. + */ +final class ChainingOps[A](private val self: A) extends AnyVal { + /** Applies `f` to the value for its side effects, and returns the original value. + * + * {{{ + * scala> import scala.util.chaining._ + * + * scala> val xs = List(1, 2, 3).tap(ys => println("debug " + ys.toString)) + * debug List(1, 2, 3) + * xs: List[Int] = List(1, 2, 3) + * }}} + * + * @param f the function to apply to the value. + * @tparam U the result type of the function `f`. + * @return the original value `self`. + */ + def tap[U](f: A => U): A = { + f(self) + self + } + + /** Converts the value by applying the function `f`. + * + * {{{ + * scala> import scala.util.chaining._ + * + * scala> val times6 = (_: Int) * 6 + * times6: Int => Int = \$\$Lambda\$2023/975629453@17143b3b + * + * scala> val i = (1 - 2 - 3).pipe(times6).pipe(scala.math.abs) + * i: Int = 24 + * }}} + * + * Note: `(1 - 2 - 3).pipe(times6)` may have a small amount of overhead at + * runtime compared to the equivalent `{ val temp = 1 - 2 - 3; times6(temp) }`. + * + * @param f the function to apply to the value. + * @tparam B the result type of the function `f`. + * @return a new value resulting from applying the given function + * `f` to this value. + */ + def pipe[B](f: A => B): B = f(self) +} diff --git a/library/src/scala/util/DynamicVariable.scala b/library/src/scala/util/DynamicVariable.scala new file mode 100644 index 000000000000..a2568c5a9c69 --- /dev/null +++ b/library/src/scala/util/DynamicVariable.scala @@ -0,0 +1,69 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +import java.lang.InheritableThreadLocal + +/** `DynamicVariables` provide a binding mechanism where the current + * value is found through dynamic scope, but where access to the + * variable itself is resolved through static scope. + * + * The current value can be retrieved with the value method. New values + * should be pushed using the `withValue` method. Values pushed via + * `withValue` only stay valid while the `withValue`'s second argument, a + * parameterless closure, executes. When the second argument finishes, + * the variable reverts to the previous value. + * + * {{{ + * someDynamicVariable.withValue(newValue) { + * // ... code called in here that calls value ... + * // ... will be given back the newValue ... + * } + * }}} + * + * Each thread gets its own stack of bindings. When a + * new thread is created, the `DynamicVariable` gets a copy + * of the stack of bindings from the parent thread, and + * from then on the bindings for the new thread + * are independent of those for the original thread. + */ +class DynamicVariable[T](init: T) { + private[this] val tl = new InheritableThreadLocal[T] { + override def initialValue: T with AnyRef = init.asInstanceOf[T with AnyRef] + } + + /** Retrieve the current value */ + def value: T = tl.get.asInstanceOf[T] + + /** Set the value of the variable while executing the specified + * thunk. + * + * @param newval The value to which to set the variable + * @param thunk The code to evaluate under the new setting + */ + def withValue[S](newval: T)(thunk: => S): S = { + val oldval = value + tl set newval + + try thunk + finally tl set oldval + } + + /** Change the currently bound value, discarding the old value. + * Usually withValue() gives better semantics. + */ + def value_=(newval: T) = tl set newval + + override def toString: String = "DynamicVariable(" + value + ")" +} diff --git a/library/src/scala/util/Either.scala b/library/src/scala/util/Either.scala new file mode 100644 index 000000000000..0542decbcf46 --- /dev/null +++ b/library/src/scala/util/Either.scala @@ -0,0 +1,838 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +/** Represents a value of one of two possible types (a disjoint union). + * An instance of `Either` is an instance of either [[scala.util.Left]] or [[scala.util.Right]]. + * + * A common use of `Either` is as an alternative to [[scala.Option]] for dealing + * with possibly missing values. In this usage, [[scala.None]] is replaced + * with a [[scala.util.Left]] which can contain useful information. + * [[scala.util.Right]] takes the place of [[scala.Some]]. Convention dictates + * that `Left` is used for failure and `Right` is used for success. + * + * For example, you could use `Either[String, Int]` to indicate whether a + * received input is a `String` or an `Int`. + * + * {{{ + * import scala.io.StdIn._ + * val in = readLine("Type Either a string or an Int: ") + * val result: Either[String,Int] = + * try Right(in.toInt) + * catch { + * case e: NumberFormatException => Left(in) + * } + * + * result match { + * case Right(x) => s"You passed me the Int: \$x, which I will increment. \$x + 1 = \${x+1}" + * case Left(x) => s"You passed me the String: \$x" + * } + * }}} + * + * `Either` is right-biased, which means that `Right` is assumed to be the default case to + * operate on. If it is `Left`, operations like `map` and `flatMap` return the `Left` value unchanged: + * + * {{{ + * def doubled(i: Int) = i * 2 + * Right(42).map(doubled) // Right(84) + * Left(42).map(doubled) // Left(42) + * }}} + * + * Since `Either` defines the methods `map` and `flatMap`, it can also be used in for comprehensions: + * {{{ + * val right1 = Right(1) : Right[Double, Int] + * val right2 = Right(2) + * val right3 = Right(3) + * val left23 = Left(23.0) : Left[Double, Int] + * val left42 = Left(42.0) + * + * for { + * x <- right1 + * y <- right2 + * z <- right3 + * } yield x + y + z // Right(6) + * + * for { + * x <- right1 + * y <- right2 + * z <- left23 + * } yield x + y + z // Left(23.0) + * + * for { + * x <- right1 + * y <- left23 + * z <- right2 + * } yield x + y + z // Left(23.0) + * + * // Guard expressions are not supported: + * for { + * i <- right1 + * if i > 0 + * } yield i + * // error: value withFilter is not a member of Right[Double,Int] + * + * // Similarly, refutable patterns are not supported: + * for (x: Int <- right1) yield x + * // error: value withFilter is not a member of Right[Double,Int] + * + * // To use a filtered value, convert to an Option first, + * // which drops the Left case, as None contains no value: + * for { + * i <- right1.toOption + * if i > 0 + * } yield i + * + * }}} + * + * Since `for` comprehensions use `map` and `flatMap`, the types + * of function parameters used in the expression must be inferred. + * These types are constrained by the `Either` values. In particular, + * because of right-biasing, `Left` values may require an explicit + * type argument for type parameter `B`, the right value. Otherwise, + * it might be inferred as `Nothing`. + * + * {{{ + * for { + * x <- left23 + * y <- right1 + * z <- left42 // type at this position: Either[Double, Nothing] + * } yield x + y + z + * // ^ + * // error: ambiguous reference to overloaded definition, + * // both method + in class Int of type (x: Char)Int + * // and method + in class Int of type (x: Byte)Int + * // match argument types (Nothing) + * + * for (x <- right2 ; y <- left23) yield x + y // Left(23.0) + * for (x <- right2 ; y <- left42) yield x + y // error + * + * for { + * x <- right1 + * y <- left42 // type at this position: Either[Double, Nothing] + * z <- left23 + * } yield x + y + z + * // Left(42.0), but unexpectedly a `Either[Double,String]` + * }}} + */ +sealed abstract class Either[+A, +B] extends Product with Serializable { + /** Projects this `Either` as a `Left`. + * + * This allows for-comprehensions over the left side of `Either` instances, + * reversing `Either`'s usual right-bias. + * + * For example {{{ + * for (s <- Left("flower").left) yield s.length // Left(6) + * }}} + * + * Continuing the analogy with [[scala.Option]], a `LeftProjection` declares + * that `Left` should be analogous to `Some` in some code. + * + * {{{ + * // using Option + * def interactWithDB(x: Query): Option[Result] = + * try Some(getResultFromDatabase(x)) + * catch { + * case _: SQLException => None + * } + * + * // this will only be executed if interactWithDB returns a Some + * val report = for (result <- interactWithDB(someQuery)) yield generateReport(result) + * report match { + * case Some(r) => send(r) + * case None => log("report not generated, not sure why...") + * } + * + * // using Either + * def interactWithDB(x: Query): Either[Exception, Result] = + * try Right(getResultFromDatabase(x)) + * catch { + * case e: SQLException => Left(e) + * } + * + * // run a report only if interactWithDB returns a Right + * val report = for (result <- interactWithDB(someQuery)) yield generateReport(result) + * report match { + * case Right(r) => send(r) + * case Left(e) => log(s"report not generated, reason was \$e") + * } + * // only report errors + * for (e <- interactWithDB(someQuery).left) log(s"query failed, reason was \$e") + * }}} + */ + def left = Either.LeftProjection(this) + + /** Projects this `Either` as a `Right`. + * + * Because `Either` is right-biased, this method is not normally needed. + */ + @deprecated("Either is now right-biased, use methods directly on Either", "2.13.0") + def right = Either.RightProjection(this) + + /** Applies `fa` if this is a `Left` or `fb` if this is a `Right`. + * + * @example {{{ + * val result = util.Try("42".toInt).toEither + * result.fold( + * e => s"Operation failed with \$e", + * v => s"Operation produced value: \$v" + * ) + * }}} + * + * @param fa the function to apply if this is a `Left` + * @param fb the function to apply if this is a `Right` + * @return the results of applying the function + */ + def fold[C](fa: A => C, fb: B => C): C = this match { + case Right(b) => fb(b) + case Left(a) => fa(a) + } + + /** If this is a `Left`, then return the left value in `Right` or vice versa. + * + * @example {{{ + * val left: Either[String, Int] = Left("left") + * val right: Either[Int, String] = left.swap // Result: Right("left") + * }}} + * @example {{{ + * val right = Right(2) + * val left = Left(3) + * for { + * r1 <- right + * r2 <- left.swap + * } yield r1 * r2 // Right(6) + * }}} + */ + def swap: Either[B, A] = this match { + case Left(a) => Right(a) + case Right(b) => Left(b) + } + + /** Joins an `Either` through `Right`. + * + * This method requires that the right side of this `Either` is itself + * an `Either` type. That is, this must be some type like: {{{ + * Either[A, Either[A, C]] + * }}} (which respects the type parameter bounds, shown below.) + * + * If this instance is a `Right[Either[A, C]]` then the contained `Either[A, C]` + * will be returned, otherwise this value will be returned unmodified. + * + * @example {{{ + * Right[String, Either[String, Int]](Right(12)).joinRight // Result: Right(12) + * Right[String, Either[String, Int]](Left("flower")).joinRight // Result: Left("flower") + * Left[String, Either[String, Int]]("flower").joinRight // Result: Left("flower") + * }}} + * + * This method, and `joinLeft`, are analogous to `Option#flatten` + */ + def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match { + case Right(b) => b + case _ => this.asInstanceOf[Either[A1, C]] + } + + /** Joins an `Either` through `Left`. + * + * This method requires that the left side of this `Either` is itself an + * `Either` type. That is, this must be some type like: {{{ + * Either[Either[C, B], B] + * }}} (which respects the type parameter bounds, shown below.) + * + * If this instance is a `Left[Either[C, B]]` then the contained `Either[C, B]` + * will be returned, otherwise this value will be returned unmodified. + * + * {{{ + * Left[Either[Int, String], String](Right("flower")).joinLeft // Result: Right("flower") + * Left[Either[Int, String], String](Left(12)).joinLeft // Result: Left(12) + * Right[Either[Int, String], String]("daisy").joinLeft // Result: Right("daisy") + * }}} + * + * This method, and `joinRight`, are analogous to `Option#flatten`. + */ + def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match { + case Left(a) => a + case _ => this.asInstanceOf[Either[C, B1]] + } + + /** Executes the given side-effecting function if this is a `Right`. + * + * {{{ + * Right(12).foreach(println) // prints "12" + * Left(12).foreach(println) // doesn't print + * }}} + * @param f The side-effecting function to execute. + */ + def foreach[U](f: B => U): Unit = this match { + case Right(b) => f(b) + case _ => + } + + /** Returns the value from this `Right` or the given argument if this is a `Left`. + * + * {{{ + * Right(12).getOrElse(17) // 12 + * Left(12).getOrElse(17) // 17 + * }}} + */ + def getOrElse[B1 >: B](or: => B1): B1 = this match { + case Right(b) => b + case _ => or + } + + /** Returns this `Right` or the given argument if this is a `Left`. + * + * {{{ + * Right(1) orElse Left(2) // Right(1) + * Left(1) orElse Left(2) // Left(2) + * Left(1) orElse Left(2) orElse Right(3) // Right(3) + * }}} + */ + def orElse[A1 >: A, B1 >: B](or: => Either[A1, B1]): Either[A1, B1] = this match { + case Right(_) => this + case _ => or + } + + /** Returns `true` if this is a `Right` and its value is equal to `elem` (as determined by `==`), + * returns `false` otherwise. + * + * {{{ + * // Returns true because value of Right is "something" which equals "something". + * Right("something") contains "something" + * + * // Returns false because value of Right is "something" which does not equal "anything". + * Right("something") contains "anything" + * + * // Returns false because it's not a Right value. + * Left("something") contains "something" + * }}} + * + * @param elem the element to test. + * @return `true` if this is a `Right` value equal to `elem`. + */ + final def contains[B1 >: B](elem: B1): Boolean = this match { + case Right(b) => b == elem + case _ => false + } + + /** Returns `true` if `Left` or returns the result of the application of + * the given predicate to the `Right` value. + * + * {{{ + * Right(12).forall(_ > 10) // true + * Right(7).forall(_ > 10) // false + * Left(12).forall(_ => false) // true + * }}} + */ + def forall(f: B => Boolean): Boolean = this match { + case Right(b) => f(b) + case _ => true + } + + /** Returns `false` if `Left` or returns the result of the application of + * the given predicate to the `Right` value. + * + * {{{ + * Right(12).exists(_ > 10) // true + * Right(7).exists(_ > 10) // false + * Left(12).exists(_ => true) // false + * }}} + */ + def exists(p: B => Boolean): Boolean = this match { + case Right(b) => p(b) + case _ => false + } + + /** Binds the given function across `Right`. + * + * @param f The function to bind across `Right`. + */ + def flatMap[A1 >: A, B1](f: B => Either[A1, B1]): Either[A1, B1] = this match { + case Right(b) => f(b) + case _ => this.asInstanceOf[Either[A1, B1]] + } + + + /** Returns the right value if this is right + * or this value if this is left + * + * @example {{{ + * val l: Either[String, Either[String, Int]] = Left("pancake") + * val rl: Either[String, Either[String, Int]] = Right(Left("flounder")) + * val rr: Either[String, Either[String, Int]] = Right(Right(7)) + * + * l.flatten //Either[String, Int]: Left("pancake") + * rl.flatten //Either[String, Int]: Left("flounder") + * rr.flatten //Either[String, Int]: Right(7) + * }}} + * + * Equivalent to `flatMap(id => id)` + */ + def flatten[A1 >: A, B1](implicit ev: B <:< Either[A1, B1]): Either[A1, B1] = flatMap(ev) + + /** The given function is applied if this is a `Right`. + * + * {{{ + * Right(12).map(x => "flower") // Result: Right("flower") + * Left(12).map(x => "flower") // Result: Left(12) + * }}} + */ + def map[B1](f: B => B1): Either[A, B1] = this match { + case Right(b) => Right(f(b)) + case _ => this.asInstanceOf[Either[A, B1]] + } + + /** Returns `Right` with the existing value of `Right` if this is a `Right` + * and the given predicate `p` holds for the right value, + * or `Left(zero)` if this is a `Right` and the given predicate `p` does not hold for the right value, + * or `Left` with the existing value of `Left` if this is a `Left`. + * + * {{{ + * Right(12).filterOrElse(_ > 10, -1) // Right(12) + * Right(7).filterOrElse(_ > 10, -1) // Left(-1) + * Left(7).filterOrElse(_ => false, -1) // Left(7) + * }}} + */ + def filterOrElse[A1 >: A](p: B => Boolean, zero: => A1): Either[A1, B] = this match { + case Right(b) if !p(b) => Left(zero) + case _ => this + } + + /** Returns a `Seq` containing the `Right` value if + * it exists or an empty `Seq` if this is a `Left`. + * + * {{{ + * Right(12).toSeq // Seq(12) + * Left(12).toSeq // Seq() + * }}} + */ + def toSeq: collection.immutable.Seq[B] = this match { + case Right(b) => collection.immutable.Seq(b) + case _ => collection.immutable.Seq.empty + } + + /** Returns a `Some` containing the `Right` value + * if it exists or a `None` if this is a `Left`. + * + * {{{ + * Right(12).toOption // Some(12) + * Left(12).toOption // None + * }}} + */ + def toOption: Option[B] = this match { + case Right(b) => Some(b) + case _ => None + } + + def toTry(implicit ev: A <:< Throwable): Try[B] = this match { + case Right(b) => Success(b) + case Left(a) => Failure(a) + } + + /** Returns `true` if this is a `Left`, `false` otherwise. + * + * {{{ + * Left("tulip").isLeft // true + * Right("venus fly-trap").isLeft // false + * }}} + */ + def isLeft: Boolean + + /** Returns `true` if this is a `Right`, `false` otherwise. + * + * {{{ + * Left("tulip").isRight // false + * Right("venus fly-trap").isRight // true + * }}} + */ + def isRight: Boolean +} + +/** The left side of the disjoint union, as opposed to the [[scala.util.Right]] side. + */ +final case class Left[+A, +B](value: A) extends Either[A, B] { + def isLeft = true + def isRight = false + + /** + * Upcasts this `Left[A, B]` to `Either[A, B1]` + * {{{ + * Left(1) // Either[Int, Nothing] + * Left(1).withRight[String] // Either[Int, String] + * }}} + */ + def withRight[B1 >: B]: Either[A, B1] = this + +} + +/** The right side of the disjoint union, as opposed to the [[scala.util.Left]] side. + */ +final case class Right[+A, +B](value: B) extends Either[A, B] { + def isLeft = false + def isRight = true + + /** + * Upcasts this `Right[A, B]` to `Either[A1, B]` + * {{{ + * Right("x") // Either[Nothing, String] + * Right("x").withLeft[Int] // Either[Int, String] + * }}} + */ + def withLeft[A1 >: A]: Either[A1, B] = this + +} + +object Either { + + /** If the condition is satisfied, return the given `B` in `Right`, + * otherwise, return the given `A` in `Left`. + * + * {{{ + * val userInput: String = readLine() + * Either.cond( + * userInput.forall(_.isDigit) && userInput.size == 10, + * PhoneNumber(userInput), + * s"The input (\$userInput) does not look like a phone number" + * }}} + */ + def cond[A, B](test: Boolean, right: => B, left: => A): Either[A, B] = + if (test) Right(right) else Left(left) + + /** Allows use of a `merge` method to extract values from Either instances + * regardless of whether they are Left or Right. + * + * {{{ + * val l = Left(List(1)): Either[List[Int], Vector[Int]] + * val r = Right(Vector(1)): Either[List[Int], Vector[Int]] + * l.merge: Seq[Int] // List(1) + * r.merge: Seq[Int] // Vector(1) + * }}} + */ + implicit class MergeableEither[A](private val x: Either[A, A]) extends AnyVal { + def merge: A = x match { + case Right(a) => a + case Left(a) => a + } + } + + /** Projects an `Either` into a `Left`. + * + * @see [[scala.util.Either#left]] + */ + final case class LeftProjection[+A, +B](e: Either[A, B]) { + /** Returns the value from this `Left` or throws `NoSuchElementException` + * if this is a `Right`. + * + * {{{ + * Left(12).left.get // 12 + * Right(12).left.get // NoSuchElementException + * }}} + * + * @throws NoSuchElementException if the projection is [[scala.util.Right]] + */ + @deprecated("use `Either.swap.getOrElse` instead", "2.13.0") + def get: A = e match { + case Left(a) => a + case _ => throw new NoSuchElementException("Either.left.get on Right") + } + + /** Executes the given side-effecting function if this is a `Left`. + * + * {{{ + * Left(12).left.foreach(x => println(x)) // prints "12" + * Right(12).left.foreach(x => println(x)) // doesn't print + * }}} + * @param f The side-effecting function to execute. + */ + def foreach[U](f: A => U): Unit = e match { + case Left(a) => f(a) + case _ => () + } + + /** Returns the value from this `Left` or the given argument if this is a `Right`. + * + * {{{ + * Left(12).left.getOrElse(17) // 12 + * Right(12).left.getOrElse(17) // 17 + * }}} + */ + def getOrElse[A1 >: A](or: => A1): A1 = e match { + case Left(a) => a + case _ => or + } + + /** Returns `true` if `Right` or returns the result of the application of + * the given function to the `Left` value. + * + * {{{ + * Left(12).left.forall(_ > 10) // true + * Left(7).left.forall(_ > 10) // false + * Right(12).left.forall(_ > 10) // true + * }}} + */ + def forall(p: A => Boolean): Boolean = e match { + case Left(a) => p(a) + case _ => true + } + + /** Returns `false` if `Right` or returns the result of the application of + * the given function to the `Left` value. + * + * {{{ + * Left(12).left.exists(_ > 10) // true + * Left(7).left.exists(_ > 10) // false + * Right(12).left.exists(_ > 10) // false + * }}} + */ + def exists(p: A => Boolean): Boolean = e match { + case Left(a) => p(a) + case _ => false + } + + /** Binds the given function across `Left`. + * + * {{{ + * Left(12).left.flatMap(x => Left("scala")) // Left("scala") + * Right(12).left.flatMap(x => Left("scala")) // Right(12) + * }}} + * @param f The function to bind across `Left`. + */ + def flatMap[A1, B1 >: B](f: A => Either[A1, B1]): Either[A1, B1] = e match { + case Left(a) => f(a) + case _ => e.asInstanceOf[Either[A1, B1]] + } + + /** Maps the function argument through `Left`. + * + * {{{ + * Left(12).left.map(_ + 2) // Left(14) + * Right[Int, Int](12).left.map(_ + 2) // Right(12) + * }}} + */ + def map[A1](f: A => A1): Either[A1, B] = e match { + case Left(a) => Left(f(a)) + case _ => e.asInstanceOf[Either[A1, B]] + } + + /** Returns `None` if this is a `Right` or if the given predicate + * `p` does not hold for the left value, otherwise, returns a `Left`. + * + * {{{ + * Left(12).left.filter(_ > 10) // Some(Left(12)) + * Left(7).left.filter(_ > 10) // None + * Right(12).left.filter(_ > 10) // None + * }}} + */ + @deprecated("Use `filterToOption`, which more accurately reflects the return type", "2.13.0") + def filter[B1](p: A => Boolean): Option[Either[A, B1]] = e match { + case x @ Left(a) if p(a) => Some(x.asInstanceOf[Either[A, B1]]) + case _ => None + } + + /** Returns `None` if this is a `Right` or if the given predicate + * `p` does not hold for the left value, otherwise, returns a `Left`. + * + * {{{ + * Left(12).left.filterToOption(_ > 10) // Some(Left(12)) + * Left(7).left.filterToOption(_ > 10) // None + * Right(12).left.filterToOption(_ > 10) // None + * }}} + */ + def filterToOption[B1](p: A => Boolean): Option[Either[A, B1]] = e match { + case x @ Left(a) if p(a) => Some(x.asInstanceOf[Either[A, B1]]) + case _ => None + } + + /** Returns a `Seq` containing the `Left` value if it exists or an empty + * `Seq` if this is a `Right`. + * + * {{{ + * Left(12).left.toSeq // Seq(12) + * Right(12).left.toSeq // Seq() + * }}} + */ + def toSeq: Seq[A] = e match { + case Left(a) => Seq(a) + case _ => Seq.empty + } + + /** Returns a `Some` containing the `Left` value if it exists or a + * `None` if this is a `Right`. + * + * {{{ + * Left(12).left.toOption // Some(12) + * Right(12).left.toOption // None + * }}} + */ + def toOption: Option[A] = e match { + case Left(a) => Some(a) + case _ => None + } + } + + /** Projects an `Either` into a `Right`. + * + * Because `Either` is already right-biased, this class is not normally needed. + * (It is retained in the library for now for easy cross-compilation between Scala + * 2.11 and 2.12.) + */ + @deprecated("Either is now right-biased, calls to `right` should be removed", "2.13.0") + final case class RightProjection[+A, +B](e: Either[A, B]) { + + /** Returns the value from this `Right` or throws + * `NoSuchElementException` if this is a `Left`. + * + * {{{ + * Right(12).right.get // 12 + * Left(12).right.get // NoSuchElementException + * }}} + * + * @throws NoSuchElementException if the projection is `Left`. + */ + @deprecated("Use `Either.toOption.get` instead", "2.13.0") + def get: B = e match { + case Right(b) => b + case _ => throw new NoSuchElementException("Either.right.get on Left") + } + + /** Executes the given side-effecting function if this is a `Right`. + * + * {{{ + * Right(12).right.foreach(x => println(x)) // prints "12" + * Left(12).right.foreach(x => println(x)) // doesn't print + * }}} + * @param f The side-effecting function to execute. + */ + def foreach[U](f: B => U): Unit = e match { + case Right(b) => f(b) + case _ => () + } + + /** Returns the value from this `Right` or the given argument if this is a `Left`. + * + * {{{ + * Right(12).right.getOrElse(17) // 12 + * Left(12).right.getOrElse(17) // 17 + * }}} + */ + def getOrElse[B1 >: B](or: => B1): B1 = e match { + case Right(b) => b + case _ => or + } + + /** Returns `true` if `Left` or returns the result of the application of + * the given function to the `Right` value. + * + * {{{ + * Right(12).right.forall(_ > 10) // true + * Right(7).right.forall(_ > 10) // false + * Left(12).right.forall(_ > 10) // true + * }}} + */ + def forall(f: B => Boolean): Boolean = e match { + case Right(b) => f(b) + case _ => true + } + + /** Returns `false` if `Left` or returns the result of the application of + * the given function to the `Right` value. + * + * {{{ + * Right(12).right.exists(_ > 10) // true + * Right(7).right.exists(_ > 10) // false + * Left(12).right.exists(_ > 10) // false + * }}} + */ + def exists(p: B => Boolean): Boolean = e match { + case Right(b) => p(b) + case _ => false + } + + /** Binds the given function across `Right`. + * + * @param f The function to bind across `Right`. + */ + def flatMap[A1 >: A, B1](f: B => Either[A1, B1]): Either[A1, B1] = e match { + case Right(b) => f(b) + case _ => e.asInstanceOf[Either[A1, B1]] + } + + /** The given function is applied if this is a `Right`. + * + * {{{ + * Right(12).right.map(x => "flower") // Result: Right("flower") + * Left(12).right.map(x => "flower") // Result: Left(12) + * }}} + */ + def map[B1](f: B => B1): Either[A, B1] = e match { + case Right(b) => Right(f(b)) + case _ => e.asInstanceOf[Either[A, B1]] + } + + /** Returns `None` if this is a `Left` or if the + * given predicate `p` does not hold for the right value, + * otherwise, returns a `Right`. + * + * {{{ + * Right(12).right.filter(_ > 10) // Some(Right(12)) + * Right(7).right.filter(_ > 10) // None + * Left(12).right.filter(_ > 10) // None + * }}} + */ + @deprecated("Use `filterToOption`, which more accurately reflects the return type", "2.13.0") + def filter[A1](p: B => Boolean): Option[Either[A1, B]] = e match { + case Right(b) if p(b) => Some(Right(b)) + case _ => None + } + + /** Returns `None` if this is a `Left` or if the + * given predicate `p` does not hold for the right value, + * otherwise, returns a `Right`. + * + * {{{ + * Right(12).right.filterToOption(_ > 10) // Some(Right(12)) + * Right(7).right.filterToOption(_ > 10) // None + * Left(12).right.filterToOption(_ > 10) // None + * }}} + */ + def filterToOption[A1](p: B => Boolean): Option[Either[A1, B]] = e match { + case r @ Right(b) if p(b) => Some(r.asInstanceOf[Either[A1, B]]) + case _ => None + } + + /** Returns a `Seq` containing the `Right` value if + * it exists or an empty `Seq` if this is a `Left`. + * + * {{{ + * Right(12).right.toSeq // Seq(12) + * Left(12).right.toSeq // Seq() + * }}} + */ + def toSeq: Seq[B] = e match { + case Right(b) => Seq(b) + case _ => Seq.empty + } + + /** Returns a `Some` containing the `Right` value + * if it exists or a `None` if this is a `Left`. + * + * {{{ + * Right(12).right.toOption // Some(12) + * Left(12).right.toOption // None + * }}} + */ + def toOption: Option[B] = e match { + case Right(b) => Some(b) + case _ => None + } + } +} diff --git a/library/src/scala/util/Properties.scala b/library/src/scala/util/Properties.scala new file mode 100644 index 000000000000..24dee49ae951 --- /dev/null +++ b/library/src/scala/util/Properties.scala @@ -0,0 +1,232 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +import java.io.{IOException, PrintWriter} +import java.util.jar.Attributes.{Name => AttributeName} +import scala.annotation.tailrec + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn: Class[Option[_]] = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: => String) = Option(System.getProperty(name)).getOrElse(alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: => String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: => Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: => String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The version of the Scala runtime, if this is not a snapshot. + */ + val releaseVersion = scalaPropOrNone("maven.version.number").filterNot(_.endsWith("-SNAPSHOT")) + + /** The version of the Scala runtime, if this is a snapshot. + */ + val developmentVersion = scalaPropOrNone("maven.version.number").filter(_.endsWith("-SNAPSHOT")).flatMap(_ => scalaPropOrNone("version.number")) + + /** The version of the Scala runtime, or the empty string if unknown. + * + * Note that the version of the Scala library need not correlate with the version of the Scala compiler + * used to emit either the library or user code. + * + * For example, Scala 3.0 and 3.1 use the Scala 2.13 library, which is reflected in this version string. + * For the Dotty version, see `dotty.tools.dotc.config.Properties.versionNumberString`. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** A verbose alternative to [[versionNumberString]]. + */ + val versionString = s"version ${scalaPropOrElse("version.number", "(unknown)")}" + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2025, LAMP/EPFL and Lightbend, Inc. dba Akka") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = System.lineSeparator() + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + lazy val isWin = osName.startsWith("Windows") + // See https://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + lazy val isMac = osName.startsWith("Mac OS X") + /** Returns `true` iff the underlying operating system is a Linux distribution. */ + lazy val isLinux = osName.startsWith("Linux") + + /* Some runtime values. */ + private[scala] lazy val isAvian = javaVmName.contains("Avian") + + private[scala] def coloredOutputEnabled: Boolean = propOrElse("scala.color", "auto") match { + case "auto" => consoleIsTerminal + case s => "" == s || "true".equalsIgnoreCase(s) + } + + /** System.console.isTerminal, or just check for null console on JDK < 22 */ + private[scala] lazy val consoleIsTerminal: Boolean = { + import language.reflectiveCalls + val console = System.console + def isTerminal: Boolean = + try console.asInstanceOf[{ def isTerminal(): Boolean }].isTerminal() + catch { case _: NoSuchMethodException => false } + console != null && (!isJavaAtLeast("22") || isTerminal) + } + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + private[scala] def versionFor(command: String) = s"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version number (legacy forms acceptable) + * @return `true` if the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 8. + * isJavaAtLeast("1.8") // true + * isJavaAtLeast("8") // true + * isJavaAtLeast("9") // false + * isJavaAtLeast("9.1") // false + * isJavaAtLeast("1.9") // throws + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def versionOf(s: String, depth: Int): (Int, String) = + s.indexOf('.') match { + case 0 => + (-2, s.substring(1)) + case 1 if depth == 0 && s.charAt(0) == '1' => + val r0 = s.substring(2) + val (v, r) = versionOf(r0, 1) + val n = if (v > 8 || r0.isEmpty) -2 else v // accept 1.8, not 1.9 or 1. + (n, r) + case -1 => + val n = if (!s.isEmpty) s.toInt else if (depth == 0) -2 else 0 + (n, "") + case i => + val r = s.substring(i + 1) + val n = if (depth < 2 && r.isEmpty) -2 else s.substring(0, i).toInt + (n, r) + } + @tailrec + def compareVersions(s: String, v: String, depth: Int): Int = { + if (depth >= 3) 0 + else { + val (sn, srest) = versionOf(s, depth) + val (vn, vrest) = versionOf(v, depth) + if (vn < 0) -2 + else if (sn < vn) -1 + else if (sn > vn) 1 + else compareVersions(srest, vrest, depth + 1) + } + } + compareVersions(javaSpecVersion, version, 0) match { + case -2 => throw new NumberFormatException(s"Not a version: $version") + case i => i >= 0 + } + } + + /** Compares the given specification version to the major version of the platform. + * @param version a specification major version number + */ + def isJavaAtLeast(version: Int): Boolean = isJavaAtLeast(math.max(version, 0).toString) + + // provide a main method so version info can be obtained by running this + def main(args: Array[String]): Unit = { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } +} diff --git a/library/src/scala/util/Random.scala b/library/src/scala/util/Random.scala new file mode 100644 index 000000000000..84b44fcc91f5 --- /dev/null +++ b/library/src/scala/util/Random.scala @@ -0,0 +1,264 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +import scala.annotation.{migration, tailrec} +import scala.collection.mutable.ArrayBuffer +import scala.collection.BuildFrom +import scala.collection.immutable.LazyList +import scala.language.implicitConversions + +class Random(val self: java.util.Random) extends AnyRef with Serializable { + /** Creates a new random number generator using a single long seed. */ + def this(seed: Long) = this(new java.util.Random(seed)) + + /** Creates a new random number generator using a single integer seed. */ + def this(seed: Int) = this(seed.toLong) + + /** Creates a new random number generator. */ + def this() = this(new java.util.Random()) + + /** Returns the next pseudorandom, uniformly distributed boolean value + * from this random number generator's sequence. + */ + def nextBoolean(): Boolean = self.nextBoolean() + + /** Generates random bytes and places them into a user-supplied byte + * array. + */ + def nextBytes(bytes: Array[Byte]): Unit = { self.nextBytes(bytes) } + + /** Generates `n` random bytes and returns them in a new array. */ + def nextBytes(n: Int): Array[Byte] = { + val bytes = new Array[Byte](0 max n) + self.nextBytes(bytes) + bytes + } + + /** Returns the next pseudorandom, uniformly distributed double value + * between 0.0 and 1.0 from this random number generator's sequence. + */ + def nextDouble(): Double = self.nextDouble() + + /** Returns the next pseudorandom, uniformly distributed double value + * between min (inclusive) and max (exclusive) from this random number generator's sequence. + */ + def between(minInclusive: Double, maxExclusive: Double): Double = { + require(minInclusive < maxExclusive, "Invalid bounds") + + val next = nextDouble() * (maxExclusive - minInclusive) + minInclusive + if (next < maxExclusive) next + else Math.nextAfter(maxExclusive, Double.NegativeInfinity) + } + + /** Returns the next pseudorandom, uniformly distributed float value + * between 0.0 and 1.0 from this random number generator's sequence. + */ + def nextFloat(): Float = self.nextFloat() + + /** Returns the next pseudorandom, uniformly distributed float value + * between min (inclusive) and max (exclusive) from this random number generator's sequence. + */ + def between(minInclusive: Float, maxExclusive: Float): Float = { + require(minInclusive < maxExclusive, "Invalid bounds") + + val next = nextFloat() * (maxExclusive - minInclusive) + minInclusive + if (next < maxExclusive) next + else Math.nextAfter(maxExclusive, Float.NegativeInfinity) + } + + /** Returns the next pseudorandom, Gaussian ("normally") distributed + * double value with mean 0.0 and standard deviation 1.0 from this + * random number generator's sequence. + */ + def nextGaussian(): Double = self.nextGaussian() + + /** Returns the next pseudorandom, uniformly distributed int value + * from this random number generator's sequence. + */ + def nextInt(): Int = self.nextInt() + + /** Returns a pseudorandom, uniformly distributed int value between 0 + * (inclusive) and the specified value (exclusive), drawn from this + * random number generator's sequence. + */ + def nextInt(n: Int): Int = self.nextInt(n) + + /** Returns a pseudorandom, uniformly distributed int value between min + * (inclusive) and the specified value max (exclusive), drawn from this + * random number generator's sequence. + */ + def between(minInclusive: Int, maxExclusive: Int): Int = { + require(minInclusive < maxExclusive, "Invalid bounds") + + val difference = maxExclusive - minInclusive + if (difference >= 0) { + nextInt(difference) + minInclusive + } else { + /* The interval size here is greater than Int.MaxValue, + * so the loop will exit with a probability of at least 1/2. + */ + @tailrec + def loop(): Int = { + val n = nextInt() + if (n >= minInclusive && n < maxExclusive) n + else loop() + } + loop() + } + } + + /** Returns the next pseudorandom, uniformly distributed long value + * from this random number generator's sequence. + */ + def nextLong(): Long = self.nextLong() + + /** Returns a pseudorandom, uniformly distributed long value between 0 + * (inclusive) and the specified value (exclusive), drawn from this + * random number generator's sequence. + */ + def nextLong(n: Long): Long = { + require(n > 0, "n must be positive") + + /* + * Divide n by two until small enough for nextInt. On each + * iteration (at most 31 of them but usually much less), + * randomly choose both whether to include high bit in result + * (offset) and whether to continue with the lower vs upper + * half (which makes a difference only if odd). + */ + + var offset = 0L + var _n = n + + while (_n >= Integer.MAX_VALUE) { + val bits = nextInt(2) + val halfn = _n >>> 1 + val nextn = + if ((bits & 2) == 0) halfn + else _n - halfn + if ((bits & 1) == 0) + offset += _n - nextn + _n = nextn + } + offset + nextInt(_n.toInt) + } + + /** Returns a pseudorandom, uniformly distributed long value between min + * (inclusive) and the specified value max (exclusive), drawn from this + * random number generator's sequence. + */ + def between(minInclusive: Long, maxExclusive: Long): Long = { + require(minInclusive < maxExclusive, "Invalid bounds") + + val difference = maxExclusive - minInclusive + if (difference >= 0) { + nextLong(difference) + minInclusive + } else { + /* The interval size here is greater than Long.MaxValue, + * so the loop will exit with a probability of at least 1/2. + */ + @tailrec + def loop(): Long = { + val n = nextLong() + if (n >= minInclusive && n < maxExclusive) n + else loop() + } + loop() + } + } + + /** Returns a pseudorandomly generated String. This routine does + * not take any measures to preserve the randomness of the distribution + * in the face of factors like unicode's variable-length encoding, + * so please don't use this for anything important. It's primarily + * intended for generating test data. + * + * @param length the desired length of the String + * @return the String + */ + def nextString(length: Int): String = { + def safeChar(): Char = { + val surrogateStart: Int = 0xD800 + val res = nextInt(surrogateStart - 1) + 1 + res.toChar + } + if (length <= 0) { + "" + } else { + val arr = new Array[Char](length) + var i = 0 + while (i < length) { + arr(i) = safeChar() + i += 1 + } + new String(arr) + } + } + + /** Returns the next pseudorandom, uniformly distributed value + * from the ASCII range 33-126. + */ + def nextPrintableChar(): Char = { + val low = 33 + val high = 127 + (self.nextInt(high - low) + low).toChar + } + + def setSeed(seed: Long): Unit = { self.setSeed(seed) } + + /** Returns a new collection of the same type in a randomly chosen order. + * + * @return the shuffled collection + */ + def shuffle[T, C](xs: IterableOnce[T])(implicit bf: BuildFrom[xs.type, T, C]): C = { + val buf = new ArrayBuffer[T] ++= xs + + def swap(i1: Int, i2: Int): Unit = { + val tmp = buf(i1) + buf(i1) = buf(i2) + buf(i2) = tmp + } + + for (n <- buf.length to 2 by -1) { + val k = nextInt(n) + swap(n - 1, k) + } + + (bf.newBuilder(xs) ++= buf).result() + } + + /** Returns a LazyList of pseudorandomly chosen alphanumeric characters, + * equally chosen from A-Z, a-z, and 0-9. + */ + @migration("`alphanumeric` returns a LazyList instead of a Stream", "2.13.0") + def alphanumeric: LazyList[Char] = { + def nextAlphaNum: Char = { + val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + chars charAt (self nextInt chars.length) + } + + LazyList continually nextAlphaNum + } + +} + +/** The object `Random` offers a default implementation + * of scala.util.Random and random-related convenience methods. + */ +object Random extends Random { + + implicit def javaRandomToRandom(r: java.util.Random): Random = new Random(r) + +} diff --git a/library/src/scala/util/Sorting.scala b/library/src/scala/util/Sorting.scala new file mode 100644 index 000000000000..7e2da2434f82 --- /dev/null +++ b/library/src/scala/util/Sorting.scala @@ -0,0 +1,302 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +import scala.reflect.ClassTag +import scala.math.Ordering + +/** The `Sorting` object provides convenience wrappers for `java.util.Arrays.sort`. + * Methods that defer to `java.util.Arrays.sort` say that they do or under what + * conditions that they do. + * + * `Sorting` also implements a general-purpose quicksort and stable (merge) sort + * for those cases where `java.util.Arrays.sort` could only be used at the cost + * of a large memory penalty. If performance rather than memory usage is the + * primary concern, one may wish to find alternate strategies to use + * `java.util.Arrays.sort` directly e.g. by boxing primitives to use + * a custom ordering on them. + * + * `Sorting` provides methods where you can provide a comparison function, or + * can request a sort of items that are [[scala.math.Ordered]] or that + * otherwise have an implicit or explicit [[scala.math.Ordering]]. + * + * Note also that high-performance non-default sorts for numeric types + * are not provided. If this is required, it is advisable to investigate + * other libraries that cover this use case. + */ +object Sorting { + /** Sort an array of Doubles using `java.util.Arrays.sort`. */ + def quickSort(a: Array[Double]): Unit = java.util.Arrays.sort(a) + + /** Sort an array of Ints using `java.util.Arrays.sort`. */ + def quickSort(a: Array[Int]): Unit = java.util.Arrays.sort(a) + + /** Sort an array of Floats using `java.util.Arrays.sort`. */ + def quickSort(a: Array[Float]): Unit = java.util.Arrays.sort(a) + + private final val qsortThreshold = 16 + + /** Sort array `a` with quicksort, using the Ordering on its elements. + * This algorithm sorts in place, so no additional memory is used aside from + * what might be required to box individual elements during comparison. + */ + def quickSort[K: Ordering](a: Array[K]): Unit = { + // Must have iN >= i0 or math will fail. Also, i0 >= 0. + def inner(a: Array[K], i0: Int, iN: Int, ord: Ordering[K]): Unit = { + if (iN - i0 < qsortThreshold) insertionSort(a, i0, iN, ord) + else { + val iK = (i0 + iN) >>> 1 // Unsigned div by 2 + // Find index of median of first, central, and last elements + var pL = + if (ord.compare(a(i0), a(iN - 1)) <= 0) + if (ord.compare(a(i0), a(iK)) < 0) + if (ord.compare(a(iN - 1), a(iK)) < 0) iN - 1 else iK + else i0 + else + if (ord.compare(a(i0), a(iK)) < 0) i0 + else + if (ord.compare(a(iN - 1), a(iK)) <= 0) iK + else iN - 1 + val pivot = a(pL) + // pL is the start of the pivot block; move it into the middle if needed + if (pL != iK) { a(pL) = a(iK); a(iK) = pivot; pL = iK } + // Elements equal to the pivot will be in range pL until pR + var pR = pL + 1 + // Items known to be less than pivot are below iA (range i0 until iA) + var iA = i0 + // Items known to be greater than pivot are at or above iB (range iB until iN) + var iB = iN + // Scan through everything in the buffer before the pivot(s) + while (pL - iA > 0) { + val current = a(iA) + ord.compare(current, pivot) match { + case 0 => + // Swap current out with pivot block + a(iA) = a(pL - 1) + a(pL - 1) = current + pL -= 1 + case x if x < 0 => + // Already in place. Just update indices. + iA += 1 + case _ if iB > pR => + // Wrong side. There's room on the other side, so swap + a(iA) = a(iB - 1) + a(iB - 1) = current + iB -= 1 + case _ => + // Wrong side and there is no room. Swap by rotating pivot block. + a(iA) = a(pL - 1) + a(pL - 1) = a(pR - 1) + a(pR - 1) = current + pL -= 1 + pR -= 1 + iB -= 1 + } + } + // Get anything remaining in buffer after the pivot(s) + while (iB - pR > 0) { + val current = a(iB - 1) + ord.compare(current, pivot) match { + case 0 => + // Swap current out with pivot block + a(iB - 1) = a(pR) + a(pR) = current + pR += 1 + case x if x > 0 => + // Already in place. Just update indices. + iB -= 1 + case _ => + // Wrong side and we already know there is no room. Swap by rotating pivot block. + a(iB - 1) = a(pR) + a(pR) = a(pL) + a(pL) = current + iA += 1 + pL += 1 + pR += 1 + } + } + // Use tail recursion on large half (Sedgewick's method) so we don't blow up the stack if pivots are poorly chosen + if (iA - i0 < iN - iB) { + inner(a, i0, iA, ord) // True recursion + inner(a, iB, iN, ord) // Should be tail recursion + } + else { + inner(a, iB, iN, ord) // True recursion + inner(a, i0, iA, ord) // Should be tail recursion + } + } + } + inner(a, 0, a.length, implicitly[Ordering[K]]) + } + + private final val mergeThreshold = 32 + + // Ordering[T] might be slow especially for boxed primitives, so use binary search variant of insertion sort + // Caller must pass iN >= i0 or math will fail. Also, i0 >= 0. + private def insertionSort[@specialized T](a: Array[T], i0: Int, iN: Int, ord: Ordering[T]): Unit = { + val n = iN - i0 + if (n < 2) return + if (ord.compare(a(i0), a(i0+1)) > 0) { + val temp = a(i0) + a(i0) = a(i0+1) + a(i0+1) = temp + } + var m = 2 + while (m < n) { + // Speed up already-sorted case by checking last element first + val next = a(i0 + m) + if (ord.compare(next, a(i0+m-1)) < 0) { + var iA = i0 + var iB = i0 + m - 1 + while (iB - iA > 1) { + val ix = (iA + iB) >>> 1 // Use bit shift to get unsigned div by 2 + if (ord.compare(next, a(ix)) < 0) iB = ix + else iA = ix + } + val ix = iA + (if (ord.compare(next, a(iA)) < 0) 0 else 1) + var i = i0 + m + while (i > ix) { + a(i) = a(i-1) + i -= 1 + } + a(ix) = next + } + m += 1 + } + } + + // Caller is required to pass iN >= i0, else math will fail. Also, i0 >= 0. + private def mergeSort[@specialized T: ClassTag](a: Array[T], i0: Int, iN: Int, ord: Ordering[T], scratch: Array[T] = null): Unit = { + if (iN - i0 < mergeThreshold) insertionSort(a, i0, iN, ord) + else { + val iK = (i0 + iN) >>> 1 // Bit shift equivalent to unsigned math, no overflow + val sc = if (scratch eq null) new Array[T](iK - i0) else scratch + mergeSort(a, i0, iK, ord, sc) + mergeSort(a, iK, iN, ord, sc) + mergeSorted(a, i0, iK, iN, ord, sc) + } + } + + // Must have 0 <= i0 < iK < iN + private def mergeSorted[@specialized T](a: Array[T], i0: Int, iK: Int, iN: Int, ord: Ordering[T], scratch: Array[T]): Unit = { + // Check to make sure we're not already in order + if (ord.compare(a(iK-1), a(iK)) > 0) { + var i = i0 + val jN = iK - i0 + var j = 0 + while (i < iK) { + scratch (j) = a(i) + i += 1 + j += 1 + } + var k = i0 + j = 0 + while (i < iN && j < jN) { + if (ord.compare(a(i), scratch(j)) < 0) { a(k) = a(i); i += 1 } + else { a(k) = scratch(j); j += 1 } + k += 1 + } + while (j < jN) { a(k) = scratch(j); j += 1; k += 1 } + // Don't need to finish a(i) because it's already in place, k = i + } + } + + // Why would you even do this? + private def booleanSort(a: Array[Boolean], from: Int, until: Int): Unit = { + var i = from + var n = 0 + while (i < until) { + if (!a(i)) n += 1 + i += 1 + } + i = 0 + while (i < n) { + a(from + i) = false + i += 1 + } + while (from + i < until) { + a(from + i) = true + i += 1 + } + } + + // TODO: add upper bound: T <: AnyRef, propagate to callers below (not binary compatible) + // Maybe also rename all these methods to `sort`. + @inline private def sort[T](a: Array[T], from: Int, until: Int, ord: Ordering[T]): Unit = (a: @unchecked) match { + case _: Array[AnyRef] => + // Note that runtime matches are covariant, so could actually be any Array[T] s.t. T is not primitive (even boxed value classes) + if (a.length > 1 && (ord eq null)) throw new NullPointerException("Ordering") + java.util.Arrays.sort(a, from, until, ord) + case a: Array[Int] => if (ord eq Ordering.Int) java.util.Arrays.sort(a, from, until) else mergeSort[Int](a, from, until, ord) + case a: Array[Double] => mergeSort[Double](a, from, until, ord) // Because not all NaNs are identical, stability is meaningful! + case a: Array[Long] => if (ord eq Ordering.Long) java.util.Arrays.sort(a, from, until) else mergeSort[Long](a, from, until, ord) + case a: Array[Float] => mergeSort[Float](a, from, until, ord) // Because not all NaNs are identical, stability is meaningful! + case a: Array[Char] => if (ord eq Ordering.Char) java.util.Arrays.sort(a, from, until) else mergeSort[Char](a, from, until, ord) + case a: Array[Byte] => if (ord eq Ordering.Byte) java.util.Arrays.sort(a, from, until) else mergeSort[Byte](a, from, until, ord) + case a: Array[Short] => if (ord eq Ordering.Short) java.util.Arrays.sort(a, from, until) else mergeSort[Short](a, from, until, ord) + case a: Array[Boolean] => if (ord eq Ordering.Boolean) booleanSort(a, from, until) else mergeSort[Boolean](a, from, until, ord) + // Array[Unit] is matched as an Array[AnyRef] due to covariance in runtime matching. Not worth catching it as a special case. + case null => throw new NullPointerException + } + + /** Sort array `a` using the Ordering on its elements, preserving the original ordering where possible. + * Uses `java.util.Arrays.sort` unless `K` is a primitive type. This is the same as `stableSort(a, 0, a.length)`. */ + @`inline` def stableSort[K: Ordering](a: Array[K]): Unit = stableSort(a, 0, a.length) + + /** Sort array `a` or a part of it using the Ordering on its elements, preserving the original ordering where possible. + * Uses `java.util.Arrays.sort` unless `K` is a primitive type. + * + * @param a The array to sort + * @param from The first index in the array to sort + * @param until The last index (exclusive) in the array to sort + */ + def stableSort[K: Ordering](a: Array[K], from: Int, until: Int): Unit = sort(a, from, until, Ordering[K]) + + /** Sort array `a` using function `f` that computes the less-than relation for each element. + * Uses `java.util.Arrays.sort` unless `K` is a primitive type. This is the same as `stableSort(a, f, 0, a.length)`. */ + @`inline` def stableSort[K](a: Array[K], f: (K, K) => Boolean): Unit = stableSort(a, f, 0, a.length) + + // TODO: make this fast for primitive K (could be specialized if it didn't go through Ordering) + /** Sort array `a` or a part of it using function `f` that computes the less-than relation for each element. + * Uses `java.util.Arrays.sort` unless `K` is a primitive type. + * + * @param a The array to sort + * @param f A function that computes the less-than relation for each element + * @param from The first index in the array to sort + * @param until The last index (exclusive) in the array to sort + */ + def stableSort[K](a: Array[K], f: (K, K) => Boolean, from: Int, until: Int): Unit = sort(a, from, until, Ordering fromLessThan f) + + /** A sorted Array, using the Ordering for the elements in the sequence `a`. Uses `java.util.Arrays.sort` unless `K` is a primitive type. */ + def stableSort[K: ClassTag: Ordering](a: scala.collection.Seq[K]): Array[K] = { + val ret = a.toArray + sort(ret, 0, ret.length, Ordering[K]) + ret + } + + // TODO: make this fast for primitive K (could be specialized if it didn't go through Ordering) + /** A sorted Array, given a function `f` that computes the less-than relation for each item in the sequence `a`. Uses `java.util.Arrays.sort` unless `K` is a primitive type. */ + def stableSort[K: ClassTag](a: scala.collection.Seq[K], f: (K, K) => Boolean): Array[K] = { + val ret = a.toArray + sort(ret, 0, ret.length, Ordering fromLessThan f) + ret + } + + /** A sorted Array, given an extraction function `f` that returns an ordered key for each item in the sequence `a`. Uses `java.util.Arrays.sort` unless `K` is a primitive type. */ + def stableSort[K: ClassTag, M: Ordering](a: scala.collection.Seq[K], f: K => M): Array[K] = { + val ret = a.toArray + sort(ret, 0, ret.length, Ordering[M] on f) + ret + } +} diff --git a/library/src/scala/util/Try.scala b/library/src/scala/util/Try.scala new file mode 100644 index 000000000000..c17d457c9fe5 --- /dev/null +++ b/library/src/scala/util/Try.scala @@ -0,0 +1,290 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +import scala.runtime.Statics +import scala.util.control.NonFatal + +/** + * The `Try` type represents a computation that may fail during evaluation by raising an exception. + * It holds either a successfully computed value or the exception that was thrown. + * This is similar to the [[scala.util.Either]] type, but with different semantics. + * + * Instances of `Try[T]` are an instance of either [[scala.util.Success]][T] or [[scala.util.Failure]][T]. + * + * For example, consider a computation that performs division on user-defined input. + * `Try` can reduce or eliminate the need for explicit exception handling in all of the places + * where an exception might be thrown. + * + * Example: + * {{{ + * import scala.io.StdIn + * import scala.util.{Try, Success, Failure} + * + * def divide: Try[Int] = { + * val dividend = Try(StdIn.readLine("Enter an Int that you'd like to divide:\n").toInt) + * val divisor = Try(StdIn.readLine("Enter an Int that you'd like to divide by:\n").toInt) + * val problem = dividend.flatMap(x => divisor.map(y => x/y)) + * problem match { + * case Success(v) => + * println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v) + * Success(v) + * case Failure(e) => + * println("You must've divided by zero or entered something that's not an Int. Try again!") + * println("Info from the exception: " + e.getMessage) + * divide + * } + * } + * + * }}} + * + * An important property of `Try` shown in the above example is its ability to ''pipeline'', or chain, operations, + * catching exceptions along the way. The `flatMap` and `map` combinators in the above example each essentially + * pass off either their successfully completed value, wrapped in the `Success` type for it to be further operated + * upon by the next combinator in the chain, or the exception wrapped in the `Failure` type usually to be simply + * passed on down the chain. Combinators such as `recover` and `recoverWith` are designed to provide some type of + * default behavior in the case of failure. + * + * ''Note'': only non-fatal exceptions are caught by the combinators on `Try` (see [[scala.util.control.NonFatal]]). + * Serious system errors, on the other hand, will be thrown. + * + * ''Note:'': all Try combinators will catch exceptions and return failure unless otherwise specified in the documentation. + */ +sealed abstract class Try[+T] extends Product with Serializable { + + /** Returns `true` if the `Try` is a `Failure`, `false` otherwise. + */ + def isFailure: Boolean + + /** Returns `true` if the `Try` is a `Success`, `false` otherwise. + */ + def isSuccess: Boolean + + /** Returns the value from this `Success` or the given `default` argument if this is a `Failure`. + * + * ''Note:'': This will throw an exception if it is not a success and default throws an exception. + */ + def getOrElse[U >: T](default: => U): U + + /** Returns this `Try` if it's a `Success` or the given `default` argument if this is a `Failure`. + */ + def orElse[U >: T](default: => Try[U]): Try[U] + + /** Returns the value from this `Success` or throws the exception if this is a `Failure`. + */ + def get: T + + /** + * Applies the given function `f` if this is a `Success`, otherwise returns `Unit` if this is a `Failure`. + * + * ''Note:'' If `f` throws, then this method may throw an exception. + */ + def foreach[U](f: T => U): Unit + + /** + * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. + */ + def flatMap[U](f: T => Try[U]): Try[U] + + /** + * Maps the given function to the value from this `Success` or returns this if this is a `Failure`. + */ + def map[U](f: T => U): Try[U] + + /** + * Applies the given partial function to the value from this `Success` or returns this if this is a `Failure`. + */ + def collect[U](pf: PartialFunction[T, U]): Try[U] + + /** + * Converts this to a `Failure` if the predicate is not satisfied. + */ + def filter(p: T => Boolean): Try[T] + + /** Creates a non-strict filter, which eventually converts this to a `Failure` + * if the predicate is not satisfied. + * + * Note: unlike filter, withFilter does not create a new Try. + * Instead, it restricts the domain of subsequent + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * + * As Try is a one-element collection, this may be a bit overkill, + * but it's consistent with withFilter on Option and the other collections. + * + * @param p the predicate used to test elements. + * @return an object of class `WithFilter`, which supports + * `map`, `flatMap`, `foreach`, and `withFilter` operations. + * All these operations apply to those elements of this Try + * which satisfy the predicate `p`. + */ + @inline final def withFilter(p: T => Boolean): WithFilter = new WithFilter(p) + + /** We need a whole WithFilter class to honor the "doesn't create a new + * collection" contract even though it seems unlikely to matter much in a + * collection with max size 1. + */ + final class WithFilter(p: T => Boolean) { + def map[U](f: T => U): Try[U] = Try.this filter p map f + def flatMap[U](f: T => Try[U]): Try[U] = Try.this filter p flatMap f + def foreach[U](f: T => U): Unit = Try.this filter p foreach f + def withFilter(q: T => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) + } + + /** + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like `flatMap` for the exception. + */ + def recoverWith[U >: T](pf: PartialFunction[Throwable, Try[U]]): Try[U] + + /** + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like map for the exception. + */ + def recover[U >: T](pf: PartialFunction[Throwable, U]): Try[U] + + /** + * Returns `None` if this is a `Failure` or a `Some` containing the value if this is a `Success`. + */ + def toOption: Option[T] + + /** + * Transforms a nested `Try`, ie, a `Try` of type `Try[Try[T]]`, + * into an un-nested `Try`, ie, a `Try` of type `Try[T]`. + */ + def flatten[U](implicit ev: T <:< Try[U]): Try[U] + + /** + * Inverts this `Try`. If this is a `Failure`, returns its exception wrapped in a `Success`. + * If this is a `Success`, returns a `Failure` containing an `UnsupportedOperationException`. + */ + def failed: Try[Throwable] + + /** Completes this `Try` by applying the function `f` to this if this is of type `Failure`, or conversely, by applying + * `s` if this is a `Success`. + */ + def transform[U](s: T => Try[U], f: Throwable => Try[U]): Try[U] + + /** + * Returns `Left` with `Throwable` if this is a `Failure`, otherwise returns `Right` with `Success` value. + */ + def toEither: Either[Throwable, T] + + /** + * Applies `fa` if this is a `Failure` or `fb` if this is a `Success`. + * If `fb` is initially applied and throws an exception, + * then `fa` is applied with this exception. + * + * @example {{{ + * val result: Try[Int] = Try { string.toInt } + * log(result.fold( + * ex => "Operation failed with " + ex, + * v => "Operation produced value: " + v + * )) + * }}} + * + * @param fa the function to apply if this is a `Failure` + * @param fb the function to apply if this is a `Success` + * @return the results of applying the function + */ + def fold[U](fa: Throwable => U, fb: T => U): U + +} + +object Try { + /** Constructs a `Try` using the by-name parameter as a result value. + * + * The evaluation of `r` is attempted once. + * + * Any non-fatal exception is caught and results in a `Failure` + * that holds the exception. + * + * @param r the result value to compute + * @return the result of evaluating the value, as a `Success` or `Failure` + */ + def apply[T](r: => T): Try[T] = + try { + val r1 = r + Success(r1) + } catch { + case NonFatal(e) => Failure(e) + } +} + +final case class Failure[+T](exception: Throwable) extends Try[T] { + override def isFailure: Boolean = true + override def isSuccess: Boolean = false + override def get: T = throw exception + override def getOrElse[U >: T](default: => U): U = default + override def orElse[U >: T](default: => Try[U]): Try[U] = + try default catch { case NonFatal(e) => Failure(e) } + override def flatMap[U](f: T => Try[U]): Try[U] = this.asInstanceOf[Try[U]] + override def flatten[U](implicit ev: T <:< Try[U]): Try[U] = this.asInstanceOf[Try[U]] + override def foreach[U](f: T => U): Unit = () + override def transform[U](s: T => Try[U], f: Throwable => Try[U]): Try[U] = + try f(exception) catch { case NonFatal(e) => Failure(e) } + override def map[U](f: T => U): Try[U] = this.asInstanceOf[Try[U]] + override def collect[U](pf: PartialFunction[T, U]): Try[U] = this.asInstanceOf[Try[U]] + override def filter(p: T => Boolean): Try[T] = this + override def recover[U >: T](pf: PartialFunction[Throwable, U]): Try[U] = { + val marker = Statics.pfMarker + try { + val v = pf.applyOrElse(exception, (x: Throwable) => marker) + if (marker ne v.asInstanceOf[AnyRef]) Success(v.asInstanceOf[U]) else this + } catch { case NonFatal(e) => Failure(e) } + } + override def recoverWith[U >: T](pf: PartialFunction[Throwable, Try[U]]): Try[U] = { + val marker = Statics.pfMarker + try { + val v = pf.applyOrElse(exception, (x: Throwable) => marker) + if (marker ne v.asInstanceOf[AnyRef]) v.asInstanceOf[Try[U]] else this + } catch { case NonFatal(e) => Failure(e) } + } + override def failed: Try[Throwable] = Success(exception) + override def toOption: Option[T] = None + override def toEither: Either[Throwable, T] = Left(exception) + override def fold[U](fa: Throwable => U, fb: T => U): U = fa(exception) +} + +final case class Success[+T](value: T) extends Try[T] { + override def isFailure: Boolean = false + override def isSuccess: Boolean = true + override def get = value + override def getOrElse[U >: T](default: => U): U = get + override def orElse[U >: T](default: => Try[U]): Try[U] = this + override def flatMap[U](f: T => Try[U]): Try[U] = + try f(value) catch { case NonFatal(e) => Failure(e) } + override def flatten[U](implicit ev: T <:< Try[U]): Try[U] = value + override def foreach[U](f: T => U): Unit = f(value) + override def transform[U](s: T => Try[U], f: Throwable => Try[U]): Try[U] = this flatMap s + override def map[U](f: T => U): Try[U] = Try[U](f(value)) + override def collect[U](pf: PartialFunction[T, U]): Try[U] = { + val marker = Statics.pfMarker + try { + val v = pf.applyOrElse(value, ((x: T) => marker).asInstanceOf[Function[T, U]]) + if (marker ne v.asInstanceOf[AnyRef]) Success(v) + else Failure(new NoSuchElementException("Predicate does not hold for " + value)) + } catch { case NonFatal(e) => Failure(e) } + } + override def filter(p: T => Boolean): Try[T] = + try { + if (p(value)) this else Failure(new NoSuchElementException("Predicate does not hold for " + value)) + } catch { case NonFatal(e) => Failure(e) } + override def recover[U >: T](pf: PartialFunction[Throwable, U]): Try[U] = this + override def recoverWith[U >: T](pf: PartialFunction[Throwable, Try[U]]): Try[U] = this + override def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed")) + override def toOption: Option[T] = Some(value) + override def toEither: Either[Throwable, T] = Right(value) + override def fold[U](fa: Throwable => U, fb: T => U): U = + try { fb(value) } catch { case NonFatal(e) => fa(e) } +} diff --git a/library/src/scala/util/Using.scala b/library/src/scala/util/Using.scala new file mode 100644 index 000000000000..6e37277a6cd7 --- /dev/null +++ b/library/src/scala/util/Using.scala @@ -0,0 +1,432 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.util + +import scala.util.control.{ControlThrowable, NonFatal} + +/** A utility for performing automatic resource management. It can be used to perform an + * operation using resources, after which it releases the resources in reverse order + * of their creation. + * + * ==Usage== + * + * There are multiple ways to automatically manage resources with `Using`. If you only need + * to manage a single resource, the [[Using.apply `apply`]] method is easiest; it wraps the + * resource opening, operation, and resource releasing in a `Try`. + * + * Example: + * {{{ + * import java.io.{BufferedReader, FileReader} + * import scala.util.{Try, Using} + * + * val lines: Try[Seq[String]] = + * Using(new BufferedReader(new FileReader("file.txt"))) { reader => + * Iterator.continually(reader.readLine()).takeWhile(_ != null).toSeq + * } + * }}} + * + * If you need to manage multiple resources, [[Using.Manager$.apply `Using.Manager`]] should + * be used. It allows the managing of arbitrarily many resources, whose creation, use, and + * release are all wrapped in a `Try`. + * + * Example: + * {{{ + * import java.io.{BufferedReader, FileReader} + * import scala.util.{Try, Using} + * + * val files = List("file1.txt", "file2.txt", "file3.txt", "file4.txt") + * val lines: Try[Seq[String]] = Using.Manager { use => + * // acquire resources + * def mkreader(filename: String) = use(new BufferedReader(new FileReader(filename))) + * + * // use your resources here + * def lines(reader: BufferedReader): Iterator[String] = + * Iterator.continually(reader.readLine()).takeWhile(_ != null) + * + * files.map(mkreader).flatMap(lines) + * } + * }}} + * + * Composed or "wrapped" resources may be acquired in order of construction, + * if "underlying" resources are not closed. Although redundant in this case, + * here is the previous example with a wrapped call to `use`: + * {{{ + * def mkreader(filename: String) = use(new BufferedReader(use(new FileReader(filename)))) + * }}} + * + * Custom resources can be registered on construction by requiring an implicit `Manager`. + * This ensures they will be released even if composition fails: + * {{{ + * import scala.util.Using + * + * case class X(x: String)(implicit mgr: Using.Manager) extends AutoCloseable { + * override def close() = println(s"CLOSE $x") + * mgr.acquire(this) + * } + * case class Y(y: String)(x: String)(implicit mgr: Using.Manager) extends AutoCloseable { + * val xres = X(x) + * override def close() = println(s"CLOSE $y") + * // an error during construction releases previously acquired resources + * require(y != null, "y is null") + * mgr.acquire(this) + * } + * + * Using.Manager { implicit mgr => + * val y = Y("Y")("X") + * println(s"USE $y") + * } + * println { + * Using.Manager { implicit mgr => + * Y(null)("X") + * } + * } // Failure(java.lang.IllegalArgumentException: requirement failed: y is null) + * }}} + * + * If you wish to avoid wrapping management and operations in a `Try`, you can use + * [[Using.resource `Using.resource`]], which throws any exceptions that occur. + * + * Example: + * {{{ + * import java.io.{BufferedReader, FileReader} + * import scala.util.Using + * + * val lines: Seq[String] = + * Using.resource(new BufferedReader(new FileReader("file.txt"))) { reader => + * Iterator.continually(reader.readLine()).takeWhile(_ != null).toSeq + * } + * }}} + * + * ==Suppression Behavior== + * + * If two exceptions are thrown (e.g., by an operation and closing a resource), + * one of them is re-thrown, and the other is + * [[java.lang.Throwable#addSuppressed added to it as a suppressed exception]]. + * If the two exceptions are of different 'severities' (see below), the one of a higher + * severity is re-thrown, and the one of a lower severity is added to it as a suppressed + * exception. If the two exceptions are of the same severity, the one thrown first is + * re-thrown, and the one thrown second is added to it as a suppressed exception. + * If an exception is a [[scala.util.control.ControlThrowable `ControlThrowable`]], or + * if it does not support suppression (see + * [[java.lang.Throwable `Throwable`'s constructor with an `enableSuppression` parameter]]), + * an exception that would have been suppressed is instead discarded. + * + * Exceptions are ranked from highest to lowest severity as follows: + * - `java.lang.VirtualMachineError` + * - `java.lang.LinkageError` + * - `java.lang.InterruptedException` and `java.lang.ThreadDeath` + * - [[scala.util.control.NonFatal fatal exceptions]], excluding `scala.util.control.ControlThrowable` + * - `scala.util.control.ControlThrowable` + * - all other exceptions + * + * When more than two exceptions are thrown, the first two are combined and + * re-thrown as described above, and each successive exception thrown is combined + * as it is thrown. + * + * @define suppressionBehavior See the main doc for [[Using `Using`]] for full details of + * suppression behavior. + */ +object Using { + /** Performs an operation using a resource, and then releases the resource, + * even if the operation throws an exception. + * + * $suppressionBehavior + * + * @return a [[Try]] containing an exception if one or more were thrown, + * or the result of the operation if no exceptions were thrown + */ + def apply[R: Releasable, A](resource: => R)(f: R => A): Try[A] = Try { Using.resource(resource)(f) } + + /** A resource manager. + * + * Resources can be registered with the manager by calling [[acquire `acquire`]]; + * such resources will be released in reverse order of their acquisition + * when the manager is closed, regardless of any exceptions thrown + * during use. + * + * $suppressionBehavior + * + * @note It is recommended for API designers to require an implicit `Manager` + * for the creation of custom resources, and to call `acquire` during those + * resources' construction. Doing so guarantees that the resource ''must'' be + * automatically managed, and makes it impossible to forget to do so. + * + * + * Example: + * {{{ + * class SafeFileReader(file: File)(implicit manager: Using.Manager) + * extends BufferedReader(new FileReader(file)) { + * + * def this(fileName: String)(implicit manager: Using.Manager) = this(new File(fileName)) + * + * manager.acquire(this) + * } + * }}} + */ + final class Manager private { + import Manager._ + + private var closed = false + private[this] var resources: List[Resource[_]] = Nil + + /** Registers the specified resource with this manager, so that + * the resource is released when the manager is closed, and then + * returns the (unmodified) resource. + */ + def apply[R: Releasable](resource: R): resource.type = { + acquire(resource) + resource + } + + /** Registers the specified resource with this manager, so that + * the resource is released when the manager is closed. + */ + def acquire[R: Releasable](resource: R): Unit = { + if (resource == null) throw new NullPointerException("null resource") + if (closed) throw new IllegalStateException("Manager has already been closed") + resources = new Resource(resource) :: resources + } + + private def manage[A](op: Manager => A): A = { + var toThrow: Throwable = null + try { + op(this) + } catch { + case t: Throwable => + toThrow = t + null.asInstanceOf[A] // compiler doesn't know `finally` will throw + } finally { + closed = true + var rs = resources + resources = null // allow GC, in case something is holding a reference to `this` + while (rs.nonEmpty) { + val resource = rs.head + rs = rs.tail + try resource.release() + catch { + case t: Throwable => + if (toThrow == null) toThrow = t + else toThrow = preferentiallySuppress(toThrow, t) + } + } + if (toThrow != null) throw toThrow + } + } + } + + object Manager { + /** Performs an operation using a `Manager`, then closes the `Manager`, + * releasing its resources (in reverse order of acquisition). + * + * Example: + * {{{ + * val lines = Using.Manager { use => + * use(new BufferedReader(new FileReader("file.txt"))).lines() + * } + * }}} + * + * If using resources which require an implicit `Manager` as a parameter, + * this method should be invoked with an `implicit` modifier before the function + * parameter: + * + * Example: + * {{{ + * val lines = Using.Manager { implicit use => + * new SafeFileReader("file.txt").lines() + * } + * }}} + * + * See the main doc for [[Using `Using`]] for full details of suppression behavior. + * + * @param op the operation to perform using the manager + * @tparam A the return type of the operation + * @return a [[Try]] containing an exception if one or more were thrown, + * or the result of the operation if no exceptions were thrown + */ + def apply[A](op: Manager => A): Try[A] = Try { (new Manager).manage(op) } + + private final class Resource[R](resource: R)(implicit releasable: Releasable[R]) { + def release(): Unit = releasable.release(resource) + } + } + + private def preferentiallySuppress(primary: Throwable, secondary: Throwable): Throwable = { + @annotation.nowarn("cat=deprecation") // avoid warning on mention of ThreadDeath + def score(t: Throwable): Int = t match { + case _: VirtualMachineError => 4 + case _: LinkageError => 3 + case _: InterruptedException | _: ThreadDeath => 2 + case _: ControlThrowable => 0 + case e if !NonFatal(e) => 1 // in case this method gets out of sync with NonFatal + case _ => -1 + } + @inline def suppress(t: Throwable, suppressed: Throwable): Throwable = { t.addSuppressed(suppressed); t } + + if (score(secondary) > score(primary)) suppress(secondary, primary) + else suppress(primary, secondary) + } + + /** Performs an operation using a resource, and then releases the resource, + * even if the operation throws an exception. This method behaves similarly + * to Java's try-with-resources. + * + * $suppressionBehavior + * + * @param resource the resource + * @param body the operation to perform with the resource + * @tparam R the type of the resource + * @tparam A the return type of the operation + * @return the result of the operation, if neither the operation nor + * releasing the resource throws + */ + def resource[R, A](resource: R)(body: R => A)(implicit releasable: Releasable[R]): A = { + if (resource == null) throw new NullPointerException("null resource") + + var toThrow: Throwable = null + try { + body(resource) + } catch { + case t: Throwable => + toThrow = t + null.asInstanceOf[A] // compiler doesn't know `finally` will throw + } finally { + if (toThrow eq null) releasable.release(resource) + else { + try releasable.release(resource) + catch { case other: Throwable => toThrow = preferentiallySuppress(toThrow, other) } + finally throw toThrow + } + } + } + + /** Performs an operation using two resources, and then releases the resources + * in reverse order, even if the operation throws an exception. This method + * behaves similarly to Java's try-with-resources. + * + * $suppressionBehavior + * + * @param resource1 the first resource + * @param resource2 the second resource + * @param body the operation to perform using the resources + * @tparam R1 the type of the first resource + * @tparam R2 the type of the second resource + * @tparam A the return type of the operation + * @return the result of the operation, if neither the operation nor + * releasing the resources throws + */ + def resources[R1: Releasable, R2: Releasable, A]( + resource1: R1, + resource2: => R2 + )(body: (R1, R2) => A + ): A = + resource(resource1) { r1 => + resource(resource2) { r2 => + body(r1, r2) + } + } + + /** Performs an operation using three resources, and then releases the resources + * in reverse order, even if the operation throws an exception. This method + * behaves similarly to Java's try-with-resources. + * + * $suppressionBehavior + * + * @param resource1 the first resource + * @param resource2 the second resource + * @param resource3 the third resource + * @param body the operation to perform using the resources + * @tparam R1 the type of the first resource + * @tparam R2 the type of the second resource + * @tparam R3 the type of the third resource + * @tparam A the return type of the operation + * @return the result of the operation, if neither the operation nor + * releasing the resources throws + */ + def resources[R1: Releasable, R2: Releasable, R3: Releasable, A]( + resource1: R1, + resource2: => R2, + resource3: => R3 + )(body: (R1, R2, R3) => A + ): A = + resource(resource1) { r1 => + resource(resource2) { r2 => + resource(resource3) { r3 => + body(r1, r2, r3) + } + } + } + + /** Performs an operation using four resources, and then releases the resources + * in reverse order, even if the operation throws an exception. This method + * behaves similarly to Java's try-with-resources. + * + * $suppressionBehavior + * + * @param resource1 the first resource + * @param resource2 the second resource + * @param resource3 the third resource + * @param resource4 the fourth resource + * @param body the operation to perform using the resources + * @tparam R1 the type of the first resource + * @tparam R2 the type of the second resource + * @tparam R3 the type of the third resource + * @tparam R4 the type of the fourth resource + * @tparam A the return type of the operation + * @return the result of the operation, if neither the operation nor + * releasing the resources throws + */ + def resources[R1: Releasable, R2: Releasable, R3: Releasable, R4: Releasable, A]( + resource1: R1, + resource2: => R2, + resource3: => R3, + resource4: => R4 + )(body: (R1, R2, R3, R4) => A + ): A = + resource(resource1) { r1 => + resource(resource2) { r2 => + resource(resource3) { r3 => + resource(resource4) { r4 => + body(r1, r2, r3, r4) + } + } + } + } + + /** A type class describing how to release a particular type of resource. + * + * A resource is anything which needs to be released, closed, or otherwise cleaned up + * in some way after it is finished being used, and for which waiting for the object's + * garbage collection to be cleaned up would be unacceptable. For example, an instance of + * [[java.io.OutputStream]] would be considered a resource, because it is important to close + * the stream after it is finished being used. + * + * An instance of `Releasable` is needed in order to automatically manage a resource + * with [[Using `Using`]]. An implicit instance is provided for all types extending + * [[java.lang.AutoCloseable]]. + * + * @tparam R the type of the resource + */ + trait Releasable[-R] { + /** Releases the specified resource. */ + def release(resource: R): Unit + } + + object Releasable { + // prefer explicit types 2.14 + //implicit val AutoCloseableIsReleasable: Releasable[AutoCloseable] = new Releasable[AutoCloseable] {} + /** An implicit `Releasable` for [[java.lang.AutoCloseable `AutoCloseable`s]]. */ + implicit object AutoCloseableIsReleasable extends Releasable[AutoCloseable] { + def release(resource: AutoCloseable): Unit = resource.close() + } + } + +} diff --git a/library/src/scala/util/package.scala b/library/src/scala/util/package.scala new file mode 100644 index 000000000000..1b8b84dd2bfe --- /dev/null +++ b/library/src/scala/util/package.scala @@ -0,0 +1,20 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +package object util { + /** + * Adds chaining methods `tap` and `pipe` to every type. See [[ChainingOps]]. + */ + object chaining extends ChainingSyntax +} From abf65b08ebccf5a500b950a571ec99d7a40b95a1 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:42:06 +0100 Subject: [PATCH 20/28] chore: add all the files in scala.util.control --- library/src/scala/util/control/Breaks.scala | 124 ++++++ .../scala/util/control/ControlThrowable.scala | 47 +++ .../src/scala/util/control/Exception.scala | 374 ++++++++++++++++++ .../src/scala/util/control/NoStackTrace.scala | 35 ++ library/src/scala/util/control/NonFatal.scala | 49 +++ .../src/scala/util/control/TailCalls.scala | 115 ++++++ 6 files changed, 744 insertions(+) create mode 100644 library/src/scala/util/control/Breaks.scala create mode 100644 library/src/scala/util/control/ControlThrowable.scala create mode 100644 library/src/scala/util/control/Exception.scala create mode 100644 library/src/scala/util/control/NoStackTrace.scala create mode 100644 library/src/scala/util/control/NonFatal.scala create mode 100644 library/src/scala/util/control/TailCalls.scala diff --git a/library/src/scala/util/control/Breaks.scala b/library/src/scala/util/control/Breaks.scala new file mode 100644 index 000000000000..888867c0acaf --- /dev/null +++ b/library/src/scala/util/control/Breaks.scala @@ -0,0 +1,124 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.util.control + +/** Provides the `break` control abstraction. + * + * The `break` method uses a `ControlThrowable` to transfer + * control up the stack to an enclosing `breakable`. + * + * It is typically used to abruptly terminate a `for` loop, + * but can be used to return from an arbitrary computation. + * + * Control resumes after the `breakable`. + * + * If there is no matching `breakable`, the `BreakControl` + * thrown by `break` is handled in the usual way: if not + * caught, it may terminate the current `Thread`. + * + * `BreakControl` carries no stack trace, so the default + * exception handler does not print useful diagnostic + * information; there is no compile-time warning if there + * is no matching `breakable`. + * + * A catch clause using `NonFatal` is safe to use with + * `break`; it will not short-circuit the transfer + * of control to the enclosing `breakable`. + * + * A `breakable` matches a call to `break` if the methods + * were invoked on the same receiver object, which may be the + * convenience value `Breaks`. + * + * Example usage: + * {{{ + * val mybreaks = new Breaks + * import mybreaks.{break, breakable} + * + * breakable { + * for (x <- xs) { + * if (done) break() + * f(x) + * } + * } + * }}} + * Calls to `break` from one instance of `Breaks` will never + * resume at the `breakable` of some other instance. + * + * Any intervening exception handlers should use `NonFatal`, + * or use `Try` for evaluation: + * {{{ + * val mybreaks = new Breaks + * import mybreaks.{break, breakable} + * + * breakable { + * for (x <- xs) Try { if (quit) break else f(x) }.foreach(println) + * } + * }}} + */ +class Breaks { + + private[this] val breakException = new BreakControl + + /** A block from which one can exit with a `break`. The `break` may be + * executed further down in the call stack provided that it is called on the + * exact same instance of `Breaks`. + */ + def breakable(op: => Unit): Unit = + try op catch { case ex: BreakControl if ex eq breakException => } + + sealed trait TryBlock[T] { + def catchBreak(onBreak: => T): T + } + + /** Try a computation that produces a value, supplying a default + * to be used if the computation terminates with a `break`. + * + * {{{ + * tryBreakable { + * (1 to 3).map(i => if (math.random < .5) break else i * 2) + * } catchBreak { + * Vector.empty + * } + * }}} + */ + def tryBreakable[T](op: => T): TryBlock[T] = + new TryBlock[T] { + def catchBreak(onBreak: => T) = + try op catch { case ex: BreakControl if ex eq breakException => onBreak } + } + + /** Break from the dynamically closest enclosing breakable block that also uses + * this `Breaks` instance. + * + * @note This might be different from the statically closest enclosing block! + * @note Invocation without parentheses relies on the conversion to "empty application". + */ + def break(): Nothing = throw breakException +} + +/** An object that can be used for the break control abstraction. + * + * Example usage: + * {{{ + * import Breaks.{break, breakable} + * + * breakable { + * for (...) { + * if (...) break + * } + * } + * }}} + */ +object Breaks extends Breaks + +private class BreakControl extends ControlThrowable diff --git a/library/src/scala/util/control/ControlThrowable.scala b/library/src/scala/util/control/ControlThrowable.scala new file mode 100644 index 000000000000..ea5ff549e121 --- /dev/null +++ b/library/src/scala/util/control/ControlThrowable.scala @@ -0,0 +1,47 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.util.control + +/** A parent class for throwable objects intended for flow control. + * + * Instances of `ControlThrowable` should not normally be caught. + * + * As a convenience, `NonFatal` does not match `ControlThrowable`. + * + * {{{ + * import scala.util.control.{Breaks, NonFatal}, Breaks.{break, breakable} + * + * breakable { + * for (v <- values) { + * try { + * if (p(v)) break + * else ??? + * } catch { + * case NonFatal(t) => log(t) // can't catch a break + * } + * } + * } + * }}} + * + * Suppression is disabled, because flow control should not suppress + * an exceptional condition. Stack traces are also disabled, allowing + * instances of `ControlThrowable` to be safely reused. + * + * Instances of `ControlThrowable` should not normally have a cause. + * Legacy subclasses may set a cause using `initCause`. + */ +abstract class ControlThrowable(message: String) extends Throwable( + message, /*cause*/ null, /*enableSuppression=*/ false, /*writableStackTrace*/ false) { + + def this() = this(message = null) +} diff --git a/library/src/scala/util/control/Exception.scala b/library/src/scala/util/control/Exception.scala new file mode 100644 index 000000000000..181bb22743a7 --- /dev/null +++ b/library/src/scala/util/control/Exception.scala @@ -0,0 +1,374 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util +package control + +import scala.annotation.tailrec +import scala.reflect.{ClassTag, classTag} +import scala.language.implicitConversions + +/** Classes representing the components of exception handling. + * + * Each class is independently composable. + * + * This class differs from [[scala.util.Try]] in that it focuses on composing exception handlers rather than + * composing behavior. All behavior should be composed first and fed to a [[Catch]] object using one of the + * `opt`, `either` or `withTry` methods. Taken together the classes provide a DSL for composing catch and finally + * behaviors. + * + * === Examples === + * + * Create a `Catch` which handles specified exceptions. + * {{{ + * import scala.util.control.Exception._ + * import java.net._ + * + * val s = "https://www.scala-lang.org/" + * + * // Some(https://www.scala-lang.org/) + * val x1: Option[URL] = catching(classOf[MalformedURLException]) opt new URL(s) + * + * // Right(https://www.scala-lang.org/) + * val x2: Either[Throwable,URL] = + * catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s) + * + * // Success(https://www.scala-lang.org/) + * val x3: Try[URL] = catching(classOf[MalformedURLException], classOf[NullPointerException]) withTry new URL(s) + * + * val defaultUrl = new URL("http://example.com") + * // URL(http://example.com) because htt/xx throws MalformedURLException + * val x4: URL = failAsValue(classOf[MalformedURLException])(defaultUrl)(new URL("htt/xx")) + * }}} + * + * Create a `Catch` which logs exceptions using `handling` and `by`. + * {{{ + * def log(t: Throwable): Unit = t.printStackTrace + * + * val withThrowableLogging: Catch[Unit] = handling(classOf[MalformedURLException]) by (log) + * + * def printUrl(url: String) : Unit = { + * val con = new URL(url) openConnection() + * val source = scala.io.Source.fromInputStream(con.getInputStream()) + * source.getLines().foreach(println) + * } + * + * val badUrl = "htt/xx" + * // Prints stacktrace, + * // java.net.MalformedURLException: no protocol: htt/xx + * // at java.net.URL.(URL.java:586) + * withThrowableLogging { printUrl(badUrl) } + * + * val goodUrl = "https://www.scala-lang.org/" + * // Prints page content, + * // <!DOCTYPE html> + * // <html> + * withThrowableLogging { printUrl(goodUrl) } + * }}} + * + * Use `unwrapping` to create a `Catch` that unwraps exceptions before rethrowing. + * {{{ + * class AppException(cause: Throwable) extends RuntimeException(cause) + * + * val unwrappingCatch: Catch[Nothing] = unwrapping(classOf[AppException]) + * + * def calcResult: Int = throw new AppException(new NullPointerException) + * + * // Throws NPE not AppException, + * // java.lang.NullPointerException + * // at .calcResult(<console>:17) + * val result = unwrappingCatch(calcResult) + * }}} + * + * Use `failAsValue` to provide a default when a specified exception is caught. + * + * {{{ + * val inputDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0) + * val candidatePick = "seven" // scala.io.StdIn.readLine() + * + * // Int = 0 + * val pick = inputDefaulting(candidatePick.toInt) + * }}} + * + * Compose multiple `Catch`s with `or` to build a `Catch` that provides default values varied by exception. + * {{{ + * val formatDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0) + * val nullDefaulting: Catch[Int] = failAsValue(classOf[NullPointerException])(-1) + * val otherDefaulting: Catch[Int] = nonFatalCatch withApply(_ => -100) + * + * val combinedDefaulting: Catch[Int] = formatDefaulting or nullDefaulting or otherDefaulting + * + * def p(s: String): Int = s.length * s.toInt + * + * // Int = 0 + * combinedDefaulting(p("tenty-nine")) + * + * // Int = -1 + * combinedDefaulting(p(null: String)) + * + * // Int = -100 + * combinedDefaulting(throw new IllegalStateException) + * + * // Int = 22 + * combinedDefaulting(p("11")) + * }}} + * + * @groupname composition-catch Catch behavior composition + * @groupprio composition-catch 10 + * @groupdesc composition-catch Build Catch objects from exception lists and catch logic + * + * @groupname composition-finally Finally behavior composition + * @groupprio composition-finally 20 + * @groupdesc composition-finally Build Catch objects from finally logic + * + * @groupname canned-behavior General purpose catch objects + * @groupprio canned-behavior 30 + * @groupdesc canned-behavior Catch objects with predefined behavior. Use combinator methods to compose additional behavior. + * + * @groupname dsl DSL behavior composition + * @groupprio dsl 40 + * @groupdesc dsl Expressive Catch behavior composition + * + * @groupname composition-catch-promiscuously Promiscuous Catch behaviors + * @groupprio composition-catch-promiscuously 50 + * @groupdesc composition-catch-promiscuously Useful if catching `ControlThrowable` or `InterruptedException` is required. + * + * @groupname logic-container Logic Containers + * @groupprio logic-container 60 + * @groupdesc logic-container Containers for catch and finally behavior. + * + * @define protectedExceptions `ControlThrowable` or `InterruptedException` + */ + +object Exception { + type Catcher[+T] = PartialFunction[Throwable, T] + + def mkCatcher[Ex <: Throwable: ClassTag, T](isDef: Ex => Boolean, f: Ex => T): PartialFunction[Throwable, T] = new Catcher[T] { + private def downcast(x: Throwable): Option[Ex] = + if (classTag[Ex].runtimeClass.isAssignableFrom(x.getClass)) Some(x.asInstanceOf[Ex]) + else None + + def isDefinedAt(x: Throwable): Boolean = downcast(x) exists isDef + def apply(x: Throwable): T = f(downcast(x).get) + } + + def mkThrowableCatcher[T](isDef: Throwable => Boolean, f: Throwable => T): PartialFunction[Throwable, T] = mkCatcher[Throwable, T](isDef, f) + + implicit def throwableSubtypeToCatcher[Ex <: Throwable: ClassTag, T](pf: PartialFunction[Ex, T]): Catcher[T] = + mkCatcher(pf.isDefinedAt, pf.apply) + + /** !!! Not at all sure of every factor which goes into this, + * and/or whether we need multiple standard variations. + * @return true if `x` is $protectedExceptions otherwise false. + */ + def shouldRethrow(x: Throwable): Boolean = x match { + case _: ControlThrowable => true + case _: InterruptedException => true + // case _: java.lang.Error => true ? + case _ => false + } + + trait Described { + protected val name: String + private[this] var _desc: String = "" + def desc: String = _desc + def withDesc(s: String): this.type = { + _desc = s + this + } + override def toString(): String = name + "(" + desc + ")" + } + + /** A container class for finally code. + * @group logic-container + */ + class Finally private[Exception](body: => Unit) extends Described { + protected val name = "Finally" + + def and(other: => Unit): Finally = new Finally({ body ; other }) + def invoke(): Unit = { body } + } + + /** A container class for catch/finally logic. + * + * Pass a different value for rethrow if you want to probably + * unwisely allow catching control exceptions and other throwables + * which the rest of the world may expect to get through. + * @tparam T result type of bodies used in try and catch blocks + * @param pf Partial function used when applying catch logic to determine result value + * @param fin Finally logic which if defined will be invoked after catch logic + * @param rethrow Predicate on throwables determining when to rethrow a caught [[Throwable]] + * @group logic-container + */ + class Catch[+T]( + val pf: Catcher[T], + val fin: Option[Finally] = None, + val rethrow: Throwable => Boolean = shouldRethrow) + extends Described { + + protected val name = "Catch" + + /** Create a new Catch with additional exception handling logic. */ + def or[U >: T](pf2: Catcher[U]): Catch[U] = new Catch(pf orElse pf2, fin, rethrow) + def or[U >: T](other: Catch[U]): Catch[U] = or(other.pf) + + /** Apply this catch logic to the supplied body. */ + def apply[U >: T](body: => U): U = + try body + catch { + case x if rethrow(x) => throw x + case x if pf isDefinedAt x => pf(x) + } + finally fin foreach (_.invoke()) + + /** Create a new Catch container from this object and the supplied finally body. + * @param body The additional logic to apply after all existing finally bodies + */ + def andFinally(body: => Unit): Catch[T] = { + val appendedFin = fin map(_ and body) getOrElse new Finally(body) + new Catch(pf, Some(appendedFin), rethrow) + } + + /** Apply this catch logic to the supplied body, mapping the result + * into `Option[T]` - `None` if any exception was caught, `Some(T)` otherwise. + */ + def opt[U >: T](body: => U): Option[U] = toOption(Some(body)) + + /** Apply this catch logic to the supplied body, mapping the result + * into `Either[Throwable, T]` - `Left(exception)` if an exception was caught, + * `Right(T)` otherwise. + */ + def either[U >: T](body: => U): Either[Throwable, U] = toEither(Right(body)) + + /** Apply this catch logic to the supplied body, mapping the result + * into `Try[T]` - `Failure` if an exception was caught, `Success(T)` otherwise. + */ + def withTry[U >: T](body: => U): scala.util.Try[U] = toTry(Success(body)) + + /** Create a `Catch` object with the same `isDefinedAt` logic as this one, + * but with the supplied `apply` method replacing the current one. */ + def withApply[U](f: Throwable => U): Catch[U] = { + val pf2 = new Catcher[U] { + def isDefinedAt(x: Throwable): Boolean = pf isDefinedAt x + def apply(x: Throwable): U = f(x) + } + new Catch(pf2, fin, rethrow) + } + + /** Convenience methods. */ + def toOption: Catch[Option[T]] = withApply(_ => None) + def toEither: Catch[Either[Throwable, T]] = withApply(Left(_)) + def toTry: Catch[scala.util.Try[T]] = withApply(x => Failure(x)) + } + + final val nothingCatcher: Catcher[Nothing] = mkThrowableCatcher(_ => false, throw _) + final def nonFatalCatcher[T]: Catcher[T] = mkThrowableCatcher({ case NonFatal(_) => true; case _ => false }, throw _) + final def allCatcher[T]: Catcher[T] = mkThrowableCatcher(_ => true, throw _) + + /** The empty `Catch` object. + * @group canned-behavior + **/ + final val noCatch: Catch[Nothing] = new Catch(nothingCatcher) withDesc "" + + /** A `Catch` object which catches everything. + * @group canned-behavior + **/ + final def allCatch[T]: Catch[T] = new Catch(allCatcher[T]) withDesc "" + + /** A `Catch` object which catches non-fatal exceptions. + * @group canned-behavior + **/ + final def nonFatalCatch[T]: Catch[T] = new Catch(nonFatalCatcher[T]) withDesc "" + + /** Creates a `Catch` object which will catch any of the supplied exceptions. + * Since the returned `Catch` object has no specific logic defined and will simply + * rethrow the exceptions it catches, you will typically want to call `opt`, + * `either` or `withTry` on the return value, or assign custom logic by calling "withApply". + * + * Note that `Catch` objects automatically rethrow `ControlExceptions` and others + * which should only be caught in exceptional circumstances. If you really want + * to catch exactly what you specify, use `catchingPromiscuously` instead. + * @group composition-catch + */ + def catching[T](exceptions: Class[_]*): Catch[T] = + new Catch(pfFromExceptions(exceptions : _*)) withDesc (exceptions map (_.getName) mkString ", ") + + def catching[T](c: Catcher[T]): Catch[T] = new Catch(c) + + /** Creates a `Catch` object which will catch any of the supplied exceptions. + * Unlike "catching" which filters out those in shouldRethrow, this one will + * catch whatever you ask of it including $protectedExceptions. + * @group composition-catch-promiscuously + */ + def catchingPromiscuously[T](exceptions: Class[_]*): Catch[T] = catchingPromiscuously(pfFromExceptions(exceptions : _*)) + def catchingPromiscuously[T](c: Catcher[T]): Catch[T] = new Catch(c, None, _ => false) + + /** Creates a `Catch` object which catches and ignores any of the supplied exceptions. + * @group composition-catch + */ + def ignoring(exceptions: Class[_]*): Catch[Unit] = + catching(exceptions: _*) withApply (_ => ()) + + /** Creates a `Catch` object which maps all the supplied exceptions to `None`. + * @group composition-catch + */ + def failing[T](exceptions: Class[_]*): Catch[Option[T]] = + catching(exceptions: _*) withApply (_ => None) + + /** Creates a `Catch` object which maps all the supplied exceptions to the given value. + * @group composition-catch + */ + def failAsValue[T](exceptions: Class[_]*)(value: => T): Catch[T] = + catching(exceptions: _*) withApply (_ => value) + + class By[T,R](f: T => R) { + def by(x: T): R = f(x) + } + + /** Returns a partially constructed `Catch` object, which you must give + * an exception handler function as an argument to `by`. + * @example + * {{{ + * handling(classOf[MalformedURLException], classOf[NullPointerException]) by (_.printStackTrace) + * }}} + * @group dsl + */ + def handling[T](exceptions: Class[_]*): By[Throwable => T, Catch[T]] = { + def fun(f: Throwable => T): Catch[T] = catching(exceptions: _*) withApply f + new By[Throwable => T, Catch[T]](fun) + } + + /** Returns a `Catch` object with no catch logic and the argument as the finally logic. + * @group composition-finally + */ + def ultimately[T](body: => Unit): Catch[T] = noCatch andFinally body + + /** Creates a `Catch` object which unwraps any of the supplied exceptions. + * @group composition-catch + */ + def unwrapping[T](exceptions: Class[_]*): Catch[T] = { + @tailrec + def unwrap(x: Throwable): Throwable = + if (wouldMatch(x, exceptions) && x.getCause != null) unwrap(x.getCause) + else x + + catching(exceptions: _*) withApply (x => throw unwrap(x)) + } + + /** Private **/ + private def wouldMatch(x: Throwable, classes: scala.collection.Seq[Class[_]]): Boolean = + classes exists (_ isAssignableFrom x.getClass) + + private def pfFromExceptions(exceptions: Class[_]*): PartialFunction[Throwable, Nothing] = + { case x if wouldMatch(x, exceptions) => throw x } +} diff --git a/library/src/scala/util/control/NoStackTrace.scala b/library/src/scala/util/control/NoStackTrace.scala new file mode 100644 index 000000000000..f34fec38180c --- /dev/null +++ b/library/src/scala/util/control/NoStackTrace.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util.control + +/** A trait for exceptions which, for efficiency reasons, do not + * fill in the stack trace. Stack trace suppression can be disabled + * on a global basis via a system property wrapper in + * [[scala.sys.SystemProperties]]. + * + * @note Since JDK 1.7, a similar effect can be achieved with `class Ex extends Throwable(..., writableStackTrace = false)` + */ +trait NoStackTrace extends Throwable { + override def fillInStackTrace(): Throwable = + if (NoStackTrace.noSuppression) super.fillInStackTrace() + else this +} + +object NoStackTrace { + final def noSuppression = _noSuppression + + // two-stage init to make checkinit happy, since sys.SystemProperties.noTraceSuppression.value calls back into NoStackTrace.noSuppression + final private[this] var _noSuppression = false + _noSuppression = System.getProperty("scala.control.noTraceSuppression", "").equalsIgnoreCase("true") +} diff --git a/library/src/scala/util/control/NonFatal.scala b/library/src/scala/util/control/NonFatal.scala new file mode 100644 index 000000000000..80d8812a42ef --- /dev/null +++ b/library/src/scala/util/control/NonFatal.scala @@ -0,0 +1,49 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util.control + +/** + * Extractor of non-fatal Throwables. Will not match fatal errors like `VirtualMachineError` + * (for example, `OutOfMemoryError` and `StackOverflowError`, subclasses of `VirtualMachineError`), `ThreadDeath`, + * `LinkageError`, `InterruptedException`, `ControlThrowable`. + * + * Note that [[scala.util.control.ControlThrowable]], an internal Throwable, is not matched by + * `NonFatal` (and would therefore be thrown). + * + * For example, all harmless Throwables can be caught by: + * {{{ + * try { + * // dangerous stuff + * } catch { + * case NonFatal(e) => log.error(e, "Something not that bad.") + * // or + * case e if NonFatal(e) => log.error(e, "Something not that bad.") + * } + * }}} + */ +object NonFatal { + /** + * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal + */ + @annotation.nowarn("cat=deprecation") // avoid warning on mention of ThreadDeath + def apply(t: Throwable): Boolean = t match { + // VirtualMachineError includes OutOfMemoryError and other fatal errors + case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false + case _ => true + } + /** + * Returns Some(t) if NonFatal(t) == true, otherwise None + */ + def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None +} diff --git a/library/src/scala/util/control/TailCalls.scala b/library/src/scala/util/control/TailCalls.scala new file mode 100644 index 000000000000..717ea9004f3f --- /dev/null +++ b/library/src/scala/util/control/TailCalls.scala @@ -0,0 +1,115 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.util.control + +import annotation.tailrec + +/** Methods exported by this object implement tail calls via trampolining. + * + * Tail calling methods must either return their result using `done` or call the + * next method using `tailcall`. Both return an instance of `TailRec`. The result + * of evaluating a tailcalling function can be retrieved from a `TailRec` + * value using method `result`. + * + * Implemented as described in "Stackless Scala with Free Monads" + * [[https://blog.higher-order.com/assets/trampolines.pdf]] + * + * Here's a usage example: + * {{{ + * import scala.util.control.TailCalls._ + * + * def isEven(xs: List[Int]): TailRec[Boolean] = + * if (xs.isEmpty) done(true) else tailcall(isOdd(xs.tail)) + * + * def isOdd(xs: List[Int]): TailRec[Boolean] = + * if (xs.isEmpty) done(false) else tailcall(isEven(xs.tail)) + * + * isEven((1 to 100000).toList).result + * + * def fib(n: Int): TailRec[Int] = + * if (n < 2) done(n) else for { + * x <- tailcall(fib(n - 1)) + * y <- tailcall(fib(n - 2)) + * } yield x + y + * + * fib(40).result + * }}} + */ +object TailCalls { + + /** This class represents a tailcalling computation. + */ + sealed abstract class TailRec[+A] { + + /** Continue the computation with `f`. */ + final def map[B](f: A => B): TailRec[B] = flatMap(a => Call(() => Done(f(a)))) + + /** Continue the computation with `f` and merge the trampolining + * of this computation with that of `f`. */ + final def flatMap[B](f: A => TailRec[B]): TailRec[B] = this match { + case Done(a) => Call(() => f(a)) + case Call(_) => Cont(this, f) + // Take advantage of the monad associative law to optimize the size of the required stack + case c: Cont[a1, b1] => Cont(c.a, (x: a1) => c.f(x).flatMap(f)) + } + + /** Returns either the next step of the tailcalling computation, + * or the result if there are no more steps. */ + @tailrec final def resume: Either[() => TailRec[A], A] = this match { + case Done(a) => Right(a) + case Call(k) => Left(k) + case Cont(a, f) => a match { + case Done(v) => f(v).resume + case Call(k) => Left(() => k().flatMap(f)) + case Cont(b, g) => b.flatMap(x => g(x).flatMap(f)).resume + } + } + + /** Returns the result of the tailcalling computation. + */ + @tailrec final def result: A = this match { + case Done(a) => a + case Call(t) => t().result + case Cont(a, f) => a match { + case Done(v) => f(v).result + case Call(t) => t().flatMap(f).result + case Cont(b, g) => b.flatMap(x => g(x).flatMap(f)).result + } + } + } + + /** Internal class representing a tailcall. */ + protected case class Call[A](rest: () => TailRec[A]) extends TailRec[A] + + /** Internal class representing the final result returned from a tailcalling + * computation. */ + protected case class Done[A](value: A) extends TailRec[A] + + /** Internal class representing a continuation with function A => TailRec[B]. + * It is needed for the flatMap to be implemented. */ + protected case class Cont[A, B](a: TailRec[A], f: A => TailRec[B]) extends TailRec[B] + + /** Perform a tailcall. + * @param rest the expression to be evaluated in the tailcall + * @return a `TailRec` object representing the expression `rest` + */ + def tailcall[A](rest: => TailRec[A]): TailRec[A] = Call(() => rest) + + /** Return the final result from a tailcalling computation. + * @param `result` the result value + * @return a `TailRec` object representing a computation which immediately + * returns `result` + */ + def done[A](result: A): TailRec[A] = Done(result) + +} From 2aed4843b4b5a777e2befb300656dc64fcef84d5 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:42:44 +0100 Subject: [PATCH 21/28] chore: add all the files in scala.util.hashing --- .../scala/util/hashing/ByteswapHashing.scala | 40 ++ library/src/scala/util/hashing/Hashing.scala | 42 ++ .../src/scala/util/hashing/MurmurHash3.scala | 426 ++++++++++++++++++ library/src/scala/util/hashing/package.scala | 40 ++ 4 files changed, 548 insertions(+) create mode 100644 library/src/scala/util/hashing/ByteswapHashing.scala create mode 100644 library/src/scala/util/hashing/Hashing.scala create mode 100644 library/src/scala/util/hashing/MurmurHash3.scala create mode 100644 library/src/scala/util/hashing/package.scala diff --git a/library/src/scala/util/hashing/ByteswapHashing.scala b/library/src/scala/util/hashing/ByteswapHashing.scala new file mode 100644 index 000000000000..ca96e4d1f4a5 --- /dev/null +++ b/library/src/scala/util/hashing/ByteswapHashing.scala @@ -0,0 +1,40 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util.hashing + + + + + + +/** A fast multiplicative hash by Phil Bagwell. + */ +final class ByteswapHashing[T] extends Hashing[T] { + + def hash(v: T) = byteswap32(v.##) + +} + + +object ByteswapHashing { + + private class Chained[T](h: Hashing[T]) extends Hashing[T] { + def hash(v: T) = byteswap32(h.hash(v)) + } + + /** Composes another `Hashing` with the Byteswap hash. + */ + def chain[T](h: Hashing[T]): Hashing[T] = new Chained(h) + +} diff --git a/library/src/scala/util/hashing/Hashing.scala b/library/src/scala/util/hashing/Hashing.scala new file mode 100644 index 000000000000..28067cce9992 --- /dev/null +++ b/library/src/scala/util/hashing/Hashing.scala @@ -0,0 +1,42 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util.hashing + +import scala.annotation.implicitNotFound + +/** `Hashing` is a trait whose instances each represent a strategy for hashing + * instances of a type. + * + * `Hashing`'s companion object defines a default hashing strategy for all + * objects - it calls their `##` method. + * + * Note: when using a custom `Hashing`, make sure to use it with the `Equiv` + * such that if any two objects are equal, then their hash codes must be equal. + */ +@implicitNotFound(msg = "No implicit Hashing defined for ${T}.") +trait Hashing[T] extends Serializable { + def hash(x: T): Int +} + +object Hashing { + final class Default[T] extends Hashing[T] { + def hash(x: T) = x.## + } + + implicit def default[T]: Default[T] = new Default[T] + + def fromFunction[T](f: T => Int) = new Hashing[T] { + def hash(x: T) = f(x) + } +} diff --git a/library/src/scala/util/hashing/MurmurHash3.scala b/library/src/scala/util/hashing/MurmurHash3.scala new file mode 100644 index 000000000000..7d741d6913df --- /dev/null +++ b/library/src/scala/util/hashing/MurmurHash3.scala @@ -0,0 +1,426 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util.hashing + +import java.lang.Integer.{ rotateLeft => rotl } + +private[hashing] class MurmurHash3 { + /** Mix in a block of data into an intermediate hash value. */ + final def mix(hash: Int, data: Int): Int = { + var h = mixLast(hash, data) + h = rotl(h, 13) + h * 5 + 0xe6546b64 + } + + /** May optionally be used as the last mixing step. Is a little bit faster than mix, + * as it does no further mixing of the resulting hash. For the last element this is not + * necessary as the hash is thoroughly mixed during finalization anyway. */ + final def mixLast(hash: Int, data: Int): Int = { + var k = data + + k *= 0xcc9e2d51 + k = rotl(k, 15) + k *= 0x1b873593 + + hash ^ k + } + + /** Finalize a hash to incorporate the length and make sure all bits avalanche. */ + final def finalizeHash(hash: Int, length: Int): Int = avalanche(hash ^ length) + + /** Force all bits of the hash to avalanche. Used for finalizing the hash. */ + private final def avalanche(hash: Int): Int = { + var h = hash + + h ^= h >>> 16 + h *= 0x85ebca6b + h ^= h >>> 13 + h *= 0xc2b2ae35 + h ^= h >>> 16 + + h + } + + private[scala] def tuple2Hash(x: Int, y: Int, seed: Int): Int = { + var h = seed + h = mix(h, "Tuple2".hashCode) + h = mix(h, x) + h = mix(h, y) + finalizeHash(h, 2) + } + + /** Compute the hash of a product */ + final def productHash(x: Product, seed: Int, ignorePrefix: Boolean = false): Int = { + val arr = x.productArity + // Case objects have the hashCode inlined directly into the + // synthetic hashCode method, but this method should still give + // a correct result if passed a case object. + if (arr == 0) { + x.productPrefix.hashCode + } else { + var h = seed + if (!ignorePrefix) h = mix(h, x.productPrefix.hashCode) + var i = 0 + while (i < arr) { + h = mix(h, x.productElement(i).##) + i += 1 + } + finalizeHash(h, arr) + } + } + + /** Compute the hash of a string */ + final def stringHash(str: String, seed: Int): Int = { + var h = seed + var i = 0 + while (i + 1 < str.length) { + val data = (str.charAt(i) << 16) + str.charAt(i + 1) + h = mix(h, data) + i += 2 + } + if (i < str.length) h = mixLast(h, str.charAt(i).toInt) + finalizeHash(h, str.length) + } + + /** Compute a hash that is symmetric in its arguments - that is a hash + * where the order of appearance of elements does not matter. + * This is useful for hashing sets, for example. + */ + final def unorderedHash(xs: IterableOnce[Any], seed: Int): Int = { + var a, b, n = 0 + var c = 1 + val iterator = xs.iterator + while (iterator.hasNext) { + val x = iterator.next() + val h = x.## + a += h + b ^= h + c *= h | 1 + n += 1 + } + var h = seed + h = mix(h, a) + h = mix(h, b) + h = mixLast(h, c) + finalizeHash(h, n) + } + + /** Compute a hash that depends on the order of its arguments. Potential range + * hashes are recognized to produce a hash that is compatible with rangeHash. + */ + final def orderedHash(xs: IterableOnce[Any], seed: Int): Int = { + val it = xs.iterator + var h = seed + if(!it.hasNext) return finalizeHash(h, 0) + val x0 = it.next() + if(!it.hasNext) return finalizeHash(mix(h, x0.##), 1) + val x1 = it.next() + + val initial = x0.## + h = mix(h, initial) + val h0 = h + var prev = x1.## + val rangeDiff = prev - initial + var i = 2 + while (it.hasNext) { + h = mix(h, prev) + val hash = it.next().## + if(rangeDiff != hash - prev || rangeDiff == 0) { + h = mix(h, hash) + i += 1 + while (it.hasNext) { + h = mix(h, it.next().##) + i += 1 + } + return finalizeHash(h, i) + } + prev = hash + i += 1 + } + avalanche(mix(mix(h0, rangeDiff), prev)) + + } + + /** Compute the hash of an array. Potential range hashes are recognized to produce a + * hash that is compatible with rangeHash. + */ + final def arrayHash[@specialized T](a: Array[T], seed: Int): Int = { + var h = seed + val l = a.length + l match { + case 0 => + finalizeHash(h, 0) + case 1 => + finalizeHash(mix(h, a(0).##), 1) + case _ => + val initial = a(0).## + h = mix(h, initial) + val h0 = h + var prev = a(1).## + val rangeDiff = prev - initial + var i = 2 + while (i < l) { + h = mix(h, prev) + val hash = a(i).## + if(rangeDiff != hash - prev || rangeDiff == 0) { + h = mix(h, hash) + i += 1 + while (i < l) { + h = mix(h, a(i).##) + i += 1 + } + return finalizeHash(h, l) + } + prev = hash + i += 1 + } + avalanche(mix(mix(h0, rangeDiff), prev)) + } + } + + /** Compute the hash of a Range with at least 2 elements. Ranges with fewer + * elements need to use seqHash instead. The `last` parameter must be the + * actual last element produced by a Range, not the nominal `end`. + */ + final def rangeHash(start: Int, step: Int, last: Int, seed: Int): Int = + avalanche(mix(mix(mix(seed, start), step), last)) + + /** Compute the hash of a byte array. Faster than arrayHash, because + * it hashes 4 bytes at once. Note that the result is not compatible with + * arrayHash! + */ + final def bytesHash(data: Array[Byte], seed: Int): Int = { + var len = data.length + var h = seed + + // Body + var i = 0 + while(len >= 4) { + var k = data(i + 0) & 0xFF + k |= (data(i + 1) & 0xFF) << 8 + k |= (data(i + 2) & 0xFF) << 16 + k |= (data(i + 3) & 0xFF) << 24 + + h = mix(h, k) + + i += 4 + len -= 4 + } + + // Tail + var k = 0 + if(len == 3) k ^= (data(i + 2) & 0xFF) << 16 + if(len >= 2) k ^= (data(i + 1) & 0xFF) << 8 + if(len >= 1) { + k ^= (data(i + 0) & 0xFF) + h = mixLast(h, k) + } + + // Finalization + finalizeHash(h, data.length) + } + + /** Compute the hash of an IndexedSeq. Potential range hashes are recognized to produce a + * hash that is compatible with rangeHash. + */ + final def indexedSeqHash(a: scala.collection.IndexedSeq[Any], seed: Int): Int = { + var h = seed + val l = a.length + l match { + case 0 => + finalizeHash(h, 0) + case 1 => + finalizeHash(mix(h, a(0).##), 1) + case _ => + val initial = a(0).## + h = mix(h, initial) + val h0 = h + var prev = a(1).## + val rangeDiff = prev - initial + var i = 2 + while (i < l) { + h = mix(h, prev) + val hash = a(i).## + if(rangeDiff != hash - prev || rangeDiff == 0) { + h = mix(h, hash) + i += 1 + while (i < l) { + h = mix(h, a(i).##) + i += 1 + } + return finalizeHash(h, l) + } + prev = hash + i += 1 + } + avalanche(mix(mix(h0, rangeDiff), prev)) + } + } + + /** Compute the hash of a List. Potential range hashes are recognized to produce a + * hash that is compatible with rangeHash. + */ + final def listHash(xs: scala.collection.immutable.List[_], seed: Int): Int = { + var n = 0 + var h = seed + var rangeState = 0 // 0 = no data, 1 = first elem read, 2 = has valid diff, 3 = invalid + var rangeDiff = 0 + var prev = 0 + var initial = 0 + var elems = xs + while (!elems.isEmpty) { + val head = elems.head + val tail = elems.tail + val hash = head.## + h = mix(h, hash) + rangeState match { + case 0 => + initial = hash + rangeState = 1 + case 1 => + rangeDiff = hash - prev + rangeState = 2 + case 2 => + if(rangeDiff != hash - prev || rangeDiff == 0) rangeState = 3 + case _ => + } + prev = hash + n += 1 + elems = tail + } + if(rangeState == 2) rangeHash(initial, rangeDiff, prev, seed) + else finalizeHash(h, n) + } +} + +/** + * An implementation of Austin Appleby's MurmurHash 3 algorithm + * (MurmurHash3_x86_32). This object contains methods that hash + * values of various types as well as means to construct `Hashing` + * objects. + * + * This algorithm is designed to generate well-distributed non-cryptographic + * hashes. It is designed to hash data in 32 bit chunks (ints). + * + * The mix method needs to be called at each step to update the intermediate + * hash value. For the last chunk to incorporate into the hash mixLast may + * be used instead, which is slightly faster. Finally finalizeHash needs to + * be called to compute the final hash value. + * + * This is based on the earlier MurmurHash3 code by Rex Kerr, but the + * MurmurHash3 algorithm was since changed by its creator Austin Appleby + * to remedy some weaknesses and improve performance. This represents the + * latest and supposedly final version of the algorithm (revision 136). Even + * so, test the generated hashes in between Scala versions, even for point + * releases, as fast, non-cryptographic hashing algorithms evolve rapidly. + * + * @see [[https://github.com/aappleby/smhasher]] + */ +object MurmurHash3 extends MurmurHash3 { + final val arraySeed = 0x3c074a61 + final val stringSeed = 0xf7ca7fd2 + final val productSeed = 0xcafebabe + final val symmetricSeed = 0xb592f7ae + final val traversableSeed = 0xe73a8b15 + final val seqSeed = "Seq".hashCode + final val mapSeed = "Map".hashCode + final val setSeed = "Set".hashCode + + def arrayHash[@specialized T](a: Array[T]): Int = arrayHash(a, arraySeed) + def bytesHash(data: Array[Byte]): Int = bytesHash(data, arraySeed) + def orderedHash(xs: IterableOnce[Any]): Int = orderedHash(xs, symmetricSeed) + def productHash(x: Product): Int = productHash(x, productSeed) + def stringHash(x: String): Int = stringHash(x, stringSeed) + def unorderedHash(xs: IterableOnce[Any]): Int = unorderedHash(xs, traversableSeed) + def rangeHash(start: Int, step: Int, last: Int): Int = rangeHash(start, step, last, seqSeed) + + private[scala] def arraySeqHash[@specialized T](a: Array[T]): Int = arrayHash(a, seqSeed) + private[scala] def tuple2Hash(x: Any, y: Any): Int = tuple2Hash(x.##, y.##, productSeed) + + /** To offer some potential for optimization. + */ + def seqHash(xs: scala.collection.Seq[_]): Int = xs match { + case xs: scala.collection.IndexedSeq[_] => indexedSeqHash(xs, seqSeed) + case xs: List[_] => listHash(xs, seqSeed) + case xs => orderedHash(xs, seqSeed) + } + + def mapHash(xs: scala.collection.Map[_, _]): Int = { + if (xs.isEmpty) emptyMapHash + else { + class accum extends Function2[Any, Any, Unit] { + var a, b, n = 0 + var c = 1 + override def apply(k: Any, v: Any): Unit = { + val h = tuple2Hash(k, v) + a += h + b ^= h + c *= h | 1 + n += 1 + } + } + val accum = new accum + var h = mapSeed + xs.foreachEntry(accum) + h = mix(h, accum.a) + h = mix(h, accum.b) + h = mixLast(h, accum.c) + finalizeHash(h, accum.n) + } + } + + private[scala] val emptyMapHash = unorderedHash(Nil, mapSeed) + def setHash(xs: scala.collection.Set[_]): Int = unorderedHash(xs, setSeed) + + class ArrayHashing[@specialized T] extends Hashing[Array[T]] { + def hash(a: Array[T]) = arrayHash(a) + } + + def arrayHashing[@specialized T] = new ArrayHashing[T] + + def bytesHashing = new Hashing[Array[Byte]] { + def hash(data: Array[Byte]) = bytesHash(data) + } + + def orderedHashing = new Hashing[IterableOnce[Any]] { + def hash(xs: IterableOnce[Any]) = orderedHash(xs) + } + + def productHashing = new Hashing[Product] { + def hash(x: Product) = productHash(x) + } + + def stringHashing = new Hashing[String] { + def hash(x: String) = stringHash(x) + } + + def unorderedHashing = new Hashing[IterableOnce[Any]] { + def hash(xs: IterableOnce[Any]) = unorderedHash(xs) + } + +// /** All this trouble and foreach still appears faster. +// * Leaving in place in case someone would like to investigate further. +// */ +// def linearSeqHash(xs: scala.collection.LinearSeq[_], seed: Int): Int = { +// var n = 0 +// var h = seed +// var elems = xs +// while (elems.nonEmpty) { +// h = mix(h, elems.head.##) +// n += 1 +// elems = elems.tail +// } +// finalizeHash(h, n) +// } +} diff --git a/library/src/scala/util/hashing/package.scala b/library/src/scala/util/hashing/package.scala new file mode 100644 index 000000000000..530b729e2f81 --- /dev/null +++ b/library/src/scala/util/hashing/package.scala @@ -0,0 +1,40 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + + + + + + +package object hashing { + + /** Fast multiplicative hash with a nice distribution. + */ + def byteswap32(v: Int): Int = { + var hc = v * 0x9e3775cd + hc = java.lang.Integer.reverseBytes(hc) + hc * 0x9e3775cd + } + + /** Fast multiplicative hash with a nice distribution + * for 64-bit values. + */ + def byteswap64(v: Long): Long = { + var hc = v * 0x9e3775cd9e3775cdL + hc = java.lang.Long.reverseBytes(hc) + hc * 0x9e3775cd9e3775cdL + } + +} From 25f67c52b1683cd843b595c3c2bd97a3082ccdaa Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:43:24 +0100 Subject: [PATCH 22/28] chore: add all the files in scala.util.matching --- library/src/scala/util/matching/Regex.scala | 922 ++++++++++++++++++++ 1 file changed, 922 insertions(+) create mode 100644 library/src/scala/util/matching/Regex.scala diff --git a/library/src/scala/util/matching/Regex.scala b/library/src/scala/util/matching/Regex.scala new file mode 100644 index 000000000000..c19bc2a925b1 --- /dev/null +++ b/library/src/scala/util/matching/Regex.scala @@ -0,0 +1,922 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +/** + * This package is concerned with regular expression (regex) matching against strings, + * with the main goal of pulling out information from those matches, or replacing + * them with something else. + * + * [[scala.util.matching.Regex]] is the class users instantiate to do regular expression matching. + * + * The companion object to [[scala.util.matching.Regex]] contains supporting members: + * * [[scala.util.matching.Regex.Match]] makes more information about a match available. + * * [[scala.util.matching.Regex.MatchIterator]] is used to iterate over matched strings. + * * [[scala.util.matching.Regex.MatchData]] is just a base trait for the above classes. + * * [[scala.util.matching.Regex.Groups]] extracts group from a [[scala.util.matching.Regex.Match]] + * without recomputing the match. + */ +package scala.util.matching + +import scala.collection.AbstractIterator +import java.util.regex.{ Pattern, Matcher } + +/** A regular expression is used to determine whether a string matches a pattern + * and, if it does, to extract or transform the parts that match. + * + * === Usage === + + * This class delegates to the [[https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html java.util.regex]] package of the Java Platform. + * See the documentation for [[java.util.regex.Pattern]] for details about + * the regular expression syntax for pattern strings. + * + * An instance of `Regex` represents a compiled regular expression pattern. + * Since compilation is expensive, frequently used `Regex`es should be constructed + * once, outside of loops and perhaps in a companion object. + * + * The canonical way to create a `Regex` is by using the method `r`, provided + * implicitly for strings: + * + * {{{ + * val date = raw"(\d{4})-(\d{2})-(\d{2})".r + * }}} + * + * Since escapes are not processed in multi-line string literals, using triple quotes + * avoids having to escape the backslash character, so that `"\\d"` can be written `"""\d"""`. + * The same result is achieved with certain interpolators, such as `raw"\d".r` or + * a custom interpolator `r"\d"` that also compiles the `Regex`. + * + * === Extraction === + * To extract the capturing groups when a `Regex` is matched, use it as + * an extractor in a pattern match: + * + * {{{ + * "2004-01-20" match { + * case date(year, month, day) => s"\$year was a good year for PLs." + * } + * }}} + * + * To check only whether the `Regex` matches, ignoring any groups, + * use a sequence wildcard: + * + * {{{ + * "2004-01-20" match { + * case date(_*) => "It's a date!" + * } + * }}} + * + * That works because a `Regex` extractor produces a sequence of strings. + * Extracting only the year from a date could also be expressed with + * a sequence wildcard: + * + * {{{ + * "2004-01-20" match { + * case date(year, _*) => s"\$year was a good year for PLs." + * } + * }}} + * + * In a pattern match, `Regex` normally matches the entire input. + * However, an unanchored `Regex` finds the pattern anywhere + * in the input. + * + * {{{ + * val embeddedDate = date.unanchored + * "Date: 2004-01-20 17:25:18 GMT (10 years, 28 weeks, 5 days, 17 hours and 51 minutes ago)" match { + * case embeddedDate("2004", "01", "20") => "A Scala is born." + * } + * }}} + * + * === Find Matches === + * To find or replace matches of the pattern, use the various find and replace methods. + * For each method, there is a version for working with matched strings and + * another for working with `Match` objects. + * + * For example, pattern matching with an unanchored `Regex`, as in the previous example, + * can also be accomplished using `findFirstMatchIn`. The `findFirst` methods return an `Option` + * which is non-empty if a match is found, or `None` for no match: + * + * {{{ + * val dates = "Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15" + * val firstDate = date.findFirstIn(dates).getOrElse("No date found.") + * val firstYear = for (m <- date.findFirstMatchIn(dates)) yield m.group(1) + * }}} + * + * To find all matches: + * + * {{{ + * val allYears = for (m <- date.findAllMatchIn(dates)) yield m.group(1) + * }}} + * + * To check whether input is matched by the regex: + * + * {{{ + * date.matches("2018-03-01") // true + * date.matches("Today is 2018-03-01") // false + * date.unanchored.matches("Today is 2018-03-01") // true + * }}} + * + * To iterate over the matched strings, use `findAllIn`, which returns a special iterator + * that can be queried for the `MatchData` of the last match: + * + * {{{ + * val mi = date.findAllIn(dates) + * while (mi.hasNext) { + * val d = mi.next + * if (mi.group(1).toInt < 1960) println(s"\$d: An oldie but goodie.") + * } + * }}} + * + * Although the `MatchIterator` returned by `findAllIn` is used like any `Iterator`, + * with alternating calls to `hasNext` and `next`, `hasNext` has the additional + * side effect of advancing the underlying matcher to the next unconsumed match. + * This effect is visible in the `MatchData` representing the "current match". + * + * {{{ + * val r = "(ab+c)".r + * val s = "xxxabcyyyabbczzz" + * r.findAllIn(s).start // 3 + * val mi = r.findAllIn(s) + * mi.hasNext // true + * mi.start // 3 + * mi.next() // "abc" + * mi.start // 3 + * mi.hasNext // true + * mi.start // 9 + * mi.next() // "abbc" + * }}} + * + * The example shows that methods on `MatchData` such as `start` will advance to + * the first match, if necessary. It also shows that `hasNext` will advance to + * the next unconsumed match, if `next` has already returned the current match. + * + * The current `MatchData` can be captured using the `matchData` method. + * Alternatively, `findAllMatchIn` returns an `Iterator[Match]`, where there + * is no interaction between the iterator and `Match` objects it has already produced. + * + * Note that `findAllIn` finds matches that don't overlap. (See [[findAllIn]] for more examples.) + * + * {{{ + * val num = raw"(\d+)".r + * val all = num.findAllIn("123").toList // List("123"), not List("123", "23", "3") + * }}} + * + * === Replace Text === + * Text replacement can be performed unconditionally or as a function of the current match: + * + * {{{ + * val redacted = date.replaceAllIn(dates, "XXXX-XX-XX") + * val yearsOnly = date.replaceAllIn(dates, m => m.group(1)) + * val months = (0 to 11).map { i => val c = Calendar.getInstance; c.set(2014, i, 1); f"\$c%tb" } + * val reformatted = date.replaceAllIn(dates, _ match { case date(y,m,d) => f"\${months(m.toInt - 1)} \$d, \$y" }) + * }}} + * + * Pattern matching the `Match` against the `Regex` that created it does not reapply the `Regex`. + * In the expression for `reformatted`, each `date` match is computed once. But it is possible to apply a + * `Regex` to a `Match` resulting from a different pattern: + * + * {{{ + * val docSpree = """2011(?:-\d{2}){2}""".r + * val docView = date.replaceAllIn(dates, _ match { + * case docSpree() => "Historic doc spree!" + * case _ => "Something else happened" + * }) + * }}} + * + * @see [[java.util.regex.Pattern]] + * + * @param pattern The compiled pattern + * @param groupNames A mapping from names to indices in capture groups + * + * @define replacementString + * In the replacement String, a dollar sign (`\$`) followed by a number will be + * interpreted as a reference to a group in the matched pattern, with numbers + * 1 through 9 corresponding to the first nine groups, and 0 standing for the + * whole match. Any other character is an error. The backslash (`\`) character + * will be interpreted as an escape character and can be used to escape the + * dollar sign. Use `Regex.quoteReplacement` to escape these characters. + */ +@SerialVersionUID(-2094783597747625537L) +class Regex private[matching](val pattern: Pattern, groupNames: String*) extends Serializable { + outer => + + import Regex._ + + /** Compile a regular expression, supplied as a string, into a pattern that + * can be matched against inputs. + * + * If group names are supplied, they can be used this way: + * + * {{{ + * val namedDate = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * val namedYears = for (m <- namedDate findAllMatchIn dates) yield m group "year" + * }}} + * + * Inline group names are preferred over group names supplied to the constructor + * when retrieving matched groups by name. Group names supplied to the constructor + * should be considered deprecated. + * + * This constructor does not support options as flags, which must be + * supplied as inline flags in the pattern string: `(?idmsuxU)`. + * + * @param regex The regular expression to compile. + * @param groupNames Names of capturing groups. + */ + // we cannot add the alternative `def this(regex: String)` in a forward binary compatible way: + // @deprecated("use inline group names like (?X) instead", "2.13.7") + def this(regex: String, groupNames: String*) = this(Pattern.compile(regex), groupNames: _*) + + /** Tries to match a [[java.lang.CharSequence]]. + * + * If the match succeeds, the result is a list of the matching + * groups (or a `null` element if a group did not match any input). + * If the pattern specifies no groups, then the result will be an empty list + * on a successful match. + * + * This method attempts to match the entire input by default; to find the next + * matching subsequence, use an unanchored `Regex`. + * + * For example: + * + * {{{ + * val p1 = "ab*c".r + * val p1Matches = "abbbc" match { + * case p1() => true // no groups + * case _ => false + * } + * val p2 = "a(b*)c".r + * val p2Matches = "abbbc" match { + * case p2(_*) => true // any groups + * case _ => false + * } + * val numberOfB = "abbbc" match { + * case p2(b) => Some(b.length) // one group + * case _ => None + * } + * val p3 = "b*".r.unanchored + * val p3Matches = "abbbc" match { + * case p3() => true // find the b's + * case _ => false + * } + * val p4 = "a(b*)(c+)".r + * val p4Matches = "abbbcc" match { + * case p4(_*) => true // multiple groups + * case _ => false + * } + * val allGroups = "abbbcc" match { + * case p4(all @ _*) => all mkString "/" // "bbb/cc" + * case _ => "" + * } + * val cGroup = "abbbcc" match { + * case p4(_, c) => c + * case _ => "" + * } + * }}} + * + * @param s The string to match + * @return The matches + */ + def unapplySeq(s: CharSequence): Option[List[String]] = { + val m = pattern.matcher(s) + if (runMatcher(m)) Some(List.tabulate(m.groupCount) { i => m.group(i + 1) }) + else None + } + + /** Tries to match the String representation of a [[scala.Char]]. + * + * If the match succeeds, the result is the first matching + * group if any groups are defined, or an empty Sequence otherwise. + * + * For example: + * + * {{{ + * val cat = "cat" + * // the case must consume the group to match + * val r = """(\p{Lower})""".r + * cat(0) match { case r(x) => true } + * cat(0) match { case r(_) => true } + * cat(0) match { case r(_*) => true } + * cat(0) match { case r() => true } // no match + * + * // there is no group to extract + * val r = """\p{Lower}""".r + * cat(0) match { case r(x) => true } // no match + * cat(0) match { case r(_) => true } // no match + * cat(0) match { case r(_*) => true } // matches + * cat(0) match { case r() => true } // matches + * + * // even if there are multiple groups, only one is returned + * val r = """((.))""".r + * cat(0) match { case r(_) => true } // matches + * cat(0) match { case r(_,_) => true } // no match + * }}} + * + * @param c The Char to match + * @return The match + */ + def unapplySeq(c: Char): Option[List[Char]] = { + val m = pattern matcher c.toString + if (runMatcher(m)) { + if (m.groupCount > 0) Some((m group 1).toList) else Some(Nil) + } else None + } + + /** Tries to match on a [[scala.util.matching.Regex.Match]]. + * + * A previously failed match results in None. + * + * If a successful match was made against the current pattern, then that result is used. + * + * Otherwise, this Regex is applied to the previously matched input, + * and the result of that match is used. + */ + def unapplySeq(m: Match): Option[List[String]] = + if (m.matched == null) None + else if (m.matcher.pattern == this.pattern) Regex.extractGroupsFromMatch(m) + else unapplySeq(m.matched) + + // @see UnanchoredRegex + protected def runMatcher(m: Matcher): Boolean = m.matches() + + /** Return all non-overlapping matches of this `Regex` in the given character + * sequence as a [[scala.util.matching.Regex.MatchIterator]], + * which is a special [[scala.collection.Iterator]] that returns the + * matched strings but can also be queried for more data about the last match, + * such as capturing groups and start position. + * + * A `MatchIterator` can also be converted into an iterator + * that returns objects of type [[scala.util.matching.Regex.Match]], + * such as is normally returned by `findAllMatchIn`. + * + * Where potential matches overlap, the first possible match is returned, + * followed by the next match that follows the input consumed by the + * first match: + * + * {{{ + * val hat = "hat[^a]+".r + * val hathaway = "hathatthattthatttt" + * val hats = hat.findAllIn(hathaway).toList // List(hath, hattth) + * val pos = hat.findAllMatchIn(hathaway).map(_.start).toList // List(0, 7) + * }}} + * + * To return overlapping matches, it is possible to formulate a regular expression + * with lookahead (`?=`) that does not consume the overlapping region. + * + * {{{ + * val madhatter = "(h)(?=(at[^a]+))".r + * val madhats = madhatter.findAllMatchIn(hathaway).map { + * case madhatter(x,y) => s"\$x\$y" + * }.toList // List(hath, hatth, hattth, hatttt) + * }}} + * + * Attempting to retrieve match information after exhausting the iterator + * results in [[java.lang.IllegalStateException]]. + * See [[scala.util.matching.Regex.MatchIterator]] for details. + * + * @param source The text to match against. + * @return A [[scala.util.matching.Regex.MatchIterator]] of matched substrings. + * @example {{{for (words <- """\w+""".r findAllIn "A simple example.") yield words}}} + */ + def findAllIn(source: CharSequence): MatchIterator = new Regex.MatchIterator(source, this, groupNames) + + /** Return all non-overlapping matches of this regexp in given character sequence as a + * [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]]. + * + * @param source The text to match against. + * @return A [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]] for all matches. + * @example {{{for (words <- """\w+""".r findAllMatchIn "A simple example.") yield words.start}}} + */ + def findAllMatchIn(source: CharSequence): Iterator[Match] = { + val matchIterator = findAllIn(source) + new AbstractIterator[Match] { + def hasNext = matchIterator.hasNext + def next(): Match = { + matchIterator.next() + new Match(matchIterator.source, matchIterator.matcher, matchIterator._groupNames).force + } + } + } + + /** Return an optional first matching string of this `Regex` in the given character sequence, + * or None if there is no match. + * + * @param source The text to match against. + * @return An [[scala.Option]] of the first matching string in the text. + * @example {{{"""\w+""".r findFirstIn "A simple example." foreach println // prints "A"}}} + */ + def findFirstIn(source: CharSequence): Option[String] = { + val m = pattern.matcher(source) + if (m.find) Some(m.group) else None + } + + /** Return an optional first match of this `Regex` in the given character sequence, + * or None if it does not exist. + * + * If the match is successful, the [[scala.util.matching.Regex.Match]] can be queried for + * more data. + * + * @param source The text to match against. + * @return A [[scala.Option]] of [[scala.util.matching.Regex.Match]] of the first matching string in the text. + * @example {{{("""[a-z]""".r findFirstMatchIn "A simple example.") map (_.start) // returns Some(2), the index of the first match in the text}}} + */ + def findFirstMatchIn(source: CharSequence): Option[Match] = { + val m = pattern.matcher(source) + if (m.find) Some(new Match(source, m, groupNames)) else None + } + + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix + * of the character sequence. + * + * Unlike `findFirstIn`, this method will only return a match at + * the beginning of the input. + * + * @param source The text to match against. + * @return A [[scala.Option]] of the matched prefix. + * @example {{{"""\p{Lower}""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} + */ + def findPrefixOf(source: CharSequence): Option[String] = { + val m = pattern.matcher(source) + if (m.lookingAt) Some(m.group) else None + } + + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix + * of the character sequence. + * + * Unlike `findFirstMatchIn`, this method will only return a match at + * the beginning of the input. + * + * @param source The text to match against. + * @return A [[scala.Option]] of the [[scala.util.matching.Regex.Match]] of the matched string. + * @example {{{"""\w+""".r findPrefixMatchOf "A simple example." map (_.after) // returns Some(" simple example.")}}} + */ + def findPrefixMatchOf(source: CharSequence): Option[Match] = { + val m = pattern.matcher(source) + if (m.lookingAt) Some(new Match(source, m, groupNames)) else None + } + + /** Returns whether this `Regex` matches the given character sequence. + * + * Like the extractor, this method takes anchoring into account. + * + * @param source The text to match against + * @return true if and only if `source` matches this `Regex`. + * @see [[Regex#unanchored]] + * @example {{{"""\d+""".r matches "123" // returns true}}} + */ + def matches(source: CharSequence): Boolean = + runMatcher(pattern.matcher(source)) + + /** Replaces all matches by a string. + * + * $replacementString + * + * @param target The string to match + * @param replacement The string that will replace each match + * @return The resulting string + * @example {{{"""\d+""".r replaceAllIn ("July 15", "") // returns "July "}}} + */ + def replaceAllIn(target: CharSequence, replacement: String): String = { + val m = pattern.matcher(target) + m.replaceAll(replacement) + } + + /** + * Replaces all matches using a replacer function. The replacer function takes a + * [[scala.util.matching.Regex.Match]] so that extra information can be obtained + * from the match. For example: + * + * {{{ + * import scala.util.matching.Regex + * val datePattern = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * val text = "From 2011-07-15 to 2011-07-17" + * val repl = datePattern replaceAllIn (text, m => s"\${m group "month"}/\${m group "day"}") + * }}} + * + * $replacementString + * + * @param target The string to match. + * @param replacer The function which maps a match to another string. + * @return The target string after replacements. + */ + def replaceAllIn(target: CharSequence, replacer: Match => String): String = { + val it = new Regex.MatchIterator(target, this, groupNames).replacementData + it foreach (md => it replace replacer(md)) + it.replaced + } + + /** + * Replaces some of the matches using a replacer function that returns an [[scala.Option]]. + * The replacer function takes a [[scala.util.matching.Regex.Match]] so that extra + * information can be obtained from the match. For example: + * + * {{{ + * import scala.util.matching.Regex._ + * + * val vars = Map("x" -> "a var", "y" -> """some \$ and \ signs""") + * val text = "A text with variables %x, %y and %z." + * val varPattern = """%(\w+)""".r + * val mapper = (m: Match) => vars get (m group 1) map (quoteReplacement(_)) + * val repl = varPattern replaceSomeIn (text, mapper) + * }}} + * + * $replacementString + * + * @param target The string to match. + * @param replacer The function which optionally maps a match to another string. + * @return The target string after replacements. + */ + def replaceSomeIn(target: CharSequence, replacer: Match => Option[String]): String = { + val it = new Regex.MatchIterator(target, this, groupNames).replacementData + for (matchdata <- it ; replacement <- replacer(matchdata)) + it replace replacement + + it.replaced + } + + /** Replaces the first match by a string. + * + * $replacementString + * + * @param target The string to match + * @param replacement The string that will replace the match + * @return The resulting string + */ + def replaceFirstIn(target: CharSequence, replacement: String): String = { + val m = pattern.matcher(target) + m.replaceFirst(replacement) + } + + /** Splits the provided character sequence around matches of this regexp. + * + * @param toSplit The character sequence to split + * @return The array of strings computed by splitting the + * input around matches of this regexp + */ + def split(toSplit: CharSequence): Array[String] = + pattern.split(toSplit) + + /** Create a new Regex with the same pattern, but no requirement that + * the entire String matches in extractor patterns and [[Regex#matches]]. + * + * Normally, matching on `date` behaves as though the pattern were + * enclosed in anchors, `"^pattern\$"`. + * + * The unanchored `Regex` behaves as though those anchors were removed. + * + * Note that this method does not actually strip any matchers from the pattern. + * + * Calling `anchored` returns the original `Regex`. + * + * {{{ + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r.unanchored + * + * val date(year, month, day) = "Date 2011-07-15" // OK + * + * val copyright: String = "Date of this document: 2011-07-15" match { + * case date(year, month, day) => s"Copyright \$year" // OK + * case _ => "No copyright" + * } + * }}} + * + * @return The new unanchored regex + */ + def unanchored: UnanchoredRegex = new Regex(pattern, groupNames: _*) with UnanchoredRegex { override def anchored = outer } + def anchored: Regex = this + + def regex: String = pattern.pattern + + /** The string defining the regular expression */ + override def toString: String = regex +} + +/** A [[Regex]] that finds the first match when used in a pattern match. + * + * @see [[Regex#unanchored]] + */ +trait UnanchoredRegex extends Regex { + override protected def runMatcher(m: Matcher): Boolean = m.find() + override def unanchored: UnanchoredRegex = this +} + +/** This object defines inner classes that describe + * regex matches and helper objects. + */ +object Regex { + + /** This class provides methods to access + * the details of a match. + */ + trait MatchData { + + /** Basically, wraps a platform Matcher. */ + protected def matcher: Matcher + + /** The source from which the match originated */ + val source: CharSequence + + /** The names of the groups, or an empty sequence if none defined */ + @deprecated("groupNames does not include inline group names, and should not be used anymore", "2.13.7") + val groupNames: Seq[String] + + /** The number of capturing groups in the pattern. + * (For a given successful match, some of those groups may not have matched any input.) + */ + def groupCount: Int + + /** The index of the first matched character, or -1 if nothing was matched */ + def start: Int + + /** The index of the first matched character in group `i`, + * or -1 if nothing was matched for that group. + */ + def start(i: Int): Int + + /** The index following the last matched character, or -1 if nothing was matched. */ + def end: Int + + /** The index following the last matched character in group `i`, + * or -1 if nothing was matched for that group. + */ + def end(i: Int): Int + + /** The matched string, or `null` if nothing was matched. */ + def matched: String = + if (start >= 0) source.subSequence(start, end).toString + else null + + /** The matched string in group `i`, + * or `null` if nothing was matched. + */ + def group(i: Int): String = + if (start(i) >= 0) source.subSequence(start(i), end(i)).toString + else null + + /** All capturing groups, i.e., not including group(0). */ + def subgroups: List[String] = (1 to groupCount).toList map group + + /** The char sequence before first character of match, + * or `null` if nothing was matched. + */ + def before: CharSequence = + if (start >= 0) source.subSequence(0, start) + else null + + /** The char sequence before first character of match in group `i`, + * or `null` if nothing was matched for that group. + */ + def before(i: Int): CharSequence = + if (start(i) >= 0) source.subSequence(0, start(i)) + else null + + /** Returns char sequence after last character of match, + * or `null` if nothing was matched. + */ + def after: CharSequence = + if (end >= 0) source.subSequence(end, source.length) + else null + + /** The char sequence after last character of match in group `i`, + * or `null` if nothing was matched for that group. + */ + def after(i: Int): CharSequence = + if (end(i) >= 0) source.subSequence(end(i), source.length) + else null + + @scala.annotation.nowarn("msg=deprecated") + private def groupNamesNowarn: Seq[String] = groupNames + + private[this] lazy val nameToIndex: Map[String, Int] = + Map[String, Int]() ++ ("" :: groupNamesNowarn.toList).zipWithIndex + + /** Returns the group with the given name. + * + * Uses explicit group names when supplied; otherwise, + * queries the underlying implementation for inline named groups. + * Not all platforms support inline group names. + * + * @param id The group name + * @return The requested group + * @throws IllegalArgumentException if the requested group name is not defined + */ + def group(id: String): String = ( + if (groupNamesNowarn.isEmpty) + matcher group id + else + nameToIndex.get(id) match { + case Some(index) => group(index) + case None => matcher group id + } + ) + + /** The matched string; equivalent to `matched.toString`. */ + override def toString: String = matched + } + + /** Provides information about a successful match. */ + class Match(val source: CharSequence, + protected[matching] val matcher: Matcher, + _groupNames: Seq[String]) extends MatchData { + + @deprecated("groupNames does not include inline group names, and should not be used anymore", "2.13.7") + val groupNames: Seq[String] = _groupNames + + /** The index of the first matched character. */ + val start: Int = matcher.start + + /** The index following the last matched character. */ + val end: Int = matcher.end + + /** The number of subgroups. */ + def groupCount: Int = matcher.groupCount + + private[this] lazy val starts: Array[Int] = + Array.tabulate(groupCount + 1) { matcher.start } + private[this] lazy val ends: Array[Int] = + Array.tabulate(groupCount + 1) { matcher.end } + + /** The index of the first matched character in group `i`. */ + def start(i: Int): Int = starts(i) + + /** The index following the last matched character in group `i`. */ + def end(i: Int): Int = ends(i) + + /** The match itself with matcher-dependent lazy vals forced, + * so that match is valid even once matcher is advanced. + */ + def force: this.type = { starts; ends; this } + } + + /** An extractor object for Matches, yielding the matched string. + * + * This can be used to help writing replacer functions when you + * are not interested in match data. For example: + * + * {{{ + * import scala.util.matching.Regex.Match + * """\w+""".r replaceAllIn ("A simple example.", _ match { case Match(s) => s.toUpperCase }) + * }}} + * + */ + object Match { + def unapply(m: Match): Some[String] = Some(m.matched) + } + + /** An extractor object that yields the groups in the match. Using this extractor + * rather than the original `Regex` ensures that the match is not recomputed. + * + * {{{ + * import scala.util.matching.Regex.Groups + * + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * val text = "The doc spree happened on 2011-07-15." + * val day = date replaceAllIn(text, _ match { case Groups(_, month, day) => s"\$month/\$day" }) + * }}} + */ + object Groups { + def unapplySeq(m: Match): Option[Seq[String]] = { + if (m.groupCount > 0) extractGroupsFromMatch(m) else None + } + } + + @inline private def extractGroupsFromMatch(m: Match): Option[List[String]] = + Some(List.tabulate(m.groupCount) { i => m.group(i + 1) }) + + /** A class to step through a sequence of regex matches. + * + * This is an iterator that returns the matched strings. + * + * Queries about match data pertain to the current state of the underlying + * matcher, which is advanced by calling `hasNext` or `next`. + * + * When matches are exhausted, queries about match data will throw + * [[java.lang.IllegalStateException]]. + * + * @see [[java.util.regex.Matcher]] + */ + class MatchIterator(val source: CharSequence, val regex: Regex, private[Regex] val _groupNames: Seq[String]) + extends AbstractIterator[String] with MatchData { self => + + @deprecated("groupNames does not include inline group names, and should not be used anymore", "2.13.7") + val groupNames: Seq[String] = _groupNames + + protected[Regex] val matcher = regex.pattern.matcher(source) + + // 0 = not yet matched, 1 = matched, 2 = advanced to match, 3 = no more matches + private[this] var nextSeen = 0 + + /** Return true if `next` will find a match. + * As a side effect, advance the underlying matcher if necessary; + * queries about the current match data pertain to the underlying matcher. + */ + def hasNext: Boolean = { + nextSeen match { + case 0 => nextSeen = if (matcher.find()) 1 else 3 + case 1 => () + case 2 => nextSeen = 0 ; hasNext + case 3 => () + } + nextSeen == 1 // otherwise, 3 + } + + /** The next matched substring of `source`. + * As a side effect, advance the underlying matcher if necessary. + */ + def next(): String = { + nextSeen match { + case 0 => if (!hasNext) throw new NoSuchElementException ; next() + case 1 => nextSeen = 2 + case 2 => nextSeen = 0 ; next() + case 3 => throw new NoSuchElementException + } + matcher.group + } + + /** Report emptiness. */ + override def toString: String = super[AbstractIterator].toString + + // ensure we're at a match + private[this] def ensure(): Unit = nextSeen match { + case 0 => if (!hasNext) throw new IllegalStateException + case 1 => () + case 2 => () + case 3 => throw new IllegalStateException + } + + /** The index of the first matched character. */ + def start: Int = { ensure() ; matcher.start } + + /** The index of the first matched character in group `i`. */ + def start(i: Int): Int = { ensure() ; matcher.start(i) } + + /** The index of the last matched character. */ + def end: Int = { ensure() ; matcher.end } + + /** The index following the last matched character in group `i`. */ + def end(i: Int): Int = { ensure() ; matcher.end(i) } + + /** The number of subgroups. */ + def groupCount: Int = { ensure() ; matcher.groupCount } + + /** Convert to an iterator that yields MatchData elements instead of Strings. */ + def matchData: Iterator[Match] = new AbstractIterator[Match] { + def hasNext = self.hasNext + def next() = { self.next(); new Match(source, matcher, _groupNames).force } + } + + /** Convert to an iterator that yields MatchData elements instead of Strings and has replacement support. */ + private[matching] def replacementData = new AbstractIterator[Match] with Replacement { + def matcher = self.matcher + def hasNext = self.hasNext + def next() = { self.next(); new Match(source, matcher, _groupNames).force } + } + } + + /** + * A trait able to build a string with replacements assuming it has a matcher. + * Meant to be mixed in with iterators. + */ + private[matching] trait Replacement { + protected def matcher: Matcher + + private[this] val sb = new java.lang.StringBuffer + + def replaced = { + val newsb = new java.lang.StringBuffer(sb) + matcher.appendTail(newsb) + newsb.toString + } + + def replace(rs: String) = matcher.appendReplacement(sb, rs) + } + + /** Quotes strings to be used literally in regex patterns. + * + * All regex metacharacters in the input match themselves literally in the output. + * + * @example {{{List("US\$", "CAN\$").map(Regex.quote).mkString("|").r}}} + */ + def quote(text: String): String = Pattern quote text + + /** Quotes replacement strings to be used in replacement methods. + * + * Replacement methods give special meaning to backslashes (`\`) and + * dollar signs (`\$`) in replacement strings, so they are not treated + * as literals. This method escapes these characters so the resulting + * string can be used as a literal replacement representing the input + * string. + * + * @param text The string one wishes to use as literal replacement. + * @return A string that can be used to replace matches with `text`. + * @example {{{"CURRENCY".r.replaceAllIn(input, Regex quoteReplacement "US\$")}}} + */ + def quoteReplacement(text: String): String = Matcher quoteReplacement text +} From 2f796346c2225c434dc9267d6713c7363122b086 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 13:45:23 +0100 Subject: [PATCH 23/28] chore: add all the files in scala --- library/src/scala/AnyVal.scala | 58 ++ library/src/scala/AnyValCompanion.scala | 25 + library/src/scala/App.scala | 104 +++ library/src/scala/Array.scala | 692 ++++++++++++++++++ library/src/scala/Boolean.scala | 140 ++++ library/src/scala/Byte.scala | 487 ++++++++++++ library/src/scala/Char.scala | 486 ++++++++++++ library/src/scala/Console.scala | 281 +++++++ library/src/scala/DelayedInit.scala | 52 ++ library/src/scala/Double.scala | 255 +++++++ library/src/scala/DummyImplicit.scala | 21 + library/src/scala/Dynamic.scala | 38 + library/src/scala/Enumeration.scala | 350 +++++++++ library/src/scala/Equals.scala | 35 + library/src/scala/Float.scala | 258 +++++++ library/src/scala/Function.scala | 130 ++++ library/src/scala/Function0.scala | 45 ++ library/src/scala/Function1.scala | 90 +++ library/src/scala/Function10.scala | 43 ++ library/src/scala/Function11.scala | 43 ++ library/src/scala/Function12.scala | 43 ++ library/src/scala/Function13.scala | 43 ++ library/src/scala/Function14.scala | 43 ++ library/src/scala/Function15.scala | 43 ++ library/src/scala/Function16.scala | 43 ++ library/src/scala/Function17.scala | 43 ++ library/src/scala/Function18.scala | 43 ++ library/src/scala/Function19.scala | 43 ++ library/src/scala/Function2.scala | 58 ++ library/src/scala/Function20.scala | 43 ++ library/src/scala/Function21.scala | 43 ++ library/src/scala/Function22.scala | 43 ++ library/src/scala/Function3.scala | 43 ++ library/src/scala/Function4.scala | 43 ++ library/src/scala/Function5.scala | 43 ++ library/src/scala/Function6.scala | 43 ++ library/src/scala/Function7.scala | 43 ++ library/src/scala/Function8.scala | 43 ++ library/src/scala/Function9.scala | 43 ++ library/src/scala/Int.scala | 486 ++++++++++++ library/src/scala/Long.scala | 483 ++++++++++++ library/src/scala/MatchError.scala | 40 + library/src/scala/NotImplementedError.scala | 21 + library/src/scala/Option.scala | 628 ++++++++++++++++ library/src/scala/PartialFunction.scala | 403 ++++++++++ library/src/scala/Predef.scala | 586 +++++++++++++++ library/src/scala/Product.scala | 72 ++ library/src/scala/Product1.scala | 51 ++ library/src/scala/Product10.scala | 96 +++ library/src/scala/Product11.scala | 101 +++ library/src/scala/Product12.scala | 106 +++ library/src/scala/Product13.scala | 111 +++ library/src/scala/Product14.scala | 116 +++ library/src/scala/Product15.scala | 121 +++ library/src/scala/Product16.scala | 126 ++++ library/src/scala/Product17.scala | 131 ++++ library/src/scala/Product18.scala | 136 ++++ library/src/scala/Product19.scala | 141 ++++ library/src/scala/Product2.scala | 56 ++ library/src/scala/Product20.scala | 146 ++++ library/src/scala/Product21.scala | 151 ++++ library/src/scala/Product22.scala | 156 ++++ library/src/scala/Product3.scala | 61 ++ library/src/scala/Product4.scala | 66 ++ library/src/scala/Product5.scala | 71 ++ library/src/scala/Product6.scala | 76 ++ library/src/scala/Product7.scala | 81 ++ library/src/scala/Product8.scala | 86 +++ library/src/scala/Product9.scala | 91 +++ library/src/scala/Proxy.scala | 48 ++ library/src/scala/SerialVersionUID.scala | 27 + library/src/scala/Short.scala | 486 ++++++++++++ library/src/scala/Specializable.scala | 38 + library/src/scala/StringContext.scala | 476 ++++++++++++ library/src/scala/Symbol.scala | 84 +++ library/src/scala/Tuple1.scala | 28 + library/src/scala/Tuple10.scala | 37 + library/src/scala/Tuple11.scala | 38 + library/src/scala/Tuple12.scala | 40 + library/src/scala/Tuple13.scala | 41 ++ library/src/scala/Tuple14.scala | 42 ++ library/src/scala/Tuple15.scala | 43 ++ library/src/scala/Tuple16.scala | 44 ++ library/src/scala/Tuple17.scala | 45 ++ library/src/scala/Tuple18.scala | 46 ++ library/src/scala/Tuple19.scala | 47 ++ library/src/scala/Tuple2.scala | 35 + library/src/scala/Tuple20.scala | 48 ++ library/src/scala/Tuple21.scala | 49 ++ library/src/scala/Tuple22.scala | 50 ++ library/src/scala/Tuple3.scala | 30 + library/src/scala/Tuple4.scala | 31 + library/src/scala/Tuple5.scala | 32 + library/src/scala/Tuple6.scala | 33 + library/src/scala/Tuple7.scala | 34 + library/src/scala/Tuple8.scala | 35 + library/src/scala/Tuple9.scala | 36 + library/src/scala/UninitializedError.scala | 19 + .../src/scala/UninitializedFieldError.scala | 23 + library/src/scala/Unit.scala | 59 ++ library/src/scala/ValueOf.scala | 55 ++ library/src/scala/deprecated.scala | 64 ++ library/src/scala/deprecatedInheritance.scala | 50 ++ library/src/scala/deprecatedName.scala | 53 ++ library/src/scala/deprecatedOverriding.scala | 52 ++ library/src/scala/inline.scala | 52 ++ library/src/scala/language.scala | 214 ++++++ library/src/scala/languageFeature.scala | 51 ++ library/src/scala/native.scala | 27 + library/src/scala/noinline.scala | 48 ++ library/src/scala/package.scala | 147 ++++ library/src/scala/specialized.scala | 34 + library/src/scala/throws.scala | 28 + library/src/scala/transient.scala | 18 + library/src/scala/typeConstraints.scala | 243 ++++++ library/src/scala/unchecked.scala | 38 + library/src/scala/volatile.scala | 18 + 117 files changed, 12777 insertions(+) create mode 100644 library/src/scala/AnyVal.scala create mode 100644 library/src/scala/AnyValCompanion.scala create mode 100644 library/src/scala/App.scala create mode 100644 library/src/scala/Array.scala create mode 100644 library/src/scala/Boolean.scala create mode 100644 library/src/scala/Byte.scala create mode 100644 library/src/scala/Char.scala create mode 100644 library/src/scala/Console.scala create mode 100644 library/src/scala/DelayedInit.scala create mode 100644 library/src/scala/Double.scala create mode 100644 library/src/scala/DummyImplicit.scala create mode 100644 library/src/scala/Dynamic.scala create mode 100644 library/src/scala/Enumeration.scala create mode 100644 library/src/scala/Equals.scala create mode 100644 library/src/scala/Float.scala create mode 100644 library/src/scala/Function.scala create mode 100644 library/src/scala/Function0.scala create mode 100644 library/src/scala/Function1.scala create mode 100644 library/src/scala/Function10.scala create mode 100644 library/src/scala/Function11.scala create mode 100644 library/src/scala/Function12.scala create mode 100644 library/src/scala/Function13.scala create mode 100644 library/src/scala/Function14.scala create mode 100644 library/src/scala/Function15.scala create mode 100644 library/src/scala/Function16.scala create mode 100644 library/src/scala/Function17.scala create mode 100644 library/src/scala/Function18.scala create mode 100644 library/src/scala/Function19.scala create mode 100644 library/src/scala/Function2.scala create mode 100644 library/src/scala/Function20.scala create mode 100644 library/src/scala/Function21.scala create mode 100644 library/src/scala/Function22.scala create mode 100644 library/src/scala/Function3.scala create mode 100644 library/src/scala/Function4.scala create mode 100644 library/src/scala/Function5.scala create mode 100644 library/src/scala/Function6.scala create mode 100644 library/src/scala/Function7.scala create mode 100644 library/src/scala/Function8.scala create mode 100644 library/src/scala/Function9.scala create mode 100644 library/src/scala/Int.scala create mode 100644 library/src/scala/Long.scala create mode 100644 library/src/scala/MatchError.scala create mode 100644 library/src/scala/NotImplementedError.scala create mode 100644 library/src/scala/Option.scala create mode 100644 library/src/scala/PartialFunction.scala create mode 100644 library/src/scala/Predef.scala create mode 100644 library/src/scala/Product.scala create mode 100644 library/src/scala/Product1.scala create mode 100644 library/src/scala/Product10.scala create mode 100644 library/src/scala/Product11.scala create mode 100644 library/src/scala/Product12.scala create mode 100644 library/src/scala/Product13.scala create mode 100644 library/src/scala/Product14.scala create mode 100644 library/src/scala/Product15.scala create mode 100644 library/src/scala/Product16.scala create mode 100644 library/src/scala/Product17.scala create mode 100644 library/src/scala/Product18.scala create mode 100644 library/src/scala/Product19.scala create mode 100644 library/src/scala/Product2.scala create mode 100644 library/src/scala/Product20.scala create mode 100644 library/src/scala/Product21.scala create mode 100644 library/src/scala/Product22.scala create mode 100644 library/src/scala/Product3.scala create mode 100644 library/src/scala/Product4.scala create mode 100644 library/src/scala/Product5.scala create mode 100644 library/src/scala/Product6.scala create mode 100644 library/src/scala/Product7.scala create mode 100644 library/src/scala/Product8.scala create mode 100644 library/src/scala/Product9.scala create mode 100644 library/src/scala/Proxy.scala create mode 100644 library/src/scala/SerialVersionUID.scala create mode 100644 library/src/scala/Short.scala create mode 100644 library/src/scala/Specializable.scala create mode 100644 library/src/scala/StringContext.scala create mode 100644 library/src/scala/Symbol.scala create mode 100644 library/src/scala/Tuple1.scala create mode 100644 library/src/scala/Tuple10.scala create mode 100644 library/src/scala/Tuple11.scala create mode 100644 library/src/scala/Tuple12.scala create mode 100644 library/src/scala/Tuple13.scala create mode 100644 library/src/scala/Tuple14.scala create mode 100644 library/src/scala/Tuple15.scala create mode 100644 library/src/scala/Tuple16.scala create mode 100644 library/src/scala/Tuple17.scala create mode 100644 library/src/scala/Tuple18.scala create mode 100644 library/src/scala/Tuple19.scala create mode 100644 library/src/scala/Tuple2.scala create mode 100644 library/src/scala/Tuple20.scala create mode 100644 library/src/scala/Tuple21.scala create mode 100644 library/src/scala/Tuple22.scala create mode 100644 library/src/scala/Tuple3.scala create mode 100644 library/src/scala/Tuple4.scala create mode 100644 library/src/scala/Tuple5.scala create mode 100644 library/src/scala/Tuple6.scala create mode 100644 library/src/scala/Tuple7.scala create mode 100644 library/src/scala/Tuple8.scala create mode 100644 library/src/scala/Tuple9.scala create mode 100644 library/src/scala/UninitializedError.scala create mode 100644 library/src/scala/UninitializedFieldError.scala create mode 100644 library/src/scala/Unit.scala create mode 100644 library/src/scala/ValueOf.scala create mode 100644 library/src/scala/deprecated.scala create mode 100644 library/src/scala/deprecatedInheritance.scala create mode 100644 library/src/scala/deprecatedName.scala create mode 100644 library/src/scala/deprecatedOverriding.scala create mode 100644 library/src/scala/inline.scala create mode 100644 library/src/scala/language.scala create mode 100644 library/src/scala/languageFeature.scala create mode 100644 library/src/scala/native.scala create mode 100644 library/src/scala/noinline.scala create mode 100644 library/src/scala/package.scala create mode 100644 library/src/scala/specialized.scala create mode 100644 library/src/scala/throws.scala create mode 100644 library/src/scala/transient.scala create mode 100644 library/src/scala/typeConstraints.scala create mode 100644 library/src/scala/unchecked.scala create mode 100644 library/src/scala/volatile.scala diff --git a/library/src/scala/AnyVal.scala b/library/src/scala/AnyVal.scala new file mode 100644 index 000000000000..79be48d642be --- /dev/null +++ b/library/src/scala/AnyVal.scala @@ -0,0 +1,58 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** `AnyVal` is the root class of all ''value types'', which describe values + * not implemented as objects in the underlying host system. Value classes + * are specified in Scala Language Specification, section 12.2. + * + * The standard implementation includes nine `AnyVal` subtypes: + * + * [[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], + * [[scala.Short]], and [[scala.Byte]] are the ''numeric value types''. + * + * [[scala.Unit]] and [[scala.Boolean]] are the ''non-numeric value types''. + * + * Other groupings: + * + * - The ''subrange types'' are [[scala.Byte]], [[scala.Short]], and [[scala.Char]]. + * - The ''integer types'' include the subrange types as well as [[scala.Int]] and [[scala.Long]]. + * - The ''floating point types'' are [[scala.Float]] and [[scala.Double]]. + * + * A subclass of `AnyVal` is called a ''user-defined value class'' + * and is treated specially by the compiler. Properly-defined user value classes provide a way + * to improve performance on user-defined types by avoiding object allocation at runtime, and by + * replacing virtual method invocations with static method invocations. + * + * User-defined value classes which avoid object allocation... + * + * - must have a single `val` parameter that is the underlying runtime representation. + * - can define `def`s, but no `val`s, `var`s, or nested `trait`s, `class`es or `object`s. + * - typically extend no other trait apart from `AnyVal`. + * - cannot be used in type tests or pattern matching. + * - may not override `equals` or `hashCode` methods. + * + * A minimal example: + * {{{ + * class Wrapper(val underlying: Int) extends AnyVal { + * def foo: Wrapper = new Wrapper(underlying * 19) + * } + * }}} + * + * It's important to note that user-defined value classes are limited, and in some circumstances, + * still must allocate a value class instance at runtime. These limitations and circumstances are + * explained in greater detail in the [[https://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]]. + */ +abstract class AnyVal extends Any { + def getClass(): Class[_ <: AnyVal] = null +} diff --git a/library/src/scala/AnyValCompanion.scala b/library/src/scala/AnyValCompanion.scala new file mode 100644 index 000000000000..2bb8584f8bac --- /dev/null +++ b/library/src/scala/AnyValCompanion.scala @@ -0,0 +1,25 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** A common supertype for companion classes of primitive types. + * + * A common trait for /companion/ objects of primitive types comes handy + * when parameterizing code on types. For instance, the specialized + * annotation is passed a sequence of types on which to specialize: + * {{{ + * class Tuple1[@specialized(Unit, Int, Double) T] + * }}} + * + */ +private[scala] trait AnyValCompanion extends Specializable { } diff --git a/library/src/scala/App.scala b/library/src/scala/App.scala new file mode 100644 index 000000000000..896cf8d9b22e --- /dev/null +++ b/library/src/scala/App.scala @@ -0,0 +1,104 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import java.lang.System.{currentTimeMillis => currentTime} + +import scala.annotation.nowarn +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * + * No explicit `main` method is needed. Instead, + * the whole class body becomes the “main method”. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * In Scala 3, the `DelayedInit` feature was dropped. `App` exists only in a limited form + * that also does not support command line arguments and will be deprecated in the future. + * + * [[https://docs.scala-lang.org/scala3/book/methods-main-methods.html @main]] methods are the + * recommended scheme to generate programs that can be invoked from the command line in Scala 3. + * + * {{{ + * @main def runMyProgram(args: String*): Unit = { + * // your program here + * } + * }}} + * + * If programs need to cross-build between Scala 2 and Scala 3, it is recommended to use an + * explicit `main` method: + * {{{ + * object Main { + * def main(args: Array[String]): Unit = { + * // your program here + * } + * } + * }}} + */ +@nowarn("""cat=deprecation&origin=scala\.DelayedInit""") +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + final val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + protected final def args: Array[String] = _args + + private[this] var _args: Array[String] = _ + + private[this] val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("the delayedInit mechanism will disappear", "2.11.0") + override def delayedInit(body: => Unit): Unit = { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + final def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } +} diff --git a/library/src/scala/Array.scala b/library/src/scala/Array.scala new file mode 100644 index 000000000000..cf8e53fe6c60 --- /dev/null +++ b/library/src/scala/Array.scala @@ -0,0 +1,692 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +//import scala.collection.generic._ +import scala.collection.{Factory, immutable, mutable} +import mutable.ArrayBuilder +import immutable.ArraySeq +import scala.language.implicitConversions +import scala.reflect.{ClassTag, classTag} +import scala.runtime.BoxedUnit +import scala.runtime.ScalaRunTime +import scala.runtime.ScalaRunTime.{array_apply, array_update} + +/** Utility methods for operating on arrays. + * For example: + * {{{ + * val a = Array(1, 2) + * val b = Array.ofDim[Int](2) + * val c = Array.concat(a, b) + * }}} + * where the array objects `a`, `b` and `c` have respectively the values + * `Array(1, 2)`, `Array(0, 0)` and `Array(1, 2, 0, 0)`. + */ +object Array { + val emptyBooleanArray = new Array[Boolean](0) + val emptyByteArray = new Array[Byte](0) + val emptyCharArray = new Array[Char](0) + val emptyDoubleArray = new Array[Double](0) + val emptyFloatArray = new Array[Float](0) + val emptyIntArray = new Array[Int](0) + val emptyLongArray = new Array[Long](0) + val emptyShortArray = new Array[Short](0) + val emptyObjectArray = new Array[Object](0) + + /** Provides an implicit conversion from the Array object to a collection Factory */ + implicit def toFactory[A : ClassTag](dummy: Array.type): Factory[A, Array[A]] = new ArrayFactory(dummy) + @SerialVersionUID(3L) + private class ArrayFactory[A : ClassTag](dummy: Array.type) extends Factory[A, Array[A]] with Serializable { + def fromSpecific(it: IterableOnce[A]): Array[A] = Array.from[A](it) + def newBuilder: mutable.Builder[A, Array[A]] = Array.newBuilder[A] + } + + /** + * Returns a new [[scala.collection.mutable.ArrayBuilder]]. + */ + def newBuilder[T](implicit t: ClassTag[T]): ArrayBuilder[T] = ArrayBuilder.make[T](using t) + + /** Build an array from the iterable collection. + * + * {{{ + * scala> val a = Array.from(Seq(1, 5)) + * val a: Array[Int] = Array(1, 5) + * + * scala> val b = Array.from(Range(1, 5)) + * val b: Array[Int] = Array(1, 2, 3, 4) + * }}} + * + * @param it the iterable collection + * @return an array consisting of elements of the iterable collection + */ + def from[A : ClassTag](it: IterableOnce[A]): Array[A] = it match { + case it: Iterable[A] => it.toArray[A] + case _ => it.iterator.toArray[A] + } + + private def slowcopy(src : AnyRef, + srcPos : Int, + dest : AnyRef, + destPos : Int, + length : Int): Unit = { + var i = srcPos + var j = destPos + val srcUntil = srcPos + length + while (i < srcUntil) { + array_update(dest, j, array_apply(src, i)) + i += 1 + j += 1 + } + } + + /** Copy one array to another. + * Equivalent to Java's + * `System.arraycopy(src, srcPos, dest, destPos, length)`, + * except that this also works for polymorphic and boxed arrays. + * + * Note that the passed-in `dest` array will be modified by this call. + * + * @param src the source array. + * @param srcPos starting position in the source array. + * @param dest destination array. + * @param destPos starting position in the destination array. + * @param length the number of array elements to be copied. + * + * @see `java.lang.System#arraycopy` + */ + def copy(src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int): Unit = { + val srcClass = src.getClass + val destClass = dest.getClass + if (srcClass.isArray && ((destClass eq srcClass) || + (destClass.isArray && !srcClass.getComponentType.isPrimitive && !destClass.getComponentType.isPrimitive))) + java.lang.System.arraycopy(src, srcPos, dest, destPos, length) + else + slowcopy(src, srcPos, dest, destPos, length) + } + + /** Copy one array to another, truncating or padding with default values (if + * necessary) so the copy has the specified length. + * + * Equivalent to Java's + * `java.util.Arrays.copyOf(original, newLength)`, + * except that this works for primitive and object arrays in a single method. + * + * @see `java.util.Arrays#copyOf` + */ + def copyOf[A](original: Array[A], newLength: Int): Array[A] = ((original: @unchecked) match { + case x: Array[BoxedUnit] => newUnitArray(newLength).asInstanceOf[Array[A]] + case x: Array[AnyRef] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Int] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Double] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Long] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Float] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Char] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Byte] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Short] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Boolean] => java.util.Arrays.copyOf(x, newLength) + }).asInstanceOf[Array[A]] + + /** Copy one array to another, truncating or padding with default values (if + * necessary) so the copy has the specified length. The new array can have + * a different type than the original one as long as the values are + * assignment-compatible. When copying between primitive and object arrays, + * boxing and unboxing are supported. + * + * Equivalent to Java's + * `java.util.Arrays.copyOf(original, newLength, newType)`, + * except that this works for all combinations of primitive and object arrays + * in a single method. + * + * @see `java.util.Arrays#copyOf` + */ + def copyAs[A](original: Array[_], newLength: Int)(implicit ct: ClassTag[A]): Array[A] = { + val runtimeClass = ct.runtimeClass + if (runtimeClass == Void.TYPE) newUnitArray(newLength).asInstanceOf[Array[A]] + else { + val destClass = runtimeClass.asInstanceOf[Class[A]] + if (destClass.isAssignableFrom(original.getClass.getComponentType)) { + if (destClass.isPrimitive) copyOf[A](original.asInstanceOf[Array[A]], newLength) + else { + val destArrayClass = java.lang.reflect.Array.newInstance(destClass, 0).getClass.asInstanceOf[Class[Array[AnyRef]]] + java.util.Arrays.copyOf(original.asInstanceOf[Array[AnyRef]], newLength, destArrayClass).asInstanceOf[Array[A]] + } + } else { + val dest = new Array[A](newLength) + Array.copy(original, 0, dest, 0, original.length) + dest + } + } + } + + private def newUnitArray(len: Int): Array[Unit] = { + val result = new Array[Unit](len) + java.util.Arrays.fill(result.asInstanceOf[Array[AnyRef]], ()) + result + } + + /** Returns an array of length 0 */ + def empty[T: ClassTag]: Array[T] = new Array[T](0) + + /** Creates an array with given elements. + * + * @param xs the elements to put in the array + * @return an array containing all elements from xs. + */ + // Subject to a compiler optimization in Cleanup. + // Array(e0, ..., en) is translated to { val a = new Array(3); a(i) = ei; a } + def apply[T: ClassTag](xs: T*): Array[T] = { + val len = xs.length + xs match { + case wa: immutable.ArraySeq[_] if wa.unsafeArray.getClass.getComponentType == classTag[T].runtimeClass => + // We get here in test/files/run/sd760a.scala, `Array[T](t)` for + // a specialized type parameter `T`. While we still pay for two + // copies of the array it is better than before when we also boxed + // each element when populating the result. + ScalaRunTime.array_clone(wa.unsafeArray).asInstanceOf[Array[T]] + case _ => + val array = new Array[T](len) + val iterator = xs.iterator + var i = 0 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + } + + /** Creates an array of `Boolean` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Boolean, xs: Boolean*): Array[Boolean] = { + val array = new Array[Boolean](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Byte` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Byte, xs: Byte*): Array[Byte] = { + val array = new Array[Byte](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Short` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Short, xs: Short*): Array[Short] = { + val array = new Array[Short](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Char` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Char, xs: Char*): Array[Char] = { + val array = new Array[Char](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Int` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Int, xs: Int*): Array[Int] = { + val array = new Array[Int](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Long` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Long, xs: Long*): Array[Long] = { + val array = new Array[Long](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Float` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Float, xs: Float*): Array[Float] = { + val array = new Array[Float](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Double` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Double, xs: Double*): Array[Double] = { + val array = new Array[Double](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Unit` objects */ + def apply(x: Unit, xs: Unit*): Array[Unit] = { + val array = new Array[Unit](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates array with given dimensions */ + def ofDim[T: ClassTag](n1: Int): Array[T] = + new Array[T](n1) + /** Creates a 2-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int): Array[Array[T]] = { + val arr: Array[Array[T]] = (new Array[Array[T]](n1): Array[Array[T]]) + for (i <- 0 until n1) arr(i) = new Array[T](n2) + arr + // tabulate(n1)(_ => ofDim[T](n2)) + } + /** Creates a 3-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int, n3: Int): Array[Array[Array[T]]] = + tabulate(n1)(_ => ofDim[T](n2, n3)) + /** Creates a 4-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int): Array[Array[Array[Array[T]]]] = + tabulate(n1)(_ => ofDim[T](n2, n3, n4)) + /** Creates a 5-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): Array[Array[Array[Array[Array[T]]]]] = + tabulate(n1)(_ => ofDim[T](n2, n3, n4, n5)) + + /** Concatenates all arrays into a single array. + * + * @param xss the given arrays + * @return the array created from concatenating `xss` + */ + def concat[T: ClassTag](xss: Array[T]*): Array[T] = { + val b = newBuilder[T] + b.sizeHint(xss.map(_.length).sum) + for (xs <- xss) b ++= xs + b.result() + } + + /** Returns an array that contains the results of some element computation a number + * of times. + * + * Note that this means that `elem` is computed a total of n times: + * {{{ + * scala> Array.fill(3){ math.random } + * res3: Array[Double] = Array(0.365461167592537, 1.550395944913685E-4, 0.7907242137333306) + * }}} + * + * @param n the number of elements desired + * @param elem the element computation + * @return an Array of size n, where each element contains the result of computing + * `elem`. + */ + def fill[T: ClassTag](n: Int)(elem: => T): Array[T] = { + if (n <= 0) { + empty[T] + } else { + val array = new Array[T](n) + var i = 0 + while (i < n) { + array(i) = elem + i += 1 + } + array + } + } + + /** Returns a two-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): Array[Array[T]] = + tabulate(n1)(_ => fill(n2)(elem)) + + /** Returns a three-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int)(elem: => T): Array[Array[Array[T]]] = + tabulate(n1)(_ => fill(n2, n3)(elem)) + + /** Returns a four-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => T): Array[Array[Array[Array[T]]]] = + tabulate(n1)(_ => fill(n2, n3, n4)(elem)) + + /** Returns a five-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => T): Array[Array[Array[Array[Array[T]]]]] = + tabulate(n1)(_ => fill(n2, n3, n4, n5)(elem)) + + /** Returns an array containing values of a given function over a range of integer + * values starting from 0. + * + * @param n The number of elements in the array + * @param f The function computing element values + * @return An `Array` consisting of elements `f(0),f(1), ..., f(n - 1)` + */ + def tabulate[T: ClassTag](n: Int)(f: Int => T): Array[T] = { + if (n <= 0) { + empty[T] + } else { + val array = new Array[T](n) + var i = 0 + while (i < n) { + array(i) = f(i) + i += 1 + } + array + } + } + + /** Returns a two-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): Array[Array[T]] = + tabulate(n1)(i1 => tabulate(n2)(f(i1, _))) + + /** Returns a three-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => T): Array[Array[Array[T]]] = + tabulate(n1)(i1 => tabulate(n2, n3)(f(i1, _, _))) + + /** Returns a four-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => T): Array[Array[Array[Array[T]]]] = + tabulate(n1)(i1 => tabulate(n2, n3, n4)(f(i1, _, _, _))) + + /** Returns a five-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => T): Array[Array[Array[Array[Array[T]]]]] = + tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _))) + + /** Returns an array containing a sequence of increasing integers in a range. + * + * @param start the start value of the array + * @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned) + * @return the array with values in range `start, start + 1, ..., end - 1` + * up to, but excluding, `end`. + */ + def range(start: Int, end: Int): Array[Int] = range(start, end, 1) + + /** Returns an array containing equally spaced values in some integer interval. + * + * @param start the start value of the array + * @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned) + * @param step the increment value of the array (may not be zero) + * @return the array with values in `start, start + step, ...` up to, but excluding `end` + */ + def range(start: Int, end: Int, step: Int): Array[Int] = { + if (step == 0) throw new IllegalArgumentException("zero step") + val array = new Array[Int](immutable.Range.count(start, end, step, isInclusive = false)) + + var n = 0 + var i = start + while (if (step < 0) end < i else i < end) { + array(n) = i + i += step + n += 1 + } + array + } + + /** Returns an array containing repeated applications of a function to a start value. + * + * @param start the start value of the array + * @param len the number of elements returned by the array + * @param f the function that is repeatedly applied + * @return the array returning `len` values in the sequence `start, f(start), f(f(start)), ...` + */ + def iterate[T: ClassTag](start: T, len: Int)(f: T => T): Array[T] = { + if (len > 0) { + val array = new Array[T](len) + var acc = start + var i = 1 + array(0) = acc + + while (i < len) { + acc = f(acc) + array(i) = acc + i += 1 + } + array + } else { + empty[T] + } + } + + /** Compare two arrays per element. + * + * A more efficient version of `xs.sameElements(ys)`. + * + * Note that arrays are invariant in Scala, but it may + * be sound to cast an array of arbitrary reference type + * to `Array[AnyRef]`. Arrays on the JVM are covariant + * in their element type. + * + * `Array.equals(xs.asInstanceOf[Array[AnyRef]], ys.asInstanceOf[Array[AnyRef]])` + * + * @param xs an array of AnyRef + * @param ys an array of AnyRef + * @return true if corresponding elements are equal + */ + def equals(xs: Array[AnyRef], ys: Array[AnyRef]): Boolean = + (xs eq ys) || + (xs.length == ys.length) && { + var i = 0 + while (i < xs.length && xs(i) == ys(i)) i += 1 + i >= xs.length + } + + /** Called in a pattern match like `{ case Array(x,y,z) => println('3 elements')}`. + * + * @param x the selector value + * @return sequence wrapped in a [[scala.Some]], if `x` is an Array, otherwise `None` + */ + def unapplySeq[T](x: Array[T]): UnapplySeqWrapper[T] = new UnapplySeqWrapper(x) + + final class UnapplySeqWrapper[T](private val a: Array[T]) extends AnyVal { + def isEmpty: false = false + def get: UnapplySeqWrapper[T] = this + def lengthCompare(len: Int): Int = a.lengthCompare(len) + def apply(i: Int): T = a(i) + def drop(n: Int): scala.Seq[T] = ArraySeq.unsafeWrapArray(a.drop(n)) // clones the array, also if n == 0 + def toSeq: scala.Seq[T] = a.toSeq // clones the array + } +} + +/** Arrays are mutable, indexed collections of values. `Array[T]` is Scala's representation + * for Java's `T[]`. + * + * {{{ + * val numbers = Array(1, 2, 3, 4) + * val first = numbers(0) // read the first element + * numbers(3) = 100 // replace the 4th array element with 100 + * val biggerNumbers = numbers.map(_ * 2) // multiply all numbers by two + * }}} + * + * Arrays make use of two common pieces of Scala syntactic sugar, shown on lines 2 and 3 of the above + * example code. + * Line 2 is translated into a call to `apply(Int)`, while line 3 is translated into a call to + * `update(Int, T)`. + * + * Two implicit conversions exist in [[scala.Predef]] that are frequently applied to arrays: a conversion + * to [[scala.collection.ArrayOps]] (shown on line 4 of the example above) and a conversion + * to [[scala.collection.mutable.ArraySeq]] (a subtype of [[scala.collection.Seq]]). + * Both types make available many of the standard operations found in the Scala collections API. + * The conversion to `ArrayOps` is temporary, as all operations defined on `ArrayOps` return an `Array`, + * while the conversion to `ArraySeq` is permanent as all operations return a `ArraySeq`. + * + * The conversion to `ArrayOps` takes priority over the conversion to `ArraySeq`. For instance, + * consider the following code: + * + * {{{ + * val arr = Array(1, 2, 3) + * val arrReversed = arr.reverse + * val seqReversed : collection.Seq[Int] = arr.reverse + * }}} + * + * Value `arrReversed` will be of type `Array[Int]`, with an implicit conversion to `ArrayOps` occurring + * to perform the `reverse` operation. The value of `seqReversed`, on the other hand, will be computed + * by converting to `ArraySeq` first and invoking the variant of `reverse` that returns another + * `ArraySeq`. + * + * @see [[https://www.scala-lang.org/files/archive/spec/2.13/ Scala Language Specification]], for in-depth information on the transformations the Scala compiler makes on Arrays (Sections 6.6 and 6.15 respectively.) + * @see [[https://docs.scala-lang.org/sips/scala-2-8-arrays.html "Scala 2.8 Arrays"]] the Scala Improvement Document detailing arrays since Scala 2.8. + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/arrays.html "The Scala 2.8 Collections' API"]] section on `Array` by Martin Odersky for more information. + * @hideImplicitConversion scala.Predef.booleanArrayOps + * @hideImplicitConversion scala.Predef.byteArrayOps + * @hideImplicitConversion scala.Predef.charArrayOps + * @hideImplicitConversion scala.Predef.doubleArrayOps + * @hideImplicitConversion scala.Predef.floatArrayOps + * @hideImplicitConversion scala.Predef.intArrayOps + * @hideImplicitConversion scala.Predef.longArrayOps + * @hideImplicitConversion scala.Predef.refArrayOps + * @hideImplicitConversion scala.Predef.shortArrayOps + * @hideImplicitConversion scala.Predef.unitArrayOps + * @hideImplicitConversion scala.LowPriorityImplicits.wrapRefArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapIntArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapDoubleArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapLongArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapFloatArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapCharArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapByteArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapShortArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapBooleanArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapUnitArray + * @hideImplicitConversion scala.LowPriorityImplicits.genericWrapArray + * @define coll array + * @define Coll `Array` + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define collectExample + * @define undefinedorder + */ +final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable { + + /** The length of the array */ + def length: Int = throw new Error() + + /** The element at given index. + * + * Indices start at `0`; `xs.apply(0)` is the first element of array `xs`. + * Note the indexing syntax `xs(i)` is a shorthand for `xs.apply(i)`. + * + * @param i the index + * @return the element at the given index + * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + */ + def apply(i: Int): T = throw new Error() + + /** Update the element at given index. + * + * Indices start at `0`; `xs.update(i, x)` replaces the i^th^ element in the array. + * Note the syntax `xs(i) = x` is a shorthand for `xs.update(i, x)`. + * + * @param i the index + * @param x the value to be written at index `i` + * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + */ + def update(i: Int, x: T): Unit = { throw new Error() } + + /** Clone the Array. + * + * @return A clone of the Array. + */ + override def clone(): Array[T] = throw new Error() +} diff --git a/library/src/scala/Boolean.scala b/library/src/scala/Boolean.scala new file mode 100644 index 000000000000..ea8a2e37cda9 --- /dev/null +++ b/library/src/scala/Boolean.scala @@ -0,0 +1,140 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Boolean` (equivalent to Java's `boolean` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Boolean` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Boolean]] => [[scala.runtime.RichBoolean]] + * which provides useful non-primitive operations. + */ +final abstract class Boolean private extends AnyVal { + /** Negates a Boolean expression. + * + * - `!a` results in `false` if and only if `a` evaluates to `true` and + * - `!a` results in `true` if and only if `a` evaluates to `false`. + * + * @return the negated expression + */ + def unary_! : Boolean + + /** Compares two Boolean expressions and returns `true` if they evaluate to the same value. + * + * `a == b` returns `true` if and only if + * - `a` and `b` are `true` or + * - `a` and `b` are `false`. + */ + def ==(x: Boolean): Boolean + + /** + * Compares two Boolean expressions and returns `true` if they evaluate to a different value. + * + * `a != b` returns `true` if and only if + * - `a` is `true` and `b` is `false` or + * - `a` is `false` and `b` is `true`. + */ + def !=(x: Boolean): Boolean + + /** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. + * + * `a || b` returns `true` if and only if + * - `a` is `true` or + * - `b` is `true` or + * - `a` and `b` are `true`. + * + * @note This method uses 'short-circuit' evaluation and + * behaves as if it was declared as `def ||(x: => Boolean): Boolean`. + * If `a` evaluates to `true`, `true` is returned without evaluating `b`. + */ + def ||(x: Boolean): Boolean + + /** Compares two Boolean expressions and returns `true` if both of them evaluate to true. + * + * `a && b` returns `true` if and only if + * - `a` and `b` are `true`. + * + * @note This method uses 'short-circuit' evaluation and + * behaves as if it was declared as `def &&(x: => Boolean): Boolean`. + * If `a` evaluates to `false`, `false` is returned without evaluating `b`. + */ + def &&(x: Boolean): Boolean + + // Compiler won't build with these seemingly more accurate signatures + // def ||(x: => Boolean): Boolean + // def &&(x: => Boolean): Boolean + + /** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. + * + * `a | b` returns `true` if and only if + * - `a` is `true` or + * - `b` is `true` or + * - `a` and `b` are `true`. + * + * @note This method evaluates both `a` and `b`, even if the result is already determined after evaluating `a`. + */ + def |(x: Boolean): Boolean + + /** Compares two Boolean expressions and returns `true` if both of them evaluate to true. + * + * `a & b` returns `true` if and only if + * - `a` and `b` are `true`. + * + * @note This method evaluates both `a` and `b`, even if the result is already determined after evaluating `a`. + */ + def &(x: Boolean): Boolean + + /** Compares two Boolean expressions and returns `true` if they evaluate to a different value. + * + * `a ^ b` returns `true` if and only if + * - `a` is `true` and `b` is `false` or + * - `a` is `false` and `b` is `true`. + */ + def ^(x: Boolean): Boolean + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Boolean] = ??? +} + +object Boolean extends AnyValCompanion { + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToBoolean`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Boolean to be boxed + * @return a java.lang.Boolean offering `x` as its underlying value. + */ + def box(x: Boolean): java.lang.Boolean = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Boolean. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToBoolean`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Boolean to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Boolean + * @return the Boolean resulting from calling booleanValue() on `x` + */ + def unbox(x: java.lang.Object): Boolean = ??? + + /** The String representation of the scala.Boolean companion object. */ + override def toString = "object scala.Boolean" + +} + diff --git a/library/src/scala/Byte.scala b/library/src/scala/Byte.scala new file mode 100644 index 000000000000..1f32d4d0bca1 --- /dev/null +++ b/library/src/scala/Byte.scala @@ -0,0 +1,487 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Byte`, a 8-bit signed integer (equivalent to Java's `byte` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Byte` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Byte]] => [[scala.runtime.RichByte]] + * which provides useful non-primitive operations. + */ +final abstract class Byte private extends AnyVal { + def toByte: Byte + def toShort: Short + def toChar: Char + def toInt: Int + def toLong: Long + def toFloat: Float + def toDouble: Double + + /** + * Returns the bitwise negation of this value. + * @example {{{ + * ~5 == -6 + * // in binary: ~00000101 == + * // 11111010 + * }}} + */ + def unary_~ : Int + /** Returns this value, unmodified. */ + def unary_+ : Int + /** Returns the negation of this value. */ + def unary_- : Int + + @deprecated("Adding a number and a String is deprecated. Use the string interpolation `s\"$num$str\"`", "2.13.0") + def +(x: String): String + + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + def <<(x: Int): Int + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def <<(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + def >>>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>>(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + def >>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>(x: Long): Int + + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Byte): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Short): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Char): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Int): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Long): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Float): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Double): Boolean + + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Byte): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Short): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Char): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Int): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Long): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Float): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Double): Boolean + + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Byte): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Short): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Char): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Int): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Long): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Float): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Double): Boolean + + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Byte): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Short): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Char): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Int): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Long): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Float): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Double): Boolean + + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Byte): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Short): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Char): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Int): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Long): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Float): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Double): Boolean + + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Byte): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Short): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Char): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Int): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Long): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Float): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Double): Boolean + + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Byte): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Short): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Char): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Int): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Long): Long + + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Byte): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Short): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Char): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Int): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Long): Long + + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Byte): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Short): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Char): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Int): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Long): Long + + /** Returns the sum of this value and `x`. */ + def +(x: Byte): Int + /** Returns the sum of this value and `x`. */ + def +(x: Short): Int + /** Returns the sum of this value and `x`. */ + def +(x: Char): Int + /** Returns the sum of this value and `x`. */ + def +(x: Int): Int + /** Returns the sum of this value and `x`. */ + def +(x: Long): Long + /** Returns the sum of this value and `x`. */ + def +(x: Float): Float + /** Returns the sum of this value and `x`. */ + def +(x: Double): Double + + /** Returns the difference of this value and `x`. */ + def -(x: Byte): Int + /** Returns the difference of this value and `x`. */ + def -(x: Short): Int + /** Returns the difference of this value and `x`. */ + def -(x: Char): Int + /** Returns the difference of this value and `x`. */ + def -(x: Int): Int + /** Returns the difference of this value and `x`. */ + def -(x: Long): Long + /** Returns the difference of this value and `x`. */ + def -(x: Float): Float + /** Returns the difference of this value and `x`. */ + def -(x: Double): Double + + /** Returns the product of this value and `x`. */ + def *(x: Byte): Int + /** Returns the product of this value and `x`. */ + def *(x: Short): Int + /** Returns the product of this value and `x`. */ + def *(x: Char): Int + /** Returns the product of this value and `x`. */ + def *(x: Int): Int + /** Returns the product of this value and `x`. */ + def *(x: Long): Long + /** Returns the product of this value and `x`. */ + def *(x: Float): Float + /** Returns the product of this value and `x`. */ + def *(x: Double): Double + + /** Returns the quotient of this value and `x`. */ + def /(x: Byte): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Short): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Char): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Int): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Long): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Float): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Double): Double + + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Byte): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Short): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Char): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Int): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Long): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Float): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Double): Double + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Byte] = ??? +} + +object Byte extends AnyValCompanion { + /** The smallest value representable as a Byte. */ + final val MinValue = java.lang.Byte.MIN_VALUE + + /** The largest value representable as a Byte. */ + final val MaxValue = java.lang.Byte.MAX_VALUE + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToByte`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Byte to be boxed + * @return a java.lang.Byte offering `x` as its underlying value. + */ + def box(x: Byte): java.lang.Byte = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Byte. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToByte`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Byte to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Byte + * @return the Byte resulting from calling byteValue() on `x` + */ + def unbox(x: java.lang.Object): Byte = ??? + + /** The String representation of the scala.Byte companion object. */ + override def toString = "object scala.Byte" + /** Language mandated coercions from Byte to "wider" types. */ + import scala.language.implicitConversions + implicit def byte2short(x: Byte): Short = x.toShort + implicit def byte2int(x: Byte): Int = x.toInt + implicit def byte2long(x: Byte): Long = x.toLong + implicit def byte2float(x: Byte): Float = x.toFloat + implicit def byte2double(x: Byte): Double = x.toDouble +} + diff --git a/library/src/scala/Char.scala b/library/src/scala/Char.scala new file mode 100644 index 000000000000..52871422a39e --- /dev/null +++ b/library/src/scala/Char.scala @@ -0,0 +1,486 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Char`, a 16-bit unsigned integer (equivalent to Java's `char` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Char` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Char]] => [[scala.runtime.RichChar]] + * which provides useful non-primitive operations. + */ +final abstract class Char private extends AnyVal { + def toByte: Byte + def toShort: Short + def toChar: Char + def toInt: Int + def toLong: Long + def toFloat: Float + def toDouble: Double + + /** + * Returns the bitwise negation of this value. + * @example {{{ + * ~5 == -6 + * // in binary: ~00000101 == + * // 11111010 + * }}} + */ + def unary_~ : Int + /** Returns this value, unmodified. */ + def unary_+ : Int + /** Returns the negation of this value. */ + def unary_- : Int + + @deprecated("Adding a number and a String is deprecated. Use the string interpolation `s\"$num$str\"`", "2.13.0") + def +(x: String): String + + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + def <<(x: Int): Int + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def <<(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + def >>>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>>(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + def >>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>(x: Long): Int + + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Byte): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Short): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Char): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Int): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Long): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Float): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Double): Boolean + + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Byte): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Short): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Char): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Int): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Long): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Float): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Double): Boolean + + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Byte): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Short): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Char): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Int): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Long): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Float): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Double): Boolean + + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Byte): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Short): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Char): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Int): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Long): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Float): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Double): Boolean + + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Byte): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Short): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Char): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Int): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Long): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Float): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Double): Boolean + + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Byte): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Short): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Char): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Int): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Long): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Float): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Double): Boolean + + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Byte): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Short): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Char): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Int): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Long): Long + + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Byte): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Short): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Char): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Int): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Long): Long + + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Byte): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Short): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Char): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Int): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Long): Long + + /** Returns the sum of this value and `x`. */ + def +(x: Byte): Int + /** Returns the sum of this value and `x`. */ + def +(x: Short): Int + /** Returns the sum of this value and `x`. */ + def +(x: Char): Int + /** Returns the sum of this value and `x`. */ + def +(x: Int): Int + /** Returns the sum of this value and `x`. */ + def +(x: Long): Long + /** Returns the sum of this value and `x`. */ + def +(x: Float): Float + /** Returns the sum of this value and `x`. */ + def +(x: Double): Double + + /** Returns the difference of this value and `x`. */ + def -(x: Byte): Int + /** Returns the difference of this value and `x`. */ + def -(x: Short): Int + /** Returns the difference of this value and `x`. */ + def -(x: Char): Int + /** Returns the difference of this value and `x`. */ + def -(x: Int): Int + /** Returns the difference of this value and `x`. */ + def -(x: Long): Long + /** Returns the difference of this value and `x`. */ + def -(x: Float): Float + /** Returns the difference of this value and `x`. */ + def -(x: Double): Double + + /** Returns the product of this value and `x`. */ + def *(x: Byte): Int + /** Returns the product of this value and `x`. */ + def *(x: Short): Int + /** Returns the product of this value and `x`. */ + def *(x: Char): Int + /** Returns the product of this value and `x`. */ + def *(x: Int): Int + /** Returns the product of this value and `x`. */ + def *(x: Long): Long + /** Returns the product of this value and `x`. */ + def *(x: Float): Float + /** Returns the product of this value and `x`. */ + def *(x: Double): Double + + /** Returns the quotient of this value and `x`. */ + def /(x: Byte): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Short): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Char): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Int): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Long): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Float): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Double): Double + + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Byte): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Short): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Char): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Int): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Long): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Float): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Double): Double + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Char] = ??? +} + +object Char extends AnyValCompanion { + /** The smallest value representable as a Char. */ + final val MinValue = java.lang.Character.MIN_VALUE + + /** The largest value representable as a Char. */ + final val MaxValue = java.lang.Character.MAX_VALUE + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToCharacter`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Char to be boxed + * @return a java.lang.Character offering `x` as its underlying value. + */ + def box(x: Char): java.lang.Character = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Character. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToChar`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Character to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Character + * @return the Char resulting from calling charValue() on `x` + */ + def unbox(x: java.lang.Object): Char = ??? + + /** The String representation of the scala.Char companion object. */ + override def toString = "object scala.Char" + /** Language mandated coercions from Char to "wider" types. */ + import scala.language.implicitConversions + implicit def char2int(x: Char): Int = x.toInt + implicit def char2long(x: Char): Long = x.toLong + implicit def char2float(x: Char): Float = x.toFloat + implicit def char2double(x: Char): Double = x.toDouble +} + diff --git a/library/src/scala/Console.scala b/library/src/scala/Console.scala new file mode 100644 index 000000000000..82e5ac10413e --- /dev/null +++ b/library/src/scala/Console.scala @@ -0,0 +1,281 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import java.io.{ BufferedReader, InputStream, InputStreamReader, OutputStream, PrintStream, Reader } +import scala.io.AnsiColor +import scala.util.DynamicVariable + +/** Implements functionality for printing Scala values on the terminal. For reading values + * use [[scala.io.StdIn$ StdIn]]. + * Also defines constants for marking up text on ANSI terminals. + * + * == Console Output == + * + * Use the print methods to output text. + * {{{ + * scala> Console.printf( + * "Today the outside temperature is a balmy %.1f°C. %<.1f°C beats the previous record of %.1f°C.\n", + * -137.0, + * -135.05) + * Today the outside temperature is a balmy -137.0°C. -137.0°C beats the previous record of -135.1°C. + * }}} + * + * == ANSI escape codes == + * Use the ANSI escape codes for colorizing console output either to STDOUT or STDERR. + * {{{ + * import Console.{GREEN, RED, RESET, YELLOW_B, UNDERLINED} + * + * object PrimeTest { + * + * def isPrime(): Unit = { + * + * val candidate = io.StdIn.readInt().ensuring(_ > 1) + * + * val prime = (2 to candidate - 1).forall(candidate % _ != 0) + * + * if (prime) + * Console.println(s"\${RESET}\${GREEN}yes\${RESET}") + * else + * Console.err.println(s"\${RESET}\${YELLOW_B}\${RED}\${UNDERLINED}NO!\${RESET}") + * } + * + * def main(args: Array[String]): Unit = isPrime() + * + * } + * }}} + * + * + * + * + * + * + * + * + *
\$ scala PrimeTest
1234567891
yes
\$ scala PrimeTest
56474
NO!
+ * + * == IO redefinition == + * + * Use IO redefinition to temporarily swap in a different set of input and/or output streams. In this example the stream based + * method above is wrapped into a function. + * + * {{{ + * import java.io.{ByteArrayOutputStream, StringReader} + * + * object FunctionalPrimeTest { + * + * def isPrime(candidate: Int): Boolean = { + * + * val input = new StringReader(s"\$candidate\n") + * val outCapture = new ByteArrayOutputStream + * val errCapture = new ByteArrayOutputStream + * + * Console.withIn(input) { + * Console.withOut(outCapture) { + * Console.withErr(errCapture) { + * PrimeTest.isPrime() + * } + * } + * } + * + * if (outCapture.toByteArray.nonEmpty) // "yes" + * true + * else if (errCapture.toByteArray.nonEmpty) // "NO!" + * false + * else throw new IllegalArgumentException(candidate.toString) + * } + * + * def main(args: Array[String]): Unit = { + * val primes = (2 to 50) filter (isPrime) + * println(s"First primes: \$primes") + * } + * + * } + * }}} + * + * + * + * + * + *
\$ scala FunctionalPrimeTest
First primes: Vector(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47)
+ * + * @groupname console-output Console Output + * @groupprio console-output 30 + * @groupdesc console-output These methods provide output via the console. + * + * @groupname io-default IO Defaults + * @groupprio io-default 50 + * @groupdesc io-default These values provide direct access to the standard IO channels + * + * @groupname io-redefinition IO Redefinition + * @groupprio io-redefinition 60 + * @groupdesc io-redefinition These methods allow substituting alternative streams for the duration of + * a body of code. Threadsafe by virtue of [[scala.util.DynamicVariable]]. + * + */ +object Console extends AnsiColor { + private[this] val outVar = new DynamicVariable[PrintStream](java.lang.System.out) + private[this] val errVar = new DynamicVariable[PrintStream](java.lang.System.err) + private[this] val inVar = new DynamicVariable[BufferedReader]( + new BufferedReader(new InputStreamReader(java.lang.System.in))) + + protected def setOutDirect(out: PrintStream): Unit = outVar.value = out + protected def setErrDirect(err: PrintStream): Unit = errVar.value = err + protected def setInDirect(in: BufferedReader): Unit = inVar.value = in + + /** The default output, can be overridden by `withOut` + * @group io-default + */ + def out: PrintStream = outVar.value + /** The default error, can be overridden by `withErr` + * @group io-default + */ + def err: PrintStream = errVar.value + /** The default input, can be overridden by `withIn` + * @group io-default + */ + def in: BufferedReader = inVar.value + + /** Sets the default output stream for the duration + * of execution of one thunk. + * + * @example {{{ + * withOut(Console.err) { println("This goes to default _error_") } + * }}} + * + * @param out the new output stream. + * @param thunk the code to execute with + * the new output stream active + * @return the results of `thunk` + * @see `withOut[T](out:OutputStream)(thunk: => T)` + * @group io-redefinition + */ + def withOut[T](out: PrintStream)(thunk: => T): T = + outVar.withValue(out)(thunk) + + /** Sets the default output stream for the duration + * of execution of one thunk. + * + * @param out the new output stream. + * @param thunk the code to execute with + * the new output stream active + * @return the results of `thunk` + * @see `withOut[T](out:PrintStream)(thunk: => T)` + * @group io-redefinition + */ + def withOut[T](out: OutputStream)(thunk: => T): T = + withOut(new PrintStream(out))(thunk) + + /** Set the default error stream for the duration + * of execution of one thunk. + * @example {{{ + * withErr(Console.out) { err.println("This goes to default _out_") } + * }}} + * + * @param err the new error stream. + * @param thunk the code to execute with + * the new error stream active + * @return the results of `thunk` + * @see `withErr[T](err:OutputStream)(thunk: => T)` + * @group io-redefinition + */ + def withErr[T](err: PrintStream)(thunk: => T): T = + errVar.withValue(err)(thunk) + + /** Sets the default error stream for the duration + * of execution of one thunk. + * + * @param err the new error stream. + * @param thunk the code to execute with + * the new error stream active + * @return the results of `thunk` + * @see `withErr[T](err:PrintStream)(thunk: => T)` + * @group io-redefinition + */ + def withErr[T](err: OutputStream)(thunk: => T): T = + withErr(new PrintStream(err))(thunk) + + /** Sets the default input stream for the duration + * of execution of one thunk. + * + * @example {{{ + * val someFile:Reader = openFile("file.txt") + * withIn(someFile) { + * // Reads a line from file.txt instead of default input + * println(readLine) + * } + * }}} + * + * @param thunk the code to execute with + * the new input stream active + * + * @return the results of `thunk` + * @see `withIn[T](in:InputStream)(thunk: => T)` + * @group io-redefinition + */ + def withIn[T](reader: Reader)(thunk: => T): T = + inVar.withValue(new BufferedReader(reader))(thunk) + + /** Sets the default input stream for the duration + * of execution of one thunk. + * + * @param in the new input stream. + * @param thunk the code to execute with + * the new input stream active + * @return the results of `thunk` + * @see `withIn[T](reader:Reader)(thunk: => T)` + * @group io-redefinition + */ + def withIn[T](in: InputStream)(thunk: => T): T = + withIn(new InputStreamReader(in))(thunk) + + /** Prints an object to `out` using its `toString` method. + * + * @param obj the object to print; may be null. + * @group console-output + */ + def print(obj: Any): Unit = { + out.print(if (null == obj) "null" else obj.toString()) + } + + /** Flushes the output stream. This function is required when partial + * output (i.e. output not terminated by a newline character) has + * to be made visible on the terminal. + * @group console-output + */ + def flush(): Unit = { out.flush() } + + /** Prints a newline character on the default output. + * @group console-output + */ + def println(): Unit = { out.println() } + + /** Prints out an object to the default output, followed by a newline character. + * + * @param x the object to print. + * @group console-output + */ + def println(x: Any): Unit = { out.println(x) } + + /** Prints its arguments as a formatted string to the default output, + * based on a string pattern (in a fashion similar to printf in C). + * + * The interpretation of the formatting patterns is described in [[java.util.Formatter]]. + * + * @param text the pattern for formatting the arguments. + * @param args the arguments used to instantiating the pattern. + * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments + * @group console-output + */ + def printf(text: String, args: Any*): Unit = { out.print(text.format(args: _*)) } +} diff --git a/library/src/scala/DelayedInit.scala b/library/src/scala/DelayedInit.scala new file mode 100644 index 000000000000..924d405d6c9d --- /dev/null +++ b/library/src/scala/DelayedInit.scala @@ -0,0 +1,52 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** Classes and objects (but note, not traits) inheriting the `DelayedInit` + * marker trait will have their initialization code rewritten as follows: + * `code` becomes `delayedInit(code)`. + * + * Initialization code comprises all statements and all value definitions + * that are executed during initialization. + * + * Example: + * {{{ + * trait Helper extends DelayedInit { + * def delayedInit(body: => Unit) = { + * println("dummy text, printed before initialization of C") + * body // evaluates the initialization code of C + * } + * } + * + * class C extends Helper { + * println("this is the initialization code of C") + * } + * + * object Test extends App { + * val c = new C + * } + * }}} + * + * Should result in the following being printed: + * {{{ + * dummy text, printed before initialization of C + * this is the initialization code of C + * }}} + * + * @see "Delayed Initialization" subsection of the Scala Language Specification (section 5.1) + * + */ +@deprecated("DelayedInit semantics can be surprising. Support for `App` will continue. See the release notes for more details: https://github.com/scala/scala/releases/tag/v2.11.0", "2.11.0") +trait DelayedInit { + def delayedInit(x: => Unit): Unit +} diff --git a/library/src/scala/Double.scala b/library/src/scala/Double.scala new file mode 100644 index 000000000000..84d9f31daa90 --- /dev/null +++ b/library/src/scala/Double.scala @@ -0,0 +1,255 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Double`, a 64-bit IEEE-754 floating point number (equivalent to Java's `double` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Double` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Double]] => [[scala.runtime.RichDouble]] + * which provides useful non-primitive operations. + */ +final abstract class Double private extends AnyVal { + def toByte: Byte + def toShort: Short + def toChar: Char + def toInt: Int + def toLong: Long + def toFloat: Float + def toDouble: Double + + /** Returns this value, unmodified. */ + def unary_+ : Double + /** Returns the negation of this value. */ + def unary_- : Double + + @deprecated("Adding a number and a String is deprecated. Use the string interpolation `s\"$num$str\"`", "2.13.0") + def +(x: String): String + + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Byte): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Short): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Char): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Int): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Long): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Float): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Double): Boolean + + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Byte): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Short): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Char): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Int): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Long): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Float): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Double): Boolean + + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Byte): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Short): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Char): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Int): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Long): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Float): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Double): Boolean + + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Byte): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Short): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Char): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Int): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Long): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Float): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Double): Boolean + + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Byte): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Short): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Char): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Int): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Long): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Float): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Double): Boolean + + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Byte): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Short): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Char): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Int): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Long): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Float): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Double): Boolean + + /** Returns the sum of this value and `x`. */ + def +(x: Byte): Double + /** Returns the sum of this value and `x`. */ + def +(x: Short): Double + /** Returns the sum of this value and `x`. */ + def +(x: Char): Double + /** Returns the sum of this value and `x`. */ + def +(x: Int): Double + /** Returns the sum of this value and `x`. */ + def +(x: Long): Double + /** Returns the sum of this value and `x`. */ + def +(x: Float): Double + /** Returns the sum of this value and `x`. */ + def +(x: Double): Double + + /** Returns the difference of this value and `x`. */ + def -(x: Byte): Double + /** Returns the difference of this value and `x`. */ + def -(x: Short): Double + /** Returns the difference of this value and `x`. */ + def -(x: Char): Double + /** Returns the difference of this value and `x`. */ + def -(x: Int): Double + /** Returns the difference of this value and `x`. */ + def -(x: Long): Double + /** Returns the difference of this value and `x`. */ + def -(x: Float): Double + /** Returns the difference of this value and `x`. */ + def -(x: Double): Double + + /** Returns the product of this value and `x`. */ + def *(x: Byte): Double + /** Returns the product of this value and `x`. */ + def *(x: Short): Double + /** Returns the product of this value and `x`. */ + def *(x: Char): Double + /** Returns the product of this value and `x`. */ + def *(x: Int): Double + /** Returns the product of this value and `x`. */ + def *(x: Long): Double + /** Returns the product of this value and `x`. */ + def *(x: Float): Double + /** Returns the product of this value and `x`. */ + def *(x: Double): Double + + /** Returns the quotient of this value and `x`. */ + def /(x: Byte): Double + /** Returns the quotient of this value and `x`. */ + def /(x: Short): Double + /** Returns the quotient of this value and `x`. */ + def /(x: Char): Double + /** Returns the quotient of this value and `x`. */ + def /(x: Int): Double + /** Returns the quotient of this value and `x`. */ + def /(x: Long): Double + /** Returns the quotient of this value and `x`. */ + def /(x: Float): Double + /** Returns the quotient of this value and `x`. */ + def /(x: Double): Double + + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Byte): Double + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Short): Double + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Char): Double + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Int): Double + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Long): Double + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Float): Double + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Double): Double + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Double] = ??? +} + +object Double extends AnyValCompanion { + /** The smallest positive value greater than 0.0d which is + * representable as a Double. + */ + final val MinPositiveValue = java.lang.Double.MIN_VALUE + final val NaN = java.lang.Double.NaN + final val PositiveInfinity = java.lang.Double.POSITIVE_INFINITY + final val NegativeInfinity = java.lang.Double.NEGATIVE_INFINITY + + /** The negative number with the greatest (finite) absolute value which is representable + * by a Double. Note that it differs from [[java.lang.Double.MIN_VALUE]], which + * is the smallest positive value representable by a Double. In Scala that number + * is called Double.MinPositiveValue. + */ + final val MinValue = -java.lang.Double.MAX_VALUE + + /** The largest finite positive number representable as a Double. */ + final val MaxValue = java.lang.Double.MAX_VALUE + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToDouble`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Double to be boxed + * @return a java.lang.Double offering `x` as its underlying value. + */ + def box(x: Double): java.lang.Double = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Double. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToDouble`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Double to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Double + * @return the Double resulting from calling doubleValue() on `x` + */ + def unbox(x: java.lang.Object): Double = ??? + + /** The String representation of the scala.Double companion object. */ + override def toString = "object scala.Double" +} + diff --git a/library/src/scala/DummyImplicit.scala b/library/src/scala/DummyImplicit.scala new file mode 100644 index 000000000000..07e7acfc6ebb --- /dev/null +++ b/library/src/scala/DummyImplicit.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** A type for which there is always an implicit value. */ +final class DummyImplicit private () + +object DummyImplicit { + /** An implicit value yielding a `DummyImplicit`. */ + implicit val dummyImplicit: DummyImplicit = new DummyImplicit +} diff --git a/library/src/scala/Dynamic.scala b/library/src/scala/Dynamic.scala new file mode 100644 index 000000000000..f80df3e49b7d --- /dev/null +++ b/library/src/scala/Dynamic.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** A marker trait that enables dynamic invocations. Instances `x` of this + * trait allow method invocations `x.meth(args)` for arbitrary method + * names `meth` and argument lists `args` as well as field accesses + * `x.field` for arbitrary field names `field`. + * + * If a call is not natively supported by `x` (i.e. if type checking + * fails), it is rewritten according to the following rules: + * + * {{{ + * foo.method("blah") ~~> foo.applyDynamic("method")("blah") + * foo.method(x = "blah") ~~> foo.applyDynamicNamed("method")(("x", "blah")) + * foo.method(x = 1, 2) ~~> foo.applyDynamicNamed("method")(("x", 1), ("", 2)) + * foo.field ~~> foo.selectDynamic("field") + * foo.varia = 10 ~~> foo.updateDynamic("varia")(10) + * foo.arr(10) = 13 ~~> foo.selectDynamic("arr").update(10, 13) + * foo.arr(10) ~~> foo.applyDynamic("arr")(10) + * }}} + * + * Defining direct or indirect subclasses of this trait + * is only possible if the language feature `dynamics` is enabled. + */ +trait Dynamic extends Any + + diff --git a/library/src/scala/Enumeration.scala b/library/src/scala/Enumeration.scala new file mode 100644 index 000000000000..bf61198f7d3b --- /dev/null +++ b/library/src/scala/Enumeration.scala @@ -0,0 +1,350 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.collection.{SpecificIterableFactory, StrictOptimizedIterableOps, View, immutable, mutable} +import java.lang.reflect.{Field => JField, Method => JMethod} + +import scala.annotation.{implicitNotFound, tailrec} +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the enumeration. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * Values SHOULD NOT be added to an enumeration after its construction; + * doing so makes the enumeration thread-unsafe. If values are added to an + * enumeration from multiple threads (in a non-synchronized fashion) after + * construction, the behavior of the enumeration is undefined. + * + * @example {{{ + * // Define a new enumeration with a type alias and work with the full set of enumerated values + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @example {{{ + * // Example of adding attributes to an enumeration by extending the Enumeration.Val class + * object Planet extends Enumeration { + * protected case class PlanetVal(mass: Double, radius: Double) extends super.Val { + * def surfaceGravity: Double = Planet.G * mass / (radius * radius) + * def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity + * } + * import scala.language.implicitConversions + * implicit def valueToPlanetVal(x: Value): PlanetVal = x.asInstanceOf[PlanetVal] + * + * val G: Double = 6.67300E-11 + * val Mercury = PlanetVal(3.303e+23, 2.4397e6) + * val Venus = PlanetVal(4.869e+24, 6.0518e6) + * val Earth = PlanetVal(5.976e+24, 6.37814e6) + * val Mars = PlanetVal(6.421e+23, 3.3972e6) + * val Jupiter = PlanetVal(1.9e+27, 7.1492e7) + * val Saturn = PlanetVal(5.688e+26, 6.0268e7) + * val Uranus = PlanetVal(8.686e+25, 2.5559e7) + * val Neptune = PlanetVal(1.024e+26, 2.4746e7) + * } + * + * println(Planet.values.filter(_.radius > 7.0e6)) + * // output: + * // Planet.ValueSet(Jupiter, Saturn, Uranus, Neptune) + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString: String = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ + private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private[this] val nmap: mutable.Map[Int, String] = new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private[this] var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private[this] var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.byName.getOrElse(s, + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap(): Unit = { + @tailrec def getFields(clazz: Class[_], acc: Array[JField]): Array[JField] = { + if (clazz == null) + acc + else + getFields(clazz.getSuperclass, if (clazz.getDeclaredFields.isEmpty) acc else acc ++ clazz.getDeclaredFields) + } + val fields = getFields(getClass.getSuperclass, getClass.getDeclaredFields) + def isValDef(m: JMethod): Boolean = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods: Array[JMethod] = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id: Int = value.id + nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any): Boolean = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value): ValueSet = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + + assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) + vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id: Int = i + override def toString(): String = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = { + val enumeration = thisenum.readResolve().asInstanceOf[Enumeration] + if (enumeration.vmap == null) this + else enumeration.vmap(i) + } + } + + /** An ordering by id for values of this set */ + implicit object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + @SerialVersionUID(7229671200427364242L) + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends immutable.AbstractSet[Value] + with immutable.SortedSet[Value] + with immutable.SortedSetOps[Value, immutable.SortedSet, ValueSet] + with StrictOptimizedIterableOps[Value, immutable.Set, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty: ValueSet = ValueSet.empty + override def knownSize: Int = nnIds.size + override def isEmpty: Boolean = nnIds.isEmpty + def contains(v: Value): Boolean = nnIds contains (v.id - bottomId) + def incl (value: Value): ValueSet = new ValueSet(nnIds + (value.id - bottomId)) + def excl (value: Value): ValueSet = new ValueSet(nnIds - (value.id - bottomId)) + def iterator: Iterator[Value] = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def iteratorFrom(start: Value): Iterator[Value] = nnIds iteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def className: String = s"$thisenum.ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + + override protected def fromSpecific(coll: IterableOnce[Value]): ValueSet = ValueSet.fromSpecific(coll) + override protected def newSpecificBuilder = ValueSet.newBuilder + + def map(f: Value => Value): ValueSet = fromSpecific(new View.Map(this, f)) + def flatMap(f: Value => IterableOnce[Value]): ValueSet = fromSpecific(new View.FlatMap(this, f)) + + // necessary for disambiguation: + override def map[B](f: Value => B)(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] = + super[SortedSet].map[B](f) + override def flatMap[B](f: Value => IterableOnce[B])(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] = + super[SortedSet].flatMap[B](f) + override def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(ValueSet.zipOrdMsg) ev: Ordering[(Value, B)]): immutable.SortedSet[(Value, B)] = + super[SortedSet].zip[B](that) + override def collect[B](pf: PartialFunction[Value, B])(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] = + super[SortedSet].collect[B](pf) + + @transient private[Enumeration] lazy val byName: Map[String, Value] = iterator.map( v => v.toString -> v).toMap + } + + /** A factory object for value sets */ + @SerialVersionUID(3L) + object ValueSet extends SpecificIterableFactory[Value, ValueSet] { + private final val ordMsg = "No implicit Ordering[${B}] found to build a SortedSet[${B}]. You may want to upcast to a Set[Value] first by calling `unsorted`." + private final val zipOrdMsg = "No implicit Ordering[${B}] found to build a SortedSet[(Value, ${B})]. You may want to upcast to a Set[Value] first by calling `unsorted`." + + /** The empty value set */ + val empty: ValueSet = new ValueSet(immutable.BitSet.empty) + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def addOne (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + def fromSpecific(it: IterableOnce[Value]): ValueSet = + newBuilder.addAll(it).result() + } +} diff --git a/library/src/scala/Equals.scala b/library/src/scala/Equals.scala new file mode 100644 index 000000000000..0c35742a6746 --- /dev/null +++ b/library/src/scala/Equals.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** An interface containing operations for equality. + * The only method not already present in class `AnyRef` is `canEqual`. + */ +trait Equals extends Any { + /** Checks whether this instance can possibly equal `that`. + * + * A method that should be called from every well-designed equals method + * that is open to be overridden in a subclass. See + * [[https://www.artima.com/pins1ed/object-equality.html Programming in Scala, + * Chapter 28]] for discussion and design. + * + * @param that the value being probed for possible equality + * @return true if this instance can possibly equal `that`, otherwise false + */ + def canEqual(that: Any): Boolean + + /** Checks whether this instance is equal to `that`. + * This universal equality method is defined in `AnyRef`. + */ + def equals(that: Any): Boolean +} diff --git a/library/src/scala/Float.scala b/library/src/scala/Float.scala new file mode 100644 index 000000000000..c63620ed8d53 --- /dev/null +++ b/library/src/scala/Float.scala @@ -0,0 +1,258 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Float`, a 32-bit IEEE-754 floating point number (equivalent to Java's `float` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Float` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Float]] => [[scala.runtime.RichFloat]] + * which provides useful non-primitive operations. + */ +final abstract class Float private extends AnyVal { + def toByte: Byte + def toShort: Short + def toChar: Char + def toInt: Int + def toLong: Long + def toFloat: Float + def toDouble: Double + + /** Returns this value, unmodified. */ + def unary_+ : Float + /** Returns the negation of this value. */ + def unary_- : Float + + @deprecated("Adding a number and a String is deprecated. Use the string interpolation `s\"$num$str\"`", "2.13.0") + def +(x: String): String + + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Byte): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Short): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Char): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Int): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Long): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Float): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Double): Boolean + + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Byte): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Short): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Char): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Int): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Long): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Float): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Double): Boolean + + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Byte): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Short): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Char): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Int): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Long): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Float): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Double): Boolean + + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Byte): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Short): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Char): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Int): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Long): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Float): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Double): Boolean + + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Byte): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Short): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Char): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Int): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Long): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Float): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Double): Boolean + + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Byte): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Short): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Char): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Int): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Long): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Float): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Double): Boolean + + /** Returns the sum of this value and `x`. */ + def +(x: Byte): Float + /** Returns the sum of this value and `x`. */ + def +(x: Short): Float + /** Returns the sum of this value and `x`. */ + def +(x: Char): Float + /** Returns the sum of this value and `x`. */ + def +(x: Int): Float + /** Returns the sum of this value and `x`. */ + def +(x: Long): Float + /** Returns the sum of this value and `x`. */ + def +(x: Float): Float + /** Returns the sum of this value and `x`. */ + def +(x: Double): Double + + /** Returns the difference of this value and `x`. */ + def -(x: Byte): Float + /** Returns the difference of this value and `x`. */ + def -(x: Short): Float + /** Returns the difference of this value and `x`. */ + def -(x: Char): Float + /** Returns the difference of this value and `x`. */ + def -(x: Int): Float + /** Returns the difference of this value and `x`. */ + def -(x: Long): Float + /** Returns the difference of this value and `x`. */ + def -(x: Float): Float + /** Returns the difference of this value and `x`. */ + def -(x: Double): Double + + /** Returns the product of this value and `x`. */ + def *(x: Byte): Float + /** Returns the product of this value and `x`. */ + def *(x: Short): Float + /** Returns the product of this value and `x`. */ + def *(x: Char): Float + /** Returns the product of this value and `x`. */ + def *(x: Int): Float + /** Returns the product of this value and `x`. */ + def *(x: Long): Float + /** Returns the product of this value and `x`. */ + def *(x: Float): Float + /** Returns the product of this value and `x`. */ + def *(x: Double): Double + + /** Returns the quotient of this value and `x`. */ + def /(x: Byte): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Short): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Char): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Int): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Long): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Float): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Double): Double + + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Byte): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Short): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Char): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Int): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Long): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Float): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Double): Double + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Float] = ??? +} + +object Float extends AnyValCompanion { + /** The smallest positive value greater than 0.0f which is + * representable as a Float. + */ + final val MinPositiveValue = java.lang.Float.MIN_VALUE + final val NaN = java.lang.Float.NaN + final val PositiveInfinity = java.lang.Float.POSITIVE_INFINITY + final val NegativeInfinity = java.lang.Float.NEGATIVE_INFINITY + + /** The negative number with the greatest (finite) absolute value which is representable + * by a Float. Note that it differs from [[java.lang.Float.MIN_VALUE]], which + * is the smallest positive value representable by a Float. In Scala that number + * is called Float.MinPositiveValue. + */ + final val MinValue = -java.lang.Float.MAX_VALUE + + /** The largest finite positive number representable as a Float. */ + final val MaxValue = java.lang.Float.MAX_VALUE + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToFloat`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Float to be boxed + * @return a java.lang.Float offering `x` as its underlying value. + */ + def box(x: Float): java.lang.Float = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Float. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToFloat`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Float to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Float + * @return the Float resulting from calling floatValue() on `x` + */ + def unbox(x: java.lang.Object): Float = ??? + + /** The String representation of the scala.Float companion object. */ + override def toString = "object scala.Float" + /** Language mandated coercions from Float to "wider" types. */ + import scala.language.implicitConversions + implicit def float2double(x: Float): Double = x.toDouble +} + diff --git a/library/src/scala/Function.scala b/library/src/scala/Function.scala new file mode 100644 index 000000000000..be612752552e --- /dev/null +++ b/library/src/scala/Function.scala @@ -0,0 +1,130 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** A module defining utility methods for higher-order functional programming. + */ +object Function { + /** Given a sequence of functions `f,,1,,`, ..., `f,,n,,`, return the + * function `f,,1,, andThen ... andThen f,,n,,`. + * + * @param fs The given sequence of functions + */ + def chain[T](fs: scala.collection.Seq[T => T]): T => T = { x => fs.foldLeft(x)((x, f) => f(x)) } + + /** The constant function */ + def const[T, U](x: T)(y: U): T = x + + /** Turns a function `A => Option[B]` into a `PartialFunction[A, B]`. + * + * '''Important note''': this transformation implies the original function + * may be called 2 or more times on each logical invocation, because the + * only way to supply an implementation of `isDefinedAt` is to call the + * function and examine the return value. + * See also [[scala.PartialFunction]], method `applyOrElse`. + * + * @param f a function `T => Option[R]` + * @return a partial function defined for those inputs where + * f returns `Some(_)` and undefined where `f` returns `None`. + * @see [[scala.PartialFunction]], method `lift`. + */ + def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = PartialFunction.unlifted(f) + + /** Uncurrying for functions of arity 2. This transforms a unary function + * returning another unary function into a function of arity 2. + */ + def uncurried[T1, T2, R](f: T1 => T2 => R): (T1, T2) => R = { + (x1, x2) => f(x1)(x2) + } + + /** Uncurrying for functions of arity 3. + */ + def uncurried[T1, T2, T3, R](f: T1 => T2 => T3 => R): (T1, T2, T3) => R = { + (x1, x2, x3) => f(x1)(x2)(x3) + } + + /** Uncurrying for functions of arity 4. + */ + def uncurried[T1, T2, T3, T4, R](f: T1 => T2 => T3 => T4 => R): (T1, T2, T3, T4) => R = { + (x1, x2, x3, x4) => f(x1)(x2)(x3)(x4) + } + + /** Uncurrying for functions of arity 5. + */ + def uncurried[T1, T2, T3, T4, T5, R](f: T1 => T2 => T3 => T4 => T5 => R): (T1, T2, T3, T4, T5) => R = { + (x1, x2, x3, x4, x5) => f(x1)(x2)(x3)(x4)(x5) + } + + /** Tupling for functions of arity 2. This transforms a function + * of arity 2 into a unary function that takes a pair of arguments. + * + * @note These functions are slotted for deprecation, but it is on + * hold pending superior type inference for tupling anonymous functions. + */ + // @deprecated("use `f.tupled` instead") + def tupled[T1, T2, R](f: (T1, T2) => R): ((T1, T2)) => R = { + case ((x1, x2)) => f(x1, x2) + } + + /** Tupling for functions of arity 3. This transforms a function + * of arity 3 into a unary function that takes a triple of arguments. + */ + // @deprecated("use `f.tupled` instead") + def tupled[T1, T2, T3, R](f: (T1, T2, T3) => R): ((T1, T2, T3)) => R = { + case ((x1, x2, x3)) => f(x1, x2, x3) + } + + /** Tupling for functions of arity 4. This transforms a function + * of arity 4 into a unary function that takes a 4-tuple of arguments. + */ + // @deprecated("use `f.tupled` instead") + def tupled[T1, T2, T3, T4, R](f: (T1, T2, T3, T4) => R): ((T1, T2, T3, T4)) => R = { + case ((x1, x2, x3, x4)) => f(x1, x2, x3, x4) + } + + /** Tupling for functions of arity 5. This transforms a function + * of arity 5 into a unary function that takes a 5-tuple of arguments. + */ + // @deprecated("use `f.tupled` instead") + def tupled[T1, T2, T3, T4, T5, R](f: (T1, T2, T3, T4, T5) => R): ((T1, T2, T3, T4, T5)) => R = { + case ((x1, x2, x3, x4, x5)) => f(x1, x2, x3, x4, x5) + } + + /** Un-tupling for functions of arity 2. This transforms a function taking + * a pair of arguments into a binary function which takes each argument separately. + */ + def untupled[T1, T2, R](f: ((T1, T2)) => R): (T1, T2) => R = { + (x1, x2) => f((x1, x2)) + } + + /** Un-tupling for functions of arity 3. This transforms a function taking + * a triple of arguments into a ternary function which takes each argument separately. + */ + def untupled[T1, T2, T3, R](f: ((T1, T2, T3)) => R): (T1, T2, T3) => R = { + (x1, x2, x3) => f((x1, x2, x3)) + } + + /** Un-tupling for functions of arity 4. This transforms a function taking + * a 4-tuple of arguments into a function of arity 4 which takes each argument separately. + */ + def untupled[T1, T2, T3, T4, R](f: ((T1, T2, T3, T4)) => R): (T1, T2, T3, T4) => R = { + (x1, x2, x3, x4) => f((x1, x2, x3, x4)) + } + + /** Un-tupling for functions of arity 5. This transforms a function taking + * a 5-tuple of arguments into a function of arity 5 which takes each argument separately. + */ + def untupled[T1, T2, T3, T4, T5, R](f: ((T1, T2, T3, T4, T5)) => R): (T1, T2, T3, T4, T5) => R = { + (x1, x2, x3, x4, x5) => f((x1, x2, x3, x4, x5)) + } +} diff --git a/library/src/scala/Function0.scala b/library/src/scala/Function0.scala new file mode 100644 index 000000000000..0cdea05ebc05 --- /dev/null +++ b/library/src/scala/Function0.scala @@ -0,0 +1,45 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. +// genprod generated these sources at: 2022-01-17T20:47:12.170348200Z + +package scala + + +/** A function of 0 parameters. + * + * In the following example, the definition of `greeting` is + * shorthand, conceptually, for the anonymous class definition + * `anonfun0`, although the implementation details of how the + * function value is constructed may differ: + * + * {{{ + * object Main extends App { + * val name = "world" + * val greeting = () => s"hello, $name" + * + * val anonfun0 = new Function0[String] { + * def apply(): String = s"hello, $name" + * } + * assert(greeting() == anonfun0()) + * } + * }}} + */ +trait Function0[@specialized(Specializable.Primitives) +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(): R + + override def toString(): String = "" +} diff --git a/library/src/scala/Function1.scala b/library/src/scala/Function1.scala new file mode 100644 index 000000000000..10d366303ab2 --- /dev/null +++ b/library/src/scala/Function1.scala @@ -0,0 +1,90 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +object Function1 { + + implicit final class UnliftOps[A, B] private[Function1](private val f: A => Option[B]) extends AnyVal { + /** Converts an optional function to a partial function. + * + * @example Unlike [[Function.unlift]], this [[UnliftOps.unlift]] method can be used in extractors. + * {{{ + * val of: Int => Option[String] = { i => + * if (i == 2) { + * Some("matched by an optional function") + * } else { + * None + * } + * } + * + * util.Random.nextInt(4) match { + * case of.unlift(m) => // Convert an optional function to a pattern + * println(m) + * case _ => + * println("Not matched") + * } + * }}} + */ + def unlift: PartialFunction[A, B] = Function.unlift(f) + } + +} + +/** A function of 1 parameter. + * + * In the following example, the definition of `succ` is + * shorthand, conceptually, for the anonymous class definition + * `anonfun1`, although the implementation details of how the + * function value is constructed may differ: + * + * {{{ + * object Main extends App { + * val succ = (x: Int) => x + 1 + * val anonfun1 = new Function1[Int, Int] { + * def apply(x: Int): Int = x + 1 + * } + * assert(succ(0) == anonfun1(0)) + * } + * }}} + * + * Note that the difference between `Function1` and [[scala.PartialFunction]] + * is that the latter can specify inputs which it will not handle. + */ +@annotation.implicitNotFound(msg = "No implicit view available from ${T1} => ${R}.") +trait Function1[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends AnyRef { self => + /** Apply the body of this function to the argument. + * @return the result of function application. + */ + def apply(v1: T1): R + + /** Composes two instances of `Function1` in a new `Function1`, with this function applied last. + * + * @tparam A the type to which function `g` can be applied + * @param g a function A => T1 + * @return a new function `f` such that `f(x) == apply(g(x))` + */ + @annotation.unspecialized def compose[A](g: A => T1): A => R = { x => apply(g(x)) } + + /** Composes two instances of `Function1` in a new `Function1`, with this function applied first. + * + * @tparam A the result type of function `g` + * @param g a function R => A + * @return a new function `f` such that `f(x) == g(apply(x))` + */ + @annotation.unspecialized def andThen[A](g: R => A): T1 => A = { x => g(apply(x)) } + + override def toString(): String = "" +} diff --git a/library/src/scala/Function10.scala b/library/src/scala/Function10.scala new file mode 100644 index 000000000000..59192bf8ee7d --- /dev/null +++ b/library/src/scala/Function10.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 10 parameters. + * + */ +trait Function10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)).curried + } + /** Creates a tupled version of this function: instead of 10 arguments, + * it accepts a single [[scala.Tuple10]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) == f(Tuple10(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function11.scala b/library/src/scala/Function11.scala new file mode 100644 index 000000000000..10b1509bf369 --- /dev/null +++ b/library/src/scala/Function11.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 11 parameters. + * + */ +trait Function11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)).curried + } + /** Creates a tupled version of this function: instead of 11 arguments, + * it accepts a single [[scala.Tuple11]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)) == f(Tuple11(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function12.scala b/library/src/scala/Function12.scala new file mode 100644 index 000000000000..08d962583108 --- /dev/null +++ b/library/src/scala/Function12.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 12 parameters. + * + */ +trait Function12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)).curried + } + /** Creates a tupled version of this function: instead of 12 arguments, + * it accepts a single [[scala.Tuple12]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)) == f(Tuple12(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function13.scala b/library/src/scala/Function13.scala new file mode 100644 index 000000000000..971368c1d467 --- /dev/null +++ b/library/src/scala/Function13.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 13 parameters. + * + */ +trait Function13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)).curried + } + /** Creates a tupled version of this function: instead of 13 arguments, + * it accepts a single [[scala.Tuple13]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)) == f(Tuple13(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function14.scala b/library/src/scala/Function14.scala new file mode 100644 index 000000000000..c0b72feef42c --- /dev/null +++ b/library/src/scala/Function14.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 14 parameters. + * + */ +trait Function14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)).curried + } + /** Creates a tupled version of this function: instead of 14 arguments, + * it accepts a single [[scala.Tuple14]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)) == f(Tuple14(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function15.scala b/library/src/scala/Function15.scala new file mode 100644 index 000000000000..67c7e1dc470a --- /dev/null +++ b/library/src/scala/Function15.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 15 parameters. + * + */ +trait Function15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)).curried + } + /** Creates a tupled version of this function: instead of 15 arguments, + * it accepts a single [[scala.Tuple15]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)) == f(Tuple15(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function16.scala b/library/src/scala/Function16.scala new file mode 100644 index 000000000000..8ea8dec9b117 --- /dev/null +++ b/library/src/scala/Function16.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 16 parameters. + * + */ +trait Function16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15, v16: T16): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)).curried + } + /** Creates a tupled version of this function: instead of 16 arguments, + * it accepts a single [[scala.Tuple16]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)) == f(Tuple16(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function17.scala b/library/src/scala/Function17.scala new file mode 100644 index 000000000000..bc157115963d --- /dev/null +++ b/library/src/scala/Function17.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 17 parameters. + * + */ +trait Function17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15, v16: T16, v17: T17): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)).curried + } + /** Creates a tupled version of this function: instead of 17 arguments, + * it accepts a single [[scala.Tuple17]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)) == f(Tuple17(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function18.scala b/library/src/scala/Function18.scala new file mode 100644 index 000000000000..d8ff8db313c6 --- /dev/null +++ b/library/src/scala/Function18.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 18 parameters. + * + */ +trait Function18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15, v16: T16, v17: T17, v18: T18): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)).curried + } + /** Creates a tupled version of this function: instead of 18 arguments, + * it accepts a single [[scala.Tuple18]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)) == f(Tuple18(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function19.scala b/library/src/scala/Function19.scala new file mode 100644 index 000000000000..9d79b5c2d7c1 --- /dev/null +++ b/library/src/scala/Function19.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 19 parameters. + * + */ +trait Function19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15, v16: T16, v17: T17, v18: T18, v19: T19): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)).curried + } + /** Creates a tupled version of this function: instead of 19 arguments, + * it accepts a single [[scala.Tuple19]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)) == f(Tuple19(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function2.scala b/library/src/scala/Function2.scala new file mode 100644 index 000000000000..f30d57e49344 --- /dev/null +++ b/library/src/scala/Function2.scala @@ -0,0 +1,58 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 2 parameters. + * + * In the following example, the definition of `max` is + * shorthand, conceptually, for the anonymous class definition + * `anonfun2`, although the implementation details of how the + * function value is constructed may differ: + * + * {{{ + * object Main extends App { + * val max = (x: Int, y: Int) => if (x < y) y else x + * + * val anonfun2 = new Function2[Int, Int, Int] { + * def apply(x: Int, y: Int): Int = if (x < y) y else x + * } + * assert(max(0, 1) == anonfun2(0, 1)) + * } + * }}} + */ +trait Function2[@specialized(Specializable.Args) -T1, @specialized(Specializable.Args) -T2, @specialized(Specializable.Return) +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2) == apply(x1, x2)` + */ + @annotation.unspecialized def curried: T1 => T2 => R = { + (x1: T1) => (x2: T2) => apply(x1, x2) + } + /** Creates a tupled version of this function: instead of 2 arguments, + * it accepts a single [[scala.Tuple2]] argument. + * + * @return a function `f` such that `f((x1, x2)) == f(Tuple2(x1, x2)) == apply(x1, x2)` + */ + + @annotation.unspecialized def tupled: ((T1, T2)) => R = { + case ((x1, x2)) => apply(x1, x2) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function20.scala b/library/src/scala/Function20.scala new file mode 100644 index 000000000000..1ed5e55a1616 --- /dev/null +++ b/library/src/scala/Function20.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 20 parameters. + * + */ +trait Function20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15, v16: T16, v17: T17, v18: T18, v19: T19, v20: T20): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19)(x20) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => T20 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)).curried + } + /** Creates a tupled version of this function: instead of 20 arguments, + * it accepts a single [[scala.Tuple20]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)) == f(Tuple20(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function21.scala b/library/src/scala/Function21.scala new file mode 100644 index 000000000000..4c81489ec323 --- /dev/null +++ b/library/src/scala/Function21.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 21 parameters. + * + */ +trait Function21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15, v16: T16, v17: T17, v18: T18, v19: T19, v20: T20, v21: T21): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19)(x20)(x21) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => T20 => T21 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20, x21: T21) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)).curried + } + /** Creates a tupled version of this function: instead of 21 arguments, + * it accepts a single [[scala.Tuple21]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)) == f(Tuple21(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function22.scala b/library/src/scala/Function22.scala new file mode 100644 index 000000000000..c3911f34c08e --- /dev/null +++ b/library/src/scala/Function22.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 22 parameters. + * + */ +trait Function22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11, v12: T12, v13: T13, v14: T14, v15: T15, v16: T16, v17: T17, v18: T18, v19: T19, v20: T20, v21: T21, v22: T22): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19)(x20)(x21)(x22) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => T20 => T21 => T22 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20, x21: T21, x22: T22) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)).curried + } + /** Creates a tupled version of this function: instead of 22 arguments, + * it accepts a single [[scala.Tuple22]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)) == f(Tuple22(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function3.scala b/library/src/scala/Function3.scala new file mode 100644 index 000000000000..77c1a8f38541 --- /dev/null +++ b/library/src/scala/Function3.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 3 parameters. + * + */ +trait Function3[-T1, -T2, -T3, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3) == apply(x1, x2, x3)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => R = { + (x1: T1) => (x2: T2) => (x3: T3) => apply(x1, x2, x3) + } + /** Creates a tupled version of this function: instead of 3 arguments, + * it accepts a single [[scala.Tuple3]] argument. + * + * @return a function `f` such that `f((x1, x2, x3)) == f(Tuple3(x1, x2, x3)) == apply(x1, x2, x3)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3)) => R = { + case ((x1, x2, x3)) => apply(x1, x2, x3) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function4.scala b/library/src/scala/Function4.scala new file mode 100644 index 000000000000..f68164cf2727 --- /dev/null +++ b/library/src/scala/Function4.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 4 parameters. + * + */ +trait Function4[-T1, -T2, -T3, -T4, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4) == apply(x1, x2, x3, x4)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => R = { + (x1: T1) => (x2: T2) => (x3: T3) => (x4: T4) => apply(x1, x2, x3, x4) + } + /** Creates a tupled version of this function: instead of 4 arguments, + * it accepts a single [[scala.Tuple4]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4)) == f(Tuple4(x1, x2, x3, x4)) == apply(x1, x2, x3, x4)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4)) => R = { + case ((x1, x2, x3, x4)) => apply(x1, x2, x3, x4) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function5.scala b/library/src/scala/Function5.scala new file mode 100644 index 000000000000..b5c347f5ee30 --- /dev/null +++ b/library/src/scala/Function5.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 5 parameters. + * + */ +trait Function5[-T1, -T2, -T3, -T4, -T5, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5) == apply(x1, x2, x3, x4, x5)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5) => self.apply(x1, x2, x3, x4, x5)).curried + } + /** Creates a tupled version of this function: instead of 5 arguments, + * it accepts a single [[scala.Tuple5]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5)) == f(Tuple5(x1, x2, x3, x4, x5)) == apply(x1, x2, x3, x4, x5)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5)) => R = { + case ((x1, x2, x3, x4, x5)) => apply(x1, x2, x3, x4, x5) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function6.scala b/library/src/scala/Function6.scala new file mode 100644 index 000000000000..784a51f61e59 --- /dev/null +++ b/library/src/scala/Function6.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 6 parameters. + * + */ +trait Function6[-T1, -T2, -T3, -T4, -T5, -T6, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6) == apply(x1, x2, x3, x4, x5, x6)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6) => self.apply(x1, x2, x3, x4, x5, x6)).curried + } + /** Creates a tupled version of this function: instead of 6 arguments, + * it accepts a single [[scala.Tuple6]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6)) == f(Tuple6(x1, x2, x3, x4, x5, x6)) == apply(x1, x2, x3, x4, x5, x6)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6)) => R = { + case ((x1, x2, x3, x4, x5, x6)) => apply(x1, x2, x3, x4, x5, x6) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function7.scala b/library/src/scala/Function7.scala new file mode 100644 index 000000000000..07c90bfd91d3 --- /dev/null +++ b/library/src/scala/Function7.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 7 parameters. + * + */ +trait Function7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7) == apply(x1, x2, x3, x4, x5, x6, x7)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7) => self.apply(x1, x2, x3, x4, x5, x6, x7)).curried + } + /** Creates a tupled version of this function: instead of 7 arguments, + * it accepts a single [[scala.Tuple7]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7)) == f(Tuple7(x1, x2, x3, x4, x5, x6, x7)) == apply(x1, x2, x3, x4, x5, x6, x7)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7)) => apply(x1, x2, x3, x4, x5, x6, x7) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function8.scala b/library/src/scala/Function8.scala new file mode 100644 index 000000000000..27ee36b2de90 --- /dev/null +++ b/library/src/scala/Function8.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 8 parameters. + * + */ +trait Function8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8) == apply(x1, x2, x3, x4, x5, x6, x7, x8)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8)).curried + } + /** Creates a tupled version of this function: instead of 8 arguments, + * it accepts a single [[scala.Tuple8]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8)) == f(Tuple8(x1, x2, x3, x4, x5, x6, x7, x8)) == apply(x1, x2, x3, x4, x5, x6, x7, x8)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8)) => apply(x1, x2, x3, x4, x5, x6, x7, x8) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Function9.scala b/library/src/scala/Function9.scala new file mode 100644 index 000000000000..5bf1a5b16565 --- /dev/null +++ b/library/src/scala/Function9.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A function of 9 parameters. + * + */ +trait Function9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends AnyRef { self => + /** Apply the body of this function to the arguments. + * @return the result of function application. + */ + def apply(v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9): R + /** Creates a curried version of this function. + * + * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9)` + */ + @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => R = { + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9)).curried + } + /** Creates a tupled version of this function: instead of 9 arguments, + * it accepts a single [[scala.Tuple9]] argument. + * + * @return a function `f` such that `f((x1, x2, x3, x4, x5, x6, x7, x8, x9)) == f(Tuple9(x1, x2, x3, x4, x5, x6, x7, x8, x9)) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9)` + */ + + @annotation.unspecialized def tupled: ((T1, T2, T3, T4, T5, T6, T7, T8, T9)) => R = { + case ((x1, x2, x3, x4, x5, x6, x7, x8, x9)) => apply(x1, x2, x3, x4, x5, x6, x7, x8, x9) + } + override def toString(): String = "" +} diff --git a/library/src/scala/Int.scala b/library/src/scala/Int.scala new file mode 100644 index 000000000000..003bd502a730 --- /dev/null +++ b/library/src/scala/Int.scala @@ -0,0 +1,486 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Int`, a 32-bit signed integer (equivalent to Java's `int` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Int` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Int]] => [[scala.runtime.RichInt]] + * which provides useful non-primitive operations. + */ +final abstract class Int private extends AnyVal { + def toByte: Byte + def toShort: Short + def toChar: Char + def toInt: Int + def toLong: Long + def toFloat: Float + def toDouble: Double + + /** + * Returns the bitwise negation of this value. + * @example {{{ + * ~5 == -6 + * // in binary: ~00000101 == + * // 11111010 + * }}} + */ + def unary_~ : Int + /** Returns this value, unmodified. */ + def unary_+ : Int + /** Returns the negation of this value. */ + def unary_- : Int + + @deprecated("Adding a number and a String is deprecated. Use the string interpolation `s\"$num$str\"`", "2.13.0") + def +(x: String): String + + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + def <<(x: Int): Int + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def <<(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + def >>>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>>(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + def >>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>(x: Long): Int + + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Byte): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Short): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Char): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Int): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Long): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Float): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Double): Boolean + + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Byte): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Short): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Char): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Int): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Long): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Float): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Double): Boolean + + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Byte): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Short): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Char): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Int): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Long): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Float): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Double): Boolean + + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Byte): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Short): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Char): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Int): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Long): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Float): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Double): Boolean + + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Byte): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Short): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Char): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Int): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Long): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Float): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Double): Boolean + + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Byte): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Short): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Char): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Int): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Long): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Float): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Double): Boolean + + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Byte): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Short): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Char): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Int): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Long): Long + + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Byte): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Short): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Char): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Int): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Long): Long + + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Byte): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Short): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Char): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Int): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Long): Long + + /** Returns the sum of this value and `x`. */ + def +(x: Byte): Int + /** Returns the sum of this value and `x`. */ + def +(x: Short): Int + /** Returns the sum of this value and `x`. */ + def +(x: Char): Int + /** Returns the sum of this value and `x`. */ + def +(x: Int): Int + /** Returns the sum of this value and `x`. */ + def +(x: Long): Long + /** Returns the sum of this value and `x`. */ + def +(x: Float): Float + /** Returns the sum of this value and `x`. */ + def +(x: Double): Double + + /** Returns the difference of this value and `x`. */ + def -(x: Byte): Int + /** Returns the difference of this value and `x`. */ + def -(x: Short): Int + /** Returns the difference of this value and `x`. */ + def -(x: Char): Int + /** Returns the difference of this value and `x`. */ + def -(x: Int): Int + /** Returns the difference of this value and `x`. */ + def -(x: Long): Long + /** Returns the difference of this value and `x`. */ + def -(x: Float): Float + /** Returns the difference of this value and `x`. */ + def -(x: Double): Double + + /** Returns the product of this value and `x`. */ + def *(x: Byte): Int + /** Returns the product of this value and `x`. */ + def *(x: Short): Int + /** Returns the product of this value and `x`. */ + def *(x: Char): Int + /** Returns the product of this value and `x`. */ + def *(x: Int): Int + /** Returns the product of this value and `x`. */ + def *(x: Long): Long + /** Returns the product of this value and `x`. */ + def *(x: Float): Float + /** Returns the product of this value and `x`. */ + def *(x: Double): Double + + /** Returns the quotient of this value and `x`. */ + def /(x: Byte): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Short): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Char): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Int): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Long): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Float): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Double): Double + + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Byte): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Short): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Char): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Int): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Long): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Float): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Double): Double + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Int] = ??? +} + +object Int extends AnyValCompanion { + /** The smallest value representable as an Int. */ + final val MinValue = java.lang.Integer.MIN_VALUE + + /** The largest value representable as an Int. */ + final val MaxValue = java.lang.Integer.MAX_VALUE + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToInteger`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Int to be boxed + * @return a java.lang.Integer offering `x` as its underlying value. + */ + def box(x: Int): java.lang.Integer = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Integer. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToInt`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Integer to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Integer + * @return the Int resulting from calling intValue() on `x` + */ + def unbox(x: java.lang.Object): Int = ??? + + /** The String representation of the scala.Int companion object. */ + override def toString = "object scala.Int" + /** Language mandated coercions from Int to "wider" types. */ + import scala.language.implicitConversions + @deprecated("Implicit conversion from Int to Float is dangerous because it loses precision. Write `.toFloat` instead.", "2.13.1") + implicit def int2float(x: Int): Float = x.toFloat + implicit def int2long(x: Int): Long = x.toLong + implicit def int2double(x: Int): Double = x.toDouble +} + diff --git a/library/src/scala/Long.scala b/library/src/scala/Long.scala new file mode 100644 index 000000000000..09bd35c64677 --- /dev/null +++ b/library/src/scala/Long.scala @@ -0,0 +1,483 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Long`, a 64-bit signed integer (equivalent to Java's `long` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Long` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Long]] => [[scala.runtime.RichLong]] + * which provides useful non-primitive operations. + */ +final abstract class Long private extends AnyVal { + def toByte: Byte + def toShort: Short + def toChar: Char + def toInt: Int + def toLong: Long + def toFloat: Float + def toDouble: Double + + /** + * Returns the bitwise negation of this value. + * @example {{{ + * ~5 == -6 + * // in binary: ~00000101 == + * // 11111010 + * }}} + */ + def unary_~ : Long + /** Returns this value, unmodified. */ + def unary_+ : Long + /** Returns the negation of this value. */ + def unary_- : Long + + @deprecated("Adding a number and a String is deprecated. Use the string interpolation `s\"$num$str\"`", "2.13.0") + def +(x: String): String + + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + def <<(x: Int): Long + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + def <<(x: Long): Long + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + def >>>(x: Int): Long + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + def >>>(x: Long): Long + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + def >>(x: Int): Long + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + def >>(x: Long): Long + + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Byte): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Short): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Char): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Int): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Long): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Float): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Double): Boolean + + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Byte): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Short): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Char): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Int): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Long): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Float): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Double): Boolean + + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Byte): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Short): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Char): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Int): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Long): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Float): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Double): Boolean + + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Byte): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Short): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Char): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Int): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Long): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Float): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Double): Boolean + + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Byte): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Short): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Char): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Int): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Long): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Float): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Double): Boolean + + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Byte): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Short): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Char): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Int): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Long): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Float): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Double): Boolean + + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Byte): Long + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Short): Long + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Char): Long + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Int): Long + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Long): Long + + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Byte): Long + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Short): Long + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Char): Long + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Int): Long + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Long): Long + + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Byte): Long + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Short): Long + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Char): Long + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Int): Long + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Long): Long + + /** Returns the sum of this value and `x`. */ + def +(x: Byte): Long + /** Returns the sum of this value and `x`. */ + def +(x: Short): Long + /** Returns the sum of this value and `x`. */ + def +(x: Char): Long + /** Returns the sum of this value and `x`. */ + def +(x: Int): Long + /** Returns the sum of this value and `x`. */ + def +(x: Long): Long + /** Returns the sum of this value and `x`. */ + def +(x: Float): Float + /** Returns the sum of this value and `x`. */ + def +(x: Double): Double + + /** Returns the difference of this value and `x`. */ + def -(x: Byte): Long + /** Returns the difference of this value and `x`. */ + def -(x: Short): Long + /** Returns the difference of this value and `x`. */ + def -(x: Char): Long + /** Returns the difference of this value and `x`. */ + def -(x: Int): Long + /** Returns the difference of this value and `x`. */ + def -(x: Long): Long + /** Returns the difference of this value and `x`. */ + def -(x: Float): Float + /** Returns the difference of this value and `x`. */ + def -(x: Double): Double + + /** Returns the product of this value and `x`. */ + def *(x: Byte): Long + /** Returns the product of this value and `x`. */ + def *(x: Short): Long + /** Returns the product of this value and `x`. */ + def *(x: Char): Long + /** Returns the product of this value and `x`. */ + def *(x: Int): Long + /** Returns the product of this value and `x`. */ + def *(x: Long): Long + /** Returns the product of this value and `x`. */ + def *(x: Float): Float + /** Returns the product of this value and `x`. */ + def *(x: Double): Double + + /** Returns the quotient of this value and `x`. */ + def /(x: Byte): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Short): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Char): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Int): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Long): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Float): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Double): Double + + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Byte): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Short): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Char): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Int): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Long): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Float): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Double): Double + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Long] = ??? +} + +object Long extends AnyValCompanion { + /** The smallest value representable as a Long. */ + final val MinValue = java.lang.Long.MIN_VALUE + + /** The largest value representable as a Long. */ + final val MaxValue = java.lang.Long.MAX_VALUE + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToLong`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Long to be boxed + * @return a java.lang.Long offering `x` as its underlying value. + */ + def box(x: Long): java.lang.Long = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Long. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToLong`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Long to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Long + * @return the Long resulting from calling longValue() on `x` + */ + def unbox(x: java.lang.Object): Long = ??? + + /** The String representation of the scala.Long companion object. */ + override def toString = "object scala.Long" + /** Language mandated coercions from Long to "wider" types. */ + import scala.language.implicitConversions + @deprecated("Implicit conversion from Long to Float is dangerous because it loses precision. Write `.toFloat` instead.", "2.13.1") + implicit def long2float(x: Long): Float = x.toFloat + @deprecated("Implicit conversion from Long to Double is dangerous because it loses precision. Write `.toDouble` instead.", "2.13.1") + implicit def long2double(x: Long): Double = x.toDouble +} + diff --git a/library/src/scala/MatchError.scala b/library/src/scala/MatchError.scala new file mode 100644 index 000000000000..39fa11e817f0 --- /dev/null +++ b/library/src/scala/MatchError.scala @@ -0,0 +1,40 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** This class implements errors which are thrown whenever an + * object doesn't match any pattern of a pattern matching + * expression. + */ +final class MatchError(@transient obj: Any) extends RuntimeException { + /** There's no reason we need to call toString eagerly, + * so defer it until getMessage is called or object is serialized + */ + private[this] lazy val objString = { + def ofClass = "of class " + obj.getClass.getName + if (obj == null) "null" + else + try s"$obj ($ofClass)" + catch { + case _: Throwable => "an instance " + ofClass + } + } + + @throws[java.io.ObjectStreamException] + private def writeReplace(): Object = { + objString + this + } + + override def getMessage() = objString +} diff --git a/library/src/scala/NotImplementedError.scala b/library/src/scala/NotImplementedError.scala new file mode 100644 index 000000000000..22361b78b85b --- /dev/null +++ b/library/src/scala/NotImplementedError.scala @@ -0,0 +1,21 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** Throwing this exception can be a temporary replacement for a method + * body that remains to be implemented. For instance, the exception is thrown by + * `Predef.???`. + */ +final class NotImplementedError(msg: String) extends Error(msg) { + def this() = this("an implementation is missing") +} diff --git a/library/src/scala/Option.scala b/library/src/scala/Option.scala new file mode 100644 index 000000000000..6605712123c8 --- /dev/null +++ b/library/src/scala/Option.scala @@ -0,0 +1,628 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +object Option { + + import scala.language.implicitConversions + + /** An implicit conversion that converts an option to an iterable value */ + implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = + if (xo.isEmpty) Iterable.empty else Iterable.single(xo.get) + + /** An Option factory which creates Some(x) if the argument is not null, + * and None if it is null. + * + * @param x the value + * @return Some(value) if value != null, None if value == null + */ + def apply[A](x: A): Option[A] = if (x == null) None else Some(x) + + /** An Option factory which returns `None` in a manner consistent with + * the collections hierarchy. + */ + def empty[A] : Option[A] = None + + /** When a given condition is true, evaluates the `a` argument and returns + * Some(a). When the condition is false, `a` is not evaluated and None is + * returned. + */ + def when[A](cond: Boolean)(a: => A): Option[A] = + if (cond) Some(a) else None + + /** Unless a given condition is true, this will evaluate the `a` argument and + * return Some(a). Otherwise, `a` is not evaluated and None is returned. + */ + @inline def unless[A](cond: Boolean)(a: => A): Option[A] = + when(!cond)(a) +} + +/** Represents optional values. Instances of `Option` + * are either an instance of $some or the object $none. + * + * The most idiomatic way to use an $option instance is to treat it + * as a collection or monad and use `map`,`flatMap`, `filter`, or + * `foreach`: + * + * {{{ + * val name: Option[String] = request getParameter "name" + * val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase } + * println(upper getOrElse "") + * }}} + * + * Note that this is equivalent to {{{ + * val upper = for { + * name <- request getParameter "name" + * trimmed <- Some(name.trim) + * upper <- Some(trimmed.toUpperCase) if trimmed.length != 0 + * } yield upper + * println(upper getOrElse "") + * }}} + * + * Because of how for comprehension works, if $none is returned + * from `request.getParameter`, the entire expression results in + * $none + * + * This allows for sophisticated chaining of $option values without + * having to check for the existence of a value. + * + * These are useful methods that exist for both $some and $none. + * - [[isDefined]] — True if not empty + * - [[isEmpty]] — True if empty + * - [[nonEmpty]] — True if not empty + * - [[orElse]] — Evaluate and return alternate optional value if empty + * - [[getOrElse]] — Evaluate and return alternate value if empty + * - [[get]] — Return value, throw exception if empty + * - [[fold]] — Apply function on optional value, return default if empty + * - [[map]] — Apply a function on the optional value + * - [[flatMap]] — Same as map but function must return an optional value + * - [[foreach]] — Apply a procedure on option value + * - [[collect]] — Apply partial pattern match on optional value + * - [[filter]] — An optional value satisfies predicate + * - [[filterNot]] — An optional value doesn't satisfy predicate + * - [[exists]] — Apply predicate on optional value, or false if empty + * - [[forall]] — Apply predicate on optional value, or true if empty + * - [[contains]] — Checks if value equals optional value, or false if empty + * - [[zip]] — Combine two optional values to make a paired optional value + * - [[unzip]] — Split an optional pair to two optional values + * - [[unzip3]] — Split an optional triple to three optional values + * - [[toList]] — Unary list of optional value, otherwise the empty list + * + * A less-idiomatic way to use $option values is via pattern matching: {{{ + * val nameMaybe = request getParameter "name" + * nameMaybe match { + * case Some(name) => + * println(name.trim.toUppercase) + * case None => + * println("No name value") + * } + * }}} + * + * Interacting with code that can occasionally return null can be + * safely wrapped in $option to become $none and $some otherwise. {{{ + * val abc = new java.util.HashMap[Int, String] + * abc.put(1, "A") + * bMaybe = Option(abc.get(2)) + * bMaybe match { + * case Some(b) => + * println(s"Found \$b") + * case None => + * println("Not found") + * } + * }}} + * + * @note Many of the methods in here are duplicative with those + * in the Iterable hierarchy, but they are duplicated for a reason: + * the implicit conversion tends to leave one with an Iterable in + * situations where one could have retained an Option. + * + * @define none `None` + * @define some [[scala.Some]] + * @define option [[scala.Option]] + * @define p `p` + * @define f `f` + * @define coll option + * @define Coll `Option` + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define collectExample + * @define undefinedorder + */ +@SerialVersionUID(-114498752079829388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4 +sealed abstract class Option[+A] extends IterableOnce[A] with Product with Serializable { + self => + + /** Returns true if the option is $none, false otherwise. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(_) => false + * case None => true + * } + * }}} + */ + final def isEmpty: Boolean = this eq None + + /** Returns true if the option is an instance of $some, false otherwise. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(_) => true + * case None => false + * } + * }}} + */ + final def isDefined: Boolean = !isEmpty + + override final def knownSize: Int = if (isEmpty) 0 else 1 + + /** Returns the option's value. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => x + * case None => throw new Exception + * } + * }}} + * @note The option must be nonempty. + * @throws NoSuchElementException if the option is empty. + */ + def get: A + + /** Returns the option's value if the option is nonempty, otherwise + * return the result of evaluating `default`. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => x + * case None => default + * } + * }}} + * + * @param default the default expression. + */ + @inline final def getOrElse[B >: A](default: => B): B = + if (isEmpty) default else this.get + + /** Returns the option's value if it is nonempty, + * or `null` if it is empty. + * + * Although the use of null is discouraged, code written to use + * $option must often interface with code that expects and returns nulls. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => x + * case None => null + * } + * }}} + * @example {{{ + * val initialText: Option[String] = getInitialText + * val textField = new JComponent(initialText.orNull,20) + * }}} + */ + @inline final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse ev(null) + + /** Returns a $some containing the result of applying $f to this $option's + * value if this $option is nonempty. + * Otherwise return $none. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => Some(f(x)) + * case None => None + * } + * }}} + * @note This is similar to `flatMap` except here, + * $f does not need to wrap its result in an $option. + * + * @param f the function to apply + * @see flatMap + * @see foreach + */ + @inline final def map[B](f: A => B): Option[B] = + if (isEmpty) None else Some(f(this.get)) + + /** Returns the result of applying $f to this $option's + * value if the $option is nonempty. Otherwise, evaluates + * expression `ifEmpty`. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => f(x) + * case None => ifEmpty + * } + * }}} + * This is also equivalent to: + * {{{ + * option map f getOrElse ifEmpty + * }}} + * @param ifEmpty the expression to evaluate if empty. + * @param f the function to apply if nonempty. + */ + @inline final def fold[B](ifEmpty: => B)(f: A => B): B = + if (isEmpty) ifEmpty else f(this.get) + + /** Returns the result of applying $f to this $option's value if + * this $option is nonempty. + * Returns $none if this $option is empty. + * Slightly different from `map` in that $f is expected to + * return an $option (which could be $none). + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => f(x) + * case None => None + * } + * }}} + * @param f the function to apply + * @see map + * @see foreach + */ + @inline final def flatMap[B](f: A => Option[B]): Option[B] = + if (isEmpty) None else f(this.get) + + /** Returns the nested $option value if it is nonempty. Otherwise, + * return $none. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(Some(b)) => Some(b) + * case _ => None + * } + * }}} + * @example {{{ + * Some(Some("something")).flatten + * }}} + * + * @param ev an implicit conversion that asserts that the value is + * also an $option. + * @see flatMap + */ + def flatten[B](implicit ev: A <:< Option[B]): Option[B] = + if (isEmpty) None else ev(this.get) + + /** Returns this $option if it is nonempty '''and''' applying the predicate $p to + * this $option's value returns true. Otherwise, return $none. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) if p(x) => Some(x) + * case _ => None + * } + * }}} + * @param p the predicate used for testing. + */ + @inline final def filter(p: A => Boolean): Option[A] = + if (isEmpty || p(this.get)) this else None + + /** Returns this $option if it is nonempty '''and''' applying the predicate $p to + * this $option's value returns false. Otherwise, return $none. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) if !p(x) => Some(x) + * case _ => None + * } + * }}} + * @param p the predicate used for testing. + */ + @inline final def filterNot(p: A => Boolean): Option[A] = + if (isEmpty || !p(this.get)) this else None + + /** Returns false if the option is $none, true otherwise. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(_) => true + * case None => false + * } + * }}} + * @note Implemented here to avoid the implicit conversion to Iterable. + */ + final def nonEmpty: Boolean = isDefined + + /** Necessary to keep $option from being implicitly converted to + * [[scala.collection.Iterable]] in `for` comprehensions. + */ + @inline final def withFilter(p: A => Boolean): WithFilter = new WithFilter(p) + + /** We need a whole WithFilter class to honor the "doesn't create a new + * collection" contract even though it seems unlikely to matter much in a + * collection with max size 1. + */ + class WithFilter(p: A => Boolean) { + def map[B](f: A => B): Option[B] = self filter p map f + def flatMap[B](f: A => Option[B]): Option[B] = self filter p flatMap f + def foreach[U](f: A => U): Unit = self filter p foreach f + def withFilter(q: A => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) + } + + /** Tests whether the option contains a given value as an element. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => x == elem + * case None => false + * } + * }}} + * @example {{{ + * // Returns true because Some instance contains string "something" which equals "something". + * Some("something") contains "something" + * + * // Returns false because "something" != "anything". + * Some("something") contains "anything" + * + * // Returns false when method called on None. + * None contains "anything" + * }}} + * + * @param elem the element to test. + * @return `true` if the option has an element that is equal (as + * determined by `==`) to `elem`, `false` otherwise. + */ + final def contains[A1 >: A](elem: A1): Boolean = + !isEmpty && this.get == elem + + /** Returns true if this option is nonempty '''and''' the predicate + * $p returns true when applied to this $option's value. + * Otherwise, returns false. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => p(x) + * case None => false + * } + * }}} + * @param p the predicate to test + */ + @inline final def exists(p: A => Boolean): Boolean = + !isEmpty && p(this.get) + + /** Returns true if this option is empty '''or''' the predicate + * $p returns true when applied to this $option's value. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => p(x) + * case None => true + * } + * }}} + * @param p the predicate to test + */ + @inline final def forall(p: A => Boolean): Boolean = isEmpty || p(this.get) + + /** Apply the given procedure $f to the option's value, + * if it is nonempty. Otherwise, do nothing. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => f(x) + * case None => () + * } + * }}} + * @param f the procedure to apply. + * @see map + * @see flatMap + */ + @inline final def foreach[U](f: A => U): Unit = { + if (!isEmpty) f(this.get) + } + + /** Returns a $some containing the result of + * applying `pf` to this $option's contained + * value, '''if''' this option is + * nonempty '''and''' `pf` is defined for that value. + * Returns $none otherwise. + * + * @example {{{ + * // Returns Some(HTTP) because the partial function covers the case. + * Some("http") collect {case "http" => "HTTP"} + * + * // Returns None because the partial function doesn't cover the case. + * Some("ftp") collect {case "http" => "HTTP"} + * + * // Returns None because the option is empty. There is no value to pass to the partial function. + * None collect {case value => value} + * }}} + * + * @param pf the partial function. + * @return the result of applying `pf` to this $option's + * value (if possible), or $none. + */ + @inline final def collect[B](pf: PartialFunction[A, B]): Option[B] = + if (!isEmpty) pf.lift(this.get) else None + + /** Returns this $option if it is nonempty, + * otherwise return the result of evaluating `alternative`. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => Some(x) + * case None => alternative + * } + * }}} + * @param alternative the alternative expression. + */ + @inline final def orElse[B >: A](alternative: => Option[B]): Option[B] = + if (isEmpty) alternative else this + + /** Returns a $some formed from this option and another option + * by combining the corresponding elements in a pair. + * If either of the two options is empty, $none is returned. + * + * This is equivalent to: + * {{{ + * (option1, option2) match { + * case (Some(x), Some(y)) => Some((x, y)) + * case _ => None + * } + * }}} + * @example {{{ + * // Returns Some(("foo", "bar")) because both options are nonempty. + * Some("foo") zip Some("bar") + * + * // Returns None because `that` option is empty. + * Some("foo") zip None + * + * // Returns None because `this` option is empty. + * None zip Some("bar") + * }}} + * + * @param that the options which is going to be zipped + */ + final def zip[A1 >: A, B](that: Option[B]): Option[(A1, B)] = + if (isEmpty || that.isEmpty) None else Some((this.get, that.get)) + + /** Converts an Option of a pair into an Option of the first element and an Option of the second element. + * + * This is equivalent to: + * {{{ + * option match { + * case Some((x, y)) => (Some(x), Some(y)) + * case _ => (None, None) + * } + * }}} + * @tparam A1 the type of the first half of the element pair + * @tparam A2 the type of the second half of the element pair + * @param asPair an implicit conversion which asserts that the element type + * of this Option is a pair. + * @return a pair of Options, containing, respectively, the first and second half + * of the element pair of this Option. + */ + final def unzip[A1, A2](implicit asPair: A <:< (A1, A2)): (Option[A1], Option[A2]) = { + if (isEmpty) + (None, None) + else { + val e = asPair(this.get) + (Some(e._1), Some(e._2)) + } + } + + /** Converts an Option of a triple into three Options, one containing the element from each position of the triple. + * + * This is equivalent to: + * {{{ + * option match { + * case Some((x, y, z)) => (Some(x), Some(y), Some(z)) + * case _ => (None, None, None) + * } + * }}} + * @tparam A1 the type of the first of three elements in the triple + * @tparam A2 the type of the second of three elements in the triple + * @tparam A3 the type of the third of three elements in the triple + * @param asTriple an implicit conversion which asserts that the element type + * of this Option is a triple. + * @return a triple of Options, containing, respectively, the first, second, and third + * elements from the element triple of this Option. + */ + final def unzip3[A1, A2, A3](implicit asTriple: A <:< (A1, A2, A3)): (Option[A1], Option[A2], Option[A3]) = { + if (isEmpty) + (None, None, None) + else { + val e = asTriple(this.get) + (Some(e._1), Some(e._2), Some(e._3)) + } + } + + /** Returns a singleton iterator returning the $option's value + * if it is nonempty, or an empty iterator if the option is empty. + */ + def iterator: Iterator[A] = + if (isEmpty) collection.Iterator.empty else collection.Iterator.single(this.get) + + /** Returns a singleton list containing the $option's value + * if it is nonempty, or the empty list if the $option is empty. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => List(x) + * case None => Nil + * } + * }}} + */ + def toList: List[A] = + if (isEmpty) List() else new ::(this.get, Nil) + + /** Returns a [[scala.util.Left]] containing the given + * argument `left` if this $option is empty, or + * a [[scala.util.Right]] containing this $option's value if + * this is nonempty. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => Right(x) + * case None => Left(left) + * } + * }}} + * @param left the expression to evaluate and return if this is empty + * @see toLeft + */ + @inline final def toRight[X](left: => X): Either[X, A] = + if (isEmpty) Left(left) else Right(this.get) + + /** Returns a [[scala.util.Right]] containing the given + * argument `right` if this is empty, or + * a [[scala.util.Left]] containing this $option's value + * if this $option is nonempty. + * + * This is equivalent to: + * {{{ + * option match { + * case Some(x) => Left(x) + * case None => Right(right) + * } + * }}} + * @param right the expression to evaluate and return if this is empty + * @see toRight + */ + @inline final def toLeft[X](right: => X): Either[A, X] = + if (isEmpty) Right(right) else Left(this.get) +} + +/** Class `Some[A]` represents existing values of type + * `A`. + */ +@SerialVersionUID(1234815782226070388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4 +final case class Some[+A](value: A) extends Option[A] { + def get: A = value +} + + +/** This case object represents non-existent values. + */ +@SerialVersionUID(5066590221178148012L) // value computed by serialver for 2.11.2, annotation added in 2.11.4 +case object None extends Option[Nothing] { + def get: Nothing = throw new NoSuchElementException("None.get") +} diff --git a/library/src/scala/PartialFunction.scala b/library/src/scala/PartialFunction.scala new file mode 100644 index 000000000000..992a7972921f --- /dev/null +++ b/library/src/scala/PartialFunction.scala @@ -0,0 +1,403 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.nowarn + +/** A partial function of type `PartialFunction[A, B]` is a unary function + * where the domain does not necessarily include all values of type `A`. + * The function [[isDefinedAt]] allows to test dynamically if a value is in + * the domain of the function. + * + * Even if `isDefinedAt` returns true for an `a: A`, calling `apply(a)` may + * still throw an exception, so the following code is legal: + * + * {{{ + * val f: PartialFunction[Int, Any] = { case x => x / 0 } // ArithmeticException: / by zero + * }}} + * + * It is the responsibility of the caller to call `isDefinedAt` before + * calling `apply`, because if `isDefinedAt` is false, it is not guaranteed + * `apply` will throw an exception to indicate an error condition. If an + * exception is not thrown, evaluation may result in an arbitrary value. + * + * The usual way to respect this contract is to call [[applyOrElse]], + * which is expected to be more efficient than calling both `isDefinedAt` + * and `apply`. + * + * The main distinction between `PartialFunction` and [[scala.Function1]] is + * that the user of a `PartialFunction` may choose to do something different + * with input that is declared to be outside its domain. For example: + * + * {{{ + * val sample = 1 to 10 + * def isEven(n: Int) = n % 2 == 0 + * val eveningNews: PartialFunction[Int, String] = { + * case x if isEven(x) => s"\$x is even" + * } + * + * // The method collect is described as "filter + map" + * // because it uses a PartialFunction to select elements + * // to which the function is applied. + * val evenNumbers = sample.collect(eveningNews) + * + * val oddlyEnough: PartialFunction[Int, String] = { + * case x if !isEven(x) => s"\$x is odd" + * } + * + * // The method orElse allows chaining another PartialFunction + * // to handle input outside the declared domain. + * val numbers = sample.map(eveningNews orElse oddlyEnough) + * + * // same as + * val numbers = sample.map(n => eveningNews.applyOrElse(n, oddlyEnough)) + * + * val half: PartialFunction[Int, Int] = { + * case x if isEven(x) => x / 2 + * } + * + * // Calculating the domain of a composition can be expensive. + * val oddByHalf = half.andThen(oddlyEnough) + * + * // Invokes `half.apply` on even elements! + * val oddBalls = sample.filter(oddByHalf.isDefinedAt) + * + * // Better than filter(oddByHalf.isDefinedAt).map(oddByHalf) + * val oddBalls = sample.collect(oddByHalf) + * + * // Providing "default" values. + * val oddsAndEnds = sample.map(n => oddByHalf.applyOrElse(n, (i: Int) => s"[\$i]")) + * }}} + * + * @note Optional [[Function]]s, [[PartialFunction]]s and extractor objects + * can be converted to each other as shown in the following table. + *   + * | How to convert ... | to a [[PartialFunction]] | to an optional [[Function]] | to an extractor | + * | :---: | --- | --- | --- | + * | from a [[PartialFunction]] | [[Predef.identity]] | [[lift]] | [[Predef.identity]] | + * | from optional [[Function]] | [[Function1.UnliftOps#unlift]] or [[Function.unlift]] | [[Predef.identity]] | [[Function1.UnliftOps#unlift]] | + * | from an extractor | `{ case extractor(x) => x }` | `extractor.unapply _` | [[Predef.identity]] | + *   + * + * @define applyOrElseOrElse Note that calling [[isDefinedAt]] on the resulting partial function + * may apply the first partial function and execute its side effect. + * For efficiency, it is recommended to call [[applyOrElse]] instead of [[isDefinedAt]] or [[apply]]. + */ +trait PartialFunction[-A, +B] extends (A => B) { self => + import PartialFunction._ + + /** Tries to extract a `B` from an `A` in a pattern matching expression. */ + def unapply(a: A): Option[B] = lift(a) + + /** Returns an extractor object with a `unapplySeq` method, which extracts each element of a sequence data. + * + * @example {{{ + * val firstChar: String => Option[Char] = _.headOption + * + * Seq("foo", "bar", "baz") match { + * case firstChar.unlift.elementWise(c0, c1, c2) => + * println(s"\$c0, \$c1, \$c2") // Output: f, b, b + * } + * }}} + */ + def elementWise: ElementWiseExtractor[A, B] = new ElementWiseExtractor[A, B](this) + + /** Checks if a value is contained in the function's domain. + * + * @param x the value to test + * @return `'''true'''`, iff `x` is in the domain of this function, `'''false'''` otherwise. + */ + def isDefinedAt(x: A): Boolean + + /** Composes this partial function with a fallback partial function which + * gets applied where this partial function is not defined. + * + * @param that the fallback function + * @tparam A1 the argument type of the fallback function + * @tparam B1 the result type of the fallback function + * @return a partial function which has as domain the union of the domains + * of this partial function and `that`. The resulting partial function + * takes `x` to `this(x)` where `this` is defined, and to `that(x)` where it is not. + */ + def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = + new OrElse[A1, B1] (this, that) + //TODO: why not overload it with orElse(that: F1): F1? + + /** Composes this partial function with a transformation function that + * gets applied to results of this partial function. + * + * If the runtime type of the function is a `PartialFunction` then the + * other `andThen` method is used (note its cautions). + * + * @param k the transformation function + * @tparam C the result type of the transformation function. + * @return a partial function with the domain of this partial function, + * possibly narrowed by the specified function, which maps + * arguments `x` to `k(this(x))`. + */ + override def andThen[C](k: B => C): PartialFunction[A, C] = k match { + case pf: PartialFunction[B, C] => andThen(pf) + case _ => new AndThen[A, B, C](this, k) + } + + /** + * Composes this partial function with another partial function that + * gets applied to results of this partial function. + * + * $applyOrElseOrElse + * + * @param k the transformation function + * @tparam C the result type of the transformation function. + * @return a partial function with the domain of this partial function narrowed by + * other partial function, which maps arguments `x` to `k(this(x))`. + */ + def andThen[C](k: PartialFunction[B, C]): PartialFunction[A, C] = + new Combined[A, B, C](this, k) + + /** + * Composes another partial function `k` with this partial function so that this + * partial function gets applied to results of `k`. + * + * $applyOrElseOrElse + * + * @param k the transformation function + * @tparam R the parameter type of the transformation function. + * @return a partial function with the domain of other partial function narrowed by + * this partial function, which maps arguments `x` to `this(k(x))`. + */ + def compose[R](k: PartialFunction[R, A]): PartialFunction[R, B] = + new Combined[R, A, B](k, this) + + /** Turns this partial function into a plain function returning an `Option` result. + * @see Function.unlift + * @return a function that takes an argument `x` to `Some(this(x))` if `this` + * is defined for `x`, and to `None` otherwise. + */ + def lift: A => Option[B] = new Lifted(this) + + /** Applies this partial function to the given argument when it is contained in the function domain. + * Applies fallback function where this partial function is not defined. + * + * Note that expression `pf.applyOrElse(x, default)` is equivalent to + * {{{ if(pf isDefinedAt x) pf(x) else default(x) }}} + * except that `applyOrElse` method can be implemented more efficiently. + * For all partial function literals the compiler generates an `applyOrElse` implementation which + * avoids double evaluation of pattern matchers and guards. + * This makes `applyOrElse` the basis for the efficient implementation for many operations and scenarios, such as: + * + * - combining partial functions into `orElse`/`andThen` chains does not lead to + * excessive `apply`/`isDefinedAt` evaluation + * - `lift` and `unlift` do not evaluate source functions twice on each invocation + * - `runWith` allows efficient imperative-style combining of partial functions + * with conditionally applied actions + * + * For non-literal partial function classes with nontrivial `isDefinedAt` method + * it is recommended to override `applyOrElse` with custom implementation that avoids + * double `isDefinedAt` evaluation. This may result in better performance + * and more predictable behavior w.r.t. side effects. + * + * @param x the function argument + * @param default the fallback function + * @return the result of this function or fallback function application. + */ + def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = + if (isDefinedAt(x)) apply(x) else default(x) + + /** Composes this partial function with an action function which + * gets applied to results of this partial function. + * The action function is invoked only for its side effects; its result is ignored. + * + * Note that expression `pf.runWith(action)(x)` is equivalent to + * {{{ if(pf isDefinedAt x) { action(pf(x)); true } else false }}} + * except that `runWith` is implemented via `applyOrElse` and thus potentially more efficient. + * Using `runWith` avoids double evaluation of pattern matchers and guards for partial function literals. + * @see `applyOrElse`. + * + * @param action the action function + * @return a function which maps arguments `x` to `isDefinedAt(x)`. The resulting function + * runs `action(this(x))` where `this` is defined. + */ + def runWith[U](action: B => U): A => Boolean = { x => + val z = applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) { action(z); true } else false + } +} + +/** A few handy operations which leverage the extra bit of information + * available in partial functions. Examples: + * {{{ + * import PartialFunction._ + * + * def strangeConditional(other: Any): Boolean = cond(other) { + * case x: String if x == "abc" || x == "def" => true + * case x: Int => true + * } + * def onlyInt(v: Any): Option[Int] = condOpt(v) { case x: Int => x } + * }}} + */ +object PartialFunction { + + final class ElementWiseExtractor[-A, +B] private[PartialFunction] (private val pf: PartialFunction[A, B]) extends AnyVal { + @nowarn("cat=lint-nonlocal-return") + def unapplySeq(seq: Seq[A]): Option[Seq[B]] = { + Some(seq.map { + case pf(b) => b + case _ => return None + }) + } + } + + /** Composite function produced by `PartialFunction#orElse` method + */ + private class OrElse[-A, +B] (f1: PartialFunction[A, B], f2: PartialFunction[A, B]) + extends scala.runtime.AbstractPartialFunction[A, B] with Serializable { + def isDefinedAt(x: A) = f1.isDefinedAt(x) || f2.isDefinedAt(x) + + override def apply(x: A): B = f1.applyOrElse(x, f2) + + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = { + val z = f1.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) z else f2.applyOrElse(x, default) + } + + override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): OrElse[A1, B1] = + new OrElse[A1, B1] (f1, f2 orElse that) + + override def andThen[C](k: B => C): OrElse[A, C] = + new OrElse[A, C] (f1 andThen k, f2 andThen k) + } + + /** Composite function produced by `PartialFunction#andThen` method + */ + private class AndThen[-A, B, +C] (pf: PartialFunction[A, B], k: B => C) extends PartialFunction[A, C] with Serializable { + def isDefinedAt(x: A) = pf.isDefinedAt(x) + + def apply(x: A): C = k(pf(x)) + + override def applyOrElse[A1 <: A, C1 >: C](x: A1, default: A1 => C1): C1 = { + val z = pf.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) k(z) else default(x) + } + } + + /** Composite function produced by `PartialFunction#andThen` method + */ + private class Combined[-A, B, +C] (pf: PartialFunction[A, B], k: PartialFunction[B, C]) extends PartialFunction[A, C] with Serializable { + def isDefinedAt(x: A): Boolean = { + val b: B = pf.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(b)) k.isDefinedAt(b) else false + } + + def apply(x: A): C = k(pf(x)) + + override def applyOrElse[A1 <: A, C1 >: C](x: A1, default: A1 => C1): C1 = { + val pfv = pf.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(pfv)) k.applyOrElse(pfv, (_: B) => default(x)) else default(x) + } + } + + /** To implement patterns like {{{ if(pf isDefinedAt x) f1(pf(x)) else f2(x) }}} efficiently + * the following trick is used: + * + * To avoid double evaluation of pattern matchers & guards `applyOrElse` method is used here + * instead of `isDefinedAt`/`apply` pair. + * + * After call to `applyOrElse` we need both the function result it returned and + * the fact if the function's argument was contained in its domain. The only degree of freedom we have here + * to achieve this goal is tweaking with the continuation argument (`default`) of `applyOrElse` method. + * The obvious way is to throw an exception from `default` function and to catch it after + * calling `applyOrElse` but I consider this somewhat inefficient. + * + * I know only one way how you can do this task efficiently: `default` function should return unique marker object + * which never may be returned by any other (regular/partial) function. This way after calling `applyOrElse` you need + * just one reference comparison to distinguish if `pf isDefined x` or not. + * + * This correctly interacts with specialization as return type of `applyOrElse` + * (which is parameterized upper bound) can never be specialized. + * + * Here `fallback_fn` is used as both unique marker object and special fallback function that returns it. + */ + private[this] val fallback_fn: Any => Any = _ => fallback_fn + private def checkFallback[B] = fallback_fn.asInstanceOf[Any => B] + private def fallbackOccurred[B](x: B) = fallback_fn eq x.asInstanceOf[AnyRef] + + private class Lifted[-A, +B] (val pf: PartialFunction[A, B]) + extends scala.runtime.AbstractFunction1[A, Option[B]] with Serializable { + + def apply(x: A): Option[B] = { + val z = pf.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) Some(z) else None + } + } + + private class Unlifted[A, B] (f: A => Option[B]) extends scala.runtime.AbstractPartialFunction[A, B] with Serializable { + def isDefinedAt(x: A): Boolean = f(x).isDefined + + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = { + f(x).getOrElse(default(x)) + } + + override def lift = f + } + + private[scala] def unlifted[A, B](f: A => Option[B]): PartialFunction[A, B] = f match { + case lf: Lifted[A, B] => lf.pf + case ff => new Unlifted(ff) + } + + /** Converts an ordinary function to a partial function. Note that calling `isDefinedAt(x)` on + * this partial function will return `true` for every `x`. + * @param f an ordinary function + * @return a partial function which delegates to the ordinary function `f` + */ + def fromFunction[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) } + + private[this] val constFalse: Any => Boolean = { _ => false} + + private[this] val empty_pf: PartialFunction[Any, Nothing] = new PartialFunction[Any, Nothing] with Serializable { + def isDefinedAt(x: Any) = false + def apply(x: Any) = throw new MatchError(x) + override def orElse[A1, B1](that: PartialFunction[A1, B1]) = that + override def andThen[C](k: Nothing => C): PartialFunction[Any, Nothing] = this + override val lift: Any => None.type = (x: Any) => None + override def runWith[U](action: Nothing => U) = constFalse + } + + /** The partial function with empty domain. + * Any attempt to invoke empty partial function leads to throwing [[scala.MatchError]] exception. + */ + def empty[A, B] : PartialFunction[A, B] = empty_pf + + /** A Boolean test that is the result of the given function where defined, + * and false otherwise. + * + * It behaves like a `case _ => false` were added to the partial function. + * + * @param x the value to test + * @param pf the partial function + * @return true, iff `x` is in the domain of `pf` and `pf(x) == true`. + */ + def cond[A](x: A)(pf: PartialFunction[A, Boolean]): Boolean = pf.applyOrElse(x, constFalse) + + /** Apply the function to the given value if defined, and return the result + * in a `Some`; otherwise, return `None`. + * + * @param x the value to test + * @param pf the PartialFunction[T, U] + * @return `Some(pf(x))` if `pf isDefinedAt x`, `None` otherwise. + */ + def condOpt[A, B](x: A)(pf: PartialFunction[A, B]): Option[B] = { + val z = pf.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) Some(z) else None + } +} diff --git a/library/src/scala/Predef.scala b/library/src/scala/Predef.scala new file mode 100644 index 000000000000..26dbc568a9ab --- /dev/null +++ b/library/src/scala/Predef.scala @@ -0,0 +1,586 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.language.implicitConversions + +import scala.collection.{mutable, immutable, ArrayOps, StringOps}, immutable.WrappedString +import scala.annotation.{elidable, implicitNotFound}, elidable.ASSERTION +import scala.annotation.meta.{ companionClass, companionMethod } + +/** The `Predef` object provides definitions that are accessible in all Scala + * compilation units without explicit qualification. + * + * === Commonly Used Types === + * Predef provides type aliases for types which are commonly used, such as + * the immutable collection types [[scala.collection.immutable.Map]] and + * [[scala.collection.immutable.Set]]. + * + * === Console Output === + * For basic console output, `Predef` provides convenience methods [[print(x:Any* print]] and [[println(x:Any* println]], + * which are aliases of the methods in the object [[scala.Console]]. + * + * === Assertions === + * A set of `assert` functions are provided for use as a way to document + * and dynamically check invariants in code. Invocations of `assert` can be elided + * at compile time by providing the command line option `-Xdisable-assertions`, + * which raises `-Xelide-below` above `elidable.ASSERTION`, to the `scalac` command. + * + * Variants of `assert` intended for use with static analysis tools are also + * provided: `assume`, `require` and `ensuring`. `require` and `ensuring` are + * intended for use as a means of design-by-contract style specification + * of pre- and post-conditions on functions, with the intention that these + * specifications could be consumed by a static analysis tool. For instance, + * + * {{{ + * def addNaturals(nats: List[Int]): Int = { + * require(nats forall (_ >= 0), "List contains negative numbers") + * nats.foldLeft(0)(_ + _) + * } ensuring(_ >= 0) + * }}} + * + * The declaration of `addNaturals` states that the list of integers passed should + * only contain natural numbers (i.e. non-negative), and that the result returned + * will also be natural. `require` is distinct from `assert` in that if the + * condition fails, then the caller of the function is to blame rather than a + * logical error having been made within `addNaturals` itself. `ensuring` is a + * form of `assert` that declares the guarantee the function is providing with + * regards to its return value. + * + * === Implicit Conversions === + * A number of commonly applied implicit conversions are also defined here, and + * in the parent type [[scala.LowPriorityImplicits]]. Implicit conversions + * are provided for the "widening" of numeric values, for instance, converting a + * Short value to a Long value as required, and to add additional higher-order + * functions to Array values. These are described in more detail in the documentation of [[scala.Array]]. + * + * @groupname utilities Utility Methods + * @groupprio utilities 10 + * + * @groupname assertions Assertions + * @groupprio assertions 20 + * @groupdesc assertions These methods support program verification and runtime correctness. + * + * @groupname console-output Console Output + * @groupprio console-output 30 + * @groupdesc console-output These methods provide output via the console. + * + * @groupname aliases Aliases + * @groupprio aliases 50 + * @groupdesc aliases These aliases bring selected immutable types into scope without any imports. + * + * @groupname conversions-string String Conversions + * @groupprio conversions-string 60 + * @groupdesc conversions-string Conversions from String to StringOps or WrappedString. + * + * @groupname implicit-classes-any Implicit Classes + * @groupprio implicit-classes-any 70 + * @groupdesc implicit-classes-any These implicit classes add useful extension methods to every type. + * + * @groupname char-sequence-wrappers CharSequence Wrappers + * @groupprio char-sequence-wrappers 80 + * @groupdesc char-sequence-wrappers Wrappers that implements CharSequence and were implicit classes. + * + * @groupname conversions-java-to-anyval Java to Scala + * @groupprio conversions-java-to-anyval 90 + * @groupdesc conversions-java-to-anyval Implicit conversion from Java primitive wrapper types to Scala equivalents. + * + * @groupname conversions-anyval-to-java Scala to Java + * @groupprio conversions-anyval-to-java 100 + * @groupdesc conversions-anyval-to-java Implicit conversion from Scala AnyVals to Java primitive wrapper types equivalents. + * + * @groupname conversions-array-to-wrapped-array Array to ArraySeq + * @groupprio conversions-array-to-wrapped-array 110 + * @groupdesc conversions-array-to-wrapped-array Conversions from Arrays to ArraySeqs. + */ +object Predef extends LowPriorityImplicits { + /** + * Retrieve the runtime representation of a class type. `classOf[T]` is equivalent to + * the class literal `T.class` in Java. + * + * @example {{{ + * val listClass = classOf[List[_]] + * // listClass is java.lang.Class[List[_]] = class scala.collection.immutable.List + * + * val mapIntString = classOf[Map[Int,String]] + * // mapIntString is java.lang.Class[Map[Int,String]] = interface scala.collection.immutable.Map + * }}} + * + * @return The runtime [[Class]] representation of type `T`. + * @group utilities + */ + def classOf[T]: Class[T] = null // This is a stub method. The actual implementation is filled in by the compiler. + + /** + * Retrieve the single value of a type with a unique inhabitant. + * + * @example {{{ + * object Foo + * val foo = valueOf[Foo.type] + * // foo is Foo.type = Foo + * + * val bar = valueOf[23] + * // bar is 23.type = 23 + * }}} + * @group utilities + */ + @inline def valueOf[T](implicit vt: ValueOf[T]): T = vt.value + + /** The `String` type in Scala has all the methods of the underlying + * [[java.lang.String]], of which it is just an alias. + * + * In addition, extension methods in [[scala.collection.StringOps]] + * are added implicitly through the conversion [[augmentString]]. + * @group aliases + */ + type String = java.lang.String + /** @group aliases */ + type Class[T] = java.lang.Class[T] + + // miscellaneous ----------------------------------------------------- + scala.`package` // to force scala package object to be seen. + scala.collection.immutable.List // to force Nil, :: to be seen. + + /** @group aliases */ + type Function[-A, +B] = Function1[A, B] + + /** @group aliases */ + type Map[K, +V] = immutable.Map[K, V] + /** @group aliases */ + type Set[A] = immutable.Set[A] + /** @group aliases */ + val Map = immutable.Map + /** @group aliases */ + val Set = immutable.Set + + /** + * Allows destructuring tuples with the same syntax as constructing them. + * + * @example {{{ + * val tup = "foobar" -> 3 + * + * val c = tup match { + * case str -> i => str.charAt(i) + * } + * }}} + * @group aliases + */ + val -> = Tuple2 + + // Manifest types, companions, and incantations for summoning + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("this notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") + type OptManifest[T] = scala.reflect.OptManifest[T] + @implicitNotFound(msg = "No Manifest available for ${T}.") + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("use `scala.reflect.ClassTag` (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") + type Manifest[T] = scala.reflect.Manifest[T] + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("use `scala.reflect.ClassTag` (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") + val Manifest = scala.reflect.Manifest + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("this notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") + val NoManifest = scala.reflect.NoManifest + + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("use scala.reflect.classTag[T] and scala.reflect.runtime.universe.typeTag[T] instead", "2.10.0") + def manifest[T](implicit m: Manifest[T]): Manifest[T] = m + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("this notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") + def optManifest[T](implicit m: OptManifest[T]): OptManifest[T] = m + + // Minor variations on identity functions + + /** + * A method that returns its input value. + * @tparam A type of the input value x. + * @param x the value of type `A` to be returned. + * @return the value `x`. + * @group utilities */ + @inline def identity[A](x: A): A = x // see `$conforms` for the implicit version + + /** Summon an implicit value of type `T`. Usually, the argument is not passed explicitly. + * + * @tparam T the type of the value to be summoned + * @return the implicit value of type `T` + * @group utilities + */ + @inline def implicitly[T](implicit e: T): T = e // TODO: when dependent method types are on by default, give this result type `e.type`, so that inliner has better chance of knowing which method to inline in calls like `implicitly[MatchingStrategy[Option]].zero` + + /** Used to mark code blocks as being expressions, instead of being taken as part of anonymous classes and the like. + * This is just a different name for [[identity]]. + * + * @example Separating code blocks from `new`: + * {{{ + * val x = new AnyRef + * { + * val y = ... + * println(y) + * } + * // the { ... } block is seen as the body of an anonymous class + * + * val x = new AnyRef + * + * { + * val y = ... + * println(y) + * } + * // an empty line is a brittle "fix" + * + * val x = new AnyRef + * locally { + * val y = ... + * println(y) + * } + * // locally guards the block and helps communicate intent + * }}} + * @group utilities + */ + @inline def locally[T](@deprecatedName("x") x: T): T = x + + // assertions --------------------------------------------------------- + + /** Tests an expression, throwing an `AssertionError` if false. + * Calls to this method will not be generated if `-Xelide-below` + * is greater than `ASSERTION`. + * + * @see [[scala.annotation.elidable elidable]] + * @param assertion the expression to test + * @group assertions + */ + @elidable(ASSERTION) + def assert(assertion: Boolean): Unit = { + if (!assertion) + throw new java.lang.AssertionError("assertion failed") + } + + /** Tests an expression, throwing an `AssertionError` if false. + * Calls to this method will not be generated if `-Xelide-below` + * is greater than `ASSERTION`. + * + * @see [[scala.annotation.elidable elidable]] + * @param assertion the expression to test + * @param message a String to include in the failure message + * @group assertions + */ + @elidable(ASSERTION) @inline + final def assert(assertion: Boolean, message: => Any): Unit = { + if (!assertion) + throw new java.lang.AssertionError("assertion failed: "+ message) + } + + /** Tests an expression, throwing an `AssertionError` if false. + * This method differs from assert only in the intent expressed: + * assert contains a predicate which needs to be proven, while + * assume contains an axiom for a static checker. Calls to this method + * will not be generated if `-Xelide-below` is greater than `ASSERTION`. + * + * @see [[scala.annotation.elidable elidable]] + * @param assumption the expression to test + * @group assertions + */ + @elidable(ASSERTION) + def assume(assumption: Boolean): Unit = { + if (!assumption) + throw new java.lang.AssertionError("assumption failed") + } + + /** Tests an expression, throwing an `AssertionError` if false. + * This method differs from assert only in the intent expressed: + * assert contains a predicate which needs to be proven, while + * assume contains an axiom for a static checker. Calls to this method + * will not be generated if `-Xelide-below` is greater than `ASSERTION`. + * + * @see [[scala.annotation.elidable elidable]] + * @param assumption the expression to test + * @param message a String to include in the failure message + * @group assertions + */ + @elidable(ASSERTION) @inline + final def assume(assumption: Boolean, message: => Any): Unit = { + if (!assumption) + throw new java.lang.AssertionError("assumption failed: "+ message) + } + + /** Tests an expression, throwing an `IllegalArgumentException` if false. + * This method is similar to `assert`, but blames the caller of the method + * for violating the condition. + * + * @param requirement the expression to test + * @group assertions + */ + def require(requirement: Boolean): Unit = { + if (!requirement) + throw new IllegalArgumentException("requirement failed") + } + + /** Tests an expression, throwing an `IllegalArgumentException` if false. + * This method is similar to `assert`, but blames the caller of the method + * for violating the condition. + * + * @param requirement the expression to test + * @param message a String to include in the failure message + * @group assertions + */ + @inline final def require(requirement: Boolean, message: => Any): Unit = { + if (!requirement) + throw new IllegalArgumentException("requirement failed: "+ message) + } + + /** `???` can be used for marking methods that remain to be implemented. + * @throws NotImplementedError when `???` is invoked. + * @group utilities + */ + def ??? : Nothing = throw new NotImplementedError + + // implicit classes ----------------------------------------------------- + + /** @group implicit-classes-any */ + implicit final class ArrowAssoc[A](private val self: A) extends AnyVal { + @inline def -> [B](y: B): (A, B) = (self, y) + @deprecated("Use `->` instead. If you still wish to display it as one character, consider using a font with programming ligatures such as Fira Code.", "2.13.0") + def →[B](y: B): (A, B) = ->(y) + } + + /** @group implicit-classes-any */ + implicit final class Ensuring[A](private val self: A) extends AnyVal { + def ensuring(cond: Boolean): A = { assert(cond); self } + def ensuring(cond: Boolean, msg: => Any): A = { assert(cond, msg); self } + def ensuring(cond: A => Boolean): A = { assert(cond(self)); self } + def ensuring(cond: A => Boolean, msg: => Any): A = { assert(cond(self), msg); self } + } + + /** @group implicit-classes-any */ + implicit final class StringFormat[A](private val self: A) extends AnyVal { + /** Returns string formatted according to given `format` string. + * Format strings are as for `String.format` + * (@see java.lang.String.format). + */ + @deprecated("Use `formatString.format(value)` instead of `value.formatted(formatString)`,\nor use the `f\"\"` string interpolator. In Java 15 and later, `formatted` resolves to the new method in String which has reversed parameters.", "2.12.16") + @inline def formatted(fmtstr: String): String = fmtstr format self + } + + /** Injects String concatenation operator `+` to any classes. + * @group implicit-classes-any + */ + @(deprecated @companionMethod)("Implicit injection of + is deprecated. Convert to String to call +", "2.13.0") + @(deprecated @companionClass)("Implicit injection of + is deprecated. Convert to String to call +", "2.13.0") // for Scaladoc + // scala/bug#8229 retaining the pre 2.11 name for source compatibility in shadowing this implicit + implicit final class any2stringadd[A](private val self: A) extends AnyVal { + def +(other: String): String = String.valueOf(self) + other + } + + /** @group char-sequence-wrappers */ + final class SeqCharSequence(sequenceOfChars: scala.collection.IndexedSeq[Char]) extends CharSequence { + def length: Int = sequenceOfChars.length + def charAt(index: Int): Char = sequenceOfChars(index) + def subSequence(start: Int, end: Int): CharSequence = new SeqCharSequence(sequenceOfChars.slice(start, end)) + override def toString = sequenceOfChars.mkString + } + + /** @group char-sequence-wrappers */ + def SeqCharSequence(sequenceOfChars: scala.collection.IndexedSeq[Char]): SeqCharSequence = new SeqCharSequence(sequenceOfChars) + + /** @group char-sequence-wrappers */ + final class ArrayCharSequence(arrayOfChars: Array[Char]) extends CharSequence { + def length: Int = arrayOfChars.length + def charAt(index: Int): Char = arrayOfChars(index) + def subSequence(start: Int, end: Int): CharSequence = new runtime.ArrayCharSequence(arrayOfChars, start, end) + override def toString = arrayOfChars.mkString + } + + /** @group char-sequence-wrappers */ + def ArrayCharSequence(arrayOfChars: Array[Char]): ArrayCharSequence = new ArrayCharSequence(arrayOfChars) + + /** @group conversions-string */ + @inline implicit def augmentString(x: String): StringOps = new StringOps(x) + + // printing ----------------------------------------------------------- + + /** Prints an object to `out` using its `toString` method. + * + * @param x the object to print; may be null. + * @group console-output + */ + def print(x: Any): Unit = Console.print(x) + + /** Prints a newline character on the default output. + * @group console-output + */ + def println(): Unit = Console.println() + + /** Prints out an object to the default output, followed by a newline character. + * + * @param x the object to print. + * @group console-output + */ + def println(x: Any): Unit = Console.println(x) + + /** Prints its arguments as a formatted string to the default output, + * based on a string pattern (in a fashion similar to printf in C). + * + * The interpretation of the formatting patterns is described in + * [[java.util.Formatter]]. + * + * Consider using the [[scala.StringContext.f f interpolator]] as more type safe and idiomatic. + * + * @param text the pattern for formatting the arguments. + * @param xs the arguments used to instantiate the pattern. + * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments + * + * @see [[scala.StringContext.f StringContext.f]] + * @group console-output + */ + def printf(text: String, xs: Any*): Unit = Console.print(text.format(xs: _*)) + + // views -------------------------------------------------------------- + + // these two are morally deprecated but the @deprecated annotation has been moved to the extension method themselves, + // in order to provide a more specific deprecation method. + implicit def tuple2ToZippedOps[T1, T2](x: (T1, T2)): runtime.Tuple2Zipped.Ops[T1, T2] = new runtime.Tuple2Zipped.Ops(x) + implicit def tuple3ToZippedOps[T1, T2, T3](x: (T1, T2, T3)): runtime.Tuple3Zipped.Ops[T1, T2, T3] = new runtime.Tuple3Zipped.Ops(x) + + // Not specialized anymore since 2.13 but we still need separate methods + // to avoid https://github.com/scala/bug/issues/10746 + // TODO: should not need @inline. add heuristic to inline factories for value classes. + @inline implicit def genericArrayOps[T](xs: Array[T]): ArrayOps[T] = new ArrayOps(xs) + @inline implicit def booleanArrayOps(xs: Array[Boolean]): ArrayOps[Boolean] = new ArrayOps(xs) + @inline implicit def byteArrayOps(xs: Array[Byte]): ArrayOps[Byte] = new ArrayOps(xs) + @inline implicit def charArrayOps(xs: Array[Char]): ArrayOps[Char] = new ArrayOps(xs) + @inline implicit def doubleArrayOps(xs: Array[Double]): ArrayOps[Double] = new ArrayOps(xs) + @inline implicit def floatArrayOps(xs: Array[Float]): ArrayOps[Float] = new ArrayOps(xs) + @inline implicit def intArrayOps(xs: Array[Int]): ArrayOps[Int] = new ArrayOps(xs) + @inline implicit def longArrayOps(xs: Array[Long]): ArrayOps[Long] = new ArrayOps(xs) + @inline implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T] = new ArrayOps(xs) + @inline implicit def shortArrayOps(xs: Array[Short]): ArrayOps[Short] = new ArrayOps(xs) + @inline implicit def unitArrayOps(xs: Array[Unit]): ArrayOps[Unit] = new ArrayOps(xs) + + // "Autoboxing" and "Autounboxing" --------------------------------------------------- + + /** @group conversions-anyval-to-java */ + implicit def byte2Byte(x: Byte): java.lang.Byte = x.asInstanceOf[java.lang.Byte] + /** @group conversions-anyval-to-java */ + implicit def short2Short(x: Short): java.lang.Short = x.asInstanceOf[java.lang.Short] + /** @group conversions-anyval-to-java */ + implicit def char2Character(x: Char): java.lang.Character = x.asInstanceOf[java.lang.Character] + /** @group conversions-anyval-to-java */ + implicit def int2Integer(x: Int): java.lang.Integer = x.asInstanceOf[java.lang.Integer] + /** @group conversions-anyval-to-java */ + implicit def long2Long(x: Long): java.lang.Long = x.asInstanceOf[java.lang.Long] + /** @group conversions-anyval-to-java */ + implicit def float2Float(x: Float): java.lang.Float = x.asInstanceOf[java.lang.Float] + /** @group conversions-anyval-to-java */ + implicit def double2Double(x: Double): java.lang.Double = x.asInstanceOf[java.lang.Double] + /** @group conversions-anyval-to-java */ + implicit def boolean2Boolean(x: Boolean): java.lang.Boolean = x.asInstanceOf[java.lang.Boolean] + + /** @group conversions-java-to-anyval */ + implicit def Byte2byte(x: java.lang.Byte): Byte = x.asInstanceOf[Byte] + /** @group conversions-java-to-anyval */ + implicit def Short2short(x: java.lang.Short): Short = x.asInstanceOf[Short] + /** @group conversions-java-to-anyval */ + implicit def Character2char(x: java.lang.Character): Char = x.asInstanceOf[Char] + /** @group conversions-java-to-anyval */ + implicit def Integer2int(x: java.lang.Integer): Int = x.asInstanceOf[Int] + /** @group conversions-java-to-anyval */ + implicit def Long2long(x: java.lang.Long): Long = x.asInstanceOf[Long] + /** @group conversions-java-to-anyval */ + implicit def Float2float(x: java.lang.Float): Float = x.asInstanceOf[Float] + /** @group conversions-java-to-anyval */ + implicit def Double2double(x: java.lang.Double): Double = x.asInstanceOf[Double] + /** @group conversions-java-to-anyval */ + implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.asInstanceOf[Boolean] + + /** An implicit of type `A => A` is available for all `A` because it can always + * be implemented using the identity function. This also means that an + * implicit of type `A => B` is always available when `A <: B`, because + * `(A => A) <: (A => B)`. + */ + // $ to avoid accidental shadowing (e.g. scala/bug#7788) + implicit def $conforms[A]: A => A = <:<.refl +} + +/** The `LowPriorityImplicits` class provides implicit values that +* are valid in all Scala compilation units without explicit qualification, +* but that are partially overridden by higher-priority conversions in object +* `Predef`. +*/ +// scala/bug#7335 Parents of Predef are defined in the same compilation unit to avoid +// cyclic reference errors compiling the standard library *without* a previously +// compiled copy on the classpath. +private[scala] abstract class LowPriorityImplicits extends LowPriorityImplicits2 { + import mutable.ArraySeq + + /** We prefer the java.lang.* boxed types to these wrappers in + * any potential conflicts. Conflicts do exist because the wrappers + * need to implement ScalaNumber in order to have a symmetric equals + * method, but that implies implementing java.lang.Number as well. + * + * Note - these are inlined because they are value classes, but + * the call to xxxWrapper is not eliminated even though it does nothing. + * Even inlined, every call site does a no-op retrieval of Predef's MODULE$ + * because maybe loading Predef has side effects! + */ + @inline implicit def byteWrapper(x: Byte): runtime.RichByte = new runtime.RichByte(x) + @inline implicit def shortWrapper(x: Short): runtime.RichShort = new runtime.RichShort(x) + @inline implicit def intWrapper(x: Int): runtime.RichInt = new runtime.RichInt(x) + @inline implicit def charWrapper(c: Char): runtime.RichChar = new runtime.RichChar(c) + @inline implicit def longWrapper(x: Long): runtime.RichLong = new runtime.RichLong(x) + @inline implicit def floatWrapper(x: Float): runtime.RichFloat = new runtime.RichFloat(x) + @inline implicit def doubleWrapper(x: Double): runtime.RichDouble = new runtime.RichDouble(x) + @inline implicit def booleanWrapper(x: Boolean): runtime.RichBoolean = new runtime.RichBoolean(x) + + /** @group conversions-array-to-wrapped-array */ + implicit def genericWrapArray[T](xs: Array[T]): ArraySeq[T] = + if (xs eq null) null + else ArraySeq.make(xs) + + // Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef] + // is as good as another for all T <: AnyRef. Instead of creating 100,000,000 + // unique ones by way of this implicit, let's share one. + /** @group conversions-array-to-wrapped-array */ + implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): ArraySeq.ofRef[T] = { + if (xs eq null) null + else if (xs.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq.ofRef[T]] + else new ArraySeq.ofRef[T](xs) + } + + /** @group conversions-array-to-wrapped-array */ + implicit def wrapIntArray(xs: Array[Int]): ArraySeq.ofInt = if (xs ne null) new ArraySeq.ofInt(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapDoubleArray(xs: Array[Double]): ArraySeq.ofDouble = if (xs ne null) new ArraySeq.ofDouble(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapLongArray(xs: Array[Long]): ArraySeq.ofLong = if (xs ne null) new ArraySeq.ofLong(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapFloatArray(xs: Array[Float]): ArraySeq.ofFloat = if (xs ne null) new ArraySeq.ofFloat(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapCharArray(xs: Array[Char]): ArraySeq.ofChar = if (xs ne null) new ArraySeq.ofChar(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapByteArray(xs: Array[Byte]): ArraySeq.ofByte = if (xs ne null) new ArraySeq.ofByte(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapShortArray(xs: Array[Short]): ArraySeq.ofShort = if (xs ne null) new ArraySeq.ofShort(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapBooleanArray(xs: Array[Boolean]): ArraySeq.ofBoolean = if (xs ne null) new ArraySeq.ofBoolean(xs) else null + /** @group conversions-array-to-wrapped-array */ + implicit def wrapUnitArray(xs: Array[Unit]): ArraySeq.ofUnit = if (xs ne null) new ArraySeq.ofUnit(xs) else null + + /** @group conversions-string */ + implicit def wrapString(s: String): WrappedString = if (s ne null) new WrappedString(s) else null +} + +private[scala] abstract class LowPriorityImplicits2 { + @deprecated("implicit conversions from Array to immutable.IndexedSeq are implemented by copying; use `toIndexedSeq` explicitly if you want to copy, or use the more efficient non-copying ArraySeq.unsafeWrapArray", since="2.13.0") + implicit def copyArrayToImmutableIndexedSeq[T](xs: Array[T]): IndexedSeq[T] = + if (xs eq null) null + else new ArrayOps(xs).toIndexedSeq +} diff --git a/library/src/scala/Product.scala b/library/src/scala/Product.scala new file mode 100644 index 000000000000..c0fa80a95ef5 --- /dev/null +++ b/library/src/scala/Product.scala @@ -0,0 +1,72 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** Base trait for all products, which in the standard library include at + * least [[scala.Product1]] through [[scala.Product22]] and therefore also + * their subclasses [[scala.Tuple1]] through [[scala.Tuple22]]. In addition, + * all case classes implement `Product` with synthetically generated methods. + */ +trait Product extends Any with Equals { + /** The size of this product. + * @return for a product `A(x,,1,,, ..., x,,k,,)`, returns `k` + */ + def productArity: Int + + /** The n^th^ element of this product, 0-based. In other words, for a + * product `A(x,,1,,, ..., x,,k,,)`, returns `x,,(n+1),,` where `0 <= n < k`. + * + * @param n the index of the element to return + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= productArity). + * @return the element `n` elements after the first element + */ + def productElement(n: Int): Any + + /** An iterator over all the elements of this product. + * @return in the default implementation, an `Iterator[Any]` + */ + def productIterator: Iterator[Any] = new scala.collection.AbstractIterator[Any] { + private[this] var c: Int = 0 + private[this] val cmax = productArity + def hasNext: Boolean = c < cmax + def next(): Any = { val result = productElement(c); c += 1; result } + } + + /** A string used in the `toString` methods of derived classes. + * Implementations may override this method to prepend a string prefix + * to the result of `toString` methods. + * + * @return in the default implementation, the empty string + */ + def productPrefix: String = "" + + /** The name of the n^th^ element of this product, 0-based. + * In the default implementation, an empty string. + * + * @param n the index of the element name to return + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= productArity). + * @return the name of the specified element + */ + def productElementName(n: Int): String = + if (n >= 0 && n < productArity) "" + else throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max ${productArity-1})") + + /** An iterator over the names of all the elements of this product. + */ + def productElementNames: Iterator[String] = new scala.collection.AbstractIterator[String] { + private[this] var c: Int = 0 + private[this] val cmax = productArity + def hasNext: Boolean = c < cmax + def next(): String = { val result = productElementName(c); c += 1; result } + } +} diff --git a/library/src/scala/Product1.scala b/library/src/scala/Product1.scala new file mode 100644 index 000000000000..912f4dc8f0d8 --- /dev/null +++ b/library/src/scala/Product1.scala @@ -0,0 +1,51 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product1 { + def unapply[T1](x: Product1[T1]): Option[Product1[T1]] = + Some(x) +} + +/** Product1 is a Cartesian product of 1 component. + */ +trait Product1[@specialized(Int, Long, Double) +T1] extends Any with Product { + /** The arity of this product. + * @return 1 + */ + override def productArity: Int = 1 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 1). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 0)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + + +} diff --git a/library/src/scala/Product10.scala b/library/src/scala/Product10.scala new file mode 100644 index 000000000000..8ab742e3458f --- /dev/null +++ b/library/src/scala/Product10.scala @@ -0,0 +1,96 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product10 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](x: Product10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): Option[Product10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] = + Some(x) +} + +/** Product10 is a Cartesian product of 10 components. + */ +trait Product10[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10] extends Any with Product { + /** The arity of this product. + * @return 10 + */ + override def productArity: Int = 10 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 10). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 9)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + + +} diff --git a/library/src/scala/Product11.scala b/library/src/scala/Product11.scala new file mode 100644 index 000000000000..c970235fc693 --- /dev/null +++ b/library/src/scala/Product11.scala @@ -0,0 +1,101 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product11 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](x: Product11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]): Option[Product11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] = + Some(x) +} + +/** Product11 is a Cartesian product of 11 components. + */ +trait Product11[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11] extends Any with Product { + /** The arity of this product. + * @return 11 + */ + override def productArity: Int = 11 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 11). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 10)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + + +} diff --git a/library/src/scala/Product12.scala b/library/src/scala/Product12.scala new file mode 100644 index 000000000000..2823288e430b --- /dev/null +++ b/library/src/scala/Product12.scala @@ -0,0 +1,106 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product12 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](x: Product12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]): Option[Product12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] = + Some(x) +} + +/** Product12 is a Cartesian product of 12 components. + */ +trait Product12[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12] extends Any with Product { + /** The arity of this product. + * @return 12 + */ + override def productArity: Int = 12 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 12). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 11)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + + +} diff --git a/library/src/scala/Product13.scala b/library/src/scala/Product13.scala new file mode 100644 index 000000000000..25e804081407 --- /dev/null +++ b/library/src/scala/Product13.scala @@ -0,0 +1,111 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product13 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](x: Product13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]): Option[Product13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] = + Some(x) +} + +/** Product13 is a Cartesian product of 13 components. + */ +trait Product13[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13] extends Any with Product { + /** The arity of this product. + * @return 13 + */ + override def productArity: Int = 13 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 13). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 12)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + + +} diff --git a/library/src/scala/Product14.scala b/library/src/scala/Product14.scala new file mode 100644 index 000000000000..76afd49e1856 --- /dev/null +++ b/library/src/scala/Product14.scala @@ -0,0 +1,116 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product14 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](x: Product14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]): Option[Product14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] = + Some(x) +} + +/** Product14 is a Cartesian product of 14 components. + */ +trait Product14[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14] extends Any with Product { + /** The arity of this product. + * @return 14 + */ + override def productArity: Int = 14 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 14). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 13)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + + +} diff --git a/library/src/scala/Product15.scala b/library/src/scala/Product15.scala new file mode 100644 index 000000000000..dd6e49a33eba --- /dev/null +++ b/library/src/scala/Product15.scala @@ -0,0 +1,121 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product15 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](x: Product15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]): Option[Product15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] = + Some(x) +} + +/** Product15 is a Cartesian product of 15 components. + */ +trait Product15[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15] extends Any with Product { + /** The arity of this product. + * @return 15 + */ + override def productArity: Int = 15 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 15). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 14)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + + +} diff --git a/library/src/scala/Product16.scala b/library/src/scala/Product16.scala new file mode 100644 index 000000000000..900ccdcab195 --- /dev/null +++ b/library/src/scala/Product16.scala @@ -0,0 +1,126 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product16 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](x: Product16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]): Option[Product16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]] = + Some(x) +} + +/** Product16 is a Cartesian product of 16 components. + */ +trait Product16[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16] extends Any with Product { + /** The arity of this product. + * @return 16 + */ + override def productArity: Int = 16 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 16). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case 15 => _16 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 15)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + /** A projection of element 16 of this Product. + * @return A projection of element 16. + */ + def _16: T16 + + +} diff --git a/library/src/scala/Product17.scala b/library/src/scala/Product17.scala new file mode 100644 index 000000000000..4e6636f2e5d1 --- /dev/null +++ b/library/src/scala/Product17.scala @@ -0,0 +1,131 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product17 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](x: Product17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]): Option[Product17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]] = + Some(x) +} + +/** Product17 is a Cartesian product of 17 components. + */ +trait Product17[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17] extends Any with Product { + /** The arity of this product. + * @return 17 + */ + override def productArity: Int = 17 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 17). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case 15 => _16 + case 16 => _17 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 16)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + /** A projection of element 16 of this Product. + * @return A projection of element 16. + */ + def _16: T16 + /** A projection of element 17 of this Product. + * @return A projection of element 17. + */ + def _17: T17 + + +} diff --git a/library/src/scala/Product18.scala b/library/src/scala/Product18.scala new file mode 100644 index 000000000000..4a68f49f5623 --- /dev/null +++ b/library/src/scala/Product18.scala @@ -0,0 +1,136 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product18 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](x: Product18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]): Option[Product18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]] = + Some(x) +} + +/** Product18 is a Cartesian product of 18 components. + */ +trait Product18[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18] extends Any with Product { + /** The arity of this product. + * @return 18 + */ + override def productArity: Int = 18 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 18). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case 15 => _16 + case 16 => _17 + case 17 => _18 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 17)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + /** A projection of element 16 of this Product. + * @return A projection of element 16. + */ + def _16: T16 + /** A projection of element 17 of this Product. + * @return A projection of element 17. + */ + def _17: T17 + /** A projection of element 18 of this Product. + * @return A projection of element 18. + */ + def _18: T18 + + +} diff --git a/library/src/scala/Product19.scala b/library/src/scala/Product19.scala new file mode 100644 index 000000000000..fdc4c232742d --- /dev/null +++ b/library/src/scala/Product19.scala @@ -0,0 +1,141 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product19 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](x: Product19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]): Option[Product19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]] = + Some(x) +} + +/** Product19 is a Cartesian product of 19 components. + */ +trait Product19[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19] extends Any with Product { + /** The arity of this product. + * @return 19 + */ + override def productArity: Int = 19 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 19). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case 15 => _16 + case 16 => _17 + case 17 => _18 + case 18 => _19 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 18)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + /** A projection of element 16 of this Product. + * @return A projection of element 16. + */ + def _16: T16 + /** A projection of element 17 of this Product. + * @return A projection of element 17. + */ + def _17: T17 + /** A projection of element 18 of this Product. + * @return A projection of element 18. + */ + def _18: T18 + /** A projection of element 19 of this Product. + * @return A projection of element 19. + */ + def _19: T19 + + +} diff --git a/library/src/scala/Product2.scala b/library/src/scala/Product2.scala new file mode 100644 index 000000000000..2498e9727b7f --- /dev/null +++ b/library/src/scala/Product2.scala @@ -0,0 +1,56 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product2 { + def unapply[T1, T2](x: Product2[T1, T2]): Option[Product2[T1, T2]] = + Some(x) +} + +/** Product2 is a Cartesian product of 2 components. + */ +trait Product2[@specialized(Int, Long, Double) +T1, @specialized(Int, Long, Double) +T2] extends Any with Product { + /** The arity of this product. + * @return 2 + */ + override def productArity: Int = 2 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 2). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 1)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + + +} diff --git a/library/src/scala/Product20.scala b/library/src/scala/Product20.scala new file mode 100644 index 000000000000..206dc1e375cc --- /dev/null +++ b/library/src/scala/Product20.scala @@ -0,0 +1,146 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product20 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](x: Product20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]): Option[Product20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]] = + Some(x) +} + +/** Product20 is a Cartesian product of 20 components. + */ +trait Product20[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20] extends Any with Product { + /** The arity of this product. + * @return 20 + */ + override def productArity: Int = 20 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 20). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case 15 => _16 + case 16 => _17 + case 17 => _18 + case 18 => _19 + case 19 => _20 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 19)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + /** A projection of element 16 of this Product. + * @return A projection of element 16. + */ + def _16: T16 + /** A projection of element 17 of this Product. + * @return A projection of element 17. + */ + def _17: T17 + /** A projection of element 18 of this Product. + * @return A projection of element 18. + */ + def _18: T18 + /** A projection of element 19 of this Product. + * @return A projection of element 19. + */ + def _19: T19 + /** A projection of element 20 of this Product. + * @return A projection of element 20. + */ + def _20: T20 + + +} diff --git a/library/src/scala/Product21.scala b/library/src/scala/Product21.scala new file mode 100644 index 000000000000..0cbb44068fc8 --- /dev/null +++ b/library/src/scala/Product21.scala @@ -0,0 +1,151 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product21 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](x: Product21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]): Option[Product21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]] = + Some(x) +} + +/** Product21 is a Cartesian product of 21 components. + */ +trait Product21[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20, +T21] extends Any with Product { + /** The arity of this product. + * @return 21 + */ + override def productArity: Int = 21 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 21). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case 15 => _16 + case 16 => _17 + case 17 => _18 + case 18 => _19 + case 19 => _20 + case 20 => _21 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 20)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + /** A projection of element 16 of this Product. + * @return A projection of element 16. + */ + def _16: T16 + /** A projection of element 17 of this Product. + * @return A projection of element 17. + */ + def _17: T17 + /** A projection of element 18 of this Product. + * @return A projection of element 18. + */ + def _18: T18 + /** A projection of element 19 of this Product. + * @return A projection of element 19. + */ + def _19: T19 + /** A projection of element 20 of this Product. + * @return A projection of element 20. + */ + def _20: T20 + /** A projection of element 21 of this Product. + * @return A projection of element 21. + */ + def _21: T21 + + +} diff --git a/library/src/scala/Product22.scala b/library/src/scala/Product22.scala new file mode 100644 index 000000000000..df6963c03843 --- /dev/null +++ b/library/src/scala/Product22.scala @@ -0,0 +1,156 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product22 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](x: Product22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]): Option[Product22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]] = + Some(x) +} + +/** Product22 is a Cartesian product of 22 components. + */ +trait Product22[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20, +T21, +T22] extends Any with Product { + /** The arity of this product. + * @return 22 + */ + override def productArity: Int = 22 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 22). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case 9 => _10 + case 10 => _11 + case 11 => _12 + case 12 => _13 + case 13 => _14 + case 14 => _15 + case 15 => _16 + case 16 => _17 + case 17 => _18 + case 18 => _19 + case 19 => _20 + case 20 => _21 + case 21 => _22 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 21)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + /** A projection of element 10 of this Product. + * @return A projection of element 10. + */ + def _10: T10 + /** A projection of element 11 of this Product. + * @return A projection of element 11. + */ + def _11: T11 + /** A projection of element 12 of this Product. + * @return A projection of element 12. + */ + def _12: T12 + /** A projection of element 13 of this Product. + * @return A projection of element 13. + */ + def _13: T13 + /** A projection of element 14 of this Product. + * @return A projection of element 14. + */ + def _14: T14 + /** A projection of element 15 of this Product. + * @return A projection of element 15. + */ + def _15: T15 + /** A projection of element 16 of this Product. + * @return A projection of element 16. + */ + def _16: T16 + /** A projection of element 17 of this Product. + * @return A projection of element 17. + */ + def _17: T17 + /** A projection of element 18 of this Product. + * @return A projection of element 18. + */ + def _18: T18 + /** A projection of element 19 of this Product. + * @return A projection of element 19. + */ + def _19: T19 + /** A projection of element 20 of this Product. + * @return A projection of element 20. + */ + def _20: T20 + /** A projection of element 21 of this Product. + * @return A projection of element 21. + */ + def _21: T21 + /** A projection of element 22 of this Product. + * @return A projection of element 22. + */ + def _22: T22 + + +} diff --git a/library/src/scala/Product3.scala b/library/src/scala/Product3.scala new file mode 100644 index 000000000000..48de4b6e7d20 --- /dev/null +++ b/library/src/scala/Product3.scala @@ -0,0 +1,61 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product3 { + def unapply[T1, T2, T3](x: Product3[T1, T2, T3]): Option[Product3[T1, T2, T3]] = + Some(x) +} + +/** Product3 is a Cartesian product of 3 components. + */ +trait Product3[+T1, +T2, +T3] extends Any with Product { + /** The arity of this product. + * @return 3 + */ + override def productArity: Int = 3 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 3). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 2)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + + +} diff --git a/library/src/scala/Product4.scala b/library/src/scala/Product4.scala new file mode 100644 index 000000000000..7b34b570f1f0 --- /dev/null +++ b/library/src/scala/Product4.scala @@ -0,0 +1,66 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product4 { + def unapply[T1, T2, T3, T4](x: Product4[T1, T2, T3, T4]): Option[Product4[T1, T2, T3, T4]] = + Some(x) +} + +/** Product4 is a Cartesian product of 4 components. + */ +trait Product4[+T1, +T2, +T3, +T4] extends Any with Product { + /** The arity of this product. + * @return 4 + */ + override def productArity: Int = 4 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 4). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 3)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + + +} diff --git a/library/src/scala/Product5.scala b/library/src/scala/Product5.scala new file mode 100644 index 000000000000..769e2f0b22d3 --- /dev/null +++ b/library/src/scala/Product5.scala @@ -0,0 +1,71 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product5 { + def unapply[T1, T2, T3, T4, T5](x: Product5[T1, T2, T3, T4, T5]): Option[Product5[T1, T2, T3, T4, T5]] = + Some(x) +} + +/** Product5 is a Cartesian product of 5 components. + */ +trait Product5[+T1, +T2, +T3, +T4, +T5] extends Any with Product { + /** The arity of this product. + * @return 5 + */ + override def productArity: Int = 5 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 5). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 4)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + + +} diff --git a/library/src/scala/Product6.scala b/library/src/scala/Product6.scala new file mode 100644 index 000000000000..aff1fbb92e46 --- /dev/null +++ b/library/src/scala/Product6.scala @@ -0,0 +1,76 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product6 { + def unapply[T1, T2, T3, T4, T5, T6](x: Product6[T1, T2, T3, T4, T5, T6]): Option[Product6[T1, T2, T3, T4, T5, T6]] = + Some(x) +} + +/** Product6 is a Cartesian product of 6 components. + */ +trait Product6[+T1, +T2, +T3, +T4, +T5, +T6] extends Any with Product { + /** The arity of this product. + * @return 6 + */ + override def productArity: Int = 6 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 6). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 5)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + + +} diff --git a/library/src/scala/Product7.scala b/library/src/scala/Product7.scala new file mode 100644 index 000000000000..7aef56fc53a6 --- /dev/null +++ b/library/src/scala/Product7.scala @@ -0,0 +1,81 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product7 { + def unapply[T1, T2, T3, T4, T5, T6, T7](x: Product7[T1, T2, T3, T4, T5, T6, T7]): Option[Product7[T1, T2, T3, T4, T5, T6, T7]] = + Some(x) +} + +/** Product7 is a Cartesian product of 7 components. + */ +trait Product7[+T1, +T2, +T3, +T4, +T5, +T6, +T7] extends Any with Product { + /** The arity of this product. + * @return 7 + */ + override def productArity: Int = 7 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 7). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 6)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + + +} diff --git a/library/src/scala/Product8.scala b/library/src/scala/Product8.scala new file mode 100644 index 000000000000..f8604b887358 --- /dev/null +++ b/library/src/scala/Product8.scala @@ -0,0 +1,86 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product8 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8](x: Product8[T1, T2, T3, T4, T5, T6, T7, T8]): Option[Product8[T1, T2, T3, T4, T5, T6, T7, T8]] = + Some(x) +} + +/** Product8 is a Cartesian product of 8 components. + */ +trait Product8[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8] extends Any with Product { + /** The arity of this product. + * @return 8 + */ + override def productArity: Int = 8 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 8). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 7)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + + +} diff --git a/library/src/scala/Product9.scala b/library/src/scala/Product9.scala new file mode 100644 index 000000000000..6731142a015b --- /dev/null +++ b/library/src/scala/Product9.scala @@ -0,0 +1,91 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + +object Product9 { + def unapply[T1, T2, T3, T4, T5, T6, T7, T8, T9](x: Product9[T1, T2, T3, T4, T5, T6, T7, T8, T9]): Option[Product9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] = + Some(x) +} + +/** Product9 is a Cartesian product of 9 components. + */ +trait Product9[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9] extends Any with Product { + /** The arity of this product. + * @return 9 + */ + override def productArity: Int = 9 + + + /** Returns the n-th projection of this product if 0 <= n < productArity, + * otherwise throws an `IndexOutOfBoundsException`. + * + * @param n number of the projection to be returned + * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= 9). + */ + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int): Any = n match { + case 0 => _1 + case 1 => _2 + case 2 => _3 + case 3 => _4 + case 4 => _5 + case 5 => _6 + case 6 => _7 + case 7 => _8 + case 8 => _9 + case _ => throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max 8)") + } + + /** A projection of element 1 of this Product. + * @return A projection of element 1. + */ + def _1: T1 + /** A projection of element 2 of this Product. + * @return A projection of element 2. + */ + def _2: T2 + /** A projection of element 3 of this Product. + * @return A projection of element 3. + */ + def _3: T3 + /** A projection of element 4 of this Product. + * @return A projection of element 4. + */ + def _4: T4 + /** A projection of element 5 of this Product. + * @return A projection of element 5. + */ + def _5: T5 + /** A projection of element 6 of this Product. + * @return A projection of element 6. + */ + def _6: T6 + /** A projection of element 7 of this Product. + * @return A projection of element 7. + */ + def _7: T7 + /** A projection of element 8 of this Product. + * @return A projection of element 8. + */ + def _8: T8 + /** A projection of element 9 of this Product. + * @return A projection of element 9. + */ + def _9: T9 + + +} diff --git a/library/src/scala/Proxy.scala b/library/src/scala/Proxy.scala new file mode 100644 index 000000000000..8da03133e3cd --- /dev/null +++ b/library/src/scala/Proxy.scala @@ -0,0 +1,48 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** This class implements a simple proxy that forwards all calls to + * the public, non-final methods defined in class `Any` to another + * object self. Those methods are: + * {{{ + * def hashCode(): Int + * def equals(other: Any): Boolean + * def toString(): String + * }}} + * '''Note:''' forwarding methods in this way will most likely create + * an asymmetric equals method, which is not generally recommended. + */ +@deprecated("Explicitly override hashCode, equals and toString instead.", "2.13.0") +trait Proxy extends Any { + def self: Any + + override def hashCode: Int = self.hashCode + override def equals(that: Any): Boolean = that match { + case null => false + case _ => + val x = that.asInstanceOf[AnyRef] + (x eq this.asInstanceOf[AnyRef]) || (x eq self.asInstanceOf[AnyRef]) || (x equals self) + } + override def toString = "" + self +} + +@deprecated("All members of this object are deprecated.", "2.13.0") +object Proxy { + /** A proxy which exposes the type it is proxying for via a type parameter. + */ + @deprecated("Explicitly override hashCode, equals and toString instead.", "2.13.0") + trait Typed[T] extends Any with Proxy { + def self: T + } +} diff --git a/library/src/scala/SerialVersionUID.scala b/library/src/scala/SerialVersionUID.scala new file mode 100644 index 000000000000..0c85b2591247 --- /dev/null +++ b/library/src/scala/SerialVersionUID.scala @@ -0,0 +1,27 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** + * Annotation for specifying the `serialVersionUID` field of a (serializable) class. + * + * On the JVM, a class with this annotation will receive a `private`, `static`, + * and `final` field called `serialVersionUID` with the provided `value`, + * which the JVM's serialization mechanism uses to determine serialization + * compatibility between different versions of a class. + * + * @see [[java.io.Serializable]] + * @see [[Serializable]] + */ +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class SerialVersionUID(value: Long) extends scala.annotation.ConstantAnnotation diff --git a/library/src/scala/Short.scala b/library/src/scala/Short.scala new file mode 100644 index 000000000000..4a56d71d0733 --- /dev/null +++ b/library/src/scala/Short.scala @@ -0,0 +1,486 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + +/** `Short`, a 16-bit signed integer (equivalent to Java's `short` primitive type) is a + * subtype of [[scala.AnyVal]]. Instances of `Short` are not + * represented by an object in the underlying runtime system. + * + * There is an implicit conversion from [[scala.Short]] => [[scala.runtime.RichShort]] + * which provides useful non-primitive operations. + */ +final abstract class Short private extends AnyVal { + def toByte: Byte + def toShort: Short + def toChar: Char + def toInt: Int + def toLong: Long + def toFloat: Float + def toDouble: Double + + /** + * Returns the bitwise negation of this value. + * @example {{{ + * ~5 == -6 + * // in binary: ~00000101 == + * // 11111010 + * }}} + */ + def unary_~ : Int + /** Returns this value, unmodified. */ + def unary_+ : Int + /** Returns the negation of this value. */ + def unary_- : Int + + @deprecated("Adding a number and a String is deprecated. Use the string interpolation `s\"$num$str\"`", "2.13.0") + def +(x: String): String + + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + def <<(x: Int): Int + /** + * Returns this value bit-shifted left by the specified number of bits, + * filling in the new right bits with zeroes. + * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def <<(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + def >>>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling the new left bits with zeroes. + * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}} + * @example {{{ + * -21 >>> 3 == 536870909 + * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == + * // 00011111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>>(x: Long): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + def >>(x: Int): Int + /** + * Returns this value bit-shifted right by the specified number of bits, + * filling in the left bits with the same value as the left-most bit of this. + * The effect of this is to retain the sign of the value. + * @example {{{ + * -21 >> 3 == -3 + * // in binary: 11111111 11111111 11111111 11101011 >> 3 == + * // 11111111 11111111 11111111 11111101 + * }}} + */ + @deprecated("shifting a value by a `Long` argument is deprecated (except when the value is a `Long`).\nCall `toInt` on the argument to maintain the current behavior and avoid the deprecation warning.", "2.12.7") + def >>(x: Long): Int + + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Byte): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Short): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Char): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Int): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Long): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Float): Boolean + /** Returns `true` if this value is equal to x, `false` otherwise. */ + def ==(x: Double): Boolean + + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Byte): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Short): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Char): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Int): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Long): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Float): Boolean + /** Returns `true` if this value is not equal to x, `false` otherwise. */ + def !=(x: Double): Boolean + + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Byte): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Short): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Char): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Int): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Long): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Float): Boolean + /** Returns `true` if this value is less than x, `false` otherwise. */ + def <(x: Double): Boolean + + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Byte): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Short): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Char): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Int): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Long): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Float): Boolean + /** Returns `true` if this value is less than or equal to x, `false` otherwise. */ + def <=(x: Double): Boolean + + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Byte): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Short): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Char): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Int): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Long): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Float): Boolean + /** Returns `true` if this value is greater than x, `false` otherwise. */ + def >(x: Double): Boolean + + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Byte): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Short): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Char): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Int): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Long): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Float): Boolean + /** Returns `true` if this value is greater than or equal to x, `false` otherwise. */ + def >=(x: Double): Boolean + + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Byte): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Short): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Char): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Int): Int + /** + * Returns the bitwise OR of this value and `x`. + * @example {{{ + * (0xf0 | 0xaa) == 0xfa + * // in binary: 11110000 + * // | 10101010 + * // -------- + * // 11111010 + * }}} + */ + def |(x: Long): Long + + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Byte): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Short): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Char): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Int): Int + /** + * Returns the bitwise AND of this value and `x`. + * @example {{{ + * (0xf0 & 0xaa) == 0xa0 + * // in binary: 11110000 + * // & 10101010 + * // -------- + * // 10100000 + * }}} + */ + def &(x: Long): Long + + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Byte): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Short): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Char): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Int): Int + /** + * Returns the bitwise XOR of this value and `x`. + * @example {{{ + * (0xf0 ^ 0xaa) == 0x5a + * // in binary: 11110000 + * // ^ 10101010 + * // -------- + * // 01011010 + * }}} + */ + def ^(x: Long): Long + + /** Returns the sum of this value and `x`. */ + def +(x: Byte): Int + /** Returns the sum of this value and `x`. */ + def +(x: Short): Int + /** Returns the sum of this value and `x`. */ + def +(x: Char): Int + /** Returns the sum of this value and `x`. */ + def +(x: Int): Int + /** Returns the sum of this value and `x`. */ + def +(x: Long): Long + /** Returns the sum of this value and `x`. */ + def +(x: Float): Float + /** Returns the sum of this value and `x`. */ + def +(x: Double): Double + + /** Returns the difference of this value and `x`. */ + def -(x: Byte): Int + /** Returns the difference of this value and `x`. */ + def -(x: Short): Int + /** Returns the difference of this value and `x`. */ + def -(x: Char): Int + /** Returns the difference of this value and `x`. */ + def -(x: Int): Int + /** Returns the difference of this value and `x`. */ + def -(x: Long): Long + /** Returns the difference of this value and `x`. */ + def -(x: Float): Float + /** Returns the difference of this value and `x`. */ + def -(x: Double): Double + + /** Returns the product of this value and `x`. */ + def *(x: Byte): Int + /** Returns the product of this value and `x`. */ + def *(x: Short): Int + /** Returns the product of this value and `x`. */ + def *(x: Char): Int + /** Returns the product of this value and `x`. */ + def *(x: Int): Int + /** Returns the product of this value and `x`. */ + def *(x: Long): Long + /** Returns the product of this value and `x`. */ + def *(x: Float): Float + /** Returns the product of this value and `x`. */ + def *(x: Double): Double + + /** Returns the quotient of this value and `x`. */ + def /(x: Byte): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Short): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Char): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Int): Int + /** Returns the quotient of this value and `x`. */ + def /(x: Long): Long + /** Returns the quotient of this value and `x`. */ + def /(x: Float): Float + /** Returns the quotient of this value and `x`. */ + def /(x: Double): Double + + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Byte): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Short): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Char): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Int): Int + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Long): Long + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Float): Float + /** Returns the remainder of the division of this value by `x`. */ + def %(x: Double): Double + + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Short] = ??? +} + +object Short extends AnyValCompanion { + /** The smallest value representable as a Short. */ + final val MinValue = java.lang.Short.MIN_VALUE + + /** The largest value representable as a Short. */ + final val MaxValue = java.lang.Short.MAX_VALUE + + /** Transform a value type into a boxed reference type. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToShort`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the Short to be boxed + * @return a java.lang.Short offering `x` as its underlying value. + */ + def box(x: Short): java.lang.Short = ??? + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a java.lang.Short. + * + * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxToShort`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. + * + * @param x the java.lang.Short to be unboxed. + * @throws ClassCastException if the argument is not a java.lang.Short + * @return the Short resulting from calling shortValue() on `x` + */ + def unbox(x: java.lang.Object): Short = ??? + + /** The String representation of the scala.Short companion object. */ + override def toString = "object scala.Short" + /** Language mandated coercions from Short to "wider" types. */ + import scala.language.implicitConversions + implicit def short2int(x: Short): Int = x.toInt + implicit def short2long(x: Short): Long = x.toLong + implicit def short2float(x: Short): Float = x.toFloat + implicit def short2double(x: Short): Double = x.toDouble +} + diff --git a/library/src/scala/Specializable.scala b/library/src/scala/Specializable.scala new file mode 100644 index 000000000000..54fb59dba83e --- /dev/null +++ b/library/src/scala/Specializable.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** A common supertype for companions of specializable types. + * Should not be extended in user code. + */ +trait Specializable + +object Specializable { + // No type parameter in @specialized annotation. + trait SpecializedGroup + + // Smuggle a list of types by way of a tuple upon which Group is parameterized. + class Group[T >: Null](value: T) extends SpecializedGroup + + final val Primitives: Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit)] = null + final val Everything: Group[(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit, AnyRef)] = null + final val Bits32AndUp: Group[(Int, Long, Float, Double)] = null + final val Integral: Group[(Byte, Short, Int, Long, Char)] = null + final val AllNumeric: Group[(Byte, Short, Int, Long, Char, Float, Double)] = null + final val BestOfBreed: Group[(Int, Double, Boolean, Unit, AnyRef)] = null + final val Unit: Group[Tuple1[Unit]] = null + + final val Arg: Group[(Int, Long, Float, Double)] = null + final val Args: Group[(Int, Long, Double)] = null + final val Return: Group[(Int, Long, Float, Double, Boolean, Unit)] = null +} diff --git a/library/src/scala/StringContext.scala b/library/src/scala/StringContext.scala new file mode 100644 index 000000000000..ec5c49a2349e --- /dev/null +++ b/library/src/scala/StringContext.scala @@ -0,0 +1,476 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import java.lang.{ StringBuilder => JLSBuilder } +import scala.annotation.tailrec +import scala.collection.mutable.ArrayBuilder + +/** This class provides the basic mechanism to do String Interpolation. + * String Interpolation allows users + * to embed variable references directly in *processed* string literals. + * Here's an example: + * {{{ + * val name = "James" + * println(s"Hello, \$name") // Hello, James + * }}} + * + * Any processed string literal is rewritten as an instantiation and + * method call against this class. For example: + * {{{ + * s"Hello, \$name" + * }}} + * + * is rewritten to be: + * + * {{{ + * StringContext("Hello, ", "").s(name) + * }}} + * + * By default, this class provides the `raw`, `s` and `f` methods as + * available interpolators. + * + * To provide your own string interpolator, create an implicit class + * which adds a method to `StringContext`. Here's an example: + * {{{ + * implicit class JsonHelper(private val sc: StringContext) extends AnyVal { + * def json(args: Any*): JSONObject = ... + * } + * val x: JSONObject = json"{ a: \$a }" + * }}} + * + * Here the `JsonHelper` extension class implicitly adds the `json` method to + * `StringContext` which can be used for `json` string literals. + * + * @param parts The parts that make up the interpolated string, + * without the expressions that get inserted by interpolation. + */ +case class StringContext(parts: String*) { + + import StringContext.{checkLengths => scCheckLengths, glob, processEscapes, standardInterpolator => scStandardInterpolator} + + @deprecated("use same-named method on StringContext companion object", "2.13.0") + def checkLengths(args: scala.collection.Seq[Any]): Unit = scCheckLengths(args, parts) + + /** The simple string interpolator. + * + * It inserts its arguments between corresponding parts of the string context. + * It also treats standard escape sequences as defined in the Scala specification. + * Here's an example of usage: + * {{{ + * val name = "James" + * println(s"Hello, \$name") // Hello, James + * }}} + * In this example, the expression \$name is replaced with the `toString` of the + * variable `name`. + * The `s` interpolator can take the `toString` of any arbitrary expression within + * a `\${}` block, for example: + * {{{ + * println(s"1 + 1 = \${1 + 1}") + * }}} + * will print the string `1 + 1 = 2`. + * + * @param `args` The arguments to be inserted into the resulting string. + * @throws IllegalArgumentException + * if the number of `parts` in the enclosing `StringContext` does not exceed + * the number of arguments `arg` by exactly 1. + * @throws StringContext.InvalidEscapeException + * if a `parts` string contains a backslash (`\`) character + * that does not start a valid escape sequence. + * @note The Scala compiler may replace a call to this method with an equivalent, but more efficient, + * use of a StringBuilder. + */ + def s(args: Any*): String = macro ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateS + object s { + /** The simple string matcher. + * + * Attempts to match the input string to the given interpolated patterns via + * a naive globbing, that is the reverse of the simple interpolator. + * + * Here is an example usage: + * + * {{{ + * val s"Hello, \$name" = "Hello, James" + * println(name) // "James" + * }}} + * + * In this example, the string "James" ends up matching the location where the pattern + * `\$name` is positioned, and thus ends up bound to that variable. + * + * Multiple matches are supported: + * + * {{{ + * val s"\$greeting, \$name" = "Hello, James" + * println(greeting) // "Hello" + * println(name) // "James" + * }}} + * + * And the `s` matcher can match an arbitrary pattern within the `\${}` block, for example: + * + * {{{ + * val TimeSplitter = "([0-9]+)[.:]([0-9]+)".r + * val s"The time is \${TimeSplitter(hours, mins)}" = "The time is 10.50" + * println(hours) // 10 + * println(mins) // 50 + * }}} + * + * Here, we use the `TimeSplitter` regex within the `s` matcher, further splitting the + * matched string "10.50" into its constituent parts + */ + def unapplySeq(s: String): Option[Seq[String]] = glob(parts.map(processEscapes), s) + } + /** The raw string interpolator. + * + * It inserts its arguments between corresponding parts of the string context. + * As opposed to the simple string interpolator `s`, this one does not treat + * standard escape sequences as defined in the Scala specification. + * + * For example, the raw processed string `raw"a\nb"` is equal to the scala string `"a\\nb"`. + * + * ''Note:'' Even when using the raw interpolator, Scala will process Unicode escapes. + * Unicode processing in the raw interpolator is deprecated as of scala 2.13.2 and + * will be removed in the future + * For example: + * {{{ + * scala> raw"\u005cu0023" + * res0: String = # + * }}} + * + * @param `args` The arguments to be inserted into the resulting string. + * @throws IllegalArgumentException + * if the number of `parts` in the enclosing `StringContext` does not exceed + * the number of arguments `arg` by exactly 1. + * @note The Scala compiler may replace a call to this method with an equivalent, but more efficient, + * use of a StringBuilder. + */ + def raw(args: Any*): String = macro ??? // fasttracked to scala.tools.reflect.FastStringInterpolator::interpolateRaw + + @deprecated("Use the static method StringContext.standardInterpolator instead of the instance method", "2.13.0") + def standardInterpolator(process: String => String, args: Seq[Any]): String = scStandardInterpolator(process, args, parts) + + /** The formatted string interpolator. + * + * It inserts its arguments between corresponding parts of the string context. + * It also treats standard escape sequences as defined in the Scala specification. + * Finally, if an interpolated expression is followed by a `parts` string + * that starts with a formatting specifier, the expression is formatted according to that + * specifier. All specifiers allowed in Java format strings are handled, and in the same + * way they are treated in Java. + * + * For example: + * {{{ + * val height = 1.9d + * val name = "James" + * println(f"\$name%s is \$height%2.2f meters tall") // James is 1.90 meters tall + * }}} + * + * @param `args` The arguments to be inserted into the resulting string. + * @throws IllegalArgumentException + * if the number of `parts` in the enclosing `StringContext` does not exceed + * the number of arguments `arg` by exactly 1. + * @throws StringContext.InvalidEscapeException + * if a `parts` string contains a backslash (`\`) character + * that does not start a valid escape sequence. + * + * Note: The `f` method works by assembling a format string from all the `parts` strings and using + * `java.lang.String.format` to format all arguments with that format string. The format string is + * obtained by concatenating all `parts` strings, and performing two transformations: + * + * 1. Let a _formatting position_ be a start of any `parts` string except the first one. + * If a formatting position does not refer to a `%` character (which is assumed to + * start a format specifier), then the string format specifier `%s` is inserted. + * + * 2. Any `%` characters not in formatting positions must begin one of the conversions + * `%%` (the literal percent) or `%n` (the platform-specific line separator). + */ + def f[A >: Any](args: A*): String = macro ??? // fasttracked to scala.tools.reflect.FormatInterpolator::interpolateF +} + +object StringContext { + /** + * Linear time glob-matching implementation. + * Adapted from https://research.swtch.com/glob + * + * @param patternChunks The non-wildcard portions of the input pattern, + * separated by wildcards + * @param input The input you wish to match against + * @return None if there is no match, Some containing the sequence of matched + * wildcard strings if there is a match + */ + def glob(patternChunks: Seq[String], input: String): Option[Seq[String]] = { + var patternIndex = 0 + var inputIndex = 0 + var nextPatternIndex = 0 + var nextInputIndex = 0 + + val numWildcards = patternChunks.length - 1 + val matchStarts = Array.fill(numWildcards)(-1) + val matchEnds = Array.fill(numWildcards)(-1) + + val nameLength = input.length + // The final pattern is as long as all the chunks, separated by 1-character + // glob-wildcard placeholders + val patternLength = patternChunks.iterator.map(_.length).sum + numWildcards + + // Convert the input pattern chunks into a single sequence of shorts; each + // non-negative short represents a character, while -1 represents a glob wildcard + val pattern = { + val b = new ArrayBuilder.ofShort ; b.sizeHint(patternLength) + patternChunks.head.foreach(c => b.addOne(c.toShort)) + patternChunks.tail.foreach { s => b.addOne(-1) ; s.foreach(c => b.addOne(c.toShort)) } + b.result() + } + + // Lookup table for each character in the pattern to check whether or not + // it refers to a glob wildcard; a non-negative integer indicates which + // glob wildcard it represents, while -1 means it doesn't represent any + val matchIndices = { + val arr = Array.fill(patternLength + 1)(-1) + patternChunks.init.zipWithIndex.foldLeft(0) { case (ttl, (chunk, i)) => + val sum = ttl + chunk.length + arr(sum) = i + sum + 1 + } + arr + } + + while (patternIndex < patternLength || inputIndex < nameLength) { + matchIndices(patternIndex) match { + case -1 => // do nothing + case n => + matchStarts(n) = matchStarts(n) match { + case -1 => inputIndex + case s => math.min(s, inputIndex) + } + matchEnds(n) = matchEnds(n) match { + case -1 => inputIndex + case s => math.max(s, inputIndex) + } + } + + val continue = if (patternIndex < patternLength) { + val c = pattern(patternIndex) + c match { + case -1 => // zero-or-more-character wildcard + // Try to match at nx. If that doesn't work out, restart at nx+1 next. + nextPatternIndex = patternIndex + nextInputIndex = inputIndex + 1 + patternIndex += 1 + true + case _ => // ordinary character + if (inputIndex < nameLength && input(inputIndex) == c) { + patternIndex += 1 + inputIndex += 1 + true + } else { + false + } + } + } else false + + // Mismatch. Maybe restart. + if (!continue) { + if (0 < nextInputIndex && nextInputIndex <= nameLength) { + patternIndex = nextPatternIndex + inputIndex = nextInputIndex + } else { + return None + } + } + } + + // Matched all of pattern to all of name. Success. + Some(collection.immutable.ArraySeq.unsafeWrapArray( + Array.tabulate(patternChunks.length - 1)(n => input.slice(matchStarts(n), matchEnds(n))) + )) + } + + /** An exception that is thrown if a string contains a backslash (`\`) character + * that does not start a valid escape sequence. + * @param str The offending string + * @param index The index of the offending backslash character in `str`. + */ + class InvalidEscapeException(str: String, val index: Int) extends IllegalArgumentException( + s"""invalid escape ${ + require(index >= 0 && index < str.length) + val ok = s"""[\\b, \\t, \\n, \\f, \\r, \\\\, \\", \\', \\uxxxx]""" + if (index == str.length - 1) "at terminal" else s"'\\${str(index + 1)}' not one of $ok at" + } index $index in "$str". Use \\\\ for literal \\.""" + ) + + protected[scala] class InvalidUnicodeEscapeException(str: String, val escapeStart: Int, val index: Int) extends IllegalArgumentException( + s"""invalid unicode escape at index $index of $str""" + ) + + private[this] def readUEscape(src: String, startindex: Int): (Char, Int) = { + val len = src.length() + def loop(uindex: Int): (Char, Int) = { + def loopCP(dindex: Int, codepoint: Int): (Char, Int) = { + //supports BMP + surrogate escapes + //but only in four hex-digit code units (uxxxx) + if(dindex >= 4) { + val usRead = uindex - startindex + val digitsRead = dindex + (codepoint.asInstanceOf[Char], usRead + digitsRead) + } + else if (dindex + uindex >= len) + throw new InvalidUnicodeEscapeException(src, startindex, uindex + dindex) + else { + val ch = src(dindex + uindex) + val e = ch.asDigit + if(e >= 0 && e <= 15) loopCP(dindex + 1, (codepoint << 4) + e) + else throw new InvalidUnicodeEscapeException(src, startindex, uindex + dindex) + } + } + if(uindex >= len) throw new InvalidUnicodeEscapeException(src, startindex, uindex - 1) + //allow one or more `u` characters between the + //backslash and the code unit + else if(src(uindex) == 'u') loop(uindex + 1) + else loopCP(0, 0) + } + loop(startindex) + } + + /** Expands standard Scala escape sequences in a string. + * Escape sequences are: + * control: `\b`, `\t`, `\n`, `\f`, `\r` + * escape: `\\`, `\"`, `\'` + * + * @param str A string that may contain escape sequences + * @return The string with all escape sequences expanded. + */ + @deprecated("use processEscapes", "2.13.0") + def treatEscapes(str: String): String = processEscapes(str) + + /** Expands standard Scala escape sequences in a string. + * Escape sequences are: + * control: `\b`, `\t`, `\n`, `\f`, `\r` + * escape: `\\`, `\"`, `\'` + * + * @param str A string that may contain escape sequences + * @return The string with all escape sequences expanded. + */ + def processEscapes(str: String): String = + str indexOf '\\' match { + case -1 => str + case i => replace(str, i) + } + + protected[scala] def processUnicode(str: String): String = + str indexOf "\\u" match { + case -1 => str + case i => replaceU(str, i) + } + + //replace escapes with given first escape + private[this] def replace(str: String, first: Int): String = { + val len = str.length() + val b = new JLSBuilder + // append replacement starting at index `i`, with `next` backslash + @tailrec def loop(i: Int, next: Int): String = { + if (next >= 0) { + //require(str(next) == '\\') + if (next > i) b.append(str, i, next) + var idx = next + 1 + if (idx >= len) throw new InvalidEscapeException(str, next) + val c = str(idx) match { + case 'u' => 'u' + case 'b' => '\b' + case 't' => '\t' + case 'n' => '\n' + case 'f' => '\f' + case 'r' => '\r' + case '"' => '"' + case '\'' => '\'' + case '\\' => '\\' + case _ => throw new InvalidEscapeException(str, next) + } + val (ch, advance) = if (c == 'u') readUEscape(str, idx) + else (c, 1) + idx += advance + b append ch + loop(idx, str.indexOf('\\', idx)) + } else { + if (i < len) b.append(str, i, len) + b.toString + } + } + loop(0, first) + } + + /** replace Unicode escapes starting at index `backslash` which must be the + * index of the first index of a backslash character followed by a `u` + * character + * + * If a backslash is followed by one or more `u` characters and there is + * an odd number of backslashes immediately preceding the `u`, processing + * the escape is attempted and an invalid escape is an error. + * The odd backslashes rule is, well, odd, but is grandfathered in from + * pre-2.13.2 times, when this same rule existed in the scanner, and was also + * odd. Since escape handling here is for backwards compatibility only, that + * backwards compatibility is also retained. + * Otherwise, the backslash is not taken to introduce an escape and the + * backslash is taken to be literal + */ + private[this] def replaceU(str: String, backslash: Int): String = { + val len = str.length() + val b = new JLSBuilder + + @tailrec def loop(i: Int, next: Int): String = { + if (next >= 0) { + //require(str(next) == '\\' && str(next + 1) == 'u') + def oddBackslashes(ibackslash: Int): Boolean = + if (ibackslash > 0 && str(ibackslash - 1) == '\\') oddBackslashes(ibackslash - 1) + else ((next - ibackslash) % 2) == 0 + + if(oddBackslashes(next)) { + if (next > i) b.append(str, i, next) + val idx = next + 1 + val (ch, advance) = readUEscape(str, idx) + val nextCharIndex = idx + advance + b.append(ch) + loop(nextCharIndex, str.indexOf("\\u", nextCharIndex)) + } + else loop(i, str.indexOf("\\u", next + 1)) + } + else { + if (i < len) b.append(str, i, len) + b.toString() + } + } + loop(0, backslash) + } + + def standardInterpolator(process: String => String, args: scala.collection.Seq[Any], parts: Seq[String]): String = { + StringContext.checkLengths(args, parts) + val pi = parts.iterator + val ai = args.iterator + val bldr = new JLSBuilder(process(pi.next())) + while (ai.hasNext) { + bldr append ai.next() + bldr append process(pi.next()) + } + bldr.toString + } + + /** Checks that the length of the given argument `args` is one less than the number + * of `parts` supplied to the `StringContext`. + * + * @throws IllegalArgumentException if this is not the case. + */ + def checkLengths(args: scala.collection.Seq[Any], parts: Seq[String]): Unit = + if (parts.length != args.length + 1) + throw new IllegalArgumentException("wrong number of arguments ("+ args.length + +") for interpolated string with "+ parts.length +" parts") + +} diff --git a/library/src/scala/Symbol.scala b/library/src/scala/Symbol.scala new file mode 100644 index 000000000000..36a99c5e4e6e --- /dev/null +++ b/library/src/scala/Symbol.scala @@ -0,0 +1,84 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** This class provides a simple way to get unique objects for equal strings. + * Since symbols are interned, they can be compared using reference equality. + */ +final class Symbol private (val name: String) extends Serializable { + /** A string representation of this symbol. + */ + override def toString(): String = s"Symbol($name)" + + @throws(classOf[java.io.ObjectStreamException]) + private def readResolve(): Any = Symbol.apply(name) + override def hashCode = name.hashCode() + override def equals(other: Any) = this eq other.asInstanceOf[AnyRef] +} + +object Symbol extends UniquenessCache[String, Symbol] { + override def apply(name: String): Symbol = super.apply(name) + protected def valueFromKey(name: String): Symbol = new Symbol(name) + protected def keyFromValue(sym: Symbol): Option[String] = Some(sym.name) +} + +/** This is private so it won't appear in the library API, but + * abstracted to offer some hope of reusability. */ +private[scala] abstract class UniquenessCache[K, V >: Null] { + import java.lang.ref.WeakReference + import java.util.WeakHashMap + import java.util.concurrent.locks.ReentrantReadWriteLock + + private[this] val rwl = new ReentrantReadWriteLock() + private[this] val rlock = rwl.readLock + private[this] val wlock = rwl.writeLock + private[this] val map = new WeakHashMap[K, WeakReference[V]] + + protected def valueFromKey(k: K): V + protected def keyFromValue(v: V): Option[K] + + def apply(name: K): V = { + def cached(): V = { + rlock.lock + try { + val reference = map get name + if (reference == null) null + else reference.get // will be null if we were gc-ed + } + finally rlock.unlock + } + def updateCache(): V = { + wlock.lock + try { + val res = cached() + if (res != null) res + else { + // If we don't remove the old String key from the map, we can + // wind up with one String as the key and a different String as + // the name field in the Symbol, which can lead to surprising GC + // behavior and duplicate Symbols. See scala/bug#6706. + map remove name + val sym = valueFromKey(name) + map.put(name, new WeakReference(sym)) + sym + } + } + finally wlock.unlock + } + cached() match { + case null => updateCache() + case res => res + } + } + def unapply(other: V): Option[K] = keyFromValue(other) +} diff --git a/library/src/scala/Tuple1.scala b/library/src/scala/Tuple1.scala new file mode 100644 index 000000000000..6af3d3582b14 --- /dev/null +++ b/library/src/scala/Tuple1.scala @@ -0,0 +1,28 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 1 elements; the canonical representation of a [[scala.Product1]]. + * + * @constructor Create a new tuple with 1 elements. + * @param _1 Element 1 of this Tuple1 + */ +final case class Tuple1[@specialized(Int, Long, Double) +T1](_1: T1) + extends Product1[T1] +{ + override def toString(): String = "(" + _1 + ")" + +} diff --git a/library/src/scala/Tuple10.scala b/library/src/scala/Tuple10.scala new file mode 100644 index 000000000000..63fa78016769 --- /dev/null +++ b/library/src/scala/Tuple10.scala @@ -0,0 +1,37 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 10 elements; the canonical representation of a [[scala.Product10]]. + * + * @constructor Create a new tuple with 10 elements. Note that it is more idiomatic to create a Tuple10 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)` + * @param _1 Element 1 of this Tuple10 + * @param _2 Element 2 of this Tuple10 + * @param _3 Element 3 of this Tuple10 + * @param _4 Element 4 of this Tuple10 + * @param _5 Element 5 of this Tuple10 + * @param _6 Element 6 of this Tuple10 + * @param _7 Element 7 of this Tuple10 + * @param _8 Element 8 of this Tuple10 + * @param _9 Element 9 of this Tuple10 + * @param _10 Element 10 of this Tuple10 + */ +final case class Tuple10[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10) + extends Product10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + "," + _10 + ")" + +} diff --git a/library/src/scala/Tuple11.scala b/library/src/scala/Tuple11.scala new file mode 100644 index 000000000000..3cdf35e84b8c --- /dev/null +++ b/library/src/scala/Tuple11.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 11 elements; the canonical representation of a [[scala.Product11]]. + * + * @constructor Create a new tuple with 11 elements. Note that it is more idiomatic to create a Tuple11 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)` + * @param _1 Element 1 of this Tuple11 + * @param _2 Element 2 of this Tuple11 + * @param _3 Element 3 of this Tuple11 + * @param _4 Element 4 of this Tuple11 + * @param _5 Element 5 of this Tuple11 + * @param _6 Element 6 of this Tuple11 + * @param _7 Element 7 of this Tuple11 + * @param _8 Element 8 of this Tuple11 + * @param _9 Element 9 of this Tuple11 + * @param _10 Element 10 of this Tuple11 + * @param _11 Element 11 of this Tuple11 + */ +final case class Tuple11[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11) + extends Product11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + "," + _10 + "," + _11 + ")" + +} diff --git a/library/src/scala/Tuple12.scala b/library/src/scala/Tuple12.scala new file mode 100644 index 000000000000..b27538f446c2 --- /dev/null +++ b/library/src/scala/Tuple12.scala @@ -0,0 +1,40 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 12 elements; the canonical representation of a [[scala.Product12]]. + * + * @constructor Create a new tuple with 12 elements. Note that it is more idiomatic to create a Tuple12 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12)` + * @param _1 Element 1 of this Tuple12 + * @param _2 Element 2 of this Tuple12 + * @param _3 Element 3 of this Tuple12 + * @param _4 Element 4 of this Tuple12 + * @param _5 Element 5 of this Tuple12 + * @param _6 Element 6 of this Tuple12 + * @param _7 Element 7 of this Tuple12 + * @param _8 Element 8 of this Tuple12 + * @param _9 Element 9 of this Tuple12 + * @param _10 Element 10 of this Tuple12 + * @param _11 Element 11 of this Tuple12 + * @param _12 Element 12 of this Tuple12 + */ +final case class Tuple12[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12) + extends Product12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + + "," + _7 + "," + _8 + "," + _9 + "," + _10 + "," + _11 + "," + _12 + ")" + +} diff --git a/library/src/scala/Tuple13.scala b/library/src/scala/Tuple13.scala new file mode 100644 index 000000000000..84f2f3cecc28 --- /dev/null +++ b/library/src/scala/Tuple13.scala @@ -0,0 +1,41 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 13 elements; the canonical representation of a [[scala.Product13]]. + * + * @constructor Create a new tuple with 13 elements. Note that it is more idiomatic to create a Tuple13 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13)` + * @param _1 Element 1 of this Tuple13 + * @param _2 Element 2 of this Tuple13 + * @param _3 Element 3 of this Tuple13 + * @param _4 Element 4 of this Tuple13 + * @param _5 Element 5 of this Tuple13 + * @param _6 Element 6 of this Tuple13 + * @param _7 Element 7 of this Tuple13 + * @param _8 Element 8 of this Tuple13 + * @param _9 Element 9 of this Tuple13 + * @param _10 Element 10 of this Tuple13 + * @param _11 Element 11 of this Tuple13 + * @param _12 Element 12 of this Tuple13 + * @param _13 Element 13 of this Tuple13 + */ +final case class Tuple13[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13) + extends Product13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + + "," + _7 + "," + _8 + "," + _9 + "," + _10 + "," + _11 + "," + _12 + "," + _13 + ")" + +} diff --git a/library/src/scala/Tuple14.scala b/library/src/scala/Tuple14.scala new file mode 100644 index 000000000000..08cd54c0cb82 --- /dev/null +++ b/library/src/scala/Tuple14.scala @@ -0,0 +1,42 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 14 elements; the canonical representation of a [[scala.Product14]]. + * + * @constructor Create a new tuple with 14 elements. Note that it is more idiomatic to create a Tuple14 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14)` + * @param _1 Element 1 of this Tuple14 + * @param _2 Element 2 of this Tuple14 + * @param _3 Element 3 of this Tuple14 + * @param _4 Element 4 of this Tuple14 + * @param _5 Element 5 of this Tuple14 + * @param _6 Element 6 of this Tuple14 + * @param _7 Element 7 of this Tuple14 + * @param _8 Element 8 of this Tuple14 + * @param _9 Element 9 of this Tuple14 + * @param _10 Element 10 of this Tuple14 + * @param _11 Element 11 of this Tuple14 + * @param _12 Element 12 of this Tuple14 + * @param _13 Element 13 of this Tuple14 + * @param _14 Element 14 of this Tuple14 + */ +final case class Tuple14[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14) + extends Product14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + + "," + _8 + "," + _9 + "," + _10 + "," + _11 + "," + _12 + "," + _13 + "," + _14 + ")" + +} diff --git a/library/src/scala/Tuple15.scala b/library/src/scala/Tuple15.scala new file mode 100644 index 000000000000..b4a932352092 --- /dev/null +++ b/library/src/scala/Tuple15.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 15 elements; the canonical representation of a [[scala.Product15]]. + * + * @constructor Create a new tuple with 15 elements. Note that it is more idiomatic to create a Tuple15 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15)` + * @param _1 Element 1 of this Tuple15 + * @param _2 Element 2 of this Tuple15 + * @param _3 Element 3 of this Tuple15 + * @param _4 Element 4 of this Tuple15 + * @param _5 Element 5 of this Tuple15 + * @param _6 Element 6 of this Tuple15 + * @param _7 Element 7 of this Tuple15 + * @param _8 Element 8 of this Tuple15 + * @param _9 Element 9 of this Tuple15 + * @param _10 Element 10 of this Tuple15 + * @param _11 Element 11 of this Tuple15 + * @param _12 Element 12 of this Tuple15 + * @param _13 Element 13 of this Tuple15 + * @param _14 Element 14 of this Tuple15 + * @param _15 Element 15 of this Tuple15 + */ +final case class Tuple15[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15) + extends Product15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + + "," + _8 + "," + _9 + "," + _10 + "," + _11 + "," + _12 + "," + _13 + "," + _14 + "," + _15 + ")" + +} diff --git a/library/src/scala/Tuple16.scala b/library/src/scala/Tuple16.scala new file mode 100644 index 000000000000..417fa3aff002 --- /dev/null +++ b/library/src/scala/Tuple16.scala @@ -0,0 +1,44 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 16 elements; the canonical representation of a [[scala.Product16]]. + * + * @constructor Create a new tuple with 16 elements. Note that it is more idiomatic to create a Tuple16 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16)` + * @param _1 Element 1 of this Tuple16 + * @param _2 Element 2 of this Tuple16 + * @param _3 Element 3 of this Tuple16 + * @param _4 Element 4 of this Tuple16 + * @param _5 Element 5 of this Tuple16 + * @param _6 Element 6 of this Tuple16 + * @param _7 Element 7 of this Tuple16 + * @param _8 Element 8 of this Tuple16 + * @param _9 Element 9 of this Tuple16 + * @param _10 Element 10 of this Tuple16 + * @param _11 Element 11 of this Tuple16 + * @param _12 Element 12 of this Tuple16 + * @param _13 Element 13 of this Tuple16 + * @param _14 Element 14 of this Tuple16 + * @param _15 Element 15 of this Tuple16 + * @param _16 Element 16 of this Tuple16 + */ +final case class Tuple16[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16) + extends Product16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + + "," + _9 + "," + _10 + "," + _11 + "," + _12 + "," + _13 + "," + _14 + "," + _15 + "," + _16 + ")" + +} diff --git a/library/src/scala/Tuple17.scala b/library/src/scala/Tuple17.scala new file mode 100644 index 000000000000..e7d63a81d1e9 --- /dev/null +++ b/library/src/scala/Tuple17.scala @@ -0,0 +1,45 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 17 elements; the canonical representation of a [[scala.Product17]]. + * + * @constructor Create a new tuple with 17 elements. Note that it is more idiomatic to create a Tuple17 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17)` + * @param _1 Element 1 of this Tuple17 + * @param _2 Element 2 of this Tuple17 + * @param _3 Element 3 of this Tuple17 + * @param _4 Element 4 of this Tuple17 + * @param _5 Element 5 of this Tuple17 + * @param _6 Element 6 of this Tuple17 + * @param _7 Element 7 of this Tuple17 + * @param _8 Element 8 of this Tuple17 + * @param _9 Element 9 of this Tuple17 + * @param _10 Element 10 of this Tuple17 + * @param _11 Element 11 of this Tuple17 + * @param _12 Element 12 of this Tuple17 + * @param _13 Element 13 of this Tuple17 + * @param _14 Element 14 of this Tuple17 + * @param _15 Element 15 of this Tuple17 + * @param _16 Element 16 of this Tuple17 + * @param _17 Element 17 of this Tuple17 + */ +final case class Tuple17[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17) + extends Product17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + + "," + _9 + "," + _10 + "," + _11 + "," + _12 + "," + _13 + "," + _14 + "," + _15 + "," + _16 + "," + _17 + ")" + +} diff --git a/library/src/scala/Tuple18.scala b/library/src/scala/Tuple18.scala new file mode 100644 index 000000000000..86875130951a --- /dev/null +++ b/library/src/scala/Tuple18.scala @@ -0,0 +1,46 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 18 elements; the canonical representation of a [[scala.Product18]]. + * + * @constructor Create a new tuple with 18 elements. Note that it is more idiomatic to create a Tuple18 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18)` + * @param _1 Element 1 of this Tuple18 + * @param _2 Element 2 of this Tuple18 + * @param _3 Element 3 of this Tuple18 + * @param _4 Element 4 of this Tuple18 + * @param _5 Element 5 of this Tuple18 + * @param _6 Element 6 of this Tuple18 + * @param _7 Element 7 of this Tuple18 + * @param _8 Element 8 of this Tuple18 + * @param _9 Element 9 of this Tuple18 + * @param _10 Element 10 of this Tuple18 + * @param _11 Element 11 of this Tuple18 + * @param _12 Element 12 of this Tuple18 + * @param _13 Element 13 of this Tuple18 + * @param _14 Element 14 of this Tuple18 + * @param _15 Element 15 of this Tuple18 + * @param _16 Element 16 of this Tuple18 + * @param _17 Element 17 of this Tuple18 + * @param _18 Element 18 of this Tuple18 + */ +final case class Tuple18[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18) + extends Product18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + + "," + _10 + "," + _11 + "," + _12 + "," + _13 + "," + _14 + "," + _15 + "," + _16 + "," + _17 + "," + _18 + ")" + +} diff --git a/library/src/scala/Tuple19.scala b/library/src/scala/Tuple19.scala new file mode 100644 index 000000000000..e3826ddd3073 --- /dev/null +++ b/library/src/scala/Tuple19.scala @@ -0,0 +1,47 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 19 elements; the canonical representation of a [[scala.Product19]]. + * + * @constructor Create a new tuple with 19 elements. Note that it is more idiomatic to create a Tuple19 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19)` + * @param _1 Element 1 of this Tuple19 + * @param _2 Element 2 of this Tuple19 + * @param _3 Element 3 of this Tuple19 + * @param _4 Element 4 of this Tuple19 + * @param _5 Element 5 of this Tuple19 + * @param _6 Element 6 of this Tuple19 + * @param _7 Element 7 of this Tuple19 + * @param _8 Element 8 of this Tuple19 + * @param _9 Element 9 of this Tuple19 + * @param _10 Element 10 of this Tuple19 + * @param _11 Element 11 of this Tuple19 + * @param _12 Element 12 of this Tuple19 + * @param _13 Element 13 of this Tuple19 + * @param _14 Element 14 of this Tuple19 + * @param _15 Element 15 of this Tuple19 + * @param _16 Element 16 of this Tuple19 + * @param _17 Element 17 of this Tuple19 + * @param _18 Element 18 of this Tuple19 + * @param _19 Element 19 of this Tuple19 + */ +final case class Tuple19[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19) + extends Product19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + + "," + _10 + "," + _11 + "," + _12 + "," + _13 + "," + _14 + "," + _15 + "," + _16 + "," + _17 + "," + _18 + "," + _19 + ")" + +} diff --git a/library/src/scala/Tuple2.scala b/library/src/scala/Tuple2.scala new file mode 100644 index 000000000000..3429ed7dea62 --- /dev/null +++ b/library/src/scala/Tuple2.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 2 elements; the canonical representation of a [[scala.Product2]]. + * + * @constructor Create a new tuple with 2 elements. Note that it is more idiomatic to create a Tuple2 via `(t1, t2)` + * @param _1 Element 1 of this Tuple2 + * @param _2 Element 2 of this Tuple2 + */ +final case class Tuple2[@specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T1, @specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T2](_1: T1, _2: T2) + extends Product2[T1, T2] +{ + override def toString(): String = "(" + _1 + "," + _2 + ")" + + /** Swaps the elements of this `Tuple`. + * @return a new Tuple where the first element is the second element of this Tuple and the + * second element is the first element of this Tuple. + */ + def swap: Tuple2[T2,T1] = Tuple2(_2, _1) + +} diff --git a/library/src/scala/Tuple20.scala b/library/src/scala/Tuple20.scala new file mode 100644 index 000000000000..1d4826b94841 --- /dev/null +++ b/library/src/scala/Tuple20.scala @@ -0,0 +1,48 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 20 elements; the canonical representation of a [[scala.Product20]]. + * + * @constructor Create a new tuple with 20 elements. Note that it is more idiomatic to create a Tuple20 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20)` + * @param _1 Element 1 of this Tuple20 + * @param _2 Element 2 of this Tuple20 + * @param _3 Element 3 of this Tuple20 + * @param _4 Element 4 of this Tuple20 + * @param _5 Element 5 of this Tuple20 + * @param _6 Element 6 of this Tuple20 + * @param _7 Element 7 of this Tuple20 + * @param _8 Element 8 of this Tuple20 + * @param _9 Element 9 of this Tuple20 + * @param _10 Element 10 of this Tuple20 + * @param _11 Element 11 of this Tuple20 + * @param _12 Element 12 of this Tuple20 + * @param _13 Element 13 of this Tuple20 + * @param _14 Element 14 of this Tuple20 + * @param _15 Element 15 of this Tuple20 + * @param _16 Element 16 of this Tuple20 + * @param _17 Element 17 of this Tuple20 + * @param _18 Element 18 of this Tuple20 + * @param _19 Element 19 of this Tuple20 + * @param _20 Element 20 of this Tuple20 + */ +final case class Tuple20[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20) + extends Product20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + "," + _10 + + "," + _11 + "," + _12 + "," + _13 + "," + _14 + "," + _15 + "," + _16 + "," + _17 + "," + _18 + "," + _19 + "," + _20 + ")" + +} diff --git a/library/src/scala/Tuple21.scala b/library/src/scala/Tuple21.scala new file mode 100644 index 000000000000..01503f0e9362 --- /dev/null +++ b/library/src/scala/Tuple21.scala @@ -0,0 +1,49 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 21 elements; the canonical representation of a [[scala.Product21]]. + * + * @constructor Create a new tuple with 21 elements. Note that it is more idiomatic to create a Tuple21 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21)` + * @param _1 Element 1 of this Tuple21 + * @param _2 Element 2 of this Tuple21 + * @param _3 Element 3 of this Tuple21 + * @param _4 Element 4 of this Tuple21 + * @param _5 Element 5 of this Tuple21 + * @param _6 Element 6 of this Tuple21 + * @param _7 Element 7 of this Tuple21 + * @param _8 Element 8 of this Tuple21 + * @param _9 Element 9 of this Tuple21 + * @param _10 Element 10 of this Tuple21 + * @param _11 Element 11 of this Tuple21 + * @param _12 Element 12 of this Tuple21 + * @param _13 Element 13 of this Tuple21 + * @param _14 Element 14 of this Tuple21 + * @param _15 Element 15 of this Tuple21 + * @param _16 Element 16 of this Tuple21 + * @param _17 Element 17 of this Tuple21 + * @param _18 Element 18 of this Tuple21 + * @param _19 Element 19 of this Tuple21 + * @param _20 Element 20 of this Tuple21 + * @param _21 Element 21 of this Tuple21 + */ +final case class Tuple21[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20, +T21](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20, _21: T21) + extends Product21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + "," + _10 + + "," + _11 + "," + _12 + "," + _13 + "," + _14 + "," + _15 + "," + _16 + "," + _17 + "," + _18 + "," + _19 + "," + _20 + "," + _21 + ")" + +} diff --git a/library/src/scala/Tuple22.scala b/library/src/scala/Tuple22.scala new file mode 100644 index 000000000000..3f84e75a5dc6 --- /dev/null +++ b/library/src/scala/Tuple22.scala @@ -0,0 +1,50 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 22 elements; the canonical representation of a [[scala.Product22]]. + * + * @constructor Create a new tuple with 22 elements. Note that it is more idiomatic to create a Tuple22 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22)` + * @param _1 Element 1 of this Tuple22 + * @param _2 Element 2 of this Tuple22 + * @param _3 Element 3 of this Tuple22 + * @param _4 Element 4 of this Tuple22 + * @param _5 Element 5 of this Tuple22 + * @param _6 Element 6 of this Tuple22 + * @param _7 Element 7 of this Tuple22 + * @param _8 Element 8 of this Tuple22 + * @param _9 Element 9 of this Tuple22 + * @param _10 Element 10 of this Tuple22 + * @param _11 Element 11 of this Tuple22 + * @param _12 Element 12 of this Tuple22 + * @param _13 Element 13 of this Tuple22 + * @param _14 Element 14 of this Tuple22 + * @param _15 Element 15 of this Tuple22 + * @param _16 Element 16 of this Tuple22 + * @param _17 Element 17 of this Tuple22 + * @param _18 Element 18 of this Tuple22 + * @param _19 Element 19 of this Tuple22 + * @param _20 Element 20 of this Tuple22 + * @param _21 Element 21 of this Tuple22 + * @param _22 Element 22 of this Tuple22 + */ +final case class Tuple22[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20, +T21, +T22](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20, _21: T21, _22: T22) + extends Product22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + "," + _10 + "," + _11 + + "," + _12 + "," + _13 + "," + _14 + "," + _15 + "," + _16 + "," + _17 + "," + _18 + "," + _19 + "," + _20 + "," + _21 + "," + _22 + ")" + +} diff --git a/library/src/scala/Tuple3.scala b/library/src/scala/Tuple3.scala new file mode 100644 index 000000000000..b053d9c4c6b2 --- /dev/null +++ b/library/src/scala/Tuple3.scala @@ -0,0 +1,30 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 3 elements; the canonical representation of a [[scala.Product3]]. + * + * @constructor Create a new tuple with 3 elements. Note that it is more idiomatic to create a Tuple3 via `(t1, t2, t3)` + * @param _1 Element 1 of this Tuple3 + * @param _2 Element 2 of this Tuple3 + * @param _3 Element 3 of this Tuple3 + */ +final case class Tuple3[+T1, +T2, +T3](_1: T1, _2: T2, _3: T3) + extends Product3[T1, T2, T3] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + ")" + +} diff --git a/library/src/scala/Tuple4.scala b/library/src/scala/Tuple4.scala new file mode 100644 index 000000000000..29970f510398 --- /dev/null +++ b/library/src/scala/Tuple4.scala @@ -0,0 +1,31 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 4 elements; the canonical representation of a [[scala.Product4]]. + * + * @constructor Create a new tuple with 4 elements. Note that it is more idiomatic to create a Tuple4 via `(t1, t2, t3, t4)` + * @param _1 Element 1 of this Tuple4 + * @param _2 Element 2 of this Tuple4 + * @param _3 Element 3 of this Tuple4 + * @param _4 Element 4 of this Tuple4 + */ +final case class Tuple4[+T1, +T2, +T3, +T4](_1: T1, _2: T2, _3: T3, _4: T4) + extends Product4[T1, T2, T3, T4] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + ")" + +} diff --git a/library/src/scala/Tuple5.scala b/library/src/scala/Tuple5.scala new file mode 100644 index 000000000000..b6dbd2ea6cd4 --- /dev/null +++ b/library/src/scala/Tuple5.scala @@ -0,0 +1,32 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 5 elements; the canonical representation of a [[scala.Product5]]. + * + * @constructor Create a new tuple with 5 elements. Note that it is more idiomatic to create a Tuple5 via `(t1, t2, t3, t4, t5)` + * @param _1 Element 1 of this Tuple5 + * @param _2 Element 2 of this Tuple5 + * @param _3 Element 3 of this Tuple5 + * @param _4 Element 4 of this Tuple5 + * @param _5 Element 5 of this Tuple5 + */ +final case class Tuple5[+T1, +T2, +T3, +T4, +T5](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5) + extends Product5[T1, T2, T3, T4, T5] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + ")" + +} diff --git a/library/src/scala/Tuple6.scala b/library/src/scala/Tuple6.scala new file mode 100644 index 000000000000..834d81a43e84 --- /dev/null +++ b/library/src/scala/Tuple6.scala @@ -0,0 +1,33 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 6 elements; the canonical representation of a [[scala.Product6]]. + * + * @constructor Create a new tuple with 6 elements. Note that it is more idiomatic to create a Tuple6 via `(t1, t2, t3, t4, t5, t6)` + * @param _1 Element 1 of this Tuple6 + * @param _2 Element 2 of this Tuple6 + * @param _3 Element 3 of this Tuple6 + * @param _4 Element 4 of this Tuple6 + * @param _5 Element 5 of this Tuple6 + * @param _6 Element 6 of this Tuple6 + */ +final case class Tuple6[+T1, +T2, +T3, +T4, +T5, +T6](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6) + extends Product6[T1, T2, T3, T4, T5, T6] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + ")" + +} diff --git a/library/src/scala/Tuple7.scala b/library/src/scala/Tuple7.scala new file mode 100644 index 000000000000..d6e86752addd --- /dev/null +++ b/library/src/scala/Tuple7.scala @@ -0,0 +1,34 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 7 elements; the canonical representation of a [[scala.Product7]]. + * + * @constructor Create a new tuple with 7 elements. Note that it is more idiomatic to create a Tuple7 via `(t1, t2, t3, t4, t5, t6, t7)` + * @param _1 Element 1 of this Tuple7 + * @param _2 Element 2 of this Tuple7 + * @param _3 Element 3 of this Tuple7 + * @param _4 Element 4 of this Tuple7 + * @param _5 Element 5 of this Tuple7 + * @param _6 Element 6 of this Tuple7 + * @param _7 Element 7 of this Tuple7 + */ +final case class Tuple7[+T1, +T2, +T3, +T4, +T5, +T6, +T7](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7) + extends Product7[T1, T2, T3, T4, T5, T6, T7] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + ")" + +} diff --git a/library/src/scala/Tuple8.scala b/library/src/scala/Tuple8.scala new file mode 100644 index 000000000000..035d44e5330e --- /dev/null +++ b/library/src/scala/Tuple8.scala @@ -0,0 +1,35 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 8 elements; the canonical representation of a [[scala.Product8]]. + * + * @constructor Create a new tuple with 8 elements. Note that it is more idiomatic to create a Tuple8 via `(t1, t2, t3, t4, t5, t6, t7, t8)` + * @param _1 Element 1 of this Tuple8 + * @param _2 Element 2 of this Tuple8 + * @param _3 Element 3 of this Tuple8 + * @param _4 Element 4 of this Tuple8 + * @param _5 Element 5 of this Tuple8 + * @param _6 Element 6 of this Tuple8 + * @param _7 Element 7 of this Tuple8 + * @param _8 Element 8 of this Tuple8 + */ +final case class Tuple8[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8) + extends Product8[T1, T2, T3, T4, T5, T6, T7, T8] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + ")" + +} diff --git a/library/src/scala/Tuple9.scala b/library/src/scala/Tuple9.scala new file mode 100644 index 000000000000..50869e2c9b22 --- /dev/null +++ b/library/src/scala/Tuple9.scala @@ -0,0 +1,36 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp. + +package scala + + +/** A tuple of 9 elements; the canonical representation of a [[scala.Product9]]. + * + * @constructor Create a new tuple with 9 elements. Note that it is more idiomatic to create a Tuple9 via `(t1, t2, t3, t4, t5, t6, t7, t8, t9)` + * @param _1 Element 1 of this Tuple9 + * @param _2 Element 2 of this Tuple9 + * @param _3 Element 3 of this Tuple9 + * @param _4 Element 4 of this Tuple9 + * @param _5 Element 5 of this Tuple9 + * @param _6 Element 6 of this Tuple9 + * @param _7 Element 7 of this Tuple9 + * @param _8 Element 8 of this Tuple9 + * @param _9 Element 9 of this Tuple9 + */ +final case class Tuple9[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9) + extends Product9[T1, T2, T3, T4, T5, T6, T7, T8, T9] +{ + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + ")" + +} diff --git a/library/src/scala/UninitializedError.scala b/library/src/scala/UninitializedError.scala new file mode 100644 index 000000000000..84332c9a9d3e --- /dev/null +++ b/library/src/scala/UninitializedError.scala @@ -0,0 +1,19 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** This class represents uninitialized variable/value errors. + */ +// TODO: remove +@deprecated("will be removed in a future release", since = "2.12.7") +final class UninitializedError extends RuntimeException("uninitialized value") diff --git a/library/src/scala/UninitializedFieldError.scala b/library/src/scala/UninitializedFieldError.scala new file mode 100644 index 000000000000..d516abb68936 --- /dev/null +++ b/library/src/scala/UninitializedFieldError.scala @@ -0,0 +1,23 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** This class implements errors which are thrown whenever a + * field is used before it has been initialized. + * + * Such runtime checks are not emitted by default. + * They can be enabled by the `-Xcheckinit` compiler option. + */ +final case class UninitializedFieldError(msg: String) extends RuntimeException(msg) { + def this(obj: Any) = this("" + obj) +} diff --git a/library/src/scala/Unit.scala b/library/src/scala/Unit.scala new file mode 100644 index 000000000000..66fde8c72038 --- /dev/null +++ b/library/src/scala/Unit.scala @@ -0,0 +1,59 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. + +package scala + + +/** `Unit` is a subtype of [[scala.AnyVal]]. There is only one value of type + * `Unit`, `()`, and it is not represented by any object in the underlying + * runtime system. A method with return type `Unit` is analogous to a Java + * method which is declared `void`. + */ +final abstract class Unit private extends AnyVal { + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Unit] = ??? +} + +@scala.annotation.compileTimeOnly("`Unit` companion object is not allowed in source; instead, use `()` for the unit value") +object Unit extends AnyValCompanion { + + /** Transform a value type into a boxed reference type. + * + * This method is not intended for use in source code. + * The runtime representation of this value is platform specific. + * + * @param x the Unit to be boxed + * @return a scala.runtime.BoxedUnit offering `x` as its underlying value. + */ + def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT + + /** Transform a boxed type into a value type. Note that this + * method is not typesafe: it accepts any Object, but will throw + * an exception if the argument is not a scala.runtime.BoxedUnit. + * + * This method is not intended for use in source code. + * The result of successfully unboxing a value is `()`. + * + * @param x the scala.runtime.BoxedUnit to be unboxed. + * @throws ClassCastException if the argument is not a scala.runtime.BoxedUnit + * @return the Unit value () + */ + def unbox(x: java.lang.Object): Unit = x.asInstanceOf[scala.runtime.BoxedUnit] + + /** The String representation of the scala.Unit companion object. */ + override def toString = "object scala.Unit" +} + diff --git a/library/src/scala/ValueOf.scala b/library/src/scala/ValueOf.scala new file mode 100644 index 000000000000..30d5f7ff40c7 --- /dev/null +++ b/library/src/scala/ValueOf.scala @@ -0,0 +1,55 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** + * `ValueOf[T]` provides the unique value of the type `T` where `T` is a type which has a + * single inhabitant. Eligible types are singleton types of the form `stablePath.type`, + * Unit and singleton types corresponding to value literals. + * + * The value itself can conveniently be retrieved with [[Predef#valueOf]], which requires + * a `ValueOf` to be available in implicit scope. + * + * The compiler provides instances of `ValueOf[T]` for all eligible types. Typically + * an instance would be required where a runtime value corresponding to a type level + * computation is needed. + + * For example, we might define a type `Residue[M <: Int]` corresponding to the group of + * integers modulo `M`. We could then mandate that residues can be summed only when they + * are parameterized by the same modulus, + * + * {{{ + * case class Residue[M <: Int](n: Int) extends AnyVal { + * def +(rhs: Residue[M])(implicit m: ValueOf[M]): Residue[M] = + * Residue((this.n + rhs.n) % valueOf[M]) + * } + * + * val fiveModTen = Residue[10](5) + * val nineModTen = Residue[10](9) + * + * fiveModTen + nineModTen // OK == Residue[10](4) + * + * val fourModEleven = Residue[11](4) + * + * fiveModTen + fourModEleven // compiler error: type mismatch; + * // found : Residue[11] + * // required: Residue[10] + * }}} + * + * Notice that here the modulus is encoded in the type of the values and so does not + * incur any additional per-value storage cost. When a runtime value of the modulus + * is required in the implementation of `+` it is provided at the call site via the + * implicit argument `m` of type `ValueOf[M]`. + */ +@scala.annotation.implicitNotFound(msg = "No singleton value available for ${T}; eligible singleton types for `ValueOf` synthesis include literals and stable paths.") +final class ValueOf[T](val value: T) extends AnyVal diff --git a/library/src/scala/deprecated.scala b/library/src/scala/deprecated.scala new file mode 100644 index 000000000000..bb1ded88437c --- /dev/null +++ b/library/src/scala/deprecated.scala @@ -0,0 +1,64 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.meta._ + +/** An annotation that designates that a definition is deprecated. + * A deprecation warning is issued upon usage of the annotated definition. + * + * Library authors should state the library's deprecation policy in their documentation to give + * developers guidance on how long a deprecated definition will be preserved. + * + * Library authors should prepend the name of their library to the version number to help + * developers distinguish deprecations coming from different libraries: + * + * {{{ + * @deprecated("this method will be removed", "FooLib 12.0") + * def oldMethod(x: Int) = ... + * }}} + * + * The compiler will emit deprecation warnings grouped by library and version: + * + * {{{ + * oldMethod(1) + * oldMethod(2) + * aDeprecatedMethodFromLibraryBar(3, 4) + * + * // warning: there was one deprecation warning (since BarLib 3.2) + * // warning: there were two deprecation warnings (since FooLib 12.0) + * // warning: there were three deprecation warnings in total; re-run with -deprecation for details + * }}} + * + * The Scala compiler also warns about using definitions annotated with [[java.lang.Deprecated]]. However it is + * recommended to use the Scala `@deprecated` annotation in Scala code because it allows providing a deprecation message. + * + * '''`@deprecated` in the Scala language and its standard library'''
+ * + * A deprecated element of the Scala language or a definition in the Scala standard library will + * be preserved at least for the current major version. + * + * This means that an element deprecated in some 2.13.x release will be preserved in + * all 2.13.x releases, but may be removed in the future. (A deprecated element + * might be kept longer to ease migration, but developers should not rely on this.) + * + * @see The official documentation on [[https://www.scala-lang.org/news/2.11.0/#binary-compatibility binary compatibility]]. + * @param message the message to print during compilation if the definition is accessed + * @param since a string identifying the first version in which the definition was deprecated + * @see [[scala.deprecatedInheritance]] + * @see [[scala.deprecatedOverriding]] + * @see [[scala.deprecatedName]] + */ +@getter @setter @beanGetter @beanSetter @field +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class deprecated(message: String = "", since: String = "") extends scala.annotation.ConstantAnnotation diff --git a/library/src/scala/deprecatedInheritance.scala b/library/src/scala/deprecatedInheritance.scala new file mode 100644 index 000000000000..f95b3ef100a0 --- /dev/null +++ b/library/src/scala/deprecatedInheritance.scala @@ -0,0 +1,50 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.meta._ + +/** An annotation that designates that inheriting from a class is deprecated. + * + * This is usually done to warn about a non-final class being made final in a future version. + * Sub-classing such a class then generates a warning. + * + * No warnings are generated if the subclass is in the same compilation unit. + * + * Library authors should state the library's deprecation policy in their documentation to give + * developers guidance on when a type annotated with `@deprecatedInheritance` will be `final`ized. + * + * Library authors should prepend the name of their library to the version number to help + * developers distinguish deprecations coming from different libraries: + * + * {{{ + * @deprecatedInheritance("this class will be made final", "FooLib 12.0") + * class Foo + * }}} + * + * {{{ + * val foo = new Foo // no deprecation warning + * class Bar extends Foo + * // warning: inheritance from class Foo is deprecated (since FooLib 12.0): this class will be made final + * // class Bar extends Foo + * // ^ + * }}} + * + * @param message the message to print during compilation if the class was sub-classed + * @param since a string identifying the first version in which inheritance was deprecated + * @see [[scala.deprecated]] + * @see [[scala.deprecatedOverriding]] + * @see [[scala.deprecatedName]] + */ +@getter @setter @beanGetter @beanSetter +final class deprecatedInheritance(message: String = "", since: String = "") extends scala.annotation.ConstantAnnotation diff --git a/library/src/scala/deprecatedName.scala b/library/src/scala/deprecatedName.scala new file mode 100644 index 000000000000..1a8341b8d498 --- /dev/null +++ b/library/src/scala/deprecatedName.scala @@ -0,0 +1,53 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.meta._ + +/** An annotation that designates that the name of a parameter is deprecated. + * + * Using this name in a named argument generates a deprecation warning. + * + * If the `name` is omitted, then using the canonical name is deprecated. + * In that case, lints such as `-Xlint:named-booleans` which encourage + * the use of a name will not warn. + * + * Library authors should state the library's deprecation policy in their documentation to give + * developers guidance on how long a deprecated name will be preserved. + * + * Library authors should prepend the name of their library to the version number to help + * developers distinguish deprecations coming from different libraries: + * + * {{{ + * def inc(x: Int, @deprecatedName("y", "FooLib 12.0") n: Int): Int = x + n + * inc(1, y = 2) + * }}} + * will produce the following warning: + * {{{ + * warning: the parameter name y is deprecated (since FooLib 12.0): use n instead + * inc(1, y = 2) + * ^ + * }}} + * + * @see [[scala.deprecated]] + * @see [[scala.deprecatedInheritance]] + * @see [[scala.deprecatedOverriding]] + */ +@param +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class deprecatedName(name: String = "", since: String = "") extends scala.annotation.StaticAnnotation { + // at the time we remove these constructors, we should also change this from a StaticAnnotation to + // a ConstantAnnotation; for now, the presence of auxiliary constructors blocks that change + @deprecated("The parameter name should be a String, not a symbol.", "2.13.0") def this(name: Symbol, since: String) = this(name.name, since) + @deprecated("The parameter name should be a String, not a symbol.", "2.13.0") def this(name: Symbol) = this(name.name, "") +} diff --git a/library/src/scala/deprecatedOverriding.scala b/library/src/scala/deprecatedOverriding.scala new file mode 100644 index 000000000000..0268bee15a10 --- /dev/null +++ b/library/src/scala/deprecatedOverriding.scala @@ -0,0 +1,52 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.meta._ + +/** An annotation that designates that overriding a member is deprecated. + * + * Overriding such a member in a sub-class then generates a warning. + * + * Library authors should state the library's deprecation policy in their documentation to give + * developers guidance on when a method annotated with `@deprecatedOverriding` will be `final`ized. + * + * Library authors should prepend the name of their library to the version number to help + * developers distinguish deprecations coming from different libraries: + * + * {{{ + * class Foo { + * @deprecatedOverriding("this method will be made final", "FooLib 12.0") + * def add(x: Int, y: Int) = x + y + * } + * }}} + * + * {{{ + * class Bar extends Foo // no deprecation warning + * class Baz extends Foo { + * override def add(x: Int, y: Int) = x - y + * } + * // warning: overriding method add in class Foo is deprecated (since FooLib 12.0): this method will be made final + * // override def add(x: Int, y: Int) = x - y + * // ^ + * }}} + * + * @param message the message to print during compilation if the member was overridden + * @param since a string identifying the first version in which overriding was deprecated + * @see [[scala.deprecated]] + * @see [[scala.deprecatedInheritance]] + * @see [[scala.deprecatedName]] + */ +@getter @setter @beanGetter @beanSetter +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class deprecatedOverriding(message: String = "", since: String = "") extends scala.annotation.ConstantAnnotation diff --git a/library/src/scala/inline.scala b/library/src/scala/inline.scala new file mode 100644 index 000000000000..d7d7b55d8d3c --- /dev/null +++ b/library/src/scala/inline.scala @@ -0,0 +1,52 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** + * An annotation for methods that the optimizer should inline. + * + * Note that by default, the Scala optimizer is disabled and no callsites are inlined. See + * `-opt:help` and [[https://docs.scala-lang.org/overviews/compiler-options/optimizer.html the overview document]] + * for information on how to enable the optimizer and inliner. + * + * When inlining is enabled, the inliner will always try to inline methods or callsites annotated + * `@inline` (under the condition that inlining from the defining class is allowed). + * If inlining is not possible, for example because the method is not + * final, an optimizer warning will be issued. See `-Wopt:help` for details. + * + * Examples: + * + * {{{ + * @inline final def f1(x: Int) = x + * @noinline final def f2(x: Int) = x + * final def f3(x: Int) = x + * + * def t1 = f1(1) // inlined if possible + * def t2 = f2(1) // not inlined + * def t3 = f3(1) // may be inlined (the inliner heuristics can select the callsite) + * def t4 = f1(1): @noinline // not inlined (override at callsite) + * def t5 = f2(1): @inline // inlined if possible (override at callsite) + * def t6 = f3(1): @inline // inlined if possible + * def t7 = f3(1): @noinline // not inlined + * } + * }}} + * + * Note: parentheses are required when annotating a callsite within a larger expression. + * + * {{{ + * def t1 = f1(1) + f1(1): @noinline // equivalent to (f1(1) + f1(1)): @noinline + * def t2 = f1(1) + (f1(1): @noinline) // the second call to f1 is not inlined + * }}} + */ +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class inline extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/language.scala b/library/src/scala/language.scala new file mode 100644 index 000000000000..d0359b8ac55f --- /dev/null +++ b/library/src/scala/language.scala @@ -0,0 +1,214 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** + * The `scala.language` object controls the language features available to the programmer, as proposed in the + * [[https://docs.google.com/document/d/1nlkvpoIRkx7at1qJEZafJwthZ3GeIklTFhqmXMvTX9Q/edit '''SIP-18 document''']]. + * + * Each of these features has to be explicitly imported into the current scope to become available: + * {{{ + * import language.postfixOps // or language._ + * List(1, 2, 3) reverse + * }}} + * + * The language features are: + * - [[dynamics `dynamics`]] enables defining calls rewriting using the [[scala.Dynamic `Dynamic`]] trait + * - [[existentials `existentials`]] enables writing existential types + * - [[higherKinds `higherKinds`]] enables writing higher-kinded types + * - [[implicitConversions `implicitConversions`]] enables defining implicit methods and members + * - [[postfixOps `postfixOps`]] enables postfix operators (not recommended) + * - [[reflectiveCalls `reflectiveCalls`]] enables using structural types + * - [[experimental `experimental`]] contains newer features that have not yet been tested in production + * + * @groupname production Language Features + * @groupname experimental Experimental Language Features + * @groupprio experimental 10 + */ +object language { + + import languageFeature._ + + /** Only where this feature is enabled, can direct or indirect subclasses of trait scala.Dynamic + * be defined. If `dynamics` is not enabled, a definition of a class, trait, + * or object that has `Dynamic` as a base trait is rejected by the compiler. + * + * Selections of dynamic members of existing subclasses of trait `Dynamic` are unaffected; + * they can be used anywhere. + * + * '''Why introduce the feature?''' To enable flexible DSLs and convenient interfacing + * with dynamic languages. + * + * '''Why control it?''' Dynamic member selection can undermine static checkability + * of programs. Furthermore, dynamic member selection often relies on reflection, + * which is not available on all platforms. + * + * @group production + */ + implicit lazy val dynamics: dynamics = languageFeature.dynamics + + /** Only where this feature is enabled, is postfix operator notation `(expr op)` permitted. + * If `postfixOps` is not enabled, an expression using postfix notation is rejected by the compiler. + * + * '''Why keep the feature?''' Postfix notation is preserved for backward + * compatibility only. Historically, several DSLs written in Scala need the notation. + * + * '''Why control it?''' Postfix operators interact poorly with semicolon inference. + * Most programmers avoid them for this reason alone. Postfix syntax is + * associated with an abuse of infix notation, `a op1 b op2 c op3`, + * that can be harder to read than ordinary method invocation with judicious + * use of parentheses. It is recommended not to enable this feature except for + * legacy code. + * + * @group production + */ + implicit lazy val postfixOps: postfixOps = languageFeature.postfixOps + + /** Where this feature is enabled, accesses to members of structural types that need + * reflection are supported. If `reflectiveCalls` is not enabled, an expression + * requiring reflection will trigger a warning from the compiler. + * + * A structural type is a type of the form + * `Parents { Decls }` where `Decls` contains declarations of new members that do + * not override any member in `Parents`. To access one of these members, a + * reflective call is needed. + * + * '''Why keep the feature?''' Structural types provide great flexibility because + * they avoid the need to define inheritance hierarchies a priori. Besides, + * their definition falls out quite naturally from Scala’s concept of type refinement. + * + * '''Why control it?''' Reflection is not available on all platforms. Popular tools + * such as ProGuard have problems dealing with it. Even where reflection is available, + * reflective dispatch can lead to surprising performance degradations. + * + * @group production + */ + implicit lazy val reflectiveCalls: reflectiveCalls = languageFeature.reflectiveCalls + + /** Where this feature is enabled, definitions of implicit conversion methods are allowed. + * If `implicitConversions` is not enabled, the definition of an implicit + * conversion method will trigger a warning from the compiler. + * + * An implicit conversion is an implicit value of unary function type `A => B`, + * or an implicit method that has in its first parameter section a single, + * non-implicit parameter. Examples: + * + * {{{ + * implicit def intToString(i: Int): String = s"\$i" + * implicit val conv: Int => String = i => s"\$i" + * implicit val numerals: List[String] = List("zero", "one", "two", "three") + * implicit val strlen: String => Int = _.length + * implicit def listToInt[T](xs: List[T])(implicit f: T => Int): Int = xs.map(f).sum + * }}} + * + * This language feature warns only for implicit conversions introduced by methods. + * + * Other values, including functions or data types which extend `Function1`, + * such as `Map`, `Set`, and `List`, do not warn. + * + * Implicit class definitions, which introduce a conversion to the wrapping class, + * also do not warn. + * + * '''Why keep the feature?''' Implicit conversions are central to many aspects + * of Scala’s core libraries. + * + * '''Why control it?''' Implicit conversions are known to cause many pitfalls + * if over-used. And there is a tendency to over-use them because they look + * very powerful and their effects seem to be easy to understand. Also, in + * most situations using implicit parameters leads to a better design than + * implicit conversions. + * + * @group production + */ + implicit lazy val implicitConversions: implicitConversions = languageFeature.implicitConversions + + /** Where this feature is enabled, higher-kinded types can be written. + * If `higherKinds` is not enabled, a higher-kinded type such as `F[A]` + * will trigger a warning from the compiler. + * + * '''Why keep the feature?''' Higher-kinded types enable the definition of very general + * abstractions such as functor, monad, or arrow. A significant set of advanced + * libraries relies on them. Higher-kinded types are also at the core of the + * scala-virtualized effort to produce high-performance parallel DSLs through staging. + * + * '''Why control it?''' Higher kinded types in Scala lead to a Turing-complete + * type system, where compiler termination is no longer guaranteed. They tend + * to be useful mostly for type-level computation and for highly generic design + * patterns. The level of abstraction implied by these design patterns is often + * a barrier to understanding for newcomers to a Scala codebase. Some syntactic + * aspects of higher-kinded types are hard to understand for the uninitiated and + * type inference is less effective for them than for normal types. Because we are + * not completely happy with them yet, it is possible that some aspects of + * higher-kinded types will change in future versions of Scala. So an explicit + * enabling also serves as a warning that code involving higher-kinded types + * might have to be slightly revised in the future. + * + * @group production + */ + @deprecated("higherKinds no longer needs to be imported explicitly", "2.13.1") + implicit lazy val higherKinds: higherKinds = languageFeature.higherKinds + + /** Where this feature is enabled, existential types that cannot be expressed as wildcard + * types can be written and are allowed in inferred types of values or return + * types of methods. If `existentials` is not enabled, those cases will trigger + * a warning from the compiler. + * + * Existential types with wildcard type syntax such as `List[_]`, + * or `Map[String, _]` are not affected. + * + * '''Why keep the feature?''' Existential types are needed to make sense of Java’s wildcard + * types and raw types and the erased types of run-time values. + * + * '''Why control it?''' Having complex existential types in a code base usually makes + * application code very brittle, with a tendency to produce type errors with + * obscure error messages. Therefore, going overboard with existential types + * is generally perceived not to be a good idea. Also, complicated existential types + * might be no longer supported in a future simplification of the language. + * + * @group production + */ + implicit lazy val existentials: existentials = languageFeature.existentials + + /** The experimental object contains features that are known to have unstable API or + * behavior that may change in future releases. + * + * Experimental features '''may undergo API changes''' in future releases, so production + * code should not rely on them. + * + * Programmers are encouraged to try out experimental features and + * [[https://github.com/scala/bug/issues report any bugs or API inconsistencies]] + * they encounter so they can be improved in future releases. + * + * @group experimental + */ + object experimental { + + import languageFeature.experimental._ + + /** Only where this feature is enabled, are macro definitions allowed. + * If `macros` is not enabled, macro definitions are rejected by the compiler. + * + * Macro implementations and macro applications are not governed by this + * language feature; they can be used anywhere. + * + * '''Why introduce the feature?''' Macros promise to make the language more regular, + * replacing ad-hoc language constructs with a general powerful abstraction + * capability that can express them. Macros are also a more disciplined and + * powerful replacement for compiler plugins. + * + * '''Why control it?''' For their very power, macros can lead to code that is hard + * to debug and understand. + */ + implicit lazy val macros: macros = languageFeature.experimental.macros + } +} diff --git a/library/src/scala/languageFeature.scala b/library/src/scala/languageFeature.scala new file mode 100644 index 000000000000..236774c990ba --- /dev/null +++ b/library/src/scala/languageFeature.scala @@ -0,0 +1,51 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.meta + +object languageFeature { + + @meta.languageFeature("extension of type scala.Dynamic", enableRequired = true) + sealed trait dynamics + object dynamics extends dynamics + + @meta.languageFeature("postfix operator #", enableRequired = true) + sealed trait postfixOps + object postfixOps extends postfixOps + + @meta.languageFeature("reflective access of structural type member #", enableRequired = false) + sealed trait reflectiveCalls + object reflectiveCalls extends reflectiveCalls + + @meta.languageFeature("implicit conversion #", enableRequired = false) + sealed trait implicitConversions + object implicitConversions extends implicitConversions + + @deprecated("scala.language.higherKinds no longer needs to be imported explicitly", "2.13.1") + @meta.languageFeature("higher-kinded type", enableRequired = false) + sealed trait higherKinds + @deprecated("scala.language.higherKinds no longer needs to be imported explicitly", "2.13.1") + object higherKinds extends higherKinds + + @meta.languageFeature("#, which cannot be expressed by wildcards,", enableRequired = false) + sealed trait existentials + object existentials extends existentials + + object experimental { + @meta.languageFeature("macro definition", enableRequired = true) + sealed trait macros + object macros extends macros + } +} + diff --git a/library/src/scala/native.scala b/library/src/scala/native.scala new file mode 100644 index 000000000000..6a453d1809d1 --- /dev/null +++ b/library/src/scala/native.scala @@ -0,0 +1,27 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** Marker for native methods. + * + * {{{ + * @native def f(x: Int, y: List[Long]): String = ... + * }}} + * + * A `@native` method is compiled to the platform's native method, + * while discarding the method's body (if any). The body will be type checked if present. + * + * A method marked @native must be a member of a class, not a trait (since 2.12). + */ +@deprecatedInheritance("Scheduled for being final in the future", "2.13.0") +class native extends scala.annotation.StaticAnnotation {} diff --git a/library/src/scala/noinline.scala b/library/src/scala/noinline.scala new file mode 100644 index 000000000000..eede8d5051f2 --- /dev/null +++ b/library/src/scala/noinline.scala @@ -0,0 +1,48 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** + * An annotation for methods that the optimizer should not inline. + * + * Note that by default, the Scala optimizer is disabled and no callsites are inlined. See + * `-opt:help` for information how to enable the optimizer and inliner. + * + * When inlining is enabled, the inliner will never inline methods or callsites annotated + * `@noinline`. + * + * Examples: + * + * {{{ + * @inline final def f1(x: Int) = x + * @noinline final def f2(x: Int) = x + * final def f3(x: Int) = x + * + * def t1 = f1(1) // inlined if possible + * def t2 = f2(1) // not inlined + * def t3 = f3(1) // may be inlined (the inliner heuristics can select the callsite) + * def t4 = f1(1): @noinline // not inlined (override at callsite) + * def t5 = f2(1): @inline // inlined if possible (override at callsite) + * def t6 = f3(1): @inline // inlined if possible + * def t7 = f3(1): @noinline // not inlined + * } + * }}} + * + * Note: parentheses are required when annotating a callsite within a larger expression. + * + * {{{ + * def t1 = f1(1) + f1(1): @noinline // equivalent to (f1(1) + f1(1)): @noinline + * def t2 = f1(1) + (f1(1): @noinline) // the second call to f1 is not inlined + * }}} + */ +final class noinline extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/package.scala b/library/src/scala/package.scala new file mode 100644 index 000000000000..f84dcfd85a73 --- /dev/null +++ b/library/src/scala/package.scala @@ -0,0 +1,147 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +import scala.annotation.migration + +/** + * Core Scala types. They are always available without an explicit import. + * @contentDiagram hideNodes "scala.Serializable" + */ +package object scala { + type Cloneable = java.lang.Cloneable + type Serializable = java.io.Serializable + + type Throwable = java.lang.Throwable + type Exception = java.lang.Exception + type Error = java.lang.Error + + type RuntimeException = java.lang.RuntimeException + type NullPointerException = java.lang.NullPointerException + type ClassCastException = java.lang.ClassCastException + type IndexOutOfBoundsException = java.lang.IndexOutOfBoundsException + type ArrayIndexOutOfBoundsException = java.lang.ArrayIndexOutOfBoundsException + type StringIndexOutOfBoundsException = java.lang.StringIndexOutOfBoundsException + type UnsupportedOperationException = java.lang.UnsupportedOperationException + type IllegalArgumentException = java.lang.IllegalArgumentException + type NoSuchElementException = java.util.NoSuchElementException + type NumberFormatException = java.lang.NumberFormatException + type AbstractMethodError = java.lang.AbstractMethodError + type InterruptedException = java.lang.InterruptedException + + // A dummy used by the specialization annotation. + val AnyRef = new Specializable { + override def toString = "object AnyRef" + } + + @deprecated("Use IterableOnce instead of TraversableOnce", "2.13.0") + type TraversableOnce[+A] = scala.collection.IterableOnce[A] + + type IterableOnce[+A] = scala.collection.IterableOnce[A] + + @deprecated("Use Iterable instead of Traversable", "2.13.0") + type Traversable[+A] = scala.collection.Iterable[A] + @deprecated("Use Iterable instead of Traversable", "2.13.0") + val Traversable = scala.collection.Iterable + + type Iterable[+A] = scala.collection.Iterable[A] + val Iterable = scala.collection.Iterable + + @migration("scala.Seq is now scala.collection.immutable.Seq instead of scala.collection.Seq", "2.13.0") + type Seq[+A] = scala.collection.immutable.Seq[A] + val Seq = scala.collection.immutable.Seq + + @migration("scala.IndexedSeq is now scala.collection.immutable.IndexedSeq instead of scala.collection.IndexedSeq", "2.13.0") + type IndexedSeq[+A] = scala.collection.immutable.IndexedSeq[A] + val IndexedSeq = scala.collection.immutable.IndexedSeq + + type Iterator[+A] = scala.collection.Iterator[A] + val Iterator = scala.collection.Iterator + + @deprecated("Use scala.collection.BufferedIterator instead of scala.BufferedIterator", "2.13.0") + type BufferedIterator[+A] = scala.collection.BufferedIterator[A] + + type List[+A] = scala.collection.immutable.List[A] + val List = scala.collection.immutable.List + + val Nil = scala.collection.immutable.Nil + + type ::[+A] = scala.collection.immutable.::[A] + val :: = scala.collection.immutable.:: + + val +: = scala.collection.+: + val :+ = scala.collection.:+ + + @deprecated("Use LazyList instead of Stream", "2.13.0") + type Stream[+A] = scala.collection.immutable.Stream[A] + @deprecated("Use LazyList instead of Stream", "2.13.0") + val Stream = scala.collection.immutable.Stream + + type LazyList[+A] = scala.collection.immutable.LazyList[A] + val LazyList = scala.collection.immutable.LazyList + // This should be an alias to LazyList.#:: but we need to support Stream, too + //val #:: = scala.collection.immutable.LazyList.#:: + object #:: { + def unapply[A](s: LazyList[A]): Option[(A, LazyList[A])] = + if (s.nonEmpty) Some((s.head, s.tail)) else None + @deprecated("Prefer LazyList instead", since = "2.13.0") + def unapply[A](s: Stream[A]): Option[(A, Stream[A])] = + if (s.nonEmpty) Some((s.head, s.tail)) else None + } + + type Vector[+A] = scala.collection.immutable.Vector[A] + val Vector = scala.collection.immutable.Vector + + type StringBuilder = scala.collection.mutable.StringBuilder + val StringBuilder = scala.collection.mutable.StringBuilder + + type Range = scala.collection.immutable.Range + val Range = scala.collection.immutable.Range + + // Numeric types which were moved into scala.math.* + + type BigDecimal = scala.math.BigDecimal + val BigDecimal = scala.math.BigDecimal + + type BigInt = scala.math.BigInt + val BigInt = scala.math.BigInt + + type Equiv[T] = scala.math.Equiv[T] + val Equiv = scala.math.Equiv + + type Fractional[T] = scala.math.Fractional[T] + val Fractional = scala.math.Fractional + + type Integral[T] = scala.math.Integral[T] + val Integral = scala.math.Integral + + type Numeric[T] = scala.math.Numeric[T] + val Numeric = scala.math.Numeric + + type Ordered[T] = scala.math.Ordered[T] + val Ordered = scala.math.Ordered + + type Ordering[T] = scala.math.Ordering[T] + val Ordering = scala.math.Ordering + + type PartialOrdering[T] = scala.math.PartialOrdering[T] + type PartiallyOrdered[T] = scala.math.PartiallyOrdered[T] + + type Either[+A, +B] = scala.util.Either[A, B] + val Either = scala.util.Either + + type Left[+A, +B] = scala.util.Left[A, B] + val Left = scala.util.Left + + type Right[+A, +B] = scala.util.Right[A, B] + val Right = scala.util.Right + +} diff --git a/library/src/scala/specialized.scala b/library/src/scala/specialized.scala new file mode 100644 index 000000000000..c099612e1370 --- /dev/null +++ b/library/src/scala/specialized.scala @@ -0,0 +1,34 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import Specializable._ + +/** Annotate type parameters on which code should be automatically + * specialized. For example: + * {{{ + * class MyList[@specialized T] ... + * }}} + * + * Type T can be specialized on a subset of the primitive types by + * specifying a list of primitive types to specialize at: + * {{{ + * class MyList[@specialized(Int, Double, Boolean) T] .. + * }}} + */ +// class tspecialized[T](group: Group[T]) extends scala.annotation.StaticAnnotation { + +final class specialized(group: SpecializedGroup) extends scala.annotation.StaticAnnotation { + def this(types: Specializable*) = this(new Group(types.toList)) + def this() = this(Primitives) +} diff --git a/library/src/scala/throws.scala b/library/src/scala/throws.scala new file mode 100644 index 000000000000..d5391ce9242c --- /dev/null +++ b/library/src/scala/throws.scala @@ -0,0 +1,28 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** + * Annotation for specifying the exceptions thrown by a method. + * For example: + * {{{ + * class Reader(fname: String) { + * private val in = new BufferedReader(new FileReader(fname)) + * @throws[IOException]("if the file doesn't exist") + * def read() = in.read() + * } + * }}} + */ +final class throws[T <: Throwable](cause: String = "") extends scala.annotation.StaticAnnotation { + def this(clazz: Class[T]) = this("") +} diff --git a/library/src/scala/transient.scala b/library/src/scala/transient.scala new file mode 100644 index 000000000000..b40ce540547a --- /dev/null +++ b/library/src/scala/transient.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.meta._ + +@field +final class transient extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/typeConstraints.scala b/library/src/scala/typeConstraints.scala new file mode 100644 index 000000000000..f17205e122cf --- /dev/null +++ b/library/src/scala/typeConstraints.scala @@ -0,0 +1,243 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.implicitNotFound + +/** An instance of `A <:< B` witnesses that `A` is a subtype of `B`. + * Requiring an implicit argument of the type `A <:< B` encodes + * the generalized constraint `A <: B`. + * + * To constrain any abstract type `T` that's in scope in a method's + * argument list (not just the method's own type parameters) simply + * add an implicit argument of type `T <:< U`, where `U` is the required + * upper bound; or for lower-bounds, use: `L <:< T`, where `L` is the + * required lower bound. + * + * In case of any confusion over which method goes in what direction, all the "Co" methods (including + * [[apply]]) go from left to right in the type ("with" the type), and all the "Contra" methods go + * from right to left ("against" the type). E.g., [[apply]] turns a `From` into a `To`, and + * [[substituteContra]] replaces the `To`s in a type with `From`s. + * + * In part contributed by Jason Zaugg. + * + * @tparam From a type which is proved a subtype of `To` + * @tparam To a type which is proved a supertype of `From` + * + * @example [[scala.Option#flatten]] + * {{{ + * sealed trait Option[+A] { + * // def flatten[B, A <: Option[B]]: Option[B] = ... + * // won't work, since the A in flatten shadows the class-scoped A. + * def flatten[B](implicit ev: A <:< Option[B]): Option[B] + * = if(isEmpty) None else ev(get) + * // Because (A <:< Option[B]) <: (A => Option[B]), ev can be called to turn the + * // A from get into an Option[B], and because ev is implicit, that call can be + * // left out and inserted automatically. + * } + * }}} + * + * @see [[=:=]] for expressing equality constraints + * + * @define isProof This method is impossible to implement without `throw`ing or otherwise "cheating" unless + * `From <: To`, so it ensures that this really represents a subtyping relationship. + * @define contraCo contravariant in the first argument and covariant in the second + * @define contraCon a contravariant type constructor + * @define coCon a covariant type constructor + * @define sameDiff but with a (potentially) different type + * @define tp <:< + */ +// All of these methods are reimplemented unsafely in =:=.singleton to avoid any indirection. +// They are here simply for reference as the "correct", safe implementations. +@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.") +sealed abstract class <:<[-From, +To] extends (From => To) with Serializable { + /** Substitute `To` for `From` and `From` for `To` in the type `F[To, From]`, given that `F` is $contraCo. + * Essentially swaps `To` and `From` in `ftf`'s type. + * + * Equivalent in power to each of [[substituteCo]] and [[substituteContra]]. + * + * $isProof + * + * @return `ftf`, $sameDiff + */ + def substituteBoth[F[-_, +_]](ftf: F[To, From]): F[From, To] + // = substituteCo[({type G[+T] = F[From, T]})#G](substituteContra[({type G[-T] = F[T, From})#G](ftf)) + // = substituteContra[({type G[-T] = F[T, To]})#G](substituteCo[({type G[+T] = F[From, T]})#G](ftf)) + /** Substitute the `From` in the type `F[From]`, where `F` is $coCon, for `To`. + * + * Equivalent in power to each of [[substituteBoth]] and [[substituteContra]]. + * + * $isProof + * + * @return `ff`, $sameDiff + */ + def substituteCo[F[+_]](ff: F[From]): F[To] = { + type G[-_, +T] = F[T] + substituteBoth[G](ff) + } + // = substituteContra[({type G[-T] = F[T] => F[To]})#G](identity)(ff) + /** Substitute the `To` in the type `F[To]`, where `F` is $contraCon, for `From`. + * + * Equivalent in power to each of [[substituteBoth]] and [[substituteCo]]. + * + * $isProof + * + * @return `ft`, $sameDiff + */ + def substituteContra[F[-_]](ft: F[To]): F[From] = { + type G[-T, +_] = F[T] + substituteBoth[G](ft) + } + // = substituteCo[({type G[+T] = F[T] => F[From]})#G](identity)(ft) + + /** Coerce a `From` into a `To`. This is guaranteed to be the identity function. + * + * This method is often called implicitly as an implicit `A $tp B` doubles as an implicit view `A => B`. + * + * @param f some value of type `From` + * @return `f`, $sameDiff + */ + override def apply(f: From): To = { + type Id[+X] = X + substituteCo[Id](f) + } + + override def compose[C](r: C => From): C => To = { + type G[+T] = C => T + substituteCo[G](r) + } + /** If `From <: To` and `C <: From`, then `C <: To` (subtyping is transitive) */ + def compose[C](r: C <:< From): C <:< To = { + type G[+T] = C <:< T + substituteCo[G](r) + } + override def andThen[C](r: To => C): From => C = { + type G[-T] = T => C + substituteContra[G](r) + } + /** If `From <: To` and `To <: C`, then `From <: C` (subtyping is transitive) */ + def andThen[C](r: To <:< C): From <:< C = { + type G[-T] = T <:< C + substituteContra[G](r) + } + + /** Lift this evidence over $coCon `F`. */ + def liftCo[F[+_]]: F[From] <:< F[To] = { + type G[+T] = F[From] <:< F[T] + substituteCo[G](implicitly[G[From]]) + } + /** Lift this evidence over $contraCon `F`. */ + def liftContra[F[-_]]: F[To] <:< F[From] = { + type G[-T] = F[To] <:< F[T] + substituteContra[G](implicitly[G[To]]) + } +} + +object <:< { + // the only instance for <:< and =:=, used to avoid overhead + private val singleton: =:=[Any, Any] = new =:=[Any,Any] { + override def substituteBoth[F[_, _]](ftf: F[Any, Any]) = ftf + override def substituteCo [F[_]](ff: F[Any]) = ff + override def substituteContra[F[_]](ff: F[Any]) = ff + override def apply(x: Any) = x + override def flip: Any =:= Any = this + override def compose[C](r: C => Any) = r + override def compose[C](r: C <:< Any) = r + override def compose[C](r: C =:= Any) = r + override def andThen[C](r: Any => C) = r + override def andThen[C](r: Any <:< C) = r + override def andThen[C](r: Any =:= C) = r + override def liftCo [F[_]] = asInstanceOf[F[Any] =:= F[Any]] + override def liftContra[F[_]] = asInstanceOf[F[Any] =:= F[Any]] + override def toString = "generalized constraint" + } + + /** `A =:= A` for all `A` (equality is reflexive). This also provides implicit views `A <:< B` + * when `A <: B`, because `(A =:= A) <: (A <:< A) <: (A <:< B)`. + */ + implicit def refl[A]: A =:= A = singleton.asInstanceOf[A =:= A] + // = new =:=[A, A] { override def substituteBoth[F[_, _]](faa: F[A, A]): F[A, A] = faa } + + /** If `A <: B` and `B <: A`, then `A = B` (subtyping is antisymmetric) */ + def antisymm[A, B](implicit l: A <:< B, r: B <:< A): A =:= B = singleton.asInstanceOf[A =:= B] + // = ??? (I don't think this is possible to implement "safely") +} + +/** An instance of `A =:= B` witnesses that the types `A` and `B` are equal. It also acts as a `A <:< B`, + * but not a `B <:< A` (directly) due to restrictions on subclassing. + * + * In case of any confusion over which method goes in what direction, all the "Co" methods (including + * [[apply]]) go from left to right in the type ("with" the type), and all the "Contra" methods go + * from right to left ("against" the type). E.g., [[apply]] turns a `From` into a `To`, and + * [[substituteContra]] replaces the `To`s in a type with `From`s. + * + * @tparam From a type which is proved equal to `To` + * @tparam To a type which is proved equal to `From` + * + * @example An in-place variant of [[scala.collection.mutable.ArrayBuffer#transpose]] {{{ + * implicit class BufOps[A](private val buf: ArrayBuffer[A]) extends AnyVal { + * def inPlaceTranspose[E]()(implicit ev: A =:= ArrayBuffer[E]) = ??? + * // Because ArrayBuffer is invariant, we can't make do with just a A <:< ArrayBuffer[E] + * // Getting buffers *out* from buf would work, but adding them back *in* wouldn't. + * } + * }}} + * @see [[<:<]] for expressing subtyping constraints + * + * @define isProof This method is impossible to implement without `throw`ing or otherwise "cheating" unless + * `From = To`, so it ensures that this really represents a type equality. + * @define contraCo a type constructor of two arguments + * @define contraCon any type constructor + * @define coCon any type constructor + * @define tp =:= + */ +// Most of the notes on <:< above apply to =:= as well +@implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.") +sealed abstract class =:=[From, To] extends (From <:< To) with Serializable { + override def substituteBoth[F[_, _]](ftf: F[To, From]): F[From, To] + override def substituteCo[F[_]](ff: F[From]): F[To] = { + type G[_, T] = F[T] + substituteBoth[G](ff) + } + // = substituteContra[({type G[T] = F[T] => F[To]})#G](identity)(ff) + override def substituteContra[F[_]](ft: F[To]): F[From] = { + type G[T, _] = F[T] + substituteBoth[G](ft) + } + // = substituteCo[({type G[T] = F[T] => F[From]})#G](identity)(ft) + + /** @inheritdoc */ override def apply(f: From) = super.apply(f) + + /** If `From = To` then `To = From` (equality is symmetric) */ + def flip: To =:= From = { + type G[T, F] = F =:= T + substituteBoth[G](this) + } + + /** If `From = To` and `C = From`, then `C = To` (equality is transitive) */ + def compose[C](r: C =:= From): C =:= To = { + type G[T] = C =:= T + substituteCo[G](r) + } + /** If `From = To` and `To = C`, then `From = C` (equality is transitive) */ + def andThen[C](r: To =:= C): From =:= C = { + type G[T] = T =:= C + substituteContra[G](r) + } + + override def liftCo[F[_]]: F[From] =:= F[To] = { + type G[T] = F[T] =:= F[To] + substituteContra[G](implicitly[G[To]]) + } + /** Lift this evidence over the type constructor `F`, but flipped. */ + override def liftContra[F[_]]: F[To] =:= F[From] = liftCo[F].flip +} diff --git a/library/src/scala/unchecked.scala b/library/src/scala/unchecked.scala new file mode 100644 index 000000000000..6927db7293e5 --- /dev/null +++ b/library/src/scala/unchecked.scala @@ -0,0 +1,38 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +/** An annotation to designate that the annotated entity + * should not be considered for additional compiler checks. + * Specific applications include annotating the subject of + * a match expression to suppress exhaustiveness and reachability warnings, and + * annotating a type argument in a match case to suppress + * unchecked warnings. + * + * Such suppression should be used with caution, without which + * one may encounter [[scala.MatchError]] or [[java.lang.ClassCastException]] + * at runtime. In most cases one can and should address the + * warning instead of suppressing it. + * + * {{{ + * object Test extends App { + * // This would normally warn "match is not exhaustive" + * // because `None` is not covered. + * def f(x: Option[String]) = (x: @unchecked) match { case Some(y) => y } + * // This would normally warn "type pattern is unchecked" + * // but here will blindly cast the head element to String. + * def g(xs: Any) = xs match { case x: List[String @unchecked] => x.head } + * } + * }}} + */ +final class unchecked extends scala.annotation.Annotation {} diff --git a/library/src/scala/volatile.scala b/library/src/scala/volatile.scala new file mode 100644 index 000000000000..2f4f3c37e676 --- /dev/null +++ b/library/src/scala/volatile.scala @@ -0,0 +1,18 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.annotation.meta._ + +@field +final class volatile extends scala.annotation.StaticAnnotation From fe34107f7c72f399910127188912c65f18c33b5d Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 14:10:30 +0100 Subject: [PATCH 24/28] chore: dotc cannot compile AnyVal --- library/src/scala/AnyVal.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/src/scala/AnyVal.scala b/library/src/scala/AnyVal.scala index 79be48d642be..412b02c44c16 100644 --- a/library/src/scala/AnyVal.scala +++ b/library/src/scala/AnyVal.scala @@ -9,7 +9,7 @@ * See the NOTICE file distributed with this work for * additional information regarding copyright ownership. */ - +/* package scala /** `AnyVal` is the root class of all ''value types'', which describe values @@ -56,3 +56,4 @@ package scala abstract class AnyVal extends Any { def getClass(): Class[_ <: AnyVal] = null } +*/ \ No newline at end of file From 2e1e154c526cebf60f1710487ff56808b25f4176 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 14:49:00 +0100 Subject: [PATCH 25/28] chore: copy memebrs from stdlibPatches to Predef and language --- library/src/scala/Predef.scala | 122 +++++++++--- library/src/scala/language.scala | 317 +++++++++++++++++++++++++++++++ 2 files changed, 410 insertions(+), 29 deletions(-) diff --git a/library/src/scala/Predef.scala b/library/src/scala/Predef.scala index 26dbc568a9ab..baf6a220c6f9 100644 --- a/library/src/scala/Predef.scala +++ b/library/src/scala/Predef.scala @@ -15,8 +15,10 @@ package scala import scala.language.implicitConversions import scala.collection.{mutable, immutable, ArrayOps, StringOps}, immutable.WrappedString -import scala.annotation.{elidable, implicitNotFound}, elidable.ASSERTION +import scala.annotation.{elidable, implicitNotFound, experimental}, elidable.ASSERTION import scala.annotation.meta.{ companionClass, companionMethod } +import scala.annotation.internal.RuntimeChecked +import scala.compiletime.summonFrom /** The `Predef` object provides definitions that are accessible in all Scala * compilation units without explicit qualification. @@ -250,34 +252,11 @@ object Predef extends LowPriorityImplicits { // assertions --------------------------------------------------------- - /** Tests an expression, throwing an `AssertionError` if false. - * Calls to this method will not be generated if `-Xelide-below` - * is greater than `ASSERTION`. - * - * @see [[scala.annotation.elidable elidable]] - * @param assertion the expression to test - * @group assertions - */ - @elidable(ASSERTION) - def assert(assertion: Boolean): Unit = { - if (!assertion) - throw new java.lang.AssertionError("assertion failed") - } + transparent inline def assert(inline assertion: Boolean, inline message: => Any): Unit = + if !assertion then scala.runtime.Scala3RunTime.assertFailed(message) - /** Tests an expression, throwing an `AssertionError` if false. - * Calls to this method will not be generated if `-Xelide-below` - * is greater than `ASSERTION`. - * - * @see [[scala.annotation.elidable elidable]] - * @param assertion the expression to test - * @param message a String to include in the failure message - * @group assertions - */ - @elidable(ASSERTION) @inline - final def assert(assertion: Boolean, message: => Any): Unit = { - if (!assertion) - throw new java.lang.AssertionError("assertion failed: "+ message) - } + transparent inline def assert(inline assertion: Boolean): Unit = + if !assertion then scala.runtime.Scala3RunTime.assertFailed() /** Tests an expression, throwing an `AssertionError` if false. * This method differs from assert only in the intent expressed: @@ -370,7 +349,7 @@ object Predef extends LowPriorityImplicits { @inline def formatted(fmtstr: String): String = fmtstr format self } - /** Injects String concatenation operator `+` to any classes. + /** Injects String concatenation operator `+` to any classes. * @group implicit-classes-any */ @(deprecated @companionMethod)("Implicit injection of + is deprecated. Convert to String to call +", "2.13.0") @@ -508,6 +487,91 @@ object Predef extends LowPriorityImplicits { */ // $ to avoid accidental shadowing (e.g. scala/bug#7788) implicit def $conforms[A]: A => A = <:<.refl + + /** + * Retrieve the single value of a type with a unique inhabitant. + * + * @example {{{ + * object Foo + * val foo = valueOf[Foo.type] + * // foo is Foo.type = Foo + * + * val bar = valueOf[23] + * // bar is 23.type = 23 + * }}} + * @group utilities + */ + inline def valueOf[T]: T = summonFrom { + case ev: ValueOf[T] => ev.value + } + + /** Summon a given value of type `T`. Usually, the argument is not passed explicitly. + * + * @tparam T the type of the value to be summoned + * @return the given value typed: the provided type parameter + */ + transparent inline def summon[T](using x: T): x.type = x + + // Extension methods for working with explicit nulls + + /** Strips away the nullability from a value. Note that `.nn` performs a checked cast, + * so if invoked on a `null` value it will throw an `NullPointerException`. + * @example {{{ + * val s1: String | Null = "hello" + * val s2: String = s1.nn + * + * val s3: String | Null = null + * val s4: String = s3.nn // throw NullPointerException + * }}} + */ + extension [T](x: T | Null) inline def nn: x.type & T = + if x.asInstanceOf[Any] == null then scala.runtime.Scala3RunTime.nnFail() + x.asInstanceOf[x.type & T] + + extension (inline x: AnyRef | Null) + /** Enables an expression of type `T|Null`, where `T` is a subtype of `AnyRef`, to be checked for `null` + * using `eq` rather than only `==`. This is needed because `Null` no longer has + * `eq` or `ne` methods, only `==` and `!=` inherited from `Any`. */ + inline def eq(inline y: AnyRef | Null): Boolean = + x.asInstanceOf[AnyRef] eq y.asInstanceOf[AnyRef] + /** Enables an expression of type `T|Null`, where `T` is a subtype of `AnyRef`, to be checked for `null` + * using `ne` rather than only `!=`. This is needed because `Null` no longer has + * `eq` or `ne` methods, only `==` and `!=` inherited from `Any`. */ + inline def ne(inline y: AnyRef | Null): Boolean = + !(x eq y) + + extension (opt: Option.type) + @experimental + inline def fromNullable[T](t: T | Null): Option[T] = Option(t).asInstanceOf[Option[T]] + + /** A type supporting Self-based type classes. + * + * A is TC + * + * expands to + * + * TC { type Self = A } + * + * which is what is needed for a context bound `[A: TC]`. + */ + @experimental + infix type is[A <: AnyKind, B <: Any{type Self <: AnyKind}] = B { type Self = A } + + extension [T](x: T) + /**Asserts that a term should be exempt from static checks that can be reliably checked at runtime. + * @example {{{ + * val xs: Option[Int] = Option(1) + * xs.runtimeChecked match + * case Some(x) => x // `Some(_)` can be checked at runtime, so no warning + * }}} + * @example {{{ + * val xs: List[Int] = List(1,2,3) + * val y :: ys = xs.runtimeChecked // `_ :: _` can be checked at runtime, so no warning + * }}} + */ + @experimental + inline def runtimeChecked: x.type @RuntimeChecked = x: @RuntimeChecked + } /** The `LowPriorityImplicits` class provides implicit values that diff --git a/library/src/scala/language.scala b/library/src/scala/language.scala index d0359b8ac55f..72f523fa08b4 100644 --- a/library/src/scala/language.scala +++ b/library/src/scala/language.scala @@ -12,6 +12,8 @@ package scala +import scala.annotation.compileTimeOnly + /** * The `scala.language` object controls the language features available to the programmer, as proposed in the * [[https://docs.google.com/document/d/1nlkvpoIRkx7at1qJEZafJwthZ3GeIklTFhqmXMvTX9Q/edit '''SIP-18 document''']]. @@ -210,5 +212,320 @@ object language { * to debug and understand. */ implicit lazy val macros: macros = languageFeature.experimental.macros + + /* Experimental support for richer dependent types (disabled for now) + * One can still run the compiler with support for parsing singleton applications + * using command line option `-language:experimental.dependent`. + * But one cannot use a feature import for this as long as this entry is commented out. + */ + //object dependent + + /** Experimental support for named type arguments. + * + * @see [[https://dotty.epfl.ch/docs/reference/other-new-features/named-typeargs]] + */ + @compileTimeOnly("`namedTypeArguments` can only be used at compile time in import statements") + object namedTypeArguments + + /** Experimental support for generic number literals. + * + * @see [[https://dotty.epfl.ch/docs/reference/changed-features/numeric-literals]] + */ + @compileTimeOnly("`genericNumberLiterals` can only be used at compile time in import statements") + object genericNumberLiterals + + /** Experimental support for `erased` modifier + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/erased-defs]] + */ + @compileTimeOnly("`erasedDefinitions` can only be used at compile time in import statements") + object erasedDefinitions + + /** Experimental support for using indentation for arguments + */ + @compileTimeOnly("`fewerBraces` can only be used at compile time in import statements") + @deprecated("`fewerBraces` is now standard, no language import is needed", since = "3.3") + object fewerBraces + + /** Experimental support for typechecked exception capabilities + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/canthrow]] + */ + @compileTimeOnly("`saferExceptions` can only be used at compile time in import statements") + object saferExceptions + + /** Adds support for clause interleaving: + * Methods can now have as many type clauses as they like, this allows to have type bounds depend on terms: `def f(x: Int)[A <: x.type]: A` + * + * @see [[https://github.com/scala/improvement-proposals/blob/main/content/clause-interleaving.md]] + */ + @compileTimeOnly("`clauseInterleaving` can only be used at compile time in import statements") + @deprecated("`clauseInterleaving` is now standard, no language import is needed", since = "3.6") + object clauseInterleaving + + /** Experimental support for pure function type syntax + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/purefuns]] + */ + @compileTimeOnly("`pureFunctions` can only be used at compile time in import statements") + object pureFunctions + + /** Experimental support for capture checking; implies support for pureFunctions + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/cc]] + */ + @compileTimeOnly("`captureChecking` can only be used at compile time in import statements") + object captureChecking + + /** Experimental support for automatic conversions of arguments, without requiring + * a language import `import scala.language.implicitConversions`. + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]] + */ + @compileTimeOnly("`into` can only be used at compile time in import statements") + object into + + /** Experimental support for named tuples. + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/named-tuples]] + */ + @compileTimeOnly("`namedTuples` can only be used at compile time in import statements") + object namedTuples + + /** Experimental support for new features for better modularity, including + * - better tracking of dependencies through classes + * - better usability of context bounds + * - better syntax and conventions for type classes + * - ability to merge exported types in intersections + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/modularity]] + * @see [[https://dotty.epfl.ch/docs/reference/experimental/typeclasses]] + */ + @compileTimeOnly("`modularity` can only be used at compile time in import statements") + object modularity + + /** Was needed to add support for relaxed imports of extension methods. + * The language import is no longer needed as this is now a standard feature since SIP was accepted. + * @see [[http://dotty.epfl.ch/docs/reference/contextual/extension-methods]] + */ + @compileTimeOnly("`relaxedExtensionImports` can only be used at compile time in import statements") + @deprecated("The experimental.relaxedExtensionImports language import is no longer needed since the feature is now standard", since = "3.4") + object relaxedExtensionImports + + /** Enhance match type extractors to follow aliases and singletons. + * + * @see [[https://github.com/scala/improvement-proposals/pull/84]] + */ + @compileTimeOnly("`betterMatchTypeExtractors` can only be used at compile time in import statements") + @deprecated("The experimental.betterMatchTypeExtractors language import is no longer needed since the feature is now standard. It now has no effect, including when setting an older source version.", since = "3.6") + object betterMatchTypeExtractors + + /** Experimental support for quote pattern matching with polymorphic functions + * + * @see [[https://dotty.epfl.ch/docs/reference/experimental/quoted-patterns-with-polymorphic-functions]] + */ + @compileTimeOnly("`quotedPatternsWithPolymorphicFunctions` can only be used at compile time in import statements") + object quotedPatternsWithPolymorphicFunctions + + /** Experimental support for improvements in `for` comprehensions + * + * @see [[https://github.com/scala/improvement-proposals/pull/79]] + */ + @compileTimeOnly("`betterFors` can only be used at compile time in import statements") + object betterFors + } + + /** The deprecated object contains features that are no longer officially suypported in Scala. + * Features in this object are slated for removal. New code should not use them and + * old code should migrate away from them. + */ + @compileTimeOnly("`deprecated` can only be used at compile time in import statements") + object deprecated: + + /** Symbol literals have been deprecated since 2.13. Since Scala 3.0 they + * are no longer an official part of Scala. For compatibility with legacy software, + * symbol literals are still supported with a language import, but new software + * should not use them. + */ + @compileTimeOnly("`symbolLiterals` can only be used at compile time in import statements") + object symbolLiterals + + end deprecated + + /** Where imported, auto-tupling is disabled. + * + * '''Why control the feature?''' Auto-tupling can lead to confusing and + * brittle code in presence of overloads. In particular, surprising overloads + * can be selected, and adding new overloads can change which overload is selected + * in suprising ways. + * + * '''Why allow it?''' Not allowing auto-tupling is difficult to reconcile with + * operators accepting tuples. + */ + @compileTimeOnly("`noAutoTupling` can only be used at compile time in import statements") + object noAutoTupling + + /** Where imported, loose equality using eqAny is disabled. + * + * '''Why allow and control the feature?''' For compatibility and migration reasons, + * strict equality is opt-in. See linked documentation for more information. + * + * @see [[https://dotty.epfl.ch/docs/reference/contextual/multiversal-equality]] + */ + @compileTimeOnly("`strictEquality` can only be used at compile time in import statements") + object strictEquality + + /** Where imported, ad hoc extensions of non-open classes in other + * compilation units are allowed. + * + * '''Why control the feature?''' Ad-hoc extensions should usually be avoided + * since they typically cannot rely on an "internal" contract between a class + * and its extensions. Only open classes need to specify such a contract. + * Ad-hoc extensions might break for future versions of the extended class, + * since the extended class is free to change its implementation without + * being constrained by an internal contract. + * + * '''Why allow it?''' An ad-hoc extension can sometimes be necessary, + * for instance when mocking a class in a testing framework, or to work + * around a bug or missing feature in the original class. Nevertheless, + * such extensions should be limited in scope and clearly documented. + * That's why the language import is required for them. + */ + @compileTimeOnly("`adhocExtensions` can only be used at compile time in import statements") + object adhocExtensions + + /** Unsafe Nulls fot Explicit Nulls + * Inside the "unsafe" scope, `Null` is considered as a subtype of all reference types. + * + * @see [[http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html]] + */ + @compileTimeOnly("`unsafeNulls` can only be used at compile time in import statements") + object unsafeNulls + + @compileTimeOnly("`future` can only be used at compile time in import statements") + object future + + @compileTimeOnly("`future-migration` can only be used at compile time in import statements") + object `future-migration` + + /** Set source version to 3.0-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.0-migration` can only be used at compile time in import statements") + object `3.0-migration` + + /** Set source version to 3.0. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.0` can only be used at compile time in import statements") + object `3.0` + + /** Set source version to 3.1-migration. + * + * This is a no-op, and should not be used. A syntax error will be reported upon import. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.1-migration` can only be used at compile time in import statements") + @deprecated("`3.1-migration` is not valid, use `3.1` instead", since = "3.2") + object `3.1-migration` + + /** Set source version to 3.1 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.1` can only be used at compile time in import statements") + object `3.1` + + /** Set source version to 3.2-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.2-migration` can only be used at compile time in import statements") + object `3.2-migration` + + /** Set source version to 3.2 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.2` can only be used at compile time in import statements") + object `3.2` + + /** Set source version to 3.3-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.3-migration` can only be used at compile time in import statements") + object `3.3-migration` + + /** Set source version to 3.3 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.3` can only be used at compile time in import statements") + object `3.3` + + /** Set source version to 3.4-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.4-migration` can only be used at compile time in import statements") + object `3.4-migration` + + /** Set source version to 3.4 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.4` can only be used at compile time in import statements") + object `3.4` + + /** Set source version to 3.5-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.5-migration` can only be used at compile time in import statements") + object `3.5-migration` + + /** Set source version to 3.5 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.5` can only be used at compile time in import statements") + object `3.5` + + /** Set source version to 3.6-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.6-migration` can only be used at compile time in import statements") + object `3.6-migration` + + /** Set source version to 3.6 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.6` can only be used at compile time in import statements") + object `3.6` + + /** Set source version to 3.7-migration. + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.7-migration` can only be used at compile time in import statements") + object `3.7-migration` + + /** Set source version to 3.7 + * + * @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]] + */ + @compileTimeOnly("`3.7` can only be used at compile time in import statements") + object `3.7` + + + // !!! Keep in sync with dotty.tools.dotc.config.SourceVersion !!! + // Also add tests in `tests/pos/source-import-3-x.scala` and `tests/pos/source-import-3-x-migration.scala` + } From 761456ef8f9c3b0b5b453379411a9cc30d6aa4af Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 14:53:47 +0100 Subject: [PATCH 26/28] chore: disable all the warnings for now in stdlib --- project/Build.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index ec0c4f5d09e9..f51a057cbce8 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -293,7 +293,8 @@ object Build { "-deprecation", "-unchecked", //"-Wconf:cat=deprecation&msg=Unsafe:s", // example usage - "-Xfatal-warnings", // -Werror in modern usage + //"-Xfatal-warnings", // -Werror in modern usage + "-nowarn", "-encoding", "UTF8", "-language:implicitConversions", ), From 57c6bcedef7d2e1e4635ae0f92c784a262b3d4e1 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 15:03:15 +0100 Subject: [PATCH 27/28] chore: make the compiler and library compile --- compiler/src/dotty/tools/backend/jvm/GenBCode.scala | 4 ++-- project/Build.scala | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 58daa01e4bdf..bff35134f878 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -109,7 +109,7 @@ class GenBCode extends Phase { self => result finally // frontendAccess and postProcessor are created lazilly, clean them up only if they were initialized - if _frontendAccess ne null then + if !(_frontendAccess eq null) then frontendAccess.compilerSettings.outputDirectory match { case jar: JarArchive => if (ctx.run.nn.suspendedUnits.nonEmpty) @@ -120,7 +120,7 @@ class GenBCode extends Phase { self => jar.close() case _ => () } - if _postProcessor ne null then + if !(_postProcessor eq null) then postProcessor.classfileWriter.close() generatedClassHandler.close() } diff --git a/project/Build.scala b/project/Build.scala index f51a057cbce8..041f2c04e9a3 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1114,7 +1114,8 @@ object Build { (Compile / scalacOptions) ++= Seq( // Needed so that the library sources are visible when `dotty.tools.dotc.core.Definitions#init` is called "-sourcepath", (Compile / sourceDirectories).value.map(_.getAbsolutePath).distinct.mkString(File.pathSeparator), - "-Yexplicit-nulls", + // "-Yexplicit-nulls", TODO: Enable it back + "-Ycompile-scala2-library" ), (Compile / doc / scalacOptions) ++= ScaladocConfigs.DefaultGenerationSettings.value.settings, (Compile / packageSrc / mappings) ++= { From 01a7a333bf5cc882c33bcbe59f0bc6e07afc5d7c Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Fri, 17 Jan 2025 16:05:29 +0100 Subject: [PATCH 28/28] chore: enable the copy plugin in scala3-library --- project/Build.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/project/Build.scala b/project/Build.scala index 041f2c04e9a3..0b3b23392e38 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2424,6 +2424,7 @@ object Build { def asDottyLibrary(implicit mode: Mode): Project = { val base = project.withCommonSettings. + enablePlugins(BuildStdLib). settings(versionScheme := Some("semver-spec")). settings(dottyLibrarySettings) if (mode == Bootstrapped) {