/**************************************************************************** * libs/libc/stdio/lib_open_memstream.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "libc.h" /**************************************************************************** * Private Types ****************************************************************************/ struct memstream_cookie_s { FAR char **buf; /* Memory buffer */ char saved; /* char at sizep before '\0' */ size_t *sizep; off_t size; /* Allocated buffer size */ off_t end; /* Maximum position we have written to */ off_t pos; /* Current position in the buffer */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: memstream_write ****************************************************************************/ static ssize_t memstream_write(FAR void *c, FAR const char *buf, size_t size) { FAR struct memstream_cookie_s *memstream_cookie = (FAR struct memstream_cookie_s *)c; FAR char *buf_grow; size_t new_size; if (memstream_cookie->pos + size + 1 > memstream_cookie->size) { /* We have to reallocate the buffer. */ new_size = memstream_cookie->pos + size + 1; buf_grow = lib_realloc(*memstream_cookie->buf, new_size); if (buf_grow == NULL) { return -ENOMEM; } memset(buf_grow + memstream_cookie->end, 0, new_size - memstream_cookie->size); *memstream_cookie->buf = buf_grow; memstream_cookie->size = new_size; } memcpy(*memstream_cookie->buf + memstream_cookie->pos, buf, size); /* POSIX: If a write moves the position to a value larger than * the current length, the current length shall be set to this position. * In this case a null character shall be appended to the current buffer. */ memstream_cookie->pos += size; if (memstream_cookie->pos > memstream_cookie->end) { memstream_cookie->end = memstream_cookie->pos; } else { memstream_cookie->saved = *(*memstream_cookie->buf + memstream_cookie->pos); } *memstream_cookie->sizep = memstream_cookie->pos; *(*memstream_cookie->buf + memstream_cookie->pos) = '\0'; return size; } /**************************************************************************** * Name: memstream_seek ****************************************************************************/ static off_t memstream_seek(FAR void *c, FAR off_t *offset, int whence) { FAR struct memstream_cookie_s *memstream_cookie = (FAR struct memstream_cookie_s *)c; off_t new_offset; switch (whence) { case SEEK_SET: new_offset = *offset; break; case SEEK_END: new_offset = memstream_cookie->end + *offset; break; case SEEK_CUR: new_offset = memstream_cookie->pos + *offset; break; default: set_errno(ENOTSUP); return -1; } /* Seek to negative value or value larger than maximum size shall fail. */ if (new_offset < 0 || new_offset > memstream_cookie->end) { set_errno(EINVAL); return -1; } if (memstream_cookie->pos < memstream_cookie->end) { /* Retrieve saved character if we painted in already written area. */ *(*memstream_cookie->buf + memstream_cookie->pos) = memstream_cookie->saved; } memstream_cookie->pos = new_offset; if (memstream_cookie->pos < memstream_cookie->end) { /* We go backwards, therefore we have to write null character * at memstream_cookie->pos. But we might want to keep this * character for future seeks, so keep it in memstream_cookie->saved. */ memstream_cookie->saved = *(*memstream_cookie->buf + memstream_cookie->pos); *(*memstream_cookie->buf + memstream_cookie->pos) = '\0'; *memstream_cookie->sizep = memstream_cookie->pos; } else { *memstream_cookie->sizep = memstream_cookie->end; } *offset = new_offset; return new_offset; } /**************************************************************************** * Name: memstream_close ****************************************************************************/ static int memstream_close(FAR void *c) { FAR struct memstream_cookie_s *memstream_cookie = (FAR struct memstream_cookie_s *)c; lib_free(memstream_cookie); return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ FAR FILE *open_memstream(FAR char **bufp, FAR size_t *sizep) { cookie_io_functions_t memstream_io; FAR struct memstream_cookie_s *memstream_cookie; FAR FILE *filep; if (bufp == NULL || sizep == NULL) { set_errno(EINVAL); return NULL; } memstream_cookie = lib_zalloc(sizeof(struct memstream_cookie_s)); if (memstream_cookie == NULL) { set_errno(ENOMEM); return NULL; } *bufp = NULL; *sizep = 0; memstream_cookie->buf = bufp; memstream_cookie->sizep = sizep; memstream_io.read = NULL; memstream_io.write = memstream_write; memstream_io.seek = memstream_seek; memstream_io.close = memstream_close; filep = fopencookie(memstream_cookie, "w", memstream_io); if (filep == NULL) { lib_free(memstream_cookie); return NULL; } return filep; }