Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
JosiahParry committed Jan 12, 2024
1 parent 925ef73 commit ffd268e
Show file tree
Hide file tree
Showing 16 changed files with 397 additions and 103 deletions.
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Config/rextendr/version: 0.3.1.9000
SystemRequirements: Cargo (rustc package manager)
Imports:
blob,
cli,
rlang
6 changes: 4 additions & 2 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Generated by roxygen2: do not edit by hand

S3method(format,alphabet)
S3method(print,alphabet)
S3method(print,b64_config)
S3method(print,engine)
export(alphabet)
export(decode)
export(decode_file)
export(encode)
export(encode_file)
export(engine)
export(new_config)
export(new_engine)
importFrom(blob,blob)
useDynLib(b64, .registration = TRUE)
58 changes: 44 additions & 14 deletions R/alphabet.R
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
#' @export
format.alphabet <- function(x, ...) {
cat("<alphabet>\n")
cat(get_alphabet_(x))
}

#' @export
print.alphabet <- function(x, ...) {
format(x, ...)
}


#' Standard base64 alphabets
#'
#' Create an alphabet from a set of standard base64 alphabets, or use your own.
#'
#' @param which default `"standard"`. Which base64 alphabet to use.
#' See details for other values.
#' @param chars a character scalar contains 64 unique characters.
#'
#' @details
#'
#' - `"bcrypt"`: the bcrypt alphabet
#' - `"bcrypt"`: bcrypt alphabet
#' - `"bin_hex"`: alphabet used in BinHex 4.0 files
#' - `"crypt"`: crypt(3) alphabet (with . and / as the first two characters)
#' - `"imap_mutf7"`: alphabet used in IMAP-modified UTF-7 (with + and ,)
#' - `"standard"`: standard alphabet (with + and /) specified in RFC 4648
#' - `"url_safe"`: URL-safe alphabet (with - and _) specified in RFC 4648
#'
#' See [base64 crate](https://docs.rs/base64/latest/base64/alphabet/index.html#constants)
#' for more details.
#' from where these definitions come.
#'
#' @export
#' @examples
#' alphabet("standard")
#' alphabet("bcrypt")
#' alphabet("bin_hex")
#' alphabet("crypt")
#' alphabet("imap_mutf7")
#' alphabet("url_safe")
#'
#' new_alphabet("qwertyuiop[]asdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890")
#' @returns an object of class `alphabet`
alphabet <- function(which = "standard") {
rlang::arg_match(
which,
Expand All @@ -29,3 +37,25 @@ alphabet <- function(which = "standard") {
structure(alphabet_(which), class = "alphabet")
}


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"
)
)
}

structure(new_alphabet_(chars), class = "alphabet")
}


#' @export
print.alphabet <- function(x, ...) {
cat("<alphabet>\n")
cat(get_alphabet_(x))
invisible(x)
}
54 changes: 54 additions & 0 deletions R/config.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#' Create a custom encoding engine
#'
#' @details
#'
#' See [base64 crate](https://docs.rs/base64/latest/base64/engine/general_purpose/struct.GeneralPurposeConfig.html#method.with_encode_padding) for more details.
#'
#' ## Decode Padding Modes
#'
#' There are three modes that can be used for `decode_padding_mode` argument.
#'
#' - `"canonical"`: padding must consist of 0, 1, or 2 `=` characters
#' - `"none"`: there must be no padding characters present
#' - `"indifferent"`: canonical padding is used, but omitted padding
#' characters are also permitted
#'
#' @param encode_padding default `TRUE` add 1-2 trailing `=` to pad results
#' @param decode_padding_trailing_bits default `FALSE`. "If invalid trailing bits are present and this is true, those bits will be silently ignored." (See details for reference).
#' @param decode_padding_mode default `"canonical"`. Other values are `"indifferent"` and `"none"`. See details for more.
#' @export
#' @return an object of class `b64_config`
new_config <- function(
encode_padding = TRUE,
decode_padding_trailing_bits = FALSE,
decode_padding_mode = c("canonical", "indifferent", "none")
) {

padding_mode <- rlang::arg_match0(
decode_padding_mode,
values = c("canonical", "indifferent", "none")
)

res <- new_config_(
encode_padding,
decode_padding_trailing_bits,
padding_mode
)

structure(res, class = "b64_config")
}

# shoddy print method for the time being

#' @export
print.b64_config <- function(x, ...) {
y <- print_config_(x)

z <- trimws(strsplit(y, "\n")[[1]][2:4])

cat("<b64_config>\n")
cat(gsub(",", "", z), sep = "\n")
invisible(x)
}


20 changes: 0 additions & 20 deletions R/decode.R

This file was deleted.

38 changes: 32 additions & 6 deletions R/encode.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#' Encode as base64
#' Encode and decode using base64
#'
#' Perform base 64 encoding.
#' @param what a character, raw, or blob vector
#' @param eng a base64 engine. See [engine()] for details.
#' @param path a path to a base64 encoded file.
#'
#' @param what a scalar character or a raw vector.
#' @param engine a base64 engine. See [engine()] for details.
#' @return
#' `encode()` is vectorized and will return a character vector of the same
#' lenght as `what`.
#' Both `encode()` and `decode()` are vectorized. They will return a character
#' and blob vector the same length as `what`, respectively.
#' @export
#' @name encode
encode <- function(what, eng = engine()) {
Expand All @@ -18,8 +18,34 @@ encode <- function(what, eng = engine()) {
}
}

#' @rdname encode
decode <- function(what, eng = engine()) {
n <- length(what)
if (inherits(what, "raw") || (n == 1 & inherits(what, "character"))) {
decode_(what, eng)
} else {
decode_vectorized_(what, eng)
}
}


#' @export
#' @name encode
encode_file <- function(path, eng = engine()) {
if (!file.exists(path)) {
cli::cli_abort("{.arg path} does not exist")
}

encode_file_(path, eng)
}


#' @export
#' @rdname encode
decode_file <- function(path, eng = engine()) {
if (!file.exists(path)) {
cli::cli_abort("{.arg path} does not exist")
}

decode_file_(path, eng)
}
34 changes: 33 additions & 1 deletion R/engine.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#' Choose an encoding engine
#' Create an encoding engine
#'
#' @param which default `"standard"`. The base64 encoding engine to be used.
#' See details for more.
#' @param .alphabet an object of class `alphabet` as created with [`alphabet()`] or [`new_alphabet()`]
#' @details
#'
#' ## Engines
Expand All @@ -20,8 +21,39 @@
#' @export
#' @examples
#' engine()
#' 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)
structure(engine_(which), class = "engine")
}

#' @export
#' @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"
)
)
} else if (!rlang::inherits_only(.config, "b64_config")) {
cli::cli_abort(
c(
"{.arg config} is not a {.cls b64_config} object",
"*" = "create one with {.fn new_config}"
)
)
}

res <- new_engine_(.alphabet, .config)
structure(res, class = "engine")
}

#' @export
print.engine <- function(x, ...) {
cat("<engine>")
invisible(x)
}
6 changes: 4 additions & 2 deletions R/extendr-wrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,20 @@ decode_vectorized_ <- function(what, engine) .Call(wrap__decode_vectorized_, wha

alphabet_ <- function(which) .Call(wrap__alphabet_, which)

new_alphabet <- function(chars) .Call(wrap__new_alphabet, chars)
new_alphabet_ <- function(chars) .Call(wrap__new_alphabet_, chars)

get_alphabet_ <- function(alphabet) .Call(wrap__get_alphabet_, alphabet)

new_engine_ <- function(alphabet, config) .Call(wrap__new_engine_, alphabet, config)

engine_ <- function(which) .Call(wrap__engine_, which)

print_engine_ <- function(`_engine`) invisible(.Call(wrap__print_engine_, `_engine`))
print_engine_ <- function(engine) .Call(wrap__print_engine_, engine)

new_config_ <- function(encode_padding, decode_padding_trailing_bits, decode_padding_mode) .Call(wrap__new_config_, encode_padding, decode_padding_trailing_bits, decode_padding_mode)

print_config_ <- function(config) .Call(wrap__print_config_, config)

chunk_b64 <- function(encoded, size) .Call(wrap__chunk_b64, encoded, size)

line_wrap <- function(chunks, newline) .Call(wrap__line_wrap, chunks, newline)
Expand Down
44 changes: 42 additions & 2 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,50 @@ bench::mark(

Out of the box, `b64` provides a number of pre-configured engines that can be used. The function `engine()` allows you to choose one of these different engines For example, `engine("url_safe")` provides a standard engine that uses a url-safe alphabet with padding.


```{r}
url_engine <- engine("url_safe")
url_safe_encoded <- encode("\xfa\xec U", url_engine)
url_safe_encoded
```

If we try to decode this using the standard engine, we will encounter an error.

```{r error=TRUE}
decode(url_safe_encoded)
```

We can use our new engine to decode it.

```{r}
unsafe_chars <- charToRaw("-uwgVQA=")
decode(url_safe_encoded, url_engine)
```

### Custom Engines

We can create custom engines with `new_engine()`. This allows us to provide our on alphabet and configuration.

We can use one of the many predefined alphabets or create one our selves with `new_alphabet()`. We can also specify our engine config using `new_config()` which lets us choose whether or not to pad and how to handle decoding.

decode(unsafe_chars, engine("url_safe"))
```{r}
my_eng <- new_engine(
alphabet("crypt"),
new_config(TRUE, TRUE, "none")
)
```

This engine can be used to encode or decode text.

```{r}
txt <- "lorem ipsum sit dolor amet"
encode(txt, my_eng)
```

Compare this to the standard encoder:

```{r}
encode(txt)
```


Expand Down
Loading

0 comments on commit ffd268e

Please sign in to comment.