Blame lib/malloc/scratch_buffer.h

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