2025-04-20 00:04:24 +05:30
|
|
|
import std/typetraits
|
|
|
|
|
import system # for GC thread setup/teardown
|
2025-03-30 23:19:15 +05:30
|
|
|
import chronos
|
2025-03-13 22:58:55 +05:30
|
|
|
import results
|
2025-03-30 23:19:15 +05:30
|
|
|
import ../src/[reliability, reliability_utils, message]
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
type CReliabilityManagerHandle* = pointer
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
type
|
|
|
|
|
# Callback Types (Imported from C Header)
|
|
|
|
|
CEventType* {.importc: "CEventType", header: "bindings.h", pure.} = enum
|
2025-04-20 00:04:24 +05:30
|
|
|
EVENT_MESSAGE_READY = 1
|
|
|
|
|
EVENT_MESSAGE_SENT = 2
|
|
|
|
|
EVENT_MISSING_DEPENDENCIES = 3
|
2025-04-07 17:19:44 +05:30
|
|
|
EVENT_PERIODIC_SYNC = 4
|
|
|
|
|
|
2025-03-30 23:19:15 +05:30
|
|
|
CResult* {.importc: "CResult", header: "bindings.h", bycopy.} = object
|
|
|
|
|
is_ok*: bool
|
|
|
|
|
error_message*: cstring
|
|
|
|
|
|
|
|
|
|
CWrapResult* {.importc: "CWrapResult", header: "bindings.h", bycopy.} = object
|
|
|
|
|
base_result*: CResult
|
|
|
|
|
message*: pointer
|
2025-04-07 17:19:44 +05:30
|
|
|
message_len*: csize_t
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
CUnwrapResult* {.importc: "CUnwrapResult", header: "bindings.h", bycopy.} = object
|
|
|
|
|
base_result*: CResult
|
|
|
|
|
message*: pointer
|
2025-04-07 17:19:44 +05:30
|
|
|
message_len*: csize_t
|
|
|
|
|
missing_deps*: ptr cstring
|
|
|
|
|
missing_deps_count*: csize_t
|
|
|
|
|
|
2025-03-30 23:19:15 +05:30
|
|
|
# --- Memory Management Helpers ---
|
|
|
|
|
|
|
|
|
|
proc allocCString*(s: string): cstring {.inline, gcsafe.} =
|
2025-04-20 00:04:24 +05:30
|
|
|
if s.len == 0:
|
|
|
|
|
echo "[Nim Binding][allocCString] Allocating empty string"
|
|
|
|
|
return nil
|
2025-03-30 23:19:15 +05:30
|
|
|
result = cast[cstring](allocShared(s.len + 1))
|
|
|
|
|
copyMem(result, s.cstring, s.len + 1)
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][allocCString] Allocated cstring at ",
|
|
|
|
|
cast[int](result), " for: ", s
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
proc allocSeqByte*(s: seq[byte]): (pointer, csize_t) {.inline, gcsafe.} =
|
2025-04-20 00:04:24 +05:30
|
|
|
if s.len == 0:
|
|
|
|
|
echo "[Nim Binding][allocSeqByte] Allocating empty seq[byte]"
|
|
|
|
|
return (nil, 0)
|
2025-03-30 23:19:15 +05:30
|
|
|
let len = s.len
|
|
|
|
|
let bufferPtr = allocShared(len)
|
|
|
|
|
if len > 0:
|
|
|
|
|
copyMem(bufferPtr, cast[pointer](s[0].unsafeAddr), len.Natural)
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][allocSeqByte] Allocated buffer at ",
|
|
|
|
|
cast[int](bufferPtr), " of length ", len
|
2025-04-07 17:19:44 +05:30
|
|
|
return (bufferPtr, len.csize_t)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc allocSeqCString*(
|
|
|
|
|
s: seq[string]
|
|
|
|
|
): (ptr cstring, csize_t) {.inline, gcsafe, cdecl.} =
|
|
|
|
|
if s.len == 0:
|
|
|
|
|
echo "[Nim Binding][allocSeqCString] Allocating empty seq[string]"
|
|
|
|
|
return (nil, 0)
|
2025-03-30 23:19:15 +05:30
|
|
|
let count = s.len
|
2025-04-07 17:19:44 +05:30
|
|
|
# Allocate memory for 'count' cstring pointers, cast to ptr UncheckedArray
|
|
|
|
|
let arrPtr = cast[ptr UncheckedArray[cstring]](allocShared(count * sizeof(cstring)))
|
2025-04-20 00:04:24 +05:30
|
|
|
for i in 0 ..< count:
|
2025-04-07 17:19:44 +05:30
|
|
|
# Allocate each string and store its pointer in the array using unchecked array indexing
|
|
|
|
|
arrPtr[i] = allocCString(s[i])
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][allocSeqCString] Allocated cstring for missingDep[",
|
|
|
|
|
i, "]: ", s[i], " at ", cast[int](arrPtr[i])
|
2025-04-07 17:19:44 +05:30
|
|
|
# Return pointer to the first element, cast back to ptr cstring
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][allocSeqCString] Allocated array at ",
|
|
|
|
|
cast[int](arrPtr), " with count ", count
|
2025-04-07 17:19:44 +05:30
|
|
|
return (cast[ptr cstring](arrPtr), count.csize_t)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
proc freeCString*(cs: cstring) {.inline, gcsafe.} =
|
2025-04-20 00:04:24 +05:30
|
|
|
if cs != nil:
|
|
|
|
|
echo "[Nim Binding][freeCString] Freeing cstring at ", cast[int](cs)
|
|
|
|
|
deallocShared(cs)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
proc freeSeqByte*(bufferPtr: pointer) {.inline, gcsafe, cdecl.} =
|
2025-04-20 00:04:24 +05:30
|
|
|
if bufferPtr != nil:
|
|
|
|
|
echo "[Nim Binding][freeSeqByte] Freeing buffer at ", cast[int](bufferPtr)
|
|
|
|
|
deallocShared(bufferPtr)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
# Corrected to accept ptr cstring
|
|
|
|
|
proc freeSeqCString*(arrPtr: ptr cstring, count: csize_t) {.inline, gcsafe, cdecl.} =
|
2025-03-30 23:19:15 +05:30
|
|
|
if arrPtr != nil:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][freeSeqCString] Freeing array at ",
|
|
|
|
|
cast[int](arrPtr), " with count ", count
|
2025-04-07 17:19:44 +05:30
|
|
|
# Cast to ptr UncheckedArray for proper iteration/indexing before freeing
|
|
|
|
|
let arr = cast[ptr UncheckedArray[cstring]](arrPtr)
|
2025-04-20 00:04:24 +05:30
|
|
|
for i in 0 ..< count:
|
|
|
|
|
echo "[Nim Binding][freeSeqCString] Freeing cstring[",
|
|
|
|
|
i, "] at ", cast[int](arr[i])
|
2025-04-07 17:19:44 +05:30
|
|
|
freeCString(arr[i]) # Free each individual cstring
|
|
|
|
|
deallocShared(arrPtr) # Free the array pointer itself
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
# --- Result Conversion Helpers ---
|
|
|
|
|
|
|
|
|
|
proc toCResultOk*(): CResult =
|
|
|
|
|
CResult(is_ok: true, error_message: nil)
|
|
|
|
|
|
|
|
|
|
proc toCResultErr*(err: ReliabilityError): CResult =
|
|
|
|
|
CResult(is_ok: false, error_message: allocCString($err))
|
|
|
|
|
|
|
|
|
|
proc toCResultErrStr*(errMsg: string): CResult =
|
|
|
|
|
CResult(is_ok: false, error_message: allocCString(errMsg))
|
|
|
|
|
|
|
|
|
|
# --- Callback Wrappers (Nim -> C) ---
|
2025-04-20 00:04:24 +05:30
|
|
|
# These wrappers retrieve the C callback info from the ReliabilityManager object.
|
|
|
|
|
|
|
|
|
|
proc nimMessageReadyCallback(rm: ReliabilityManager, messageId: MessageID) {.gcsafe.} =
|
|
|
|
|
setupForeignThreadGc() # Setup GC for this Go thread
|
|
|
|
|
defer:
|
|
|
|
|
tearDownForeignThreadGc() # Ensure teardown even if callback errors
|
2025-04-07 17:19:44 +05:30
|
|
|
|
|
|
|
|
echo "[Nim Binding] nimMessageReadyCallback called for: ", messageId
|
2025-04-20 00:04:24 +05:30
|
|
|
let handle = cast[CReliabilityManagerHandle](rm) # Still use handle for C side
|
|
|
|
|
let cb = rm.cCallback
|
|
|
|
|
|
|
|
|
|
if cb == nil:
|
|
|
|
|
echo "[Nim Binding] No C callback stored in handle: ", cast[int](handle)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Pass handle, event type, and messageId (as data1), plus user_data
|
2025-04-07 17:19:44 +05:30
|
|
|
cb(handle, EVENT_MESSAGE_READY, cast[pointer](messageId.cstring), nil, 0)
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc nimMessageSentCallback(rm: ReliabilityManager, messageId: MessageID) {.gcsafe.} =
|
|
|
|
|
setupForeignThreadGc()
|
|
|
|
|
defer:
|
|
|
|
|
tearDownForeignThreadGc()
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
echo "[Nim Binding] nimMessageSentCallback called for: ", messageId
|
|
|
|
|
let handle = cast[CReliabilityManagerHandle](rm)
|
2025-04-20 00:04:24 +05:30
|
|
|
let cb = rm.cCallback
|
|
|
|
|
|
|
|
|
|
if cb == nil:
|
|
|
|
|
echo "[Nim Binding] No C callback stored in handle: ", cast[int](handle)
|
|
|
|
|
return
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
cb(handle, EVENT_MESSAGE_SENT, cast[pointer](messageId.cstring), nil, 0)
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc nimMissingDependenciesCallback(
|
|
|
|
|
rm: ReliabilityManager, messageId: MessageID, missingDeps: seq[MessageID]
|
|
|
|
|
) {.gcsafe.} =
|
|
|
|
|
setupForeignThreadGc()
|
|
|
|
|
defer:
|
|
|
|
|
tearDownForeignThreadGc()
|
|
|
|
|
|
|
|
|
|
echo "[Nim Binding] nimMissingDependenciesCallback called for: ",
|
|
|
|
|
messageId, " with deps: ", $missingDeps
|
2025-04-07 17:19:44 +05:30
|
|
|
let handle = cast[CReliabilityManagerHandle](rm)
|
2025-04-20 00:04:24 +05:30
|
|
|
let cb = rm.cCallback
|
|
|
|
|
|
|
|
|
|
if cb == nil:
|
|
|
|
|
echo "[Nim Binding] No C callback stored in handle: ", cast[int](handle)
|
|
|
|
|
return
|
2025-04-07 17:19:44 +05:30
|
|
|
|
|
|
|
|
# Prepare data for the callback
|
|
|
|
|
var cDepsPtr: ptr cstring = nil
|
|
|
|
|
var cDepsCount: csize_t = 0
|
|
|
|
|
var cDepsNim: seq[cstring] = @[] # Keep Nim seq alive during call
|
|
|
|
|
if missingDeps.len > 0:
|
|
|
|
|
cDepsNim = newSeq[cstring](missingDeps.len)
|
2025-03-30 23:19:15 +05:30
|
|
|
for i, dep in missingDeps:
|
2025-04-07 17:19:44 +05:30
|
|
|
cDepsNim[i] = dep.cstring # Nim GC manages these cstrings via the seq
|
|
|
|
|
cDepsPtr = cast[ptr cstring](cDepsNim[0].addr)
|
|
|
|
|
cDepsCount = missingDeps.len.csize_t
|
2025-04-20 00:04:24 +05:30
|
|
|
# Ensure cDepsNim stays alive during the call if cDepsPtr points into it
|
|
|
|
|
# Using allocSeqCString might be safer if Go needs to hold onto the data.
|
|
|
|
|
# For now, assuming Go copies the data immediately during the callback.
|
|
|
|
|
|
|
|
|
|
cb(
|
|
|
|
|
handle,
|
|
|
|
|
EVENT_MISSING_DEPENDENCIES,
|
|
|
|
|
cast[pointer](messageId.cstring),
|
|
|
|
|
cast[pointer](cDepsPtr),
|
|
|
|
|
cDepsCount,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
proc nimPeriodicSyncCallback(rm: ReliabilityManager) {.gcsafe.} =
|
|
|
|
|
setupForeignThreadGc()
|
|
|
|
|
defer:
|
|
|
|
|
tearDownForeignThreadGc()
|
2025-04-07 17:19:44 +05:30
|
|
|
|
|
|
|
|
echo "[Nim Binding] nimPeriodicSyncCallback called"
|
|
|
|
|
let handle = cast[CReliabilityManagerHandle](rm)
|
2025-04-20 00:04:24 +05:30
|
|
|
let cb = rm.cCallback
|
|
|
|
|
|
|
|
|
|
if cb == nil:
|
|
|
|
|
echo "[Nim Binding] No C callback stored in handle: ", cast[int](handle)
|
|
|
|
|
return
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
cb(handle, EVENT_PERIODIC_SYNC, nil, nil, 0)
|
|
|
|
|
|
|
|
|
|
# --- Exported C Functions - Using Opaque Pointer ---
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc NewReliabilityManager*(
|
|
|
|
|
channelIdCStr: cstring
|
|
|
|
|
): CReliabilityManagerHandle {.exportc, dynlib, cdecl, gcsafe.} =
|
2025-03-30 23:19:15 +05:30
|
|
|
let channelId = $channelIdCStr
|
|
|
|
|
if channelId.len == 0:
|
|
|
|
|
echo "Error creating ReliabilityManager: Channel ID cannot be empty"
|
|
|
|
|
return nil # Return nil pointer
|
|
|
|
|
let rmResult = newReliabilityManager(channelId)
|
|
|
|
|
if rmResult.isOk:
|
|
|
|
|
let rm = rmResult.get()
|
2025-04-20 00:04:24 +05:30
|
|
|
rm.onMessageReady = proc(rmArg: ReliabilityManager, msgId: MessageID) {.gcsafe.} =
|
|
|
|
|
nimMessageReadyCallback(rmArg, msgId)
|
|
|
|
|
rm.onMessageSent = proc(rmArg: ReliabilityManager, msgId: MessageID) {.gcsafe.} =
|
|
|
|
|
nimMessageSentCallback(rmArg, msgId)
|
|
|
|
|
rm.onMissingDependencies = proc(
|
|
|
|
|
rmArg: ReliabilityManager, msgId: MessageID, deps: seq[MessageID]
|
|
|
|
|
) {.gcsafe.} =
|
|
|
|
|
nimMissingDependenciesCallback(rmArg, msgId, deps)
|
|
|
|
|
rm.onPeriodicSync = proc(rmArg: ReliabilityManager) {.gcsafe.} =
|
|
|
|
|
nimPeriodicSyncCallback(rmArg)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
# Return the Nim ref object cast to the opaque pointer type
|
2025-04-07 17:19:44 +05:30
|
|
|
let handle = cast[CReliabilityManagerHandle](rm)
|
|
|
|
|
GC_ref(rm) # Prevent GC from moving the object while Go holds the handle
|
|
|
|
|
return handle
|
2025-03-30 23:19:15 +05:30
|
|
|
else:
|
|
|
|
|
echo "Error creating ReliabilityManager: ", rmResult.error
|
|
|
|
|
return nil # Return nil pointer
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc CleanupReliabilityManager*(
|
|
|
|
|
handle: CReliabilityManagerHandle
|
|
|
|
|
) {.exportc, dynlib, cdecl.} =
|
2025-04-07 17:19:44 +05:30
|
|
|
let handlePtr = handle
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][Cleanup] Called with handle: ", cast[int](handlePtr)
|
2025-04-07 17:19:44 +05:30
|
|
|
if handlePtr != nil:
|
|
|
|
|
# Go side should handle removing the handle from its registry.
|
|
|
|
|
# We just need to unref the Nim object.
|
|
|
|
|
|
2025-03-30 23:19:15 +05:30
|
|
|
# Cast opaque pointer back to Nim ref type
|
2025-04-07 17:19:44 +05:30
|
|
|
let rm = cast[ReliabilityManager](handlePtr)
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][Cleanup] Calling Nim core cleanup for handle: ",
|
|
|
|
|
cast[int](handlePtr)
|
|
|
|
|
cleanup(rm)
|
|
|
|
|
echo "[Nim Binding][Cleanup] Calling GC_unref for handle: ", cast[int](handlePtr)
|
2025-04-07 17:19:44 +05:30
|
|
|
GC_unref(rm) # Allow GC to collect the object now that Go is done
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][Cleanup] GC_unref returned for handle: ", cast[int](handlePtr)
|
2025-03-30 23:19:15 +05:30
|
|
|
else:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][Cleanup] Warning: CleanupReliabilityManager called with NULL handle"
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc ResetReliabilityManager*(
|
|
|
|
|
handle: CReliabilityManagerHandle
|
|
|
|
|
): CResult {.exportc, dynlib, cdecl, gcsafe.} =
|
2025-03-30 23:19:15 +05:30
|
|
|
if handle == nil:
|
|
|
|
|
return toCResultErrStr("ReliabilityManager handle is NULL")
|
2025-04-07 17:19:44 +05:30
|
|
|
let rm = cast[ReliabilityManager](handle)
|
2025-03-30 23:19:15 +05:30
|
|
|
let result = resetReliabilityManager(rm)
|
|
|
|
|
if result.isOk:
|
|
|
|
|
return toCResultOk()
|
|
|
|
|
else:
|
|
|
|
|
return toCResultErr(result.error)
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc WrapOutgoingMessage*(
|
|
|
|
|
handle: CReliabilityManagerHandle,
|
|
|
|
|
messageC: pointer,
|
|
|
|
|
messageLen: csize_t,
|
|
|
|
|
messageIdCStr: cstring,
|
|
|
|
|
): CWrapResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe
|
|
|
|
|
echo "[Nim Binding][WrapOutgoingMessage] Called with handle=",
|
|
|
|
|
cast[int](handle), " messageLen=", messageLen, " messageId=", $messageIdCStr
|
2025-03-30 23:19:15 +05:30
|
|
|
if handle == nil:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][WrapOutgoingMessage] Error: handle is nil"
|
|
|
|
|
return
|
|
|
|
|
CWrapResult(base_result: toCResultErrStr("ReliabilityManager handle is NULL"))
|
2025-04-07 17:19:44 +05:30
|
|
|
let rm = cast[ReliabilityManager](handle)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
if messageC == nil and messageLen > 0:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][WrapOutgoingMessage] Error: message pointer is NULL but length > 0"
|
|
|
|
|
return CWrapResult(
|
|
|
|
|
base_result: toCResultErrStr("Message pointer is NULL but length > 0")
|
|
|
|
|
)
|
2025-03-30 23:19:15 +05:30
|
|
|
if messageIdCStr == nil:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][WrapOutgoingMessage] Error: messageId pointer is NULL"
|
|
|
|
|
return CWrapResult(base_result: toCResultErrStr("Message ID pointer is NULL"))
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
let messageId = $messageIdCStr
|
|
|
|
|
var messageNim: seq[byte]
|
|
|
|
|
if messageLen > 0:
|
|
|
|
|
messageNim = newSeq[byte](messageLen)
|
|
|
|
|
copyMem(messageNim[0].addr, messageC, messageLen.Natural)
|
|
|
|
|
else:
|
|
|
|
|
messageNim = @[]
|
|
|
|
|
|
|
|
|
|
let wrapResult = wrapOutgoingMessage(rm, messageNim, messageId)
|
|
|
|
|
if wrapResult.isOk:
|
|
|
|
|
let (wrappedDataPtr, wrappedDataLen) = allocSeqByte(wrapResult.get())
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][WrapOutgoingMessage] Returning wrapped message at ",
|
|
|
|
|
cast[int](wrappedDataPtr), " len=", wrappedDataLen
|
2025-03-30 23:19:15 +05:30
|
|
|
return CWrapResult(
|
2025-04-20 00:04:24 +05:30
|
|
|
base_result: toCResultOk(), message: wrappedDataPtr, message_len: wrappedDataLen
|
2025-03-13 22:58:55 +05:30
|
|
|
)
|
|
|
|
|
else:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][WrapOutgoingMessage] Error: ", $wrapResult.error
|
2025-03-30 23:19:15 +05:30
|
|
|
return CWrapResult(base_result: toCResultErr(wrapResult.error))
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc UnwrapReceivedMessage*(
|
|
|
|
|
handle: CReliabilityManagerHandle, messageC: pointer, messageLen: csize_t
|
|
|
|
|
): CUnwrapResult {.exportc, dynlib, cdecl.} =
|
|
|
|
|
echo "[Nim Binding][UnwrapReceivedMessage] Called with handle=",
|
|
|
|
|
cast[int](handle), " messageLen=", messageLen
|
2025-03-30 23:19:15 +05:30
|
|
|
if handle == nil:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][UnwrapReceivedMessage] Error: handle is nil"
|
|
|
|
|
return
|
|
|
|
|
CUnwrapResult(base_result: toCResultErrStr("ReliabilityManager handle is NULL"))
|
2025-04-07 17:19:44 +05:30
|
|
|
let rm = cast[ReliabilityManager](handle)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
if messageC == nil and messageLen > 0:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][UnwrapReceivedMessage] Error: message pointer is NULL but length > 0"
|
|
|
|
|
return CUnwrapResult(
|
|
|
|
|
base_result: toCResultErrStr("Message pointer is NULL but length > 0")
|
|
|
|
|
)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
var messageNim: seq[byte]
|
|
|
|
|
if messageLen > 0:
|
|
|
|
|
messageNim = newSeq[byte](messageLen)
|
|
|
|
|
copyMem(messageNim[0].addr, messageC, messageLen.Natural)
|
2025-03-13 22:58:55 +05:30
|
|
|
else:
|
2025-03-30 23:19:15 +05:30
|
|
|
messageNim = @[]
|
|
|
|
|
|
|
|
|
|
let unwrapResult = unwrapReceivedMessage(rm, messageNim)
|
|
|
|
|
if unwrapResult.isOk:
|
|
|
|
|
let (unwrappedContent, missingDepsNim) = unwrapResult.get()
|
|
|
|
|
let (contentPtr, contentLen) = allocSeqByte(unwrappedContent)
|
|
|
|
|
let (depsPtr, depsCount) = allocSeqCString(missingDepsNim)
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][UnwrapReceivedMessage] Returning content at ",
|
|
|
|
|
cast[int](contentPtr),
|
|
|
|
|
" len=",
|
|
|
|
|
contentLen,
|
|
|
|
|
" missingDepsPtr=",
|
|
|
|
|
cast[int](depsPtr),
|
|
|
|
|
" count=",
|
|
|
|
|
depsCount
|
2025-03-30 23:19:15 +05:30
|
|
|
return CUnwrapResult(
|
|
|
|
|
base_result: toCResultOk(),
|
|
|
|
|
message: contentPtr,
|
|
|
|
|
message_len: contentLen,
|
|
|
|
|
missing_deps: depsPtr,
|
2025-04-20 00:04:24 +05:30
|
|
|
missing_deps_count: depsCount,
|
2025-03-30 23:19:15 +05:30
|
|
|
)
|
2025-03-13 22:58:55 +05:30
|
|
|
else:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][UnwrapReceivedMessage] Error: ", $unwrapResult.error
|
2025-03-30 23:19:15 +05:30
|
|
|
return CUnwrapResult(base_result: toCResultErr(unwrapResult.error))
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc MarkDependenciesMet*(
|
|
|
|
|
handle: CReliabilityManagerHandle, messageIDsC: ptr cstring, count: csize_t
|
|
|
|
|
): CResult {.exportc, dynlib, cdecl.} =
|
|
|
|
|
echo "[Nim Binding][MarkDependenciesMet] Called with handle=",
|
|
|
|
|
cast[int](handle), " count=", count
|
2025-03-30 23:19:15 +05:30
|
|
|
if handle == nil:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][MarkDependenciesMet] Error: handle is nil"
|
2025-03-30 23:19:15 +05:30
|
|
|
return toCResultErrStr("ReliabilityManager handle is NULL")
|
2025-04-07 17:19:44 +05:30
|
|
|
let rm = cast[ReliabilityManager](handle)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
if messageIDsC == nil and count > 0:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][MarkDependenciesMet] Error: messageIDs pointer is NULL but count > 0"
|
2025-03-30 23:19:15 +05:30
|
|
|
return toCResultErrStr("MessageIDs pointer is NULL but count > 0")
|
|
|
|
|
|
|
|
|
|
var messageIDsNim = newSeq[string](count)
|
2025-04-07 17:19:44 +05:30
|
|
|
# Cast to ptr UncheckedArray for indexing
|
|
|
|
|
let messageIDsCArray = cast[ptr UncheckedArray[cstring]](messageIDsC)
|
2025-04-20 00:04:24 +05:30
|
|
|
for i in 0 ..< count:
|
2025-04-07 17:19:44 +05:30
|
|
|
let currentCStr = messageIDsCArray[i] # Use unchecked array indexing
|
2025-03-30 23:19:15 +05:30
|
|
|
if currentCStr != nil:
|
|
|
|
|
messageIDsNim[i] = $currentCStr
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][MarkDependenciesMet] messageID[",
|
|
|
|
|
i, "] = ", messageIDsNim[i], " at ", cast[int](currentCStr)
|
2025-03-30 23:19:15 +05:30
|
|
|
else:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][MarkDependenciesMet] NULL message ID found in array at index ",
|
|
|
|
|
i
|
2025-03-30 23:19:15 +05:30
|
|
|
return toCResultErrStr("NULL message ID found in array")
|
|
|
|
|
|
|
|
|
|
let result = markDependenciesMet(rm, messageIDsNim)
|
|
|
|
|
if result.isOk:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][MarkDependenciesMet] Success"
|
2025-03-30 23:19:15 +05:30
|
|
|
return toCResultOk()
|
2025-03-13 22:58:55 +05:30
|
|
|
else:
|
2025-04-20 00:04:24 +05:30
|
|
|
echo "[Nim Binding][MarkDependenciesMet] Error: ", $result.error
|
2025-03-30 23:19:15 +05:30
|
|
|
return toCResultErr(result.error)
|
|
|
|
|
|
2025-04-20 00:04:24 +05:30
|
|
|
proc RegisterCallback*(
|
|
|
|
|
handle: CReliabilityManagerHandle,
|
|
|
|
|
cEventCallback: CEventCallback,
|
|
|
|
|
cUserDataPtr: pointer,
|
|
|
|
|
) {.exportc, dynlib, cdecl, gcsafe.} =
|
|
|
|
|
if handle == nil:
|
|
|
|
|
echo "[Nim Binding][RegisterCallback] Error: handle is NULL"
|
|
|
|
|
return
|
|
|
|
|
let rm = cast[ReliabilityManager](handle)
|
|
|
|
|
rm.cCallback = cEventCallback
|
|
|
|
|
rm.cUserData = cUserDataPtr # Store user data pointer
|
|
|
|
|
echo "[Nim Binding] Stored C callback and user data for handle: ", cast[int](handle)
|
2025-04-07 17:19:44 +05:30
|
|
|
|
|
|
|
|
proc StartPeriodicTasks*(handle: CReliabilityManagerHandle) {.exportc, dynlib, cdecl.} =
|
2025-03-30 23:19:15 +05:30
|
|
|
if handle == nil:
|
|
|
|
|
echo "Error: Cannot start periodic tasks: NULL ReliabilityManager handle"
|
|
|
|
|
return
|
2025-04-07 17:19:44 +05:30
|
|
|
let rm = cast[ReliabilityManager](handle)
|
2025-03-30 23:19:15 +05:30
|
|
|
startPeriodicTasks(rm)
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
# --- Memory Freeing Functions ---
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
proc FreeCResultError*(result: CResult) {.exportc, dynlib, gcsafe, cdecl.} =
|
|
|
|
|
freeCString(result.error_message)
|
|
|
|
|
|
|
|
|
|
proc FreeCWrapResult*(result: CWrapResult) {.exportc, dynlib, gcsafe, cdecl.} =
|
|
|
|
|
freeCString(result.base_result.error_message)
|
|
|
|
|
freeSeqByte(result.message)
|
|
|
|
|
|
|
|
|
|
proc FreeCUnwrapResult*(result: CUnwrapResult) {.exportc, dynlib, gcsafe, cdecl.} =
|
|
|
|
|
freeCString(result.base_result.error_message)
|
|
|
|
|
freeSeqByte(result.message)
|
|
|
|
|
freeSeqCString(result.missing_deps, result.missing_deps_count)
|