diff --git a/snapshot/src/main/scala/com/evolutiongaming/kafka/journal/BufferNr.scala b/snapshot/src/main/scala/com/evolutiongaming/kafka/journal/BufferNr.scala index 9575ace3e..c3602229e 100644 --- a/snapshot/src/main/scala/com/evolutiongaming/kafka/journal/BufferNr.scala +++ b/snapshot/src/main/scala/com/evolutiongaming/kafka/journal/BufferNr.scala @@ -6,6 +6,7 @@ import com.evolutiongaming.kafka.journal.util.Fail import com.evolutiongaming.kafka.journal.util.Fail.implicits._ import com.evolutiongaming.scassandra._ +/** Snapshot index in a stored ring buffer */ sealed abstract case class BufferNr(value: Int) { override def toString: String = value.toString } @@ -15,13 +16,23 @@ object BufferNr { val min: BufferNr = BufferNr.fromIntUnsafe(0) val max: BufferNr = BufferNr.fromIntUnsafe(Int.MaxValue) - // TODO: should we cache the common indexes to avoid unnecessary allocations? + /** Create all list of buffer indicies of a given size. + * + * I.e. for `size = 3` the following list will be created: + * {{{ + * List(BufferNr(0), BufferNr(1), BufferNr(2)) + * }}} + */ def listOf(size: Int): List[BufferNr] = (0 until size).toList.map(fromIntUnsafe) private def fromIntUnsafe(value: Int): BufferNr = new BufferNr(value) {} + /** Create `BufferNr` out of a value or fail it is out of an allowed range. + * + * A returned value may be reused to minimize number of allocations. + */ def of[F[_]: Applicative: Fail](value: Int): F[BufferNr] = { if (value < min.value) { s"invalid BufferNr of $value, it must be greater or equal to $min".fail[F, BufferNr] @@ -32,7 +43,7 @@ object BufferNr { } else if (value === max.value) { max.pure[F] } else { - new BufferNr(value) {}.pure[F] + fromIntUnsafe(value).pure[F] } } diff --git a/snapshot/src/test/scala/com/evolutiongaming/kafka/journal/BufferNrSpec.scala b/snapshot/src/test/scala/com/evolutiongaming/kafka/journal/BufferNrSpec.scala new file mode 100644 index 000000000..6c1441ef6 --- /dev/null +++ b/snapshot/src/test/scala/com/evolutiongaming/kafka/journal/BufferNrSpec.scala @@ -0,0 +1,41 @@ +package com.evolutiongaming.kafka.journal + +import org.scalatest.funsuite.AnyFunSuite + +class BufferNrSpec extends AnyFunSuite { + + type F[T] = Either[String, T] + + test("create BufferNr out of an Int") { + BufferNr.of[F](17) match { + case Left(message) => fail(message) + case Right(bufferNr) => assert(bufferNr.value == 17) + } + } + + test("fail to create negative BufferNr") { + BufferNr.of[F](-1) match { + case Left(message) => assert(message == "invalid BufferNr of -1, it must be greater or equal to 0") + case Right(bufferNr) => fail(s"exception was not thrown, but got $bufferNr instead") + } + } + + test("reuse BufferNr.min instance") { + BufferNr.of[F](0) match { + case Left(message) => fail(message) + case Right(bufferNr) => assert(bufferNr == BufferNr.min) + } + } + + test("reuse BufferNr.max instance") { + BufferNr.of[F](Int.MaxValue) match { + case Left(message) => fail(message) + case Right(bufferNr) => assert(bufferNr == BufferNr.max) + } + } + + test("use listOf") { + assert(BufferNr.listOf(3).map(_.value) == List(0, 1, 2)) + } + +}