diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/Directory.cxx | 15 | ||||
-rw-r--r-- | src/Directory.hxx | 4 | ||||
-rw-r--r-- | src/util/VarSize.hxx | 84 |
4 files changed, 94 insertions, 10 deletions
diff --git a/Makefile.am b/Makefile.am index 3541714c0..f59adabfd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -251,6 +251,7 @@ libutil_a_SOURCES = \ src/util/Macros.hxx \ src/util/Cast.hxx \ src/util/Clamp.hxx \ + src/util/VarSize.hxx \ src/util/Error.cxx src/util/Error.hxx \ src/util/Domain.hxx \ src/util/ReusableArray.hxx \ diff --git a/src/Directory.cxx b/src/Directory.cxx index 20fb44618..f9c1e2ba4 100644 --- a/src/Directory.cxx +++ b/src/Directory.cxx @@ -25,6 +25,7 @@ #include "SongSort.hxx" #include "Song.hxx" #include "fs/Traits.hxx" +#include "util/VarSize.hxx" #include "util/Error.hxx" extern "C" { @@ -42,14 +43,9 @@ Directory::Allocate(const char *path) { assert(path != nullptr); - const size_t path_size = strlen(path) + 1; - Directory *directory = - (Directory *)g_malloc(sizeof(*directory) - - sizeof(directory->path) - + path_size); - new(directory) Directory(path); - - return directory; + return NewVarSize<Directory>(sizeof(Directory::path), + strlen(path) + 1, + path); } Directory::Directory() @@ -96,8 +92,7 @@ Directory::NewGeneric(const char *path, Directory *parent) void Directory::Free() { - this->Directory::~Directory(); - g_free(this); + DeleteVarSize(this); } void diff --git a/src/Directory.hxx b/src/Directory.hxx index 380a6b790..1ef4693e2 100644 --- a/src/Directory.hxx +++ b/src/Directory.hxx @@ -49,6 +49,10 @@ class SongFilter; class Error; struct Directory { + template<class T, typename... Args> friend T * + NewVarSize(size_t declared_tail_size, size_t real_tail_size, + Args&&... args); + /** * Pointers to the siblings of this directory within the * parent directory. It is unused (undefined) in the root diff --git a/src/util/VarSize.hxx b/src/util/VarSize.hxx new file mode 100644 index 000000000..b1123b858 --- /dev/null +++ b/src/util/VarSize.hxx @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008-2014 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MPD_VAR_SIZE_HXX +#define MPD_VAR_SIZE_HXX + +#include "Compiler.h" + +#include <type_traits> +#include <utility> +#include <new> + +#include <glib.h> + +/** + * Allocate and construct a variable-size object. That is useful for + * example when you want to store a variable-length string as the last + * attribute without the overhead of a second allocation. + * + * @param T a struct/class with a variable-size last attribute + * @param declared_tail_size the declared size of the last element in + * #T + * @param real_tail_size the real required size of the last element in + * #T + */ +template<class T, typename... Args> +gcc_malloc +T * +NewVarSize(size_t declared_tail_size, size_t real_tail_size, Args&&... args) +{ + static_assert(std::is_standard_layout<T>::value, + "Not standard-layout"); + + /* determine the total size of this instance */ + size_t size = sizeof(T) - declared_tail_size + real_tail_size; + + /* allocate memory */ + T *instance = (T *)g_malloc(size); + + /* call the constructor */ + new(instance) T(std::forward<Args>(args)...); + + return instance; +} + +template<typename T> +gcc_nonnull_all +void +DeleteVarSize(T *instance) +{ + /* call the destructor */ + instance->T::~T(); + + /* free memory */ + g_free(instance); +} + +#endif |