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.
8 <<open_memstream>>, <<open_wmemstream>>---open a write stream around an arbitrary-length string
17 FILE *open_memstream(char **restrict <[buf]>,
18 size_t *restrict <[size]>);
21 FILE *open_wmemstream(wchar_t **restrict <[buf]>,
22 size_t *restrict <[size]>);
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.
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.
61 Supporting OS subroutines required: <<sbrk>>.
72 #ifndef __LARGE64_FILES
75 # define OFF_T _off64_t
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 */
89 } saved
; /* saved character that lived at *psize before NUL */
90 int8_t wide
; /* wide-oriented (>0) or byte-oriented (<0) */
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
,
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
;
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
);
123 return EOF
; /* errno already set to ENOMEM */
127 /* If we have previously done a seek beyond eof, ensure all
128 intermediate bytes are NUL. */
130 memset (cbuf
+ c
->eof
, '\0', c
->pos
- c
->eof
);
131 memcpy (cbuf
+ c
->pos
, buf
, n
);
133 /* If the user has previously written further, remember what the
134 trailing NUL is overwriting. Otherwise, extend the stream. */
137 else if (c
->wide
> 0)
138 c
->saved
.w
= *(wchar_t *)(cbuf
+ c
->pos
);
140 c
->saved
.c
= cbuf
[c
->pos
];
142 *c
->psize
= (c
->wide
> 0) ? c
->pos
/ sizeof (wchar_t) : c
->pos
;
146 /* Seek to position POS relative to WHENCE within stream described by
147 COOKIE; return resulting position or fail with EOF. */
149 memseeker (struct _reent
*ptr
,
154 memstream
*c
= (memstream
*) cookie
;
155 OFF_T offset
= (OFF_T
) pos
;
157 if (whence
== SEEK_CUR
)
159 else if (whence
== SEEK_END
)
163 _REENT_ERRNO(ptr
) = EINVAL
;
166 else if ((size_t) offset
!= offset
)
168 _REENT_ERRNO(ptr
) = ENOSPC
;
171 #ifdef __LARGE64_FILES
172 else if ((_fpos_t
) offset
!= offset
)
174 _REENT_ERRNO(ptr
) = EOVERFLOW
;
177 #endif /* __LARGE64_FILES */
183 *(wchar_t *)((*c
->pbuf
) + c
->pos
) = c
->saved
.w
;
185 (*c
->pbuf
)[c
->pos
] = c
->saved
.c
;
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);
199 c
->saved
.c
= (*c
->pbuf
)[c
->pos
];
200 (*c
->pbuf
)[c
->pos
] = '\0';
204 else if (c
->wide
> 0)
205 *c
->psize
= c
->eof
/ sizeof (wchar_t);
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
216 memseeker64 (struct _reent
*ptr
,
221 _off64_t offset
= (_off64_t
) pos
;
222 memstream
*c
= (memstream
*) cookie
;
224 if (whence
== SEEK_CUR
)
226 else if (whence
== SEEK_END
)
230 _REENT_ERRNO(ptr
) = EINVAL
;
233 else if ((size_t) offset
!= offset
)
235 _REENT_ERRNO(ptr
) = ENOSPC
;
243 *(wchar_t *)((*c
->pbuf
) + c
->pos
) = c
->saved
.w
;
245 (*c
->pbuf
)[c
->pos
] = c
->saved
.c
;
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);
259 c
->saved
.c
= (*c
->pbuf
)[c
->pos
];
260 (*c
->pbuf
)[c
->pos
] = '\0';
264 else if (c
->wide
> 0)
265 *c
->psize
= c
->eof
/ sizeof (wchar_t);
269 return (_fpos64_t
) offset
;
271 #endif /* __LARGE64_FILES */
273 /* Reclaim resources used by stream described by COOKIE. */
275 memcloser (struct _reent
*ptr
,
278 memstream
*c
= (memstream
*) cookie
;
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)
287 _free_r (ptr
, c
->storage
);
291 /* Open a memstream that tracks a dynamic buffer in BUF and SIZE.
292 Return the new stream, or fail with NULL. */
294 internal_open_memstream_r (struct _reent
*ptr
,
304 _REENT_ERRNO(ptr
) = EINVAL
;
307 if ((fp
= __sfp (ptr
)) == 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
);
316 _newlib_sfp_lock_end ();
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). */
325 c
->max
*= sizeof(wchar_t);
328 #if (SIZE_MAX >= 64 * 1024)
329 else if (c
->max
> (size_t)64 * 1024)
330 c
->max
= (size_t)64 * 1024;
333 *buf
= _malloc_r (ptr
, c
->max
);
336 _newlib_sfp_lock_start ();
337 fp
->_flags
= 0; /* release */
338 #ifndef __SINGLE_THREAD__
339 __lock_close_recursive (fp
->_lock
);
341 _newlib_sfp_lock_end ();
346 **((wchar_t **)buf
) = L
'\0';
356 c
->wide
= (int8_t) wide
;
358 _newlib_flockfile_start (fp
);
363 fp
->_write
= memwriter
;
364 fp
->_seek
= memseeker
;
365 #ifdef __LARGE64_FILES
366 fp
->_seek64
= memseeker64
;
367 fp
->_flags
|= __SL64
;
369 fp
->_close
= memcloser
;
370 (void) ORIENT (fp
, wide
);
371 _newlib_flockfile_end (fp
);
376 _open_memstream_r (struct _reent
*ptr
,
380 return internal_open_memstream_r (ptr
, buf
, size
, -1);
384 _open_wmemstream_r (struct _reent
*ptr
,
388 return internal_open_memstream_r (ptr
, (char **)buf
, size
, 1);
393 open_memstream (char **buf
,
396 return _open_memstream_r (_REENT
, buf
, size
);
400 open_wmemstream (wchar_t **buf
,
403 return _open_wmemstream_r (_REENT
, buf
, size
);
405 #endif /* !_REENT_ONLY */