Skip to content

Commit

Permalink
Merge #87
Browse files Browse the repository at this point in the history
  • Loading branch information
durban committed Apr 27, 2020
2 parents 4764c6a + 0cc3232 commit 79fd263
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 142 deletions.
239 changes: 186 additions & 53 deletions core/src/main/scala/dev/tauri/seals/core/IdentitySet.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* Copyright 2016-2020 Daniel Urban and contributors listed in AUTHORS
* Copyright 2020 Nokia
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,87 +27,218 @@ import scala.collection.mutable.Builder
import scala.collection.mutable.SetBuilder
import scala.collection.SetLike

private final class IdentitySet[A <: AnyRef] private (m: HashMap[Int, List[A]])
extends Set[A] with SetLike[A, IdentitySet[A]] { self =>
private sealed abstract class IdentitySet[A <: AnyRef]
extends Set[A]
with SetLike[A, IdentitySet[A]] { self =>

import IdentitySet._
override def empty: IdentitySet[A] =
IdentitySet.empty[A]
}

private object IdentitySet {

private final object HashIdentitySet {

private[this] val map = m
def nonEmpty[A <: AnyRef](elem: A, rest: Array[A]): HashIdentitySet[A] =
new HashIdentitySet[A](rest.foldLeft(HashMap(id(elem) -> (elem :: Nil)))(folder))

def this() = this(HashMap.empty)
private def folder[A <: AnyRef](map: HashMap[Int, List[A]], a: A): HashMap[Int, List[A]] = {
val k = id(a)
val lst = map.getOrElse(k, Nil)
if (lstContains(lst, a)) {
map
} else {
map.updated(k, a :: lst)
}
}
}

override def iterator: Iterator[A] = new AbstractIterator[A] {
private final class HashIdentitySet[A <: AnyRef](map: HashMap[Int, List[A]])
extends IdentitySet[A] { self =>

private[this] val lsts = self.map.valuesIterator
private[this] var curr: List[A] = Nil // scalastyle:ignore
def this(elems: A*) =
this(elems.foldLeft(HashMap.empty[Int, List[A]])(HashIdentitySet.folder))

def hasNext: Boolean = {
nextNonEmptyList()
curr.nonEmpty
override def -(elem: A): IdentitySet[A] = {
val k = id(elem)
val lst = map.getOrElse(k, Nil)
if (lstContains(lst, elem)) {
val newLst = lst.filter(_ ne elem)
val m = if (newLst.nonEmpty) {
map.updated(k, newLst)
} else {
map - k
}
new HashIdentitySet[A](m)
} else {
this
}
}

def next(): A = {
nextNonEmptyList()
curr match {
case nxt :: rest =>
curr = rest
nxt
case Nil =>
throw new IllegalStateException
override def +(elem: A): IdentitySet[A] = {
val k = id(elem)
val lst = map.getOrElse(k, Nil)
if (lstContains(lst, elem)) {
this
} else {
val m = map.updated(k, elem :: lst)
new HashIdentitySet[A](m)
}
}

@tailrec
private def nextNonEmptyList(): Unit = {
if (curr.isEmpty) {
if (lsts.hasNext) {
curr = lsts.next()
nextNonEmptyList()
override def contains(elem: A): Boolean = {
lstContains(map.getOrElse(id(elem), Nil), elem)
}

override def iterator: Iterator[A] = new AbstractIterator[A] {

private[this] val lsts = self.map.valuesIterator
private[this] var curr: List[A] = Nil // scalastyle:ignore

def hasNext: Boolean = {
nextNonEmptyList()
curr.nonEmpty
}

def next(): A = {
nextNonEmptyList()
curr match {
case nxt :: rest =>
curr = rest
nxt
case Nil =>
throw new IllegalStateException
}
}

@tailrec
private def nextNonEmptyList(): Unit = {
if (curr.isEmpty) {
if (lsts.hasNext) {
curr = lsts.next()
nextNonEmptyList()
}
}
}
}
}

override def -(elem: A): IdentitySet[A] = {
val k = id(elem)
val lst = map.getOrElse(k, Nil)
lst.iterator
if (lstContains(lst, elem)) {
val newLst = lst.filter(_ ne elem)
val m = if (newLst.nonEmpty) {
map.updated(k, newLst)
} else {
map - k
}
new IdentitySet[A](m)
} else {
private def identitySet0[A <: AnyRef]: IdentitySet[A] =
emptyIdentitySet.asInstanceOf[IdentitySet[A]]

private val emptyIdentitySet: IdentitySet[AnyRef] = new IdentitySet[AnyRef] {

final override def iterator =
Iterator.empty

final override def +(a: AnyRef) =
new IdentitySet1(a)

final override def -(a: AnyRef) =
this

final override def contains(a: AnyRef): Boolean =
false
}

private final class IdentitySet1[A <: AnyRef](e: A) extends IdentitySet[A] {

final override def iterator: Iterator[A] =
Iterator(e)

final override def +(a: A): IdentitySet[A] = {
if (a eq e) this
else new IdentitySet2(a, e)
}

final override def -(a: A): IdentitySet[A] = {
if (a eq e) IdentitySet.empty
else this
}

final override def contains(a: A): Boolean =
a eq e
}

override def +(elem: A): IdentitySet[A] = {
val k = id(elem)
val lst = map.getOrElse(k, Nil)
if (lstContains(lst, elem)) {
this
} else {
val m = map.updated(k, elem :: lst)
new IdentitySet[A](m)
private final class IdentitySet2[A <: AnyRef](e1: A, e2: A) extends IdentitySet[A] {

final override def iterator: Iterator[A] =
Iterator(e1, e2)

final override def +(a: A): IdentitySet[A] = {
if ((a eq e1) || (a eq e2)) this
else new IdentitySet3(e1, e2, a)
}

final override def -(a: A): IdentitySet[A] = {
if (a eq e1) new IdentitySet1(e2)
else if (a eq e2) new IdentitySet1(e1)
else this
}

final override def contains(a: A): Boolean =
(a eq e1) || (a eq e2)
}

override def contains(elem: A): Boolean = {
lstContains(map.getOrElse(id(elem), Nil), elem)
private final class IdentitySet3[A <: AnyRef](e1: A, e2: A, e3: A) extends IdentitySet[A] {

final override def iterator: Iterator[A] =
Iterator(e1, e2, e3)

final override def +(a: A): IdentitySet[A] = {
if ((a eq e1) || (a eq e2) || (a eq e3)) this
else new IdentitySetN(Array[AnyRef](e1, e2, e3, a))
}

final override def -(a: A): IdentitySet[A] = {
if (a eq e1) new IdentitySet2(e2, e3)
else if (a eq e2) new IdentitySet2(e1, e3)
else if (a eq e3) new IdentitySet2(e1, e2)
else this
}

final override def contains(a: A): Boolean =
(a eq e1) || (a eq e2) || (a eq e3)
}

override def empty: IdentitySet[A] =
IdentitySet.empty[A]
}
final val maxN = 32

private object IdentitySet {
private final class IdentitySetN[A <: AnyRef](arr: Array[AnyRef]) extends IdentitySet[A] {

final override def iterator: Iterator[A] =
arr.iterator.asInstanceOf[Iterator[A]]

final override def +(a: A): IdentitySet[A] = {
if (this.contains(a)) this
else if (arr.length < maxN) new IdentitySetN[A]({
val newArr = Array.ofDim[AnyRef](arr.length + 1)
System.arraycopy(arr, 0, newArr, 0, arr.length)
newArr(arr.length) = a
newArr
})
else HashIdentitySet.nonEmpty[AnyRef](a, arr).asInstanceOf[IdentitySet[A]]
}

final override def -(a: A): IdentitySet[A] = {
if (this.contains(a)) {
if (arr.length > 1) {
val newArr = arr.filterNot(_ eq a)
new IdentitySetN(newArr)
} else {
IdentitySet.empty
}
} else {
this
}
}

final override def contains(a: A): Boolean = {
arr.exists(_ eq a)
}
}

def empty[A <: AnyRef]: IdentitySet[A] =
new IdentitySet[A]
identitySet0

implicit def identitySetCanBuildFrom[A <: AnyRef]: CanBuildFrom[IdentitySet[A], A, IdentitySet[A]] = {
new CanBuildFrom[IdentitySet[A], A, IdentitySet[A]] {
Expand Down
10 changes: 7 additions & 3 deletions core/src/main/scala/dev/tauri/seals/core/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ sealed trait Model extends Serializable {
false
}

final override def hashCode: Int = {
@transient
final override lazy val hashCode: Int =
hashCodeImpl

private[this] final def hashCodeImpl: Int = {
val st = this.foldImpl[State[(Int, Int), Unit]](
hNil = _ => hash.mix(hash.hNil),
hCons = (_, l, o, r, h, t) => hash.mix(o) *> hash.mix(r.map(_.uuid)) *> hash.mixLht(l, h, t, hash.hCons),
Expand Down Expand Up @@ -219,8 +223,8 @@ object Model {

final case class Ctx(m: Model, p: Path)

type Path = scala.Vector[String]
val Path = scala.Vector
type Path = cats.data.Chain[String]
val Path = cats.data.Chain

private[core] type Memo = Memo.Memo[Model]

Expand Down
84 changes: 84 additions & 0 deletions core/src/main/scala/dev/tauri/seals/core/modelDecoding.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2016-2020 Daniel Urban and contributors listed in AUTHORS
* Copyright 2020 Nokia
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dev.tauri.seals
package core

import cats.{ Monad, Eval }
import cats.data.Kleisli
import cats.implicits._

final object ModelDecoding {

sealed abstract class Proc[F[_], S] extends Monad[F] {
def get: F[S]
def set(s: S): F[Unit]
def raise[A](err: String): F[A]
def force[A](proc: F[A], s: S): Either[String, A]
def forceAS[A](proc: F[A], s: S): (S, Either[String, A])
}

sealed abstract class Api {
type F[S, A]
implicit def procInstance[S]: Proc[F[S, ?], S]
}

val Api: Api = Impl

type Error = String

final case class DecodingError(err: Error)
extends Exception(sh"error while decoding model: ${err}")

private final object Impl extends Api {
override type F[S, A] = Kleisli[Eval, LocRef[S], A]
implicit override def procInstance[S]: Proc[F[S, ?], S] = {
new Proc[F[S, ?], S] {
override def pure[A](x: A) =
Kleisli.pure(x)
override def flatMap[A, B](fa: Kleisli[Eval, LocRef[S], A])(
f: A => Kleisli[Eval, LocRef[S], B]
) = fa.flatMap(f)
override def tailRecM[A, B](a: A)(f: A => F[S, Either[A, B]]): F[S, B] =
Kleisli.catsDataMonadForKleisli[Eval, LocRef[S]].tailRecM(a)(f)
override def raise[A](err: String) =
Kleisli { _ => Eval.always(throw new DecodingError(err)) }
override def get =
Kleisli { ref => ref.get }
override def set(s: S) =
Kleisli { ref => ref.set(s) }
override def force[A](proc: F[S, A], s: S): Either[String, A] = {
try Right(proc.run(new LocRef[S](s)).value)
catch { case DecodingError(err) => Left(err) }
}
override def forceAS[A](proc: F[S, A], s: S): (S, Either[String, A]) = {
val ref = new LocRef[S](s)
val res = try {
Right(proc.run(ref).value)
} catch { case DecodingError(err) => Left(err) }
(ref.get.value, res)
}
}
}
}

private final class LocRef[A](private[this] var value: A) {
def get: Eval[A] = Eval.always(this.value)
def set(a: A): Eval[Unit] = Eval.always(this.value = a)
}
}
Loading

0 comments on commit 79fd263

Please sign in to comment.