rust_chain/
name.rs

1// This is a Rust implementation of an `name` object.
2// The `name` object is a 64-bit integer that is used to represent
3// an account name in the EOSIO blockchain.
4// This implementation includes functions for converting strings to name objects and vice versa,
5// as well as functions for checking the validity of a eosio::name object.
6
7#[cfg(feature = "std")]
8use eosio_scale_info::TypeInfo;
9
10use crate::{
11    string::String,
12};
13
14use crate::serializer::{ 
15    Packer,
16	Encoder,
17};
18
19use crate::vmapi::eosio::{
20    check,
21    eosio_memcpy,
22};
23
24const INVALID_NAME_CHAR: u8 = 0xffu8;
25
26/// a helper function that converts a single ASCII character to
27/// a symbol used by the eosio::name object.
28/// ".12345abcdefghijklmnopqrstuvwxyz"
29pub const fn char_to_index(c: u8) -> u8 {
30	match c as char {
31		'a'..='z' => {
32			return (c - 'a' as u8) + 6;
33		}
34		'1'..='5' => {
35			return  (c - '1' as u8) + 1;
36		}
37		'.' => {
38			return 0;
39		}
40		_ => {
41			return INVALID_NAME_CHAR;
42		}
43	}
44}
45
46const INVALID_NAME: u64 = 0xFFFF_FFFF_FFFF_FFFFu64;
47
48
49// converts a static string to an `name` object.
50pub const fn static_str_to_name(s: &'static str) -> u64 {
51	let mut value: u64 = 0;
52	let _s = s.as_bytes();
53
54	if _s.len() > 13 {
55		return INVALID_NAME;
56	}
57
58	if _s.len() == 0 {
59		return 0;
60	}
61
62	let mut n = _s.len();
63	if n == 13 {
64		n = 12;
65	}
66
67	let mut i = 0usize;
68
69	loop {
70		if i >= n {
71			break;
72		}
73		let tmp = char_to_index(_s[i]) as u64;
74		if tmp == INVALID_NAME_CHAR as u64 {
75			return INVALID_NAME;
76		}
77		value <<= 5;
78		value |= tmp;
79
80		i += 1;
81	}
82	value <<=  4 + 5*(12 - n);
83
84    if _s.len() == 13 {
85		let tmp = char_to_index(_s[12]) as u64;
86		if tmp == INVALID_NAME_CHAR as u64 {
87			return INVALID_NAME;
88		}
89		if tmp > 0x0f {
90			return INVALID_NAME;
91		}
92		value |= tmp;
93    }
94
95	return value;
96}
97
98
99/// similar to static_str_to_name,
100/// but also checks the validity of the resulting `name` object.
101pub fn static_str_to_name_checked(s: &'static str) -> u64 {
102	let n = static_str_to_name(s);
103	check(n != INVALID_NAME, "bad name");
104	return n;
105}
106
107
108// a shorthand for static_str_to_name_checked.
109pub fn s2n(s: &'static str) -> u64 {
110	return static_str_to_name_checked(s);
111}
112
113// ".12345abcdefghijklmnopqrstuvwxyz"
114pub const CHAR_MAP: [u8; 32] = [46,49,50,51,52,53,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122];
115
116/// converts an `name` object to a string.
117pub fn n2s(value: u64) -> String {
118	// 13 dots
119	let mut s: [u8; 13] = [46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46]; //'.'
120	let mut tmp = value;
121	for i in 0..13 {
122		let c: u8;
123		if i == 0 {
124			c = CHAR_MAP[(tmp&0x0f) as usize];
125		} else {
126			c = CHAR_MAP[(tmp&0x1f) as usize];
127		}
128		s[12-i] = c;
129		if i == 0 {
130			tmp >>= 4
131		} else {
132			tmp >>= 5
133		}
134	}
135
136	let mut i = s.len() - 1;
137	while i != 0 {
138		if s[i] != '.' as u8 {
139			break
140		}
141        i -= 1;
142	}
143	return String::from_utf8(s[0..i+1].to_vec()).unwrap();
144}
145
146
147///
148fn str_to_name(s: &str) -> u64 {
149	let mut value: u64 = 0;
150	let _s = s.as_bytes();
151
152	if _s.len() > 13 {
153		return INVALID_NAME;
154	}
155
156	if _s.len() == 0 {
157		return 0;
158	}
159
160	let mut n = _s.len();
161	if n == 13 {
162		n = 12;
163	}
164
165	let mut i = 0usize;
166
167	loop {
168		if i >= n {
169			break;
170		}
171		let tmp = char_to_index(_s[i]) as u64;
172		if tmp == 0xff {
173			return INVALID_NAME;
174		}
175		value <<= 5;
176		value |= tmp;
177
178		i += 1;
179	}
180	value <<=  4 + 5*(12 - n);
181
182    if _s.len() == 13 {
183		let tmp = char_to_index(_s[12]) as u64;
184		if tmp == 0xff {
185			return INVALID_NAME;
186		}
187		if tmp > 0x0f {
188			return INVALID_NAME;
189		}
190		value |= tmp;
191    }
192
193	return value;
194}
195
196fn str_to_name_checked(s: &str) -> u64 {
197	let n = str_to_name(s);
198	check(n != INVALID_NAME, "bad name string");
199	return n;
200}
201
202/// a wrapper around a 64-bit unsigned integer that represents a name in the EOSIO blockchain
203#[repr(C, align(8))]
204#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
205#[cfg_attr(feature = "std", derive(TypeInfo))]
206pub struct Name {
207    ///
208    pub n: u64,
209}
210
211impl Name {
212    ///
213    pub fn new(s: &'static str) -> Self {
214        Name { n: s2n(s) }
215    }
216
217	pub fn value(&self) -> u64 {
218		return self.n
219	}
220
221    ///
222    pub fn from_u64(n: u64) -> Self {
223        check(n != INVALID_NAME, "bad name value");
224        Name { n: n }
225    }
226
227    ///
228    pub fn from_str(s: &str) -> Self {
229		return Name{ n: str_to_name_checked(s) };
230    }
231
232	///
233    pub fn to_string(&self) -> String {
234        n2s(self.n)
235    }
236}
237
238impl Packer for Name {
239    fn size(&self) -> usize {
240        return 8;
241    }
242
243    fn pack(&self, enc: &mut Encoder) -> usize {
244		self.n.pack(enc)
245    }
246
247    fn unpack(&mut self, raw: &[u8]) -> usize {
248        check(raw.len() >= 8, "Name.unpack: buffer overflow!");
249        self.n = 0;
250        eosio_memcpy(&self.n as *const u64 as *mut u8, raw.as_ptr(), 8);
251        return 8;
252    }
253}
254
255pub const SAME_PAYER: Name = Name{n: 0};
256pub const ACTIVE: Name = Name{n: static_str_to_name("active")};
257pub const OWNER: Name = Name{n: static_str_to_name("owner")};
258pub const CODE: Name = Name{n: static_str_to_name("eosio.code")};
259
260
261///
262#[macro_export]
263macro_rules! name {
264     ( $head:expr ) => {
265        {
266            const n: u64 = $crate::name::static_str_to_name($head);
267            $crate::name::Name::from_u64(n)
268        }
269    };
270}