#include <whiteout/interfaces.h>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "whiteout_c_common.h"
namespace {
using whiteout::u8;
using whiteout::interfaces::DirectoryEntry;
using whiteout::interfaces::VirtualPathFileSystem;
struct HostVfsFnTable {
void (*readFile) (void* userdata, const char* path, std::size_t path_len,
std::uint8_t** out_data, std::size_t* out_size);
void (*freeBuffer) (std::uint8_t* data);
std::int32_t (*writeFile) (void* userdata, const char* path, std::size_t path_len,
const std::uint8_t* data, std::size_t data_size);
std::int32_t (*fileExists) (void* userdata, const char* path, std::size_t path_len);
};
class HostVirtualPathFileSystem : public VirtualPathFileSystem {
public:
HostVirtualPathFileSystem(void* userdata, const HostVfsFnTable* fns)
: userdata_(userdata), fns_(*fns) {}
std::vector<u8> readFile(const std::string& path) const override {
std::uint8_t* data = nullptr;
std::size_t size = 0;
fns_.readFile(userdata_, path.data(), path.size(), &data, &size);
if (data == nullptr || size == 0) {
return {};
}
std::vector<u8> result(data, data + size);
fns_.freeBuffer(data);
return result;
}
bool writeFile(const std::string& path, const std::vector<u8>& data) override {
return fns_.writeFile(userdata_, path.data(), path.size(),
data.data(), data.size()) != 0;
}
bool fileExists(const std::string& path) const override {
return fns_.fileExists(userdata_, path.data(), path.size()) != 0;
}
std::vector<DirectoryEntry> listDirectory(const std::string&) const override {
return {};
}
private:
void* userdata_;
HostVfsFnTable fns_;
};
}
using whiteout::u32;
using whiteout::u64;
using whiteout::i32;
using whiteout::interfaces::HttpCallback;
using whiteout::interfaces::HttpHandler;
using whiteout::interfaces::HttpResponse;
namespace {
struct HostHttpFnTable {
std::uint32_t (*capabilities) (void* userdata);
void (*getAsync) (void* userdata, const char* url, std::size_t url_len,
void* callback_handle);
void (*getRangeAsync) (void* userdata, const char* url, std::size_t url_len,
std::uint64_t start, std::uint64_t end,
void* callback_handle);
};
class HostHttpHandler : public HttpHandler {
public:
HostHttpHandler(void* userdata, const HostHttpFnTable* fns)
: userdata_(userdata), fns_(*fns) {}
u32 capabilities() const noexcept override {
return fns_.capabilities(userdata_);
}
void getAsync(const std::string& url, HttpCallback callback) override {
auto* cb = new HttpCallback(std::move(callback));
fns_.getAsync(userdata_, url.data(), url.size(), cb);
}
void getRangeAsync(const std::string& url, u64 start, u64 end,
HttpCallback callback) override {
auto* cb = new HttpCallback(std::move(callback));
fns_.getRangeAsync(userdata_, url.data(), url.size(), start, end, cb);
}
private:
void* userdata_;
HostHttpFnTable fns_;
};
}
extern "C" {
void* whiteout_hostimpl_VirtualPathFileSystem_create(void* userdata,
const HostVfsFnTable* fns) {
return new HostVirtualPathFileSystem(userdata, fns);
}
void whiteout_hostimpl_VirtualPathFileSystem_delete(void* handle) {
delete reinterpret_cast<HostVirtualPathFileSystem*>(handle);
}
void* whiteout_hostimpl_HttpHandler_create(void* userdata, const HostHttpFnTable* fns) {
return new HostHttpHandler(userdata, fns);
}
void whiteout_hostimpl_HttpHandler_delete(void* handle) {
delete reinterpret_cast<HostHttpHandler*>(handle);
}
void whiteout_hostimpl_HttpResponseCallback_fire(
void* callback_handle, std::int32_t status_code,
const std::uint8_t* body, std::size_t body_len,
const char* error) {
auto* cb = reinterpret_cast<HttpCallback*>(callback_handle);
if (cb == nullptr) return;
HttpResponse response;
response.statusCode = static_cast<i32>(status_code);
if (body != nullptr && body_len > 0) {
response.body.assign(body, body + body_len);
}
if (error != nullptr) {
response.error = error;
}
(*cb)(std::move(response));
delete cb;
}
void whiteout_hostimpl_HttpResponseCallback_cancel(void* callback_handle) {
auto* cb = reinterpret_cast<HttpCallback*>(callback_handle);
if (cb == nullptr) return;
HttpResponse response;
response.statusCode = 0;
response.error = "callback cancelled (managed-side exception)";
(*cb)(std::move(response));
delete cb;
}
whiteout_Bytes whiteout_hostimpl_test_VirtualPathFileSystem_readFile(
void* handle, const char* path) {
auto* vfs = reinterpret_cast<VirtualPathFileSystem*>(handle);
auto data = vfs->readFile(std::string(path));
if (data.empty()) {
return whiteout_Bytes{nullptr, 0, nullptr};
}
auto* owned = new std::vector<u8>(std::move(data));
return whiteout_Bytes{owned->data(), owned->size(), owned};
}
std::int32_t whiteout_hostimpl_test_VirtualPathFileSystem_fileExists(
void* handle, const char* path) {
auto* vfs = reinterpret_cast<VirtualPathFileSystem*>(handle);
return vfs->fileExists(std::string(path)) ? 1 : 0;
}
std::int32_t whiteout_hostimpl_test_VirtualPathFileSystem_writeFile(
void* handle, const char* path, const std::uint8_t* data, std::size_t size) {
auto* vfs = reinterpret_cast<VirtualPathFileSystem*>(handle);
std::vector<u8> v(data, data + size);
return vfs->writeFile(std::string(path), v) ? 1 : 0;
}
std::uint32_t whiteout_hostimpl_test_HttpHandler_capabilities(void* handle) {
auto* h = reinterpret_cast<HttpHandler*>(handle);
return static_cast<std::uint32_t>(h->capabilities());
}
namespace {
struct CapturedHttpResponse {
std::int32_t statusCode = 0;
std::vector<u8> body;
std::string error;
};
void pack_response(const CapturedHttpResponse& captured,
std::int32_t* out_status,
whiteout_Bytes* out_body,
whiteout_CString* out_error) {
*out_status = captured.statusCode;
if (!captured.body.empty()) {
auto* ownedBody = new std::vector<u8>(captured.body);
out_body->data = ownedBody->data();
out_body->size = ownedBody->size();
out_body->_owner = ownedBody;
} else {
out_body->data = nullptr;
out_body->size = 0;
out_body->_owner = nullptr;
}
if (!captured.error.empty()) {
auto* ownedErr = new std::string(captured.error);
out_error->chars = ownedErr->c_str();
out_error->length = ownedErr->size();
out_error->_owner = ownedErr;
} else {
out_error->chars = nullptr;
out_error->length = 0;
out_error->_owner = nullptr;
}
}
}
void whiteout_hostimpl_test_HttpHandler_getAsync(
void* handle, const char* url,
std::int32_t* out_status, whiteout_Bytes* out_body, whiteout_CString* out_error) {
auto* h = reinterpret_cast<HttpHandler*>(handle);
CapturedHttpResponse captured;
h->getAsync(std::string(url), [&captured](HttpResponse r) {
captured.statusCode = r.statusCode;
captured.body = std::move(r.body);
captured.error = std::move(r.error);
});
pack_response(captured, out_status, out_body, out_error);
}
void whiteout_hostimpl_test_HttpHandler_getRangeAsync(
void* handle, const char* url, std::uint64_t start, std::uint64_t end,
std::int32_t* out_status, whiteout_Bytes* out_body, whiteout_CString* out_error) {
auto* h = reinterpret_cast<HttpHandler*>(handle);
CapturedHttpResponse captured;
h->getRangeAsync(std::string(url), start, end, [&captured](HttpResponse r) {
captured.statusCode = r.statusCode;
captured.body = std::move(r.body);
captured.error = std::move(r.error);
});
pack_response(captured, out_status, out_body, out_error);
}
}
using whiteout::interfaces::TimelineSemaphore;
using whiteout::interfaces::WorkerPool;
using whiteout::interfaces::WorkerTask;
namespace {
struct HostWorkerTaskFlat {
void* fn_handle; void* wait_semaphore; std::uint64_t wait_value;
void* signal_semaphore; std::uint64_t signal_value;
};
struct HostWorkerPoolFnTable {
void (*submit) (void* userdata, const HostWorkerTaskFlat* task);
void (*waitIdle) (void* userdata);
std::size_t (*threadCount) (void* userdata);
};
class HostWorkerPool : public WorkerPool {
public:
HostWorkerPool(void* userdata, const HostWorkerPoolFnTable* fns)
: userdata_(userdata), fns_(*fns) {}
void submit(const WorkerTask& task) override {
auto* fnHeap = new std::function<void()>(task.fn);
HostWorkerTaskFlat flat{
fnHeap,
task.waitSemaphore, task.waitValue,
task.signalSemaphore, task.signalValue,
};
fns_.submit(userdata_, &flat);
}
void waitIdle() override {
fns_.waitIdle(userdata_);
}
std::size_t threadCount() const noexcept override {
return fns_.threadCount(userdata_);
}
private:
void* userdata_;
HostWorkerPoolFnTable fns_;
};
}
extern "C" {
void* whiteout_hostimpl_WorkerPool_create(void* userdata, const HostWorkerPoolFnTable* fns) {
return new HostWorkerPool(userdata, fns);
}
void whiteout_hostimpl_WorkerPool_delete(void* handle) {
delete reinterpret_cast<HostWorkerPool*>(handle);
}
void whiteout_hostimpl_WorkerTaskFn_fire(void* fn_handle) {
auto* fn = reinterpret_cast<std::function<void()>*>(fn_handle);
if (fn == nullptr) return;
(*fn)();
delete fn;
}
void whiteout_hostimpl_WorkerTaskFn_cancel(void* fn_handle) {
auto* fn = reinterpret_cast<std::function<void()>*>(fn_handle);
if (fn == nullptr) return;
delete fn;
}
void whiteout_hostimpl_TimelineSemaphore_await(void* sem_handle, std::uint64_t value) {
auto* sem = reinterpret_cast<TimelineSemaphore*>(sem_handle);
if (sem == nullptr) return;
sem->wait(value);
}
void whiteout_hostimpl_TimelineSemaphore_signal(void* sem_handle, std::uint64_t value) {
auto* sem = reinterpret_cast<TimelineSemaphore*>(sem_handle);
if (sem == nullptr) return;
sem->signal(value);
}
std::size_t whiteout_hostimpl_test_WorkerPool_threadCount(void* handle) {
auto* pool = reinterpret_cast<WorkerPool*>(handle);
return pool->threadCount();
}
void whiteout_hostimpl_test_WorkerPool_waitIdle(void* handle) {
auto* pool = reinterpret_cast<WorkerPool*>(handle);
pool->waitIdle();
}
void whiteout_hostimpl_test_WorkerPool_submitIncrementSentinel(
void* handle, std::int32_t* out_sentinel) {
auto* pool = reinterpret_cast<WorkerPool*>(handle);
WorkerTask task;
task.fn = [out_sentinel]() {
if (out_sentinel != nullptr) {
(*out_sentinel)++;
}
};
pool->submit(task);
}
}
using whiteout::interfaces::CascFileSystem;
namespace {
struct HostCascFsFnTable {
void (*readFile)(void* userdata, std::uint32_t fileId,
std::uint8_t** out_data, std::size_t* out_size);
void (*freeBuffer)(std::uint8_t* data);
std::int32_t (*reserveFileId)(void* userdata, const char* path,
std::size_t path_len, std::uint32_t* out_id);
std::int32_t (*writeFile)(void* userdata, std::uint32_t fileId,
const std::uint8_t* data, std::size_t size);
std::int32_t (*fileExists)(void* userdata, std::uint32_t fileId);
};
class HostCascFileSystem : public CascFileSystem {
public:
HostCascFileSystem(void* userdata, const HostCascFsFnTable* fns)
: userdata_(userdata), fns_(*fns) {}
std::vector<u8> readFile(u32 fileId) const override {
std::uint8_t* data = nullptr;
std::size_t size = 0;
fns_.readFile(userdata_, fileId, &data, &size);
if (data == nullptr || size == 0) return {};
std::vector<u8> result(data, data + size);
fns_.freeBuffer(data);
return result;
}
std::optional<u32> reserveFileId(const std::string& path) override {
std::uint32_t id = 0;
if (fns_.reserveFileId(userdata_, path.data(), path.size(), &id) == 0) {
return std::nullopt;
}
return id;
}
bool writeFile(u32 fileId, const std::vector<u8>& data) override {
return fns_.writeFile(userdata_, fileId, data.data(), data.size()) != 0;
}
bool fileExists(u32 fileId) const override {
return fns_.fileExists(userdata_, fileId) != 0;
}
private:
void* userdata_;
HostCascFsFnTable fns_;
};
}
extern "C" {
void* whiteout_hostimpl_CascFileSystem_create(void* userdata,
const HostCascFsFnTable* fns) {
return new HostCascFileSystem(userdata, fns);
}
void whiteout_hostimpl_CascFileSystem_delete(void* handle) {
delete reinterpret_cast<HostCascFileSystem*>(handle);
}
whiteout_Bytes whiteout_hostimpl_test_CascFileSystem_readFile(void* handle,
std::uint32_t fileId) {
auto* fs = reinterpret_cast<CascFileSystem*>(handle);
auto data = fs->readFile(fileId);
if (data.empty()) return whiteout_Bytes{nullptr, 0, nullptr};
auto* owned = new std::vector<u8>(std::move(data));
return whiteout_Bytes{owned->data(), owned->size(), owned};
}
std::int32_t whiteout_hostimpl_test_CascFileSystem_fileExists(void* handle,
std::uint32_t fileId) {
return reinterpret_cast<CascFileSystem*>(handle)->fileExists(fileId) ? 1 : 0;
}
}
extern "C" {
void* whiteout_csharp_VirtualPathFileSystem_create(void* userdata, const void* fns) {
return whiteout_hostimpl_VirtualPathFileSystem_create(
userdata, reinterpret_cast<const HostVfsFnTable*>(fns));
}
void whiteout_csharp_VirtualPathFileSystem_delete(void* handle) {
whiteout_hostimpl_VirtualPathFileSystem_delete(handle);
}
void* whiteout_csharp_HttpHandler_create(void* userdata, const void* fns) {
return whiteout_hostimpl_HttpHandler_create(
userdata, reinterpret_cast<const HostHttpFnTable*>(fns));
}
void whiteout_csharp_HttpHandler_delete(void* handle) {
whiteout_hostimpl_HttpHandler_delete(handle);
}
void whiteout_csharp_HttpResponseCallback_fire(void* cb, std::int32_t status,
const std::uint8_t* body,
std::size_t body_len,
const char* error) {
whiteout_hostimpl_HttpResponseCallback_fire(cb, status, body, body_len, error);
}
void whiteout_csharp_HttpResponseCallback_cancel(void* cb) {
whiteout_hostimpl_HttpResponseCallback_cancel(cb);
}
void* whiteout_csharp_WorkerPool_create(void* userdata, const void* fns) {
return whiteout_hostimpl_WorkerPool_create(
userdata, reinterpret_cast<const HostWorkerPoolFnTable*>(fns));
}
void whiteout_csharp_WorkerPool_delete(void* handle) {
whiteout_hostimpl_WorkerPool_delete(handle);
}
void whiteout_csharp_WorkerTaskFn_fire(void* fn) { whiteout_hostimpl_WorkerTaskFn_fire(fn); }
void whiteout_csharp_WorkerTaskFn_cancel(void* fn) { whiteout_hostimpl_WorkerTaskFn_cancel(fn); }
void whiteout_csharp_TimelineSemaphore_await(void* sem, std::uint64_t v) {
whiteout_hostimpl_TimelineSemaphore_await(sem, v);
}
void whiteout_csharp_TimelineSemaphore_signal(void* sem, std::uint64_t v) {
whiteout_hostimpl_TimelineSemaphore_signal(sem, v);
}
whiteout_Bytes whiteout_csharp_test_VirtualPathFileSystem_readFile(void* h, const char* p) {
return whiteout_hostimpl_test_VirtualPathFileSystem_readFile(h, p);
}
std::int32_t whiteout_csharp_test_VirtualPathFileSystem_fileExists(void* h, const char* p) {
return whiteout_hostimpl_test_VirtualPathFileSystem_fileExists(h, p);
}
std::int32_t whiteout_csharp_test_VirtualPathFileSystem_writeFile(
void* h, const char* p, const std::uint8_t* d, std::size_t n) {
return whiteout_hostimpl_test_VirtualPathFileSystem_writeFile(h, p, d, n);
}
std::uint32_t whiteout_csharp_test_HttpHandler_capabilities(void* h) {
return whiteout_hostimpl_test_HttpHandler_capabilities(h);
}
void whiteout_csharp_test_HttpHandler_getAsync(
void* h, const char* url, std::int32_t* s, whiteout_Bytes* b, whiteout_CString* e) {
whiteout_hostimpl_test_HttpHandler_getAsync(h, url, s, b, e);
}
void whiteout_csharp_test_HttpHandler_getRangeAsync(
void* h, const char* url, std::uint64_t start, std::uint64_t end,
std::int32_t* s, whiteout_Bytes* b, whiteout_CString* e) {
whiteout_hostimpl_test_HttpHandler_getRangeAsync(h, url, start, end, s, b, e);
}
std::size_t whiteout_csharp_test_WorkerPool_threadCount(void* h) {
return whiteout_hostimpl_test_WorkerPool_threadCount(h);
}
void whiteout_csharp_test_WorkerPool_waitIdle(void* h) {
whiteout_hostimpl_test_WorkerPool_waitIdle(h);
}
void whiteout_csharp_test_WorkerPool_submitIncrementSentinel(void* h, std::int32_t* out) {
whiteout_hostimpl_test_WorkerPool_submitIncrementSentinel(h, out);
}
}