PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/OPAL/common/src/main/scala/org/opalj/control/package.scala

https://bitbucket.org/delors/opal
Scala | 390 lines | 237 code | 36 blank | 117 comment | 25 complexity | ff767f4e43da8a2ae05191a3c71c42c6 MD5 | raw file
  1. /* BSD 2-Clause License - see OPAL/LICENSE for details. */
  2. package org.opalj
  3. import scala.language.experimental.macros
  4. import scala.annotation.tailrec
  5. import scala.reflect.macros.blackbox.Context
  6. import org.opalj.collection.immutable.RefArray
  7. import org.opalj.collection.immutable.IntArray
  8. /**
  9. * Defines common control abstractions.
  10. *
  11. * @author Michael Eichberg
  12. */
  13. package object control {
  14. /**
  15. * Iterates over a given array `a` and calls the given function `f` for
  16. * each non-null value in the array.
  17. *
  18. * @note '''This is a macro.'''
  19. */
  20. final def foreachNonNullValue[T <: AnyRef](
  21. a: Array[T]
  22. )(
  23. f: (Int, T) Unit
  24. ): Unit = macro ControlAbstractionsImplementation.foreachNonNullValue[T]
  25. /**
  26. * Allocation free, local iteration over all elements of an array.
  27. *
  28. * @note '''This is a macro.'''
  29. */
  30. final def foreachValue[T <: AnyRef](
  31. a: Array[T]
  32. )(
  33. f: (Int, T) Unit
  34. ): Unit = macro ControlAbstractionsImplementation.foreachValue[T]
  35. /**
  36. * Executes the given function `f` for the first `n` values of the given list.
  37. * The behavior is undefined if the given list does not have at least `n` elements.
  38. *
  39. * @note '''This is a macro.'''
  40. */
  41. final def forFirstN[T <: AnyRef](
  42. l: List[T], n: Int
  43. )(
  44. f: T Unit
  45. ): Unit = macro ControlAbstractionsImplementation.forFirstN[T]
  46. /**
  47. * Evaluates the given expression `f` with type `T` the given number of
  48. * `times` and stores the result in a [[org.opalj.collection.immutable.RefArray]].
  49. *
  50. * ==Example Usage==
  51. * {{{
  52. * val result = repeat(15) {
  53. * System.in.read()
  54. * }
  55. * }}}
  56. *
  57. * @note '''This is a macro.'''
  58. *
  59. * @param times The number of times the expression f` is evaluated. The `times`
  60. * expression is evaluated exactly once.
  61. * @param f An expression that is evaluated the given number of times unless an
  62. * exception is thrown. Hence, even though `f` is not a by-name parameter,
  63. * it behaves in the same way.
  64. * @return The result of the evaluation of the expression `f` the given number of
  65. * times stored in an `IndexedSeq`. If `times` is zero an empty sequence is
  66. * returned.
  67. */
  68. def fillRefArray[T <: AnyRef](
  69. times: Int
  70. )(
  71. f: T
  72. ): RefArray[T] = macro ControlAbstractionsImplementation.fillRefArray[T]
  73. // OLD IMPLEMENTATION USING HIGHER-ORDER FUNCTIONS
  74. // (DO NOT DELETE - TO DOCUMENT THE DESIGN DECISION FOR MACROS)
  75. // def repeat[T](times: Int)(f: ⇒ T): IndexedSeq[T] = {
  76. // val array = new scala.collection.mutable.ArrayBuffer[T](times)
  77. // var i = 0
  78. // while (i < times) {
  79. // array += f
  80. // i += 1
  81. // }
  82. // array
  83. // }
  84. // The macro-based implementation has proven to be approx. 1,3 to 1,4 times faster
  85. // when the number of times that we repeat an operation is small (e.g., 1 to 15 times)
  86. // (which is very often the case when we read in Java class files)
  87. /**
  88. * Evaluates the given expression `f` the given number of
  89. * `times` and stores the result in an [[org.opalj.collection.immutable.IntArray]].
  90. *
  91. * ==Example Usage==
  92. * {{{
  93. * val result = fillIntArray(15) { System.in.readByte() }
  94. * }}}
  95. *
  96. * @note '''This is a macro.'''
  97. *
  98. * @param times The number of times the expression `f` is evaluated. The `times`
  99. * expression is evaluated exactly once.
  100. * @param f An expression that is evaluated the given number of times unless an
  101. * exception is thrown. Hence, even though `f` is not a by-name parameter,
  102. * it behaves in the same way.
  103. * @return The result of the evaluation of the expression `f` the given number of
  104. * times stored in an `IndexedSeq`. If `times` is zero an empty sequence is
  105. * returned.
  106. */
  107. def fillIntArray(
  108. times: Int
  109. )(
  110. f: Int
  111. ): IntArray = macro ControlAbstractionsImplementation.fillIntArray
  112. def fillArrayOfInt(
  113. times: Int
  114. )(
  115. f: Int
  116. ): Array[Int] = macro ControlAbstractionsImplementation.fillArrayOfInt
  117. /**
  118. * Iterates over the given range of integer values `[from,to]` and calls the given
  119. * function f for each value.
  120. *
  121. * If `from` is smaller or equal to `to`, `f` will not be called.
  122. *
  123. * @note '''This is a macro.'''
  124. */
  125. def iterateTo(
  126. from: Int, to: Int
  127. )(
  128. f: Int Unit
  129. ): Unit = macro ControlAbstractionsImplementation.iterateTo
  130. /**
  131. * Iterates over the given range of integer values `[from,until)` and calls the given
  132. * function f for each value.
  133. *
  134. * If `from` is smaller than `until`, `f` will not be called.
  135. */
  136. def iterateUntil(
  137. from: Int, until: Int
  138. )(
  139. f: Int Unit
  140. ): Unit = macro ControlAbstractionsImplementation.iterateUntil
  141. /**
  142. * Runs the given function f the given number of times.
  143. */
  144. def repeat(times: Int)(f: Unit): Unit = macro ControlAbstractionsImplementation.repeat
  145. /**
  146. * Finds the value identified by the given comparator, if any.
  147. *
  148. * @note The comparator has to be able to handle `null` values if the given array may
  149. * contain null values.
  150. *
  151. * @note The array must contain less than Int.MaxValue/2 values.
  152. *
  153. * @param data An array sorted in ascending order according to the test done by the
  154. * comparator.
  155. * @param comparator A comparator which is used to search for the matching value.
  156. * If the comparator matches multiple values, the returned value is not
  157. * precisely specified.
  158. */
  159. // TODO Rename: binarySearch
  160. def find[T <: AnyRef](data: Array[T], comparator: Comparator[T]): Option[T] = {
  161. find(data)(comparator.evaluate)
  162. }
  163. // TODO Rename: binarySearch
  164. def find[T <: AnyRef](data: Array[T])(evaluate: T Int): Option[T] = {
  165. @tailrec @inline def find(low: Int, high: Int): Option[T] = {
  166. if (high < low)
  167. return None;
  168. val mid = (low + high) / 2 // <= will never overflow...(by constraint...)
  169. val e = data(mid)
  170. val eComparison = evaluate(e)
  171. if (eComparison == 0) {
  172. Some(e)
  173. } else if (eComparison < 0) {
  174. find(mid + 1, high)
  175. } else {
  176. find(low, mid - 1)
  177. }
  178. }
  179. find(0, data.length - 1)
  180. }
  181. }
  182. package control {
  183. /**
  184. * Implementation of the macros.
  185. *
  186. * @author Michael Eichberg
  187. */
  188. private object ControlAbstractionsImplementation {
  189. def foreachNonNullValue[T <: AnyRef: c.WeakTypeTag](
  190. c: Context
  191. )(
  192. a: c.Expr[Array[T]]
  193. )(
  194. f: c.Expr[(Int, T) Unit]
  195. ): c.Expr[Unit] = {
  196. import c.universe._
  197. reify {
  198. val array = a.splice // evaluate only once!
  199. val arrayLength = array.length
  200. var i = 0
  201. while (i < arrayLength) {
  202. val arrayEntry = array(i)
  203. if (arrayEntry ne null) f.splice(i, arrayEntry)
  204. i += 1
  205. }
  206. }
  207. }
  208. def foreachValue[T <: AnyRef: c.WeakTypeTag](
  209. c: Context
  210. )(
  211. a: c.Expr[Array[T]]
  212. )(
  213. f: c.Expr[(Int, T) Unit]
  214. ): c.Expr[Unit] = {
  215. import c.universe._
  216. reify {
  217. val array = a.splice // evaluate only once!
  218. val arrayLength = array.length
  219. var i = 0
  220. while (i < arrayLength) {
  221. val arrayEntry = array(i)
  222. f.splice(i, arrayEntry)
  223. i += 1
  224. }
  225. }
  226. }
  227. def forFirstN[T <: AnyRef: c.WeakTypeTag](
  228. c: Context
  229. )(
  230. l: c.Expr[List[T]], n: c.Expr[Int]
  231. )(
  232. f: c.Expr[T Unit]
  233. ): c.Expr[Unit] = {
  234. import c.universe._
  235. reify {
  236. var remainingList = l.splice
  237. val max = n.splice
  238. var i = 0
  239. while (i < max) {
  240. val head = remainingList.head
  241. remainingList = remainingList.tail
  242. f.splice(head)
  243. i += 1
  244. }
  245. }
  246. }
  247. def fillRefArray[T <: AnyRef: c.WeakTypeTag](
  248. c: Context
  249. )(
  250. times: c.Expr[Int]
  251. )(
  252. f: c.Expr[T]
  253. ): c.Expr[RefArray[T]] = {
  254. import c.universe._
  255. reify {
  256. val size = times.splice // => times is evaluated only once
  257. if (size == 0) {
  258. RefArray.empty[T]
  259. } else {
  260. val array = new Array[AnyRef](size)
  261. var i = 0
  262. while (i < size) {
  263. val value = f.splice // => we evaluate f the given number of times
  264. array(i) = value
  265. i += 1
  266. }
  267. RefArray._UNSAFE_from[T](array)
  268. }
  269. }
  270. }
  271. def fillIntArray(c: Context)(times: c.Expr[Int])(f: c.Expr[Int]): c.Expr[IntArray] = {
  272. import c.universe._
  273. reify {
  274. val size = times.splice // => times is evaluated only once
  275. if (size == 0) {
  276. IntArray.empty
  277. } else {
  278. val array = new Array[Int](size)
  279. var i = 0
  280. while (i < size) {
  281. val value = f.splice // => we evaluate f the given number of times
  282. array(i) = value
  283. i += 1
  284. }
  285. IntArray._UNSAFE_from(array)
  286. }
  287. }
  288. }
  289. def fillArrayOfInt(c: Context)(times: c.Expr[Int])(f: c.Expr[Int]): c.Expr[Array[Int]] = {
  290. import c.universe._
  291. reify {
  292. val size = times.splice // => times is evaluated only once
  293. if (size == 0) {
  294. IntArray.EmptyArrayOfInt
  295. } else {
  296. val array = new Array[Int](size)
  297. var i = 0
  298. while (i < size) {
  299. val value = f.splice // => we evaluate f the given number of times
  300. array(i) = value
  301. i += 1
  302. }
  303. array
  304. }
  305. }
  306. }
  307. def iterateTo(
  308. c: Context
  309. )(
  310. from: c.Expr[Int],
  311. to: c.Expr[Int]
  312. )(
  313. f: c.Expr[(Int) Unit]
  314. ): c.Expr[Unit] = {
  315. import c.universe._
  316. reify {
  317. var i = from.splice
  318. val max = to.splice // => to is evaluated only once
  319. while (i <= max) {
  320. f.splice(i) // => we evaluate f the given number of times
  321. i += 1
  322. }
  323. }
  324. }
  325. def iterateUntil(
  326. c: Context
  327. )(
  328. from: c.Expr[Int],
  329. until: c.Expr[Int]
  330. )(
  331. f: c.Expr[(Int) Unit]
  332. ): c.Expr[Unit] = {
  333. import c.universe._
  334. reify {
  335. var i = from.splice
  336. val max = until.splice // => until is evaluated only once
  337. while (i < max) {
  338. f.splice(i) // => we evaluate f the given number of times
  339. i += 1
  340. }
  341. }
  342. }
  343. def repeat(c: Context)(times: c.Expr[Int])(f: c.Expr[Unit]): c.Expr[Unit] = {
  344. import c.universe._
  345. reify {
  346. var i = times.splice
  347. while (i > 0) {
  348. f.splice // => we evaluate f the given number of times
  349. i -= 1
  350. }
  351. }
  352. }
  353. }
  354. }