Merge pull request #638 from triska/base64
ADDED: chars_base64/3 for efficient bidirectional Base64 conversion.
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,48 @@ 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.
|
||||||
|
|
||||||
|
At least one of the arguments must be instantiated.
|
||||||
|
|
||||||
|
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),
|
||||||
|
must_be(atom, Charset),
|
||||||
|
( member(Charset, [standard,url]) -> true
|
||||||
|
; domain_error(charset, Charset, chars_base64/3)
|
||||||
|
),
|
||||||
|
( 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)
|
||||||
|
).
|
||||||
|
|||||||
@@ -426,61 +426,17 @@ crypto_password_hash(Password0, Hash, Options) :-
|
|||||||
|
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Bidirectional Bytes <-> Base64 conversion
|
Bidirectional Bytes <-> Base64 conversion *without padding*.
|
||||||
=========================================
|
|
||||||
|
|
||||||
This implements Base64 conversion *without padding*.
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
n_base64(0 , 'A'). n_base64(1 , 'B'). n_base64(2 , 'C'). n_base64(3 , 'D').
|
bytes_base64(Bytes, Base64) :-
|
||||||
n_base64(4 , 'E'). n_base64(5 , 'F'). n_base64(6 , 'G'). n_base64(7 , 'H').
|
( var(Bytes) ->
|
||||||
n_base64(8 , 'I'). n_base64(9 , 'J'). n_base64(10, 'K'). n_base64(11, 'L').
|
chars_base64(Chars, Base64, [padding(false)]),
|
||||||
n_base64(12, 'M'). n_base64(13, 'N'). n_base64(14, 'O'). n_base64(15, 'P').
|
maplist(char_code, Chars, Bytes)
|
||||||
n_base64(16, 'Q'). n_base64(17, 'R'). n_base64(18, 'S'). n_base64(19, 'T').
|
; maplist(char_code, Chars, Bytes),
|
||||||
n_base64(20, 'U'). n_base64(21, 'V'). n_base64(22, 'W'). n_base64(23, 'X').
|
chars_base64(Chars, Base64, [padding(false)])
|
||||||
n_base64(24, 'Y'). n_base64(25, 'Z'). n_base64(26, 'a'). n_base64(27, 'b').
|
|
||||||
n_base64(28, 'c'). n_base64(29, 'd'). n_base64(30, 'e'). n_base64(31, 'f').
|
|
||||||
n_base64(32, 'g'). n_base64(33, 'h'). n_base64(34, 'i'). n_base64(35, 'j').
|
|
||||||
n_base64(36, 'k'). n_base64(37, 'l'). n_base64(38, 'm'). n_base64(39, 'n').
|
|
||||||
n_base64(40, 'o'). n_base64(41, 'p'). n_base64(42, 'q'). n_base64(43, 'r').
|
|
||||||
n_base64(44, 's'). n_base64(45, 't'). n_base64(46, 'u'). n_base64(47, 'v').
|
|
||||||
n_base64(48, 'w'). n_base64(49, 'x'). n_base64(50, 'y'). n_base64(51, 'z').
|
|
||||||
n_base64(52, '0'). n_base64(53, '1'). n_base64(54, '2'). n_base64(55, '3').
|
|
||||||
n_base64(56, '4'). n_base64(57, '5'). n_base64(58, '6'). n_base64(59, '7').
|
|
||||||
n_base64(60, '8'). n_base64(61, '9'). n_base64(62, '+'). n_base64(63, '/').
|
|
||||||
|
|
||||||
bytes_base64(Ls, Bs) :-
|
|
||||||
( list(Bs), maplist(atom, Bs) ->
|
|
||||||
maplist(n_base64, Is, Bs),
|
|
||||||
phrase(bytes_base64_(Ls), Is),
|
|
||||||
Ls ins 0..255
|
|
||||||
; phrase(bytes_base64_(Ls), Is),
|
|
||||||
Is ins 0..63,
|
|
||||||
maplist(n_base64, Is, Bs)
|
|
||||||
).
|
).
|
||||||
|
|
||||||
list(Ls) :-
|
|
||||||
nonvar(Ls),
|
|
||||||
( Ls = [] -> true
|
|
||||||
; Ls = [_|Rest],
|
|
||||||
list(Rest)
|
|
||||||
).
|
|
||||||
|
|
||||||
bytes_base64_([]) --> [].
|
|
||||||
bytes_base64_([A]) --> [W,X],
|
|
||||||
{ A #= W*4 + X//16,
|
|
||||||
X #= 16*_ }.
|
|
||||||
bytes_base64_([A,B]) --> [W,X,Y],
|
|
||||||
{ A #= W*4 + X//16,
|
|
||||||
B #= (X mod 16)*16 + Y//4,
|
|
||||||
Y #= 4*_ }.
|
|
||||||
bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z],
|
|
||||||
{ A #= W*4 + X//16,
|
|
||||||
B #= (X mod 16)*16 + Y//4,
|
|
||||||
C #= (Y mod 4)*64 + Z },
|
|
||||||
bytes_base64_(Ls).
|
|
||||||
|
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
crypto_data_encrypt(+PlainText,
|
crypto_data_encrypt(+PlainText,
|
||||||
+Algorithm,
|
+Algorithm,
|
||||||
|
|||||||
@@ -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,81 @@ 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() {
|
||||||
|
if c as u32 > 255 {
|
||||||
|
|
||||||
|
let stub = MachineError::functor_stub(clause_name!("chars_base64"), 3);
|
||||||
|
|
||||||
|
let err = MachineError::type_error(
|
||||||
|
self.heap.h(),
|
||||||
|
ValidType::Byte,
|
||||||
|
Addr::Char(c),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(self.error_form(err, stub));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user