-
Notifications
You must be signed in to change notification settings - Fork 18
/
memstream.c
172 lines (156 loc) · 5.29 KB
/
memstream.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* Compile this file and link the object with your program. On a recent
* GNU/Linux machine the object file will be empty. On anything derived from
* 4.4BSD (Darwin, the Three BSDs, etc.) it will contain an implementation of
* open_memstream() as described in the POSIX and Linux manual pages. On
* anything else it will probably cause a compilation error.
*
* ----------------------------------------------------------------------------
*
* OPEN_MEMSTREAM(3) BSD and Linux Library Functions OPEN_MEMSTREAM(3)
*
* SYNOPSIS
* #include "memstream.h"
*
* 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
*
* ----------------------------------------------------------------------------
*/
#include "memstream.h"
#include "sysconfig.h"
#ifndef HAVE_OPEN_MEMSTREAM
#include <stddef.h>
#include <stdlib.h>
#include <stdint.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;
};
#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;
while (newcap <= minsize) newcap *= 2;
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);
if (n < 1) return 0;
memcpy(buf, ms->contents, n);
ms->position += n;
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)
return -1;
memcpy(ms->contents + ms->position, buf, count);
ms->position += count;
if (ms->size < ms->position) *ms->sizeloc= ms->size= ms->position;
return count;
}
static fpos_t memstream_seek(void *cookie, fpos_t offset, int whence)
{
struct memstream *ms= (struct memstream *)cookie;
fpos_t pos;
if (ms->contents == NULL) {
errno = ENOMEM;
return -1;
}
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;
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;
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;
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;
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