Line data Source code
1 : /* Copyright (C) 2020 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 : #include <cstring>
34 : #include <string>
35 :
36 4 : static Status validate_da(DynArray* da)
37 : {
38 4 : if(!da)
39 0 : WARN_RETURN(ERR::INVALID_POINTER);
40 : // u8* const base = da->base;
41 4 : const size_t max_size_pa = da->max_size_pa;
42 4 : const size_t cur_size = da->cur_size;
43 4 : const size_t pos = da->pos;
44 :
45 : // note: this happens if max_size == 0
46 : // if(debug_IsPointerBogus(base))
47 : // WARN_RETURN(ERR::_1);
48 : // note: don't check if base is page-aligned -
49 : // might not be true for 'wrapped' mem regions.
50 4 : if(!IsAligned(max_size_pa, g_PageSize))
51 0 : WARN_RETURN(ERR::_3);
52 4 : if(cur_size > max_size_pa)
53 0 : WARN_RETURN(ERR::_4);
54 4 : if(pos > cur_size || pos > max_size_pa)
55 0 : WARN_RETURN(ERR::_5);
56 :
57 4 : return INFO::OK;
58 : }
59 :
60 : #define CHECK_DA(da) RETURN_STATUS_IF_ERR(validate_da(da))
61 :
62 :
63 1 : Status da_alloc(DynArray* da, size_t max_size)
64 : {
65 1 : ENSURE(max_size != 0);
66 1 : const size_t max_size_pa = Align<g_PageSize>(max_size);
67 :
68 1 : u8* p = (u8*)vm::ReserveAddressSpace(max_size_pa);
69 1 : if(!p)
70 0 : return ERR::NO_MEM; // NOWARN (already done in vm)
71 :
72 1 : da->base = p;
73 1 : da->max_size_pa = max_size_pa;
74 1 : da->cur_size = 0;
75 1 : da->cur_size_pa = 0;
76 1 : da->pos = 0;
77 1 : CHECK_DA(da);
78 1 : return INFO::OK;
79 : }
80 :
81 :
82 1 : Status da_free(DynArray* da)
83 : {
84 1 : CHECK_DA(da);
85 :
86 1 : vm::ReleaseAddressSpace(da->base, da->max_size_pa);
87 :
88 : // wipe out the DynArray for safety
89 1 : memset(da, 0, sizeof(*da));
90 :
91 1 : return INFO::OK;
92 : }
93 :
94 :
95 1 : Status da_set_size(DynArray* da, size_t new_size)
96 : {
97 1 : CHECK_DA(da);
98 :
99 : // determine how much to add/remove
100 1 : const size_t cur_size_pa = Align<g_PageSize>(da->cur_size);
101 1 : const size_t new_size_pa = Align<g_PageSize>(new_size);
102 1 : const ssize_t size_delta_pa = (ssize_t)new_size_pa - (ssize_t)cur_size_pa;
103 :
104 : // not enough memory to satisfy this expand request: abort.
105 : // note: do not complain - some allocators (e.g. file_cache)
106 : // legitimately use up all available space.
107 1 : if(new_size_pa > da->max_size_pa)
108 0 : return ERR::LIMIT; // NOWARN
109 :
110 1 : u8* end = da->base + cur_size_pa;
111 1 : bool ok = true;
112 : // expanding
113 1 : if(size_delta_pa > 0)
114 : {
115 1 : ok = vm::Commit(uintptr_t(end), size_delta_pa);
116 1 : if(!ok)
117 0 : debug_printf("Commit failed (%p %lld)\n", (void *)end, (long long)size_delta_pa);
118 : }
119 : // shrinking
120 0 : else if(size_delta_pa < 0)
121 0 : ok = vm::Decommit(uintptr_t(end+size_delta_pa), -size_delta_pa);
122 : // else: no change in page count, e.g. if going from size=1 to 2
123 : // (we don't want mem_* to have to handle size=0)
124 :
125 1 : da->cur_size = new_size;
126 1 : da->cur_size_pa = new_size_pa;
127 1 : CHECK_DA(da);
128 1 : return ok? INFO::OK : ERR::FAIL;
129 : }
130 :
131 :
132 0 : Status da_reserve(DynArray* da, size_t size)
133 : {
134 0 : if(da->pos+size > da->cur_size_pa)
135 0 : RETURN_STATUS_IF_ERR(da_set_size(da, da->cur_size_pa+size));
136 0 : da->cur_size = std::max(da->cur_size, da->pos+size);
137 0 : return INFO::OK;
138 : }
139 :
140 :
141 0 : Status da_append(DynArray* da, const void* data, size_t size)
142 : {
143 0 : RETURN_STATUS_IF_ERR(da_reserve(da, size));
144 0 : memcpy(da->base+da->pos, data, size);
145 0 : da->pos += size;
146 0 : return INFO::OK;
147 3 : }
|