Line data Source code
1 : /* Serialize a chunk of values.
2 : Each call may only produce up to FD_SSMANIFEST_BUF_MIN bytes worth of
3 : serialized data. Therefore, we split the serializer into multiple
4 : states. */
5 :
6 1296 : ENCODE_FN {
7 1296 : fd_bank_t const * bank = enc->bank;
8 :
9 1296 : PREP
10 :
11 1296 : switch( enc->state ) {
12 24 : case STATE_BLOCKHASH_QUEUE: {
13 12 : fd_blockhashes_t const * bhq = &bank->f.block_hash_queue;
14 12 : fd_blockhash_info_t const * deq = bhq->d.deque;
15 12 : ulong total = fd_blockhash_deq_cnt( deq );
16 :
17 : /* Write the newest FD_BLOCKHASHES_MAX entries by count, each at its
18 : own absolute hash_index. with skipped indices it can keep an
19 : entry older than max_age. */
20 24 : ulong cnt = fd_ulong_min( total, FD_BLOCKHASHES_MAX );
21 12 : ulong to_skip = total - cnt;
22 :
23 12 : fd_blockhash_info_t const * last = fd_blockhashes_peek_last( bhq );
24 24 : PUSH_VAL( ulong, last ? last->hash_index : 0UL ); /* last hash index */
25 24 : PUSH_VAL( uchar, !!last );
26 24 : if( last ) PUSH_VAL( fd_hash_t, last->hash );
27 :
28 24 : PUSH_VAL( ulong, cnt );
29 120 : for( ulong i=to_skip; i<total; i++ ) {
30 96 : fd_blockhash_info_t const * ele = fd_blockhash_deq_peek_index_const( deq, i );
31 96 : PUSH_VAL( fd_hash_t, ele->hash );
32 96 : PUSH_VAL( ulong, ele->lamports_per_signature );
33 96 : PUSH_VAL( ulong, ele->hash_index );
34 96 : PUSH_VAL( ulong, 0UL ); /* timestamp, ignored */
35 48 : }
36 24 : PUSH_VAL( ulong, FD_BLOCKHASHES_MAX-1UL ); /* max_age */
37 24 : enc->state = STATE_HASHES;
38 12 : break;
39 12 : }
40 24 : case STATE_HASHES: {
41 24 : PUSH_VAL( ulong, 0UL ); /* ancestors */
42 24 : PUSH_VAL( fd_hash_t, bank->f.bank_hash );
43 24 : PUSH_VAL( fd_hash_t, bank->f.prev_bank_hash );
44 24 : PUSH_VAL( ulong, bank->f.parent_slot );
45 24 : enc->state = STATE_HARD_FORKS;
46 12 : break;
47 12 : }
48 24 : case STATE_HARD_FORKS: {
49 24 : PUSH_VAL( ulong, bank->f.hard_fork_cnt );
50 24 : for( ulong i=0UL; i<bank->f.hard_fork_cnt; i++ ) {
51 0 : PUSH_VAL( ulong, bank->f.hard_forks[ i ].slot );
52 0 : PUSH_VAL( ulong, bank->f.hard_forks[ i ].cnt );
53 0 : }
54 24 : enc->state = STATE_COUNTERS;
55 12 : break;
56 12 : }
57 24 : case STATE_COUNTERS: {
58 24 : PUSH_VAL( ulong, bank->f.parent_txn_count + bank->f.txn_count );
59 24 : PUSH_VAL( ulong, bank->f.max_tick_height );
60 24 : PUSH_VAL( ulong, bank->f.signature_count );
61 24 : PUSH_VAL( ulong, bank->f.capitalization );
62 24 : PUSH_VAL( ulong, bank->f.max_tick_height );
63 24 : PUSH_VAL( uchar, 0 ); /* hashes_per_tick: None */
64 24 : PUSH_VAL( ulong, bank->f.ticks_per_slot );
65 24 : PUSH_VAL( fd_w_u128_t, (fd_w_u128_t){ .ud = bank->f.slot_params.ns_per_slot } );
66 24 : PUSH_VAL( ulong, bank->f.genesis_creation_time );
67 24 : PUSH_VAL( double, bank->f.slot_params.slots_per_year );
68 24 : PUSH_VAL( ulong, 0UL ); /* accounts_data_len, unused */
69 24 : PUSH_VAL( ulong, bank->f.slot );
70 24 : PUSH_VAL( ulong, bank->f.epoch );
71 24 : PUSH_VAL( ulong, bank->f.block_height );
72 24 : PUSH_VAL( fd_pubkey_t, enc->leader );
73 24 : PUSH_VAL( ulong, 0UL ); /* unused_collector_fees */
74 24 : PUSH_VAL( ulong, 0UL ); /* unused_fee_calculator */
75 :
76 12 : fd_fee_rate_governor_t const * frg = &bank->f.fee_rate_governor;
77 24 : PUSH_VAL( ulong, frg->target_lamports_per_signature );
78 24 : PUSH_VAL( ulong, frg->target_signatures_per_slot );
79 24 : PUSH_VAL( ulong, frg->min_lamports_per_signature );
80 24 : PUSH_VAL( ulong, frg->max_lamports_per_signature );
81 24 : PUSH_VAL( uchar, frg->burn_percent );
82 24 : PUSH_VAL( ulong, 0UL );
83 24 : PUSH_VAL( ulong, bank->f.epoch );
84 :
85 12 : fd_epoch_schedule_t const * es = &bank->f.epoch_schedule;
86 12 : fd_rent_t const * rent = &bank->f.rent;
87 24 : PUSH_VAL( ulong, es->slots_per_epoch );
88 24 : PUSH_VAL( ulong, es->leader_schedule_slot_offset );
89 24 : PUSH_VAL( uchar, es->warmup );
90 24 : PUSH_VAL( ulong, es->first_normal_epoch );
91 24 : PUSH_VAL( ulong, es->first_normal_slot );
92 24 : PUSH_VAL( double, bank->f.slot_params.slots_per_year );
93 24 : PUSH_VAL( ulong, rent->lamports_per_uint8_year );
94 24 : PUSH_VAL( double, rent->exemption_threshold );
95 24 : PUSH_VAL( uchar, rent->burn_percent );
96 24 : PUSH_VAL( ulong, es->slots_per_epoch );
97 24 : PUSH_VAL( ulong, es->leader_schedule_slot_offset );
98 24 : PUSH_VAL( uchar, es->warmup );
99 24 : PUSH_VAL( ulong, es->first_normal_epoch );
100 24 : PUSH_VAL( ulong, es->first_normal_slot );
101 :
102 12 : fd_inflation_t const * inf = &bank->f.inflation;
103 24 : PUSH_VAL( double, inf->initial );
104 24 : PUSH_VAL( double, inf->terminal );
105 24 : PUSH_VAL( double, inf->taper );
106 24 : PUSH_VAL( double, inf->foundation );
107 24 : PUSH_VAL( double, inf->foundation_term );
108 24 : PUSH_VAL( double, inf->unused );
109 :
110 24 : enc->state = STATE_VOTE_ACCOUNTS;
111 12 : break;
112 12 : }
113 24 : case STATE_VOTE_ACCOUNTS: {
114 24 : PUSH_VAL( ulong, 0UL ); /* zero vote_accounts */
115 24 : PUSH_VAL( ulong, 0UL ); /* zero stake delegations */
116 24 : PUSH_VAL( ulong, 0UL ); /* unused */
117 24 : PUSH_VAL( ulong, bank->f.epoch );
118 24 : enc->state = STATE_STAKE_HISTORY;
119 12 : break;
120 12 : }
121 0 : case STATE_STAKE_DELEGATION: { FD_LOG_ERR(( "TODO")); }
122 0 : case STATE_STAKE_EPOCH: { FD_LOG_ERR(( "TODO")); }
123 24 : case STATE_STAKE_HISTORY: {
124 24 : PUSH_VAL( ulong, 0UL ); /* zero stake history entries */
125 24 : enc->state = STATE_BANK_TRAILER;
126 12 : break;
127 12 : }
128 24 : case STATE_BANK_TRAILER: {
129 24 : PUSH_VAL( ulong, 0UL ); /* unused_accounts.unused1 */
130 24 : PUSH_VAL( ulong, 0UL ); /* unused_accounts.unused2 */
131 24 : PUSH_VAL( ulong, 0UL ); /* unused_accounts.unused3 */
132 24 : PUSH_VAL( ulong, 0UL ); /* unused_epoch_stakes */
133 24 : PUSH_VAL( uchar, 0 ); /* is_delta */
134 24 : PUSH_VAL( ulong, 0UL ); /* zero account_storage_entries */
135 24 : enc->state = STATE_BANK_HASH_INFO;
136 12 : break;
137 12 : }
138 0 : case STATE_ACCOUNT_STORAGE_ENTRY: { FD_LOG_ERR(( "TODO")); }
139 24 : case STATE_BANK_HASH_INFO: {
140 : /* AccountsDbFields */
141 24 : PUSH_VAL( ulong, 0UL ); /* write_version, unused */
142 24 : PUSH_VAL( ulong, bank->f.slot ); /* slot */
143 24 : PUSH_VAL( fd_hash_t, (fd_hash_t){0} ); /* unused_accounts_delta_hash */
144 24 : PUSH_VAL( fd_hash_t, (fd_hash_t){0} ); /* unused_accounts_hash */
145 24 : PUSH_VAL( ulong, 0UL ); /* num_updated_accounts, unused */
146 24 : PUSH_VAL( ulong, 0UL ); /* num_removed_accounts, unused */
147 24 : PUSH_VAL( ulong, 0UL ); /* num_lamports_stored, unused */
148 24 : PUSH_VAL( ulong, 0UL ); /* total_data_len, unused */
149 24 : PUSH_VAL( ulong, 0UL ); /* num_executable_accounts, unused */
150 24 : PUSH_VAL( ulong, 0UL ); /* historical_roots, unused */
151 24 : PUSH_VAL( ulong, 0UL ); /* historical_roots_with_hash, unused */
152 : /* ExtraFieldsToSerialize */
153 24 : PUSH_VAL( ulong, bank->f.rbh_lamports_per_sig );
154 24 : PUSH_VAL( uchar, 0 ); /* unused_incremental_snapshot_persistence */
155 24 : PUSH_VAL( uchar, 0 ); /* unused_epoch_accounts_hash */
156 :
157 12 : ulong epoch = bank->f.epoch;
158 12 : ulong epoch_cnt = fd_ulong_min( epoch, 3UL ) + 2UL; /* keys max(E-3,0)..E+1, as agave retains */
159 24 : PUSH_VAL( ulong, epoch_cnt );
160 12 : enc->epoch_cnt = (uchar)epoch_cnt;
161 12 : enc->epoch_idx = 0;
162 24 : enc->state = STATE_EPOCH_STAKES;
163 12 : break;
164 12 : }
165 120 : case STATE_EPOCH_STAKES: {
166 60 : ulong epoch_key = epoch_stakes_key ( bank, enc->epoch_idx );
167 60 : int iter_kind = epoch_stakes_iter_kind( bank, enc->epoch_idx );
168 60 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
169 60 : ushort fork_id = bank->vote_stakes_fork_id;
170 :
171 60 : uint vote_cnt = 0U;
172 60 : for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_iter_init( vote_stakes, fork_id, iter_kind, enc->vote_stakes_iter_mem );
173 552 : !fd_vote_stakes_iter_done( vote_stakes, fork_id, iter_kind, iter );
174 432 : fd_vote_stakes_iter_next( vote_stakes, fork_id, iter_kind, iter ) ) vote_cnt++;
175 60 : fd_vote_stakes_iter_init( vote_stakes, fork_id, iter_kind, enc->vote_stakes_iter_mem );
176 60 : enc->vote_cnt = vote_cnt;
177 60 : enc->vote_idx = 0;
178 60 : enc->total_stake = 0UL;
179 :
180 120 : PUSH_VAL( ulong, epoch_key );
181 120 : PUSH_VAL( uint, 0U ); /* variant = 0 (VersionedEpochStakes::Current) */
182 120 : PUSH_VAL( ulong, (ulong)vote_cnt );
183 :
184 120 : enc->state = (vote_cnt > 0U) ? STATE_EPOCH_STAKES_STAKES : STATE_EPOCH_STAKES_EPOCH;
185 60 : break;
186 60 : }
187 432 : case STATE_EPOCH_STAKES_STAKES: {
188 432 : int iter_kind = epoch_stakes_iter_kind( bank, enc->epoch_idx );
189 :
190 432 : ulong ec_cnt = 0UL;
191 432 : fd_epoch_credits_t const * ec = NULL;
192 :
193 432 : fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
194 432 : ushort fork_id = bank->vote_stakes_fork_id;
195 :
196 432 : fd_vote_stakes_iter_t * iter = fd_type_pun( enc->vote_stakes_iter_mem );
197 432 : FD_TEST( !fd_vote_stakes_iter_done( vote_stakes, fork_id, iter_kind, iter ) );
198 432 : fd_vote_stakes_ele_t ele[1];
199 432 : fd_vote_stakes_iter_ele( vote_stakes, fork_id, iter_kind, iter, ele );
200 :
201 432 : if( iter_kind==FD_VOTE_STAKES_ITER_T_1 ) {
202 72 : ec = find_epoch_credits( enc->bank, &ele->pubkey );
203 72 : FD_TEST( ec );
204 72 : ec_cnt = ec->cnt;
205 72 : }
206 :
207 : /* SIMD-0232 collectors. t-3..t-5 collectors are never consulted on
208 : reload; encoded as zero. */
209 432 : fd_pubkey_t inflation_collector = {0};
210 432 : fd_pubkey_t block_collector = {0};
211 432 : if( FD_LIKELY( iter_kind==FD_VOTE_STAKES_ITER_T_1 || iter_kind==FD_VOTE_STAKES_ITER_T_2 ) ) {
212 144 : inflation_collector = ele->inflation_collector;
213 144 : block_collector = ele->block_collector;
214 144 : }
215 :
216 : /* A zeroed key means no BLS key is registered (serialized as None) */
217 432 : static uchar const no_bls_key[ FD_BLS_PUB_COMPRESSED_SZ ] = {0};
218 432 : int has_bls = !fd_memeq( ele->bls_key, no_bls_key, FD_BLS_PUB_COMPRESSED_SZ );
219 432 : int has_location = ele->location.x || ele->location.y || ele->location.z;
220 :
221 432 : enc->total_stake += ele->stake;
222 :
223 432 : FD_TEST( ec_cnt<=FD_EPOCH_CREDITS_MAX );
224 :
225 432 : ulong data_length = fd_ulong_if( has_location, FD_VOTE_STATE_V5_SZ, 186UL + fd_ulong_if( !!has_bls, FD_BLS_PUB_COMPRESSED_SZ, 0UL ) + 24UL * ec_cnt );
226 :
227 : /* Vote account key + stake */
228 432 : PUSH_VAL( fd_pubkey_t, ele->pubkey );
229 432 : PUSH_VAL( ulong, ele->stake );
230 :
231 : /* AccountSharedData: lamports, data_length */
232 432 : PUSH_VAL( ulong, 0UL );
233 432 : PUSH_VAL( ulong, data_length );
234 :
235 432 : if( has_location ) {
236 72 : ulong epoch_key = epoch_stakes_key( bank, enc->epoch_idx );
237 72 : ulong ec_skip = fd_ulong_sat_sub( ec_cnt, FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX );
238 72 : fd_vote_state_v5_t v5 = {
239 72 : .node_pubkey = ele->node_account,
240 72 : .inflation_rewards_collector = inflation_collector,
241 72 : .block_revenue_collector = block_collector,
242 72 : .inflation_rewards_commission_bps_len = 1,
243 72 : .block_revenue_commission_bps_len = 1,
244 72 : .pending_delegator_rewards = ele->pending_delegator_rewards,
245 72 : .location = ele->location,
246 72 : .inflation_rewards_commission_bps_epochs = { epoch_key },
247 72 : .inflation_rewards_commission_bps = { ele->commission },
248 72 : .block_revenue_commission_bps_epochs = { epoch_key },
249 72 : .block_revenue_commission_bps = { ele->block_revenue_commission_bps },
250 72 : .latest_vote = ULONG_MAX,
251 72 : .root_slot = ULONG_MAX,
252 72 : .accumulated_lamports_len = (uint)( ec_cnt-ec_skip ),
253 72 : .rank_epoch = ULONG_MAX,
254 72 : .rank = USHORT_MAX
255 72 : };
256 72 : memcpy( v5.bls_pubkey_compressed, ele->bls_key, FD_BLS_PUB_COMPRESSED_SZ );
257 144 : for( ulong j=ec_skip; j<ec_cnt; j++ ) {
258 72 : v5.accumulated_lamports[ j-ec_skip ] = (fd_vote_epoch_credits_t){ (ulong)ec->epoch[j],
259 72 : ec->base_credits + (ulong)ec->credits_delta[j],
260 72 : ec->base_credits + (ulong)ec->prev_credits_delta[j] };
261 72 : }
262 72 : typedef struct { uchar b[ FD_VOTE_STATE_V5_SZ-sizeof(uint)-sizeof(fd_vote_state_v5_t) ]; } v5_tail_t;
263 72 : PUSH_VAL( uint, fd_vote_state_versioned_enum_v5 );
264 72 : PUSH_VAL( fd_vote_state_v5_t, v5 );
265 72 : PUSH_VAL( v5_tail_t, (v5_tail_t){0} );
266 360 : } else {
267 : /* VoteStateV4 */
268 360 : PUSH_VAL( uint, 3U ); /* variant = V4 */
269 360 : PUSH_VAL( fd_pubkey_t, ele->node_account ); /* node_pubkey */
270 360 : PUSH_VAL( fd_pubkey_t, (fd_pubkey_t){0} ); /* authorized_withdrawer */
271 360 : PUSH_VAL( fd_pubkey_t, inflation_collector ); /* inflation_rewards_collector */
272 360 : PUSH_VAL( fd_pubkey_t, block_collector ); /* block_revenue_collector */
273 360 : PUSH_VAL( ushort, ele->commission ); /* inflation_rewards_commission_bps */
274 360 : PUSH_VAL( ushort, ele->block_revenue_commission_bps ); /* block_revenue_commission_bps */
275 360 : PUSH_VAL( ulong, ele->pending_delegator_rewards ); /* pending_delegator_rewards */
276 360 : if( has_bls ) {
277 0 : typedef struct { uchar b[ FD_BLS_PUB_COMPRESSED_SZ ]; } bls_key_compressed_t;
278 0 : PUSH_VAL( uchar, 1 ); /* bls_pubkey_compressed = Some */
279 0 : PUSH_VAL( bls_key_compressed_t, *(bls_key_compressed_t const *)ele->bls_key );
280 360 : } else {
281 360 : PUSH_VAL( uchar, 0 ); /* bls_pubkey_compressed = None */
282 180 : }
283 360 : PUSH_VAL( ulong, 0UL ); /* votes_length = 0 */
284 360 : PUSH_VAL( uchar, 0 ); /* root_slot = None */
285 360 : PUSH_VAL( ulong, 0UL ); /* authorized_voters_length = 0 */
286 :
287 : /* Epoch credits */
288 360 : PUSH_VAL( ulong, ec_cnt );
289 504 : for( ulong j=0UL; j<ec_cnt; j++ ) {
290 144 : PUSH_VAL( ulong, (ulong)ec->epoch[j] );
291 144 : PUSH_VAL( ulong, ec->base_credits + (ulong)ec->credits_delta[j] );
292 144 : PUSH_VAL( ulong, ec->base_credits + (ulong)ec->prev_credits_delta[j] );
293 72 : }
294 :
295 360 : PUSH_VAL( ulong, 0UL ); /* last_timestamp_slot */
296 360 : PUSH_VAL( ulong, 0UL ); /* last_timestamp_timestamp */
297 180 : }
298 :
299 : /* AccountSharedData trailer */
300 432 : PUSH_VAL( fd_pubkey_t, fd_solana_vote_program_id ); /* owner */
301 432 : PUSH_VAL( uchar, 1 ); /* executable */
302 432 : PUSH_VAL( ulong, 0UL ); /* rent_epoch */
303 :
304 216 : enc->vote_idx++;
305 216 : fd_vote_stakes_iter_next( vote_stakes, fork_id, iter_kind, iter );
306 432 : if( enc->vote_idx >= enc->vote_cnt ) enc->state = STATE_EPOCH_STAKES_EPOCH;
307 216 : break;
308 216 : }
309 120 : case STATE_EPOCH_STAKES_EPOCH: {
310 60 : ulong epoch_key = epoch_stakes_key( bank, enc->epoch_idx );
311 :
312 120 : PUSH_VAL( ulong, 0UL ); /* stake_delegations_length = 0 */
313 120 : PUSH_VAL( ulong, 0UL ); /* unused */
314 120 : PUSH_VAL( ulong, epoch_key ); /* epoch */
315 120 : PUSH_VAL( ulong, 0UL ); /* stake_history_length = 0 */
316 120 : enc->state = STATE_EPOCH_TOTAL_STAKE;
317 60 : break;
318 60 : }
319 120 : case STATE_EPOCH_TOTAL_STAKE: {
320 60 : int iter_kind = epoch_stakes_iter_kind( bank, enc->epoch_idx );
321 120 : ulong total_stake = (iter_kind==FD_VOTE_STAKES_ITER_T_1) ? bank->f.total_epoch_stake : enc->total_stake;
322 120 : PUSH_VAL( ulong, total_stake );
323 120 : enc->state = STATE_NODE_VOTE_ACCOUNTS;
324 60 : break;
325 60 : }
326 120 : case STATE_NODE_VOTE_ACCOUNTS: {
327 : /* node_id_to_vote_accounts: HashMap<node, (Vec<vote>, total_stake)> */
328 60 : fd_ssmanifest_epoch_map_t const * map = &enc->epoch_map[ enc->epoch_idx ];
329 120 : PUSH_VAL( ulong, map->node_cnt );
330 336 : for( ulong i=0UL; i<map->vote_cnt; ) {
331 216 : fd_pubkey_t const * node = &map->vote[ i ].node;
332 216 : ulong j = i;
333 216 : ulong total_stake = 0UL;
334 456 : for( ; j<map->vote_cnt && fd_memeq( map->vote[ j ].node.uc, node->uc, sizeof(fd_pubkey_t) ); j++ ) {
335 240 : total_stake += map->vote[ j ].stake;
336 240 : }
337 216 : PUSH_VAL( fd_pubkey_t, *node );
338 216 : PUSH_VAL( ulong, j-i );
339 456 : for( ulong k=i; k<j; k++ ) {
340 240 : PUSH_VAL( fd_pubkey_t, map->vote[ k ].vote );
341 120 : }
342 216 : PUSH_VAL( ulong, total_stake );
343 216 : i = j;
344 216 : }
345 120 : enc->state = STATE_AUTH_VOTER;
346 60 : break;
347 60 : }
348 120 : case STATE_AUTH_VOTER: {
349 : /* epoch_authorized_voters: HashMap<vote, voter> */
350 60 : fd_ssmanifest_epoch_map_t const * map = &enc->epoch_map[ enc->epoch_idx ];
351 120 : PUSH_VAL( ulong, map->vote_cnt );
352 360 : for( ulong i=0UL; i<map->vote_cnt; i++ ) {
353 240 : PUSH_VAL( fd_pubkey_t, map->vote[ i ].vote );
354 240 : PUSH_VAL( fd_pubkey_t, map->vote[ i ].voter );
355 120 : }
356 60 : enc->epoch_idx++;
357 120 : enc->state = (enc->epoch_idx < enc->epoch_cnt) ? STATE_EPOCH_STAKES : STATE_LTHASH;
358 60 : break;
359 60 : }
360 24 : case STATE_LTHASH: {
361 24 : PUSH_VAL( uchar, 1 );
362 24 : PUSH_VAL( fd_lthash_value_t, bank->f.lthash );
363 24 : enc->state = STATE_BLOCK_ID;
364 12 : break;
365 12 : }
366 24 : case STATE_BLOCK_ID: {
367 12 : int has_block_id = !fd_hash_check_zero( &bank->f.block_id );
368 24 : PUSH_VAL( uchar, (uchar)has_block_id );
369 24 : if( has_block_id ) PUSH_VAL( fd_hash_t, bank->f.block_id );
370 24 : enc->state = STATE_DONE;
371 12 : break;
372 12 : }
373 24 : case STATE_DONE:
374 24 : enc->state = STATE_INIT; /* ready for the next pass */
375 24 : return 0UL;
376 0 : default:
377 0 : FD_LOG_CRIT(( "invalid state reached (%u)", enc->state ));
378 1296 : }
379 :
380 1272 : return RET_EXPR;
381 1296 : }
382 :
383 : #undef PREP
384 : #undef ENCODE_FN
385 : #undef PUSH_VAL
386 : #undef RET_EXPR
|