posix-api-0.3.0.0: cbits/HaskellPosix.c
#define _GNU_SOURCE
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdint.h>
#include "HaskellPosix.h"
#include "Rts.h"
#ifdef __GNUC__
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
// Generally, this library tries to avoid wrapping POSIX functions
// in an additional function. However, for some functions whose wrappers
// unsafe FFI wrappers use unpinned ByteArray instead of Addr, the only
// way to support providing an offset (without just copying the bytes
// into pinned memory) is to use a wrapper.
ssize_t recv_offset(int socket, char *buffer, int offset, size_t length, int flags) {
return recv(socket, (void*)(buffer + offset), length, flags);
}
ssize_t send_offset(int socket, const char *buffer, int offset, size_t length, int flags) {
return send(socket, (const void*)(buffer + offset), length, flags);
}
ssize_t sendto_offset(int socket, const char *message, int offset, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len){
return sendto(socket, (const void*)(message + offset), length, flags, dest_addr, dest_len);
}
ssize_t sendto_inet_offset(int socket, const char *message, int offset, size_t length, int flags, uint16_t port, uint32_t inet_addr){
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr;
dest.sin_port = port;
return sendto(socket, (const void*)(message + offset), length, flags, (struct sockaddr*)&dest, sizeof(dest));
}
ssize_t recvfrom_offset(int socket, char *restrict buffer, int offset, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len) {
return recvfrom(socket, (void*)(buffer + offset), length, flags, address, address_len);
}
ssize_t recvfrom_offset_inet
( int socket
, char *restrict buffer_base
, HsInt offset
, size_t length
, int flags
, struct sockaddr_in *restrict addresses
, HsInt address_offset
) {
void* buffer = (void*)(buffer_base + offset);
struct sockaddr_in* address = addresses + address_offset;
socklen_t address_len[1] = {sizeof(struct sockaddr_in)};
ssize_t r = recvfrom(socket, buffer, length, flags, address, address_len);
if (likely(address_len[0] == sizeof(struct sockaddr_in))) {
return r;
} else {
fprintf(stderr, "posix-api: recvfrom_offset_bufs");
exit(EXIT_FAILURE);
}
}
int setsockopt_int(int socket, int level, int option_name, int option_value) {
return setsockopt(socket,level,option_name,&option_value,sizeof(int));
}
// The second buffer is char* instead of void* because we need
// to apply an offset to it.
int sendmsg_a
( int sockfd
, void *bufA
, size_t lenA
, char *bufB
, HsInt offB
, size_t lenB
, int flags
) {
struct iovec bufs[2] =
{ { .iov_base = bufA, .iov_len = lenA }
, { .iov_base = (void*)(bufB + offB), .iov_len = lenB }
};
struct msghdr msg =
{ .msg_name = NULL
, .msg_namelen = 0
, .msg_iov = bufs
, .msg_iovlen = 2
, .msg_control = NULL
, .msg_controllen = 0
};
return sendmsg(sockfd,&msg,flags);
}
// The first buffer is char* instead of void* because we need
// to apply an offset to it.
int sendmsg_b
( int sockfd
, char *bufA
, HsInt offA
, size_t lenA
, void *bufB
, size_t lenB
, int flags
) {
struct iovec bufs[2] =
{ { .iov_base = (void*)(bufA + offA), .iov_len = lenA }
, { .iov_base = bufB, .iov_len = lenB }
};
struct msghdr msg =
{ .msg_name = NULL
, .msg_namelen = 0
, .msg_iov = bufs
, .msg_iovlen = 2
, .msg_control = NULL
, .msg_controllen = 0
};
return sendmsg(sockfd,&msg,flags);
}
int recvmmsg_sockaddr_in
( int sockfd
, int *lens // used for output
, struct sockaddr_in *addrs // used for output
, StgMutArrPtrs *arr // used for output
, unsigned int vlen
, int flags
) {
StgClosure **bufsX = arr->payload;
StgArrBytes **bufs = (StgArrBytes**)bufsX;
struct mmsghdr msgs[vlen];
struct iovec vecs[vlen];
for(unsigned int i = 0; i < vlen; i++) {
vecs[i].iov_base = (void*)(bufs[i]->payload);
vecs[i].iov_len = (size_t)(bufs[i]->bytes);
// We deliberately leave msg_len unassigned.
msgs[i].msg_hdr.msg_name = addrs + i;
msgs[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_in);
msgs[i].msg_hdr.msg_iov = vecs + i;
msgs[i].msg_hdr.msg_iovlen = 1;
msgs[i].msg_hdr.msg_control = NULL;
msgs[i].msg_hdr.msg_controllen = 0;
msgs[i].msg_hdr.msg_flags = flags;
}
int r = recvmmsg(sockfd,msgs,vlen,flags,NULL);
// If no errors occurred, copy all of the lengths into the
// length buffer. This copy makes me feel a little sad.
// It is the only copy in a wrapper that otherwise is
// able to share buffers perfectly between Haskell and C.
if(r > (-1)) {
for(int i = 0; i < r; i++) {
lens[i] = msgs[i].msg_len;
}
}
return r;
}
int recvmmsg_sockaddr_discard
( int sockfd
, int *lens // used for output
, StgMutArrPtrs *arr // used for output
, unsigned int vlen
, int flags
) {
StgClosure **bufsX = arr->payload;
StgArrBytes **bufs = (StgArrBytes**)bufsX;
struct mmsghdr msgs[vlen];
struct iovec vecs[vlen];
for(unsigned int i = 0; i < vlen; i++) {
vecs[i].iov_base = (void*)(bufs[i]->payload);
vecs[i].iov_len = (size_t)(bufs[i]->bytes);
// We deliberately leave msg_len unassigned.
msgs[i].msg_hdr.msg_name = NULL;
msgs[i].msg_hdr.msg_namelen = 0;
msgs[i].msg_hdr.msg_iov = vecs + i;
msgs[i].msg_hdr.msg_iovlen = 1;
msgs[i].msg_hdr.msg_control = NULL;
msgs[i].msg_hdr.msg_controllen = 0;
msgs[i].msg_hdr.msg_flags = flags;
}
int r = recvmmsg(sockfd,msgs,vlen,flags,NULL);
// If no errors occurred, copy all of the lengths into the
// length buffer. This copy makes me feel a little sad.
// It is the only copy in a wrapper that otherwise is
// able to share buffers perfectly between Haskell and C.
if(r > (-1)) {
for(int i = 0; i < r; i++) {
lens[i] = msgs[i].msg_len;
}
}
return r;
}