2
* MIO, an I/O abstraction layer replicating C file I/O API.
3
* Copyright (C) 2010 Colomban Wendling <ban@herbesfolles.org>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License along
16
* with this program; if not, write to the Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
/* memory IO implementation */
32
#define MEM_SET_VTABLE(mio) \
34
mio->v_free = mem_free; \
35
mio->v_read = mem_read; \
36
mio->v_write = mem_write; \
37
mio->v_getc = mem_getc; \
38
mio->v_gets = mem_gets; \
39
mio->v_ungetc = mem_ungetc; \
40
mio->v_putc = mem_putc; \
41
mio->v_puts = mem_puts; \
42
mio->v_vprintf = mem_vprintf; \
43
mio->v_clearerr = mem_clearerr; \
44
mio->v_eof = mem_eof; \
45
mio->v_error = mem_error; \
46
mio->v_seek = mem_seek; \
47
mio->v_tell = mem_tell; \
48
mio->v_rewind = mem_rewind; \
49
mio->v_getpos = mem_getpos; \
50
mio->v_setpos = mem_setpos; \
54
/* minimal reallocation chunk size */
55
#define MIO_CHUNK_SIZE 4096
61
if (mio->impl.mem.free_func) {
62
mio->impl.mem.free_func (mio->impl.mem.buf);
64
mio->impl.mem.buf = NULL;
65
mio->impl.mem.pos = 0;
66
mio->impl.mem.size = 0;
67
mio->impl.mem.allocated_size = 0;
68
mio->impl.mem.realloc_func = NULL;
69
mio->impl.mem.free_func = NULL;
70
mio->impl.mem.eof = FALSE;
71
mio->impl.mem.error = FALSE;
82
if (size != 0 && nmemb != 0) {
83
if (mio->impl.mem.ungetch != EOF) {
84
*((guchar *)ptr) = (guchar)mio->impl.mem.ungetch;
85
mio->impl.mem.ungetch = EOF;
89
} else if (mio->impl.mem.pos + (size - 1) <= mio->impl.mem.size) {
90
memcpy (&(((guchar *)ptr)[1]),
91
&mio->impl.mem.buf[mio->impl.mem.pos], size - 1);
92
mio->impl.mem.pos += size - 1;
96
for (; n_read < nmemb; n_read++) {
97
if (mio->impl.mem.pos + size > mio->impl.mem.size) {
100
memcpy (&(((guchar *)ptr)[n_read * size]),
101
&mio->impl.mem.buf[mio->impl.mem.pos], size);
102
mio->impl.mem.pos += size;
105
if (mio->impl.mem.pos >= mio->impl.mem.size) {
106
mio->impl.mem.eof = TRUE;
115
* @mio: A #MIO object of the type %MIO_TYPE_MEMORY
116
* @new_size: Requested new size
118
* Tries to resize the underlying buffer of an in-memory #MIO object.
119
* This supports both growing and shrinking.
121
* Returns: %TRUE on success, %FALSE otherwise.
124
mem_try_resize (MIO *mio,
127
gboolean success = FALSE;
129
if (mio->impl.mem.realloc_func) {
130
if (G_UNLIKELY (new_size == G_MAXSIZE)) {
135
if (new_size > mio->impl.mem.size) {
136
if (new_size <= mio->impl.mem.allocated_size) {
137
mio->impl.mem.size = new_size;
143
newsize = MAX (mio->impl.mem.allocated_size + MIO_CHUNK_SIZE,
145
newbuf = mio->impl.mem.realloc_func (mio->impl.mem.buf, newsize);
147
mio->impl.mem.buf = newbuf;
148
mio->impl.mem.allocated_size = newsize;
149
mio->impl.mem.size = new_size;
156
newbuf = mio->impl.mem.realloc_func (mio->impl.mem.buf, new_size);
157
if (G_LIKELY (newbuf || new_size == 0)) {
158
mio->impl.mem.buf = newbuf;
159
mio->impl.mem.allocated_size = new_size;
160
mio->impl.mem.size = new_size;
171
* mem_try_ensure_space:
172
* @mio: A #MIO object
173
* @n: Requested size from the current (cursor) position
175
* Tries to ensure there is enough space for @n bytes to be written from the
176
* current cursor position.
178
* Returns: %TRUE if there is enough space, %FALSE otherwise.
181
mem_try_ensure_space (MIO *mio,
184
gboolean success = TRUE;
186
if (mio->impl.mem.pos + n > mio->impl.mem.size) {
187
success = mem_try_resize (mio, mio->impl.mem.pos + n);
201
if (size != 0 && nmemb != 0) {
202
if (mem_try_ensure_space (mio, size * nmemb)) {
203
memcpy (&mio->impl.mem.buf[mio->impl.mem.pos], ptr, size * nmemb);
204
mio->impl.mem.pos += size * nmemb;
218
if (mem_try_ensure_space (mio, 1)) {
219
mio->impl.mem.buf[mio->impl.mem.pos] = (guchar)c;
221
rv = (gint)((guchar)c);
235
if (mem_try_ensure_space (mio, len)) {
236
memcpy (&mio->impl.mem.buf[mio->impl.mem.pos], s, len);
237
mio->impl.mem.pos += len;
245
mem_vprintf (MIO *mio,
255
old_pos = mio->impl.mem.pos;
256
old_size = mio->impl.mem.size;
257
G_VA_COPY (ap_copy, ap);
258
/* compute the size we will need into the buffer */
259
n = g_printf_string_upper_bound (format, ap_copy);
261
if (mem_try_ensure_space (mio, n)) {
264
/* backup character at n+1 that will be overwritten by a \0 ... */
265
c = mio->impl.mem.buf[mio->impl.mem.pos + (n - 1)];
266
rv = vsprintf ((gchar *)&mio->impl.mem.buf[mio->impl.mem.pos], format, ap);
267
/* ...and restore it */
268
mio->impl.mem.buf[mio->impl.mem.pos + (n - 1)] = c;
269
if (G_LIKELY (rv >= 0 && (gsize)rv == (n - 1))) {
270
/* re-compute the actual size since we might have allocated one byte
271
* more than needed */
272
mio->impl.mem.size = MAX (old_size, old_pos + (guint)rv);
273
mio->impl.mem.pos += (guint)rv;
275
mio->impl.mem.size = old_size;
288
if (mio->impl.mem.ungetch != EOF) {
289
rv = mio->impl.mem.ungetch;
290
mio->impl.mem.ungetch = EOF;
292
} else if (mio->impl.mem.pos < mio->impl.mem.size) {
293
rv = mio->impl.mem.buf[mio->impl.mem.pos];
296
mio->impl.mem.eof = TRUE;
303
mem_ungetc (MIO *mio,
308
if (ch != EOF && mio->impl.mem.ungetch == EOF) {
309
rv = mio->impl.mem.ungetch = ch;
311
mio->impl.mem.eof = FALSE;
327
if (mio->impl.mem.ungetch != EOF) {
328
s[i] = (gchar)mio->impl.mem.ungetch;
329
mio->impl.mem.ungetch = EOF;
333
for (; mio->impl.mem.pos < mio->impl.mem.size && i < (size - 1); i++) {
334
s[i] = (gchar)mio->impl.mem.buf[mio->impl.mem.pos];
345
if (mio->impl.mem.pos >= mio->impl.mem.size) {
346
mio->impl.mem.eof = TRUE;
354
mem_clearerr (MIO *mio)
356
mio->impl.mem.error = FALSE;
357
mio->impl.mem.eof = FALSE;
363
return mio->impl.mem.eof != FALSE;
369
return mio->impl.mem.error != FALSE;
372
/* FIXME: should we support seeking out of bounds like lseek() seems to do? */
382
if (offset < 0 || (gsize)offset > mio->impl.mem.size) {
385
mio->impl.mem.pos = (gsize)offset;
391
if ((offset < 0 && (gsize)-offset > mio->impl.mem.pos) ||
392
mio->impl.mem.pos + (gsize)offset > mio->impl.mem.size) {
395
mio->impl.mem.pos = (gsize)((gssize)mio->impl.mem.pos + offset);
401
if (offset > 0 || (gsize)-offset > mio->impl.mem.size) {
404
mio->impl.mem.pos = mio->impl.mem.size - (gsize)-offset;
413
mio->impl.mem.eof = FALSE;
414
mio->impl.mem.ungetch = EOF;
425
if (mio->impl.mem.pos > G_MAXLONG) {
430
rv = (glong)mio->impl.mem.pos;
437
mem_rewind (MIO *mio)
439
mio->impl.mem.pos = 0;
440
mio->impl.mem.ungetch = EOF;
441
mio->impl.mem.eof = FALSE;
442
mio->impl.mem.error = FALSE;
446
mem_getpos (MIO *mio,
451
if (mio->impl.mem.pos == (gsize)-1) {
452
/* this happens if ungetc() was called at the start of the stream */
457
pos->impl.mem = mio->impl.mem.pos;
465
mem_setpos (MIO *mio,
470
if (pos->impl.mem > mio->impl.mem.size) {
473
mio->impl.mem.ungetch = EOF;
474
mio->impl.mem.pos = pos->impl.mem;