Skip to content

Commit

Permalink
Initial code for scala-uv.
Browse files Browse the repository at this point in the history
  • Loading branch information
quelgar committed Jan 1, 2024
0 parents commit 1fd34c8
Show file tree
Hide file tree
Showing 19 changed files with 1,890 additions and 0 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Scala CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Install libuv
run: sudo apt-get install libuv1-dev
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
cache: 'sbt'
- name: Run tests
run: sbt test
# Optional: This step uploads information to the GitHub dependency graph and unblocking Dependabot alerts for the repository
- name: Upload dependency graph
uses: scalacenter/sbt-dependency-submission@ab086b50c947c9774b70f39fc7f6e20ca2706c91
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/target
/project/target
/project/project
sbt-launch.jar
src/test/resources/write-test.txt
2 changes: 2 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
version = "3.7.3"
runner.dialect = scala3
32 changes: 32 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
scalaVersion := "3.3.1"

enablePlugins(ScalaNativePlugin)
enablePlugins(ScalaNativeJUnitPlugin)

organization := "quelgar.github.com"

name := "scala-uv"

// set to Debug for compilation details (Info is default)
logLevel := Level.Info

// import to add Scala Native options
import scala.scalanative.build._

// defaults set with common options shown
nativeConfig ~= { c =>
c.withLTO(LTO.none) // thin
.withMode(Mode.debug) // releaseFast
.withGC(GC.immix) // commix
}

scalacOptions ++= Seq(
"-new-syntax",
"-no-indent",
"-Wvalue-discard",
"-Wunused:all",
"-Werror",
"-deprecation"
)

// Test / nativeLinkingOptions += "-luv"
1 change: 1 addition & 0 deletions project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version = 1.9.7
1 change: 1 addition & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.16")
9 changes: 9 additions & 0 deletions src/main/resources/scala-native/errors.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <uv.h>

#define XX(error_name, message) \
int uv_scala_errorcode_##error_name() \
{ \
return UV_##error_name; \
}
UV_ERRNO_MAP(XX)
#undef XX
75 changes: 75 additions & 0 deletions src/main/resources/scala-native/helpers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include <uv.h>
#include <netinet/in.h>

void uv_scala_buf_init(char *base, unsigned int len, uv_buf_t *buffer)
{
uv_buf_t buf = uv_buf_init(base, len);
buffer->base = buf.base;
buffer->len = buf.len;
}

void *uv_scala_buf_base(const uv_buf_t *buffer)
{
return buffer->base;
}

size_t uv_scala_buf_len(const uv_buf_t *buffer)
{
return buffer->len;
}

size_t uv_scala_buf_struct_size()
{
return sizeof(uv_buf_t);
}

size_t uv_scala_mutex_t_size()
{
return sizeof(uv_mutex_t);
}

uv_stream_t *uv_scala_connect_stream_handle(const uv_connect_t *req)
{
return req->handle;
}

uv_stream_t *uv_scala_shutdown_stream_handle(const uv_shutdown_t *req)
{
return req->handle;
}

uv_stream_t *uv_scala_write_stream_handle(const uv_write_t *req)
{
return req->handle;
}

uv_stream_t *uv_scala_send_stream_handle(const uv_write_t *req)
{
return req->send_handle;
}

size_t uv_scala_sizeof_sockaddr_in()
{
return sizeof(struct sockaddr_in);
}

void uv_scala_init_sockaddr_in(int address, int port, struct sockaddr_in *addr)
{
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = htonl(address);
addr->sin_port = htons(port);
}

size_t uv_scala_sizeof_sockaddr_in6()
{
return sizeof(struct sockaddr_in6);
}

void uv_scala_init_sockaddr_in6(const char *address, int port, unsigned int flow_info, unsigned int scope_id, struct sockaddr_in6 *addr)
{
addr->sin6_family = AF_INET6;
memcpy(&(addr->sin6_addr), address, sizeof(struct in6_addr));
addr->sin6_port = htons(port);
addr->sin6_flowinfo = htonl(flow_info);
addr->sin6_scope_id = htonl(scope_id);
}
7 changes: 7 additions & 0 deletions src/main/resources/scala-native/platform_specific.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


#ifdef _WIN32

#else

#endif
70 changes: 70 additions & 0 deletions src/main/scala/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import scalauv.*

import scala.scalanative.*
import unsafe.*
import unsigned.*
import LibUv.*
import UvUtils.*

final class Test {

private var done = false

def callback: AsyncCallback = { (handle: AsyncHandle) =>
println("Callback!!")
done = true
uv_close(handle, null)
}

def run() = {

withZone {

val loop = uv_default_loop()

val asyncHandle = UvUtils.zoneAllocateHandle(HandleType.UV_ASYNC)
uv_async_init(loop, asyncHandle, callback).checkErrorThrowIO()

uv_async_send(asyncHandle).checkErrorThrowIO()

println(s"Test before, done = $done")
uv_run(loop, RunMode.DEFAULT).checkErrorThrowIO()
println(s"Test after, done = $done")
}

}

}

object Test

object Main {

private var done = false

private val callback: AsyncCallback = { (handle: AsyncHandle) =>
println("Callback!!")
done = true
uv_close(handle, null)
}

def main(args: Array[String]): Unit = {

withZone {
val loop = uv_default_loop()

val asyncHandle = UvUtils.zoneAllocateHandle(HandleType.UV_ASYNC)
uv_async_init(loop, asyncHandle, callback).checkErrorThrowIO()

uv_async_send(asyncHandle).checkErrorThrowIO()

println(s"Main before, done = $done")
uv_run(loop, RunMode.DEFAULT).checkErrorThrowIO()
println(s"Main after, done = $done")
}

// val test = new Test
// test.run()
}

}
109 changes: 109 additions & 0 deletions src/main/scala/scalauv/Buffer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package scalauv

import scalanative.unsafe.*
import scalanative.unsigned.*
import java.nio
import java.nio.charset.StandardCharsets
import scala.scalanative.libc.stdlib

opaque type Buffer = Ptr[Byte]

extension (buffer: Buffer) {

def base: Ptr[Byte] = helpers.uv_scala_buf_base(buffer)
def length: Int = helpers.uv_scala_buf_len(buffer).toInt

def apply(index: Int): Byte = base(index)

def asNio: nio.ByteBuffer = ???

def asUtf8String(max: Int): String =
new String(asArray(max), StandardCharsets.UTF_8)

def asArray(max: Int): Array[Byte] = {
val a = Array.ofDim[Byte](max)
for i <- 0 until max do {
a(i) = base(i)
}
a
}

def foreachByte(max: Int)(f: Byte => Unit): Unit = {
for i <- 0 until max do {
f(base(i))
}
}

def +(index: Int): Buffer =
buffer + (index.toLong * Buffer.structureSize.toLong)

inline def toNative: Ptr[Byte] = buffer

inline def init(base: Ptr[Byte], size: CSize): Unit =
helpers.uv_scala_buf_init(base, size.toUInt, buffer)

inline def mallocInit(size: CSize): Unit =
helpers.uv_scala_buf_init(stdlib.malloc(size), size.toUInt, buffer)

inline def free(): Unit = stdlib.free(buffer)
}

object Buffer {

given Tag[Buffer] = Tag.Ptr[Byte](summon[Tag[Byte]])

val structureSize: CSize = helpers.uv_scala_buf_struct_size()

inline def unsafeFromNative(ptr: Ptr[Byte]): Buffer = ptr

inline def stackAllocate(
ptr: Ptr[Byte],
size: CUnsignedInt
): Buffer = {
val uvBuf = stackalloc[Byte](structureSize)
helpers.uv_scala_buf_init(ptr, size, uvBuf)
uvBuf
}

inline def stackAllocate(
array: Array[Byte],
index: Int = 0
): Buffer = {
val uvBuf = stackalloc[Byte](structureSize)
helpers.uv_scala_buf_init(
array.at(index),
(array.length - index).toUInt,
uvBuf
)
uvBuf
}

def zoneAllocate(array: Array[Byte], index: Int = 0)(using
Zone
): Buffer = {
val uvBuf = alloc[Byte](structureSize)
helpers.uv_scala_buf_init(
array.at(index),
(array.length - index).toUInt,
uvBuf
)
uvBuf
}

def malloc(base: Ptr[Byte], size: CSize): Buffer = {
val uvBuf = stdlib.malloc(structureSize.toULong)
helpers.uv_scala_buf_init(base, size.toUInt, uvBuf)
uvBuf
}

def malloc(array: Array[Byte], index: Int = 0): Buffer = {
val uvBuf = stdlib.malloc(structureSize.toULong)
helpers.uv_scala_buf_init(
array.at(index),
(array.length - index).toUInt,
uvBuf
)
uvBuf
}

}
Loading

0 comments on commit 1fd34c8

Please sign in to comment.