github.com/afumu/libc@v0.0.6/musl/src/malloc/mallocng/meta.h (about)

     1  #ifndef MALLOC_META_H
     2  #define MALLOC_META_H
     3  
     4  #include <stdint.h>
     5  #include <errno.h>
     6  #include <limits.h>
     7  #include "glue.h"
     8  
     9  __attribute__((__visibility__("hidden")))
    10  extern const uint16_t size_classes[];
    11  
    12  #define MMAP_THRESHOLD 131052
    13  
    14  #define UNIT 16
    15  #define IB 4
    16  
    17  struct group {
    18  	struct meta *meta;
    19  	unsigned char active_idx:5;
    20  	char pad[UNIT - sizeof(struct meta *) - 1];
    21  	unsigned char storage[];
    22  };
    23  
    24  struct meta {
    25  	struct meta *prev, *next;
    26  	struct group *mem;
    27  	volatile int avail_mask, freed_mask;
    28  	uintptr_t last_idx:5;
    29  	uintptr_t freeable:1;
    30  	uintptr_t sizeclass:6;
    31  	uintptr_t maplen:8*sizeof(uintptr_t)-12;
    32  };
    33  
    34  struct meta_area {
    35  	uint64_t check;
    36  	struct meta_area *next;
    37  	int nslots;
    38  	struct meta slots[];
    39  };
    40  
    41  struct malloc_context {
    42  	uint64_t secret;
    43  #ifndef PAGESIZE
    44  	size_t pagesize;
    45  #endif
    46  	int init_done;
    47  	unsigned mmap_counter;
    48  	struct meta *free_meta_head;
    49  	struct meta *avail_meta;
    50  	size_t avail_meta_count, avail_meta_area_count, meta_alloc_shift;
    51  	struct meta_area *meta_area_head, *meta_area_tail;
    52  	unsigned char *avail_meta_areas;
    53  	struct meta *active[48];
    54  	size_t usage_by_class[48];
    55  	uint8_t unmap_seq[32], bounces[32];
    56  	uint8_t seq;
    57  	uintptr_t brk;
    58  };
    59  
    60  __attribute__((__visibility__("hidden")))
    61  extern struct malloc_context ctx;
    62  
    63  #ifdef PAGESIZE
    64  #define PGSZ PAGESIZE
    65  #else
    66  #define PGSZ ctx.pagesize
    67  #endif
    68  
    69  __attribute__((__visibility__("hidden")))
    70  struct meta *alloc_meta(void);
    71  
    72  __attribute__((__visibility__("hidden")))
    73  int is_allzero(void *);
    74  
    75  static inline void queue(struct meta **phead, struct meta *m)
    76  {
    77  	assert(!m->next);
    78  	assert(!m->prev);
    79  	if (*phead) {
    80  		struct meta *head = *phead;
    81  		m->next = head;
    82  		m->prev = head->prev;
    83  		m->next->prev = m->prev->next = m;
    84  	} else {
    85  		m->prev = m->next = m;
    86  		*phead = m;
    87  	}
    88  }
    89  
    90  static inline void dequeue(struct meta **phead, struct meta *m)
    91  {
    92  	if (m->next != m) {
    93  		m->prev->next = m->next;
    94  		m->next->prev = m->prev;
    95  		if (*phead == m) *phead = m->next;
    96  	} else {
    97  		*phead = 0;
    98  	}
    99  	m->prev = m->next = 0;
   100  }
   101  
   102  static inline struct meta *dequeue_head(struct meta **phead)
   103  {
   104  	struct meta *m = *phead;
   105  	if (m) dequeue(phead, m);
   106  	return m;
   107  }
   108  
   109  static inline void free_meta(struct meta *m)
   110  {
   111  	*m = (struct meta){0};
   112  	queue(&ctx.free_meta_head, m);
   113  }
   114  
   115  static inline uint32_t activate_group(struct meta *m)
   116  {
   117  	assert(!m->avail_mask);
   118  	uint32_t mask, act = (2u<<m->mem->active_idx)-1;
   119  	do mask = m->freed_mask;
   120  	while (a_cas(&m->freed_mask, mask, mask&~act)!=mask);
   121  	return m->avail_mask = mask & act;
   122  }
   123  
   124  static inline int get_slot_index(const unsigned char *p)
   125  {
   126  	return p[-3] & 31;
   127  }
   128  
   129  static inline struct meta *get_meta(const unsigned char *p)
   130  {
   131  	assert(!((uintptr_t)p & 15));
   132  	int offset = *(const uint16_t *)(p - 2);
   133  	int index = get_slot_index(p);
   134  	if (p[-4]) {
   135  		assert(!offset);
   136  		offset = *(uint32_t *)(p - 8);
   137  		assert(offset > 0xffff);
   138  	}
   139  	const struct group *base = (const void *)(p - UNIT*offset - UNIT);
   140  	const struct meta *meta = base->meta;
   141  	assert(meta->mem == base);
   142  	assert(index <= meta->last_idx);
   143  	assert(!(meta->avail_mask & (1u<<index)));
   144  	assert(!(meta->freed_mask & (1u<<index)));
   145  	const struct meta_area *area = (void *)((uintptr_t)meta & -4096);
   146  	assert(area->check == ctx.secret);
   147  	if (meta->sizeclass < 48) {
   148  		assert(offset >= size_classes[meta->sizeclass]*index);
   149  		assert(offset < size_classes[meta->sizeclass]*(index+1));
   150  	} else {
   151  		assert(meta->sizeclass == 63);
   152  	}
   153  	if (meta->maplen) {
   154  		assert(offset <= meta->maplen*4096UL/UNIT - 1);
   155  	}
   156  	return (struct meta *)meta;
   157  }
   158  
   159  static inline size_t get_nominal_size(const unsigned char *p, const unsigned char *end)
   160  {
   161  	size_t reserved = p[-3] >> 5;
   162  	if (reserved >= 5) {
   163  		assert(reserved == 5);
   164  		reserved = *(const uint32_t *)(end-4);
   165  		assert(reserved >= 5);
   166  		assert(!end[-5]);
   167  	}
   168  	assert(reserved <= end-p);
   169  	assert(!*(end-reserved));
   170  	// also check the slot's overflow byte
   171  	assert(!*end);
   172  	return end-reserved-p;
   173  }
   174  
   175  static inline size_t get_stride(const struct meta *g)
   176  {
   177  	if (!g->last_idx && g->maplen) {
   178  		return g->maplen*4096UL - UNIT;
   179  	} else {
   180  		return UNIT*size_classes[g->sizeclass];
   181  	}
   182  }
   183  
   184  static inline void set_size(unsigned char *p, unsigned char *end, size_t n)
   185  {
   186  	int reserved = end-p-n;
   187  	if (reserved) end[-reserved] = 0;
   188  	if (reserved >= 5) {
   189  		*(uint32_t *)(end-4) = reserved;
   190  		end[-5] = 0;
   191  		reserved = 5;
   192  	}
   193  	p[-3] = (p[-3]&31) + (reserved<<5);
   194  }
   195  
   196  static inline void *enframe(struct meta *g, int idx, size_t n, int ctr)
   197  {
   198  	size_t stride = get_stride(g);
   199  	size_t slack = (stride-IB-n)/UNIT;
   200  	unsigned char *p = g->mem->storage + stride*idx;
   201  	unsigned char *end = p+stride-IB;
   202  	// cycle offset within slot to increase interval to address
   203  	// reuse, facilitate trapping double-free.
   204  	int off = (p[-3] ? *(uint16_t *)(p-2) + 1 : ctr) & 255;
   205  	assert(!p[-4]);
   206  	if (off > slack) {
   207  		size_t m = slack;
   208  		m |= m>>1; m |= m>>2; m |= m>>4;
   209  		off &= m;
   210  		if (off > slack) off -= slack+1;
   211  		assert(off <= slack);
   212  	}
   213  	if (off) {
   214  		// store offset in unused header at offset zero
   215  		// if enframing at non-zero offset.
   216  		*(uint16_t *)(p-2) = off;
   217  		p[-3] = 7<<5;
   218  		p += UNIT*off;
   219  		// for nonzero offset there is no permanent check
   220  		// byte, so make one.
   221  		p[-4] = 0;
   222  	}
   223  	*(uint16_t *)(p-2) = (size_t)(p-g->mem->storage)/UNIT;
   224  	p[-3] = idx;
   225  	set_size(p, end, n);
   226  	return p;
   227  }
   228  
   229  static inline int size_to_class(size_t n)
   230  {
   231  	n = (n+IB-1)>>4;
   232  	if (n<10) return n;
   233  	n++;
   234  	int i = (28-a_clz_32(n))*4 + 8;
   235  	if (n>size_classes[i+1]) i+=2;
   236  	if (n>size_classes[i]) i++;
   237  	return i;
   238  }
   239  
   240  static inline int size_overflows(size_t n)
   241  {
   242  	if (n >= SIZE_MAX/2 - 4096) {
   243  		errno = ENOMEM;
   244  		return 1;
   245  	}
   246  	return 0;
   247  }
   248  
   249  static inline void step_seq(void)
   250  {
   251  	if (ctx.seq==255) {
   252  		for (int i=0; i<32; i++) ctx.unmap_seq[i] = 0;
   253  		ctx.seq = 1;
   254  	} else {
   255  		ctx.seq++;
   256  	}
   257  }
   258  
   259  static inline void record_seq(int sc)
   260  {
   261  	if (sc-7U < 32) ctx.unmap_seq[sc-7] = ctx.seq;
   262  }
   263  
   264  static inline void account_bounce(int sc)
   265  {
   266  	if (sc-7U < 32) {
   267  		int seq = ctx.unmap_seq[sc-7];
   268  		if (seq && ctx.seq-seq < 10) {
   269  			if (ctx.bounces[sc-7]+1 < 100)
   270  				ctx.bounces[sc-7]++;
   271  			else
   272  				ctx.bounces[sc-7] = 150;
   273  		}
   274  	}
   275  }
   276  
   277  static inline void decay_bounces(int sc)
   278  {
   279  	if (sc-7U < 32 && ctx.bounces[sc-7])
   280  		ctx.bounces[sc-7]--;
   281  }
   282  
   283  static inline int is_bouncing(int sc)
   284  {
   285  	return (sc-7U < 32 && ctx.bounces[sc-7] >= 100);
   286  }
   287  
   288  #endif