/* Copyright (c) 2019-2024 Griefer@Work                                       *
 *                                                                            *
 * This software is provided 'as-is', without any express or implied          *
 * warranty. In no event will the authors be held liable for any damages      *
 * arising from the use of this software.                                     *
 *                                                                            *
 * Permission is granted to anyone to use this software for any purpose,      *
 * including commercial applications, and to alter it and redistribute it     *
 * freely, subject to the following restrictions:                             *
 *                                                                            *
 * 1. The origin of this software must not be misrepresented; you must not    *
 *    claim that you wrote the original software. If you use this software    *
 *    in a product, an acknowledgement (see the following) in the product     *
 *    documentation is required:                                              *
 *    Portions Copyright (c) 2019-2024 Griefer@Work                           *
 * 2. Altered source versions must be plainly marked as such, and must not be *
 *    misrepresented as being the original software.                          *
 * 3. This notice may not be removed or altered from any source distribution. *
 */
#ifndef GUARD_LIBSERVICE_CLIENT_H
#define GUARD_LIBSERVICE_CLIENT_H 1

#include "api.h"

#include <kos/types.h>

#include <libservice/client.h>

DECL_BEGIN

struct service;

/* Client API interface for service. Note that opening the same
 * service more than once will return unique handles each time,
 * and  also establish individual connections to the associated
 * server!
 *
 * @return: * :   Opaque handle for the service. In this case,  the
 *                server is/has/will have created a context for the
 *                current process.
 * @return: NULL: [errno=ENOENT] No function exists with the given name.
 * @return: NULL: [errno=EINTR]  The calling thread was interrupted.
 * @return: NULL: [errno=ENOMEM] Insufficient memory. */
INTDEF WUNUSED NONNULL((1)) struct service *
NOTHROW_RPC(CC libservice_open_nx)(char const *filename);

/* Close/detach a given service. WARNING: After this function was called,
 * all function pointers  returned by `service_dlsym()'  will point  into
 * the void /  into unmapped memory.  As such, it  is up to  the user  to
 * ensure that no part  of the process  is still using  a service at  the
 * time of it being closed.
 * Services not closed when the calling process exits, or makes a call
 * to one  of the  `exec()' functions  will be  closed  automatically. */
INTDEF NOBLOCK NONNULL((1)) void
NOTHROW(CC libservice_close)(struct service *__restrict self);


/* The heart-piece of all of libservice:  lookup a symbol exported by  the
 * given service `self', dynamically generate a callback wrapper function,
 * and finally return the address of said wrapper function which can  then
 * be called like any other C-function (using `CC' calling
 * convention, which is guarantied to be the default calling convention
 * for the current architecture)
 *
 * Note that server functions like those generated by this API are always
 * re-entrance safe, in that they can  be invoked from any context  (esp.
 * including signal handler) and by  any number of threads,  irregardless
 * of how  the server-side  implementation of  the function  looks  like!
 * Generally  speaking, you  can think  of service  functions like system
 * calls,  and as a matter of fact:  the entire idea of service functions
 * stems from the idea of having a user-space API to expose inter-process
 * functions which can be called from an arbitrary context similar to how
 * system calls also can!
 *
 * @return: * :   Pointer to the service wrapper function.
 * @return: NULL: [errno=ENOENT] No function exists with the given name.
 * @return: NULL: [errno=EINTR]  The calling thread was interrupted.
 * @return: NULL: [errno=ENOMEM] Insufficient memory. */
INTDEF WUNUSED NONNULL((1, 2)) void *
NOTHROW_RPC(CC libservice_dlsym_nx)(struct service *__restrict self,
                                    char const *__restrict symname);



/* Service interface buffer alloc/free. These functions will allocate memory
 * in very special locations can be serialized/deserialized much faster when
 * passed as arguments to server functions than when using buffers allocated
 * by other means.
 * Use these functions for creating/freeing buffers for the purpose of holding
 * large  quantities of  data to-be shared  with the server.  Note that memory
 * returned  by these functions  is literally shared  with the server, meaning
 * that  you really don't want to store any information in here that you don't
 * want to share with the server!
 * NOTE: These  functions only  guaranty that  returned pointers  are aligned by
 *       sizeof(void *), rather than `__ALIGNOF_MAX_ALIGN_T__', as is guarantied
 *       by the regular `malloc'!
 * @return: * :   Base address of the buffer.
 * @return: NULL: [errno=ENOMEM] Insufficient memory. */
INTDEF WUNUSED NONNULL((1)) void *
NOTHROW(CC libservice_buffer_malloc_nx)(struct service *__restrict self,
                                        size_t num_bytes);
INTDEF WUNUSED NONNULL((1)) void *
NOTHROW(CC libservice_buffer_calloc_nx)(struct service *__restrict self,
                                        size_t num_bytes);
INTDEF WUNUSED NONNULL((1)) void *
NOTHROW(CC libservice_buffer_realloc_nx)(struct service *__restrict self,
                                         void *ptr, size_t num_bytes);
INTDEF NOBLOCK NONNULL((1)) void
NOTHROW(CC libservice_buffer_free)(struct service *__restrict self,
                                   void *ptr);
/* Returns the usable size of the given `ptr'. */
INTDEF NOBLOCK ATTR_PURE WUNUSED NONNULL((1)) size_t
NOTHROW(CC libservice_buffer_malloc_usable_size)(struct service *__restrict self,
                                                 void *ptr);



/* Exception-enabled versions of the above. */
INTDEF ATTR_RETNONNULL WUNUSED NONNULL((1)) struct service *CC
libservice_open(char const *filename) THROWS(E_FSERROR, E_BADALLOC, E_INTERRUPT);
INTDEF ATTR_RETNONNULL WUNUSED NONNULL((1, 2)) void *CC
libservice_dlsym(struct service *__restrict self, char const *__restrict symname)
		THROWS(E_NO_SUCH_OBJECT, E_BADALLOC, E_INTERRUPT);
INTDEF ATTR_RETNONNULL WUNUSED NONNULL((1)) void *CC
libservice_buffer_malloc(struct service *__restrict self, size_t num_bytes)
		THROWS(E_BADALLOC);
INTDEF ATTR_RETNONNULL WUNUSED NONNULL((1)) void *CC
libservice_buffer_calloc(struct service *__restrict self, size_t num_bytes)
		THROWS(E_BADALLOC);
INTDEF ATTR_RETNONNULL WUNUSED NONNULL((1)) void *CC
libservice_buffer_realloc(struct service *__restrict self, void *ptr, size_t num_bytes)
		THROWS(E_BADALLOC);



DECL_END

#endif /* !GUARD_LIBSERVICE_CLIENT_H */
