1#ifndef _GNU_SOURCE
2# define _GNU_SOURCE
3#endif
4
5#include <stdint.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/mman.h>
10
11#include "alignment.h"
12#include "mmarray.h"
13
14typedef struct {
15 unsigned capacity;
16 unsigned max_capacity;
17 unsigned length;
18 unsigned item_size;
19} ArrayHeader;
20
21
22[[nodiscard]] static size_t calc_memsize(unsigned capacity, unsigned item_size)
23{
24 return align_unsigned_to_page(sizeof(ArrayHeader) + ((size_t) capacity) * item_size);
25}
26
27[[nodiscard]] static inline ArrayHeader* get_array_header(void* array)
28{
29 return (ArrayHeader*) (
30 ((ptrdiff_t) array) & ~(ptrdiff_t) (sys_page_size() - 1) // header starts on page boundary
31 );
32}
33
34[[nodiscard]] void* mmarray_allocate(unsigned max_capacity, unsigned length, unsigned item_size)
35{
36 ArrayHeader* a;
37 size_t memsize;
38
39 if (max_capacity) {
40 // capped array
41 // reserve address space
42 memsize = calc_memsize(max_capacity, item_size);
43 a = mmap(NULL, memsize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
44 if (a == MAP_FAILED) {
45 perror("mmap");
46 abort();
47 }
48 // commit pages for `length` items
49 memsize = calc_memsize(length, item_size);
50 if (mprotect(a, memsize, PROT_READ | PROT_WRITE)) {
51 perror("mprotect");
52 abort();
53 }
54 } else {
55 // relocatable array
56 // allocate pages
57 memsize = calc_memsize(length, item_size);
58 a = mmap(NULL, memsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
59 if (a == MAP_FAILED) {
60 perror("mmap");
61 abort();
62 }
63 }
64 a->capacity = (memsize - sizeof(ArrayHeader)) / item_size;
65 if (a->capacity == 0) {
66 fprintf(stderr, "mmarray_allocate: item size %u too big\n", item_size);
67 abort();
68 }
69 a->max_capacity = max_capacity;
70 a->length = length;
71 a->item_size = item_size;
72
73 // return pointer to the array data
74 return a + 1;
75}
76
77[[nodiscard]] void* mmarray_grow(void* array, unsigned increment)
78{
79 ArrayHeader* a = get_array_header(array);
80
81 if (a->length + increment > a->capacity) {
82
83 size_t old_memsize = calc_memsize(a->capacity, a->item_size);
84 size_t new_memsize = calc_memsize(a->length + increment, a->item_size);
85
86 if (a->max_capacity) {
87 if (a->length + increment > a->max_capacity) {
88 fprintf(stderr, "mmarray_grow: reached maximum capacity of %u items\n", a->max_capacity);
89 abort();
90 }
91 if (mprotect(((uint8_t*) a) + old_memsize, new_memsize - old_memsize, PROT_READ | PROT_WRITE)) {
92 perror("mprotect");
93 abort();
94 }
95 } else {
96 ArrayHeader* new_array = mremap(a, old_memsize, new_memsize, MREMAP_MAYMOVE);
97 if (new_array == MAP_FAILED) {
98 perror("mremap");
99 abort();
100 }
101 a = new_array;
102 a->capacity = (new_memsize - sizeof(ArrayHeader)) / a->item_size;
103 }
104 }
105 a->length += increment;
106
107 // return pointer to the array data
108 return a + 1;
109}
110
111void mmarray_reset(void* array, unsigned new_item_size)
112{
113 ArrayHeader* a = get_array_header(array);
114
115 size_t memsize = calc_memsize(a->capacity, a->item_size);
116 a->capacity = (memsize - sizeof(ArrayHeader)) / new_item_size;
117 if (a->capacity == 0) {
118 fprintf(stderr, "mmarray_reset: new item size %u too big\n", new_item_size);
119 abort();
120 }
121 a->length = 0;
122 a->item_size = new_item_size;
123}
124
125void mmarray_free(void* array)
126{
127 ArrayHeader* a = get_array_header(array);
128 size_t memsize;
129
130 if (a->max_capacity) {
131 memsize = calc_memsize(a->max_capacity, a->item_size);
132 } else {
133 memsize = calc_memsize(a->capacity, a->item_size);
134 }
135 if (munmap(a, memsize) == -1) {
136 perror("munmap");
137 }
138}
139
140[[nodiscard]] void* mmarray_append_item(void* array, void* item)
141{
142 unsigned index = mmarray_length(array);
143
144 array = mmarray_grow(array, 1);
145 ArrayHeader* a = get_array_header(array);
146
147 memcpy(((char*) array) + (index * a->item_size), item, a->item_size);
148
149 // return pointer to the array data
150 return a + 1;
151}
152
153[[nodiscard]] unsigned mmarray_length(void* array)
154{
155 return get_array_header(array)->length;
156}
157
158[[nodiscard]] unsigned mmarray_capacity(void* array)
159{
160 return get_array_header(array)->capacity;
161}