Line data Source code
1 : #ifndef HEADER_fd_src_funk_fd_funk_val_h
2 : #define HEADER_fd_src_funk_fd_funk_val_h
3 :
4 : /* This provides APIs for managing funk record values. It is generally
5 : not meant to be included directly. Use fd_funk.h instead. */
6 :
7 : #include "fd_funk_rec.h" /* Includes fd_funk_txn.h, fd_funk_base.h */
8 :
9 : /* FD_FUNK_REC_VAL_MAX is the maximum size of a record value. */
10 :
11 463226412 : #define FD_FUNK_REC_VAL_MAX UINT_MAX
12 265110030 : #define FD_FUNK_VAL_ALIGN 8UL
13 :
14 : FD_PROTOTYPES_BEGIN
15 :
16 : /* Accessors */
17 :
18 : /* fd_funk_val_{sz,max} returns the current size of the value associated
19 : with a record and the amount of wksp allocated currently for a value.
20 : Assumes funk is a current local join. These value might change on
21 : subsequent calls if the record is resized.
22 : 0<=sz<=max<=FD_FUNK_REC_VAL_MAX. */
23 :
24 : FD_FN_PURE static inline ulong /* Current size of the record's value in bytes */
25 284132988 : fd_funk_val_sz( fd_funk_rec_t const * rec ) { /* Assumes pointer in caller's address space to a live funk record */
26 284132988 : return (ulong)rec->val_sz; /* Covers the marked ERASE case too */
27 284132988 : }
28 :
29 : FD_FN_PURE static inline ulong /* Current size of the record's value allocation in bytes */
30 384159501 : fd_funk_val_max( fd_funk_rec_t const * rec ) { /* Assumes pointer in caller's address space to a live funk record */
31 384159501 : return (ulong)rec->val_max; /* Covers the marked ERASE case too */
32 384159501 : }
33 :
34 : /* fd_funk_val returns a pointer in the caller's address space to the
35 : current value associated with a record. fd_funk_rec_val_const is a
36 : const-correct version. There are sz bytes at the returned pointer.
37 : IMPORTANT SAFETY TIP! There are _no_ alignment guarantees on the
38 : returned value. Returns NULL if the record has a zero sz (which also
39 : covers the case where rec has been marked ERASE). max 0 implies val
40 : NULL and vice versa. Assumes no concurrent operations on rec. */
41 :
42 : FD_FN_PURE static inline void * /* Lifetime is the lesser of rec or the value size is modified */
43 : fd_funk_val( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */
44 79382580 : fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */
45 79382580 : ulong val_gaddr = rec->val_gaddr;
46 79382580 : if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */
47 67777086 : return fd_wksp_laddr_fast( wksp, val_gaddr );
48 79382580 : }
49 :
50 : FD_FN_PURE static inline void const * /* Lifetime is the lesser of rec or the value size is modified */
51 : fd_funk_val_const( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */
52 479554812 : fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */
53 479554812 : ulong val_gaddr = rec->val_gaddr;
54 479554812 : if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */
55 460284615 : return fd_wksp_laddr_fast( wksp, val_gaddr );
56 479554812 : }
57 :
58 : /* fd_funk_val_safe copies out the record value into a buffer
59 : * allocated by the valloc. The result should eventually be freed by
60 : * the same valloc. This API is safe in the presence of concurrent writes. */
61 :
62 : void *
63 : fd_funk_val_safe( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */
64 : fd_wksp_t const * wksp,
65 : fd_valloc_t valloc,
66 : ulong * result_len );
67 :
68 :
69 : /* fd_funk_rec_read reads bytes [off,off+sz) and returns a pointer to
70 : the requested data on success and NULL on failure. Reasons for
71 : failure include NULL rec, 0 sz, [off,off+sz) does not overlap
72 : completely val, NULL wksp, marked ERASE. Assumes no concurrent
73 : operations on rec.
74 :
75 : The returned pointer is in the caller's address space and, if
76 : non-NULL, the value at the pointer is stable for its lifetime or
77 : until it it is modified.
78 :
79 : IMPORTANT SAFETY TIP! There are _no_ alignment guarantees on the
80 : returned value (even if off itself is aligned).
81 :
82 : Note that if reading two overlapping regions of a record, the return
83 : pointers can overlap (along these will also overlap the regions
84 : returned by the above accessors). Further, if the region is changed
85 : by a below write, the value at the returned pointers will immediately
86 : reflect those writes. (That is, the returned pointers are zero copy
87 : into the actual value data of the record.) */
88 :
89 : FD_FN_PURE static inline void const * /* Lifetime is lesser of current local join, the record or val is resized */
90 : fd_funk_val_read( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record
91 : (NULL returns NULL) */
92 : ulong off, /* Should be in [0,sz] */
93 : ulong sz, /* Should be in [1,val_sz-off] */
94 2854776354 : fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is current local join */
95 :
96 2854776354 : ulong end = off + sz;
97 :
98 2854776354 : if( FD_UNLIKELY( (!rec) | (end<=off) | (!wksp) ) || /* NULL rec, sz==0 or off+sz wrapped, NULL wksp */
99 2854776354 : FD_UNLIKELY( (end>(ulong)rec->val_sz) ) ) return NULL; /* Read past end (covers marked ERASE case too) */
100 :
101 1656906138 : return fd_wksp_laddr_fast( wksp, rec->val_gaddr + off );
102 2854776354 : }
103 :
104 : /* Operations */
105 :
106 : /* fd_funk_rec_write writes record bytes [off,off+sz) and returns rec on
107 : success and NULL on failure.
108 :
109 : data points to the bytes to sz write. [data,data+sz) should not
110 : overlap with the current value bytes [off,off+sz) but otherwise can
111 : point anywhere valid in the caller's address space.
112 :
113 : The write retains no interest in data on return. sz 0 is considered
114 : a no-op regardless of anything else and immediately returns rec.
115 :
116 : Reasons for failure include NULL rec, NULL data with non-zero sz,
117 : [data,data+sz) wraps, [off,off+sz) does not overlap with the record
118 : completely, [data,data+sz) overlaps with [off,off+sz). Assumes no
119 : concurrent operations on rec or data. */
120 :
121 : FD_FN_UNUSED static fd_funk_rec_t * /* Returns rec on success, NULL on failure */ /* Workaround -Winline */
122 : fd_funk_val_write( fd_funk_rec_t * rec, /* Assumed in caller's address space to live funk record (NULL returns NULL) */
123 : ulong off, /* First byte of record to write, in [0,val_sz], NULL if too large */
124 : ulong sz, /* Number of bytes to write, 0 is a no-op, in [0,val_sz-off], NULL if too large */
125 : void const * data, /* Assumed in caller's address space, NULL okay if sz 0 */
126 1637541933 : fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is current local join */
127 :
128 1637541933 : if( FD_UNLIKELY( !sz ) ) return rec; /* Empty write request */
129 :
130 1440117519 : ulong end = off + sz;
131 :
132 1440117519 : ulong d0 = (ulong)data;
133 1440117519 : ulong d1 = d0 + sz;
134 :
135 1440117519 : if( FD_UNLIKELY( (!rec) | (end<off) | (!data) | (d1<d0) | (!wksp) ) || /* NULL rec, off+sz wrapped, NULL data w sz!=0, data wrapped, NULL wksp */
136 1440117519 : FD_UNLIKELY( end > (ulong)rec->val_max ) ) return NULL; /* too large (covers marked ERASE case too) */
137 :
138 50108889 : ulong v0 = (ulong)fd_wksp_laddr_fast( wksp, rec->val_gaddr + off );
139 50108889 : ulong v1 = v0 + sz;
140 :
141 50108889 : if( FD_UNLIKELY( !((d1<=v0) | (d0>=v1)) ) ) return NULL; /* data overlaps with val */
142 :
143 50108889 : fd_memcpy( (void *)v0, data, sz );
144 :
145 50108889 : if ( FD_UNLIKELY( end > (ulong)rec->val_sz ) )
146 0 : rec->val_sz = (uint)end;
147 :
148 50108889 : return rec;
149 50108889 : }
150 :
151 : /* fd_funk_val_copy copies sz bytes starting at data into the record
152 : value, replacing the existing record value. rec's value will be able
153 : to accommodate at least sz_est in the future without resizing on
154 : return. If sz_est is 0 on entry, it will be be set to sz as a
155 : reasonable default before any argument checking.
156 :
157 : data points to the bytes to write. [data,data+sz) should not overlap
158 : with the existing record but can otherwise can point anywhere valid
159 : in the caller's address space.
160 :
161 : This generally will resize the record value to something at least
162 : sz_est so this function should be assumed to kill any existing
163 : pointers into this record's value storage. Note that sz_est==0 will
164 : set the record to the NULL val. And sz==0 with sz_est!=0 can be used
165 : to preallocate record sizes with a NULL initial value.
166 :
167 : Returns rec on success and NULL on failure. If opt_err is non-NULL,
168 : on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
169 : FD_FUNK_ERR_* code on failure. Reasons for failure include
170 : FD_FUNK_ERR_INVAL (NULL rec, NULL data with non-zero sz, NULL alloc,
171 : NULL wksp, data region wraps, sz>sz_est, sz_est too large, rec is
172 : marked as ERASE, data region overlaps the existing val allocation)
173 : and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp). On
174 : failure, the current value is unchanged.
175 :
176 : Assumes no concurrent operations on rec or data. The copy retains no
177 : interest in data on return. */
178 :
179 : fd_funk_rec_t * /* Returns rec on success, NULL on failure */
180 : fd_funk_val_copy( fd_funk_rec_t * rec, /* Assumed in caller's address space to live funk record (NULL returns NULL) */
181 : void const * data, /* Points to first byte to copy in caller's address space, NULL okay if sz 0 */
182 : ulong sz, /* Number of bytes to copy, in [0,sz_est], NULL if too large */
183 : ulong sz_est, /* Est final size, 0 means use sz, in [sz,FD_FUNK_REC_VAL_MAX], NULL if too large */
184 : fd_alloc_t * alloc, /* ==fd_funk_alloc( funk, wksp ) */
185 : fd_wksp_t * wksp, /* ==fd_funk_wksp( funk ) where funk is current local join */
186 : int * opt_err ); /* If non-NULL, *opt_err returns operation error code */
187 :
188 : /* fd_funk_val_append appends sz bytes starting at data to the end of
189 : the record rec. [data,data+sz) should not overlap with the existing
190 : value allocation but can otherwise can point anywhere valid in the
191 : caller's address space.
192 :
193 : This might need to resize the record value so this function should be
194 : assumed to kill any existing pointers into this record's value
195 : storage. Unlike copy above and truncate below, this function does
196 : try to minimize the amount of value allocations it might do.
197 :
198 : Returns rec on success and NULL on failure. If opt_err is non-NULL,
199 : on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
200 : FD_FUNK_ERR_* code on failure. Reasons for failure include
201 : FD_FUNK_ERR_INVAL (NULL rec, NULL data with non-zero sz,
202 : [data,data+sz) wraps, NULL alloc, NULL wksp, rec marked ERASE, sz too
203 : large, data region overlaps with existing record value allocation)
204 : and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp). On
205 : failure, the current value is unchanged.
206 :
207 : Assumes no concurrent operations on rec or data. The append retains
208 : no interest in data on return. sz 0 is considered a no-op regardless
209 : of anything else and immediately returns rec / SUCCESS. */
210 :
211 : fd_funk_rec_t * /* Returns rec on success, NULL on failure */
212 : fd_funk_val_append( fd_funk_rec_t * rec, /* Assumed in caller's address space to a live funk record (NULL returns NULL) */
213 : void const * data, /* Points to first byte to append in caller's address space, NULL okay if sz 0 */
214 : ulong sz, /* Number of bytes to append, 0 is a no-op, NULL if too large */
215 : fd_alloc_t * alloc, /* ==fd_funk_alloc( funk, wksp ) */
216 : fd_wksp_t * wksp, /* ==fd_funk_wksp( funk ) where funk is current local join */
217 : int * opt_err ); /* If non-NULL, *opt_err returns operation error code */
218 :
219 : /* fd_funk_val_truncate resizes a record to be new_val_sz bytes in size.
220 :
221 : This function is optimized for the user knowing the actual long term
222 : record size when they call this. So avoid using this to
223 : incrementally increase a size of a value by a constant amount over
224 : time. See append above for that.
225 :
226 : Likewise, regardless of the current and new value sizes, this will
227 : always attempt to resize the record in order to minimize the amount
228 : of excess allocation used by the record. So this function should be
229 : assumed to kill any existing pointers into this record's value
230 : storage.
231 :
232 : Returns rec on success and NULL on failure. If opt_err is non-NULL,
233 : on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
234 : FD_FUNK_ERR_* code on failure. Reasons for failure include
235 : FD_FUNK_ERR_INVAL (NULL rec, too large new_val_sz, rec is marked
236 : ERASE) and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp).
237 : On failure, the current value is unchanged.
238 :
239 : Assumes no concurrent operations on rec. */
240 :
241 : fd_funk_rec_t * /* Returns rec on success, NULL on failure */
242 : fd_funk_val_truncate( fd_funk_rec_t * rec, /* Assumed in caller's address space to a live funk record (NULL returns NULL) */
243 : ulong new_val_sz, /* Should be in [0,FD_FUNK_REC_VAL_MAX] (returns NULL otherwise) */
244 : fd_alloc_t * alloc, /* ==fd_funk_alloc( funk, wksp ) */
245 : fd_wksp_t * wksp, /* ==fd_funk_wksp( funk ) where funk is current local join */
246 : int * opt_err ); /* If non-NULL, *opt_err returns operation error code */
247 :
248 : /* fd_funk_val_speed_load sets the record value to space allocated
249 : from the funk speed bump. This space is never freed. This function
250 : is used when loading large snapshots.
251 :
252 : Returns rec on success and NULL on failure. If opt_err is non-NULL,
253 : on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
254 : FD_FUNK_ERR_* code on failure. Reasons for failure include
255 : FD_FUNK_ERR_INVAL (NULL rec, too large new_val_sz, rec is marked
256 : ERASE) and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp).
257 : On failure, the current value is unchanged.
258 :
259 : Assumes no concurrent operations on rec. */
260 :
261 : fd_funk_rec_t * /* Returns rec on success, NULL on failure */
262 : fd_funk_val_speed_load( fd_funk_t * funk,
263 : fd_funk_rec_t * rec, /* Assumed in caller's address space to a live funk record (NULL returns NULL) */
264 : ulong new_val_sz, /* Should be in [0,FD_FUNK_REC_VAL_MAX] (returns NULL otherwise) */
265 : fd_wksp_t * wksp, /* ==fd_funk_wksp( funk ) where funk is current local join */
266 : int * opt_err ); /* If non-NULL, *opt_err returns operation error code */
267 :
268 : /* Misc */
269 :
270 : /* fd_funk_val_init sets a record with uninitialized value metadata to
271 : the NULL value. Meant for internal use. */
272 :
273 : static inline fd_funk_rec_t * /* Returns rec */
274 18256539 : fd_funk_val_init( fd_funk_rec_t * rec ) { /* Assumed record in caller's address space with uninitialized value metadata */
275 18256539 : rec->val_sz = 0U;
276 18256539 : rec->val_max = 0U;
277 18256539 : rec->val_gaddr = 0UL;
278 18256539 : rec->val_no_free = 0;
279 18256539 : return rec;
280 18256539 : }
281 :
282 : /* fd_funk_val_flush sets a record to the NULL value, discarding the
283 : current value if any. Meant for internal use. */
284 :
285 : static inline fd_funk_rec_t * /* Returns rec */
286 : fd_funk_val_flush( fd_funk_rec_t * rec, /* Assumed live funk record in caller's address space */
287 : fd_alloc_t * alloc, /* ==fd_funk_alloc( funk, wksp ) */
288 12024858 : fd_wksp_t * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */
289 12024858 : ulong val_gaddr = rec->val_gaddr;
290 12024858 : int val_no_free = rec->val_no_free;
291 12024858 : fd_funk_val_init( rec );
292 12024858 : if( val_gaddr && !val_no_free ) fd_alloc_free( alloc, fd_wksp_laddr_fast( wksp, val_gaddr ) );
293 12024858 : return rec;
294 12024858 : }
295 :
296 : /* fd_funk_val_verify verifies the record values. Returns
297 : FD_FUNK_SUCCESS if the values appear intact and FD_FUNK_ERR_INVAL if
298 : not (logs details). Meant to be called as part of fd_funk_verify.
299 : As such, it assumes funk is non-NULL, fd_funk_{wksp,rec_map,wksp_tag}
300 : have been verified to work and the rec_map has been verified. */
301 :
302 : int
303 : fd_funk_val_verify( fd_funk_t * funk );
304 :
305 : FD_PROTOTYPES_END
306 :
307 : /* TODO: Retune fd_alloc and fd_wksp for Solana record size optimized
308 : size classes and transition point to fd_wksp backing. */
309 :
310 : #endif /* HEADER_fd_src_funk_fd_funk_val_h */
|