From 49690fb6199eaa948e61373c1c486063fe3876b6 Mon Sep 17 00:00:00 2001 From: Mark Hammons Date: Sat, 20 May 2023 15:49:43 +0200 Subject: [PATCH] feat: Add read/write for fixed-sized arrays Fixes #181 --- build.sc | 2 +- core/src/fr/hammons/slinc/DescriptorOf.scala | 5 ++-- core/src/fr/hammons/slinc/SetSizeArray.scala | 2 +- .../src/fr/hammons/slinc/TypeDescriptor.scala | 18 +++++++++---- .../src/fr/hammons/slinc/TransferSpec.scala | 26 +++++++++++++++++++ 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/build.sc b/build.sc index bdf43a2..cfb68bc 100644 --- a/build.sc +++ b/build.sc @@ -13,7 +13,7 @@ import mill.contrib.scoverage.{ScoverageModule, ScoverageReport} object scoverage extends BaseModule with ScoverageReport trait BaseModule extends ScoverageModule with ScalafmtModule { - def scalaVersion = "3.3.0-RC5" + def scalaVersion = "3.3.0-RC6" def scoverageVersion = "2.0.7" val munitVersion = "1.0.0-M7" diff --git a/core/src/fr/hammons/slinc/DescriptorOf.scala b/core/src/fr/hammons/slinc/DescriptorOf.scala index f819886..e94c7ce 100644 --- a/core/src/fr/hammons/slinc/DescriptorOf.scala +++ b/core/src/fr/hammons/slinc/DescriptorOf.scala @@ -4,6 +4,7 @@ import fr.hammons.slinc.container.* import scala.quoted.* import scala.compiletime.{summonInline, erasedValue, constValue} import scala.NonEmptyTuple +import scala.reflect.ClassTag /** Typeclass that summons TypeDescriptors */ @@ -78,8 +79,8 @@ object DescriptorOf: CUnionDescriptor(helper[A]) .asInstanceOf[CUnionDescriptor { type Inner = CUnion[A] }] - inline given [A, B <: Int](using - innerDesc: DescriptorOf[A] + inline given [A, B <: Int](using innerDesc: DescriptorOf[A])(using + classTag: ClassTag[innerDesc.descriptor.Inner] ): DescriptorOf[SetSizeArray[A, B]] = new DescriptorOf[SetSizeArray[A, B]]: val descriptor: TypeDescriptor { type Inner = SetSizeArray[A, B] } = SetSizeArrayDescriptor(innerDesc.descriptor, constValue[B]).asInstanceOf[ diff --git a/core/src/fr/hammons/slinc/SetSizeArray.scala b/core/src/fr/hammons/slinc/SetSizeArray.scala index b56cbb7..91c6b9a 100644 --- a/core/src/fr/hammons/slinc/SetSizeArray.scala +++ b/core/src/fr/hammons/slinc/SetSizeArray.scala @@ -5,7 +5,6 @@ import scala.compiletime.ops.int.{`*`, `-`, `<=`, `+`, `<`} import scala.compiletime.constValue import scala.quoted.* import scala.language.experimental.erasedDefinitions -import scala.annotation.experimental class SetSizeArray[A, B <: Int] private[slinc] (private val array: Array[A]) extends AnyVal: @@ -16,6 +15,7 @@ class SetSizeArray[A, B <: Int] private[slinc] (private val array: Array[A]) ): SetSizeArray[C, B * D] = new SetSizeArray[C, B * D](array.flatMap(fn.andThen(_.array))) def toSeq: Seq[A] = array.toSeq + def toArray: Array[A] = array inline def take[C <: Int](using C <= B =:= true, 0 <= C =:= true diff --git a/core/src/fr/hammons/slinc/TypeDescriptor.scala b/core/src/fr/hammons/slinc/TypeDescriptor.scala index fc97d2b..50291da 100644 --- a/core/src/fr/hammons/slinc/TypeDescriptor.scala +++ b/core/src/fr/hammons/slinc/TypeDescriptor.scala @@ -14,12 +14,14 @@ import fr.hammons.slinc.modules.TransitionModule import fr.hammons.slinc.modules.{ArgumentTransition, ReturnTransition} import scala.NonEmptyTuple import scala.language.implicitConversions -import dotty.tools.dotc.transform.patmat.Typ /** Describes types used by C interop */ sealed trait TypeDescriptor: + self => type Inner + given DescriptorOf[Inner] with + val descriptor = self def size(using dm: DescriptorModule): Bytes = dm.sizeOf(this) def alignment(using dm: DescriptorModule): Bytes = dm.alignmentOf(this) def toCarrierType(using dm: DescriptorModule): Class[?] = @@ -220,13 +222,19 @@ case class CUnionDescriptor(possibleTypes: Set[TypeDescriptor]) case class SetSizeArrayDescriptor( val contained: TypeDescriptor, val number: Int -) extends TypeDescriptor: +)(using ClassTag[contained.Inner]) + extends TypeDescriptor: override val reader: (ReadWriteModule, DescriptorModule) ?=> Reader[Inner] = - ??? + (mem, offset) => + new SetSizeArray( + summon[ReadWriteModule].readArray[contained.Inner](mem, offset, number) + ) override val writer: (ReadWriteModule, DescriptorModule) ?=> Writer[Inner] = - ??? + (mem, offset, value) => + summon[ReadWriteModule] + .writeArray[contained.Inner](mem, offset, value.toArray) override val argumentTransition : (TransitionModule, ReadWriteModule, Allocator) ?=> ArgumentTransition[ @@ -236,4 +244,4 @@ case class SetSizeArrayDescriptor( override val returnTransition : (TransitionModule, ReadWriteModule) ?=> ReturnTransition[Inner] = ??? - type Inner = SetSizeArray[?, ?] + type Inner = SetSizeArray[contained.Inner, ?] diff --git a/core/test/src/fr/hammons/slinc/TransferSpec.scala b/core/test/src/fr/hammons/slinc/TransferSpec.scala index 5f6d4af..e8ed0be 100644 --- a/core/test/src/fr/hammons/slinc/TransferSpec.scala +++ b/core/test/src/fr/hammons/slinc/TransferSpec.scala @@ -28,6 +28,8 @@ trait TransferSpec[ThreadException <: Throwable](val slinc: Slinc)(using case class F(u: CUnion[(CInt, CFloat)]) derives Struct + case class G(arr: SetSizeArray[CLong, 2]) derives Struct + test("can read and write jvm ints") { Scope.global { val mem = Ptr.blank[Int] @@ -353,3 +355,27 @@ trait TransferSpec[ThreadException <: Throwable](val slinc: Slinc)(using } assertEquals(fReturn.u.get[CFloat], union.get[CFloat]) + + test("can copy SetSizeArray[Int, 2] to native memory"): + val ssa = SetSizeArray(1, 2) + + Scope.confined { + val ptr = Ptr.copy(ssa) + assertEquals((!ptr)[0], 1) + } + + test("can copy SetSizeArray[CLong, 2] to native memory"): + val ssa = SetSizeArray(CLong(1), CLong(2)) + + Scope.confined { + val ptr = Ptr.copy(ssa) + assertEquals((!ptr)[0], CLong(1)) + } + + test("can copy G to native memory and back"): + val g = G(SetSizeArray(CLong(1), CLong(2))) + + Scope.confined { + val ptr = Ptr.copy(g) + assertEquals((!ptr).arr[0], CLong(1)) + }