1538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 2538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * Copyright (c) 2004i-2010 Alex Pankratov. All rights reserved. 3538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * 4538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * Hierarchical memory allocator, 1.2.1 5538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * http://swapped.cc/halloc 6538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 7538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 8538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 9538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * The program is distributed under terms of BSD license. 10538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * You can obtain the copy of the license by visiting: 11538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * 12538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * http://www.opensource.org/licenses/bsd-license.php 13538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 14538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 15538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#include <stdlib.h> /* realloc */ 16538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#include <string.h> /* memset & co */ 17538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 18538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#include "../halloc.h" 19538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#include "align.h" 20538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#include "hlist.h" 21538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 22538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 23538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * block control header 24538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 25538f6170b788de7408b06efc6613dc98579aa6a6Andreas Hubertypedef struct hblock 26538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 27538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#ifndef NDEBUG 28538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#define HH_MAGIC 0x20040518L 29538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber long magic; 30538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#endif 31538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_item_t siblings; /* 2 pointers */ 32538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_head_t children; /* 1 pointer */ 33538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber max_align_t data[1]; /* not allocated, see below */ 34538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 35538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} hblock_t; 36538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 37538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#define sizeof_hblock offsetof(hblock_t, data) 38538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 39538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 40538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * 41538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 42538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberrealloc_t halloc_allocator = NULL; 43538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 44538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#define allocator halloc_allocator 45538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 46538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 47538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * static methods 48538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 49538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic void _set_allocator(void); 50538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic void * _realloc(void * ptr, size_t n); 51538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 52538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic int _relate(hblock_t * b, hblock_t * p); 53538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic void _free_children(hblock_t * p); 54538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 55538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 56538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * Core API 57538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 58538f6170b788de7408b06efc6613dc98579aa6a6Andreas Hubervoid * halloc(void * ptr, size_t len) 59538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 60538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hblock_t * p; 61538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 62538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* set up default allocator */ 63538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! allocator) 64538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber { 65538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber _set_allocator(); 66538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(allocator); 67538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber } 68538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 69538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* calloc */ 70538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! ptr) 71538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber { 72538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! len) 73538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return NULL; 74538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 75538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber p = allocator(0, len + sizeof_hblock); 76538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! p) 77538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return NULL; 78538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#ifndef NDEBUG 79538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber p->magic = HH_MAGIC; 80538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#endif 81538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_init(&p->children); 82538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_init_item(&p->siblings); 83538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 84538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return p->data; 85538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber } 86538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 87538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber p = structof(ptr, hblock_t, data); 88538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(p->magic == HH_MAGIC); 89538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 90538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* realloc */ 91538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (len) 92538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber { 93538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber p = allocator(p, len + sizeof_hblock); 94538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! p) 95538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return NULL; 96538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 97538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_relink(&p->siblings); 98538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_relink_head(&p->children); 99538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 100538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return p->data; 101538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber } 102538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 103538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* free */ 104538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber _free_children(p); 105538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_del(&p->siblings); 106538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber allocator(p, 0); 107538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 108538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return NULL; 109538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 110538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 111538f6170b788de7408b06efc6613dc98579aa6a6Andreas Hubervoid hattach(void * block, void * parent) 112538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 113538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hblock_t * b, * p; 114538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 115538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! block) 116538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber { 117538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(! parent); 118538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return; 119538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber } 120538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 121538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* detach */ 122538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber b = structof(block, hblock_t, data); 123538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(b->magic == HH_MAGIC); 124538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 125538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_del(&b->siblings); 126538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 127538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! parent) 128538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return; 129538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 130538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* attach */ 131538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber p = structof(parent, hblock_t, data); 132538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(p->magic == HH_MAGIC); 133538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 134538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* sanity checks */ 135538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(b != p); /* trivial */ 136538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(! _relate(p, b)); /* heavy ! */ 137538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 138538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_add(&p->children, &b->siblings); 139538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 140538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 141538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 142538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * malloc/free api 143538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 144538f6170b788de7408b06efc6613dc98579aa6a6Andreas Hubervoid * h_malloc(size_t len) 145538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 146538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return halloc(0, len); 147538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 148538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 149538f6170b788de7408b06efc6613dc98579aa6a6Andreas Hubervoid * h_calloc(size_t n, size_t len) 150538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 151538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber void * ptr = halloc(0, len*=n); 152538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return ptr ? memset(ptr, 0, len) : NULL; 153538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 154538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 155538f6170b788de7408b06efc6613dc98579aa6a6Andreas Hubervoid * h_realloc(void * ptr, size_t len) 156538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 157538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return halloc(ptr, len); 158538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 159538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 160538f6170b788de7408b06efc6613dc98579aa6a6Andreas Hubervoid h_free(void * ptr) 161538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 162538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber halloc(ptr, 0); 163538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 164538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 165538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberchar * h_strdup(const char * str) 166538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 167538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber size_t len = strlen(str); 168538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber char * ptr = halloc(0, len + 1); 169538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return ptr ? (ptr[len] = 0, memcpy(ptr, str, len)) : NULL; 170538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 171538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 172538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber/* 173538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * static stuff 174538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 175538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic void _set_allocator(void) 176538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 177538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber void * p; 178538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(! allocator); 179538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 180538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* 181538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * the purpose of the test below is to check the behaviour 182538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * of realloc(ptr, 0), which is defined in the standard 183538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * as an implementation-specific. if it returns zero, 184538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * then it's equivalent to free(). it can however return 185538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * non-zero, in which case it cannot be used for freeing 186538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * memory blocks and we'll need to supply our own version 187538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * 188538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * Thanks to Stan Tobias for pointing this tricky part out. 189538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 190538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber allocator = realloc; 191538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (! (p = malloc(1))) 192538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* hmm */ 193538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return; 194538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 195538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if ((p = realloc(p, 0))) 196538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber { 197538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* realloc cannot be used as free() */ 198538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber allocator = _realloc; 199538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber free(p); 200538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber } 201538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 202538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 203538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic void * _realloc(void * ptr, size_t n) 204538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 205538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* 206538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * free'ing realloc() 207538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 208538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (n) 209538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return realloc(ptr, n); 210538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber free(ptr); 211538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return NULL; 212538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 213538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 214538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic int _relate(hblock_t * b, hblock_t * p) 215538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 216538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_item_t * i; 217538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 218538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (!b || !p) 219538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return 0; 220538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 221538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* 222538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * since there is no 'parent' pointer, which would've allowed 223538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * O(log(n)) upward traversal, the check must use O(n) downward 224538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * iteration of the entire hierarchy; and this can be VERY SLOW 225538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 226538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_for_each(i, &p->children) 227538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber { 228538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hblock_t * q = structof(i, hblock_t, siblings); 229538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber if (q == b || _relate(b, q)) 230538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return 1; 231538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber } 232538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber return 0; 233538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 234538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 235538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huberstatic void _free_children(hblock_t * p) 236538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber{ 237538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_item_t * i, * tmp; 238538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 239538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#ifndef NDEBUG 240538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber /* 241538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * this catches loops in hierarchy with almost zero 242538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber * overhead (compared to _relate() running time) 243538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber */ 244538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber assert(p && p->magic == HH_MAGIC); 245538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber p->magic = 0; 246538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber#endif 247538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hlist_for_each_safe(i, tmp, &p->children) 248538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber { 249538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber hblock_t * q = structof(i, hblock_t, siblings); 250538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber _free_children(q); 251538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber allocator(q, 0); 252538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber } 253538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber} 254538f6170b788de7408b06efc6613dc98579aa6a6Andreas Huber 255