logos-storage-nim/library/libcodex.nim

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

566 lines
17 KiB
Nim
Raw Normal View History

2025-09-09 20:30:03 +02:00
# libcodex.nim - C-exported interface for the Codex shared library
#
# This file implements the public C API for libcodex.
# It acts as the bridge between C programs and the internal Nim implementation.
#
# This file defines:
# - Initialization logic for the Nim runtime (once per process)
# - Thread-safe exported procs callable from C
# - Callback registration and invocation for asynchronous communication
# cdecl is C declaration calling convention.
# Its the standard way C compilers expect functions to behave:
# 1- Caller cleans up the stack after the call
# 2- Symbol names are exported in a predictable way
# In other termes, it is a glue that makes Nim functions callable as normal C functions.
{.pragma: exported, exportc, cdecl, raises: [].}
{.pragma: callback, cdecl, raises: [], gcsafe.}
# Ensure code is position-independent so it can be built into a shared library (.so).
# In other terms, the code that can run no matter where its placed in memory.
{.passc: "-fPIC".}
when defined(linux):
# Define the canonical name for this library
{.passl: "-Wl,-soname,libcodex.so".}
import std/[atomics]
import chronicles
import chronos
import chronos/threadsync
2025-09-09 20:30:03 +02:00
import ./codex_context
import ./codex_thread_requests/codex_thread_request
import ./codex_thread_requests/requests/node_lifecycle_request
import ./codex_thread_requests/requests/node_info_request
2025-09-17 07:50:11 +02:00
import ./codex_thread_requests/requests/node_debug_request
2025-09-17 13:25:30 +02:00
import ./codex_thread_requests/requests/node_p2p_request
2025-09-18 09:14:49 +02:00
import ./codex_thread_requests/requests/node_upload_request
import ./codex_thread_requests/requests/node_download_request
2025-10-01 09:16:47 +02:00
import ./codex_thread_requests/requests/node_storage_request
2025-09-09 20:30:03 +02:00
import ./ffi_types
2025-10-23 16:51:16 +02:00
from ../codex/conf import codexVersion
2025-09-12 12:18:17 +02:00
logScope:
topics = "codexlib"
2025-09-09 20:30:03 +02:00
template checkLibcodexParams*(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
) =
if not isNil(ctx):
ctx[].userData = userData
if isNil(callback):
return RET_MISSING_CALLBACK
# From Nim doc:
# "the C targets require you to initialize Nim's internals, which is done calling a NimMain function."
# "The name NimMain can be influenced via the --nimMainPrefix:prefix switch."
# "Use --nimMainPrefix:MyLib and the function to call is named MyLibNimMain."
proc libcodexNimMain() {.importc.}
# Atomic flag to prevent multiple initializations
var initialized: Atomic[bool]
if defined(android):
# Redirect chronicles to Android System logs
when compiles(defaultChroniclesStream.outputs[0].writer):
defaultChroniclesStream.outputs[0].writer = proc(
logLevel: LogLevel, msg: LogOutputStr
) {.raises: [].} =
echo logLevel, msg
# Initializes the Nim runtime and foreign-thread GC
proc initializeLibrary() {.exported.} =
if not initialized.exchange(true):
## Every Nim library must call `<prefix>NimMain()` once
libcodexNimMain()
when declared(setupForeignThreadGc):
setupForeignThreadGc()
when declared(nimGC_setStackBottom):
var locals {.volatile, noinit.}: pointer
locals = addr(locals)
nimGC_setStackBottom(locals)
proc codex_new(
configJson: cstring, callback: CodexCallback, userData: pointer
): pointer {.dynlib, exported.} =
2025-09-09 20:30:03 +02:00
initializeLibrary()
if isNil(callback):
2025-09-25 09:06:25 +02:00
error "Failed to create codex instance: the callback is missing."
2025-09-09 20:30:03 +02:00
return nil
var ctx = codex_context.createCodexContext().valueOr:
2025-09-25 09:06:25 +02:00
let msg = $error
2025-09-09 20:30:03 +02:00
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
return nil
ctx.userData = userData
let reqContent =
NodeLifecycleRequest.createShared(NodeLifecycleMsgType.CREATE_NODE, configJson)
codex_context.sendRequestToCodexThread(
ctx, RequestType.LIFECYCLE, reqContent, callback, userData
).isOkOr:
2025-09-25 09:06:25 +02:00
let msg = $error
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
2025-09-09 20:30:03 +02:00
return nil
return ctx
2025-09-12 12:18:17 +02:00
proc codex_version(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-12 12:18:17 +02:00
callback(
RET_OK,
cast[ptr cchar](conf.codexVersion),
cast[csize_t](len(conf.codexVersion)),
userData,
)
return RET_OK
2025-09-12 12:18:17 +02:00
2025-09-12 12:22:16 +02:00
proc codex_revision(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
callback(
RET_OK,
cast[ptr cchar](conf.codexRevision),
cast[csize_t](len(conf.codexRevision)),
userData,
)
return RET_OK
proc codex_repo(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
2025-09-12 12:22:16 +02:00
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let reqContent = NodeInfoRequest.createShared(NodeInfoMsgType.REPO)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.INFO, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-16 15:45:11 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-16 15:45:11 +02:00
proc codex_debug(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-16 15:45:11 +02:00
2025-09-17 07:50:11 +02:00
let reqContent = NodeDebugRequest.createShared(NodeDebugMsgType.DEBUG)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-17 07:50:11 +02:00
ctx, RequestType.DEBUG, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-16 16:10:57 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-16 16:10:57 +02:00
proc codex_spr(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-16 16:10:57 +02:00
let reqContent = NodeInfoRequest.createShared(NodeInfoMsgType.SPR)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-16 16:10:57 +02:00
ctx, RequestType.INFO, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-17 07:50:11 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-17 07:50:11 +02:00
proc codex_peer_id(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-17 07:50:11 +02:00
let reqContent = NodeInfoRequest.createShared(NodeInfoMsgType.PEERID)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-17 07:50:11 +02:00
ctx, RequestType.INFO, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-12 12:22:16 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-12 12:22:16 +02:00
2025-09-17 09:03:56 +02:00
## Set the log level of the library at runtime.
## It uses updateLogLevel which is a synchronous proc and
## cannot be used inside an async context because of gcsafe issue.
proc codex_log_level(
ctx: ptr CodexContext, logLevel: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-17 09:03:56 +02:00
2025-10-23 16:51:16 +02:00
let reqContent =
NodeDebugRequest.createShared(NodeDebugMsgType.LOG_LEVEL, logLevel = logLevel)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.DEBUG, reqContent, callback, userData
)
2025-09-17 09:03:56 +02:00
2025-10-23 16:51:16 +02:00
return callback.okOrError(res, userData)
2025-09-17 09:03:56 +02:00
2025-09-17 13:25:30 +02:00
proc codex_connect(
ctx: ptr CodexContext,
peerId: cstring,
peerAddressesPtr: ptr cstring,
peerAddressesLength: csize_t,
callback: CodexCallback,
userData: pointer,
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-17 13:25:30 +02:00
var peerAddresses = newSeq[cstring](peerAddressesLength)
let peers = cast[ptr UncheckedArray[cstring]](peerAddressesPtr)
for i in 0 ..< peerAddressesLength:
peerAddresses[i] = peers[i]
let reqContent = NodeP2PRequest.createShared(
NodeP2PMsgType.CONNECT, peerId = peerId, peerAddresses = peerAddresses
)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-17 13:25:30 +02:00
ctx, RequestType.P2P, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-17 14:37:34 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-17 14:37:34 +02:00
proc codex_peer_debug(
ctx: ptr CodexContext, peerId: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-17 14:37:34 +02:00
let reqContent = NodeDebugRequest.createShared(NodeDebugMsgType.PEER, peerId = peerId)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-17 14:37:34 +02:00
ctx, RequestType.DEBUG, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-17 13:25:30 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-17 13:25:30 +02:00
proc codex_close(
2025-09-12 12:18:17 +02:00
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
2025-09-09 20:30:03 +02:00
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-09 20:30:03 +02:00
let reqContent = NodeLifecycleRequest.createShared(NodeLifecycleMsgType.CLOSE_NODE)
var res = codex_context.sendRequestToCodexThread(
ctx, RequestType.LIFECYCLE, reqContent, callback, userData
)
2025-09-18 11:36:31 +02:00
if res.isErr:
return callback.error(res.error, userData)
2025-09-09 20:30:03 +02:00
return callback.okOrError(res, userData)
proc codex_destroy(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let res = codex_context.destroyCodexContext(ctx)
if res.isErr:
return RET_ERR
return RET_OK
2025-09-09 20:30:03 +02:00
2025-09-18 09:14:49 +02:00
proc codex_upload_init(
2025-09-22 15:33:15 +02:00
ctx: ptr CodexContext,
filepath: cstring,
chunkSize: csize_t,
callback: CodexCallback,
userData: pointer,
2025-09-18 09:14:49 +02:00
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-22 15:33:15 +02:00
let reqContent = NodeUploadRequest.createShared(
NodeUploadMsgType.INIT, filepath = filepath, chunkSize = chunkSize
2025-09-22 15:33:15 +02:00
)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-18 09:14:49 +02:00
ctx, RequestType.UPLOAD, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-18 09:14:49 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-18 09:14:49 +02:00
proc codex_upload_chunk(
ctx: ptr CodexContext,
sessionId: cstring,
data: ptr byte,
2025-09-19 15:05:03 +02:00
len: csize_t,
2025-09-18 09:14:49 +02:00
callback: CodexCallback,
userData: pointer,
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-18 09:14:49 +02:00
let chunk = newSeq[byte](len)
copyMem(addr chunk[0], data, len)
let reqContent = NodeUploadRequest.createShared(
NodeUploadMsgType.CHUNK, sessionId = sessionId, chunk = chunk
)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-18 09:14:49 +02:00
ctx, RequestType.UPLOAD, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-18 09:14:49 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-18 09:14:49 +02:00
proc codex_upload_finalize(
ctx: ptr CodexContext,
sessionId: cstring,
callback: CodexCallback,
userData: pointer,
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-18 09:14:49 +02:00
let reqContent =
NodeUploadRequest.createShared(NodeUploadMsgType.FINALIZE, sessionId = sessionId)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-18 09:14:49 +02:00
ctx, RequestType.UPLOAD, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-18 09:14:49 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-18 09:14:49 +02:00
proc codex_upload_cancel(
ctx: ptr CodexContext,
sessionId: cstring,
callback: CodexCallback,
userData: pointer,
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-18 09:14:49 +02:00
let reqContent =
NodeUploadRequest.createShared(NodeUploadMsgType.CANCEL, sessionId = sessionId)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
2025-09-18 09:14:49 +02:00
ctx, RequestType.UPLOAD, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-18 09:14:49 +02:00
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-18 09:14:49 +02:00
2025-09-19 15:05:03 +02:00
proc codex_upload_file(
ctx: ptr CodexContext,
sessionId: cstring,
callback: CodexCallback,
userData: pointer,
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
2025-09-19 15:05:03 +02:00
let reqContent =
NodeUploadRequest.createShared(NodeUploadMsgType.FILE, sessionId = sessionId)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.UPLOAD, reqContent, callback, userData
)
return callback.okOrError(res, userData)
proc codex_download_init(
ctx: ptr CodexContext,
cid: cstring,
chunkSize: csize_t,
local: bool,
callback: CodexCallback,
userData: pointer,
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeDownloadRequest.createShared(
NodeDownloadMsgType.INIT, cid = cid, chunkSize = chunkSize, local = local
)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.DOWNLOAD, req, callback, userData
)
2025-09-19 15:05:03 +02:00
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
proc codex_download_chunk(
ctx: ptr CodexContext, cid: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeDownloadRequest.createShared(NodeDownloadMsgType.CHUNK, cid = cid)
2025-09-19 15:05:03 +02:00
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.DOWNLOAD, req, callback, userData
2025-09-19 15:05:03 +02:00
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
2025-09-30 12:47:44 +02:00
proc codex_download_stream(
ctx: ptr CodexContext,
cid: cstring,
chunkSize: csize_t,
2025-09-30 12:47:44 +02:00
local: bool,
filepath: cstring,
callback: CodexCallback,
userData: pointer,
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeDownloadRequest.createShared(
2025-09-30 12:47:44 +02:00
NodeDownloadMsgType.STREAM,
cid = cid,
chunkSize = chunkSize,
local = local,
filepath = filepath,
)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.DOWNLOAD, req, callback, userData
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
proc codex_download_cancel(
ctx: ptr CodexContext, cid: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeDownloadRequest.createShared(NodeDownloadMsgType.CANCEL, cid = cid)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.DOWNLOAD, req, callback, userData
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
2025-09-19 15:05:03 +02:00
2025-10-01 06:10:54 +02:00
proc codex_download_manifest(
ctx: ptr CodexContext, cid: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeDownloadRequest.createShared(NodeDownloadMsgType.MANIFEST, cid = cid)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.DOWNLOAD, req, callback, userData
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
2025-10-01 06:10:54 +02:00
2025-10-01 09:16:47 +02:00
proc codex_storage_list(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeStorageRequest.createShared(NodeStorageMsgType.LIST)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.STORAGE, req, callback, userData
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
2025-10-01 09:16:47 +02:00
proc codex_storage_space(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeStorageRequest.createShared(NodeStorageMsgType.SPACE)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.STORAGE, req, callback, userData
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
2025-10-01 09:16:47 +02:00
proc codex_storage_delete(
ctx: ptr CodexContext, cid: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeStorageRequest.createShared(NodeStorageMsgType.DELETE, cid = cid)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.STORAGE, req, callback, userData
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
2025-10-01 09:16:47 +02:00
proc codex_storage_fetch(
ctx: ptr CodexContext, cid: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeStorageRequest.createShared(NodeStorageMsgType.FETCH, cid = cid)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.STORAGE, req, callback, userData
)
2025-10-21 07:24:56 +02:00
return callback.okOrError(res, userData)
2025-10-01 09:16:47 +02:00
2025-11-02 16:30:26 +01:00
proc codex_storage_exists(
ctx: ptr CodexContext, cid: cstring, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let req = NodeStorageRequest.createShared(NodeStorageMsgType.EXISTS, cid = cid)
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.STORAGE, req, callback, userData
)
return callback.okOrError(res, userData)
2025-09-09 20:30:03 +02:00
proc codex_start(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let reqContent: ptr NodeLifecycleRequest =
NodeLifecycleRequest.createShared(NodeLifecycleMsgType.START_NODE)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.LIFECYCLE, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-09 20:30:03 +02:00
proc codex_stop(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
): cint {.dynlib, exportc.} =
initializeLibrary()
checkLibcodexParams(ctx, callback, userData)
let reqContent: ptr NodeLifecycleRequest =
NodeLifecycleRequest.createShared(NodeLifecycleMsgType.STOP_NODE)
2025-09-18 11:36:31 +02:00
let res = codex_context.sendRequestToCodexThread(
ctx, RequestType.LIFECYCLE, reqContent, callback, userData
2025-09-18 11:36:31 +02:00
)
2025-09-18 11:36:31 +02:00
return callback.okOrError(res, userData)
2025-09-09 20:30:03 +02:00
proc codex_set_event_callback(
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
) {.dynlib, exportc.} =
initializeLibrary()
ctx[].eventCallback = cast[pointer](callback)
ctx[].eventUserData = userData