1 /* Jitter: allocated heap memory blocks.
2 
3    Copyright (C) 2020 Luca Saiu
4    Written by Luca Saiu
5 
6    This file is part of Jitter.
7 
8    Jitter is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    Jitter is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with Jitter.  If not, see <http://www.gnu.org/licenses/>. */
20 
21 
22 #include <jitter/jitter-aligned-block.h>
23 #include <jitter/jitter-bitwise.h>
24 #include <jitter/jitter-fatal.h>
25 
26 /* Include the right header, according to the selected implementation. */
27 #if defined (JITTER_ALIGNED_BLOCK_USE_MMAP)
28 # include <unistd.h>   /* For sysconf . */
29 # include <sys/mman.h> /* For mmap and munmap . */
30 #elif defined (JITTER_ALIGNED_BLOCK_USE_ALIGNED_ALLOC)
31 # include <stdlib.h>
32 #elif defined (JITTER_ALIGNED_BLOCK_USE_POSIX_MEMALIGN)
33 # include <stdlib.h>
34 #elif defined (JITTER_ALIGNED_BLOCK_USE_FALLBACK)
35 # include <jitter/jitter-malloc.h>
36 #else
37 # error "no aligned block implementation defined.  This should never happen."
38 #endif
39 
40 
41 
42 
43 /* Aligned block allocation and destruction.
44  * ************************************************************************** */
45 
46 void *
jitter_aligned_block_make(jitter_aligned_block_id * id,size_t alignment_in_bytes,size_t size_in_bytes)47 jitter_aligned_block_make (jitter_aligned_block_id *id,
48                            size_t alignment_in_bytes, size_t size_in_bytes)
49 {
50   void *res;
51 
52   /* The mmap and malloc implementations share the strategy of allocating a
53      larger block guaranteed to contain an aligned block inside. */
54 #if defined (JITTER_ALIGNED_BLOCK_USE_MMAP)         \
55     || defined (JITTER_ALIGNED_BLOCK_USE_FALLBACK)
56   /* Allocate a larger buffer which is guaranteed to contain an aligned buffer
57      of the required size as a sub-buffer.  Keep a pointer to the initial
58      buffer, in order to be able to free it later, in the block id. */
59   size_t allocated_size_in_bytes;
60   if (size_in_bytes < alignment_in_bytes)
61     allocated_size_in_bytes = alignment_in_bytes * 2;
62   else
63     allocated_size_in_bytes = size_in_bytes * 2;
64   void *initial_pointer;
65 #endif
66 
67 #if defined (JITTER_ALIGNED_BLOCK_USE_MMAP)
68   /* Allocate a larger block. */
69   initial_pointer = mmap (NULL,
70                           allocated_size_in_bytes,
71                           PROT_READ | PROT_WRITE,
72                           MAP_PRIVATE | MAP_ANONYMOUS,
73                           -1,
74                           0);
75   if (initial_pointer == NULL)
76     jitter_fatal ("mmap failed");
77   /* Find the aligned part of the mapping, which is the only part we are
78      interested in. */
79   res = ((void *)
80          JITTER_NEXT_MULTIPLE_OF_POWER_OF_TWO ((jitter_uint) initial_pointer,
81                                                alignment_in_bytes));
82   id->initial_map = res;
83   id->mapping_length_in_bytes = size_in_bytes;
84   /* Unmap the misaligned part (which means: aligned to a double memory page,
85      but not to the required alignment) at the beginning and the end.  This also
86      checks that the block alignment is a multiple of double the page size, as
87      munmap fails otherwise. */
88   void *misaligned_before = initial_pointer;
89   size_t misaligned_before_length
90     = (char *) res - (char *) initial_pointer;
91   void *misaligned_after = (char *) res + size_in_bytes;
92   size_t misaligned_after_length
93     = (((char *) initial_pointer + allocated_size_in_bytes)
94        - (char *) misaligned_after);
95   if (misaligned_before_length > 0)
96     if (munmap (misaligned_before, misaligned_before_length) != 0)
97       jitter_fatal ("munmap failed (%li B not an even multiple of the page "
98                     "size?)", (long) alignment_in_bytes);
99   if (misaligned_after_length > 0)
100     if (munmap (misaligned_after, misaligned_after_length) != 0)
101       jitter_fatal ("munmap failed (%li B not an even multiple of the page "
102                     "size?)", (long) alignment_in_bytes);
103 #elif defined (JITTER_ALIGNED_BLOCK_USE_ALIGNED_ALLOC)
104   /* According to the specification aligned_alloc requires that the size be a
105      multiple of the alignment -- thanks to Bruno Haible for letting me notice.
106      This solution is therefore, in theory (in practice alignment_in_bytes will
107      usually be the same as size_in_bytes), wasteful. */
108   size_in_bytes = JITTER_NEXT_MULTIPLE_OF_POWER_OF_TWO (size_in_bytes,
109                                                         alignment_in_bytes);
110   res = aligned_alloc (alignment_in_bytes, size_in_bytes);
111   if (res == NULL)
112     jitter_fatal ("aligned_alloc failed");
113   id->aligned_alloced_buffer = res;
114 #elif defined (JITTER_ALIGNED_BLOCK_USE_POSIX_MEMALIGN)
115   if (posix_memalign (& res, alignment_in_bytes, size_in_bytes) != 0)
116     jitter_fatal ("posix_memalign failed");
117   id->posix_memaligned_buffer = res;
118 #elif defined (JITTER_ALIGNED_BLOCK_USE_FALLBACK)
119   initial_pointer = jitter_xmalloc (allocated_size_in_bytes);
120   res = ((void *)
121          JITTER_NEXT_MULTIPLE_OF_POWER_OF_TWO ((jitter_uint) initial_pointer,
122                                                alignment_in_bytes));
123   id->initial_pointer = initial_pointer;
124 #else
125 # error "no aligned block implementation defined.  This should never happen."
126 #endif
127   return res;
128 }
129 
130 void
jitter_aligned_block_destroy(jitter_aligned_block_id id)131 jitter_aligned_block_destroy (jitter_aligned_block_id id)
132 {
133 #if defined (JITTER_ALIGNED_BLOCK_USE_MMAP)
134   munmap (id.initial_map, id.mapping_length_in_bytes);
135 #elif defined (JITTER_ALIGNED_BLOCK_USE_ALIGNED_ALLOC)
136   free (id.aligned_alloced_buffer);
137 #elif defined (JITTER_ALIGNED_BLOCK_USE_POSIX_MEMALIGN)
138   free (id.posix_memaligned_buffer);
139 #elif defined (JITTER_ALIGNED_BLOCK_USE_FALLBACK)
140   free (id.initial_pointer);
141 #else
142 # error "no aligned block implementation defined.  This should never happen."
143 #endif
144 }
145