Line data Source code
1 : #include "fd_funk.h"
2 :
3 : fd_funk_rec_t *
4 : fd_funk_val_copy( fd_funk_rec_t * rec,
5 : void const * data,
6 : ulong sz,
7 : ulong sz_est,
8 : fd_alloc_t * alloc,
9 : fd_wksp_t * wksp,
10 74953857 : int * opt_err ) {
11 :
12 : /* Check input args */
13 :
14 74953857 : sz_est = fd_ulong_if( !sz_est, sz, sz_est ); /* Use reasonable default for sz_est */
15 :
16 74953857 : ulong d0 = (ulong)data;
17 74953857 : ulong d1 = d0 + sz;
18 :
19 74953857 : if( FD_UNLIKELY( (!rec) | ((!data) & (!!sz)) | (!alloc) | (!wksp) | /* NULL rec,NULL data w sz!=0,NULL alloc,NULL wksp */
20 74953857 : (d1<d0) | (sz>sz_est) | (sz_est>FD_FUNK_REC_VAL_MAX) ) ) { /* data wraps, too large sz, too large sz_est */
21 7450896 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
22 7450896 : return NULL;
23 7450896 : }
24 :
25 67502961 : ulong val_max = (ulong)rec->val_max;
26 67502961 : ulong val_gaddr = rec->val_gaddr;
27 :
28 67502961 : ulong v0 = val_max ? (ulong)fd_wksp_laddr_fast( wksp, val_gaddr ) : 0UL; /* Technically don't need trinary */
29 67502961 : ulong v1 = v0 + val_max;
30 :
31 67502961 : if( FD_UNLIKELY( ((!!sz) & (!!val_max) & (!((d1<=v0) | (d0>=v1)))) | /* data overlaps val alloc */
32 67502961 : (!!(rec->flags & FD_FUNK_REC_FLAG_ERASE)) ) ) { /* marked erase */
33 55709796 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
34 55709796 : return NULL;
35 55709796 : }
36 :
37 11793165 : uchar * val = (uchar *)v0;
38 :
39 11793165 : if( FD_UNLIKELY( !sz_est ) ) {
40 :
41 : /* User requested to flush the existing value */
42 :
43 3449085 : fd_funk_val_flush( rec, alloc, wksp );
44 :
45 8344080 : } else {
46 :
47 : /* User requested to allocate at least sz_est for value. Allocate
48 : space for the copy. If allocation fails, we do any remaining
49 : data copy into the current allocation (if possible). Otherwise,
50 : we copy the data into the new space, free the old space (if any)
51 : and switch the value to the new space. We do the alloc first
52 : such that if it fails, we haven't affected the state. */
53 :
54 8344080 : ulong new_val_max;
55 8344080 : uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, sz_est, &new_val_max );
56 :
57 8344080 : if( FD_UNLIKELY( !new_val ) ) { /* Fallback on in-place */
58 :
59 0 : new_val_max = rec->val_max;
60 0 : if( FD_UNLIKELY( new_val_max < sz ) ) { /* Fallback failed too */
61 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_MEM );
62 0 : return NULL;
63 0 : }
64 :
65 8344080 : } else { /* Out-of-place */
66 :
67 8344080 : rec->val_max = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
68 8344080 : rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
69 :
70 8344080 : if( val ) fd_alloc_free( alloc, val );
71 8344080 : val = new_val;
72 :
73 8344080 : }
74 :
75 : /* At this point we have room for the copy, do the copy, clear out
76 : trailing padding to be on the safe side and update the value
77 : size. */
78 :
79 8344080 : if( FD_LIKELY( sz ) ) fd_memcpy( val, data, sz );
80 8344080 : fd_memset( val + sz, 0, new_val_max - sz );
81 8344080 : rec->val_sz = (uint)sz;
82 :
83 8344080 : }
84 :
85 11793165 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
86 11793165 : return rec;
87 11793165 : }
88 :
89 : fd_funk_rec_t *
90 : fd_funk_val_append( fd_funk_rec_t * rec,
91 : void const * data,
92 : ulong sz,
93 : fd_alloc_t * alloc,
94 : fd_wksp_t * wksp,
95 4940016336 : int * opt_err ) {
96 :
97 : /* Check input args */
98 :
99 4940016336 : if( FD_UNLIKELY( !sz ) ) { /* Empty append request */
100 635276898 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
101 635276898 : return rec;
102 635276898 : }
103 :
104 4304739438 : ulong d0 = (ulong)data;
105 4304739438 : ulong d1 = d0 + sz;
106 :
107 4304739438 : if( FD_UNLIKELY( (!rec) | (!d0) | (d1<d0) | (!alloc) | (!wksp) ) ) { /* NULL rec, NULL data, data wrap, NULL alloc, NULL wksp */
108 3811661388 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
109 3811661388 : return NULL;
110 3811661388 : }
111 :
112 493078050 : ulong val_sz = (ulong)rec->val_sz;
113 493078050 : ulong val_max = (ulong)rec->val_max;
114 493078050 : ulong val_gaddr = rec->val_gaddr;
115 :
116 493078050 : ulong new_val_sz = val_sz + sz;
117 :
118 493078050 : ulong v0 = val_max ? (ulong)fd_wksp_laddr_fast( wksp, val_gaddr ) : 0UL; /* Technically don't need trinary */
119 493078050 : ulong v1 = v0 + val_max;
120 :
121 493078050 : if( FD_UNLIKELY( (new_val_sz<val_sz) | (new_val_sz>FD_FUNK_REC_VAL_MAX) | /* too large sz */
122 493078050 : ((!!val_max) & (!((d1<=v0) | (d0>=v1)))) | /* data overlaps with val alloc */
123 493078050 : (!!(rec->flags & FD_FUNK_REC_FLAG_ERASE)) ) ) { /* marked erase */
124 419407206 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
125 419407206 : return NULL;
126 419407206 : }
127 :
128 73670844 : uchar * val = (uchar *)v0;
129 :
130 : /* If we need to resize val or do the initial allocation of val),
131 : compute target new size, allocate a new region of at least the
132 : target new size, copy the current value into it, free the old one
133 : and update the value. We use malloc_at_least with 1 alignment to
134 : pack wksp memory as tight as possible. */
135 :
136 73670844 : if( FD_UNLIKELY( new_val_sz > val_max ) ) {
137 :
138 73670844 : ulong new_val_max = fd_ulong_min( fd_alloc_max_expand( val_max, FD_FUNK_VAL_ALIGN, new_val_sz ), FD_FUNK_REC_VAL_MAX );
139 73670844 : if( FD_UNLIKELY( new_val_max<=val_max ) ) { /* Already expanded as much as possible */
140 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
141 0 : return NULL;
142 0 : }
143 :
144 73670844 : uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, new_val_max, &new_val_max );
145 73670844 : if( FD_UNLIKELY( !new_val ) ) { /* Allocation failure */
146 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_MEM );
147 0 : return NULL;
148 0 : }
149 :
150 73670844 : if( val_sz ) fd_memcpy( new_val, val, val_sz ); /* Copy the existing val */
151 73670844 : fd_memset( new_val + val_sz, 0, new_val_max - val_sz ); /* Clear out trailing padding to be on the safe side */
152 73670844 : fd_alloc_free( alloc, val ); /* Free the old val */
153 :
154 73670844 : rec->val_max = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
155 73670844 : rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
156 :
157 73670844 : val = new_val;
158 :
159 73670844 : }
160 :
161 : /* At this point, we have room to do the append. Do the append.
162 : Trailing padding was cleared out previously. */
163 :
164 73670844 : fd_memcpy( val+val_sz, data, sz );
165 :
166 73670844 : rec->val_sz = (uint)new_val_sz;
167 :
168 73670844 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
169 73670844 : return rec;
170 73670844 : }
171 :
172 : fd_funk_rec_t *
173 : fd_funk_val_truncate( fd_funk_rec_t * rec,
174 : ulong new_val_sz,
175 : fd_alloc_t * alloc,
176 : fd_wksp_t * wksp,
177 98902581 : int * opt_err ) {
178 :
179 : /* Check input args */
180 :
181 98902581 : if( FD_UNLIKELY( (!rec) | (new_val_sz>FD_FUNK_REC_VAL_MAX) | (!alloc) | (!wksp) ) || /* NULL rec,too big,NULL alloc,NULL wksp */
182 98902581 : FD_UNLIKELY( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) { /* Marked erase */
183 54778434 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
184 54778434 : return NULL;
185 54778434 : }
186 :
187 44124147 : ulong val_sz = (ulong)rec->val_sz;
188 :
189 44124147 : if( FD_UNLIKELY( !new_val_sz ) ) {
190 :
191 : /* User asked to truncate to 0. Flush the any existing value. */
192 :
193 1201311 : fd_funk_val_flush( rec, alloc, wksp );
194 :
195 42922836 : } else if( FD_LIKELY( new_val_sz > val_sz ) ) {
196 :
197 : /* User requested to increase the value size. We presume they are
198 : asking for a specific size (as opposed to bumping up the size ala
199 : append) so we don't build in extra padding to amortize the cost
200 : of future truncates. Note that new_val_sz is at least 1 at this
201 : point but val_sz / val_gaddr could be zero / zero. */
202 :
203 3420279 : ulong val_max = (ulong)rec->val_max;
204 3420279 : ulong val_gaddr = rec->val_gaddr;
205 3420279 : uchar * val = val_max ? fd_wksp_laddr_fast( wksp, val_gaddr ) : NULL; /* TODO: branchless */
206 :
207 3420279 : ulong new_val_max;
208 3420279 : uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, new_val_sz, &new_val_max );
209 3420279 : if( FD_UNLIKELY( !new_val ) ) { /* Allocation failure! */
210 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_MEM );
211 0 : return NULL;
212 0 : }
213 :
214 3420279 : if( val_sz ) fd_memcpy( new_val, val, val_sz ); /* Copy the existing value */
215 3420279 : fd_memset( new_val + val_sz, 0, new_val_max - val_sz ); /* Clear out trailing padding to be on the safe side */
216 :
217 : /* Order of updates is important for fd_funk_val_safe */
218 3420279 : rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
219 3420279 : rec->val_sz = (uint)new_val_sz;
220 3420279 : rec->val_max = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
221 :
222 3420279 : if( val ) fd_alloc_free( alloc, val ); /* Free the old value (if any) */
223 :
224 39502557 : } else {
225 :
226 : /* User requested to reduce the value size or keep it the same.
227 : Even though we could in principle just set rec->val_sz to its new
228 : value, we do a new allocation as it is still (usually) O(1),
229 : presumably the caller knew it wanted a particular size and that
230 : the resize might free up resources needed in the future. Note
231 : that new_val_sz is at least 1, val_sz at least 2 and val_gaddr is
232 : non-zero at this point. */
233 :
234 39502557 : uchar * val = (uchar *)fd_wksp_laddr_fast( wksp, rec->val_gaddr );
235 :
236 39502557 : ulong new_val_max;
237 39502557 : uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, new_val_sz, &new_val_max );
238 :
239 39502557 : if( FD_UNLIKELY( !new_val ) ) { /* Fallback on in-place */
240 :
241 0 : new_val_max = (ulong)rec->val_max;
242 :
243 0 : fd_memset( val + new_val_sz, 0, new_val_max - new_val_sz ); /* Clear out the trailing padding to be on the safe side */
244 :
245 0 : rec->val_sz = (uint)new_val_sz;
246 :
247 39502557 : } else { /* Out of place */
248 :
249 39502557 : fd_memcpy( new_val, val, new_val_sz ); /* Copy the (truncated) existing value */
250 39502557 : fd_memset( new_val + new_val_sz, 0, new_val_max - new_val_sz ); /* Clear out the trailing padding to be on the safe side */
251 :
252 : /* Order of updates is important for fd_funk_val_safe */
253 39502557 : rec->val_sz = (uint)new_val_sz;
254 39502557 : rec->val_max = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
255 39502557 : rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
256 :
257 39502557 : if( val ) fd_alloc_free( alloc, val ); /* Free the old value (if any) */
258 :
259 39502557 : }
260 :
261 39502557 : }
262 :
263 44124147 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
264 44124147 : return rec;
265 44124147 : }
266 :
267 : void *
268 : fd_funk_val_safe( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */
269 : fd_wksp_t const * wksp,
270 : fd_valloc_t valloc,
271 0 : ulong * result_len ) {
272 0 : uint val_sz = rec->val_sz;
273 0 : *result_len = val_sz;
274 0 : if( !val_sz ) return NULL;
275 0 : void * res = fd_valloc_malloc( valloc, FD_FUNK_VAL_ALIGN, val_sz );
276 : /* Note that this memcpy may copy recently freed memory, but it
277 : won't crash, which is the important thing */
278 0 : fd_memcpy( res, fd_wksp_laddr_fast( wksp, rec->val_gaddr ), val_sz );
279 0 : return res;
280 0 : }
281 :
282 : int
283 22275108 : fd_funk_val_verify( fd_funk_t * funk ) {
284 22275108 : fd_wksp_t * wksp = fd_funk_wksp( funk ); /* Previously verified */
285 22275108 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp ); /* Previously verified */
286 22275108 : ulong wksp_tag = funk->wksp_tag; /* Previously verified */
287 :
288 : /* At this point, rec_map has been extensively verified */
289 :
290 2848661277 : # define TEST(c) do { \
291 2848661277 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
292 2848661277 : } while(0)
293 :
294 : /* Iterate over all records in use */
295 :
296 22275108 : for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter_init( rec_map );
297 903401892 : !fd_funk_rec_map_iter_done( rec_map, iter );
298 881126784 : iter = fd_funk_rec_map_iter_next( rec_map, iter ) ) {
299 881126784 : fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( rec_map, iter );
300 :
301 : /* Make sure values look sane */
302 : /* TODO: consider doing an alias analysis on allocated values?
303 : (tricky to do algo efficient in place) */
304 :
305 881126784 : ulong val_sz = (ulong)rec->val_sz;
306 881126784 : ulong val_max = (ulong)rec->val_max;
307 881126784 : ulong val_gaddr = rec->val_gaddr;
308 :
309 881126784 : TEST( val_sz<=val_max );
310 :
311 881126784 : if( rec->flags & FD_FUNK_REC_FLAG_ERASE ) {
312 355153215 : TEST( !val_max );
313 355153215 : TEST( !val_gaddr );
314 525973569 : } else {
315 525973569 : TEST( val_max<=FD_FUNK_REC_VAL_MAX );
316 525973569 : if( !val_gaddr ) TEST( !val_max );
317 205280925 : else {
318 205280925 : TEST( (0UL<val_max) & (val_max<=FD_FUNK_REC_VAL_MAX) );
319 205280925 : TEST( fd_wksp_tag( wksp, val_gaddr )==wksp_tag );
320 205280925 : }
321 525973569 : }
322 881126784 : }
323 :
324 22275108 : # undef TEST
325 :
326 22275108 : return FD_FUNK_SUCCESS;
327 22275108 : }
|