]> Repositorios git - scryer-prolog.git/commitdiff
ADDED: chars_base64/3 for efficient bidirectional Base64 conversion.
authorMarkus Triska <[email protected]>
Wed, 22 Jul 2020 18:17:46 +0000 (20:17 +0200)
committerMarkus Triska <[email protected]>
Wed, 22 Jul 2020 18:35:48 +0000 (20:35 +0200)
Cargo.toml
README.md
src/clause_types.rs
src/lib/charsio.pl
src/machine/system_calls.rs

index bc13c81f179acaf4b8f409e98a8a6a8c07aa40ed..6ac8ebe48391f6bf6ba716e7f606a5927d0d58a2 100644 (file)
@@ -46,3 +46,4 @@ native-tls = "0.2.4"
 chrono = "0.4.11"
 select = "0.4.3"
 roxmltree = "0.11.0"
+base64 = "0.12.3"
index d31d1739b28f88426b63362ae299c8c1ad549ba8..0cb1a268ca22d07743fdbf4990e1539ac57890db 100644 (file)
--- a/README.md
+++ b/README.md
@@ -387,9 +387,10 @@ The modules that ship with Scryer&nbsp;Prolog are also called
   file, reading lazily only as much as is needed. Due to the compact
   internal string representation, also extremely large files can be
   efficiently processed with Scryer&nbsp;Prolog in this way.
-* [`charsio`](src/lib/charsio.pl) Various predicates that are
-  useful for parsing and reasoning about characters, notably
-  `char_type/2` to classify characters according to their type.
+* [`charsio`](src/lib/charsio.pl) Various predicates that are useful
+  for parsing and reasoning about characters, notably `char_type/2` to
+  classify characters according to their type, and conversion
+  predicates for different encodings of strings.
 * [`error`](src/lib/error.pl)
   `must_be/2` and `can_be/2` complement the type checks provided by
   [`library(si)`](src/lib/si.pl), and are especially useful for
index ab55563078e6c69ff6595a6ef65dbe2b3a19c173..ee6ad24ce718c103665ea9e17dee4020c0a34de4 100644 (file)
@@ -314,6 +314,7 @@ pub enum SystemClauseType {
     GetEnv,
     SetEnv,
     UnsetEnv,
+    CharsBase64,
 }
 
 impl SystemClauseType {
@@ -526,6 +527,7 @@ impl SystemClauseType {
             &SystemClauseType::GetEnv => clause_name!("$getenv"),
             &SystemClauseType::SetEnv => clause_name!("$setenv"),
             &SystemClauseType::UnsetEnv => clause_name!("$unsetenv"),
+            &SystemClauseType::CharsBase64 => clause_name!("$chars_base64"),
         }
     }
 
@@ -718,6 +720,7 @@ impl SystemClauseType {
             ("$getenv", 2) => Some(SystemClauseType::GetEnv),
             ("$setenv", 2) => Some(SystemClauseType::SetEnv),
             ("$unsetenv", 1) => Some(SystemClauseType::UnsetEnv),
+            ("$chars_base64", 4) => Some(SystemClauseType::CharsBase64),
             _ => None,
         }
     }
index 4e82f23be8eaa50d026a2446176b3574ad1f635a..dd41a597d9dcc5dddb8128755c5274db84005a9c 100644 (file)
@@ -3,7 +3,8 @@
                     get_single_char/1,
                     read_line_to_chars/3,
                     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(iso_ext)).
@@ -194,3 +195,42 @@ read_line_to_chars(Stream, Cs0, 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)
+        ).
index 31024167b8dc921e8e313959e633c878067dd7d2..7971583f653081fddf04210b6345beaabfba584b 100644 (file)
@@ -55,6 +55,7 @@ use crate::native_tls::TlsConnector;
 
 extern crate select;
 use roxmltree;
+use base64;
 
 pub fn get_key() -> KeyEvent {
     let key;
@@ -5745,6 +5746,68 @@ impl MachineState {
                 let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
                 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)