@@ -404,20 +404,20 @@ pub trait BaseStateWithExtensions<S: BaseState> {
404
404
///
405
405
/// Provides the correct answer regardless if the extension is already present
406
406
/// in the TLV data.
407
- fn try_get_new_account_len < V : Extension + VariableLenPack > (
407
+ fn try_get_new_account_len < V : Extension > (
408
408
& self ,
409
- new_extension : & V ,
409
+ new_extension_len : usize ,
410
410
) -> Result < usize , ProgramError > {
411
411
// get the new length used by the extension
412
- let new_extension_len = add_type_and_length_to_len ( new_extension . get_packed_len ( ) ? ) ;
412
+ let new_extension_tlv_len = add_type_and_length_to_len ( new_extension_len ) ;
413
413
let tlv_info = get_tlv_data_info ( self . get_tlv_data ( ) ) ?;
414
414
// If we're adding an extension, then we must have at least BASE_ACCOUNT_LENGTH
415
415
// and account type
416
416
let current_len = tlv_info
417
417
. used_len
418
418
. saturating_add ( BASE_ACCOUNT_AND_TYPE_LENGTH ) ;
419
419
let new_len = if tlv_info. extension_types . is_empty ( ) {
420
- current_len. saturating_add ( new_extension_len )
420
+ current_len. saturating_add ( new_extension_tlv_len )
421
421
} else {
422
422
// get the current length used by the extension
423
423
let current_extension_len = self
@@ -426,7 +426,7 @@ pub trait BaseStateWithExtensions<S: BaseState> {
426
426
. unwrap_or ( 0 ) ;
427
427
current_len
428
428
. saturating_sub ( current_extension_len)
429
- . saturating_add ( new_extension_len )
429
+ . saturating_add ( new_extension_tlv_len )
430
430
} ;
431
431
Ok ( adjust_len_for_multisig ( new_len) )
432
432
}
@@ -1178,6 +1178,58 @@ impl Extension for AccountPaddingTest {
1178
1178
const TYPE : ExtensionType = ExtensionType :: AccountPaddingTest ;
1179
1179
}
1180
1180
1181
+ /// Packs a fixed-length extension into a TLV space
1182
+ ///
1183
+ /// This function reallocates the account as needed to accommodate for the
1184
+ /// change in space.
1185
+ ///
1186
+ /// If the extension already exists, it will overwrite the existing extension
1187
+ /// if `overwrite` is `true`, otherwise it will return an error.
1188
+ ///
1189
+ /// If the extension does not exist, it will reallocate the account and write
1190
+ /// the extension into the TLV buffer.
1191
+ ///
1192
+ /// NOTE: Since this function deals with fixed-size extensions, it does not
1193
+ /// handle _decreasing_ the size of an account's data buffer, like the function
1194
+ /// `alloc_and_serialize_variable_len_extension` does.
1195
+ pub fn alloc_and_serialize < S : BaseState , V : Default + Extension + Pod > (
1196
+ account_info : & AccountInfo ,
1197
+ new_extension : & V ,
1198
+ overwrite : bool ,
1199
+ ) -> Result < ( ) , ProgramError > {
1200
+ let previous_account_len = account_info. try_data_len ( ) ?;
1201
+ let ( new_account_len, extension_already_exists) = {
1202
+ let data = account_info. try_borrow_data ( ) ?;
1203
+ let state = StateWithExtensions :: < S > :: unpack ( & data) ?;
1204
+ let new_account_len = state. try_get_new_account_len :: < V > ( pod_get_packed_len :: < V > ( ) ) ?;
1205
+ let extension_already_exists = state. get_extension_bytes :: < V > ( ) . is_ok ( ) ;
1206
+ ( new_account_len, extension_already_exists)
1207
+ } ;
1208
+
1209
+ if extension_already_exists {
1210
+ if !overwrite {
1211
+ return Err ( TokenError :: ExtensionAlreadyInitialized . into ( ) ) ;
1212
+ } else {
1213
+ // Overwrite the extension
1214
+ let mut buffer = account_info. try_borrow_mut_data ( ) ?;
1215
+ let mut state = StateWithExtensionsMut :: < S > :: unpack ( & mut buffer) ?;
1216
+ let extension = state. get_extension_mut :: < V > ( ) ?;
1217
+ * extension = * new_extension;
1218
+ }
1219
+ } else {
1220
+ // Realloc the account, then write the new extension
1221
+ account_info. realloc ( new_account_len, false ) ?;
1222
+ let mut buffer = account_info. try_borrow_mut_data ( ) ?;
1223
+ if previous_account_len <= BASE_ACCOUNT_LENGTH {
1224
+ set_account_type :: < S > ( * buffer) ?;
1225
+ }
1226
+ let mut state = StateWithExtensionsMut :: < S > :: unpack ( & mut buffer) ?;
1227
+ let extension = state. init_extension :: < V > ( false ) ?;
1228
+ * extension = * new_extension;
1229
+ }
1230
+ Ok ( ( ) )
1231
+ }
1232
+
1181
1233
/// Packs a variable-length extension into a TLV space
1182
1234
///
1183
1235
/// This function reallocates the account as needed to accommodate for the
@@ -1186,7 +1238,7 @@ impl Extension for AccountPaddingTest {
1186
1238
///
1187
1239
/// NOTE: Unlike the `reallocate` instruction, this function will reduce the
1188
1240
/// size of an account if it has too many bytes allocated for the given value.
1189
- pub fn alloc_and_serialize < S : BaseState , V : Extension + VariableLenPack > (
1241
+ pub fn alloc_and_serialize_variable_len_extension < S : BaseState , V : Extension + VariableLenPack > (
1190
1242
account_info : & AccountInfo ,
1191
1243
new_extension : & V ,
1192
1244
overwrite : bool ,
@@ -1195,7 +1247,8 @@ pub fn alloc_and_serialize<S: BaseState, V: Extension + VariableLenPack>(
1195
1247
let ( new_account_len, extension_already_exists) = {
1196
1248
let data = account_info. try_borrow_data ( ) ?;
1197
1249
let state = StateWithExtensions :: < S > :: unpack ( & data) ?;
1198
- let new_account_len = state. try_get_new_account_len ( new_extension) ?;
1250
+ let new_account_len =
1251
+ state. try_get_new_account_len :: < V > ( new_extension. get_packed_len ( ) ?) ?;
1199
1252
let extension_already_exists = state. get_extension_bytes :: < V > ( ) . is_ok ( ) ;
1200
1253
( new_account_len, extension_already_exists)
1201
1254
} ;
@@ -2282,7 +2335,7 @@ mod test {
2282
2335
let current_len = state. try_get_account_len ( ) . unwrap ( ) ;
2283
2336
assert_eq ! ( current_len, Mint :: LEN ) ;
2284
2337
let new_len = state
2285
- . try_get_new_account_len :: < VariableLenMintTest > ( & variable_len )
2338
+ . try_get_new_account_len :: < VariableLenMintTest > ( value_len )
2286
2339
. unwrap ( ) ;
2287
2340
assert_eq ! (
2288
2341
new_len,
@@ -2297,19 +2350,23 @@ mod test {
2297
2350
2298
2351
// Reduce the extension size
2299
2352
let new_len = state
2300
- . try_get_new_account_len :: < VariableLenMintTest > ( & small_variable_len)
2353
+ . try_get_new_account_len :: < VariableLenMintTest > (
2354
+ small_variable_len. get_packed_len ( ) . unwrap ( ) ,
2355
+ )
2301
2356
. unwrap ( ) ;
2302
2357
assert_eq ! ( current_len. checked_sub( new_len) . unwrap( ) , 1 ) ;
2303
2358
2304
2359
// Increase the extension size
2305
2360
let new_len = state
2306
- . try_get_new_account_len :: < VariableLenMintTest > ( & big_variable_len)
2361
+ . try_get_new_account_len :: < VariableLenMintTest > (
2362
+ big_variable_len. get_packed_len ( ) . unwrap ( ) ,
2363
+ )
2307
2364
. unwrap ( ) ;
2308
2365
assert_eq ! ( new_len. checked_sub( current_len) . unwrap( ) , 1 ) ;
2309
2366
2310
2367
// Maintain the extension size
2311
2368
let new_len = state
2312
- . try_get_new_account_len :: < VariableLenMintTest > ( & variable_len)
2369
+ . try_get_new_account_len :: < VariableLenMintTest > ( variable_len. get_packed_len ( ) . unwrap ( ) )
2313
2370
. unwrap ( ) ;
2314
2371
assert_eq ! ( new_len, current_len) ;
2315
2372
}
@@ -2382,7 +2439,8 @@ mod test {
2382
2439
let key = Pubkey :: new_unique ( ) ;
2383
2440
let account_info = ( & key, & mut data) . into_account_info ( ) ;
2384
2441
2385
- alloc_and_serialize :: < Mint , _ > ( & account_info, & variable_len, false ) . unwrap ( ) ;
2442
+ alloc_and_serialize_variable_len_extension :: < Mint , _ > ( & account_info, & variable_len, false )
2443
+ . unwrap ( ) ;
2386
2444
let new_account_len = BASE_ACCOUNT_AND_TYPE_LENGTH + add_type_and_length_to_len ( value_len) ;
2387
2445
assert_eq ! ( data. len( ) , new_account_len) ;
2388
2446
let state = StateWithExtensions :: < Mint > :: unpack ( data. data ( ) ) . unwrap ( ) ;
@@ -2395,12 +2453,18 @@ mod test {
2395
2453
2396
2454
// alloc again succeeds with "overwrite"
2397
2455
let account_info = ( & key, & mut data) . into_account_info ( ) ;
2398
- alloc_and_serialize :: < Mint , _ > ( & account_info, & variable_len, true ) . unwrap ( ) ;
2456
+ alloc_and_serialize_variable_len_extension :: < Mint , _ > ( & account_info, & variable_len, true )
2457
+ . unwrap ( ) ;
2399
2458
2400
2459
// alloc again fails without "overwrite"
2401
2460
let account_info = ( & key, & mut data) . into_account_info ( ) ;
2402
2461
assert_eq ! (
2403
- alloc_and_serialize:: <Mint , _>( & account_info, & variable_len, false ) . unwrap_err( ) ,
2462
+ alloc_and_serialize_variable_len_extension:: <Mint , _>(
2463
+ & account_info,
2464
+ & variable_len,
2465
+ false ,
2466
+ )
2467
+ . unwrap_err( ) ,
2404
2468
TokenError :: ExtensionAlreadyInitialized . into( )
2405
2469
) ;
2406
2470
}
@@ -2429,7 +2493,8 @@ mod test {
2429
2493
let key = Pubkey :: new_unique ( ) ;
2430
2494
let account_info = ( & key, & mut data) . into_account_info ( ) ;
2431
2495
2432
- alloc_and_serialize :: < Mint , _ > ( & account_info, & variable_len, false ) . unwrap ( ) ;
2496
+ alloc_and_serialize_variable_len_extension :: < Mint , _ > ( & account_info, & variable_len, false )
2497
+ . unwrap ( ) ;
2433
2498
let new_account_len = BASE_ACCOUNT_AND_TYPE_LENGTH
2434
2499
+ add_type_and_length_to_len ( value_len)
2435
2500
+ add_type_and_length_to_len ( size_of :: < MetadataPointer > ( ) ) ;
@@ -2447,12 +2512,18 @@ mod test {
2447
2512
2448
2513
// alloc again succeeds with "overwrite"
2449
2514
let account_info = ( & key, & mut data) . into_account_info ( ) ;
2450
- alloc_and_serialize :: < Mint , _ > ( & account_info, & variable_len, true ) . unwrap ( ) ;
2515
+ alloc_and_serialize_variable_len_extension :: < Mint , _ > ( & account_info, & variable_len, true )
2516
+ . unwrap ( ) ;
2451
2517
2452
2518
// alloc again fails without "overwrite"
2453
2519
let account_info = ( & key, & mut data) . into_account_info ( ) ;
2454
2520
assert_eq ! (
2455
- alloc_and_serialize:: <Mint , _>( & account_info, & variable_len, false ) . unwrap_err( ) ,
2521
+ alloc_and_serialize_variable_len_extension:: <Mint , _>(
2522
+ & account_info,
2523
+ & variable_len,
2524
+ false ,
2525
+ )
2526
+ . unwrap_err( ) ,
2456
2527
TokenError :: ExtensionAlreadyInitialized . into( )
2457
2528
) ;
2458
2529
}
@@ -2488,7 +2559,8 @@ mod test {
2488
2559
let key = Pubkey :: new_unique ( ) ;
2489
2560
let account_info = ( & key, & mut data) . into_account_info ( ) ;
2490
2561
let variable_len = VariableLenMintTest { data : vec ! [ 1 , 2 ] } ;
2491
- alloc_and_serialize :: < Mint , _ > ( & account_info, & variable_len, true ) . unwrap ( ) ;
2562
+ alloc_and_serialize_variable_len_extension :: < Mint , _ > ( & account_info, & variable_len, true )
2563
+ . unwrap ( ) ;
2492
2564
2493
2565
let state = StateWithExtensions :: < Mint > :: unpack ( data. data ( ) ) . unwrap ( ) ;
2494
2566
let extension = state. get_extension :: < MetadataPointer > ( ) . unwrap ( ) ;
@@ -2505,7 +2577,8 @@ mod test {
2505
2577
let variable_len = VariableLenMintTest {
2506
2578
data : vec ! [ 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ,
2507
2579
} ;
2508
- alloc_and_serialize :: < Mint , _ > ( & account_info, & variable_len, true ) . unwrap ( ) ;
2580
+ alloc_and_serialize_variable_len_extension :: < Mint , _ > ( & account_info, & variable_len, true )
2581
+ . unwrap ( ) ;
2509
2582
2510
2583
let state = StateWithExtensions :: < Mint > :: unpack ( data. data ( ) ) . unwrap ( ) ;
2511
2584
let extension = state. get_extension :: < MetadataPointer > ( ) . unwrap ( ) ;
@@ -2522,7 +2595,8 @@ mod test {
2522
2595
let variable_len = VariableLenMintTest {
2523
2596
data : vec ! [ 7 , 6 , 5 , 4 , 3 , 2 , 1 ] ,
2524
2597
} ;
2525
- alloc_and_serialize :: < Mint , _ > ( & account_info, & variable_len, true ) . unwrap ( ) ;
2598
+ alloc_and_serialize_variable_len_extension :: < Mint , _ > ( & account_info, & variable_len, true )
2599
+ . unwrap ( ) ;
2526
2600
2527
2601
let state = StateWithExtensions :: < Mint > :: unpack ( data. data ( ) ) . unwrap ( ) ;
2528
2602
let extension = state. get_extension :: < MetadataPointer > ( ) . unwrap ( ) ;
0 commit comments