diff --git a/.gitignore b/.gitignore
index eb4a11b..9be5147 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
.Rproj.user
src/rust/vendor
+src/vendor
docs
diff --git a/DESCRIPTION b/DESCRIPTION
index fba95ed..c39129d 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,9 +1,11 @@
Package: b64
Title: Fast and Vectorized Base 64 Engine
-Version: 0.1.0
-Authors@R:
+Version: 0.1.0.9000
+Authors@R: c(
person("Josiah", "Parry", , "josiah.parry@gmail.com", role = c("aut", "cre"),
- comment = c(ORCID = "0000-0001-9910-865X"))
+ comment = c(ORCID = "0000-0001-9910-865X")),
+ person("Etienne", "Bacher", email = "etienne.bacher@protonmail.com", role = "ctb",
+ comment = c(ORCID = "0000-0002-9271-5075")))
Description: Provides a fast, lightweight, and vectorized base 64 engine
to encode and decode character and raw vectors as well as files stored
on disk. Common base 64 alphabets are supported out of the box
@@ -14,13 +16,10 @@ License: MIT + file LICENSE
Encoding: UTF-8
Language: en
Roxygen: list(markdown = TRUE)
-RoxygenNote: 7.2.3
+RoxygenNote: 7.3.0
Config/rextendr/version: 0.3.1.9000
SystemRequirements: Cargo (Rust's package manager), rustc
-Imports:
- blob,
- cli,
- rlang
Suggests:
+ blob,
testthat (>= 3.0.0)
Config/testthat/edition: 3
diff --git a/NAMESPACE b/NAMESPACE
index 822940c..652530e 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -15,5 +15,4 @@ export(engine)
export(new_alphabet)
export(new_config)
export(new_engine)
-importFrom(blob,blob)
useDynLib(b64, .registration = TRUE)
diff --git a/NEWS.md b/NEWS.md
index a69ec04..b94aabf 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,7 @@
+# b64 (development version)
+
+* All hard dependencies were removed (#2, @etiennebacher).
+
# b64 0.1.0
* Initial CRAN submission.
diff --git a/R/alphabet.R b/R/alphabet.R
index c46ff1a..756055b 100644
--- a/R/alphabet.R
+++ b/R/alphabet.R
@@ -30,9 +30,9 @@
#' new_alphabet("qwertyuiop[]asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890")
#' @returns an object of class `alphabet`
alphabet <- function(which = "standard") {
- rlang::arg_match(
+ match.arg(
which,
- values = c("standard", "bcrypt", "bin_hex", "crypt", "imap_mutf7", "url_safe")
+ choices = c("standard", "bcrypt", "bin_hex", "crypt", "imap_mutf7", "url_safe")
)
structure(alphabet_(which), class = "alphabet")
}
@@ -42,10 +42,9 @@ alphabet <- function(which = "standard") {
new_alphabet <- function(chars) {
n <- nchar(chars)
if (nchar(chars) != 64) {
- cli::cli_abort(
- c(
- "{.arg chars} must be 64 unique characters",
- "i" = "{n} characters provided"
+ stop(
+ paste(
+ "`chars` must contain 64 unique characters. Only", n, "characters were provided."
)
)
}
diff --git a/R/b64-package.R b/R/b64-package.R
index 5481d76..a65cf64 100644
--- a/R/b64-package.R
+++ b/R/b64-package.R
@@ -1,5 +1,4 @@
#' @keywords internal
-#' @importFrom blob blob
"_PACKAGE"
## usethis namespace: start
diff --git a/R/config.R b/R/config.R
index a3bcfaf..78ee9e2 100644
--- a/R/config.R
+++ b/R/config.R
@@ -27,9 +27,9 @@ new_config <- function(
decode_padding_mode = c("canonical", "indifferent", "none")
) {
- padding_mode <- rlang::arg_match0(
+ padding_mode <- match.arg(
decode_padding_mode,
- values = c("canonical", "indifferent", "none")
+ choices = c("canonical", "indifferent", "none")
)
res <- new_config_(
diff --git a/R/encode.R b/R/encode.R
index 1fb3aac..f7975c8 100644
--- a/R/encode.R
+++ b/R/encode.R
@@ -45,7 +45,7 @@ decode <- function(what, eng = engine()) {
#' @name encode
encode_file <- function(path, eng = engine()) {
if (!file.exists(path)) {
- cli::cli_abort("{.arg path} does not exist")
+ stop(paste0("`", path, "` does not exist"))
}
encode_file_(path, eng)
@@ -56,7 +56,7 @@ encode_file <- function(path, eng = engine()) {
#' @rdname encode
decode_file <- function(path, eng = engine()) {
if (!file.exists(path)) {
- cli::cli_abort("{.arg path} does not exist")
+ stop(paste0("`", path, "` does not exist"))
}
decode_file_(path, eng)
diff --git a/R/engine.R b/R/engine.R
index 64a0987..26b0ed2 100644
--- a/R/engine.R
+++ b/R/engine.R
@@ -28,7 +28,7 @@
#' new_engine(alphabet("bcrypt"), new_config())
engine <- function(which = "standard") {
provided <- c("standard", "standard_no_pad", "url_safe", "url_safe_no_pad")
- rlang::arg_match0(which, provided)
+ match.arg(which, choices = provided)
structure(engine_(which), class = "engine")
}
@@ -36,18 +36,18 @@ engine <- function(which = "standard") {
#' @rdname engine
new_engine <- function(.alphabet = alphabet(), .config = new_config()) {
- if (!rlang::inherits_only(.alphabet, "alphabet")) {
- cli::cli_abort(
- c(
- "{.arg .alphabet} is not an object of class {.cls alphabet}",
- "*" = "use {.fn alphabet} for a standard base64 alphabet"
+ if (!inherits(.alphabet, "alphabet")) {
+ stop(
+ paste(
+ "`.alphabet` is not an object of class 'alphabet'.\n" ,
+ "Use `alphabet()` for a standard base64 alphabet."
)
)
- } else if (!rlang::inherits_only(.config, "engine_config")) {
- cli::cli_abort(
- c(
- "{.arg config} is not a {.cls engine_config} object",
- "*" = "create one with {.fn new_config}"
+ } else if (!inherits(.config, "engine_config")) {
+ stop(
+ paste(
+ "`.config` is not an object of class 'engine_config'.\n" ,
+ "Create one with `new_config()`."
)
)
}
diff --git a/R/extendr-wrappers.R b/R/extendr-wrappers.R
index 7c5048a..a03ed18 100644
--- a/R/extendr-wrappers.R
+++ b/R/extendr-wrappers.R
@@ -39,30 +39,28 @@ new_config_ <- function(encode_padding, decode_padding_trailing_bits, decode_pad
print_config_ <- function(config) .Call(wrap__print_config_, config)
-#' Utility Functions
-#'
+#' Utility Functions
+#'
#' Functions to perform common tasks when working with base64 encoded strings.
-#'
+#'
#' @details
-#'
-#' `b64_chunk()` splits a character vector of base64 encoded strings into chunks of a
+#'
+#' `b64_chunk()` splits a character vector of base64 encoded strings into chunks of a
#' specified width.
-#'
+#'
#' `b64_wrap()` wraps a character vector of base64 encoded strings with a newline character.
-#'
+#'
#' @returns
-#'
+#'
#' - `b64_chunk()` returns a list of character vectors.
#' - `b64_wrap()` returns a scalar character vector.
-#'
+#'
#' @examples
#' encoded <- encode("Hello, world!")
#' chunked <- b64_chunk(encoded, 4)
#' chunked
-#'
+#'
#' b64_wrap(chunked, "\n")
-#'
-#'
#' @param width a numeric scalar defining the width of the chunks. Must be divisible by 4.
#' @param encoded a character vector of base64 encoded strings.
#' @export
diff --git a/README.Rmd b/README.Rmd
index 12b9fc4..e16e1cc 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -40,9 +40,10 @@ hello <- encode("Hello, from extendr")
hello
```
-Decode using `decode()`
+Decode using `decode()`. Note that the returned object will always have the `"blob"` class. To achieve 0 dependencies, the `blob` package is only listed as a suggested dependency but if you attach it, its print method will be used.
```{r}
+library(blob)
decoded <- decode(hello)
decoded
```
@@ -66,7 +67,7 @@ encoded <- encode(lorem)
encoded
```
-We can decode all of these using `decode()` as well. This will always return a `blob` object.
+We can decode all of these using `decode()` as well.
```{r}
decode(encoded)
diff --git a/README.md b/README.md
index bfa3db3..16d00f3 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-# b64
+# b64
@@ -31,9 +31,13 @@ hello
#> [1] "SGVsbG8sIGZyb20gZXh0ZW5kcg=="
```
-Decode using `decode()`
+Decode using `decode()`. Note that the returned object will always have
+the `"blob"` class. To achieve 0 dependencies, the `blob` package is
+only listed as a suggested dependency but if you attach it, its print
+method will be used.
``` r
+library(blob)
decoded <- decode(hello)
decoded
#>
@@ -54,28 +58,27 @@ Both `encode()` and `decode()` are vectorized.
``` r
lorem <- unlist(lorem::ipsum(5, 1, 5))
lorem
-#> [1] "Sit ligula senectus litora viverra consequat."
-#> [2] "Consectetur vulputate vivamus sapien a ridiculus porta."
-#> [3] "Ipsum orci cras posuere lacus."
-#> [4] "Lorem nostra hendrerit nascetur vel duis consequat."
-#> [5] "Adipiscing dui blandit vestibulum bibendum?"
+#> [1] "Elit porttitor litora phasellus primis."
+#> [2] "Sit vel natoque eu quisque."
+#> [3] "Sit accumsan elementum pharetra aliquet parturient ullamcorper."
+#> [4] "Consectetur iaculis nunc elementum."
+#> [5] "Dolor donec iaculis sem."
encoded <- encode(lorem)
encoded
-#> [1] "U2l0IGxpZ3VsYSBzZW5lY3R1cyBsaXRvcmEgdml2ZXJyYSBjb25zZXF1YXQu"
-#> [2] "Q29uc2VjdGV0dXIgdnVscHV0YXRlIHZpdmFtdXMgc2FwaWVuIGEgcmlkaWN1bHVzIHBvcnRhLg=="
-#> [3] "SXBzdW0gb3JjaSBjcmFzIHBvc3VlcmUgbGFjdXMu"
-#> [4] "TG9yZW0gbm9zdHJhIGhlbmRyZXJpdCBuYXNjZXR1ciB2ZWwgZHVpcyBjb25zZXF1YXQu"
-#> [5] "QWRpcGlzY2luZyBkdWkgYmxhbmRpdCB2ZXN0aWJ1bHVtIGJpYmVuZHVtPw=="
+#> [1] "RWxpdCBwb3J0dGl0b3IgbGl0b3JhIHBoYXNlbGx1cyBwcmltaXMu"
+#> [2] "U2l0IHZlbCBuYXRvcXVlIGV1IHF1aXNxdWUu"
+#> [3] "U2l0IGFjY3Vtc2FuIGVsZW1lbnR1bSBwaGFyZXRyYSBhbGlxdWV0IHBhcnR1cmllbnQgdWxsYW1jb3JwZXIu"
+#> [4] "Q29uc2VjdGV0dXIgaWFjdWxpcyBudW5jIGVsZW1lbnR1bS4="
+#> [5] "RG9sb3IgZG9uZWMgaWFjdWxpcyBzZW0u"
```
-We can decode all of these using `decode()` as well. This will always
-return a `blob` object.
+We can decode all of these using `decode()` as well.
``` r
decode(encoded)
#>
-#> [1] blob[45 B] blob[55 B] blob[30 B] blob[51 B] blob[43 B]
+#> [1] blob[39 B] blob[27 B] blob[63 B] blob[35 B] blob[24 B]
```
## Encoding and decoding files
@@ -99,8 +102,8 @@ bench::mark(
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#>
-#> 1 b64 39.8ms 41.3ms 24.0 24MB 0
-#> 2 base64enc 112.1ms 115.2ms 8.56 66.5MB 17.1
+#> 1 b64 67.5ms 74.2ms 13.7 24.1MB 1.96
+#> 2 base64enc 177.9ms 183.3ms 5.50 66.9MB 11.0
```
While the encoding is very impressive, better yet is the decoding
@@ -122,8 +125,8 @@ bench::mark(
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#>
-#> 1 b64 16.1ms 16.8ms 56.1 18MB 9.34
-#> 2 base64enc 209.1ms 210.7ms 4.75 18MB 0
+#> 1 b64 43.4ms 51.7ms 19.7 18.1MB 5.63
+#> 2 base64enc 356.7ms 373.3ms 2.68 18.1MB 0
```
## Alternative engines
diff --git a/man/b64-package.Rd b/man/b64-package.Rd
index 18ddd4e..583c06c 100644
--- a/man/b64-package.Rd
+++ b/man/b64-package.Rd
@@ -1,15 +1,21 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/b64-package.R
+% Please edit documentation in R/b64-package.R, R/extendr-wrappers.R
\docType{package}
\name{b64-package}
-\alias{b64}
\alias{b64-package}
\title{b64: Fast and Vectorized Base 64 Engine}
\description{
+Provides a fast, lightweight, and vectorized base 64 engine to encode and decode character and raw vectors as well as files stored on disk. Common base 64 alphabets are supported out of the box including the standard, URL-safe, bcrypt, crypt, 'BinHex', and IMAP-modified UTF-7 alphabets. Custom engines can be created to support unique base 64 encoding and decoding needs.
+
Provides a fast, lightweight, and vectorized base 64 engine to encode and decode character and raw vectors as well as files stored on disk. Common base 64 alphabets are supported out of the box including the standard, URL-safe, bcrypt, crypt, 'BinHex', and IMAP-modified UTF-7 alphabets. Custom engines can be created to support unique base 64 encoding and decoding needs.
}
\author{
\strong{Maintainer}: Josiah Parry \email{josiah.parry@gmail.com} (\href{https://orcid.org/0000-0001-9910-865X}{ORCID})
+Other contributors:
+\itemize{
+ \item Etienne Bacher \email{etienne.bacher@protonmail.com} (\href{https://orcid.org/0000-0002-9271-5075}{ORCID}) [contributor]
+}
+
}
\keyword{internal}
diff --git a/man/utils.Rd b/man/utils.Rd
index a13cd5e..8d06b05 100644
--- a/man/utils.Rd
+++ b/man/utils.Rd
@@ -39,6 +39,4 @@ chunked <- b64_chunk(encoded, 4)
chunked
b64_wrap(chunked, "\n")
-
-
}
diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs
index 22e0257..c95ed35 100644
--- a/src/rust/src/lib.rs
+++ b/src/rust/src/lib.rs
@@ -33,22 +33,17 @@ fn encode_vectorized_(what: Either, engine: Robj) -> Strings {
}
})
.collect::(),
- Either::Right(r) => {
- if !r.inherits("blob") {
- throw_r_error("Expected a character vector or an object of class `blob`")
- }
-
- r.into_iter()
- .map(|(_, b)| {
- if b.is_null() {
- Rstr::na()
- } else {
- let raw: Raw = b.try_into().unwrap();
- Rstr::from(eng.encode(raw.as_slice()))
- }
- })
- .collect::()
- }
+ Either::Right(r) => r
+ .into_iter()
+ .map(|(_, b)| {
+ if b.is_null() {
+ Rstr::na()
+ } else {
+ let raw: Raw = b.try_into().unwrap();
+ Rstr::from(eng.encode(raw.as_slice()))
+ }
+ })
+ .collect::(),
}
}
@@ -63,28 +58,27 @@ fn encode_file_(path: &str, engine: Robj) -> String {
encoder.into_inner()
}
-
-/// Utility Functions
-///
+/// Utility Functions
+///
/// Functions to perform common tasks when working with base64 encoded strings.
-///
+///
/// @details
-///
-/// `b64_chunk()` splits a character vector of base64 encoded strings into chunks of a
+///
+/// `b64_chunk()` splits a character vector of base64 encoded strings into chunks of a
/// specified width.
-///
+///
/// `b64_wrap()` wraps a character vector of base64 encoded strings with a newline character.
-///
+///
/// @returns
-///
+///
/// - `b64_chunk()` returns a list of character vectors.
/// - `b64_wrap()` returns a scalar character vector.
-///
+///
/// @examples
/// encoded <- encode("Hello, world!")
/// chunked <- b64_chunk(encoded, 4)
/// chunked
-///
+///
/// b64_wrap(chunked, "\n")
/// @param width a numeric scalar defining the width of the chunks. Must be divisible by 4.
/// @param encoded a character vector of base64 encoded strings.
@@ -92,7 +86,6 @@ fn encode_file_(path: &str, engine: Robj) -> String {
/// @rdname utils
#[extendr(use_try_from = true)]
fn b64_chunk(encoded: Strings, width: Either) -> List {
-
let width = match width {
Left(l) => l,
Right(r) => r as i32,
@@ -117,7 +110,6 @@ fn b64_chunk(encoded: Strings, width: Either) -> List {
.collect::()
}
-
/// @param chunks a character vector of base64 encoded strings.
/// @param newline a character scalar defining the newline character.
/// @export
@@ -136,9 +128,7 @@ fn b64_wrap(chunks: Either, newline: &str) -> Strings {
}
})
.collect::(),
- Right(r) => {
- b64_wrap_(r, newline).into()
- }
+ Right(r) => b64_wrap_(r, newline).into(),
}
}
@@ -156,12 +146,10 @@ fn decode_(input: Either, engine: Robj) -> Robj {
Ok(d) => d,
Err(e) => throw_r_error(e.to_string().as_str()),
}
- },
- Either::Right(r) => {
- match eng.decode(r.as_slice()) {
- Ok(d) => d,
- Err(e) => throw_r_error(e.to_string().as_str()),
- }
+ }
+ Either::Right(r) => match eng.decode(r.as_slice()) {
+ Ok(d) => d,
+ Err(e) => throw_r_error(e.to_string().as_str()),
},
};
@@ -194,27 +182,24 @@ fn decode_vectorized_(what: Either, engine: Robj) -> Robj {
.collect::()
.set_class(&["blob", "vctrs_list_of", "vctrs_vctr", "list"])
.unwrap(),
- Either::Right(r) => {
- if !r.inherits("blob") {
- throw_r_error("Expected a character vector or an object of class `blob`")
- }
- r.into_iter()
- .map(|(_, b)| {
- if b.is_null() {
- ().into_robj()
- } else {
- let raw: Raw = b.try_into().unwrap();
- let decoded = eng.decode(raw.as_slice());
+ Either::Right(r) => r
+ .into_iter()
+ .map(|(_, b)| {
+ let raw = Raw::try_from(b);
+ match raw {
+ Ok(r) => {
+ let decoded = eng.decode(r.as_slice());
match decoded {
Ok(d) => Raw::from_bytes(&d).into_robj(),
Err(_) => ().into_robj(),
}
}
- })
- .collect::()
- .set_class(&["blob", "vctrs_list_of", "vctrs_vctr", "list"])
- .unwrap()
- }
+ Err(_) => ().into_robj(),
+ }
+ })
+ .collect::()
+ .set_class(&["blob", "vctrs_list_of", "vctrs_vctr", "list"])
+ .unwrap(),
}
}
@@ -240,7 +225,7 @@ fn alphabet_(which: &str) -> ExternalPtr {
"imap_mutf7" => ExternalPtr::new(alphabet::IMAP_MUTF7),
"standard" => ExternalPtr::new(alphabet::STANDARD),
"url_safe" => ExternalPtr::new(alphabet::URL_SAFE),
- _ => extendr_api::throw_r_error(&format!("Unknown alphabet: {}", which)),
+ _ => extendr_api::throw_r_error(format!("Unknown alphabet: {}", which)),
}
}
@@ -251,9 +236,8 @@ fn new_alphabet_(chars: &str) -> ExternalPtr {
match res {
Ok(r) => ExternalPtr::new(r),
- Err(e) => extendr_api::throw_r_error(&format!("Error creating alphabet: {}", e)),
+ Err(e) => extendr_api::throw_r_error(format!("Error creating alphabet: {}", e)),
}
-
}
// get alphabet as a string for printing
@@ -277,7 +261,7 @@ fn new_config_(
"indifferent" => DecodePaddingMode::Indifferent,
"canonical" => DecodePaddingMode::RequireCanonical,
"none" => DecodePaddingMode::RequireNone,
- _ => extendr_api::throw_r_error(&format!("Unknown padding mode: {}", decode_padding_mode)),
+ _ => extendr_api::throw_r_error(format!("Unknown padding mode: {}", decode_padding_mode)),
};
let config = GeneralPurposeConfig::new()
@@ -301,7 +285,7 @@ fn engine_(which: &str) -> ExternalPtr {
"standard_no_pad" => ExternalPtr::new(general_purpose::STANDARD_NO_PAD),
"url_safe" => ExternalPtr::new(general_purpose::URL_SAFE),
"url_safe_no_pad" => ExternalPtr::new(general_purpose::URL_SAFE_NO_PAD),
- _ => extendr_api::throw_r_error(&format!("Unknown engine: {}", which)),
+ _ => extendr_api::throw_r_error(format!("Unknown engine: {}", which)),
}
}