2025-03-30 23:19:15 +05:30
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
/*
|
2025-04-08 14:46:27 +03:00
|
|
|
#cgo CFLAGS: -I${SRCDIR}/library
|
2025-04-08 15:31:57 +03:00
|
|
|
#cgo LDFLAGS: -L${SRCDIR}/build -llibsds
|
2025-04-08 14:46:27 +03:00
|
|
|
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/build
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
#include <stdlib.h> // For C.free
|
2025-04-08 14:46:27 +03:00
|
|
|
#include "library/libsds.h" // Update include path
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// Forward declaration for the single Go callback relay function
|
|
|
|
|
extern void globalCallbackRelay(void* handle, CEventType eventType, void* data1, void* data2, size_t data3);
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
// Helper function to call the C memory freeing functions
|
|
|
|
|
static void callFreeCResultError(CResult res) { FreeCResultError(res); }
|
|
|
|
|
static void callFreeCWrapResult(CWrapResult res) { FreeCWrapResult(res); }
|
|
|
|
|
static void callFreeCUnwrapResult(CUnwrapResult res) { FreeCUnwrapResult(res); }
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
import "C"
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"sync"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// --- Go Types ---
|
|
|
|
|
|
|
|
|
|
// ReliabilityManagerHandle represents the opaque handle to the Nim object
|
|
|
|
|
type ReliabilityManagerHandle unsafe.Pointer
|
|
|
|
|
|
|
|
|
|
// MessageID is a type alias for string for clarity
|
|
|
|
|
type MessageID string
|
|
|
|
|
|
|
|
|
|
// Callbacks holds the Go functions to be called by the Nim library
|
|
|
|
|
type Callbacks struct {
|
|
|
|
|
OnMessageReady func(messageId MessageID)
|
|
|
|
|
OnMessageSent func(messageId MessageID)
|
|
|
|
|
OnMissingDependencies func(messageId MessageID, missingDeps []MessageID)
|
|
|
|
|
OnPeriodicSync func()
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// Global map to store callbacks associated with handles
|
2025-03-30 23:19:15 +05:30
|
|
|
var (
|
|
|
|
|
callbackRegistry = make(map[ReliabilityManagerHandle]*Callbacks)
|
|
|
|
|
registryMutex sync.RWMutex
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// --- Go Wrapper Functions ---
|
|
|
|
|
|
|
|
|
|
// NewReliabilityManager creates a new instance of the Nim ReliabilityManager
|
|
|
|
|
func NewReliabilityManager(channelId string) (ReliabilityManagerHandle, error) {
|
|
|
|
|
cChannelId := C.CString(channelId)
|
|
|
|
|
defer C.free(unsafe.Pointer(cChannelId))
|
|
|
|
|
|
|
|
|
|
handle := C.NewReliabilityManager(cChannelId)
|
|
|
|
|
if handle == nil {
|
|
|
|
|
// Note: Nim side currently just prints to stdout on creation failure
|
|
|
|
|
return nil, errors.New("failed to create ReliabilityManager (check Nim logs/stdout)")
|
|
|
|
|
}
|
|
|
|
|
return ReliabilityManagerHandle(handle), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CleanupReliabilityManager frees the resources associated with the handle
|
|
|
|
|
func CleanupReliabilityManager(handle ReliabilityManagerHandle) {
|
|
|
|
|
if handle == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
registryMutex.Lock()
|
|
|
|
|
delete(callbackRegistry, handle)
|
|
|
|
|
registryMutex.Unlock()
|
|
|
|
|
C.CleanupReliabilityManager(unsafe.Pointer(handle))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ResetReliabilityManager resets the state of the manager
|
|
|
|
|
func ResetReliabilityManager(handle ReliabilityManagerHandle) error {
|
|
|
|
|
if handle == nil {
|
|
|
|
|
return errors.New("handle is nil")
|
|
|
|
|
}
|
|
|
|
|
cResult := C.ResetReliabilityManager(unsafe.Pointer(handle))
|
|
|
|
|
if !cResult.is_ok {
|
|
|
|
|
errMsg := C.GoString(cResult.error_message)
|
|
|
|
|
C.callFreeCResultError(cResult) // Free the error message
|
|
|
|
|
return errors.New(errMsg)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WrapOutgoingMessage wraps a message with reliability metadata
|
|
|
|
|
func WrapOutgoingMessage(handle ReliabilityManagerHandle, message []byte, messageId MessageID) ([]byte, error) {
|
|
|
|
|
if handle == nil {
|
|
|
|
|
return nil, errors.New("handle is nil")
|
|
|
|
|
}
|
|
|
|
|
cMessageId := C.CString(string(messageId))
|
|
|
|
|
defer C.free(unsafe.Pointer(cMessageId))
|
|
|
|
|
|
|
|
|
|
var cMessagePtr unsafe.Pointer
|
|
|
|
|
if len(message) > 0 {
|
|
|
|
|
cMessagePtr = C.CBytes(message) // C.CBytes allocates memory that needs to be freed
|
|
|
|
|
defer C.free(cMessagePtr)
|
|
|
|
|
} else {
|
|
|
|
|
cMessagePtr = nil
|
|
|
|
|
}
|
|
|
|
|
cMessageLen := C.size_t(len(message))
|
|
|
|
|
|
|
|
|
|
cWrapResult := C.WrapOutgoingMessage(unsafe.Pointer(handle), cMessagePtr, cMessageLen, cMessageId)
|
|
|
|
|
|
|
|
|
|
if !cWrapResult.base_result.is_ok {
|
|
|
|
|
errMsg := C.GoString(cWrapResult.base_result.error_message)
|
|
|
|
|
C.callFreeCWrapResult(cWrapResult) // Free error and potentially allocated message
|
|
|
|
|
return nil, errors.New(errMsg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy the wrapped message from C memory to Go slice
|
|
|
|
|
// Explicitly cast the message pointer to unsafe.Pointer
|
|
|
|
|
wrappedMessage := C.GoBytes(unsafe.Pointer(cWrapResult.message), C.int(cWrapResult.message_len))
|
|
|
|
|
C.callFreeCWrapResult(cWrapResult) // Free the C-allocated message buffer
|
|
|
|
|
|
|
|
|
|
return wrappedMessage, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UnwrapReceivedMessage unwraps a received message
|
|
|
|
|
func UnwrapReceivedMessage(handle ReliabilityManagerHandle, message []byte) ([]byte, []MessageID, error) {
|
|
|
|
|
if handle == nil {
|
|
|
|
|
return nil, nil, errors.New("handle is nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cMessagePtr unsafe.Pointer
|
|
|
|
|
if len(message) > 0 {
|
|
|
|
|
cMessagePtr = C.CBytes(message)
|
|
|
|
|
defer C.free(cMessagePtr)
|
|
|
|
|
} else {
|
|
|
|
|
cMessagePtr = nil
|
|
|
|
|
}
|
|
|
|
|
cMessageLen := C.size_t(len(message))
|
|
|
|
|
|
|
|
|
|
cUnwrapResult := C.UnwrapReceivedMessage(unsafe.Pointer(handle), cMessagePtr, cMessageLen)
|
|
|
|
|
|
|
|
|
|
if !cUnwrapResult.base_result.is_ok {
|
|
|
|
|
errMsg := C.GoString(cUnwrapResult.base_result.error_message)
|
|
|
|
|
C.callFreeCUnwrapResult(cUnwrapResult) // Free error and potentially allocated fields
|
|
|
|
|
return nil, nil, errors.New(errMsg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy unwrapped message content
|
|
|
|
|
unwrappedContent := C.GoBytes(unsafe.Pointer(cUnwrapResult.message), C.int(cUnwrapResult.message_len))
|
|
|
|
|
|
|
|
|
|
// Copy missing dependencies
|
|
|
|
|
missingDeps := make([]MessageID, cUnwrapResult.missing_deps_count)
|
|
|
|
|
if cUnwrapResult.missing_deps_count > 0 {
|
|
|
|
|
// Convert C array of C strings to Go slice of strings
|
|
|
|
|
cDepsArray := (*[1 << 30]*C.char)(unsafe.Pointer(cUnwrapResult.missing_deps))[:cUnwrapResult.missing_deps_count:cUnwrapResult.missing_deps_count]
|
|
|
|
|
for i, s := range cDepsArray {
|
|
|
|
|
missingDeps[i] = MessageID(C.GoString(s))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C.callFreeCUnwrapResult(cUnwrapResult) // Free C-allocated message, deps array, and strings
|
|
|
|
|
|
|
|
|
|
return unwrappedContent, missingDeps, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MarkDependenciesMet informs the library that dependencies are met
|
|
|
|
|
func MarkDependenciesMet(handle ReliabilityManagerHandle, messageIDs []MessageID) error {
|
|
|
|
|
if handle == nil {
|
|
|
|
|
return errors.New("handle is nil")
|
|
|
|
|
}
|
|
|
|
|
if len(messageIDs) == 0 {
|
|
|
|
|
return nil // Nothing to do
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert Go string slice to C array of C strings (char**)
|
|
|
|
|
cMessageIDs := make([]*C.char, len(messageIDs))
|
|
|
|
|
for i, id := range messageIDs {
|
|
|
|
|
cMessageIDs[i] = C.CString(string(id))
|
|
|
|
|
defer C.free(unsafe.Pointer(cMessageIDs[i])) // Ensure each CString is freed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a pointer (**C.char) to the first element of the slice
|
|
|
|
|
var cMessageIDsPtr **C.char
|
|
|
|
|
if len(cMessageIDs) > 0 {
|
|
|
|
|
cMessageIDsPtr = &cMessageIDs[0]
|
|
|
|
|
} else {
|
|
|
|
|
cMessageIDsPtr = nil // Handle empty slice case
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// Pass the pointer variable (cMessageIDsPtr) directly, which is of type **C.char
|
|
|
|
|
cResult := C.MarkDependenciesMet(unsafe.Pointer(handle), cMessageIDsPtr, C.size_t(len(messageIDs)))
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
if !cResult.is_ok {
|
|
|
|
|
errMsg := C.GoString(cResult.error_message)
|
|
|
|
|
C.callFreeCResultError(cResult)
|
|
|
|
|
return errors.New(errMsg)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// RegisterCallback sets the single Go callback relay function
|
|
|
|
|
func RegisterCallback(handle ReliabilityManagerHandle, callbacks Callbacks) error {
|
2025-03-30 23:19:15 +05:30
|
|
|
if handle == nil {
|
|
|
|
|
return errors.New("handle is nil")
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// Store the Go callbacks associated with this handle
|
2025-03-30 23:19:15 +05:30
|
|
|
registryMutex.Lock()
|
|
|
|
|
callbackRegistry[handle] = &callbacks
|
|
|
|
|
registryMutex.Unlock()
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// Register the single global Go relay function with the Nim library
|
|
|
|
|
// Nim will call globalCallbackRelay, passing the handle as the first argument.
|
|
|
|
|
C.RegisterCallback(
|
2025-03-30 23:19:15 +05:30
|
|
|
unsafe.Pointer(handle),
|
2025-04-07 17:19:44 +05:30
|
|
|
(C.CEventCallback)(C.globalCallbackRelay), // Pass the Go relay function pointer
|
|
|
|
|
nil, // user_data is not used here, handle is passed directly by Nim wrapper
|
2025-03-30 23:19:15 +05:30
|
|
|
)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StartPeriodicTasks starts the background tasks in the Nim library
|
|
|
|
|
func StartPeriodicTasks(handle ReliabilityManagerHandle) error {
|
|
|
|
|
if handle == nil {
|
|
|
|
|
return errors.New("handle is nil")
|
|
|
|
|
}
|
|
|
|
|
C.StartPeriodicTasks(unsafe.Pointer(handle))
|
|
|
|
|
// Assuming StartPeriodicTasks doesn't return an error status in C API
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// globalCallbackRelay is called by Nim for all events.
|
|
|
|
|
// It uses the handle to find the correct Go Callbacks struct and dispatch the call.
|
2025-04-08 14:46:27 +03:00
|
|
|
//
|
2025-04-07 17:19:44 +05:30
|
|
|
//export globalCallbackRelay
|
|
|
|
|
func globalCallbackRelay(handle unsafe.Pointer, eventType C.CEventType, data1 unsafe.Pointer, data2 unsafe.Pointer, data3 C.size_t) {
|
|
|
|
|
goHandle := ReliabilityManagerHandle(handle)
|
2025-03-30 23:19:15 +05:30
|
|
|
|
|
|
|
|
registryMutex.RLock()
|
2025-04-07 17:19:44 +05:30
|
|
|
callbacks, ok := callbackRegistry[goHandle]
|
|
|
|
|
registryMutex.RUnlock()
|
2025-03-30 23:19:15 +05:30
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
if !ok || callbacks == nil {
|
|
|
|
|
fmt.Printf("Go: globalCallbackRelay: No callbacks registered for handle %v\n", goHandle) // Uncommented
|
|
|
|
|
return
|
2025-03-30 23:19:15 +05:30
|
|
|
}
|
|
|
|
|
|
2025-04-07 17:19:44 +05:30
|
|
|
// Use a goroutine to avoid blocking the Nim thread
|
|
|
|
|
go func() {
|
|
|
|
|
switch eventType {
|
|
|
|
|
case C.EVENT_MESSAGE_READY:
|
|
|
|
|
if callbacks.OnMessageReady != nil {
|
|
|
|
|
msgIdStr := C.GoString((*C.char)(data1))
|
|
|
|
|
callbacks.OnMessageReady(MessageID(msgIdStr))
|
|
|
|
|
}
|
|
|
|
|
case C.EVENT_MESSAGE_SENT:
|
|
|
|
|
if callbacks.OnMessageSent != nil {
|
|
|
|
|
msgIdStr := C.GoString((*C.char)(data1))
|
|
|
|
|
callbacks.OnMessageSent(MessageID(msgIdStr))
|
|
|
|
|
}
|
|
|
|
|
case C.EVENT_MISSING_DEPENDENCIES:
|
|
|
|
|
if callbacks.OnMissingDependencies != nil {
|
|
|
|
|
msgIdStr := C.GoString((*C.char)(data1))
|
|
|
|
|
depsCount := int(data3)
|
|
|
|
|
deps := make([]MessageID, depsCount)
|
|
|
|
|
if depsCount > 0 {
|
|
|
|
|
// Convert C array of C strings (**char) to Go slice
|
|
|
|
|
cDepsArray := (*[1 << 30]*C.char)(data2)[:depsCount:depsCount]
|
|
|
|
|
for i, s := range cDepsArray {
|
|
|
|
|
deps[i] = MessageID(C.GoString(s))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
callbacks.OnMissingDependencies(MessageID(msgIdStr), deps)
|
|
|
|
|
}
|
|
|
|
|
case C.EVENT_PERIODIC_SYNC:
|
|
|
|
|
if callbacks.OnPeriodicSync != nil {
|
|
|
|
|
callbacks.OnPeriodicSync()
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
fmt.Printf("Go: globalCallbackRelay: Received unknown event type %d for handle %v\n", eventType, goHandle)
|
2025-03-30 23:19:15 +05:30
|
|
|
}
|
2025-04-07 17:19:44 +05:30
|
|
|
}()
|
2025-03-30 23:19:15 +05:30
|
|
|
}
|