Blame include/scratch_buffer.h

Packit 6c4009
/* Variable-sized buffer with on-stack default allocation.
Packit 6c4009
   Copyright (C) 2015-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#ifndef _SCRATCH_BUFFER_H
Packit 6c4009
#define _SCRATCH_BUFFER_H
Packit 6c4009
Packit 6c4009
/* Scratch buffers with a default stack allocation and fallback to
Packit 6c4009
   heap allocation.  It is expected that this function is used in this
Packit 6c4009
   way:
Packit 6c4009
Packit 6c4009
     struct scratch_buffer tmpbuf;
Packit 6c4009
     scratch_buffer_init (&tmpbuf);
Packit 6c4009
Packit 6c4009
     while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
Packit 6c4009
       if (!scratch_buffer_grow (&tmpbuf))
Packit 6c4009
	 return -1;
Packit 6c4009
Packit 6c4009
     scratch_buffer_free (&tmpbuf);
Packit 6c4009
     return 0;
Packit 6c4009
Packit 6c4009
   The allocation functions (scratch_buffer_grow,
Packit 6c4009
   scratch_buffer_grow_preserve, scratch_buffer_set_array_size) make
Packit 6c4009
   sure that the heap allocation, if any, is freed, so that the code
Packit 6c4009
   above does not have a memory leak.  The buffer still remains in a
Packit 6c4009
   state that can be deallocated using scratch_buffer_free, so a loop
Packit 6c4009
   like this is valid as well:
Packit 6c4009
Packit 6c4009
     struct scratch_buffer tmpbuf;
Packit 6c4009
     scratch_buffer_init (&tmpbuf);
Packit 6c4009
Packit 6c4009
     while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
Packit 6c4009
       if (!scratch_buffer_grow (&tmpbuf))
Packit 6c4009
	 break;
Packit 6c4009
Packit 6c4009
     scratch_buffer_free (&tmpbuf);
Packit 6c4009
Packit 6c4009
   scratch_buffer_grow and scratch_buffer_grow_preserve are guaranteed
Packit 6c4009
   to grow the buffer by at least 512 bytes.  This means that when
Packit 6c4009
   using the scratch buffer as a backing store for a non-character
Packit 6c4009
   array whose element size, in bytes, is 512 or smaller, the scratch
Packit 6c4009
   buffer only has to grow once to make room for at least one more
Packit 6c4009
   element.
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stddef.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
Packit 6c4009
/* Scratch buffer.  Must be initialized with scratch_buffer_init
Packit 6c4009
   before its use.  */
Packit 6c4009
struct scratch_buffer {
Packit 6c4009
  void *data;    /* Pointer to the beginning of the scratch area.  */
Packit 6c4009
  size_t length; /* Allocated space at the data pointer, in bytes.  */
Packit 6c4009
  union { max_align_t __align; char __c[1024]; } __space;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Initializes *BUFFER so that BUFFER->data points to BUFFER->__space
Packit 6c4009
   and BUFFER->length reflects the available space.  */
Packit 6c4009
static inline void
Packit 6c4009
scratch_buffer_init (struct scratch_buffer *buffer)
Packit 6c4009
{
Packit 6c4009
  buffer->data = buffer->__space.__c;
Packit 6c4009
  buffer->length = sizeof (buffer->__space);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Deallocates *BUFFER (if it was heap-allocated).  */
Packit 6c4009
static inline void
Packit 6c4009
scratch_buffer_free (struct scratch_buffer *buffer)
Packit 6c4009
{
Packit 6c4009
  if (buffer->data != buffer->__space.__c)
Packit 6c4009
    free (buffer->data);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Grow *BUFFER by some arbitrary amount.  The buffer contents is NOT
Packit 6c4009
   preserved.  Return true on success, false on allocation failure (in
Packit 6c4009
   which case the old buffer is freed).  On success, the new buffer is
Packit 6c4009
   larger than the previous size.  On failure, *BUFFER is deallocated,
Packit 6c4009
   but remains in a free-able state, and errno is set.  */
Packit 6c4009
bool __libc_scratch_buffer_grow (struct scratch_buffer *buffer);
Packit 6c4009
libc_hidden_proto (__libc_scratch_buffer_grow)
Packit 6c4009
Packit 6c4009
/* Alias for __libc_scratch_buffer_grow.  */
Packit 6c4009
static __always_inline bool
Packit 6c4009
scratch_buffer_grow (struct scratch_buffer *buffer)
Packit 6c4009
{
Packit 6c4009
  return __glibc_likely (__libc_scratch_buffer_grow (buffer));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Like __libc_scratch_buffer_grow, but preserve the old buffer
Packit 6c4009
   contents on success, as a prefix of the new buffer.  */
Packit 6c4009
bool __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer);
Packit 6c4009
libc_hidden_proto (__libc_scratch_buffer_grow_preserve)
Packit 6c4009
Packit 6c4009
/* Alias for __libc_scratch_buffer_grow_preserve.  */
Packit 6c4009
static __always_inline bool
Packit 6c4009
scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
Packit 6c4009
{
Packit 6c4009
  return __glibc_likely (__libc_scratch_buffer_grow_preserve (buffer));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Grow *BUFFER so that it can store at least NELEM elements of SIZE
Packit 6c4009
   bytes.  The buffer contents are NOT preserved.  Both NELEM and SIZE
Packit 6c4009
   can be zero.  Return true on success, false on allocation failure
Packit 6c4009
   (in which case the old buffer is freed, but *BUFFER remains in a
Packit 6c4009
   free-able state, and errno is set).  It is unspecified whether this
Packit 6c4009
   function can reduce the array size.  */
Packit 6c4009
bool __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
Packit 6c4009
					   size_t nelem, size_t size);
Packit 6c4009
libc_hidden_proto (__libc_scratch_buffer_set_array_size)
Packit 6c4009
Packit 6c4009
/* Alias for __libc_scratch_set_array_size.  */
Packit 6c4009
static __always_inline bool
Packit 6c4009
scratch_buffer_set_array_size (struct scratch_buffer *buffer,
Packit 6c4009
			       size_t nelem, size_t size)
Packit 6c4009
{
Packit 6c4009
  return __glibc_likely (__libc_scratch_buffer_set_array_size
Packit 6c4009
			 (buffer, nelem, size));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#endif /* _SCRATCH_BUFFER_H */