xrpl/core/binarycodec/types/
mod.rs

1//! Top-level exports for types used in binary_codec.
2
3pub mod account_id;
4pub mod amount;
5pub mod blob;
6pub mod currency;
7pub mod exceptions;
8pub mod hash;
9pub mod issue;
10pub mod paths;
11pub(crate) mod test_cases;
12pub mod utils;
13pub mod vector256;
14pub mod xchain_bridge;
15
16use core::convert::TryFrom;
17use core::convert::TryInto;
18use core::fmt::Debug;
19use core::fmt::Display;
20use core::iter::FromIterator;
21
22pub use self::account_id::AccountId;
23pub use self::amount::Amount;
24pub use self::blob::Blob;
25pub use self::currency::Currency;
26pub use self::hash::Hash;
27pub use self::hash::Hash128;
28pub use self::hash::Hash160;
29pub use self::hash::Hash256;
30pub use self::issue::Issue;
31pub use self::paths::Path;
32pub use self::paths::PathSet;
33pub use self::paths::PathStep;
34pub use self::vector256::Vector256;
35pub use self::xchain_bridge::XChainBridge;
36
37use crate::core::binarycodec::binary_wrappers::Serialization;
38use crate::core::binarycodec::definitions::get_field_instance;
39use crate::core::binarycodec::definitions::get_transaction_result_code;
40use crate::core::binarycodec::definitions::get_transaction_type_code;
41use crate::core::binarycodec::definitions::FieldInstance;
42use crate::core::exceptions::XRPLCoreResult;
43use crate::core::BinaryParser;
44use alloc::borrow::Cow;
45use alloc::borrow::ToOwned;
46use alloc::string::String;
47use alloc::string::ToString;
48use alloc::vec;
49use alloc::vec::Vec;
50use amount::IssuedCurrency;
51use exceptions::XRPLTypeException;
52use serde::Deserialize;
53use serde_json::Map;
54use serde_json::Value;
55
56use super::BinarySerializer;
57use crate::core::addresscodec::is_valid_xaddress;
58use crate::core::addresscodec::xaddress_to_classic_address;
59
60const ACCOUNT: &str = "Account";
61const SOURCE_TAG: &str = "SourceTag";
62const DESTINATION: &str = "Destination";
63const DESTINATION_TAG: &str = "DestinationTag";
64const UNL_MODIFY_TX_TYPE: &str = "0066";
65const ST_OBJECT: &str = "STObject";
66const OBJECT_END_MARKER_BYTES: [u8; 1] = [0xE1];
67const ARRAY_END_MARKER: [u8; 1] = [0xF1];
68
69#[derive(Debug)]
70pub enum XRPLTypes {
71    AccountID(AccountId),
72    Amount(Amount),
73    Blob(Blob),
74    Currency(Currency),
75    Hash128(Hash128),
76    Hash160(Hash160),
77    Hash256(Hash256),
78    Issue(Issue),
79    Path(Path),
80    PathSet(PathSet),
81    PathStep(PathStep),
82    Vector256(Vector256),
83    STArray(STArray),
84    STObject(STObject),
85    UInt8(u8),
86    UInt16(u16),
87    UInt32(u32),
88    UInt64(u64),
89    XChainBridge(XChainBridge),
90    Unknown,
91}
92
93impl XRPLTypes {
94    pub fn from_value(name: &str, value: Value) -> XRPLCoreResult<Option<XRPLTypes>> {
95        if value.is_null() {
96            Ok(None)
97        } else if let Some(value) = value.as_str() {
98            // dbg!("is str");
99            match name {
100                "AccountID" => Ok(Some(XRPLTypes::AccountID(Self::type_from_str(value)?))),
101                "Amount" => Ok(Some(XRPLTypes::Amount(Self::type_from_str(value)?))),
102                "Blob" => Ok(Some(XRPLTypes::Blob(Self::type_from_str(value)?))),
103                "Currency" => Ok(Some(XRPLTypes::Currency(Self::type_from_str(value)?))),
104                "Hash128" => Ok(Some(XRPLTypes::Hash128(Self::type_from_str(value)?))),
105                "Hash160" => Ok(Some(XRPLTypes::Hash160(Self::type_from_str(value)?))),
106                "Hash256" => Ok(Some(XRPLTypes::Hash256(Self::type_from_str(value)?))),
107                "XChainClaimID" => Ok(Some(XRPLTypes::Hash256(Self::type_from_str(value)?))),
108                "UInt8" => Ok(Some(XRPLTypes::UInt8(
109                    value
110                        .parse::<u8>()
111                        .map_err(XRPLTypeException::ParseIntError)?,
112                ))),
113                "UInt16" => Ok(Some(XRPLTypes::UInt16(
114                    value
115                        .parse::<u16>()
116                        .map_err(XRPLTypeException::ParseIntError)?,
117                ))),
118                "UInt32" => Ok(Some(XRPLTypes::UInt32(
119                    value
120                        .parse::<u32>()
121                        .map_err(XRPLTypeException::ParseIntError)?,
122                ))),
123                "UInt64" => Ok(Some(XRPLTypes::UInt64(
124                    value
125                        .parse::<u64>()
126                        .map_err(XRPLTypeException::ParseIntError)?,
127                ))),
128                _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
129            }
130        } else if let Some(value) = value.as_u64() {
131            // dbg!("is u64");
132            match name {
133                "UInt8" => Ok(Some(XRPLTypes::UInt8(value as u8))),
134                "UInt16" => Ok(Some(XRPLTypes::UInt16(value as u16))),
135                "UInt32" => Ok(Some(XRPLTypes::UInt32(value as u32))),
136                "UInt64" => Ok(Some(XRPLTypes::UInt64(value))),
137                _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
138            }
139        } else if let Some(value) = value.as_object() {
140            // dbg!("is object");
141            match name {
142                "Amount" => Ok(Some(XRPLTypes::Amount(Self::amount_from_map(
143                    value.to_owned(),
144                )?))),
145                "STObject" => Ok(Some(XRPLTypes::STObject(STObject::try_from_value(
146                    Value::Object(value.to_owned()),
147                    false,
148                )?))),
149                "XChainBridge" => Ok(Some(XRPLTypes::XChainBridge(XChainBridge::try_from(
150                    Value::Object(value.to_owned()),
151                )?))),
152                _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
153            }
154        } else if let Some(value) = value.as_array() {
155            // dbg!("is array");
156            match name {
157                "STArray" => Ok(Some(XRPLTypes::STArray(STArray::try_from_value(
158                    Value::Array(value.to_owned()),
159                )?))),
160                _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
161            }
162        } else {
163            // dbg!("Unknown XRPLType", name, &value);
164            Err(exceptions::XRPLTypeException::UnknownXRPLType.into())
165        }
166    }
167
168    fn type_from_str<'a, T>(value: &'a str) -> XRPLCoreResult<T>
169    where
170        T: TryFrom<&'a str>,
171        <T as TryFrom<&'a str>>::Error: Display,
172    {
173        value
174            .try_into()
175            .map_err(|_| XRPLTypeException::TryFromStrError.into())
176    }
177
178    fn amount_from_map<T>(value: Map<String, Value>) -> XRPLCoreResult<T>
179    where
180        T: TryFrom<IssuedCurrency>,
181        <T as TryFrom<IssuedCurrency>>::Error: Display,
182    {
183        match IssuedCurrency::try_from(Value::Object(value)) {
184            Ok(value) => value
185                .try_into()
186                .map_err(|_| XRPLTypeException::TryFromIssuedCurrencyError.into()),
187            Err(error) => Err(error),
188        }
189    }
190}
191
192impl From<XRPLTypes> for SerializedType {
193    fn from(val: XRPLTypes) -> Self {
194        match val {
195            XRPLTypes::AccountID(account_id) => SerializedType::from(account_id),
196            XRPLTypes::Amount(amount) => SerializedType::from(amount),
197            XRPLTypes::Blob(blob) => SerializedType::from(blob),
198            XRPLTypes::Currency(currency) => SerializedType::from(currency),
199            XRPLTypes::Hash128(hash128) => SerializedType::from(hash128),
200            XRPLTypes::Hash160(hash160) => SerializedType::from(hash160),
201            XRPLTypes::Hash256(hash256) => SerializedType::from(hash256),
202            XRPLTypes::Path(path) => SerializedType::from(path),
203            XRPLTypes::PathSet(path_set) => SerializedType::from(path_set),
204            XRPLTypes::PathStep(path_step) => SerializedType::from(path_step),
205            XRPLTypes::Vector256(vector256) => SerializedType::from(vector256),
206            XRPLTypes::STArray(st_array) => st_array.0,
207            XRPLTypes::STObject(st_object) => st_object.0,
208            XRPLTypes::UInt8(value) => SerializedType(value.to_be_bytes().to_vec()),
209            XRPLTypes::UInt16(value) => SerializedType(value.to_be_bytes().to_vec()),
210            XRPLTypes::UInt32(value) => SerializedType(value.to_be_bytes().to_vec()),
211            XRPLTypes::UInt64(value) => SerializedType(value.to_be_bytes().to_vec()),
212            XRPLTypes::XChainBridge(x_chain_bridge) => SerializedType::from(x_chain_bridge),
213            XRPLTypes::Issue(issue) => SerializedType::from(issue),
214            XRPLTypes::Unknown => SerializedType(vec![]),
215        }
216    }
217}
218
219/// Contains a serialized buffer of a Serializer type.
220#[derive(Debug, Deserialize, Clone)]
221pub struct SerializedType(Vec<u8>);
222
223/// Class for serializing and deserializing Lists of objects.
224///
225/// See Array Fields:
226/// `<https://xrpl.org/serialization.html#array-fields>`
227#[derive(Debug)]
228pub struct STArray(SerializedType);
229
230impl STArray {
231    /// Create a SerializedArray from a serde_json::Value.
232    ///
233    /// ```
234    /// use xrpl::core::binarycodec::types::STArray;
235    /// use serde_json::Value;
236    /// use hex::ToHex;
237    ///
238    /// let array_end_marker = [0xF1];
239    /// let memo = r#"{
240    ///     "Memo": {
241    ///         "MemoType": "687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E65726963",
242    ///         "MemoData": "72656E74"
243    ///     }
244    /// }"#;
245    /// let memo_hex = "EA7C1F687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E657269637D0472656E74E1";
246    /// let expected_json = Value::Array(vec![serde_json::from_str(memo).unwrap(), serde_json::from_str(memo).unwrap()]);
247    /// let expected_hex = memo_hex.to_owned() + memo_hex + &array_end_marker.to_vec().encode_hex_upper::<String>();
248    /// let st_array = STArray::try_from_value(expected_json).unwrap();
249    /// let actual_hex = hex::encode_upper(st_array.as_ref());
250    ///
251    /// assert_eq!(actual_hex, expected_hex);
252    /// ```
253    pub fn try_from_value(value: Value) -> XRPLCoreResult<Self> {
254        if let Some(array) = value.as_array() {
255            if !array.is_empty() && array.iter().filter(|v| v.is_object()).count() != array.len() {
256                Err(exceptions::XRPLSerializeArrayException::ExpectedObjectArray.into())
257            } else {
258                let mut serializer = BinarySerializer::new();
259                for object in array {
260                    let obj = match object {
261                        Value::Object(map) => map,
262                        _ => {
263                            return Err(
264                                exceptions::XRPLSerializeArrayException::ExpectedObjectArray.into(),
265                            )
266                        }
267                    };
268                    let transaction = STObject::try_from_value(Value::Object(obj.clone()), false)?;
269                    serializer.append(transaction.as_ref().to_vec().as_mut());
270                }
271                serializer.append(ARRAY_END_MARKER.to_vec().as_mut());
272                Ok(STArray(serializer.into()))
273            }
274        } else {
275            Err(exceptions::XRPLSerializeArrayException::ExpectedArray.into())
276        }
277    }
278}
279
280impl XRPLType for STArray {
281    type Error = XRPLTypeException;
282
283    fn new(buffer: Option<&[u8]>) -> XRPLCoreResult<Self, Self::Error> {
284        if let Some(data) = buffer {
285            Ok(STArray(SerializedType(data.to_vec())))
286        } else {
287            Ok(STArray(SerializedType(vec![])))
288        }
289    }
290}
291
292impl AsRef<[u8]> for STArray {
293    fn as_ref(&self) -> &[u8] {
294        self.0.as_ref()
295    }
296}
297
298/// Class for serializing/deserializing Indexmaps of objects.
299///
300/// See Object Fields:
301/// `<https://xrpl.org/serialization.html#object-fields>`
302#[derive(Debug)]
303pub struct STObject(SerializedType);
304
305impl STObject {
306    /// Create a SerializedMap from a serde_json::Value.
307    ///
308    /// ```
309    /// use xrpl::core::binarycodec::types::STObject;
310    ///
311    /// let expected_json = r#"{
312    ///     "Account": "raD5qJMAShLeHZXf9wjUmo6vRK4arj9cF3",
313    ///     "Fee": "10",
314    ///     "Flags": 0,
315    ///     "Sequence": 103929,
316    ///     "SigningPubKey": "028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A7166",
317    ///     "TakerGets": {
318    ///         "value": "1694.768",
319    ///         "currency": "ILS",
320    ///         "issuer": "rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9"
321    ///     },
322    ///     "TakerPays": "98957503520",
323    ///     "TransactionType": "OfferCreate",
324    ///     "TxnSignature": "304502202ABE08D5E78D1E74A4C18F2714F64E87B8BD57444AFA5733109EB3C077077520022100DB335EE97386E4C0591CAC024D50E9230D8F171EEB901B5E5E4BD6D1E0AEF98C"
325    /// }"#;
326    ///
327    /// let buffer = "120007220000000024000195F964400000170A53AC2065D5460561E\
328    ///     C9DE000000000000000000000000000494C53000000000092D70596\
329    ///     8936C419CE614BF264B5EEB1CEA47FF468400000000000000A73210\
330    ///     28472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F\
331    ///     418D6A71667447304502202ABE08D5E78D1E74A4C18F2714F64E87B\
332    ///     8BD57444AFA5733109EB3C077077520022100DB335EE97386E4C059\
333    ///     1CAC024D50E9230D8F171EEB901B5E5E4BD6D1E0AEF98C811439408\
334    ///     A69F0895E62149CFCC006FB89FA7D1E6E5D";
335    /// let value = serde_json::from_str(expected_json).unwrap();
336    /// let serialized_map = STObject::try_from_value(value, false).unwrap();
337    /// let hex = hex::encode_upper(serialized_map.as_ref());
338    /// assert_eq!(hex, buffer);
339    /// ```
340    pub fn try_from_value(value: Value, signing_only: bool) -> XRPLCoreResult<Self> {
341        let object = match value {
342            Value::Object(map) => map,
343            _ => return Err(exceptions::XRPLSerializeMapException::ExpectedObject.into()),
344        };
345        let mut serializer = BinarySerializer::new();
346        let mut value_xaddress_handled = Map::new();
347        for (field, value) in &object {
348            if let Some(value) = value.as_str() {
349                if is_valid_xaddress(value) {
350                    let handled_xaddress = handle_xaddress(field.into(), value.into())?;
351                    if let Some(handled_tag) = handled_xaddress.get(SOURCE_TAG) {
352                        if let Some(object_tag) = object.get(SOURCE_TAG) {
353                            if handled_tag != object_tag {
354                                return Err(
355                                    exceptions::XRPLSerializeMapException::AccountMismatchingTags
356                                        .into(),
357                                );
358                            }
359                        }
360                    }
361                    if let Some(handled_tag) = handled_xaddress.get(DESTINATION_TAG) {
362                        if let Some(object_tag) = object.get(DESTINATION_TAG) {
363                            if handled_tag != object_tag {
364                                return Err(
365                                    exceptions::XRPLSerializeMapException::DestinationMismatchingTags.into()
366                                );
367                            }
368                        }
369                    }
370                    value_xaddress_handled.extend(handled_xaddress);
371                } else if field == "TransactionType" {
372                    let transaction_type_code = match get_transaction_type_code(value) {
373                        Some(code) => code,
374                        None => {
375                            return Err(
376                                exceptions::XRPLSerializeMapException::UnknownTransactionType(
377                                    value.to_string(),
378                                )
379                                .into(),
380                            )
381                        }
382                    };
383                    value_xaddress_handled.insert(
384                        field.to_owned(),
385                        Value::Number(transaction_type_code.to_owned().into()),
386                    );
387                } else if field == "TransactionResult" {
388                    let transaction_result_code =
389                        match get_transaction_result_code(value) {
390                            Some(code) => code,
391                            None => return Err(
392                                exceptions::XRPLSerializeMapException::UnknownTransactionResult(
393                                    value.to_string(),
394                                )
395                                .into(),
396                            ),
397                        };
398                    value_xaddress_handled.insert(
399                        field.to_owned(),
400                        Value::Number(transaction_result_code.to_owned().into()),
401                    );
402                } else if field == "LedgerEntryType" {
403                    let ledger_entry_type_code = match get_transaction_type_code(value) {
404                        Some(code) => code,
405                        None => {
406                            return Err(
407                                exceptions::XRPLSerializeMapException::UnknownLedgerEntryType(
408                                    value.to_string(),
409                                )
410                                .into(),
411                            )
412                        }
413                    };
414                    value_xaddress_handled.insert(
415                        field.to_owned(),
416                        Value::Number(ledger_entry_type_code.to_owned().into()),
417                    );
418                } else {
419                    value_xaddress_handled
420                        .insert(field.to_owned(), Value::String(value.to_owned()));
421                }
422            } else {
423                value_xaddress_handled.insert(field.to_owned(), value.clone());
424            }
425        }
426
427        let mut sorted_keys: Vec<FieldInstance> = Vec::new();
428        for (field, _) in &value_xaddress_handled {
429            let field_instance = get_field_instance(field);
430            if let Some(field_instance) = field_instance {
431                if value_xaddress_handled.contains_key(&field_instance.name)
432                    && field_instance.is_serialized
433                {
434                    sorted_keys.push(field_instance);
435                }
436            }
437        }
438        sorted_keys.sort_by_key(|k| k.ordinal);
439        if signing_only {
440            sorted_keys.retain(|k| k.is_signing);
441        }
442        let mut is_unl_modify = false;
443
444        for field_instance in &sorted_keys {
445            let associated_value = value_xaddress_handled.get(&field_instance.name).ok_or(
446                exceptions::XRPLTypeException::MissingField(field_instance.name.clone()),
447            )?;
448            let associated_value = XRPLTypes::from_value(
449                &field_instance.associated_type,
450                associated_value.to_owned(),
451            )?;
452            if associated_value.is_none() {
453                continue;
454            }
455            let associated_value = associated_value.unwrap(); // safe to unwrap because only `null` values are `None` and we skip those
456            let associated_value: SerializedType = associated_value.into();
457            // dbg!(
458            //     field_instance.name.clone(),
459            //     hex::encode_upper(associated_value.clone())
460            // );
461            if field_instance.name == "TransactionType"
462                && associated_value.to_string() == UNL_MODIFY_TX_TYPE
463            {
464                is_unl_modify = true;
465            }
466            let is_unl_modify_workaround = field_instance.name == "Account" && is_unl_modify;
467
468            serializer.write_field_and_value(
469                field_instance.to_owned(),
470                associated_value.as_ref(),
471                is_unl_modify_workaround,
472            );
473            if field_instance.associated_type == ST_OBJECT {
474                serializer.append(OBJECT_END_MARKER_BYTES.to_vec().as_mut());
475            }
476        }
477
478        Ok(STObject(serializer.into()))
479    }
480}
481
482impl XRPLType for STObject {
483    type Error = XRPLTypeException;
484
485    fn new(buffer: Option<&[u8]>) -> XRPLCoreResult<Self, Self::Error> {
486        if let Some(data) = buffer {
487            Ok(STObject(SerializedType(data.to_vec())))
488        } else {
489            Ok(STObject(SerializedType(vec![])))
490        }
491    }
492}
493
494impl AsRef<[u8]> for STObject {
495    fn as_ref(&self) -> &[u8] {
496        self.0.as_ref()
497    }
498}
499
500fn handle_xaddress(field: Cow<str>, xaddress: Cow<str>) -> XRPLCoreResult<Map<String, Value>> {
501    let (classic_address, tag, _is_test_net) = xaddress_to_classic_address(&xaddress)?;
502    if let Some(tag) = tag {
503        if field == DESTINATION {
504            let tag_name = DESTINATION_TAG;
505            Ok(Map::from_iter(vec![
506                (field.to_string(), Value::String(classic_address)),
507                (tag_name.to_string(), Value::Number(tag.into())),
508            ]))
509        } else if field == ACCOUNT {
510            let tag_name = SOURCE_TAG;
511            Ok(Map::from_iter(vec![
512                (field.to_string(), Value::String(classic_address)),
513                (tag_name.to_string(), Value::Number(tag.into())),
514            ]))
515        } else {
516            Err(exceptions::XRPLSerializeMapException::DisallowedTag {
517                field: field.to_string(),
518            }
519            .into())
520        }
521    } else {
522        Ok(Map::from_iter(vec![(
523            field.to_string(),
524            Value::String(classic_address),
525        )]))
526    }
527}
528
529/// An XRPL Type will implement this trait.
530///
531/// # Examples
532///
533/// ## Basic usage
534///
535/// ```
536/// use xrpl::core::binarycodec::types::XRPLType;
537/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException;
538/// use xrpl::core::exceptions::XRPLCoreResult;
539///
540/// pub struct Example(Vec<u8>);
541///
542/// impl XRPLType for Example {
543///     type Error = XRPLBinaryCodecException;
544///
545///     fn new(buffer: Option<&[u8]>) -> XRPLCoreResult<Self, Self::Error> {
546///         if let Some(data) = buffer {
547///             Ok(Example(data.to_vec()))
548///         } else {
549///             Ok(Example(vec![]))
550///         }
551///     }
552/// }
553/// ```
554pub trait XRPLType {
555    /// Error type for implementing type.
556    type Error;
557
558    /// Create a new instance of a type.
559    fn new(buffer: Option<&[u8]>) -> XRPLCoreResult<Self, Self::Error>
560    where
561        Self: Sized;
562}
563
564/// Converter for transforming a BinaryParser into a type.
565///
566/// # Examples
567///
568/// ## Basic usage
569///
570/// ```
571/// use xrpl::core::binarycodec::types::TryFromParser;
572/// use xrpl::core::binarycodec::BinaryParser;
573/// use xrpl::core::Parser;
574/// use xrpl::core::exceptions::{XRPLCoreResult, XRPLCoreException};
575///
576/// pub struct Example(Vec<u8>);
577///
578/// impl TryFromParser for Example {
579///     type Error = XRPLCoreException;
580///
581///     fn from_parser(
582///         parser: &mut BinaryParser,
583///         _length: Option<usize>,
584///     ) -> XRPLCoreResult<Example, Self::Error> {
585///         Ok(Example(parser.read(42)?))
586///     }
587/// }
588/// ```
589pub trait TryFromParser {
590    /// Error type for implementing type.
591    type Error;
592
593    /// Construct a type from a BinaryParser.
594    fn from_parser(
595        parser: &mut BinaryParser,
596        length: Option<usize>,
597    ) -> XRPLCoreResult<Self, Self::Error>
598    where
599        Self: Sized;
600}
601
602impl Display for SerializedType {
603    /// Get the hex representation of the SerializedType bytes.
604    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
605        write!(f, "{}", hex::encode_upper(self.0.as_slice()))
606    }
607}
608
609impl From<Vec<u8>> for SerializedType {
610    /// Create a SerializedType from a Vec<u8>.
611    fn from(buffer: Vec<u8>) -> Self {
612        SerializedType(buffer)
613    }
614}
615
616impl AsRef<[u8]> for SerializedType {
617    /// Get a reference of the byte representation.
618    fn as_ref(&self) -> &[u8] {
619        self.0.as_slice()
620    }
621}
622
623impl<T> From<T> for SerializedType
624where
625    T: XRPLType + AsRef<[u8]>,
626{
627    /// Create a serialized type from an XRPLType.
628    fn from(instance: T) -> Self {
629        SerializedType(instance.as_ref().to_vec())
630    }
631}