ADDED: chars_base64/3 for efficient bidirectional Base64 conversion.

This commit is contained in:
Markus Triska
2020-07-22 20:17:46 +02:00
parent 82dfa690d2
commit 4c510001ce
5 changed files with 112 additions and 4 deletions

View File

@@ -46,3 +46,4 @@ native-tls = "0.2.4"
chrono = "0.4.11" chrono = "0.4.11"
select = "0.4.3" select = "0.4.3"
roxmltree = "0.11.0" roxmltree = "0.11.0"
base64 = "0.12.3"

View File

@@ -387,9 +387,10 @@ The modules that ship with Scryer Prolog are also called
file, reading lazily only as much as is needed. Due to the compact file, reading lazily only as much as is needed. Due to the compact
internal string representation, also extremely large files can be internal string representation, also extremely large files can be
efficiently processed with Scryer Prolog in this way. efficiently processed with Scryer Prolog in this way.
* [`charsio`](src/lib/charsio.pl) Various predicates that are * [`charsio`](src/lib/charsio.pl) Various predicates that are useful
useful for parsing and reasoning about characters, notably for parsing and reasoning about characters, notably `char_type/2` to
`char_type/2` to classify characters according to their type. classify characters according to their type, and conversion
predicates for different encodings of strings.
* [`error`](src/lib/error.pl) * [`error`](src/lib/error.pl)
`must_be/2` and `can_be/2` complement the type checks provided by `must_be/2` and `can_be/2` complement the type checks provided by
[`library(si)`](src/lib/si.pl), and are especially useful for [`library(si)`](src/lib/si.pl), and are especially useful for

View File

@@ -314,6 +314,7 @@ pub enum SystemClauseType {
GetEnv, GetEnv,
SetEnv, SetEnv,
UnsetEnv, UnsetEnv,
CharsBase64,
} }
impl SystemClauseType { impl SystemClauseType {
@@ -526,6 +527,7 @@ impl SystemClauseType {
&SystemClauseType::GetEnv => clause_name!("$getenv"), &SystemClauseType::GetEnv => clause_name!("$getenv"),
&SystemClauseType::SetEnv => clause_name!("$setenv"), &SystemClauseType::SetEnv => clause_name!("$setenv"),
&SystemClauseType::UnsetEnv => clause_name!("$unsetenv"), &SystemClauseType::UnsetEnv => clause_name!("$unsetenv"),
&SystemClauseType::CharsBase64 => clause_name!("$chars_base64"),
} }
} }
@@ -718,6 +720,7 @@ impl SystemClauseType {
("$getenv", 2) => Some(SystemClauseType::GetEnv), ("$getenv", 2) => Some(SystemClauseType::GetEnv),
("$setenv", 2) => Some(SystemClauseType::SetEnv), ("$setenv", 2) => Some(SystemClauseType::SetEnv),
("$unsetenv", 1) => Some(SystemClauseType::UnsetEnv), ("$unsetenv", 1) => Some(SystemClauseType::UnsetEnv),
("$chars_base64", 4) => Some(SystemClauseType::CharsBase64),
_ => None, _ => None,
} }
} }

View File

@@ -3,7 +3,8 @@
get_single_char/1, get_single_char/1,
read_line_to_chars/3, read_line_to_chars/3,
read_term_from_chars/2, read_term_from_chars/2,
write_term_to_chars/3]). write_term_to_chars/3,
chars_base64/3]).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
@@ -194,3 +195,42 @@ read_line_to_chars(Stream, Cs0, Cs) :-
; read_line_to_chars(Stream, Rest, Cs) ; read_line_to_chars(Stream, Rest, Cs)
) )
). ).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Relation between a list of characters Cs and its Base64 encoding Bs,
also a list of characters.
Options are:
- padding(Boolean)
Whether to use padding: true (the default) or false.
- charset(C)
Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5).
Example:
?- chars_base64("hello", Bs, []).
Bs = "aGVsbG8="
; false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
chars_base64(Cs, Bs, Options) :-
must_be(list, Options),
( member(O, Options), var(O) ->
instantiation_error(chars_base64/3)
; ( member(padding(Padding), Options) -> true
; Padding = true
),
( member(charset(Charset), Options) -> true
; Charset = standard
)
),
must_be(boolean, Padding),
( var(Cs) ->
must_be(list, Bs),
maplist(must_be(character), Bs),
'$chars_base64'(Cs, Bs, Padding, Charset)
; must_be(list, Cs),
maplist(must_be(character), Cs),
'$chars_base64'(Cs, Bs, Padding, Charset)
).

View File

@@ -55,6 +55,7 @@ use crate::native_tls::TlsConnector;
extern crate select; extern crate select;
use roxmltree; use roxmltree;
use base64;
pub fn get_key() -> KeyEvent { pub fn get_key() -> KeyEvent {
let key; let key;
@@ -5745,6 +5746,68 @@ impl MachineState {
let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string(); let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
env::remove_var(key); env::remove_var(key);
} }
&SystemClauseType::CharsBase64 => {
let mut options = vec![];
for i in 3..5 {
match self.store(self.deref(self[temp_v!(i)])) {
Addr::Con(h) if self.heap.atom_at(h) => {
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
options.push(atom.as_str());
} else {
unreachable!()
}
}
_ => {
unreachable!()
}
};
}
let config =
if options[0] == "true" {
if options[1] == "standard" {
base64::STANDARD
} else {
base64::URL_SAFE
}
} else {
if options[1] == "standard" {
base64::STANDARD_NO_PAD
} else {
base64::URL_SAFE_NO_PAD
}
};
if self.store(self.deref(self[temp_v!(1)])).is_ref() {
let b64 = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
let bytes = base64::decode_config(b64, config);
match bytes {
Ok(bs) => {
let mut string = String::new();
for c in bs {
string.push(c as char);
}
let cstr = self.heap.put_complete_string(&string);
self.unify(self[temp_v!(1)], cstr);
}
_ => {
self.fail = true;
return Ok(());
}
}
} else {
let mut bytes = vec![];
for c in self.heap_pstr_iter(self[temp_v!(1)]).to_string().chars() {
bytes.push(c as u8);
}
let b64 = base64::encode_config(bytes, config);
let cstr = self.heap.put_complete_string(&b64);
self.unify(self[temp_v!(2)], cstr);
}
}
}; };
return_from_clause!(self.last_call, self) return_from_clause!(self.last_call, self)