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