From bce0671c7bf011a2c44d04a3e0706849c8029516 Mon Sep 17 00:00:00 2001 From: shash256 <111925100+shash256@users.noreply.github.com> Date: Thu, 13 Mar 2025 22:58:55 +0530 Subject: [PATCH] feat: add bindings script --- .gitignore | 5 +- bindings/bindings.nim | 242 ++++++++++++++++++++++++++++++++++++++++++ reliability.nimble | 19 +++- 3 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 bindings/bindings.nim diff --git a/.gitignore b/.gitignore index 82f3c3e..08a21fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ - .DS_Store tests/test_reliability +tests/bloom +nph +bindings/generated* +do_not_commit diff --git a/bindings/bindings.nim b/bindings/bindings.nim new file mode 100644 index 0000000..3e1eba1 --- /dev/null +++ b/bindings/bindings.nim @@ -0,0 +1,242 @@ +import genny +import std/[times, strutils] +import results +import ../src/[reliability, message, reliability_utils, rolling_bloom_filter] + +# Define required sequence wrapper types for C FFI +type + SeqByte* = ref object + s*: seq[byte] + + SeqMessageID* = ref object + s*: seq[MessageID] + + SeqMessage* = ref object + s*: seq[Message] + + SeqUnacknowledgedMessage* = ref object + s*: seq[UnacknowledgedMessage] + +# Error handling +var lastError: ReliabilityError + +proc takeError(): string = + result = $lastError + lastError = ReliabilityError.reInternalError # Reset to default + +proc checkError(): bool = + result = lastError != ReliabilityError.reInternalError + +# Callback function types for C FFI +type + CMessageReadyCallback* = proc(messageId: cstring) {.cdecl, gcsafe.} + CMessageSentCallback* = proc(messageId: cstring) {.cdecl, gcsafe.} + CMissingDepsCallback* = proc(messageId: cstring, missingDeps: cstring, count: cint) {.cdecl, gcsafe.} + CPeriodicSyncCallback* = proc() {.cdecl, gcsafe.} + +# Global callback storage +var + onMessageReadyCallback: CMessageReadyCallback + onMessageSentCallback: CMessageSentCallback + onMissingDepsCallback: CMissingDepsCallback + onPeriodicSyncCallback: CPeriodicSyncCallback + +# Register callbacks +proc registerMessageReadyCallback*(callback: CMessageReadyCallback) = + onMessageReadyCallback = callback + +proc registerMessageSentCallback*(callback: CMessageSentCallback) = + onMessageSentCallback = callback + +proc registerMissingDepsCallback*(callback: CMissingDepsCallback) = + onMissingDepsCallback = callback + +proc registerPeriodicSyncCallback*(callback: CPeriodicSyncCallback) = + onPeriodicSyncCallback = callback + +# Individual adapter functions +proc onMessageReadyAdapter(messageId: MessageID) {.gcsafe, raises: [].} = + if onMessageReadyCallback != nil: + try: + onMessageReadyCallback(cstring(messageId)) + except: + discard # Ignore exceptions + +proc onMessageSentAdapter(messageId: MessageID) {.gcsafe, raises: [].} = + if onMessageSentCallback != nil: + try: + onMessageSentCallback(cstring(messageId)) + except: + discard + +proc onMissingDependenciesAdapter(messageId: MessageID, missingDeps: seq[MessageID]) {.gcsafe, raises: [].} = + if onMissingDepsCallback != nil and missingDeps.len > 0: + try: + let joinedDeps = missingDeps.join(",") + onMissingDepsCallback(cstring(messageId), cstring(joinedDeps), cint(missingDeps.len)) + except: + discard + +proc onPeriodicSyncAdapter() {.gcsafe, raises: [].} = + if onPeriodicSyncCallback != nil: + try: + onPeriodicSyncCallback() + except: + discard + +# Apply registered callbacks to a ReliabilityManager +proc applyCallbacks*(rm: ReliabilityManager): bool = + if rm == nil: + lastError = ReliabilityError.reInvalidArgument + return false + + try: + rm.setCallbacks( + onMessageReadyAdapter, + onMessageSentAdapter, + onMissingDependenciesAdapter, + onPeriodicSyncAdapter + ) + return true + except: + lastError = ReliabilityError.reInternalError + return false + +# Wrapper for creating a ReliabilityManager +proc safeNewReliabilityManager(channelId: string, config: ReliabilityConfig = defaultConfig()): ReliabilityManager = + let res = newReliabilityManager(channelId, config) + if res.isOk: + return res.get + else: + lastError = res.error + return nil + +# Wrapper for wrapping outgoing messages +proc safeWrapOutgoingMessage(rm: ReliabilityManager, message: seq[byte], messageId: MessageID): seq[byte] = + if rm == nil: + lastError = ReliabilityError.reInvalidArgument + return @[] + + let res = rm.wrapOutgoingMessage(message, messageId) + if res.isOk: + return res.get + else: + lastError = res.error + return @[] + +# Wrapper for unwrapping received messages +proc safeUnwrapReceivedMessage(rm: ReliabilityManager, message: seq[byte]): tuple[message: seq[byte], missingDeps: seq[MessageID]] = + if rm == nil: + lastError = ReliabilityError.reInvalidArgument + return (@[], @[]) + + let res = rm.unwrapReceivedMessage(message) + if res.isOk: + return res.get + else: + lastError = res.error + return (@[], @[]) + +# Wrapper for marking dependencies as met +proc safeMarkDependenciesMet(rm: ReliabilityManager, messageIds: seq[MessageID]): bool = + if rm == nil: + lastError = ReliabilityError.reInvalidArgument + return false + + let res = rm.markDependenciesMet(messageIds) + if res.isOk: + return true + else: + lastError = res.error + return false + +# Helper to create a Duration from milliseconds +proc durationFromMs(ms: int64): Duration = + initDuration(milliseconds = ms) + +# Wrapper for creating a ReliabilityConfig with Duration values in milliseconds +proc configFromMs( + bloomFilterCapacity: int = DefaultBloomFilterCapacity, + bloomFilterErrorRate: float = DefaultBloomFilterErrorRate, + bloomFilterWindowMs: int64 = 3600000, # 1 hour default + maxMessageHistory: int = DefaultMaxMessageHistory, + maxCausalHistory: int = DefaultMaxCausalHistory, + resendIntervalMs: int64 = 60000, # 1 minute default + maxResendAttempts: int = DefaultMaxResendAttempts, + syncMessageIntervalMs: int64 = 30000, # 30 seconds default + bufferSweepIntervalMs: int64 = 60000 # 1 minute default +): ReliabilityConfig = + var config = ReliabilityConfig( + bloomFilterCapacity: bloomFilterCapacity, + bloomFilterErrorRate: bloomFilterErrorRate, + bloomFilterWindow: durationFromMs(bloomFilterWindowMs), + maxMessageHistory: maxMessageHistory, + maxCausalHistory: maxCausalHistory, + resendInterval: durationFromMs(resendIntervalMs), + maxResendAttempts: maxResendAttempts, + syncMessageInterval: durationFromMs(syncMessageIntervalMs), + bufferSweepInterval: durationFromMs(bufferSweepIntervalMs) + ) + return config + +# Helper to parse comma-separated string into seq[MessageID] +proc parseMessageIDs*(commaSeparated: string): seq[MessageID] = + if commaSeparated.len == 0: + return @[] + return commaSeparated.split(',') + +# Constants +exportConsts: + DefaultBloomFilterCapacity + DefaultBloomFilterErrorRate + DefaultMaxMessageHistory + DefaultMaxCausalHistory + DefaultMaxResendAttempts + MaxMessageSize + +# Enums +exportEnums: + ReliabilityError + +# Helper procs +exportProcs: + checkError + takeError + configFromMs + durationFromMs + parseMessageIDs + registerMessageReadyCallback + registerMessageSentCallback + registerMissingDepsCallback + registerPeriodicSyncCallback + applyCallbacks + +# Core objects +exportObject ReliabilityConfig: + constructor: + configFromMs(int, float, int64, int, int, int64, int, int64, int64) + +# Main ref object +exportRefObject ReliabilityManager: + constructor: + safeNewReliabilityManager(string, ReliabilityConfig) + procs: + safeWrapOutgoingMessage(ReliabilityManager, seq[byte], MessageID) + safeUnwrapReceivedMessage(ReliabilityManager, seq[byte]) + safeMarkDependenciesMet(ReliabilityManager, seq[MessageID]) + checkUnacknowledgedMessages(ReliabilityManager) + startPeriodicTasks(ReliabilityManager) + cleanup(ReliabilityManager) + getMessageHistory(ReliabilityManager) + getOutgoingBuffer(ReliabilityManager) + getIncomingBuffer(ReliabilityManager) + +# Sequences +exportSeq seq[byte]: + discard + +exportSeq seq[MessageID]: + discard + +# Finally generate the files +writeFiles("bindings/generated", "sds_bindings") \ No newline at end of file diff --git a/reliability.nimble b/reliability.nimble index 0be9270..8c60032 100644 --- a/reliability.nimble +++ b/reliability.nimble @@ -9,8 +9,25 @@ srcDir = "src" requires "nim >= 2.0.8" requires "chronicles" requires "libp2p" +requires "genny >= 0.1.0" # Tasks task test, "Run the test suite": exec "nim c -r tests/test_bloom.nim" - exec "nim c -r tests/test_reliability.nim" \ No newline at end of file + exec "nim c -r tests/test_reliability.nim" + +task bindings, "Generate bindings": + proc compile(libName: string, flags = "") = + exec "nim c -f " & flags & " -d:release --app:lib --mm:arc --tlsEmulation:off --out:" & libName & " --outdir:bindings/generated bindings/bindings.nim" + + # Create required directories + mkDir "bindings/generated" + + when defined(windows): + compile "reliability.dll" + elif defined(macosx): + compile "libsds.dylib.arm", "--cpu:arm64 -l:'-target arm64-apple-macos11' -t:'-target arm64-apple-macos11'" + compile "libsds.dylib.x64", "--cpu:amd64 -l:'-target x86_64-apple-macos10.12' -t:'-target x86_64-apple-macos10.12'" + exec "lipo bindings/generated/libsds.dylib.arm bindings/generated/libsds.dylib.x64 -output bindings/generated/libsds.dylib -create" + else: + compile "libsds.so" \ No newline at end of file