1 /* Copyright (C) 2018 Wildfire Games.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 /*
24 * dynamic (expandable) array
25 */
26
27 #include "precompiled.h"
28 #include "lib/allocators/dynarray.h"
29
30 #include "lib/alignment.h"
31 #include "lib/sysdep/vm.h"
32
33
validate_da(DynArray * da)34 static Status validate_da(DynArray* da)
35 {
36 if(!da)
37 WARN_RETURN(ERR::INVALID_POINTER);
38 // u8* const base = da->base;
39 const size_t max_size_pa = da->max_size_pa;
40 const size_t cur_size = da->cur_size;
41 const size_t pos = da->pos;
42
43 // note: this happens if max_size == 0
44 // if(debug_IsPointerBogus(base))
45 // WARN_RETURN(ERR::_1);
46 // note: don't check if base is page-aligned -
47 // might not be true for 'wrapped' mem regions.
48 if(!IsAligned(max_size_pa, g_PageSize))
49 WARN_RETURN(ERR::_3);
50 if(cur_size > max_size_pa)
51 WARN_RETURN(ERR::_4);
52 if(pos > cur_size || pos > max_size_pa)
53 WARN_RETURN(ERR::_5);
54
55 return INFO::OK;
56 }
57
58 #define CHECK_DA(da) RETURN_STATUS_IF_ERR(validate_da(da))
59
60
da_alloc(DynArray * da,size_t max_size)61 Status da_alloc(DynArray* da, size_t max_size)
62 {
63 ENSURE(max_size != 0);
64 const size_t max_size_pa = Align<g_PageSize>(max_size);
65
66 u8* p = (u8*)vm::ReserveAddressSpace(max_size_pa);
67 if(!p)
68 return ERR::NO_MEM; // NOWARN (already done in vm)
69
70 da->base = p;
71 da->max_size_pa = max_size_pa;
72 da->cur_size = 0;
73 da->cur_size_pa = 0;
74 da->pos = 0;
75 CHECK_DA(da);
76 return INFO::OK;
77 }
78
79
da_free(DynArray * da)80 Status da_free(DynArray* da)
81 {
82 CHECK_DA(da);
83
84 vm::ReleaseAddressSpace(da->base, da->max_size_pa);
85
86 // wipe out the DynArray for safety
87 memset(da, 0, sizeof(*da));
88
89 return INFO::OK;
90 }
91
92
da_set_size(DynArray * da,size_t new_size)93 Status da_set_size(DynArray* da, size_t new_size)
94 {
95 CHECK_DA(da);
96
97 // determine how much to add/remove
98 const size_t cur_size_pa = Align<g_PageSize>(da->cur_size);
99 const size_t new_size_pa = Align<g_PageSize>(new_size);
100 const ssize_t size_delta_pa = (ssize_t)new_size_pa - (ssize_t)cur_size_pa;
101
102 // not enough memory to satisfy this expand request: abort.
103 // note: do not complain - some allocators (e.g. file_cache)
104 // legitimately use up all available space.
105 if(new_size_pa > da->max_size_pa)
106 return ERR::LIMIT; // NOWARN
107
108 u8* end = da->base + cur_size_pa;
109 bool ok = true;
110 // expanding
111 if(size_delta_pa > 0)
112 {
113 ok = vm::Commit(uintptr_t(end), size_delta_pa);
114 if(!ok)
115 debug_printf("Commit failed (%p %lld)\n", (void *)end, (long long)size_delta_pa);
116 }
117 // shrinking
118 else if(size_delta_pa < 0)
119 ok = vm::Decommit(uintptr_t(end+size_delta_pa), -size_delta_pa);
120 // else: no change in page count, e.g. if going from size=1 to 2
121 // (we don't want mem_* to have to handle size=0)
122
123 da->cur_size = new_size;
124 da->cur_size_pa = new_size_pa;
125 CHECK_DA(da);
126 return ok? INFO::OK : ERR::FAIL;
127 }
128
129
da_reserve(DynArray * da,size_t size)130 Status da_reserve(DynArray* da, size_t size)
131 {
132 if(da->pos+size > da->cur_size_pa)
133 RETURN_STATUS_IF_ERR(da_set_size(da, da->cur_size_pa+size));
134 da->cur_size = std::max(da->cur_size, da->pos+size);
135 return INFO::OK;
136 }
137
138
da_append(DynArray * da,const void * data,size_t size)139 Status da_append(DynArray* da, const void* data, size_t size)
140 {
141 RETURN_STATUS_IF_ERR(da_reserve(da, size));
142 memcpy(da->base+da->pos, data, size);
143 da->pos += size;
144 return INFO::OK;
145 }
146