ENHANCED: library(crypto): Retain the compact representation of strings.

This avoids the costly (in terms of space requirements!) conversion of
compact lists of characters to lists of integers, making hashing, HKDF,
encryption, decryption, signing and signature verification an order of
magnitude more efficient (primarily in terms of space, also in time).

This makes library(crypto) suitable to process also very large files.
This commit is contained in:
Markus Triska
2020-07-12 10:07:56 +02:00
parent 0016365446
commit 88a2b82f7e
3 changed files with 114 additions and 81 deletions

View File

@@ -688,16 +688,16 @@ impl SystemClauseType {
("$write_term_to_chars", 7) => Some(SystemClauseType::WriteTermToChars), ("$write_term_to_chars", 7) => Some(SystemClauseType::WriteTermToChars),
("$scryer_prolog_version", 1) => Some(SystemClauseType::ScryerPrologVersion), ("$scryer_prolog_version", 1) => Some(SystemClauseType::ScryerPrologVersion),
("$crypto_random_byte", 1) => Some(SystemClauseType::CryptoRandomByte), ("$crypto_random_byte", 1) => Some(SystemClauseType::CryptoRandomByte),
("$crypto_data_hash", 3) => Some(SystemClauseType::CryptoDataHash), ("$crypto_data_hash", 4) => Some(SystemClauseType::CryptoDataHash),
("$crypto_data_hkdf", 6) => Some(SystemClauseType::CryptoDataHKDF), ("$crypto_data_hkdf", 7) => Some(SystemClauseType::CryptoDataHKDF),
("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash), ("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
("$crypto_data_encrypt", 5) => Some(SystemClauseType::CryptoDataEncrypt), ("$crypto_data_encrypt", 6) => Some(SystemClauseType::CryptoDataEncrypt),
("$crypto_data_decrypt", 5) => Some(SystemClauseType::CryptoDataDecrypt), ("$crypto_data_decrypt", 6) => Some(SystemClauseType::CryptoDataDecrypt),
("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult), ("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult),
("$ed25519_sign", 3) => Some(SystemClauseType::Ed25519Sign), ("$ed25519_sign", 5) => Some(SystemClauseType::Ed25519Sign),
("$ed25519_verify", 3) => Some(SystemClauseType::Ed25519Verify), ("$ed25519_verify", 5) => Some(SystemClauseType::Ed25519Verify),
("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair), ("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),
("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey), ("$ed25519_keypair_public_key", 3) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
("$load_html", 3) => Some(SystemClauseType::LoadHTML), ("$load_html", 3) => Some(SystemClauseType::LoadHTML),
("$load_xml", 3) => Some(SystemClauseType::LoadXML), ("$load_xml", 3) => Some(SystemClauseType::LoadXML),
("$getenv", 2) => Some(SystemClauseType::GetEnv), ("$getenv", 2) => Some(SystemClauseType::GetEnv),

View File

@@ -76,13 +76,13 @@ hex_bytes([]) --> [].
hex_bytes([H1,H2|Hs]) --> [Byte], hex_bytes([H1,H2|Hs]) --> [Byte],
{ char_hexval(H1, High), { char_hexval(H1, High),
char_hexval(H2, Low), char_hexval(H2, Low),
Byte is High*16 + Low }, Byte #= High*16 + Low },
hex_bytes(Hs). hex_bytes(Hs).
bytes_hex([]) --> []. bytes_hex([]) --> [].
bytes_hex([B|Bs]) --> [C0,C1], bytes_hex([B|Bs]) --> [C0,C1],
{ High is B>>4, { High #= B>>4,
Low is B /\ 0xf, Low #= B /\ 0xf,
char_hexval(C0, High), char_hexval(C0, High),
char_hexval(C1, Low) char_hexval(C1, Low)
}, },
@@ -101,6 +101,16 @@ must_be_bytes(Bytes, Context) :-
). ).
must_be_byte_chars(Chars, Context) :-
must_be(list, Chars),
( member(Char, Chars),
char_code(Char, Code),
\+ between(0, 255, Code) ->
domain_error(byte_char, Char, Context)
; true
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cryptographically secure random numbers Cryptographically secure random numbers
======================================= =======================================
@@ -191,18 +201,18 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
crypto_data_hash(Data0, Hash, Options0) :- crypto_data_hash(Data0, Hash, Options0) :-
must_be(list, Options0), must_be(list, Options0),
options_data_bytes(Options0, Data0, Data), options_data_chars(Options0, Data0, Data, Encoding),
functor_hash_options(algorithm, A, Options0, _), functor_hash_options(algorithm, A, Options0, _),
( hash_algorithm(A) -> true ( hash_algorithm(A) -> true
; domain_error(hash_algorithm, A, crypto_data_hash/3) ; domain_error(hash_algorithm, A, crypto_data_hash/3)
), ),
'$crypto_data_hash'(Data, HashBytes, A), '$crypto_data_hash'(Data, Encoding, HashBytes, A),
hex_bytes(Hash, HashBytes). hex_bytes(Hash, HashBytes).
options_data_bytes(Options, Data, Bytes) :- options_data_chars(Options, Data, Chars, Encoding) :-
option(encoding(Encoding), Options, utf8), option(encoding(Encoding), Options, utf8),
must_be(atom, Encoding), must_be(atom, Encoding),
encoding_bytes(Encoding, Data, Bytes). encoding_chars(Encoding, Data, Chars).
default_hash(sha256). default_hash(sha256).
@@ -270,12 +280,12 @@ crypto_data_hkdf(Data0, L, Bytes, Options0) :-
), ),
must_be(integer, L), must_be(integer, L),
L >= 0, L >= 0,
options_data_bytes(Options, Data0, Data), options_data_chars(Options, Data0, Data, Encoding),
option(salt(SaltBytes), Options, []), option(salt(SaltBytes), Options, []),
must_be_bytes(SaltBytes, crypto_data_hkdf/4), must_be_bytes(SaltBytes, crypto_data_hkdf/4),
option(info(Info0), Options, []), option(info(Info0), Options, []),
chars_bytes_(Info0, Info, crypto_data_hkdf/4), chars_bytes_(Info0, Info, crypto_data_hkdf/4),
'$crypto_data_hkdf'(Data, SaltBytes, Info, Algorithm, L, Bytes). '$crypto_data_hkdf'(Data, Encoding, SaltBytes, Info, Algorithm, L, Bytes).
hkdf_algorithm(sha256). hkdf_algorithm(sha256).
hkdf_algorithm(sha384). hkdf_algorithm(sha384).
@@ -558,7 +568,7 @@ bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z],
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
options_data_bytes(Options, PlainText0, PlainText), options_data_chars(Options, PlainText0, PlainText, Encoding),
option(tag(Tag), Options, _), option(tag(Tag), Options, _),
( nonvar(Tag) -> ( nonvar(Tag) ->
must_be_bytes(Tag, crypto_data_encrypt/6) must_be_bytes(Tag, crypto_data_encrypt/6)
@@ -570,7 +580,7 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
( Algorithm = 'chacha20-poly1305' -> true ( Algorithm = 'chacha20-poly1305' -> true
; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6) ; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6)
), ),
'$crypto_data_encrypt'(PlainText, Key, IV, Tag, CipherText). '$crypto_data_encrypt'(PlainText, Encoding, Key, IV, Tag, CipherText).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
crypto_data_decrypt(+CipherText, crypto_data_decrypt(+CipherText,
@@ -610,26 +620,25 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :-
must_be(atom, Encoding), must_be(atom, Encoding),
member(Encoding, [utf8,octet]), member(Encoding, [utf8,octet]),
must_be(list, CipherText0), must_be(list, CipherText0),
encoding_bytes(octet, CipherText0, CipherText1), encoding_chars(octet, CipherText0, CipherText1),
append(CipherText1, Tag, CipherText), maplist(char_code, TagChars, Tag),
append(CipherText1, TagChars, CipherText),
( Algorithm = 'chacha20-poly1305' -> true ( Algorithm = 'chacha20-poly1305' -> true
; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6) ; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6)
), ),
'$crypto_data_decrypt'(CipherText, Key, IV, Encoding, PlainText). '$crypto_data_decrypt'(CipherText, octet, Key, IV, Encoding, PlainText).
encoding_bytes(octet, Bs0, Bs) :-
must_be(list, Bs0), encoding_chars(octet, Bs, Cs) :-
( maplist(integer, Bs0) -> must_be(list, Bs),
Bs0 = Bs ( maplist(integer, Bs) ->
; maplist(char_code, Bs0, Bs) maplist(char_code, Cs, Bs)
; Bs = Cs
), ),
must_be_bytes(Bs, crypto_encoding). must_be_byte_chars(Cs, crypto_encoding).
encoding_bytes(utf8, Cs, Bs) :- encoding_chars(utf8, Cs, Cs) :-
must_be(list, Cs), must_be(list, Cs),
( maplist(atom, Cs) -> maplist(must_be(character), Cs).
chars_bytes_(Cs, Bs, crypto_encoding)
; domain_error(encryption_encoding, Cs, crypto)
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Digital signatures with Ed25519 Digital signatures with Ed25519
@@ -667,21 +676,21 @@ encoding_bytes(utf8, Cs, Bs) :-
ed25519_new_keypair(Pair) :- ed25519_new_keypair(Pair) :-
'$ed25519_new_keypair'(Pair). '$ed25519_new_keypair'(Pair).
ed25519_keypair_public_key(Pair0, PublicKey) :- ed25519_keypair_public_key(Pair, PublicKey) :-
encoding_bytes(octet, Pair0, Pair), must_be_byte_chars(Pair, ed25519_keypair_public_key),
'$ed25519_keypair_public_key'(Pair, PublicKey). '$ed25519_keypair_public_key'(Pair, octet, PublicKey).
ed25519_sign(Key0, Data0, Signature, Options) :- ed25519_sign(Key, Data0, Signature, Options) :-
options_data_bytes(Options, Data0, Data), must_be_byte_chars(Key, ed25519_sign),
encoding_bytes(octet, Key0, Key), options_data_chars(Options, Data0, Data, Encoding),
'$ed25519_sign'(Key, Data, Signature0), '$ed25519_sign'(Key, octet, Data, Encoding, Signature0),
hex_bytes(Signature, Signature0). hex_bytes(Signature, Signature0).
ed25519_verify(Key0, Data0, Signature0, Options) :- ed25519_verify(Key, Data0, Signature0, Options) :-
options_data_bytes(Options, Data0, Data), must_be_byte_chars(Key, ed25519_verify),
encoding_bytes(octet, Key0, Key), options_data_chars(Options, Data0, Data, Encoding),
hex_bytes(Signature0, Signature), hex_bytes(Signature0, Signature),
'$ed25519_verify'(Key, Data, Signature). '$ed25519_verify'(Key, octet, Data, Encoding, Signature).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Operations on Elliptic Curves Operations on Elliptic Curves

View File

@@ -5291,11 +5291,9 @@ impl MachineState {
self.unify(arg, byte); self.unify(arg, byte);
} }
&SystemClauseType::CryptoDataHash => { &SystemClauseType::CryptoDataHash => {
let stub = MachineError::functor_stub(clause_name!("crypto_data_hash"), 3); let bytes = self.string_encoding_bytes(1, 2);
let bytes = self.integers_to_bytevec(temp_v!(1), stub);
let algorithm = self[temp_v!(3)]; let algorithm_str = match self.store(self.deref(self[temp_v!(4)])) {
let algorithm_str = match self.store(self.deref(algorithm)) {
Addr::Con(h) if self.heap.atom_at(h) => { Addr::Con(h) if self.heap.atom_at(h) => {
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
atom.as_str() atom.as_str()
@@ -5344,17 +5342,16 @@ impl MachineState {
} }
}; };
self.unify(self[temp_v!(2)], ints_list); self.unify(self[temp_v!(3)], ints_list);
} }
&SystemClauseType::CryptoDataHKDF => { &SystemClauseType::CryptoDataHKDF => {
let data = self.string_encoding_bytes(1, 2);
let stub1 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4); let stub1 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4);
let data = self.integers_to_bytevec(temp_v!(1), stub1); let salt = self.integers_to_bytevec(temp_v!(3), stub1);
let stub2 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4); let stub2 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4);
let salt = self.integers_to_bytevec(temp_v!(2), stub2); let info = self.integers_to_bytevec(temp_v!(4), stub2);
let stub3 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4);
let info = self.integers_to_bytevec(temp_v!(3), stub3);
let algorithm = match self.store(self.deref(self[temp_v!(4)])) { let algorithm = match self.store(self.deref(self[temp_v!(5)])) {
Addr::Con(h) if self.heap.atom_at(h) => { Addr::Con(h) if self.heap.atom_at(h) => {
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
atom.as_str() atom.as_str()
@@ -5368,7 +5365,7 @@ impl MachineState {
}; };
let length = let length =
match Number::try_from((self[temp_v!(5)], &self.heap)) { match Number::try_from((self[temp_v!(6)], &self.heap)) {
Ok(Number::Fixnum(n)) => { Ok(Number::Fixnum(n)) => {
usize::try_from(n).unwrap() usize::try_from(n).unwrap()
} }
@@ -5400,7 +5397,7 @@ impl MachineState {
Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize)))))
}; };
self.unify(self[temp_v!(6)], ints_list); self.unify(self[temp_v!(7)], ints_list);
} }
&SystemClauseType::CryptoPasswordHash => { &SystemClauseType::CryptoPasswordHash => {
let stub1 = MachineError::functor_stub(clause_name!("crypto_password_hash"), 3); let stub1 = MachineError::functor_stub(clause_name!("crypto_password_hash"), 3);
@@ -5436,12 +5433,11 @@ impl MachineState {
self.unify(self[temp_v!(4)], ints_list); self.unify(self[temp_v!(4)], ints_list);
} }
&SystemClauseType::CryptoDataEncrypt => { &SystemClauseType::CryptoDataEncrypt => {
let stub1 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6); let data = self.string_encoding_bytes(1, 2);
let data = self.integers_to_bytevec(temp_v!(1), stub1);
let stub2 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6); let stub2 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6);
let key = self.integers_to_bytevec(temp_v!(2), stub2); let key = self.integers_to_bytevec(temp_v!(3), stub2);
let stub3 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6); let stub3 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6);
let iv = self.integers_to_bytevec(temp_v!(3), stub3); let iv = self.integers_to_bytevec(temp_v!(4), stub3);
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap(); let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap();
@@ -5462,18 +5458,17 @@ impl MachineState {
self.heap.put_complete_string(&buffer) self.heap.put_complete_string(&buffer)
}; };
self.unify(self[temp_v!(4)], tag_list); self.unify(self[temp_v!(5)], tag_list);
self.unify(self[temp_v!(5)], complete_string); self.unify(self[temp_v!(6)], complete_string);
} }
&SystemClauseType::CryptoDataDecrypt => { &SystemClauseType::CryptoDataDecrypt => {
let data = self.string_encoding_bytes(1, 2);
let stub1 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6); let stub1 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6);
let data = self.integers_to_bytevec(temp_v!(1), stub1); let key = self.integers_to_bytevec(temp_v!(3), stub1);
let stub2 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6); let stub2 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6);
let key = self.integers_to_bytevec(temp_v!(2), stub2); let iv = self.integers_to_bytevec(temp_v!(4), stub2);
let stub3 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6);
let iv = self.integers_to_bytevec(temp_v!(3), stub3);
let encoding = match self.store(self.deref(self[temp_v!(4)])) { let encoding = match self.store(self.deref(self[temp_v!(5)])) {
Addr::Con(h) if self.heap.atom_at(h) => { Addr::Con(h) if self.heap.atom_at(h) => {
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
atom.as_str() atom.as_str()
@@ -5512,7 +5507,7 @@ impl MachineState {
self.heap.put_complete_string(&buffer) self.heap.put_complete_string(&buffer)
}; };
self.unify(self[temp_v!(5)], complete_string); self.unify(self[temp_v!(6)], complete_string);
} }
&SystemClauseType::CryptoCurveScalarMult => { &SystemClauseType::CryptoCurveScalarMult => {
let curve = match self.store(self.deref(self[temp_v!(1)])) { let curve = match self.store(self.deref(self[temp_v!(1)])) {
@@ -5573,8 +5568,7 @@ impl MachineState {
self.unify(self[temp_v!(1)], complete_string); self.unify(self[temp_v!(1)], complete_string);
} }
&SystemClauseType::Ed25519KeyPairPublicKey => { &SystemClauseType::Ed25519KeyPairPublicKey => {
let stub1 = MachineError::functor_stub(clause_name!("ed25519_keypair_public_key"), 2); let bytes = self.string_encoding_bytes(1, 2);
let bytes = self.integers_to_bytevec(temp_v!(1), stub1);
let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) { let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) {
Ok(kp) => { kp } Ok(kp) => { kp }
@@ -5586,13 +5580,11 @@ impl MachineState {
self.heap.put_complete_string(&buffer) self.heap.put_complete_string(&buffer)
}; };
self.unify(self[temp_v!(2)], complete_string); self.unify(self[temp_v!(3)], complete_string);
} }
&SystemClauseType::Ed25519Sign => { &SystemClauseType::Ed25519Sign => {
let stub1 = MachineError::functor_stub(clause_name!("ed25519_sign"), 4); let key = self.string_encoding_bytes(1, 2);
let key = self.integers_to_bytevec(temp_v!(1), stub1); let data = self.string_encoding_bytes(3, 4);
let stub2 = MachineError::functor_stub(clause_name!("ed25519_sign"), 4);
let data = self.integers_to_bytevec(temp_v!(2), stub2);
let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) { let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) {
Ok(kp) => { kp } Ok(kp) => { kp }
@@ -5604,15 +5596,13 @@ impl MachineState {
let sig_list = let sig_list =
Addr::HeapCell(self.heap.to_list(sig.as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))); Addr::HeapCell(self.heap.to_list(sig.as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize)))));
self.unify(self[temp_v!(3)], sig_list); self.unify(self[temp_v!(5)], sig_list);
} }
&SystemClauseType::Ed25519Verify => { &SystemClauseType::Ed25519Verify => {
let stub1 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4); let key = self.string_encoding_bytes(1, 2);
let key = self.integers_to_bytevec(temp_v!(1), stub1); let data = self.string_encoding_bytes(3, 4);
let stub2 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4); let stub = MachineError::functor_stub(clause_name!("ed25519_verify"), 5);
let data = self.integers_to_bytevec(temp_v!(2), stub2); let signature = self.integers_to_bytevec(temp_v!(5), stub);
let stub3 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4);
let signature = self.integers_to_bytevec(temp_v!(3), stub3);
let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, &key); let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, &key);
match peer_public_key.verify(&data, &signature) { match peer_public_key.verify(&data, &signature) {
@@ -5665,6 +5655,40 @@ impl MachineState {
return_from_clause!(self.last_call, self) return_from_clause!(self.last_call, self)
} }
pub(super)
fn string_encoding_bytes(
&mut self,
data_arg: usize,
encoding_arg: usize,
) -> Vec<u8> {
let data = self.heap_pstr_iter(self[temp_v!(data_arg)]).to_string();
let encoding_str = match self.store(self.deref(self[temp_v!(encoding_arg)])) {
Addr::Con(h) if self.heap.atom_at(h) => {
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
atom.as_str()
} else {
unreachable!()
}
}
_ => {
unreachable!()
}
};
match encoding_str {
"utf8" => { data.into_bytes() }
"octet" => {
let mut buf = vec![];
for c in data.chars() {
buf.push(c as u8);
}
buf
}
_ => { unreachable!() }
}
}
pub(super) pub(super)
fn xml_node_to_term( fn xml_node_to_term(
&mut self, &mut self,