/*
Copyright (C) 2008-2015 Jaroslav Hajek
This file is part of Octave.
Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.
Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING. If not, see
.
*/
#if !defined (octave_oct_locbuf_h)
#define octave_oct_locbuf_h 1
#include
#include "oct-cmplx.h"
// The default local buffer simply encapsulates an *array* pointer
// that gets deleted automatically. For common POD types, we provide
// specializations.
template
class octave_local_buffer
{
public:
octave_local_buffer (size_t size)
: data (0)
{
if (size)
data = new T [size];
}
~octave_local_buffer (void) { delete [] data; }
operator T *() const { return data; }
private:
T *data;
// No copying!
octave_local_buffer (const octave_local_buffer&);
octave_local_buffer& operator = (const octave_local_buffer&);
};
// For buffers of POD types, we'll be smarter. There is one thing
// that differentiates a local buffer from a dynamic array - the local
// buffers, if not manipulated improperly, have a FIFO semantics,
// meaning that if buffer B is allocated after buffer A, B *must* be
// deallocated before A. This is *guaranteed* if you use local buffer
// exclusively through the OCTAVE_LOCAL_BUFFER macro, because the C++
// standard requires that explicit local objects be destroyed in
// reverse order of declaration. Therefore, we can avoid memory
// fragmentation by allocating fairly large chunks of memory and
// serving local buffers from them in a stack-like manner. The first
// returning buffer in previous chunk will be responsible for
// deallocating the chunk.
class octave_chunk_buffer
{
public:
OCTAVE_API octave_chunk_buffer (size_t size);
OCTAVE_API virtual ~octave_chunk_buffer (void);
char *data (void) const { return dat; }
static OCTAVE_API void clear (void);
private:
// The number of bytes we allocate for each large chunk of memory we
// manage.
static const size_t chunk_size;
// Pointer to the end end of the last allocation.
static char *top;
// Pointer to the current active chunk.
static char *chunk;
// The number of bytes remaining in the active chunk.
static size_t left;
// The number of active allocations.
static size_t active;
// Pointer to the current chunk.
char *cnk;
// Pointer to the beginning of the most recent allocation.
char *dat;
// No copying!
octave_chunk_buffer (const octave_chunk_buffer&);
octave_chunk_buffer& operator = (const octave_chunk_buffer&);
};
// This specializes octave_local_buffer to use the chunked buffer
// mechanism for POD types.
#define SPECIALIZE_POD_BUFFER(TYPE) \
template <> \
class octave_local_buffer : private octave_chunk_buffer \
{ \
public: \
octave_local_buffer (size_t size) \
: octave_chunk_buffer (size * sizeof (TYPE)) { } \
\
operator TYPE *() const \
{ \
return reinterpret_cast (this->data ()); \
} \
}
SPECIALIZE_POD_BUFFER (bool);
SPECIALIZE_POD_BUFFER (char);
SPECIALIZE_POD_BUFFER (unsigned short);
SPECIALIZE_POD_BUFFER (short);
SPECIALIZE_POD_BUFFER (int);
SPECIALIZE_POD_BUFFER (unsigned int);
SPECIALIZE_POD_BUFFER (long);
SPECIALIZE_POD_BUFFER (unsigned long);
SPECIALIZE_POD_BUFFER (float);
SPECIALIZE_POD_BUFFER (double);
// FIXME: Are these guaranteed to be POD and satisfy alignment?
SPECIALIZE_POD_BUFFER (Complex);
SPECIALIZE_POD_BUFFER (FloatComplex);
// MORE ?
// All pointers and const pointers are also POD types.
template
class octave_local_buffer : private octave_chunk_buffer
{
public:
octave_local_buffer (size_t size)
: octave_chunk_buffer (size * sizeof (T *))
{ }
operator T **() const { return reinterpret_cast (this->data ()); }
};
template
class octave_local_buffer : private octave_chunk_buffer
{
public:
octave_local_buffer (size_t size)
: octave_chunk_buffer (size * sizeof (const T *))
{ }
operator const T **() const
{
return reinterpret_cast (this->data ());
}
};
// If the compiler supports dynamic stack arrays, we can use the
// attached hack to place small buffer arrays on the stack. It may be
// even faster than our obstack-like optimization, but is dangerous
// because stack is a very limited resource, so we disable it.
#if 0 // defined (HAVE_DYNAMIC_AUTO_ARRAYS)
// Maximum buffer size (in bytes) to be placed on the stack.
#define OCTAVE_LOCAL_BUFFER_MAX_STACK_SIZE 8192
// If we have automatic arrays, we use an automatic array if the size
// is small enough. To avoid possibly evaluating 'size' multiple
// times, we first cache it. Note that we always construct both the
// stack array and the octave_local_buffer object, but only one of
// them will be nonempty.
#define OCTAVE_LOCAL_BUFFER(T, buf, size) \
const size_t _bufsize_ ## buf = size; \
const bool _lbufaut_ ## buf = _bufsize_ ## buf * sizeof (T) \
<= OCTAVE_LOCAL_BUFFER_MAX_STACK_SIZE; \
T _bufaut_ ## buf [_lbufaut_ ## buf ? _bufsize_ ## buf : 0]; \
octave_local_buffer _bufheap_ ## buf \
(!_lbufaut_ ## buf ? _bufsize_ ## buf : 0); \
T *buf = _lbufaut_ ## buf \
? _bufaut_ ## buf : static_cast (_bufheap_ ## buf)
#else
// If we don't have automatic arrays, we simply always use
// octave_local_buffer.
#define OCTAVE_LOCAL_BUFFER(T, buf, size) \
octave_local_buffer _buffer_ ## buf (size); \
T *buf = _buffer_ ## buf
#endif
// Note: we use weird variables in the for loop to avoid warnings
// about shadowed parameters.
#define OCTAVE_LOCAL_BUFFER_INIT(T, buf, size, value) \
OCTAVE_LOCAL_BUFFER (T, buf, size); \
for (size_t _buf_iter = 0, _buf_size = size; \
_buf_iter < _buf_size; _buf_iter++) \
buf[_buf_iter] = value
#endif