![JAR search and dependency download from the Maven repository](/logo.png)
cgta.cenum.CEnum.scala Maven / Gradle / Ivy
package cgta.cenum
import cgta.serland.gen.Gen
import cgta.serland.{SerSchemas, SerClass, SerInput, SerOutput}
import cgta.serland.SerHints.Ser32Hints
import cgta.serland
//////////////////////////////////////////////////////////////
// Copyright (c) 2014 Ben Jackman, Jeff Gomberg
// All Rights Reserved
// please contact [email protected] or [email protected]
// for licensing inquiries
// Copyright (c) 2010 Ben Jackman, Jeff Gomberg
// Created by bjackman @ 9/21/13 2:43 PM
// Created by bjackman @ 5/8/14 3:38 PM
//////////////////////////////////////////////////////////////
object CEnum {
import scala.language.experimental.macros
def getElements[A <: CEnum](enum: A): IVec[A#EET] = macro CEnumMacroImpl.getElementsImpl[A]
/**
* This method creates a thread and runs the blk of code passed to it.
* It is used since constructors in java are not re-entrant accross threads
* so if two different threads grab two different enum elements very close
* to each other in time, a deadlock can occur since CEnum.this.getClass
* will end up calling the constructors of all the member enum elements.
* And, in this (atleast) one of those constructors will be blocked in
* another thread on the result of CEnum.this.getClass.
*
* Thread1: EnumElementA Base Trait Ctor Begins
* Thread2: EnumElementB Base Trait Ctor Begins
* either makes it to the call CEnum.this.getClass
* add(EnumElementA) get's called, if this is thread1 ok (since the ctor was called there)
* otherwise this will block since no instance of A is constructed yet. This block will
* then be a deadlock, since the add cannot complete and hence the CEnum derived class
* cannot complete construction.
*
* This code works around all that by making a separate thread that does initialization.
* The ordinal method will busy block until the ordinal is set.
*
* On ScalaJS this works around issues with parent obj vs contained object initialization order
*
*/
val initStrategy: CEnumInitStrategy = CEnumInitStrategyImpl
}
/**
* Use this class for enumerations.
* Be sure to call the add method after each element.
*
*/
trait CEnum {self =>
/**
* The base type of the elements in the enumeration.
* For example for Sides the Base type would be Side
* and Bid and Ask would extend it.
*
* Side should extend the EnumElement trait. And you should
* add a override type ET = Side in your CEnum.
*
*/
type EET <: EnumElement
object EnumElement {
//This has to be specifically included for things like .sorted and so on.
implicit val ordering: Ordering[EET] = new Ordering[EET] {
def compare(x: EET, y: EET): Int = x.compare(y)
}
}
trait EnumElement extends Ordered[EnumElement] {
@volatile private[cenum] var _ord = -1
final def ordinal: Int = {
if (_ord == -1) {
CEnum.initStrategy.initOrdinal(CEnum.this, this)
} else {
_ord
}
}
//Converts the ordinal to a flag suitable for storage in
//int-sized bit-sets (classic flag programming pattern)
final def flag32: Int = 1 << ordinal
//Converts the ordinal to a flag suitable for storage in
//long-sized bit sets (classic flag programming pattern)
final def flag64: Long = 1L << ordinal
//LEGACY
final def toIFlag: Int = flag32
final def toLFlag: Long = flag64
def compare(that: EnumElement): Int = this.ordinal - that.ordinal
}
private[cenum] def setOrdinals() {
elements.zipWithIndex.foreach { case (e, i) =>
if (e._ord == -1) {
e._ord = i
}
}
}
final lazy val toIVec: IVec[EET] = elements
final lazy val toIMap: IMap[String, EET] = IMap(toIVec.map(el => el.toString -> el): _*)
def fromString(s: String): EET = toIMap(s)
implicit lazy val ser: SerClass[EET] = new SerClass[EET]() {
// override lazy val schema = SerSchemas.Enum(CEnum.this.toIVec.map(_.toString))
override lazy val schema = {
import SerSchemas._
XEnum(XCEnum, toIVec.map(ee => XEnumElement(ee.toString, ee.ordinal)))
}
//SerSchemas.Enum(CEnum.this.toIVec.map(_.toString))
override def gen: Gen[EET] = Gen.oneOf(toIVec)
override def write(a: EET, out: SerOutput) = {
if (out.isHumanReadable) {
out.writeString(a.toString)
} else {
out.writeInt32(a.ordinal, hint = Ser32Hints.UVarInt32)
}
}
override def read(in: SerInput): EET = {
if (in.isHumanReadable()) {
val s = in.readString()
toIMap.get(s).getOrElse(serland.READ_ERROR(s"Unknown enum element [$s]"))
} else {
val ordinal = in.readInt32(hint = Ser32Hints.UVarInt32)
val arr = CEnum.this.toIVec
if (ordinal < 0 || ordinal >= arr.size) {
serland.READ_ERROR(s"Out of bounds ordinal $ordinal in enum parse")
} else {
arr(ordinal)
}
}
}
}
//Needs to be overrided in child class with final override val elements = CEnum.getElements(this)
//final override val elements = CEnum.getElements(this)
def elements: IVec[EET]
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy