source: trunk/libs/newlib/src/newlib/libc/stdio/open_memstream.c @ 450

Last change on this file since 450 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 10.5 KB
Line 
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 */
5
6/*
7FUNCTION
8<<open_memstream>>, <<open_wmemstream>>---open a write stream around an arbitrary-length string
9
10INDEX
11        open_memstream
12INDEX
13        open_wmemstream
14
15SYNOPSIS
16        #include <stdio.h>
17        FILE *open_memstream(char **restrict <[buf]>,
18                             size_t *restrict <[size]>);
19
20        #include <wchar.h>
21        FILE *open_wmemstream(wchar_t **restrict <[buf]>,
22                              size_t *restrict <[size]>);
23
24DESCRIPTION
25<<open_memstream>> creates a seekable, byte-oriented <<FILE>> stream that
26wraps an arbitrary-length buffer, created as if by <<malloc>>.  The current
27contents of *<[buf]> are ignored; this implementation uses *<[size]>
28as a hint of the maximum size expected, but does not fail if the hint
29was wrong.  The parameters <[buf]> and <[size]> are later stored
30through following any call to <<fflush>> or <<fclose>>, set to the
31current address and usable size of the allocated string; although
32after fflush, the pointer is only valid until another stream operation
33that results in a write.  Behavior is undefined if the user alters
34either *<[buf]> or *<[size]> prior to <<fclose>>.
35
36<<open_wmemstream>> is like <<open_memstream>> just with the associated
37stream being wide-oriented.  The size set in <[size]> in subsequent
38operations is the number of wide characters.
39
40The stream is write-only, since the user can directly read *<[buf]>
41after a flush; see <<fmemopen>> for a way to wrap a string with a
42readable stream.  The user is responsible for calling <<free>> on
43the final *<[buf]> after <<fclose>>.
44
45Any time the stream is flushed, a NUL byte is written at the current
46position (but is not counted in the buffer length), so that the string
47is always NUL-terminated after at most *<[size]> bytes (or wide characters
48in case of <<open_wmemstream>>).  However, data previously written beyond
49the current stream offset is not lost, and the NUL value written during a
50flush is restored to its previous value when seeking elsewhere in the string.
51
52RETURNS
53The return value is an open FILE pointer on success.  On error,
54<<NULL>> is returned, and <<errno>> will be set to EINVAL if <[buf]>
55or <[size]> is NULL, ENOMEM if memory could not be allocated, or
56EMFILE if too many streams are already open.
57
58PORTABILITY
59POSIX.1-2008
60
61Supporting OS subroutines required: <<sbrk>>.
62*/
63
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"
71
72#ifndef __LARGE64_FILES
73# define OFF_T off_t
74#else
75# define OFF_T _off64_t
76#endif
77
78/* Describe details of an open memstream.  */
79typedef 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;
92
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.  */
95static _READ_WRITE_RETURN_TYPE
96memwriter (struct _reent *ptr,
97       void *cookie,
98       const char *buf,
99       _READ_WRITE_BUFSIZE_TYPE n)
100{
101  memstream *c = (memstream *) cookie;
102  char *cbuf = *c->pbuf;
103
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)
107    {
108      ptr->_errno = EFBIG;
109      return EOF;
110    }
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)
117    {
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;
126    }
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;
144}
145
146/* Seek to position POS relative to WHENCE within stream described by
147   COOKIE; return resulting position or fail with EOF.  */
148static _fpos_t
149memseeker (struct _reent *ptr,
150       void *cookie,
151       _fpos_t pos,
152       int whence)
153{
154  memstream *c = (memstream *) cookie;
155  OFF_T offset = (OFF_T) pos;
156
157  if (whence == SEEK_CUR)
158    offset += c->pos;
159  else if (whence == SEEK_END)
160    offset += c->eof;
161  if (offset < 0)
162    {
163      ptr->_errno = EINVAL;
164      offset = -1;
165    }
166  else if ((size_t) offset != offset)
167    {
168      ptr->_errno = ENOSPC;
169      offset = -1;
170    }
171#ifdef __LARGE64_FILES
172  else if ((_fpos_t) offset != offset)
173    {
174      ptr->_errno = EOVERFLOW;
175      offset = -1;
176    }
177#endif /* __LARGE64_FILES */
178  else
179    {
180      if (c->pos < c->eof)
181        {
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';
187        }
188      c->pos = offset;
189      if (c->pos < c->eof)
190        {
191          if (c->wide > 0)
192            {
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);
196            }
197          else
198            {
199              c->saved.c = (*c->pbuf)[c->pos];
200              (*c->pbuf)[c->pos] = '\0';
201              *c->psize = c->pos;
202            }
203        }
204      else if (c->wide > 0)
205        *c->psize = c->eof / sizeof (wchar_t);
206      else
207        *c->psize = c->eof;
208    }
209  return (_fpos_t) offset;
210}
211
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
215static _fpos64_t
216memseeker64 (struct _reent *ptr,
217       void *cookie,
218       _fpos64_t pos,
219       int whence)
220{
221  _off64_t offset = (_off64_t) pos;
222  memstream *c = (memstream *) cookie;
223
224  if (whence == SEEK_CUR)
225    offset += c->pos;
226  else if (whence == SEEK_END)
227    offset += c->eof;
228  if (offset < 0)
229    {
230      ptr->_errno = EINVAL;
231      offset = -1;
232    }
233  else if ((size_t) offset != offset)
234    {
235      ptr->_errno = ENOSPC;
236      offset = -1;
237    }
238  else
239    {
240      if (c->pos < c->eof)
241        {
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';
247        }
248      c->pos = offset;
249      if (c->pos < c->eof)
250        {
251          if (c->wide > 0)
252            {
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);
256            }
257          else
258            {
259              c->saved.c = (*c->pbuf)[c->pos];
260              (*c->pbuf)[c->pos] = '\0';
261              *c->psize = c->pos;
262            }
263        }
264      else if (c->wide > 0)
265        *c->psize = c->eof / sizeof (wchar_t);
266      else
267        *c->psize = c->eof;
268    }
269  return (_fpos64_t) offset;
270}
271#endif /* __LARGE64_FILES */
272
273/* Reclaim resources used by stream described by COOKIE.  */
274static int
275memcloser (struct _reent *ptr,
276       void *cookie)
277{
278  memstream *c = (memstream *) cookie;
279  char *buf;
280
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;
289}
290
291/* Open a memstream that tracks a dynamic buffer in BUF and SIZE.
292   Return the new stream, or fail with NULL.  */
293static FILE *
294internal_open_memstream_r (struct _reent *ptr,
295       char **buf,
296       size_t *size,
297       int wide)
298{
299  FILE *fp;
300  memstream *c;
301
302  if (!buf || !size)
303    {
304      ptr->_errno = EINVAL;
305      return NULL;
306    }
307  if ((fp = __sfp (ptr)) == NULL)
308    return NULL;
309  if ((c = (memstream *) _malloc_r (ptr, sizeof *c)) == NULL)
310    {
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;
318    }
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 > 64 * 1024)
330    c->max = 64 * 1024;
331#endif
332  *size = 0;
333  *buf = _malloc_r (ptr, c->max);
334  if (!*buf)
335    {
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;
344    }
345  if (wide == 1)
346    **((wchar_t **)buf) = L'\0';
347  else
348    **buf = '\0';
349
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;
357
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  ORIENT (fp, wide);
371  _newlib_flockfile_end (fp);
372  return fp;
373}
374
375FILE *
376_open_memstream_r (struct _reent *ptr,
377       char **buf,
378       size_t *size)
379{
380  return internal_open_memstream_r (ptr, buf, size, -1);
381}
382
383FILE *
384_open_wmemstream_r (struct _reent *ptr,
385       wchar_t **buf,
386       size_t *size)
387{
388  return internal_open_memstream_r (ptr, (char **)buf, size, 1);
389}
390
391#ifndef _REENT_ONLY
392FILE *
393open_memstream (char **buf,
394       size_t *size)
395{
396  return _open_memstream_r (_REENT, buf, size);
397}
398
399FILE *
400open_wmemstream (wchar_t **buf,
401       size_t *size)
402{
403  return _open_wmemstream_r (_REENT, buf, size);
404}
405#endif /* !_REENT_ONLY */
Note: See TracBrowser for help on using the repository browser.