use crate::cell::{Load, Store};
use crate::error::ParseGlobalCapabilityError;
macro_rules! decl_global_capability {
($(#[doc = $doc:expr])* $vis:vis enum $ident:ident {$(
$(#[doc = $var_doc:expr])*
$field:ident = $descr:literal
),*$(,)?}) => {
$(#[doc = $doc])*
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u64)]
$vis enum $ident {$(
$(#[doc = $var_doc])*
$field = 1u64 << $descr
),*,}
impl GlobalCapability {
const fn from_bit_offset(bit_offset: u32) -> Option<Self> {
Some(match bit_offset {
$($descr => Self::$field),*,
_ => return None,
})
}
}
impl std::fmt::Display for GlobalCapability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
$(Self::$field => stringify!($field),)*
})
}
}
impl std::str::FromStr for GlobalCapability {
type Err = ParseGlobalCapabilityError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
$(stringify!($field) => Self::$field,)*
_ => return Err(ParseGlobalCapabilityError::UnknownCapability),
})
}
}
};
}
decl_global_capability! {
pub enum GlobalCapability {
CapIhrEnabled = 0,
CapCreateStatsEnabled = 1,
CapBounceMsgBody = 2,
CapReportVersion = 3,
CapSplitMergeTransactions = 4,
CapShortDequeue = 5,
CapMbppEnabled = 6,
CapFastStorageStat = 7,
CapInitCodeHash = 8,
CapOffHypercube = 9,
CapMyCode = 10,
CapSetLibCode = 11,
CapFixTupleIndexBug = 12,
CapRemp = 13,
CapDelections = 14,
CapFullBodyInBounced = 16,
CapStorageFeeToTvm = 17,
CapCopyleft = 18,
CapIndexAccounts = 19,
CapDiff = 20,
CapsTvmBugfixes2022 = 21,
CapWorkchains = 22,
CapStcontNewFormat = 23,
CapFastStorageStatBugfix = 24,
CapResolveMerkleCell = 25,
CapSignatureWithId = 26,
CapBounceAfterFailedAction = 27,
CapGroth16 = 28,
CapFeeInGasUnits = 29,
CapBigCells = 30,
CapSuspendedList = 31,
CapFastFinality = 32,
}
}
impl std::ops::BitOr<GlobalCapability> for GlobalCapability {
type Output = GlobalCapabilities;
#[inline]
fn bitor(self, rhs: GlobalCapability) -> Self::Output {
GlobalCapabilities(self as u64 | rhs as u64)
}
}
impl std::ops::BitOr<GlobalCapability> for u64 {
type Output = GlobalCapabilities;
#[inline]
fn bitor(self, rhs: GlobalCapability) -> Self::Output {
GlobalCapabilities(self | rhs as u64)
}
}
impl std::ops::BitOrAssign<GlobalCapability> for u64 {
#[inline]
fn bitor_assign(&mut self, rhs: GlobalCapability) {
*self = (*self | rhs).0;
}
}
impl std::ops::BitOr<u64> for GlobalCapability {
type Output = GlobalCapabilities;
#[inline]
fn bitor(self, rhs: u64) -> Self::Output {
GlobalCapabilities(self as u64 | rhs)
}
}
impl std::ops::BitOr<GlobalCapability> for GlobalCapabilities {
type Output = GlobalCapabilities;
#[inline]
fn bitor(self, rhs: GlobalCapability) -> Self::Output {
GlobalCapabilities(self.0 | rhs as u64)
}
}
impl std::ops::BitOr<GlobalCapabilities> for GlobalCapability {
type Output = GlobalCapabilities;
#[inline]
fn bitor(self, rhs: GlobalCapabilities) -> Self::Output {
GlobalCapabilities(self as u64 | rhs.0)
}
}
impl std::ops::BitOrAssign<u64> for GlobalCapabilities {
#[inline]
fn bitor_assign(&mut self, rhs: u64) {
*self = GlobalCapabilities(self.0 | rhs);
}
}
impl std::ops::BitOrAssign<GlobalCapability> for GlobalCapabilities {
#[inline]
fn bitor_assign(&mut self, rhs: GlobalCapability) {
*self = *self | rhs;
}
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[tlb(tag = "#c4")]
pub struct GlobalVersion {
pub version: u32,
pub capabilities: GlobalCapabilities,
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Store, Load)]
#[repr(transparent)]
pub struct GlobalCapabilities(u64);
impl GlobalCapabilities {
#[inline]
pub const fn new(inner: u64) -> Self {
Self(inner)
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
pub const fn len(&self) -> usize {
self.0.count_ones() as usize
}
#[inline]
pub const fn contains(&self, capability: GlobalCapability) -> bool {
(self.0 & (capability as u64)) != 0
}
#[inline]
pub const fn into_inner(self) -> u64 {
self.0
}
#[inline]
pub fn iter(&self) -> GlobalCapabilitiesIter {
GlobalCapabilitiesIter(self.0)
}
}
impl From<u64> for GlobalCapabilities {
#[inline]
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<GlobalCapabilities> for u64 {
#[inline]
fn from(value: GlobalCapabilities) -> Self {
value.0
}
}
impl PartialEq<u64> for GlobalCapabilities {
#[inline]
fn eq(&self, other: &u64) -> bool {
self.0 == *other
}
}
impl IntoIterator for GlobalCapabilities {
type Item = GlobalCapability;
type IntoIter = GlobalCapabilitiesIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
GlobalCapabilitiesIter(self.0)
}
}
impl FromIterator<GlobalCapability> for GlobalCapabilities {
fn from_iter<T: IntoIterator<Item = GlobalCapability>>(iter: T) -> Self {
let mut res = GlobalCapabilities::default();
for item in iter {
res |= item;
}
res
}
}
impl<const N: usize> From<[GlobalCapability; N]> for GlobalCapabilities {
fn from(value: [GlobalCapability; N]) -> Self {
let mut res = GlobalCapabilities::default();
for item in value.iter() {
res |= *item;
}
res
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for GlobalCapabilities {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
if serializer.is_human_readable() {
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for capability in self.iter() {
seq.serialize_element(&capability)?;
}
seq.end()
} else {
serializer.serialize_u64(self.0)
}
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for GlobalCapabilities {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Visitor;
struct GlobalCapabilitiesVisitor;
impl<'de> Visitor<'de> for GlobalCapabilitiesVisitor {
type Value = GlobalCapabilities;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a list of global capabilities")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut res = GlobalCapabilities::default();
while let Some(capacility) = ok!(seq.next_element::<GlobalCapability>()) {
res |= capacility;
}
Ok(res)
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_seq(GlobalCapabilitiesVisitor)
} else {
u64::deserialize(deserializer).map(Self)
}
}
}
#[derive(Clone)]
pub struct GlobalCapabilitiesIter(u64);
impl Iterator for GlobalCapabilitiesIter {
type Item = GlobalCapability;
fn next(&mut self) -> Option<Self::Item> {
while self.0 != 0 {
let mask = self.0 & !(self.0 - 1);
self.0 &= !mask;
if let Some(item) = GlobalCapability::from_bit_offset(mask.trailing_zeros()) {
return Some(item);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.count_ones() as usize;
(len, Some(len))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn capabilities_iter() {
let capabilities = GlobalCapability::CapCreateStatsEnabled
| GlobalCapability::CapBounceMsgBody
| GlobalCapability::CapReportVersion
| GlobalCapability::CapShortDequeue
| GlobalCapability::CapFastStorageStat
| GlobalCapability::CapOffHypercube
| GlobalCapability::CapMyCode
| GlobalCapability::CapFixTupleIndexBug;
let capabilities = capabilities.into_iter().collect::<Vec<_>>();
assert_eq!(
capabilities,
[
GlobalCapability::CapCreateStatsEnabled,
GlobalCapability::CapBounceMsgBody,
GlobalCapability::CapReportVersion,
GlobalCapability::CapShortDequeue,
GlobalCapability::CapFastStorageStat,
GlobalCapability::CapOffHypercube,
GlobalCapability::CapMyCode,
GlobalCapability::CapFixTupleIndexBug
]
);
}
}