Skip to content

Commit

Permalink
stdio.h: add open_memstream of (OS X < 10.13)
Browse files Browse the repository at this point in the history
The implementation originates from https://piumarta.com/software/memstream/
  • Loading branch information
sideeffect42 authored and mascguy committed Jul 1, 2023
1 parent 1d2eccf commit d24f1d5
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 4 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,20 @@ Wrapped headers and replaced functions are:
<td>Adds declaration of <code>AI_NUMERICSERV</code></td>
<td>OSX10.5</td>
</tr>
<tr>
<tr>
<td rowspan="2"><code>pthread.h</code></td>
<td>Adds <code>PTHREAD_RWLOCK_INITIALIZER</code></td>
<td>OSX10.4</td>
</tr>
<tr>
<tr>
<td>Adds <code>pthread_setname_np</code> function</td>
<td>OSX10.5</td>
</tr>
<tr>
<td><code>stdio.h</code></td>
<td>Adds <code>dprintf</code>, <code>getline</code>, and <code>getdelim</code> functions</td>
<td>OSX10.6</td>
<td>Adds <code>dprintf</code>, <code>getline</code>, <code>getdelim</code>,
and <code>open_memstream</code> functions</td>
<td>OSX10.6, OSX10.12 (open_memstream)</td>
</tr>
<tr>
<td rowspan="2"><code>stdlib.h</code></td>
Expand Down
3 changes: 3 additions & 0 deletions include/MacportsLegacySupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@
/* flsll */
#define __MP_LEGACY_SUPPORT_FLSLL__ (__APPLE__ && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1090)

/* open_memstream */
#define __MP_LEGACY_SUPPORT_OPEN_MEMSTREAM__ (__APPLE__ && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300)

/* pthread_setname_np */
#define __MP_LEGACY_SUPPORT_PTHREAD_SETNAME_NP__ (__APPLE__ && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1060)

Expand Down
9 changes: 9 additions & 0 deletions include/stdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ __MP__END_DECLS

#endif /* __MP_LEGACY_SUPPORT_GETLINE__ */

/* open_memstream */
#if __MP_LEGACY_SUPPORT_OPEN_MEMSTREAM__

__MP__BEGIN_DECLS
FILE *open_memstream(char **ptr, size_t *sizeloc);
__MP__END_DECLS

#endif /* __MP_LEGACY_SUPPORT_OPEN_MEMSTREAM__ */

/* renameat */
#if __MP_LEGACY_SUPPORT_ATCALLS__

Expand Down
180 changes: 180 additions & 0 deletions src/memstream.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/* memstream.c
*
* This file is licensed under the MIT license by
*
* Copyright (c) 2013 Ian Piumarta.
*
* Modifications for inclusion in macports-legacy-support
* Copyright (c) 2023 Dennis Camera.
*
* ----------------------------------------------------------------------------
*
* OPEN_MEMSTREAM(3) BSD and Linux Library Functions OPEN_MEMSTREAM(3)
*
* SYNOPSIS
* FILE *open_memstream(char **bufp, size_t *sizep);
*
* DESCRIPTION
* The open_memstream() function opens a stream for writing to a buffer.
* The buffer is dynamically allocated (as with malloc(3)), and
* automatically grows as required. After closing the stream, the caller
* should free(3) this buffer.
*
* When the stream is closed (fclose(3)) or flushed (fflush(3)), the
* locations pointed to by bufp and sizep are updated to contain,
* respectively, a pointer to the buffer and the current size of the
* buffer. These values remain valid only as long as the caller performs
* no further output on the stream. If further output is performed, then
* the stream must again be flushed before trying to access these
* variables.
*
* A null byte is maintained at the end of the buffer. This byte is not
* included in the size value stored at sizep.
*
* The stream's file position can be changed with fseek(3) or fseeko(3).
* Moving the file position past the end of the data already written fills
* the intervening space with zeros.
*
* RETURN VALUE
* Upon successful completion open_memstream() returns a FILE pointer.
* Otherwise, NULL is returned and errno is set to indicate the error.
*
* CONFORMING TO
* POSIX.1-2008
*
* ----------------------------------------------------------------------------
*/

/* MP support header */
#include "MacportsLegacySupport.h"
#if __MP_LEGACY_SUPPORT_OPEN_MEMSTREAM__

#include "stdio.h"

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

#define min(X, Y) (((X) < (Y)) ? (X) : (Y))

struct memstream
{
int position;
int size;
int capacity;
char *contents;
char **ptr;
size_t *sizeloc;
};

#if MEMSTREAM_DEBUG
static void memstream_print(struct memstream *ms)
{
printf("memstream %p {", ms);
printf(" %i", ms->position);
printf(" %i", ms->size);
printf(" %i", ms->capacity);
printf(" %p", ms->contents);
printf(" }\n");
}
# define memstream_info(ARGS) printf ARGS
#else
# define memstream_print(ms)
# define memstream_info(ARGS)
#endif

#define memstream_check(MS) if (!(MS)->contents) { errno= ENOMEM; return -1; }

static int memstream_grow(struct memstream *ms, int minsize)
{
int newcap= ms->capacity * 2; memstream_check(ms);
while (newcap <= minsize) newcap *= 2; memstream_info(("grow %p to %i\n", ms, newcap));
ms->contents= realloc(ms->contents, newcap);
if (!ms->contents) return -1; /* errno == ENOMEM */
memset(ms->contents + ms->capacity, 0, newcap - ms->capacity);
ms->capacity= newcap;
*ms->ptr= ms->contents; /* size has not changed */
return 0;
}

static int memstream_read(void *cookie, char *buf, int count)
{
struct memstream *ms= (struct memstream *)cookie; memstream_check(ms);
int n= min(ms->size - ms->position, count); memstream_info(("memstream_read %p %i\n", ms, count));
if (n < 1) return 0;
memcpy(buf, ms->contents, n);
ms->position += n; memstream_print(ms);
return n;
}

static int memstream_write(void *cookie, const char *buf, int count)
{
struct memstream *ms= (struct memstream *)cookie; memstream_check(ms);
if (ms->capacity <= ms->position + count)
if (memstream_grow(ms, ms->position + count) < 0) /* errno == ENOMEM */
return -1;
memcpy(ms->contents + ms->position, buf, count); memstream_info(("memstream_write %p %i\n", ms, count));
ms->position += count;
if (ms->size < ms->position) *ms->sizeloc= ms->size= ms->position; memstream_print(ms);
assert(ms->size < ms->capacity);
assert(ms->contents[ms->size] == 0);
return count;
}

static fpos_t memstream_seek(void *cookie, fpos_t offset, int whence)
{
struct memstream *ms= (struct memstream *)cookie;
fpos_t pos= 0; memstream_check(ms);
memstream_info(("memstream_seek %p %i %i\n", ms, (int)offset, whence));
switch (whence) {
case SEEK_SET: pos= offset; break;
case SEEK_CUR: pos= ms->position + offset; break;
case SEEK_END: pos= ms->size + offset; break;
default: errno= EINVAL; return -1;
}
if (pos >= ms->capacity) memstream_grow(ms, pos);
ms->position= pos;
if (ms->size < ms->position) *ms->sizeloc= ms->size= ms->position; memstream_print(ms); memstream_info(("=> %i\n", (int)pos));
assert(ms->size < ms->capacity && ms->contents[ms->size] == 0);
return pos;
}

static int memstream_close(void *cookie)
{
struct memstream *ms= (struct memstream *)cookie; if (!ms->contents) { free(ms); errno= ENOMEM; return -1; }
ms->size= min(ms->size, ms->position);
*ms->ptr= ms->contents;
*ms->sizeloc= ms->size; assert(ms->size < ms->capacity);
ms->contents[ms->size]= 0;
free(ms);
return 0;
}

FILE *open_memstream(char **ptr, size_t *sizeloc)
{
if (ptr && sizeloc) {
struct memstream *ms= calloc(1, sizeof(struct memstream));
FILE *fp= 0; if (!ms) return 0; /* errno == ENOMEM */
ms->position= ms->size= 0;
ms->capacity= 4096;
ms->contents= calloc(ms->capacity, 1); if (!ms->contents) { free(ms); return 0; } /* errno == ENOMEM */
ms->ptr= ptr;
ms->sizeloc= sizeloc;
memstream_print(ms);
fp= funopen(ms, memstream_read, memstream_write, memstream_seek, memstream_close);
if (!fp) {
free(ms->contents);
free(ms);
return 0; /* errno set by funopen */
}
*ptr= ms->contents;
*sizeloc= ms->size;
return fp;
}
errno= EINVAL;
return 0;
}

#endif /* __MP_LEGACY_SUPPORT_OPEN_MEMSTREAM__ */
29 changes: 29 additions & 0 deletions test/test_open_memstream.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*#include "memstream.h"*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
char *buffer = 0;
size_t size = 0;
FILE *fp = open_memstream(&buffer, &size);
int i;
for (i = 0; i < 10240; ++i) {
static char c = 42;
fflush(fp);
assert(size == i);
fwrite(&c, 1, 1, fp);
}
fclose(fp);
assert(size == 10240);
free(buffer);
fp = open_memstream(&buffer, &size);
fprintf(fp, "This is a test of memstream, from main at %p.\n", main);
fclose(fp);
fputs(buffer, stdout);
free(buffer);

return 0;
}

0 comments on commit d24f1d5

Please sign in to comment.