From cf295e30d555737ece712697a8ef05c99447a182 Mon Sep 17 00:00:00 2001 From: spamegg1 Date: Tue, 16 Jul 2024 18:03:37 +0300 Subject: [PATCH] ch07 finished examples - they are all conceptual pseudocode, not meant to be taken seriously --- src/main/scala/ch07/examples/api.scala | 2 +- src/main/scala/ch07/examples/curl.scala | 30 ++-- src/main/scala/ch07/examples/loop.scala | 2 +- src/main/scala/ch07/examples/multiCurl | 113 --------------- src/main/scala/ch07/examples/multiCurl.scala | 132 ++++++++++++++++++ src/main/scala/ch07/examples/simplemulti | 75 ---------- .../scala/ch07/examples/simplemulti.scala | 85 +++++++++++ 7 files changed, 234 insertions(+), 205 deletions(-) delete mode 100644 src/main/scala/ch07/examples/multiCurl create mode 100644 src/main/scala/ch07/examples/multiCurl.scala delete mode 100644 src/main/scala/ch07/examples/simplemulti create mode 100644 src/main/scala/ch07/examples/simplemulti.scala diff --git a/src/main/scala/ch07/examples/api.scala b/src/main/scala/ch07/examples/api.scala index 2fe00a1..47114a2 100644 --- a/src/main/scala/ch07/examples/api.scala +++ b/src/main/scala/ch07/examples/api.scala @@ -37,7 +37,7 @@ object HTTPAPI: makeRequest(Request(PUT, uri, headers, Some(body))) @main -def libuvApi(args: String*): Unit = +def libuvApi: Unit = HTTPAPI .get("http://example.com") // made up! .onComplete: attempt => diff --git a/src/main/scala/ch07/examples/curl.scala b/src/main/scala/ch07/examples/curl.scala index 79e0be9..d6dc195 100644 --- a/src/main/scala/ch07/examples/curl.scala +++ b/src/main/scala/ch07/examples/curl.scala @@ -33,7 +33,7 @@ object CurlExample extends LazyLogging with LoopExtension: var multi: MultiCurl = null var handle: IdleHandle = null - def init(): Unit = + def init: Unit = if multi == null then global_init(1) multi = multi_init() @@ -54,7 +54,7 @@ object CurlExample extends LazyLogging with LoopExtension: request.allocated = newAllocatedSize memcpy(request.data + request.used, ptr, readSize) - // printf(c"req %d: got body line of size %d\n", !private_data._4, size) // ??? + printf(c"req %d: got body line of size %d\n", !request.data, size) // ??? request.used = newSize val stringEnd = request.data + newSize !stringEnd = 0 @@ -66,7 +66,7 @@ object CurlExample extends LazyLogging with LoopExtension: size * nmemb - val write_cb = CFuncPtr4.fromScalaFunction(dataReady) // changed this! 0.5 + val writeCB = CFuncPtr4.fromScalaFunction(dataReady) // changed this! 0.5 def writeHeader( ptr: Ptr[Byte], @@ -75,7 +75,7 @@ object CurlExample extends LazyLogging with LoopExtension: data: Ptr[CurlBuffer] ): CSize = // Refactor! - val len = stackalloc[Double]() + val len = stackalloc[Double](1) !len = 0 println( easy_getinfo( @@ -97,10 +97,11 @@ object CurlExample extends LazyLogging with LoopExtension: ): CurlRequest = val data = RequestData(Promise[String](), 65536, 0, malloc(65536)) val curlHandle = easy_init() - // Zone(curl_easy_setopt(curlHandle, URL, toCString(url))) - // curl_easy_setopt(curlHandle, WRITECALLBACK, write_cb) // !!! - // curl_easy_setopt(curlHandle, WRITEDATA, idCell) // what's idCell? no idea. - // curl_easy_setopt(curlHandle, PRIVATEDATA, idCell) + Zone: + curl_easy_setopt(curlHandle, URL, toCString(url)) + // curl_easy_setopt(curlHandle, WRITECALLBACK, writeCB) // !!! + // curl_easy_setopt(curlHandle, WRITEDATA, idCell) // what's idCell? no idea. + // curl_easy_setopt(curlHandle, PRIVATEDATA, idCell) ??? def simpleRequest( // made it up! @@ -136,10 +137,11 @@ object CurlExample extends LazyLogging with LoopExtension: requests(id) = data val curlHandle = easy_init() - Zone(curl_easy_setopt(curlHandle, URL, toCString(url))) - // curl_easy_setopt(curlHandle, WRITECALLBACK, write_cb) // can't figure out - // curl_easy_setopt(curlHandle, WRITEDATA, idCell) - // curl_easy_setopt(curlHandle, PRIVATEDATA, idCell) + Zone: + curl_easy_setopt(curlHandle, URL, toCString(url)) + // curl_easy_setopt(curlHandle, WRITECALLBACK, writeCB) // can't figure out + // curl_easy_setopt(curlHandle, WRITEDATA, idCell) + // curl_easy_setopt(curlHandle, PRIVATEDATA, idCell) multi_add_handle(multi, curlHandle) data.promise.future @@ -153,7 +155,7 @@ object CurlExample extends LazyLogging with LoopExtension: def checkCurl(handle: IdleHandle): Unit = println("checking curl status") val multi = !(handle.asInstanceOf[Ptr[MultiCurl]]) - val requests = stackalloc[Int]() + val requests = stackalloc[Int](1) val perform_result = multi_perform(multi, requests) activeRequests = !requests @@ -179,8 +181,6 @@ object CurlExample extends LazyLogging with LoopExtension: val check_cb = CFuncPtr1.fromScalaFunction(checkCurl) private def initDispatcher(loop: LibUV.Loop, multi: MultiCurl): IdleHandle = - import LibUV.*, LibUVConstants.* - println("initializing curl dispatcher") // can't find these UV functions and constants anywhere. diff --git a/src/main/scala/ch07/examples/loop.scala b/src/main/scala/ch07/examples/loop.scala index 3a4fdfb..9adb3f1 100644 --- a/src/main/scala/ch07/examples/loop.scala +++ b/src/main/scala/ch07/examples/loop.scala @@ -13,7 +13,7 @@ object EventLoopExample extends ExecutionContextExecutor: val loop = uv_default_loop() private val taskQueue = ListBuffer[Runnable]() - // private val extensions = ListBuffer[LoopExtension]() // what is this? no idea. + private val extensions = ListBuffer[LoopExtension]() private def dispatch(handle: PrepareHandle): Unit = while taskQueue.nonEmpty do diff --git a/src/main/scala/ch07/examples/multiCurl b/src/main/scala/ch07/examples/multiCurl deleted file mode 100644 index c6552a5..0000000 --- a/src/main/scala/ch07/examples/multiCurl +++ /dev/null @@ -1,113 +0,0 @@ -package ch07 -package examples - -object curlMulti: - // START: onPoll - def onPoll(pollHandle: PollHandle, status: Int, events: Int): Unit = - val socket = !(pollHandle.cast[Ptr[Ptr[Byte]]]) - val actions = (events & 1) | (events & 2) // Whoa, nelly! - val running_handles = stackalloc[Int]() - val result = multi_socket_action(multi, socket, actions, running_handles) // check - - def on_timer(handle: TimerHandle): Unit = - val running_handles = stackalloc[Int]() - multi_socket_action(multi, -1.cast[Ptr[Byte]], 0, running_handles) - logger.debug(s"on_timer fired, ${!running_handles} sockets running") - - val writeCB = CFunctionPtr.fromFunction4(writeData) - val headerCB = CFunctionPtr.fromFunction4(writeHeader) - val readyCB = CFunctionPtr.fromFunction3(ready_for_curl) - val timerCB = CFunctionPtr.fromFunction1(on_timer) - - def socket_state_change( - curl: Curl, - socket: Ptr[Byte], - action: Int, - data: Ptr[Byte], - socket_data: Ptr[Byte] - ): Int = - logger.debug(s"handle_socket called with action ${action}") - logger.trace(c"\tdata %p, socket %p, action %d\n", socket, socket_data, action) - val pollHandle = - if socket_data == null then - logger.debug("uninitialized poll_handle") - val buf = malloc(uv_handle_size(UV_POLL_T)).cast[Ptr[Ptr[Byte]]] - !buf = socket - check(uv_poll_init_socket(loop, buf, socket), "uv_poll_init_socket") - debug_check("multi_assign", multi_assign(multi, socket, buf.cast[Ptr[Byte]])) - printf(c"\t assigned data %x containing socket %x to socket %x\n", buf, !buf, socket) - buf - else - logger.trace("poll_handle exists") - socket_data.cast[Ptr[Ptr[Byte]]] - - val events = action match - case POLL_NONE => None - case POLL_IN => Some(UV_READABLE) - case POLL_OUT => Some(UV_WRITABLE) - case POLL_INOUT => Some(UV_READABLE | UV_WRITABLE) - case POLL_REMOVE => None - - events match - case Some(ev) => - logger.info(s"starting poll with events $ev") - uv_poll_start(pollHandle, ev, readyCB) - case None => - logger.warn("stopping poll") - uv_poll_stop(pollHandle) - start_timer(multi, 1, null) - uv_timer_start(timer_handle, timerCB, 1, 0) - free(pollHandle) - 0 - - - def cleanup_requests(): Unit = - val messages = stackalloc[Int]() - val privateDataPtr = stackalloc[Ptr[CurlRequest]]() - var message: Ptr[CurlMessage] = multi_info_read(multi,messages) - while message != null do - logger.info(s"""Got a message ${!message._1} from multi_info_read, - ${!messages} left in queue""") - val handle = !message._2 - trace_check("easy_getinfo", easy_getinfo(handle, GET_PRIVATEDATA, privateDataPtr)) - val privateData = !privateDataPtr - Curl.complete_request(!privateDataPtr) - message = multi_info_read(multi, messages) - - logger.debug("done handling messages") - - def shutdown(): Unit = - debug_check("cleanup",multi_cleanup(multi)) - global_cleanup() - initialized = false - - def set_timeout(curl: MultiCurl, timeout_ms: Long, data: Ptr[Byte]): Int = - logger.info(s"start_timer called with timeout ${timeout_ms} ms") - val time = - if timeout_ms < 1 then - logger.warn("setting effective timeout to 1") - 1 - else timeout_ms - printf(c"timer handle is %p, timer cb is %p\n", timer_handle, timerCB) - check(uv_timer_start(timer_handle, timerCB, time, 0), "uv_timer_start") - cleanup_requests() - 0 - - val socketCB = CFunctionPtr.fromFunction5(handle_socket) - val curltimerCB = CFunctionPtr.fromFunction3(start_timer) - - def init(): Unit = - if initialized == false then - loop = uv_default_loop() - global_init(1) - multi = multi_init() - val setopt_r_1 = multi_setopt(multi, SOCKETFUNCTION, socketCB) - logger.debug(s"multi_setopt SOCKETFUNCTION ${setopt_r_1}") - val setopt_r_2 = multi_setopt(multi, TIMERFUNCTION, curltimerCB) - logger.debug(s"multi_setopt TIMERFUNCTION ${setopt_r_2}") - - timer_handle = malloc(uv_handle_size(UV_TIMER_T)) - check(uv_timer_init(loop,timer_handle), "uv_timer_init") - logger.debug("CURL INITIALIZED") - initialized = true - diff --git a/src/main/scala/ch07/examples/multiCurl.scala b/src/main/scala/ch07/examples/multiCurl.scala new file mode 100644 index 0000000..1b8796a --- /dev/null +++ b/src/main/scala/ch07/examples/multiCurl.scala @@ -0,0 +1,132 @@ +package ch07 +package examples + +import scalanative.unsafe.* +import scalanative.libc.{stdlib, stdio} + +// This code is not meant to compile and run, it's conceptual pseudocode. +// I just made up some stuff to make it compile. + +object curlMulti: + import LibCurl.*, LibCurlConstants.*, LibUV.*, LibUVConstants.* + + var multi = stackalloc[Byte](1) // made up! + var initialized = false // made up! + var loop = uv_default_loop() // made up! + var timer_handle = stdlib.malloc(uv_handle_size(UV_TIMER_T)) // made up! + + // START: onPoll + def onPoll(pollHandle: PollHandle, status: Int, events: Int): Unit = + val socket = !(pollHandle.asInstanceOf[Ptr[Ptr[Byte]]]) + val actions = (events & 1) | (events & 2) // Whoa, nelly! + val running_handles = stackalloc[Int](1) + val result = multi_socket_action(multi, socket, actions, running_handles) // check + + def on_timer(handle: TimerHandle): Unit = + val running_handles = stackalloc[Int](1) + multi_socket_action(multi, -1.asInstanceOf[Ptr[Byte]], 0, running_handles) + // logger.debug(s"on_timer fired, ${!running_handles} sockets running") // ??? + + // val writeCB = CFuncPtr4.fromScalaFunction(writeData) // no idea what these are + // val headerCB = CFuncPtr4.fromScalaFunction(writeHeader) + // val readyCB = CFuncPtr3.fromScalaFunction(ready_for_curl) + val timerCB = CFuncPtr1.fromScalaFunction(on_timer) + + def socket_state_change( + curl: Curl, + socket: Ptr[Byte], + action: Int, + data: Ptr[Byte], + socket_data: Ptr[Byte] + ): Int = + // logger.debug(s"handle_socket called with action ${action}") + // logger.trace(c"\tdata %p, socket %p, action %d\n", socket, socket_data, action) + val pollHandle = + if socket_data == null then + // logger.debug("uninitialized poll_handle") + val buf = stdlib.malloc(uv_handle_size(UV_POLL_T)).asInstanceOf[Ptr[Ptr[Byte]]] + !buf = socket + checkError( + uv_poll_init_socket(EventLoop.loop, buf, socket), + "uv_poll_init_socket" + ) + // debug_check("multi_assign", multi_assign(multi, socket, buf.cast[Ptr[Byte]])) + stdio.printf( + c"\t assigned data %x containing socket %x to socket %x\n", + buf, + !buf, + socket + ) + buf + else + // logger.trace("poll_handle exists") + socket_data.asInstanceOf[Ptr[Ptr[Byte]]] + + val events = action match + case POLL_NONE => None + case POLL_IN => Some(UV_READABLE) + case POLL_OUT => Some(UV_WRITABLE) + case POLL_INOUT => Some(UV_READABLE | UV_WRITABLE) + case POLL_REMOVE => None + + events match + case Some(ev) => + // logger.info(s"starting poll with events $ev") + // uv_poll_start(pollHandle, ev, readyCB) + case None => + // logger.warn("stopping poll") + uv_poll_stop(pollHandle) + // start_timer(multi, 1, null) + uv_timer_start(timer_handle, timerCB, 1, 0) + stdlib.free(pollHandle) + 0 + + def cleanup_requests(): Unit = + val messages = stackalloc[Int]() + val privateDataPtr = stackalloc[Ptr[CurlRequest]](1) + var message: Ptr[CurlMessage] = multi_info_read(multi, messages) + while message != null do + // logger.info(s"""Got a message ${!message._1} from multi_info_read, + // ${!messages} left in queue""") + val handle = !message._2 + // trace_check("easy_getinfo", easy_getinfo(handle, GET_PRIVATEDATA, privateDataPtr)) + val privateData = !privateDataPtr + // Curl.complete_request(!privateDataPtr) + message = multi_info_read(multi, messages) + + // logger.debug("done handling messages") + + def shutdown(): Unit = + // debug_check("cleanup", multi_cleanup(multi)) + global_cleanup() + initialized = false + + def set_timeout(curl: MultiCurl, timeout_ms: Long, data: Ptr[Byte]): Int = + // logger.info(s"start_timer called with timeout ${timeout_ms} ms") + val time = + if timeout_ms < 1 then + // logger.warn("setting effective timeout to 1") + 1 + else timeout_ms + stdio.printf(c"timer handle is %p, timer cb is %p\n", timer_handle, timerCB) + checkError(uv_timer_start(timer_handle, timerCB, time, 0), "uv_timer_start") + cleanup_requests() + 0 + + // val socketCB = CFuncPtr5.fromScalaFunction(handle_socket) + // val curltimerCB = CFuncPtr1.fromScalaFunction(start_timer) + + def init(): Unit = + if initialized == false then + loop = uv_default_loop() + global_init(1) + multi = multi_init() + // val setopt_r_1 = curl_multi_setopt(multi, SOCKETFUNCTION, socketCB) + // logger.debug(s"multi_setopt SOCKETFUNCTION ${setopt_r_1}") + // val setopt_r_2 = curl_multi_setopt(multi, TIMERFUNCTION, curltimerCB) + // logger.debug(s"multi_setopt TIMERFUNCTION ${setopt_r_2}") + + timer_handle = stdlib.malloc(uv_handle_size(UV_TIMER_T)) + // checkError(uv_timer_init(loop, timer_handle), "uv_timer_init") + // logger.debug("CURL INITIALIZED") + initialized = true diff --git a/src/main/scala/ch07/examples/simplemulti b/src/main/scala/ch07/examples/simplemulti deleted file mode 100644 index 7889226..0000000 --- a/src/main/scala/ch07/examples/simplemulti +++ /dev/null @@ -1,75 +0,0 @@ -/*** - * Excerpted from "Modern Systems Programming with Scala Native", - * published by The Pragmatic Bookshelf. - * Copyrights apply to this code. It may not be used to create training material, - * courses, books, articles, and the like. Contact us if you are in doubt. - * We make no guarantees that this code is fit for any purpose. - * Visit http://www.pragmaticprogrammer.com/titles/rwscala for more book information. -***/ -import scalanative.native.* -import Curl.* -import LibCurl.* -import stdlib.* -import stdio.* - -object curlBasic: - def writeData(ptr: Ptr[Byte], size: CSize, nmemb: CSize, data: Ptr[CurlBuffer]): CSize = - val len = stackalloc[Double]() - !len = 0 - println(easy_getinfo(data.cast[Ptr[Byte]], CONTENTLENGTHDOWNLOADT, len)) - println(s"got data of size ${size} x ${nmemb}, body length ${!len}") - size * nmemb - - def writeHeader( - ptr: Ptr[Byte], - size: CSize, - nmemb: CSize, - data: Ptr[CurlBuffer] - ): CSize = - val len = stackalloc[Double]() - !len = 0 - println(easy_getinfo(data.cast[Ptr[Byte]], CONTENTLENGTHDOWNLOADT, len)) - val byteSize = size * nmemb - printf(c"got header line of size %d, body length %f: ", !len, byteSize) - fwrite(ptr, size, nmemb, stdout) - byteSize - - val writeCB = CFunctionPtr.fromFunction4(writeData) - val headerCB = CFunctionPtr.fromFunction4(writeHeader) - - def main(args: Array[String]): Unit = - println("hello world") - println("initializing") - global_init(1) - println("initialized, creating handle") - val curl = easy_init() - println("initialized") - - val bodyResp = malloc(sizeof[CurlBuffer]).cast[Ptr[CurlBuffer]] - !bodyResp._1 = malloc(4096).cast[CString] - !bodyResp._2 = 0 - val headersResp = malloc(sizeof[CurlBuffer]).cast[Ptr[CurlBuffer]] - !headersResp._1 = malloc(4096).cast[CString] - !headersResp._2 = 0 - println(easy_setopt(curl, URL, c"http://www.example.com")) - println(easy_setopt(curl, WRITECALLBACK, writeCB)) - println(easy_setopt(curl, WRITEDATA, curl)) - println(easy_setopt(curl, HEADERCALLBACK, headerCB)) - println(easy_setopt(curl, HEADERDATA, curl)) - - val multi = multi_init() - val handles = stackalloc[Int]() - !handles = 1 - println("multi_add_handle", multi_add_handle(multi, curl)) - while !handles > 0 do - val poll_result = multi_perform(multi, handles) - println("multi_perform", poll_result, "handles:", !handles) - if !handles > 0 then Thread.sleep(100) - - println("cleaning up") - easy_cleanup(curl) - multi_cleanup(multi) - println("global cleanup...") - global_cleanup() - println("done") - diff --git a/src/main/scala/ch07/examples/simplemulti.scala b/src/main/scala/ch07/examples/simplemulti.scala new file mode 100644 index 0000000..abdf0d9 --- /dev/null +++ b/src/main/scala/ch07/examples/simplemulti.scala @@ -0,0 +1,85 @@ +package ch07 +package examples + +import scalanative.libc.*, stdlib.*, stdio.* +import scalanative.unsafe.* +import scalanative.unsigned.UnsignedRichInt +import Curl.* + +// This code is not meant to compile and run, it's conceptual pseudocode. +// I just made up some stuff to make it compile. + +object curlBasic: + import LibCurl.*, LibCurlConstants.* + def writeData(ptr: Ptr[Byte], size: CSize, nmemb: CSize, data: Ptr[CurlBuffer]): CSize = + val len = stackalloc[Double](1) + !len = 0.0 + println( + easy_getinfo( + data.asInstanceOf[Ptr[Byte]], + CONTENTLENGTHDOWNLOADT, + len.asInstanceOf[Ptr[Byte]] + ) + ) + println(s"got data of size ${size} x ${nmemb}, body length ${!len}") + size * nmemb + + def writeHeader( + ptr: Ptr[Byte], + size: CSize, + nmemb: CSize, + data: Ptr[CurlBuffer] + ): CSize = + val len = stackalloc[Double]() + !len = 0 + println( + easy_getinfo( + data.asInstanceOf[Ptr[Byte]], + CONTENTLENGTHDOWNLOADT, + len.asInstanceOf[Ptr[Byte]] + ) + ) + val byteSize = size * nmemb + printf(c"got header line of size %d, body length %f: ", !len, byteSize) + fwrite(ptr, size, nmemb, stdout) + byteSize + + val writeCB = CFuncPtr4.fromScalaFunction(writeData) + val headerCB = CFuncPtr4.fromScalaFunction(writeHeader) + + @main + def run(args: String*): Unit = + println("hello world") + println("initializing") + global_init(1) + println("initialized, creating handle") + val curl = easy_init() + println("initialized") + + val bodyResp = malloc(sizeof[CurlBuffer]).asInstanceOf[Ptr[CurlBuffer]] + !bodyResp._1 = malloc(4096).asInstanceOf[CChar] + bodyResp._2 = 0.toUSize + val headersResp = malloc(sizeof[CurlBuffer]).asInstanceOf[Ptr[CurlBuffer]] + !headersResp._1 = malloc(4096).asInstanceOf[CChar] + headersResp._2 = 0.toUSize + println(curl_easy_setopt(curl, URL, c"http://www.example.com")) + // println(curl_easy_setopt(curl, WRITECALLBACK, writeCB)) + println(curl_easy_setopt(curl, WRITEDATA, curl)) + // println(curl_easy_setopt(curl, HEADERCALLBACK, headerCB)) + println(curl_easy_setopt(curl, HEADERDATA, curl)) + + val multi = multi_init() + val handles = stackalloc[Int]() + !handles = 1 + println(s"multi_add_handle: ${multi_add_handle(multi, curl)}") + while !handles > 0 do + val pollResult = multi_perform(multi, handles) + println(s"multi_perform: $pollResult, handles: ${!handles}") + if !handles > 0 then Thread.sleep(100) + + println("cleaning up") + easy_cleanup(curl) + multi_cleanup(multi) + println("global cleanup...") + global_cleanup() + println("done")