get rid of populatable and bytesconvertable traits (#2019)
This commit is contained in:
parent
3439c06a1c
commit
9a5668f802
@ -127,14 +127,14 @@ impl Impl for EcRecover {
|
||||
let s = H256::from_slice(&input[96..128]);
|
||||
|
||||
let bit = match v[31] {
|
||||
27 | 28 if &v.as_slice()[..31] == &[0; 31] => v[31] - 27,
|
||||
27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let s = Signature::from_rsv(&r, &s, bit);
|
||||
if s.is_valid() {
|
||||
if let Ok(p) = ec_recover(&s, &hash) {
|
||||
let r = p.as_slice().sha3();
|
||||
let r = p.sha3();
|
||||
|
||||
let out_len = min(output.len(), 32);
|
||||
|
||||
|
@ -28,7 +28,7 @@ use ethkey::Error as EthkeyError;
|
||||
|
||||
pub use types::executed::{ExecutionError, CallError};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
/// Errors concerning transaction processing.
|
||||
pub enum TransactionError {
|
||||
/// Transaction is already imported to the queue
|
||||
@ -87,7 +87,7 @@ impl fmt::Display for TransactionError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
|
||||
/// Errors concerning block processing.
|
||||
pub enum BlockError {
|
||||
/// Block has too many uncles.
|
||||
@ -185,7 +185,7 @@ impl fmt::Display for BlockError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
/// Import to the block queue result
|
||||
pub enum ImportError {
|
||||
/// Already in the block chain.
|
||||
|
@ -76,7 +76,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
instructions::SSTORE => {
|
||||
let address = H256::from(stack.peek(0));
|
||||
let newval = stack.peek(1);
|
||||
let val = U256::from(ext.storage_at(&address).as_slice());
|
||||
let val = U256::from(&*ext.storage_at(&address));
|
||||
|
||||
let gas = if val.is_zero() && !newval.is_zero() {
|
||||
schedule.sstore_set_gas
|
||||
|
@ -403,18 +403,18 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let offset = stack.pop_back();
|
||||
let size = stack.pop_back();
|
||||
let sha3 = self.mem.read_slice(offset, size).sha3();
|
||||
stack.push(U256::from(sha3.as_slice()));
|
||||
stack.push(U256::from(&*sha3));
|
||||
},
|
||||
instructions::SLOAD => {
|
||||
let key = H256::from(&stack.pop_back());
|
||||
let word = U256::from(ext.storage_at(&key).as_slice());
|
||||
let word = U256::from(&*ext.storage_at(&key));
|
||||
stack.push(word);
|
||||
},
|
||||
instructions::SSTORE => {
|
||||
let address = H256::from(&stack.pop_back());
|
||||
let val = stack.pop_back();
|
||||
|
||||
let current_val = U256::from(ext.storage_at(&address).as_slice());
|
||||
let current_val = U256::from(&*ext.storage_at(&address));
|
||||
// Increase refund for clear
|
||||
if !self.is_zero(¤t_val) && self.is_zero(&val) {
|
||||
ext.inc_sstore_clears();
|
||||
@ -491,7 +491,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
instructions::BLOCKHASH => {
|
||||
let block_number = stack.pop_back();
|
||||
let block_hash = ext.blockhash(&block_number);
|
||||
stack.push(U256::from(block_hash.as_slice()));
|
||||
stack.push(U256::from(&*block_hash));
|
||||
},
|
||||
instructions::COINBASE => {
|
||||
stack.push(address_to_u256(ext.env_info().author.clone()));
|
||||
@ -807,7 +807,7 @@ fn u256_to_address(value: &U256) -> Address {
|
||||
|
||||
#[inline]
|
||||
fn address_to_u256(value: Address) -> U256 {
|
||||
U256::from(H256::from(value).as_slice())
|
||||
U256::from(&*H256::from(value))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -57,7 +57,7 @@ impl PodAccount {
|
||||
let mut stream = RlpStream::new_list(4);
|
||||
stream.append(&self.nonce);
|
||||
stream.append(&self.balance);
|
||||
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect()));
|
||||
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(&**v)).to_vec())).collect()));
|
||||
stream.append(&self.code.as_ref().unwrap_or(&vec![]).sha3());
|
||||
stream.out()
|
||||
}
|
||||
@ -71,7 +71,7 @@ impl PodAccount {
|
||||
let mut r = H256::new();
|
||||
let mut t = SecTrieDBMut::new(db, &mut r);
|
||||
for (k, v) in &self.storage {
|
||||
if let Err(e) = t.insert(k, &encode(&U256::from(v.as_slice()))) {
|
||||
if let Err(e) = t.insert(k, &encode(&U256::from(&**v))) {
|
||||
warn!("Encountered potential DB corruption: {}", e);
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ impl Spec {
|
||||
{
|
||||
let mut t = SecTrieDBMut::new(db, &mut root);
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
try!(t.insert(address.as_slice(), &account.rlp()));
|
||||
try!(t.insert(&**address, &account.rlp()));
|
||||
}
|
||||
}
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
|
@ -287,7 +287,7 @@ impl Account {
|
||||
// so we can call overloaded `to_bytes` method
|
||||
let res = match v.is_zero() {
|
||||
true => t.remove(k),
|
||||
false => t.insert(k, &encode(&U256::from(v.as_slice()))),
|
||||
false => t.insert(k, &encode(&U256::from(&*v))),
|
||||
};
|
||||
|
||||
if let Err(e) = res {
|
||||
|
@ -98,12 +98,10 @@ impl AccountDiff {
|
||||
|
||||
// TODO: refactor into something nicer.
|
||||
fn interpreted_hash(u: &H256) -> String {
|
||||
use util::bytes::*;
|
||||
|
||||
if u <= &H256::from(0xffffffff) {
|
||||
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32())
|
||||
format!("{} = 0x{:x}", U256::from(&**u).low_u32(), U256::from(&**u).low_u32())
|
||||
} else if u <= &H256::from(u64::max_value()) {
|
||||
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u64(), U256::from(u.as_slice()).low_u64())
|
||||
format!("{} = 0x{:x}", U256::from(&**u).low_u64(), U256::from(&**u).low_u64())
|
||||
// } else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") {
|
||||
// format!("@{}", Address::from(u))
|
||||
} else {
|
||||
@ -113,7 +111,7 @@ fn interpreted_hash(u: &H256) -> String {
|
||||
|
||||
impl fmt::Display for AccountDiff {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use util::bytes::*;
|
||||
use util::bytes::ToPretty;
|
||||
|
||||
match self.nonce {
|
||||
Diff::Born(ref x) => try!(write!(f, " non {}", x)),
|
||||
|
@ -18,9 +18,8 @@
|
||||
|
||||
use ipc::binary::{BinaryConvertError, BinaryConvertable};
|
||||
use error::{TransactionError, Error};
|
||||
use util::Populatable;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
/// Represents the result of importing transaction.
|
||||
pub enum TransactionImportResult {
|
||||
/// Transaction was imported to current queue.
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
//! Binary representation of types
|
||||
|
||||
use util::bytes::Populatable;
|
||||
use util::{U256, U512, H256, H2048, Address};
|
||||
use std::mem;
|
||||
use std::collections::{VecDeque, BTreeMap};
|
||||
@ -706,14 +705,21 @@ pub fn serialize<T: BinaryConvertable>(t: &T) -> Result<Vec<u8>, BinaryError> {
|
||||
#[macro_export]
|
||||
macro_rules! binary_fixed_size {
|
||||
($target_ty: ty) => {
|
||||
impl BinaryConvertable for $target_ty {
|
||||
impl BinaryConvertable for $target_ty where $target_ty: Copy {
|
||||
fn from_bytes(bytes: &[u8], _length_stack: &mut ::std::collections::VecDeque<usize>) -> Result<Self, BinaryConvertError> {
|
||||
match bytes.len().cmp(&::std::mem::size_of::<$target_ty>()) {
|
||||
let size = ::std::mem::size_of::<$target_ty>();
|
||||
match bytes.len().cmp(&size) {
|
||||
::std::cmp::Ordering::Equal => (),
|
||||
_ => return Err(BinaryConvertError::size(::std::mem::size_of::<$target_ty>(), bytes.len())),
|
||||
_ => return Err(BinaryConvertError::size(size, bytes.len())),
|
||||
};
|
||||
let mut res: Self = unsafe { ::std::mem::uninitialized() };
|
||||
res.copy_raw(bytes);
|
||||
let res: Self = unsafe {
|
||||
let mut temp = ::std::mem::zeroed();
|
||||
let temp_ptr = &mut temp as *mut _ as *mut u8;
|
||||
::std::ptr::copy_nonoverlapping(bytes.as_ptr(), temp_ptr, size);
|
||||
|
||||
temp
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@ -731,14 +737,14 @@ macro_rules! binary_fixed_size {
|
||||
}
|
||||
|
||||
/// Fixed-sized version of Handshake struct
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct BinHandshake {
|
||||
api_version: BinVersion,
|
||||
protocol_version: BinVersion,
|
||||
}
|
||||
|
||||
/// Shorten version of semver Version without `pre` and `build` information
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct BinVersion {
|
||||
pub major: u64,
|
||||
pub minor: u64,
|
||||
|
@ -17,7 +17,7 @@
|
||||
use std::sync::Arc;
|
||||
use network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId,
|
||||
NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError};
|
||||
use util::{U256, H256, Populatable};
|
||||
use util::{U256, H256};
|
||||
use io::{TimerToken};
|
||||
use ethcore::client::{BlockChainClient, ChainNotify};
|
||||
use ethcore::header::BlockNumber;
|
||||
@ -32,7 +32,7 @@ use parking_lot::RwLock;
|
||||
pub const ETH_PROTOCOL: &'static str = "eth";
|
||||
|
||||
/// Sync configuration
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SyncConfig {
|
||||
/// Max blocks to download ahead
|
||||
pub max_download_ahead_blocks: usize,
|
||||
|
@ -156,7 +156,7 @@ pub enum SyncState {
|
||||
}
|
||||
|
||||
/// Syncing status and statistics
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SyncStatus {
|
||||
/// State
|
||||
pub state: SyncState,
|
||||
|
@ -14,26 +14,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Unified interfaces for bytes operations on basic types
|
||||
//! General bytes-related utilities.
|
||||
//!
|
||||
//! # Examples
|
||||
//! ```rust
|
||||
//! extern crate ethcore_util as util;
|
||||
//!
|
||||
//! fn bytes_convertable() {
|
||||
//! use util::bytes::BytesConvertable;
|
||||
//!
|
||||
//! let arr = [0; 5];
|
||||
//! let slice: &[u8] = arr.as_slice();
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! bytes_convertable();
|
||||
//! }
|
||||
//! ```
|
||||
//! Includes a pretty-printer for bytes, in the form of `ToPretty` and `PrettySlice`
|
||||
//! as
|
||||
|
||||
use std::fmt;
|
||||
use std::slice;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Slice pretty print helper
|
||||
@ -71,20 +57,9 @@ pub trait ToPretty {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPretty for &'a [u8] {
|
||||
impl<T: AsRef<[u8]>> ToPretty for T {
|
||||
fn pretty(&self) -> PrettySlice {
|
||||
PrettySlice(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToPretty for &'a Bytes {
|
||||
fn pretty(&self) -> PrettySlice {
|
||||
PrettySlice(self.as_slice())
|
||||
}
|
||||
}
|
||||
impl ToPretty for Bytes {
|
||||
fn pretty(&self) -> PrettySlice {
|
||||
PrettySlice(self.as_slice())
|
||||
PrettySlice(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,128 +91,5 @@ impl <'a> DerefMut for BytesRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Vector of bytes
|
||||
pub type Bytes = Vec<u8>;
|
||||
|
||||
/// Slice of bytes to underlying memory
|
||||
pub trait BytesConvertable {
|
||||
/// Get the underlying byte-wise representation of the value.
|
||||
fn as_slice(&self) -> &[u8];
|
||||
/// Get a copy of the underlying byte-wise representation.
|
||||
fn to_bytes(&self) -> Bytes { self.as_slice().to_vec() }
|
||||
}
|
||||
|
||||
impl<T> BytesConvertable for T where T: AsRef<[u8]> {
|
||||
fn as_slice(&self) -> &[u8] { self.as_ref() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_convertable() {
|
||||
assert_eq!(vec![0x12u8, 0x34].as_slice(), &[0x12u8, 0x34]);
|
||||
assert!([0u8; 0].as_slice().is_empty());
|
||||
}
|
||||
|
||||
/// Simple trait to allow for raw population of a Sized object from a byte slice.
|
||||
pub trait Populatable {
|
||||
/// Copies a bunch of bytes `d` to `self`, overwriting as necessary.
|
||||
///
|
||||
/// If `d` is smaller, zero-out the remaining bytes.
|
||||
fn populate_raw(&mut self, d: &[u8]) {
|
||||
let mut s = self.as_slice_mut();
|
||||
for i in 0..s.len() {
|
||||
s[i] = if i < d.len() {d[i]} else {0};
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies a bunch of bytes `d` to `self`, overwriting as necessary.
|
||||
///
|
||||
/// If `d` is smaller, will leave some bytes untouched.
|
||||
fn copy_raw(&mut self, d: &[u8]) {
|
||||
use std::io::Write;
|
||||
self.as_slice_mut().write(d).unwrap();
|
||||
}
|
||||
|
||||
/// Copies the raw representation of an object `d` to `self`, overwriting as necessary.
|
||||
///
|
||||
/// If `d` is smaller, zero-out the remaining bytes.
|
||||
fn populate_raw_from(&mut self, d: &BytesConvertable) { self.populate_raw(d.as_slice()); }
|
||||
|
||||
/// Copies the raw representation of an object `d` to `self`, overwriting as necessary.
|
||||
///
|
||||
/// If `d` is smaller, will leave some bytes untouched.
|
||||
fn copy_raw_from(&mut self, d: &BytesConvertable) { self.copy_raw(d.as_slice()); }
|
||||
|
||||
/// Get the raw slice for this object.
|
||||
fn as_slice_mut(&mut self) -> &mut [u8];
|
||||
}
|
||||
|
||||
impl<T> Populatable for T where T: Sized {
|
||||
fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
use std::mem;
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut T as *mut u8, mem::size_of::<T>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Populatable for [T] where T: Sized {
|
||||
fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
use std::mem;
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self.as_mut_ptr() as *mut u8, mem::size_of::<T>() * self.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fax_raw() {
|
||||
let mut x = [255u8; 4];
|
||||
x.copy_raw(&[1u8; 2][..]);
|
||||
assert_eq!(x, [1u8, 1, 255, 255]);
|
||||
let mut x = [255u8; 4];
|
||||
x.copy_raw(&[1u8; 6][..]);
|
||||
assert_eq!(x, [1u8, 1, 1, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn populate_raw() {
|
||||
let mut x = [255u8; 4];
|
||||
x.populate_raw(&[1u8; 2][..]);
|
||||
assert_eq!(x, [1u8, 1, 0, 0]);
|
||||
let mut x = [255u8; 4];
|
||||
x.populate_raw(&[1u8; 6][..]);
|
||||
assert_eq!(x, [1u8, 1, 1, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn populate_raw_dyn() {
|
||||
let mut x = [255u8; 4];
|
||||
x.populate_raw(&[1u8; 2][..]);
|
||||
assert_eq!(&x[..], [1u8, 1, 0, 0]);
|
||||
let mut x = [255u8; 4];
|
||||
x.populate_raw(&[1u8; 6][..]);
|
||||
assert_eq!(&x[..], [1u8, 1, 1, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fax_raw_dyn() {
|
||||
let mut x = [255u8; 4];
|
||||
x.copy_raw(&[1u8; 2][..]);
|
||||
assert_eq!(&x[..], [1u8, 1, 255, 255]);
|
||||
let mut x = [255u8; 4];
|
||||
x.copy_raw(&[1u8; 6][..]);
|
||||
assert_eq!(&x[..], [1u8, 1, 1, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn populate_big_types() {
|
||||
use hash::*;
|
||||
let a: H160 = "ffffffffffffffffffffffffffffffffffffffff".into();
|
||||
let mut h: H256 = 0x69.into();
|
||||
h.populate_raw_from(&a);
|
||||
assert_eq!(h, "ffffffffffffffffffffffffffffffffffffffff000000000000000000000000".into());
|
||||
|
||||
let mut h: H256 = 0x69.into();
|
||||
h.copy_raw_from(&a);
|
||||
assert_eq!(h, "ffffffffffffffffffffffffffffffffffffffff000000000000000000000069".into());
|
||||
}
|
||||
/// Vector of bytes.
|
||||
pub type Bytes = Vec<u8>;
|
@ -74,7 +74,7 @@ impl fmt::Display for UtilError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
/// Error indicating an expected value was not found.
|
||||
pub struct Mismatch<T: fmt::Debug> {
|
||||
/// Value expected.
|
||||
@ -89,7 +89,7 @@ impl<T: fmt::Debug + fmt::Display> fmt::Display for Mismatch<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
/// Error indicating value found is outside of a valid range.
|
||||
pub struct OutOfBounds<T: fmt::Debug> {
|
||||
/// Minimum allowed value.
|
||||
|
@ -95,7 +95,7 @@ impl EarlyMergeDB {
|
||||
}
|
||||
|
||||
fn morph_key(key: &H256, index: u8) -> Bytes {
|
||||
let mut ret = key.to_bytes();
|
||||
let mut ret = (&**key).to_owned();
|
||||
ret.push(index);
|
||||
ret
|
||||
}
|
||||
|
@ -296,8 +296,8 @@ fn memorydb_denote() {
|
||||
for _ in 0..1000 {
|
||||
let r = H256::random();
|
||||
let k = r.sha3();
|
||||
let (v, rc) = m.denote(&k, r.to_bytes());
|
||||
assert_eq!(v, r.as_slice());
|
||||
let (v, rc) = m.denote(&k, r.to_vec());
|
||||
assert_eq!(v, &*r);
|
||||
assert_eq!(rc, 0);
|
||||
}
|
||||
|
||||
|
@ -18,16 +18,13 @@
|
||||
extern crate sha3 as sha3_ext;
|
||||
|
||||
use std::io;
|
||||
use std::mem::uninitialized;
|
||||
use tiny_keccak::Keccak;
|
||||
use bytes::{BytesConvertable, Populatable};
|
||||
use hash::{H256, FixedHash};
|
||||
use self::sha3_ext::*;
|
||||
|
||||
/// Get the SHA3 (i.e. Keccak) hash of the empty bytes string.
|
||||
pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] );
|
||||
|
||||
|
||||
/// Types implementing this trait are sha3able.
|
||||
///
|
||||
/// ```
|
||||
@ -50,17 +47,16 @@ pub trait Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Hashable for T where T: BytesConvertable {
|
||||
impl<T> Hashable for T where T: AsRef<[u8]> {
|
||||
fn sha3(&self) -> H256 {
|
||||
unsafe {
|
||||
let mut ret: H256 = uninitialized();
|
||||
self.sha3_into(ret.as_slice_mut());
|
||||
ret
|
||||
}
|
||||
let mut ret: H256 = H256::zero();
|
||||
self.sha3_into(&mut *ret);
|
||||
ret
|
||||
}
|
||||
fn sha3_into(&self, dest: &mut [u8]) {
|
||||
let input: &[u8] = self.as_ref();
|
||||
|
||||
unsafe {
|
||||
let input: &[u8] = self.as_slice();
|
||||
sha3_256(dest.as_mut_ptr(), dest.len(), input.as_ptr(), input.len());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user