/**********************************************************************
io_buffer.c
Copyright (C) 2021 Samuel Grant Dawson Williams
**********************************************************************/
#include "ruby/io/buffer.h"
#include "ruby/fiber/scheduler.h"
// For `rb_nogvl`.
#include "ruby/thread.h"
#include "internal.h"
#include "internal/array.h"
#include "internal/bits.h"
#include "internal/error.h"
#include "internal/gc.h"
#include "internal/numeric.h"
#include "internal/string.h"
#include "internal/io.h"
VALUE rb_cIOBuffer;
VALUE rb_eIOBufferLockedError;
VALUE rb_eIOBufferAllocationError;
VALUE rb_eIOBufferAccessError;
VALUE rb_eIOBufferInvalidatedError;
VALUE rb_eIOBufferMaskError;
size_t RUBY_IO_BUFFER_PAGE_SIZE;
size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
#ifdef _WIN32
#else
#include <unistd.h>
#include <sys/mman.h>
#endif
enum {
RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH = 16,
RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE = 256,
RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH = 16,
// This is used to validate the flags given by the user.
RB_IO_BUFFER_FLAGS_MASK = RB_IO_BUFFER_EXTERNAL | RB_IO_BUFFER_INTERNAL | RB_IO_BUFFER_MAPPED | RB_IO_BUFFER_SHARED | RB_IO_BUFFER_LOCKED | RB_IO_BUFFER_PRIVATE | RB_IO_BUFFER_READONLY,
RB_IO_BUFFER_DEBUG = 0,
};
struct rb_io_buffer {
void *base;
size_t size;
enum rb_io_buffer_flags flags;
#if defined(_WIN32)
HANDLE mapping;
#endif
VALUE source;
};
static inline void *
io_buffer_map_memory(size_t size, int flags)
{
#if defined(_WIN32)
void * base = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
if (!base) {
rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
}
#else
int mmap_flags = MAP_ANONYMOUS;
if (flags & RB_IO_BUFFER_SHARED) {
mmap_flags |= MAP_SHARED;
}
else {
mmap_flags |= MAP_PRIVATE;
}
void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
if (base == MAP_FAILED) {
rb_sys_fail("io_buffer_map_memory:mmap");
}
ruby_annotate_mmap(base, size, "Ruby:io_buffer_map_memory");
#endif
return base;
}
static void
io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
{
#if defined(_WIN32)
HANDLE file = (HANDLE)_get_osfhandle(descriptor);
if (!file) rb_sys_fail("io_buffer_map_descriptor:_get_osfhandle");
DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
if (flags & RB_IO_BUFFER_READONLY) {
buffer->flags |= RB_IO_BUFFER_READONLY;
}
else {
protect = PAGE_READWRITE;
access = FILE_MAP_WRITE;
}
if (flags & RB_IO_BUFFER_PRIVATE) {
protect = PAGE_WRITECOPY;
access = FILE_MAP_COPY;
buffer->flags |= RB_IO_BUFFER_PRIVATE;
}
else {
// This buffer refers to external buffer.
buffer->flags |= RB_IO_BUFFER_EXTERNAL;
buffer->flags |= RB_IO_BUFFER_SHARED;
}
HANDLE mapping = CreateFileMapping(file, NULL, protect, 0, 0, NULL);
if (RB_IO_BUFFER_DEBUG) fprintf(stderr, "io_buffer_map_file:CreateFileMapping -> %p\n", mapping);
if (!mapping) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
if (!base) {
CloseHandle(mapping);
rb_sys_fail("io_buffer_map_file:MapViewOfFile");
}
buffer->mapping = mapping;
#else
int protect = PROT_READ, access = 0;
if (flags & RB_IO_BUFFER_READONLY) {
buffer->flags |= RB_IO_BUFFER_READONLY;
}
else {
protect |= PROT_WRITE;
}
if (flags & RB_IO_BUFFER_PRIVATE) {
buffer->flags |= RB_IO_BUFFER_PRIVATE;
access |= MAP_PRIVATE;
}
else {
// This buffer refers to external buffer.
buffer->flags |= RB_IO_BUFFER_EXTERNAL;
buffer->flags |= RB_IO_BUFFER_SHARE
|