Cygwin: mmap: allow remapping part of an existing anonymous mapping
[newlib-cygwin.git] / newlib / libc / stdio / open_memstream.c
blob97b13b7bfc102afa91ea79d7d24748815b4d7c8b
1 /* Copyright (C) 2007 Eric Blake
2 * Permission to use, copy, modify, and distribute this software
3 * is freely granted, provided that this notice is preserved.
4 */
6 /*
7 FUNCTION
8 <<open_memstream>>, <<open_wmemstream>>---open a write stream around an arbitrary-length string
10 INDEX
11 open_memstream
12 INDEX
13 open_wmemstream
15 SYNOPSIS
16 #include <stdio.h>
17 FILE *open_memstream(char **restrict <[buf]>,
18 size_t *restrict <[size]>);
20 #include <wchar.h>
21 FILE *open_wmemstream(wchar_t **restrict <[buf]>,
22 size_t *restrict <[size]>);
24 DESCRIPTION
25 <<open_memstream>> creates a seekable, byte-oriented <<FILE>> stream that
26 wraps an arbitrary-length buffer, created as if by <<malloc>>. The current
27 contents of *<[buf]> are ignored; this implementation uses *<[size]>
28 as a hint of the maximum size expected, but does not fail if the hint
29 was wrong. The parameters <[buf]> and <[size]> are later stored
30 through following any call to <<fflush>> or <<fclose>>, set to the
31 current address and usable size of the allocated string; although
32 after fflush, the pointer is only valid until another stream operation
33 that results in a write. Behavior is undefined if the user alters
34 either *<[buf]> or *<[size]> prior to <<fclose>>.
36 <<open_wmemstream>> is like <<open_memstream>> just with the associated
37 stream being wide-oriented. The size set in <[size]> in subsequent
38 operations is the number of wide characters.
40 The stream is write-only, since the user can directly read *<[buf]>
41 after a flush; see <<fmemopen>> for a way to wrap a string with a
42 readable stream. The user is responsible for calling <<free>> on
43 the final *<[buf]> after <<fclose>>.
45 Any time the stream is flushed, a NUL byte is written at the current
46 position (but is not counted in the buffer length), so that the string
47 is always NUL-terminated after at most *<[size]> bytes (or wide characters
48 in case of <<open_wmemstream>>). However, data previously written beyond
49 the current stream offset is not lost, and the NUL value written during a
50 flush is restored to its previous value when seeking elsewhere in the string.
52 RETURNS
53 The return value is an open FILE pointer on success. On error,
54 <<NULL>> is returned, and <<errno>> will be set to EINVAL if <[buf]>
55 or <[size]> is NULL, ENOMEM if memory could not be allocated, or
56 EMFILE if too many streams are already open.
58 PORTABILITY
59 POSIX.1-2008
61 Supporting OS subroutines required: <<sbrk>>.
64 #include <stdio.h>
65 #include <wchar.h>
66 #include <errno.h>
67 #include <string.h>
68 #include <sys/lock.h>
69 #include <stdint.h>
70 #include "local.h"
72 #ifndef __LARGE64_FILES
73 # define OFF_T off_t
74 #else
75 # define OFF_T _off64_t
76 #endif
78 /* Describe details of an open memstream. */
79 typedef struct memstream {
80 void *storage; /* storage to free on close */
81 char **pbuf; /* pointer to the current buffer */
82 size_t *psize; /* pointer to the current size, smaller of pos or eof */
83 size_t pos; /* current position */
84 size_t eof; /* current file size */
85 size_t max; /* current malloc buffer size, always > eof */
86 union {
87 char c;
88 wchar_t w;
89 } saved; /* saved character that lived at *psize before NUL */
90 int8_t wide; /* wide-oriented (>0) or byte-oriented (<0) */
91 } memstream;
93 /* Write up to non-zero N bytes of BUF into the stream described by COOKIE,
94 returning the number of bytes written or EOF on failure. */
95 static _READ_WRITE_RETURN_TYPE
96 memwriter (struct _reent *ptr,
97 void *cookie,
98 const char *buf,
99 _READ_WRITE_BUFSIZE_TYPE n)
101 memstream *c = (memstream *) cookie;
102 char *cbuf = *c->pbuf;
104 /* size_t is unsigned, but off_t is signed. Don't let stream get so
105 big that user cannot do ftello. */
106 if (sizeof (OFF_T) == sizeof (size_t) && (ssize_t) (c->pos + n) < 0)
108 _REENT_ERRNO(ptr) = EFBIG;
109 return EOF;
111 /* Grow the buffer, if necessary. Choose a geometric growth factor
112 to avoid quadratic realloc behavior, but use a rate less than
113 (1+sqrt(5))/2 to accomodate malloc overhead. Overallocate, so
114 that we can add a trailing \0 without reallocating. The new
115 allocation should thus be max(prev_size*1.5, c->pos+n+1). */
116 if (c->pos + n >= c->max)
118 size_t newsize = c->max * 3 / 2;
119 if (newsize < c->pos + n + 1)
120 newsize = c->pos + n + 1;
121 cbuf = _realloc_r (ptr, cbuf, newsize);
122 if (! cbuf)
123 return EOF; /* errno already set to ENOMEM */
124 *c->pbuf = cbuf;
125 c->max = newsize;
127 /* If we have previously done a seek beyond eof, ensure all
128 intermediate bytes are NUL. */
129 if (c->pos > c->eof)
130 memset (cbuf + c->eof, '\0', c->pos - c->eof);
131 memcpy (cbuf + c->pos, buf, n);
132 c->pos += n;
133 /* If the user has previously written further, remember what the
134 trailing NUL is overwriting. Otherwise, extend the stream. */
135 if (c->pos > c->eof)
136 c->eof = c->pos;
137 else if (c->wide > 0)
138 c->saved.w = *(wchar_t *)(cbuf + c->pos);
139 else
140 c->saved.c = cbuf[c->pos];
141 cbuf[c->pos] = '\0';
142 *c->psize = (c->wide > 0) ? c->pos / sizeof (wchar_t) : c->pos;
143 return n;
146 /* Seek to position POS relative to WHENCE within stream described by
147 COOKIE; return resulting position or fail with EOF. */
148 static _fpos_t
149 memseeker (struct _reent *ptr,
150 void *cookie,
151 _fpos_t pos,
152 int whence)
154 memstream *c = (memstream *) cookie;
155 OFF_T offset = (OFF_T) pos;
157 if (whence == SEEK_CUR)
158 offset += c->pos;
159 else if (whence == SEEK_END)
160 offset += c->eof;
161 if (offset < 0)
163 _REENT_ERRNO(ptr) = EINVAL;
164 offset = -1;
166 else if ((size_t) offset != offset)
168 _REENT_ERRNO(ptr) = ENOSPC;
169 offset = -1;
171 #ifdef __LARGE64_FILES
172 else if ((_fpos_t) offset != offset)
174 _REENT_ERRNO(ptr) = EOVERFLOW;
175 offset = -1;
177 #endif /* __LARGE64_FILES */
178 else
180 if (c->pos < c->eof)
182 if (c->wide > 0)
183 *(wchar_t *)((*c->pbuf) + c->pos) = c->saved.w;
184 else
185 (*c->pbuf)[c->pos] = c->saved.c;
186 c->saved.w = L'\0';
188 c->pos = offset;
189 if (c->pos < c->eof)
191 if (c->wide > 0)
193 c->saved.w = *(wchar_t *)((*c->pbuf) + c->pos);
194 *(wchar_t *)((*c->pbuf) + c->pos) = L'\0';
195 *c->psize = c->pos / sizeof (wchar_t);
197 else
199 c->saved.c = (*c->pbuf)[c->pos];
200 (*c->pbuf)[c->pos] = '\0';
201 *c->psize = c->pos;
204 else if (c->wide > 0)
205 *c->psize = c->eof / sizeof (wchar_t);
206 else
207 *c->psize = c->eof;
209 return (_fpos_t) offset;
212 /* Seek to position POS relative to WHENCE within stream described by
213 COOKIE; return resulting position or fail with EOF. */
214 #ifdef __LARGE64_FILES
215 static _fpos64_t
216 memseeker64 (struct _reent *ptr,
217 void *cookie,
218 _fpos64_t pos,
219 int whence)
221 _off64_t offset = (_off64_t) pos;
222 memstream *c = (memstream *) cookie;
224 if (whence == SEEK_CUR)
225 offset += c->pos;
226 else if (whence == SEEK_END)
227 offset += c->eof;
228 if (offset < 0)
230 _REENT_ERRNO(ptr) = EINVAL;
231 offset = -1;
233 else if ((size_t) offset != offset)
235 _REENT_ERRNO(ptr) = ENOSPC;
236 offset = -1;
238 else
240 if (c->pos < c->eof)
242 if (c->wide > 0)
243 *(wchar_t *)((*c->pbuf) + c->pos) = c->saved.w;
244 else
245 (*c->pbuf)[c->pos] = c->saved.c;
246 c->saved.w = L'\0';
248 c->pos = offset;
249 if (c->pos < c->eof)
251 if (c->wide > 0)
253 c->saved.w = *(wchar_t *)((*c->pbuf) + c->pos);
254 *(wchar_t *)((*c->pbuf) + c->pos) = L'\0';
255 *c->psize = c->pos / sizeof (wchar_t);
257 else
259 c->saved.c = (*c->pbuf)[c->pos];
260 (*c->pbuf)[c->pos] = '\0';
261 *c->psize = c->pos;
264 else if (c->wide > 0)
265 *c->psize = c->eof / sizeof (wchar_t);
266 else
267 *c->psize = c->eof;
269 return (_fpos64_t) offset;
271 #endif /* __LARGE64_FILES */
273 /* Reclaim resources used by stream described by COOKIE. */
274 static int
275 memcloser (struct _reent *ptr,
276 void *cookie)
278 memstream *c = (memstream *) cookie;
279 char *buf;
281 /* Be nice and try to reduce any unused memory. */
282 buf = _realloc_r (ptr, *c->pbuf,
283 c->wide > 0 ? (*c->psize + 1) * sizeof (wchar_t)
284 : *c->psize + 1);
285 if (buf)
286 *c->pbuf = buf;
287 _free_r (ptr, c->storage);
288 return 0;
291 /* Open a memstream that tracks a dynamic buffer in BUF and SIZE.
292 Return the new stream, or fail with NULL. */
293 static FILE *
294 internal_open_memstream_r (struct _reent *ptr,
295 char **buf,
296 size_t *size,
297 int wide)
299 FILE *fp;
300 memstream *c;
302 if (!buf || !size)
304 _REENT_ERRNO(ptr) = EINVAL;
305 return NULL;
307 if ((fp = __sfp (ptr)) == NULL)
308 return NULL;
309 if ((c = (memstream *) _malloc_r (ptr, sizeof *c)) == NULL)
311 _newlib_sfp_lock_start ();
312 fp->_flags = 0; /* release */
313 #ifndef __SINGLE_THREAD__
314 __lock_close_recursive (fp->_lock);
315 #endif
316 _newlib_sfp_lock_end ();
317 return NULL;
319 /* Use *size as a hint for initial sizing, but bound the initial
320 malloc between 64 bytes (same as asprintf, to avoid frequent
321 mallocs on small strings) and 64k bytes (to avoid overusing the
322 heap if *size was garbage). */
323 c->max = *size;
324 if (wide == 1)
325 c->max *= sizeof(wchar_t);
326 if (c->max < 64)
327 c->max = 64;
328 #if (SIZE_MAX >= 64 * 1024)
329 else if (c->max > (size_t)64 * 1024)
330 c->max = (size_t)64 * 1024;
331 #endif
332 *size = 0;
333 *buf = _malloc_r (ptr, c->max);
334 if (!*buf)
336 _newlib_sfp_lock_start ();
337 fp->_flags = 0; /* release */
338 #ifndef __SINGLE_THREAD__
339 __lock_close_recursive (fp->_lock);
340 #endif
341 _newlib_sfp_lock_end ();
342 _free_r (ptr, c);
343 return NULL;
345 if (wide == 1)
346 **((wchar_t **)buf) = L'\0';
347 else
348 **buf = '\0';
350 c->storage = c;
351 c->pbuf = buf;
352 c->psize = size;
353 c->pos = 0;
354 c->eof = 0;
355 c->saved.w = L'\0';
356 c->wide = (int8_t) wide;
358 _newlib_flockfile_start (fp);
359 fp->_file = -1;
360 fp->_flags = __SWR;
361 fp->_cookie = c;
362 fp->_read = NULL;
363 fp->_write = memwriter;
364 fp->_seek = memseeker;
365 #ifdef __LARGE64_FILES
366 fp->_seek64 = memseeker64;
367 fp->_flags |= __SL64;
368 #endif
369 fp->_close = memcloser;
370 (void) ORIENT (fp, wide);
371 _newlib_flockfile_end (fp);
372 return fp;
375 FILE *
376 _open_memstream_r (struct _reent *ptr,
377 char **buf,
378 size_t *size)
380 return internal_open_memstream_r (ptr, buf, size, -1);
383 FILE *
384 _open_wmemstream_r (struct _reent *ptr,
385 wchar_t **buf,
386 size_t *size)
388 return internal_open_memstream_r (ptr, (char **)buf, size, 1);
391 #ifndef _REENT_ONLY
392 FILE *
393 open_memstream (char **buf,
394 size_t *size)
396 return _open_memstream_r (_REENT, buf, size);
399 FILE *
400 open_wmemstream (wchar_t **buf,
401 size_t *size)
403 return _open_wmemstream_r (_REENT, buf, size);
405 #endif /* !_REENT_ONLY */