Line data Source code
1 : #ifndef HEADER_fd_src_funk_fd_funk_rec_h
2 : #define HEADER_fd_src_funk_fd_funk_rec_h
3 :
4 : /* This provides APIs for managing funk records. It is generally not
5 : meant to be included directly. Use fd_funk.h instead. */
6 :
7 : #include "fd_funk_txn.h" /* Includes fd_funk_base.h */
8 :
9 : /* FD_FUNK_REC_{ALIGN,FOOTPRINT} describe the alignment and footprint of
10 : a fd_funk_rec_t. ALIGN will be an power of 2, footprint will be a
11 : multiple of align. These are provided to facilitate compile time
12 : declarations. */
13 :
14 : #define FD_FUNK_REC_ALIGN (32UL)
15 :
16 : /* FD_FUNK_REC_FLAG_* are flags that can be bit-ored together to specify
17 : how records are to be interpreted.
18 :
19 : - ERASE indicates a record in an in-preparation transaction should be
20 : erased if and when the in-preparation transaction is published. If
21 : set, there will be no value resources used by this record. Will not
22 : be set on a published record. Will not be set if an in-preparation
23 : transaction ancestor has this record with erase set. If set, the
24 : first ancestor transaction encountered (going from youngest to
25 : oldest) will not have erased set. */
26 :
27 744189564 : #define FD_FUNK_REC_FLAG_ERASE (1UL<<0)
28 :
29 : /* FD_FUNK_REC_IDX_NULL gives the map record idx value used to represent
30 : NULL. This value also set a limit on how large rec_max can be. */
31 :
32 4270200807 : #define FD_FUNK_REC_IDX_NULL (ULONG_MAX)
33 :
34 : /* FD_FUNK_PART_NULL is the partition number of records that are not
35 : in a partition */
36 655138083 : #define FD_FUNK_PART_NULL (UINT_MAX)
37 :
38 : /* A fd_funk_rec_t describes a funk record. */
39 :
40 : struct __attribute__((aligned(FD_FUNK_REC_ALIGN))) fd_funk_rec {
41 :
42 : /* These fields are managed by the funk's rec_map */
43 :
44 : fd_funk_xid_key_pair_t pair; /* Transaction id and record key pair */
45 : ulong map_next; /* Internal use by map */
46 : ulong map_hash; /* Internal use by map */
47 :
48 : /* These fields are managed by funk. TODO: Consider using record
49 : index compression here (much more debatable than in txn itself). */
50 :
51 : ulong prev_idx; /* Record map index of previous record */
52 : ulong next_idx; /* Record map index of next record */
53 : uint txn_cidx; /* Compressed transaction map index (or compressed FD_FUNK_TXN_IDX if this is in the last published) */
54 : uint tag; /* Internal use only */
55 : ulong flags; /* Flags that indicate how to interpret a record */
56 :
57 : /* Note: use of uint here requires FD_FUNK_REC_VAL_MAX to be at most
58 : UINT_MAX. */
59 :
60 : uint val_sz; /* Num bytes in record value, in [0,val_max] */
61 : uint val_max; /* Max byte in record value, in [0,FD_FUNK_REC_VAL_MAX], 0 if erase flag set or val_gaddr is 0 */
62 : ulong val_gaddr; /* Wksp gaddr on record value if any, 0 if erase flag set or val_max is 0
63 : If non-zero, the region [val_gaddr,val_gaddr+val_max) will be a current fd_alloc allocation (such that it is
64 : has tag wksp_tag) and the owner of the region will be the record. IMPORTANT! HAS NO GUARANTEED ALIGNMENT! */
65 :
66 : ulong prev_part_idx; /* Record map index of previous record in partition chain */
67 : ulong next_part_idx; /* Record map index of next record in partition chain */
68 : uint part; /* Partition number, FD_FUNK_PART_NULL if none */
69 :
70 : int val_no_free; /* If set, do not call alloc_free on the value */
71 :
72 : /* Padding to FD_FUNK_REC_ALIGN here (TODO: consider using self index
73 : in the structures to accelerate indexing computations if padding
74 : permits as this structure is currently has 8 bytes of padding) */
75 : };
76 :
77 : typedef struct fd_funk_rec fd_funk_rec_t;
78 :
79 : FD_STATIC_ASSERT( sizeof(fd_funk_rec_t) == 5U*32U, record size is wrong );
80 :
81 : /* fd_funk_rec_map allows for indexing records by their (xid,key) pair.
82 : It is used to store all records of the last published transaction and
83 : the records being updated for a transaction that is in-preparation.
84 : Published records are stored under the pair (root,key). (This is
85 : done so that publishing a transaction doesn't require updating all
86 : transaction id of all the records that were not updated by the
87 : publish.) */
88 :
89 : #define MAP_NAME fd_funk_rec_map
90 : #define MAP_T fd_funk_rec_t
91 : #define MAP_KEY_T fd_funk_xid_key_pair_t
92 : #define MAP_KEY pair
93 1064751135 : #define MAP_KEY_EQ(k0,k1) fd_funk_xid_key_pair_eq((k0),(k1))
94 584361765 : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
95 6478461 : #define MAP_KEY_COPY(kd,ks) fd_funk_xid_key_pair_copy((kd),(ks))
96 9627330816 : #define MAP_NEXT map_next
97 : #define MAP_HASH map_hash
98 : #define MAP_MAGIC (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
99 : #define MAP_IMPL_STYLE 1
100 : #define MAP_MEMOIZE 1
101 : #include "../util/tmpl/fd_map_giant.c"
102 :
103 : FD_PROTOTYPES_BEGIN
104 :
105 : FD_FN_PURE ulong fd_funk_rec_map_list_idx( fd_funk_rec_t const * join, fd_funk_xid_key_pair_t const * key );
106 :
107 : void fd_funk_rec_map_set_key_cnt( fd_funk_rec_t * join, ulong key_cnt );
108 :
109 : /* fd_funk_rec_idx_is_null returns 1 if idx is FD_FUNK_REC_IDX_NULL and
110 : 0 otherwise. */
111 :
112 4063155303 : FD_FN_CONST static inline int fd_funk_rec_idx_is_null( ulong idx ) { return idx==FD_FUNK_REC_IDX_NULL; }
113 :
114 : /* Accessors */
115 :
116 : /* fd_funk_rec_cnt returns the number of records in the record map.
117 : Assumes map==fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) where funk
118 : is a current local join. See fd_funk.h for fd_funk_rec_max. */
119 :
120 3 : FD_FN_PURE static inline ulong fd_funk_rec_cnt( fd_funk_rec_t const * map ) { return fd_funk_rec_map_key_cnt( map ); }
121 :
122 : /* fd_funk_rec_is_full returns 1 if the record map is full (i.e. the
123 : maximum of records that can be concurrently tracked has been reached)
124 : and 0 otherwise. Note that this includes all the records in the last
125 : published transactions and records being updated by in-preparation
126 : transactions. Assumes funk is a current local join and
127 : map==fd_funk_rec_map( funk, fd_funk_wksp( funk ) ). */
128 :
129 52934394 : FD_FN_PURE static inline int fd_funk_rec_is_full( fd_funk_rec_t const * map ) { return fd_funk_rec_map_is_full( map ); }
130 :
131 : /* fd_funk_rec_query queries the in-preparation transaction pointed to
132 : by txn for the record whose key matches the key pointed to by key.
133 : If txn is NULL, the query will be done for the funk's last published
134 : transaction. Returns a pointer to current record on success and NULL
135 : on failure. Reasons for failure include txn is neither NULL nor a
136 : pointer to a in-preparation transaction, key is NULL or not a record
137 : in the given transaction.
138 :
139 : The returned pointer is in the caller's address space and, if the
140 : return value is non-NULL, the lifetime of the returned pointer is the
141 : lesser of the current local join, the key is removed from the
142 : transaction, the lifetime of the in-preparation transaction (txn is
143 : non-NULL) or the next successful publication (txn is NULL).
144 :
145 : Assumes funk is a current local join (NULL returns NULL), txn is NULL
146 : or points to an in-preparation transaction in the caller's address
147 : space, key points to a record key in the caller's address space (NULL
148 : returns NULL), and no concurrent operations on funk, txn or key.
149 : funk retains no interest in key. The funk retains ownership of any
150 : returned record. The record value metadata will be updated whenever
151 : the record value modified.
152 :
153 : These are a reasonably fast O(1).
154 :
155 : fd_funk_rec_query_global is the same but will query txn's ancestors
156 : for key from youngest to oldest if key is not part of txn. As such,
157 : the txn of the returned record may not match txn but will be the txn
158 : of most recent ancestor with the key otherwise.
159 :
160 : fd_funk_rec_query_global_const is the same but it is safe to have
161 : multiple threads concurrently run queries.
162 :
163 : Important safety tip! These functions can potentially return records
164 : that have the ERASE flag set. (This allows, for example, a caller to
165 : discard an erase for an unfrozen in-preparation transaction.) In
166 : such cases, the record will have no value resources in use.
167 :
168 : These are a reasonably fast O(in_prep_ancestor_cnt). */
169 :
170 : FD_FN_PURE fd_funk_rec_t const *
171 : fd_funk_rec_query( fd_funk_t * funk,
172 : fd_funk_txn_t const * txn,
173 : fd_funk_rec_key_t const * key );
174 :
175 :
176 : /* fd_funk_rec_query_global is a query that searches for the
177 : supplied key by walking up the funk txn stack.
178 :
179 : if txn_out is supplied (non-null), the txn the key was found in
180 : is returned. If *txn_out == NULL, the key was found in the root
181 : context */
182 : FD_FN_PURE fd_funk_rec_t const *
183 : fd_funk_rec_query_global( fd_funk_t * funk,
184 : fd_funk_txn_t const * txn,
185 : fd_funk_rec_key_t const * key,
186 : fd_funk_txn_t const ** txn_out );
187 :
188 : /* fd_funk_rec_query_safe is a query that is safe in the presence of
189 : concurrent writes. The result data is copied into a buffer
190 : allocated by the given valloc and should be freed with the same
191 : valloc. NULL is returned if the query fails. The query is always
192 : against the root transaction. */
193 :
194 : FD_FN_PURE void *
195 : fd_funk_rec_query_safe( fd_funk_t * funk,
196 : fd_funk_rec_key_t const * key,
197 : fd_valloc_t valloc,
198 : ulong * result_len );
199 :
200 : FD_FN_PURE void *
201 : fd_funk_rec_query_xid_safe( fd_funk_t * funk,
202 : fd_funk_rec_key_t const * key,
203 : fd_funk_txn_xid_t const * xid,
204 : fd_valloc_t valloc,
205 : ulong * result_len );
206 :
207 : /* fd_funk_rec_test tests the record pointed to by rec. Returns
208 : FD_FUNK_SUCCESS (0) if rec appears to be a live unfrozen record in
209 : funk and a FD_FUNK_ERR_* (negative) otherwise. Specifically:
210 :
211 : FD_FUNK_ERR_INVAL - bad inputs (NULL funk, NULL rec, rec is clearly
212 : not from funk, etc)
213 :
214 : FD_FUNK_ERR_KEY - the record did not appear to be a live record.
215 : Specifically rec's (xid,key) did not resolve to to itself.
216 :
217 : FD_FUNK_ERR_XID - memory corruption was detected in testing rec
218 :
219 : FD_FUNK_ERR_FROZEN - rec is part of a frozen transaction
220 :
221 : If fd_funk_rec_test returns SUCCESS, modify and remove are guaranteed
222 : to succeed immediately after return. The value returned by test will
223 : stable for the same lifetime as a modify.
224 :
225 : Assumes funk is a current local join (NULL returns NULL).
226 :
227 : This is a reasonably fast O(1). */
228 :
229 : FD_FN_PURE int
230 : fd_funk_rec_test( fd_funk_t * funk,
231 : fd_funk_rec_t const * rec );
232 :
233 : /* fd_funk_rec_{pair,xid,key} returns a pointer in the local address
234 : space of the {(transaction id,record key) pair,transaction id,record
235 : key} of a live record. Assumes rec points to a live record in the
236 : caller's address space. The lifetime of the returned pointer is the
237 : same as rec. The value at the pointer will be constant for its
238 : lifetime. */
239 :
240 303882510 : FD_FN_CONST static inline fd_funk_xid_key_pair_t const * fd_funk_rec_pair( fd_funk_rec_t const * rec ) { return &rec->pair; }
241 1631683956 : FD_FN_CONST static inline fd_funk_txn_xid_t const * fd_funk_rec_xid ( fd_funk_rec_t const * rec ) { return rec->pair.xid; }
242 803710527 : FD_FN_CONST static inline fd_funk_rec_key_t const * fd_funk_rec_key ( fd_funk_rec_t const * rec ) { return rec->pair.key; }
243 :
244 : /* fd_funk_rec_txn returns the in-preparation transaction to which the
245 : live record rec belongs or NULL if rec belongs to the last published
246 : transaction.
247 :
248 : fd_funk_rec_{next,prev} returns the {next,prev} record
249 : ({younger,older} record) of the set of records to which rec belongs
250 : or NULL if rec is the {youngest,oldest}.
251 :
252 : fd_funk_txn_rec_{head,tail} returns the {head,tail} record
253 : ({oldest,youngest} record) of the in-preparation transaction to which
254 : rec belongs or NULL if rec_next is the {youngest,oldest} in that
255 : transaction.
256 :
257 : All pointers are in the caller's address space. These are all a fast
258 : O(1) but not fortified against memory data corruption. */
259 :
260 : FD_FN_PURE static inline fd_funk_txn_t const * /* Lifetime as described in fd_funk_txn_query */
261 : fd_funk_rec_txn( fd_funk_rec_t const * rec, /* Assumes live funk record, funk current local join */
262 227968530 : fd_funk_txn_t const * txn_map ) { /* Assumes == fd_funk_txn_map( funk, fd_funk_wksp( funk ) ) */
263 227968530 : ulong txn_idx = fd_funk_txn_idx( rec->txn_cidx );
264 227968530 : if( fd_funk_txn_idx_is_null( txn_idx ) ) return NULL; /* TODO: consider branchless */
265 120381033 : return txn_map + txn_idx;
266 227968530 : }
267 :
268 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
269 : fd_funk_rec_next( fd_funk_rec_t const * rec, /* Assumes live funk record, funk current local join */
270 439209666 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
271 439209666 : ulong rec_idx = rec->next_idx;
272 439209666 : if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL; /* TODO: consider branchless */
273 359823306 : return rec_map + rec_idx;
274 439209666 : }
275 :
276 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
277 : fd_funk_rec_prev( fd_funk_rec_t const * rec, /* Assumes live funk record, funk current local join */
278 219604833 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
279 219604833 : ulong rec_idx = rec->prev_idx;
280 219604833 : if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL; /* TODO: consider branchless */
281 179911653 : return rec_map + rec_idx;
282 219604833 : }
283 :
284 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
285 : fd_funk_txn_rec_head( fd_funk_txn_t const * txn, /* Assumes an in-preparation transaction, funk current local join */
286 164233041 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
287 164233041 : ulong rec_head_idx = txn->rec_head_idx;
288 164233041 : if( fd_funk_rec_idx_is_null( rec_head_idx ) ) return NULL; /* TODO: consider branchless */
289 153933318 : return rec_map + rec_head_idx;
290 164233041 : }
291 :
292 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
293 : fd_funk_txn_rec_tail( fd_funk_txn_t const * txn, /* Assumes an in-preparation transaction, funk current local join */
294 117381282 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
295 117381282 : ulong rec_tail_idx = txn->rec_tail_idx;
296 117381282 : if( fd_funk_rec_idx_is_null( rec_tail_idx ) ) return NULL; /* TODO: consider branchless */
297 117381282 : return rec_map + rec_tail_idx;
298 117381282 : }
299 :
300 : /* fd_funk_rec_modify returns rec as a non-const rec if rec is currently
301 : safe to modify the val / discard a change to the record for an
302 : in-preparation transaction (incl discard an erase) / erase a
303 : published record / etc. Reasons for NULL include NULL funk, NULL
304 : rec, rec does not appear to be a live record, or the transaction to
305 : which rec belongs is frozen.
306 :
307 : The returned pointer is in the caller's address space and, if the
308 : return value is non-NULL, the lifetime of the returned pointer is the
309 : the lesser of the current local join, the key has been removed from
310 : the transaction, the transaction becomes frozen due to birth of an
311 : in-preparation child, (for a key part of an in-preparation
312 : transaction) the lifetime of in-preparation transaction, (for a key
313 : part of the last published transaction) the next successful
314 : publication.
315 :
316 : Assumes funk is a current local join (NULL returns NULL), rec is a
317 : pointer in the caller's address space to a fd_funk_rec_t (NULL
318 : returns NULL), and no concurrent operations on funk or rec. The funk
319 : retains ownership of rec. The record value metadata will be updated
320 : whenever the record value modified.
321 :
322 : This is a reasonably fast O(1). */
323 :
324 : FD_FN_PURE fd_funk_rec_t *
325 : fd_funk_rec_modify( fd_funk_t * funk,
326 : fd_funk_rec_t const * rec );
327 :
328 : /* Returns 1 if the record has been modified in its transaction
329 : compared to the prior incarnation of the record with the same
330 : key (or there is no prior incarnation). Returns -1 if rec is part
331 : of a published transaction. Return 0 otherwise. */
332 :
333 : FD_FN_PURE int
334 : fd_funk_rec_is_modified( fd_funk_t * funk,
335 : fd_funk_rec_t const * rec );
336 :
337 : /* TODO: Consider instead doing something like: modify_init, modify_fini and
338 : preventing forking the txn if records are being modified instead of
339 : the long laundry list of lifetime constraints? */
340 :
341 : /* fd_funk_rec_insert inserts a new record whose key will be the same
342 : as the record key pointed to by key to the in-preparation transaction
343 : pointed to by txn. If txn is NULL, the record will be inserted into
344 : the last published transaction.
345 :
346 : Returns a pointer to the new record on success and NULL on failure.
347 : If opt_err is non-NULL, on return, *opt_err will indicate the result
348 : of the operation.
349 :
350 : FD_FUNK_SUCCESS - success
351 :
352 : FD_FUNK_ERR_INVAL - failed due to bad inputs (NULL funk, NULL key,
353 : txn was neither NULL nor pointer to an in-preparation
354 : transaction)
355 :
356 : FD_FUNK_ERR_REC - failed due to too many records in the func,
357 : increase rec_max
358 :
359 : FD_FUNK_ERR_FROZEN - txn is a transaction that is a parent of an
360 : in-preparation transaction.
361 :
362 : FD_FUNK_ERR_KEY - key referred to an record that is already present
363 : in the transaction.
364 :
365 : The returned pointer is in the caller's address space and, if the
366 : return value is non-NULL, the lifetime of the returned pointer is the
367 : lesser of the current local join, the record is removed, the txn's
368 : lifetime (only applicable if txn is non-NULL) or the next successful
369 : publication (only applicable if txn is NULL).
370 :
371 : Note, if this insert is for a record that in txn with the ERASE flag
372 : set, the ERASE flag of the record will be cleared and it will return
373 : that record.
374 :
375 : Assumes funk is a current local join (NULL returns NULL), txn is NULL
376 : or points to an in-preparation transaction in the caller's address
377 : space, key points to a record key in the caller's address space (NULL
378 : returns NULL), and no concurrent operations on funk, txn or key.
379 : funk retains no interest in key or opt_err. The funk retains
380 : ownership of txn and any returned record. The record value metadata
381 : will be updated whenever the record value modified.
382 :
383 : This is a reasonably fast O(1) and fortified against memory
384 : corruption.
385 :
386 : Note that when a record is newly created, it is initially created
387 : with a NULL value. If intending to modify the value in the most
388 : recent ancestor version of the record, a record can be loaded with
389 : the data via:
390 :
391 : fd_funk_val_copy( rec, fd_funk_val_const( orig_rec ), fd_funk_val_sz( orig_rec ), 0UL, alloc, wksp, NULL );
392 :
393 : // Note: could use fd_funk_val_max( orig_rec ) or some other
394 : // intelligence about planned changes via sz_est instead of 0UL
395 :
396 : This is O(orig_rec) size. If the caller doesn't have the
397 : original record lying around, it can be found via:
398 :
399 : fd_funk_rec_t const * orig_rec = fd_funk_rec_query_global( funk, txn_parent, key, NULL );
400 :
401 : This is O(ancestor depth to orig rec) and accounts for that the
402 : previous version of the record might not be in txn's parent. */
403 :
404 : fd_funk_rec_t const *
405 : fd_funk_rec_insert( fd_funk_t * funk,
406 : fd_funk_txn_t * txn,
407 : fd_funk_rec_key_t const * key,
408 : int * opt_err );
409 :
410 : /* fd_funk_rec_remove removes the live record pointed to by rec from
411 : the funk. Returns FD_FUNK_SUCCESS (0) on success and a FD_FUNK_ERR_*
412 : (negative) on failure. Reasons for failure include:
413 :
414 : FD_FUNK_ERR_INVAL - bad inputs (NULL funk, NULL rec, rec is
415 : obviously not from funk, etc)
416 :
417 : FD_FUNK_ERR_KEY - the record did not appear to be a live record.
418 : Specifically, a record query of funk for rec's (xid,key) pair did
419 : not return rec.
420 :
421 : FD_FUNK_ERR_XID - the record to remove is published but erase was
422 : not specified.
423 :
424 : FD_FUNK_ERR_FROZEN - rec is part of a transaction that is frozen.
425 :
426 : All changes to the record in that transaction will be undone.
427 :
428 : Further, if erase is zero, if and when the transaction is published
429 : (assuming no subsequent insert of key into that transaction), no
430 : changes will be made to the published record. This type of remove
431 : cannot be done on a published record.
432 :
433 : However, if erase is non-zero, the record will cease to exist in that
434 : transaction and any of transaction's subsequently created descendants
435 : (again, assuming no subsequent insert of key). This type of remove
436 : can be done on a published record (assuming the last published
437 : transaction is unfrozen).
438 :
439 : Any information in an erased record is lost.
440 :
441 : Detailed record erasure handling:
442 :
443 : rec's | erase | rec's | rec's | return | info
444 : txn | req | txn | erase | |
445 : frozen | | published | | |
446 : -------+-------+-----------+-------+------------+-----
447 : no | no | no | clear | SUCCESS | discards updates to a record, rec dead on return
448 : no | no | no | set | SUCCESS | discards erase of most recent ancestor, rec dead on return
449 : no | no | yes | clear | ERR_XID | can't revert published record to an older version, rec live on return
450 : no | no | yes | set | *LOG_CRIT* | detected corruption, repurpose to allow for unerasable recs?
451 : no | yes | no | clear | SUCCESS | erase the most recent ancestor version of rec, if no such ancestor version,
452 : | | | | | rec dead on return, otherwise, rec live on return and rec's erase will be
453 : | | | | | set, O(ancestor_hops_to_last_publish) worst case
454 : no | yes | no | set | SUCCESS | no-op, previously marked erase, rec live on return
455 : no | yes | yes | clear | SUCCESS | erases published record, rec dead on return
456 : no | yes | yes | set | *LOG_CRIT* | detected corruption, repurpose to allow for unerasable recs?
457 : yes | - | - | - | ERR_FROZEN | can't remove rec because rec's txn is frozen, rec live on return
458 :
459 : On ERR_INVAL and ERR_KEY, rec didn't seem to point to a live record
460 : on entry with and still doesn't on return.
461 :
462 : Assumes funk is a current local join (NULL returns ERR_INVAL) and rec
463 : points to a record in the caller's address space (NULL returns
464 : ERR_INVAL). As the funk still has ownership of rec before and after
465 : the call if live, the user doesn't need to, for example, match
466 : inserts with removes.
467 :
468 : This is a reasonably fast O(1) except in the case noted above and
469 : fortified against memory corruption.
470 :
471 : IMPORTANT SAFETY TIP! DO NOT CAST AWAY CONST FROM A FD_FUNK_REC_T TO
472 : USE THIS FUNCTION (E.G. PASS A RESULT DIRECTLY FROM QUERY). USE A
473 : LIVE RESULT FROM FD_FUNK_REC_MODIFY! */
474 :
475 : int
476 : fd_funk_rec_remove( fd_funk_t * funk,
477 : fd_funk_rec_t * rec,
478 : int erase );
479 :
480 : /* fd_funk_rec_write_prepare combines several operations into one
481 : convenient package. There are 3 basic cases:
482 :
483 : 1. If the given record key already exists in the transaction, the
484 : record is returned in modifiable form. This is equivalent to
485 : fd_funk_rec_query combined with fd_funk_rec_modify.
486 :
487 : 2. If the record key is entirely new (not present in the
488 : transaction or any of its ancestors), a new record is inserted and
489 : returned in modifiable form. This is equivalent to
490 : fd_funk_rec_insert combined with fd_funk_rec_modify. Note that if
491 : the do_create argument is false, a NULL will be returned in this case.
492 :
493 : 3. Otherwise, the record is copied from the ancestor transaction
494 : into the given transaction. This is returned in modifiable
495 : form. This is equivalent to fd_funk_rec_query_global,
496 : fd_funk_rec_insert, fd_funk_val_copy, and fd_funk_rec_modify.
497 :
498 : In all cases, the record is grown to min_val_size if it is less
499 : than this size, padding with zeros if necessary.
500 :
501 : The irec argument is the previous incarnation of the record if
502 : known (i.e. the result of fd_funk_rec_query_global( funk, txn, key
503 : ) ). This allows the elimination of the query in some cases. Use
504 : NULL if this value is unavailable. */
505 : fd_funk_rec_t *
506 : fd_funk_rec_write_prepare( fd_funk_t * funk, /* Funky database */
507 : fd_funk_txn_t * txn, /* Write the record into this transaction */
508 : fd_funk_rec_key_t const * key, /* Key of new/modified record */
509 : ulong min_val_size, /* Minimum value size of writable record */
510 : int do_create, /* Can create new record */
511 : fd_funk_rec_t const * irec, /* Prior result of fd_funk_rec_query_global if known */
512 : int * opt_err ); /* Optional error code return */
513 :
514 : /* Misc */
515 :
516 : /* fd_funk_rec_verify verifies the record map. Returns FD_FUNK_SUCCESS
517 : if the record map appears intact and FD_FUNK_ERR_INVAL if not (logs
518 : details). Meant to be called as part of fd_funk_verify. As such, it
519 : assumes funk is non-NULL, fd_funk_{wksp,txn_map,rec_map} have been
520 : verified to work and the txn_map has been verified. */
521 :
522 : int
523 : fd_funk_rec_verify( fd_funk_t * funk );
524 :
525 : FD_PROTOTYPES_END
526 :
527 : #endif /* HEADER_fd_src_funk_fd_funk_rec_h */
|