Permalink
Browse files

fmem: added base implementations

  • Loading branch information...
Snaipe committed May 21, 2017
1 parent 157785d commit ebe90726f8e7779d58f351e337aca91a5f70c744
Showing with 855 additions and 0 deletions.
  1. +27 −0 .cmake/FindCriterion.cmake
  2. +123 −0 CMakeLists.txt
  3. +19 −0 include/fmem.h.in
  4. +61 −0 src/alloc.c
  5. +21 −0 src/alloc.h
  6. +124 −0 src/fmem-fopencookie.c
  7. +132 −0 src/fmem-funopen.c
  8. +41 −0 src/fmem-open_memstream.c
  9. +67 −0 src/fmem-tmpfile.c
  10. +124 −0 src/fmem-winapi-tmpfile.c
  11. +6 −0 test/CMakeLists.txt
  12. +110 −0 test/tests.c
@@ -0,0 +1,27 @@
# This file is licensed under the WTFPL version 2 -- you can see the full
# license over at http://www.wtfpl.net/txt/copying/
#
# - Try to find Criterion
#
# Once done this will define
# CRITERION_FOUND - System has Criterion
# CRITERION_INCLUDE_DIRS - The Criterion include directories
# CRITERION_LIBRARIES - The libraries needed to use Criterion

find_package(PkgConfig)

find_path(CRITERION_INCLUDE_DIR criterion/criterion.h
PATH_SUFFIXES criterion)

find_library(CRITERION_LIBRARY NAMES criterion libcriterion)

set(CRITERION_LIBRARIES ${CRITERION_LIBRARY})
set(CRITERION_INCLUDE_DIRS ${CRITERION_INCLUDE_DIR})

include(FindPackageHandleStandardArgs)
# handle the QUIET and REQUIRED arguments and set CRITERION_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(Criterion DEFAULT_MSG
CRITERION_LIBRARY CRITERION_INCLUDE_DIR)

mark_as_advanced(CRITERION_INCLUDE_DIR CRITERION_LIBRARY)
@@ -0,0 +1,123 @@
# Copyright (C) 2017 Franklin "Snaipe" Mathieu.
# Redistribution and use of this file is allowed according to the terms of the MIT license.
# For details see the LICENSE file distributed with Mimick.

cmake_minimum_required (VERSION 2.8)

project (fmem C)

list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.cmake")

include (CheckSymbolExists)
include (CheckCSourceCompiles)
include (GNUInstallDirs)

list (APPEND CMAKE_REQUIRED_DEFINITIONS
-D_GNU_SOURCE
-D_CRT_RAND_S
-DVC_EXTRALEAN
-DWIN32_LEAN_AND_MEAN)

check_symbol_exists (open_memstream stdio.h HAVE_OPEN_MEMSTREAM)
check_symbol_exists (fopencookie stdio.h HAVE_FOPENCOOKIE)
check_symbol_exists (funopen stdio.h HAVE_FUNOPEN)
check_symbol_exists (tmpfile stdio.h HAVE_TMPFILE)
check_symbol_exists (rand_s stdlib.h HAVE_WINAPI_RAND_S)
check_symbol_exists (CreateFile windows.h HAVE_WINAPI_CREATEFILE)
check_symbol_exists (CloseHandle windows.h HAVE_WINAPI_CLOSEHANDLE)
check_symbol_exists (GetFileSize windows.h HAVE_WINAPI_GETFILESIZE)
check_symbol_exists (CreateFileMapping windows.h HAVE_WINAPI_CREATEFILEMAPPING)
check_symbol_exists (MapViewOfFile windows.h HAVE_WINAPI_MAPVIEWOFFILE)
check_symbol_exists (UnmapViewOfFile windows.h HAVE_WINAPI_UNMAPVIEWOFFILE)
check_symbol_exists (GetTempPath windows.h HAVE_WINAPI_GETTEMPPATH)
check_symbol_exists (_open_osfhandle io.h HAVE_WINAPI_OPEN_OSFHANDLE)
check_symbol_exists (_get_osfhandle io.h HAVE_WINAPI_GET_OSFHANDLE)
check_symbol_exists (_fdopen stdio.h HAVE_WINAPI_FDOPEN)
check_symbol_exists (_fileno stdio.h HAVE_WINAPI_FILENO)
check_symbol_exists (_close io.h HAVE_WINAPI_CLOSE)

set (SOURCES)

if (HAVE_OPEN_MEMSTREAM)
list (APPEND SOURCES src/fmem-open_memstream.c)
elseif (HAVE_FOPENCOOKIE)
list (APPEND SOURCES
src/alloc.c
src/alloc.h
src/fmem-fopencookie.c)
elseif (HAVE_FUNOPEN)
list (APPEND SOURCES
src/alloc.c
src/alloc.h
src/fmem-funopen.c)
elseif (HAVE_WINAPI_CREATEFILE
AND HAVE_WINAPI_CLOSEHANDLE
AND HAVE_WINAPI_GETFILESIZE
AND HAVE_WINAPI_CREATEFILEMAPPING
AND HAVE_WINAPI_MAPVIEWOFFILE
AND HAVE_WINAPI_UNMAPVIEWOFFILE
AND HAVE_WINAPI_GETTEMPPATH
AND HAVE_WINAPI_FDOPEN
AND HAVE_WINAPI_FILENO
AND HAVE_WINAPI_CLOSE
AND HAVE_WINAPI_OPEN_OSFHANDLE
AND HAVE_WINAPI_GET_OSFHANDLE
AND HAVE_WINAPI_RAND_S)
list (APPEND SOURCES src/fmem-winapi-tmpfile.c)
elseif (HAVE_TMPFILE)
list (APPEND SOURCES src/fmem-tmpfile.c)
else ()
message (FATAL_ERROR "No memory stream implementation found")
endif ()

include_directories (include src ${PROJECT_BINARY_DIR}/gen)
add_library (fmem ${SOURCES})

get_property (FMEM_LIBTYPE
TARGET fmem
PROPERTY TYPE)

set (CMAKE_REQUIRED_DEFINITIONS -fvisibility=hidden)
check_c_source_compiles (
"__attribute__((visibility(\"default\"))) int main(void) { return 0; }"
CC_HAVE_VISIBILITY)
set (CMAKE_REQUIRED_DEFINITIONS)

if ("${FMEM_LIBTYPE}" MATCHES "SHARED_LIBRARY")
if (WIN32)
set (EXPORT_MACROS
"#ifdef FMEM_BUILD_LIBRARY
# define FMEM_API __declspec(dllexport)
#else /* !FMEM_BUILD_LIBRARY */
# define FMEM_API __declspec(dllimport)
#endif /* !FMEM_BUILD_LIBRARY */")
add_definitions (-DFMEM_BUILD_LIBRARY)
elseif (CC_HAVE_VISIBILITY)
set (EXPORT_MACROS "#define FMEM_API __attribute__((visibility(\"default\")))")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
endif ()
else ()
set (EXPORT_MACROS "#define FMEM_API")
endif ()

configure_file (
${PROJECT_SOURCE_DIR}/include/fmem.h.in
${PROJECT_BINARY_DIR}/gen/fmem.h
@ONLY)

install(TARGETS fmem
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})

install(FILES
fmem.h
${PROJECT_BINARY_DIR}/gen/fmem-export.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

include (CTest)

if (BUILD_TESTING)
find_package (Criterion REQUIRED)
add_subdirectory (test)
endif ()
@@ -0,0 +1,19 @@
#ifndef FMEM_H_
#define FMEM_H_

#include <stdio.h>

@EXPORT_MACROS@

struct fmem_reserved {
char reserved[32];
};

typedef struct fmem_reserved fmem;

FMEM_API void fmem_init(fmem *file);
FMEM_API void fmem_term(fmem *file);
FMEM_API FILE *fmem_open(fmem *file, const char *mode);
FMEM_API void fmem_mem(fmem *file, void **mem, size_t *size);

#endif /* !FMEM_H_ */
@@ -0,0 +1,61 @@
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "alloc.h"

static inline size_t golden_growth_ceil(size_t n)
{
/* This effectively is a return ceil(n * φ).
φ is approximatively 207 / (2^7), so we shift our result by
6, then perform our ceil by adding the remainder of the last division
by 2 of the result to itself. */

n = (n * 207) >> 6;
n = (n >> 1) + (n & 1);
return n;
}

int fmemi_grow(struct fmem_stream *stream, size_t required)
{
if (stream->cursor > SIZE_MAX - required) {
errno = EOVERFLOW;
return -1;
}
required += stream->cursor;

size_t newsize = stream->region_size;
if (required <= newsize) {
return 0;
}

while (required > newsize) {
newsize = golden_growth_ceil(newsize);
}

char *newmem = realloc(stream->buf->mem, newsize);
if (!newmem) {
return -1;
}
stream->buf->mem = newmem;
stream->region_size = newsize;
return 0;
}

int fmemi_cursor(struct fmemi_buf *buf, struct fmem_stream *from)
{
if (from->buf->size < from->cursor) {
return -1;
}

buf->mem = from->buf->mem + from->cursor;
buf->size = from->buf->size - from->cursor;
return 0;
}

size_t fmemi_copy(struct fmemi_buf *to, struct fmemi_buf *from)
{
size_t copied = from->size < to->size ? from->size : to->size;
memcpy(to->mem, from->mem, copied);
return copied;
}
@@ -0,0 +1,21 @@
#ifndef ALLOC_H_
#define ALLOC_H_

#include <stddef.h>

struct fmemi_buf {
char *mem;
size_t size;
};

struct fmem_stream {
struct fmemi_buf *buf;
size_t cursor;
size_t region_size;
};

int fmemi_grow(struct fmem_stream *stream, size_t required);
int fmemi_cursor(struct fmemi_buf *buf, struct fmem_stream *from);
size_t fmemi_copy(struct fmemi_buf *to, struct fmemi_buf *from);

#endif /* !ALLOC_H_ */
@@ -0,0 +1,124 @@
#define _GNU_SOURCE
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "alloc.h"
#include "fmem.h"

union fmem_conv {
fmem *fm;
struct fmemi_buf *buf;
};

void fmem_init(fmem *file)
{
union fmem_conv cv = { .fm = file };
memset(cv.buf, 0, sizeof (*cv.buf));
}

void fmem_term(fmem *file)
{
union fmem_conv cv = { .fm = file };
free(cv.buf->mem);
}

static ssize_t mem_write(void *cookie, const char *buf, size_t size)
{
struct fmem_stream *stream = cookie;

struct fmemi_buf from = { (char *) buf, size };
struct fmemi_buf to;

if (fmemi_grow(stream, size) < 0) {
return -1;
}
if (fmemi_cursor(&to, stream) < 0) {
return 0;
}

size_t copied = fmemi_copy(&to, &from);
stream->cursor += copied;
return copied;
}

static ssize_t mem_read(void *cookie, char *buf, size_t size)
{
struct fmem_stream *stream = cookie;

struct fmemi_buf to = { buf, size };
struct fmemi_buf from;

if (fmemi_cursor(&from, stream) < 0) {
return 0;
}

size_t copied = fmemi_copy(&to, &from);
stream->cursor += copied;
return copied;
}

static int mem_seek(void *cookie, off64_t *off, int whence)
{
struct fmem_stream *stream = cookie;

size_t newoff;
switch (whence) {
case SEEK_SET: newoff = *off; break;
case SEEK_CUR: newoff = stream->cursor + *off; break;
case SEEK_END: newoff = stream->buf->size + *off; break;
default: errno = EINVAL; return -1;
}
if (newoff > stream->buf->size || (off64_t)newoff < 0) {
return -1;
}
*off = newoff;
return 0;
}

static int mem_close(void *cookie)
{
free(cookie);
return 0;
}

FILE *fmem_open(fmem *file, const char *mode)
{
static cookie_io_functions_t funcs = {
.read = mem_read,
.write = mem_write,
.seek = mem_seek,
.close = mem_close,
};

union fmem_conv cv = { .fm = file };

free(cv.buf->mem);
cv.buf->mem = malloc(128);
if (!cv.buf->mem)
return NULL;

struct fmem_stream *stream = malloc(sizeof (*stream));
if (!stream) {
free(cv.buf->mem);
cv.buf->mem = NULL;
return NULL;
}

*stream = (struct fmem_stream) {
.buf = &cv.buf,
.region_size = 128,
};

FILE *f = fopencookie(stream, mode, funcs);
if (!f)
free(stream);
return f;
}

void fmem_mem(fmem *file, void **mem, size_t *size)
{
union fmem_conv cv = { .fm = file };
*mem = cv.buf->mem;
*size = cv.buf->size;
}
Oops, something went wrong.

0 comments on commit ebe9072

Please sign in to comment.