1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5use rust_embed::RustEmbed;
7use std::ffi::{CStr, CString};
8
9#[derive(RustEmbed)]
11#[folder = "resources/"]
12pub struct AbiFiles;
13
14include!("./bindings.rs");
15
16pub mod encodeABI;
17pub mod errors;
18
19extern crate error_chain;
21
22use crate::errors::{Error, ErrorKind, Result};
23use std::os::raw::c_char;
24
25pub type ABIName = u64;
26
27pub struct ABIEOS {
28 context: *mut abieos_context,
29}
30impl Default for ABIEOS {
31 fn default() -> Self {
32 ABIEOS::new()
33 }
34}
35impl ABIEOS {
36 pub fn new() -> ABIEOS {
40 unsafe {
41 let context: *mut abieos_context = abieos_create();
42 ABIEOS { context }
43 }
44 }
45
46 pub fn new_with_abi(contract_name: &str, abi: &str) -> Result<ABIEOS> {
50 unsafe {
51 let context: *mut abieos_context = abieos_create();
52 let abi_obj = ABIEOS { context };
53 abi_obj.set_abi(contract_name, abi).map_err(|e| {
54 abi_obj.destroy();
55 Error::with_chain(e, "new_with_abi")
56 })?;
57
58 Ok(abi_obj)
59 }
60 }
61
62 pub fn destroy(&self) {
66 unsafe {
67 abieos_destroy(self.context);
68 }
69 }
71
72 pub fn set_abi(&self, contract_name: &str, abi: &str) -> Result<bool> {
73 let name = self.str_to_name(contract_name)?;
74 let abi_cs = CString::new(abi)?;
75 let result = unsafe { abieos_set_abi(self.context, name, abi_cs.as_ptr() as *const i8) };
76 if result == 0 {
77 self.abieos_error()?;
78 }
79
80 Ok(true)
81 }
82
83 pub fn str_to_name(&self, str_name: &str) -> Result<ABIName> {
84 let cs = CString::new(str_name)?;
85 let result = unsafe { abieos_string_to_name(self.context, cs.as_ptr() as *const i8) };
86 Ok(result)
87 }
88
89 pub fn hex_to_json(&self, contract_name: &str, type_str: &str, hex: &[u8]) -> Result<String> {
90 let name = self.str_to_name(contract_name)?;
91 let typeCS = CString::new(type_str).unwrap();
92 let hexCS = CString::new(hex).unwrap();
93 let json_p = unsafe {
94 abieos_hex_to_json(
95 self.context,
96 name,
97 typeCS.as_ptr(),
98 hexCS.as_ptr() as *const i8,
99 )
100 };
101
102 if json_p.is_null() {
103 self.abieos_error()?;
104 Err("FAIL".into()) } else {
106 let json = unsafe { ABIEOS::fix_json(CStr::from_ptr(json_p).to_str()?)? };
107 Ok(json)
108 }
109 }
110
111 pub fn bin_to_json(&self, contract_name: &str, type_str: &str, hex: &[u8]) -> Result<String> {
112 let name = self.str_to_name(contract_name)?;
113 let typeCS = CString::new(type_str).unwrap();
114 let json_p = unsafe {
116 abieos_bin_to_json(
117 self.context,
118 name,
119 typeCS.as_ptr(),
120 hex.as_ptr() as *const i8,
121 hex.len() as u64,
122 )
123 };
124
125 if json_p.is_null() {
126 self.abieos_error()?;
127 Err("FAIL".into()) } else {
129 unsafe {
130 let json = ABIEOS::fix_json(CStr::from_ptr(json_p).to_str()?)?;
131 Ok(json.clone())
132 }
133 }
134 }
135 fn json_to_xx(&self, contract_name: &str, type_str: &str, json: &str) -> Result<i32> {
136 let name = self.str_to_name(contract_name)?;
137 let typeCS = CString::new(type_str).unwrap();
138 let jsonCS = CString::new(json).unwrap();
139 let result = unsafe {
140 abieos_json_to_bin_reorderable(
141 self.context,
142 name,
143 typeCS.as_ptr(),
144 jsonCS.as_ptr() as *const i8,
145 )
146 };
147 Ok(result)
148 }
149 pub fn json_to_hex(&self, contract_name: &str, type_str: &str, json: &str) -> Result<String> {
154 match self.json_to_xx(contract_name, type_str, json) {
155 Ok(0) => {
156 self.abieos_error()
157 .map_err(|e| Error::with_chain(e, "json_to_hex"))?;
158 Err("FAIL".into()) }
160 Ok(_) => {
161 unsafe {
162 let hex_p = abieos_get_bin_hex(self.context);
163 let s = CStr::from_ptr(hex_p);
164 Ok(String::from(s.to_str()?).clone())
166 }
167 }
168 Err(e) => Err(Error::with_chain(e, "json_to_hex")),
169 }
170 }
171
172 pub fn json_to_bin(&self, contract_name: &str, type_str: &str, json: &str) -> Result<Vec<u8>> {
177 match self.json_to_xx(contract_name, type_str, json) {
178 Ok(0) => {
179 self.abieos_error()?;
180 Err("FAIL".into()) }
182 Ok(_) => unsafe {
183 let bin_size: usize = abieos_get_bin_size(self.context) as usize;
184 let bin: *const ::std::os::raw::c_char = abieos_get_bin_data(self.context);
185
186 let v: &[u8] = std::slice::from_raw_parts(bin as *const u8, bin_size);
187 let ve: Vec<u8> = v.to_vec().clone();
189 Ok(ve)
190 },
191 Err(e) => Err(Error::with_chain(e, "json_to_hex")),
192 }
193 }
194
195 fn abieos_error(&self) -> Result<String> {
196 unsafe {
197 let err_raw: *const c_char = abieos_get_error(self.context);
198 if !err_raw.is_null() {
199 let err_s = CStr::from_ptr(err_raw).to_str()?;
200 Err(ErrorKind::ABIEOS(String::from(err_s)).into())
201 } else {
202 Err(ErrorKind::ABIEOS_INT.into())
203 }
204 }
205 }
206
207 fn fix_json(in_str: &str) -> Result<String> {
211 let mut x: String;
212 let mut i = 0;
213 x = in_str.replacen("\\u000A", "\\n", 999);
214 while x.contains("\\u000A") {
215 x = x.replacen("\\u000A", "\\n", 999);
216 i += 1;
217 if i > 100 {
218 return Err(ErrorKind::ABIEOS_LOOP.into());
219 }
220 }
221 Ok(x)
222 }
223}
224fn hex_to_bin_char(c: u8) -> u8 {
225 if (b'a'..=b'z').contains(&c) {
226 let v: u8 = (c - b'a') + 10;
227 return v;
228 }
229 if (b'A'..=b'Z').contains(&c) {
230 let v: u8 = (c - b'A') + 10;
231 return v;
232 }
233 if (b'0'..=b'9').contains(&c) {
234 let v = c - b'0';
235 return v;
236 }
237 0
238}
239
240pub fn hex_to_bin(hex: &str) -> Vec<u8> {
241 let mut bin: Vec<u8> = Vec::with_capacity(hex.len() / 2);
242 let bytes = hex.as_bytes();
243 let mut i = 0;
244 while i < bytes.len() {
245 let b = hex_to_bin_char(bytes[i]).checked_shl(4).unwrap() + hex_to_bin_char(bytes[i + 1]);
246 i += 2;
247 bin.push(b);
248 }
249 bin
250}
251pub fn varuint32_from_bin(bin_str: &[u8]) -> Result<(u32, Vec<u8>)> {
252 let mut dest: u32 = 0;
253 let mut shift = 0;
254 let mut i = 0;
255 let mut b: u8;
256 while {
257 if shift >= 35 {
258 return Err(ErrorKind::ABIEOS_VARUINT_ENCODING.into());
259 }
260 b = bin_str[i];
261 dest |= ((b & 0x7f) as u32).checked_shl(shift).unwrap();
262 shift += 7;
263 i += 1;
264 i < bin_str.len() && b & 0x80 != 0
265 } {}
266
267 Ok((dest, bin_str[i..].to_vec()))
268}
269pub fn varuint64_from_bin(bin_str: &[u8]) -> Result<(u64, Vec<u8>)> {
270 let mut dest: u64 = 0;
271 let mut shift = 0;
272 let mut i = 0;
273 let mut b: u8;
274 while {
275 if shift >= 70 {
276 return Err(ErrorKind::ABIEOS_VARUINT_ENCODING.into());
277 }
278 b = bin_str[i];
279 dest |= ((b & 0x7f) as u64).checked_shl(shift).unwrap();
280 shift += 7;
281 i += 1;
282 i < bin_str.len() && b & 0x80 != 0
283 } {}
284 Ok((dest, bin_str[i..].to_vec()))
285}
286
287pub mod eosio_datetime_format {
288 use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
289 use serde::{self, Deserialize, Deserializer, Serializer};
290
291 const FORMAT: &str = "%Y-%m-%dT%H:%M:%S";
292
293 #[allow(dead_code)]
301 pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
302 where
303 S: Serializer,
304 {
305 let s = format!("{}", date.format(FORMAT));
306 serializer.serialize_str(&s)
307 }
308
309 pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
317 where
318 D: Deserializer<'de>,
319 {
320 let s: String = String::deserialize(deserializer)?;
321 let len = s.len();
322 let slice_len = if s.contains('.') {
323 len.saturating_sub(4)
324 } else {
325 len
326 };
327
328 let sliced = &s[0..slice_len];
330 match NaiveDateTime::parse_from_str(sliced, FORMAT) {
331 Err(_e) => {
332 eprintln!("DateTime Fail {} {:#?}", sliced, _e);
333 Err(serde::de::Error::custom(_e))
334 }
335 Ok(dt) => Ok(Utc.from_utc_datetime(&dt)),
336 }
337 }
338}
339#[cfg(test)]
340mod test {
341 use super::*;
342 use std::fs;
343
344 const JSON: &str = "{ \
345 \"expiration\": \"2018-08-02T20:24:36\", \
346 \"ref_block_num\": 14207, \
347 \"ref_block_prefix\": 1438248607, \
348 \"max_net_usage_words\": 0, \
349 \"max_cpu_usage_ms\": 0, \
350 \"delay_sec\": 0, \
351 \"context_free_actions\": [], \
352 \"actions\": [{ \
353 \"account\": \"eosio\", \
354 \"name\": \"newaccount\", \
355 \"authorization\": [{ \
356 \"actor\": \"eosio\", \
357 \"permission\": \"active\" }], \
358 \"data\": \"0000000000ea305500a6823403ea30550100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d8010000000100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d801000000\" \
359 }], \
360 \"transaction_extensions\": []}";
361
362 const PACKED: &str = "8468635b7f379feeb95500000000010000000000ea305500409e9a2264b89a010000000000ea305500000000a8ed3232660000000000ea305500a6823403ea30550100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d8010000000100000001000240cc0bf90a5656c8bb81f0eb86f49f89613c5cd988c018715d4646c6bd0ad3d80100000000";
363
364 #[test]
365 fn tst_abi() -> Result<()> {
366 let hex = "0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d6505087472616e7366657200040466726f6d0c6163636f756e745f6e616d6502746f0c6163636f756e745f6e616d65087175616e74697479056173736574046d656d6f06737472696e67066372656174650002066973737565720c6163636f756e745f6e616d650e6d6178696d756d5f737570706c79056173736574056973737565000302746f0c6163636f756e745f6e616d65087175616e74697479056173736574046d656d6f06737472696e67076163636f756e7400010762616c616e63650561737365740e63757272656e63795f7374617473000306737570706c790561737365740a6d61785f737570706c79056173736574066973737565720c6163636f756e745f6e616d6503000000572d3ccdcd087472616e73666572bc072d2d2d0a7469746c653a20546f6b656e205472616e736665720a73756d6d6172793a205472616e7366657220746f6b656e732066726f6d206f6e65206163636f756e7420746f20616e6f746865722e0a69636f6e3a2068747470733a2f2f63646e2e746573746e65742e6465762e62316f70732e6e65742f746f6b656e2d7472616e736665722e706e6723636535316566396639656563613334333465383535303765306564343965373666666631323635343232626465643032353566333139366561353963386230630a2d2d2d0a0a2323205472616e73666572205465726d73202620436f6e646974696f6e730a0a492c207b7b66726f6d7d7d2c20636572746966792074686520666f6c6c6f77696e6720746f206265207472756520746f207468652062657374206f66206d79206b6e6f776c656467653a0a0a312e204920636572746966792074686174207b7b7175616e746974797d7d206973206e6f74207468652070726f6365656473206f66206672617564756c656e74206f722076696f6c656e7420616374697669746965732e0a322e2049206365727469667920746861742c20746f207468652062657374206f66206d79206b6e6f776c656467652c207b7b746f7d7d206973206e6f7420737570706f7274696e6720696e6974696174696f6e206f662076696f6c656e636520616761696e7374206f74686572732e0a332e2049206861766520646973636c6f73656420616e7920636f6e747261637475616c207465726d73202620636f6e646974696f6e732077697468207265737065637420746f207b7b7175616e746974797d7d20746f207b7b746f7d7d2e0a0a4920756e6465727374616e6420746861742066756e6473207472616e736665727320617265206e6f742072657665727369626c6520616674657220746865207b7b247472616e73616374696f6e2e64656c61795f7365637d7d207365636f6e6473206f72206f746865722064656c617920617320636f6e66696775726564206279207b7b66726f6d7d7d2773207065726d697373696f6e732e0a0a4966207468697320616374696f6e206661696c7320746f20626520697272657665727369626c7920636f6e6669726d656420616674657220726563656976696e6720676f6f6473206f722073657276696365732066726f6d20277b7b746f7d7d272c204920616772656520746f206569746865722072657475726e2074686520676f6f6473206f72207365727669636573206f7220726573656e64207b7b7175616e746974797d7d20696e20612074696d656c79206d616e6e65722e0000000000a531760569737375650000000000a86cd445066372656174650002000000384f4d113203693634010863757272656e6379010675696e743634076163636f756e740000000000904dc603693634010863757272656e6379010675696e7436340e63757272656e63795f737461747300000000";
367 let jsonResult = "{\"version\":\"eosio::abi/1.0\",\"types\":[{\"new_type_name\":\"account_name\",\"type\":\"name\"}],\"structs\":[{\"name\":\"transfer\",\"base\":\"\",\"fields\":[{\"name\":\"from\",\"type\":\"account_name\"},{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"create\",\"base\":\"\",\"fields\":[{\"name\":\"issuer\",\"type\":\"account_name\"},{\"name\":\"maximum_supply\",\"type\":\"asset\"}]},{\"name\":\"issue\",\"base\":\"\",\"fields\":[{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"account\",\"base\":\"\",\"fields\":[{\"name\":\"balance\",\"type\":\"asset\"}]},{\"name\":\"currency_stats\",\"base\":\"\",\"fields\":[{\"name\":\"supply\",\"type\":\"asset\"},{\"name\":\"max_supply\",\"type\":\"asset\"},{\"name\":\"issuer\",\"type\":\"account_name\"}]}],\"actions\":[{\"name\":\"transfer\",\"type\":\"transfer\",\"ricardian_contract\":\"---\\ntitle: Token Transfer\\nsummary: Transfer tokens from one account to another.\\nicon: https://cdn.testnet.dev.b1ops.net/token-transfer.png#ce51ef9f9eeca3434e85507e0ed49e76fff1265422bded0255f3196ea59c8b0c\\n---\\n\\n## Transfer Terms & Conditions\\n\\nI, {{from}}, certify the following to be true to the best of my knowledge:\\n\\n1. I certify that {{quantity}} is not the proceeds of fraudulent or violent activities.\\n2. I certify that, to the best of my knowledge, {{to}} is not supporting initiation of violence against others.\\n3. I have disclosed any contractual terms & conditions with respect to {{quantity}} to {{to}}.\\n\\nI understand that funds transfers are not reversible after the {{$transaction.delay_sec}} seconds or other delay as configured by {{from}}'s permissions.\\n\\nIf this action fails to be irreversibly confirmed after receiving goods or services from '{{to}}', I agree to either return the goods or services or resend {{quantity}} in a timely manner.\"},{\"name\":\"issue\",\"type\":\"issue\",\"ricardian_contract\":\"\"},{\"name\":\"create\",\"type\":\"create\",\"ricardian_contract\":\"\"}],\"tables\":[{\"name\":\"accounts\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"account\"},{\"name\":\"stat\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"currency_stats\"}],\"ricardian_clauses\":[],\"error_messages\":[],\"abi_extensions\":[],\"variants\":[]}";
368 let abi = fs::read_to_string("abi.abi.json").unwrap();
369
370 let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
371 let do_hex_2_json = || -> Result<String> {
372 let json = abieos.hex_to_json("eosio", "abi_def", hex.as_bytes())?;
373 assert_eq!(jsonResult, json);
374 Ok(String::from(json))
375 }();
376 let do_json_2_hex = || -> Result<String> {
377 let hex_out = abieos.json_to_hex("eosio", "abi_def", jsonResult)?;
378 assert_eq!(hex_out.to_ascii_lowercase(), hex);
379 Ok(hex_out)
380 }();
381 abieos.destroy();
382 do_hex_2_json?;
383 do_json_2_hex?;
384
385 Ok(())
386 }
387
388 #[test]
389 fn tst_txn() -> Result<()> {
390 let hex = "AE0D635CDCAC90A6DCFA000000000100A6823403EA3055000000572D3CCDCD0100AEAA4AC15CFD4500000000A8ED32323B00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C69657320686561767900";
391 let jsonResult = "{\"expiration\":\"2019-02-12T18:17:18.000\",\"ref_block_num\":44252,\"ref_block_prefix\":4208764560,\"max_net_usage_words\":0,\"max_cpu_usage_ms\":0,\"delay_sec\":0,\"context_free_actions\":[],\"actions\":[{\"account\":\"eosio.token\",\"name\":\"transfer\",\"authorization\":[{\"actor\":\"cryptkeeper\",\"permission\":\"active\"}],\"data\":\"00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C696573206865617679\"}],\"transaction_extensions\":[]}";
392 let abi = fs::read_to_string("transaction.abi.json").unwrap();
393
394 let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
395 let do_hex_2_json = || -> Result<String> {
396 let json = abieos.hex_to_json("eosio", "transaction", hex.as_bytes())?;
397 assert_eq!(jsonResult, json);
398 Ok(String::from(json))
399 }();
400 let do_json_2_hex = || -> Result<String> {
401 let hex_out = abieos.json_to_hex("eosio", "transaction", jsonResult)?;
402 assert_eq!(hex_out.to_ascii_uppercase(), hex);
403
404 Ok(hex_out)
405 }();
406 abieos.destroy();
407 do_json_2_hex?;
408 do_hex_2_json?;
409
410 Ok(())
411 }
412
413 #[test]
414 pub fn test_from_example_2h() -> Result<()> {
415 let abi = fs::read_to_string("transaction.abi.json").unwrap();
416
417 let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
418
419 let do_json_2_hex = || -> Result<String> {
420 let hex_out = abieos.json_to_hex("eosio", "transaction", JSON)?;
421 assert_eq!(hex_out.to_ascii_lowercase(), PACKED);
422 Ok(hex_out)
423 }();
424 abieos.destroy();
425 do_json_2_hex?;
426
427 Ok(())
428 }
429
430 #[test]
431 pub fn test_ttt_abi() -> Result<()> {
432 let test_abi = fs::read_to_string("test/good-2.abi").unwrap();
433 let abi = fs::read_to_string("abi.abi.json").unwrap();
434
435 let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
436 let hex_out = abieos.json_to_hex("eosio", "abi_def", &test_abi);
437 abieos.destroy();
438 hex_out?;
439
440 Ok(())
441 }
442
443 #[test]
444 pub fn test_ttt_abi_bin() -> Result<()> {
445 let test_abi = fs::read_to_string("test/good-2.abi").unwrap();
446 let abi = fs::read_to_string("abi.abi.json").unwrap();
447
448 let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
449 let hex_out = abieos.json_to_hex("eosio", "abi_def", &test_abi);
450 let bin_out = abieos.json_to_bin("eosio", "abi_def", &test_abi);
451 abieos.destroy();
452 let hex = hex_out?.to_string();
453 let bin = bin_out?;
454 let mut bin_h: String = String::with_capacity(bin.len() * 2);
455 for u in &bin {
456 let hex_bit = char_to_hex(u)?.into_bytes();
457 bin_h.push(char::from(hex_bit[0]));
458 bin_h.push(char::from(hex_bit[1]));
459 }
460 assert_eq!(hex.to_ascii_lowercase(), bin_h.to_ascii_lowercase());
461 let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
462 let _json_out = abieos.bin_to_json("eosio", "abi_def", &bin).map_err(|e| {
463 abieos.destroy();
464 Error::with_chain(e, "parsing shipper abi")
465 })?;
466 Ok(())
469 }
470
471 #[test]
472 pub fn test_from_example_2j() -> Result<()> {
473 let abi = fs::read_to_string("transaction.abi.json").unwrap();
474
475 let abieos: ABIEOS = ABIEOS::new_with_abi("eosio", &abi)?;
476 let _do_hex_2_json = || -> Result<String> {
477 let json = abieos.hex_to_json("eosio", "transaction", PACKED.as_bytes())?;
478 let hex_out = abieos.json_to_hex("eosio", "transaction", &json)?;
479 assert_eq!(hex_out.to_ascii_lowercase(), PACKED);
480 Ok(json)
481 }();
482 abieos.destroy();
483
484 Ok(())
485 }
486 #[test]
487 fn test_varuint32() -> Result<()> {
488 let hex_str = "b6843d0123";
489 let hex_str2 = "01002BAD64FF47";
490 let hex_str3 = "ffffffff0faa";
491 let hex_str4 = "ffffffffff0faa";
492 let hex_str3_mixed = "FFfffFff0fAa";
493 let bin_str = hex_to_bin(&hex_str);
494 let (val, bin_str_ex) = varuint32_from_bin(&bin_str)?;
495 assert_eq!(999990, val);
496 assert_eq!(bin_str_ex.len(), 2);
497 assert_eq!(bin_str_ex[0], 01);
498 assert_eq!(bin_str_ex[1], 0x23);
499 let bin_str2 = hex_to_bin(&hex_str2);
500 let (val, bin_str_ex) = varuint32_from_bin(&bin_str2)?;
501 assert_eq!(1, val);
502 assert_eq!(bin_str_ex.len(), 6);
503 let (val64, bin_str_ex) = varuint64_from_bin(&bin_str)?;
504 assert_eq!(999990, val64);
505 assert_eq!(bin_str_ex.len(), 2);
506 assert_eq!(bin_str_ex[0], 01);
507 let bin_str3 = hex_to_bin(&hex_str3);
508 let (val, _bin_str_ex) = varuint32_from_bin(&bin_str3)?;
509 assert_eq!(0xff_ff_ff_ff, val);
510 let bin_str4 = hex_to_bin(&hex_str4);
511 let v: Result<(u32, Vec<u8>)> = varuint32_from_bin(&bin_str4);
512 assert!(v.is_err());
513 let v: Result<(u64, Vec<u8>)> = varuint64_from_bin(&bin_str4);
514 assert!(v.is_ok());
515 assert_eq!(0x7f_ff_ff_ff_ff, v?.0);
516 let bin_str3_mixed = hex_to_bin(hex_str3_mixed);
517 assert_eq!(bin_str3, bin_str3_mixed);
518
519 Ok(())
520 }
521
522 fn char_to_hex(c: &u8) -> Result<String> {
523 let mut r: [u8; 2] = [0; 2];
524 let b1_1 = c & 0xf0;
525 let b1 = b1_1.checked_shr(4).unwrap_or(0);
526 if b1 >= 10 {
527 r[0] = b1 - 10 + b'a';
528 } else {
529 r[0] = b1 + b'0';
530 }
531 let b1 = c & 0x0f;
532 if b1 >= 10 {
533 r[1] = b1 - 10 + b'a';
534 } else {
535 r[1] = b1 + b'0';
536 }
537 Ok(String::from_utf8(r.to_vec())?)
538 }
539}
540fn byte_to_char(x: u8) -> char {
541 (if x <= 9 { x + b'0' } else { x - 10 + b'a' }) as char
542}
543
544pub fn vec_u8_to_hex(out: &[u8]) -> Result<String> {
545 let mut str = String::with_capacity(out.len());
546 for x in out {
547 str.push(byte_to_char((x & 0xf0).checked_shr(4).unwrap_or(0)));
548 str.push(byte_to_char(x & 0x0f));
549 }
550 Ok(str)
551}