diff --git a/NAMESPACE b/NAMESPACE index 60ccb03..822940c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,6 +5,8 @@ S3method(print,alphabet) S3method(print,engine) S3method(print,engine_config) export(alphabet) +export(b64_chunk) +export(b64_wrap) export(decode) export(decode_file) export(encode) diff --git a/R/extendr-wrappers.R b/R/extendr-wrappers.R index 76ad2f4..7c5048a 100644 --- a/R/extendr-wrappers.R +++ b/R/extendr-wrappers.R @@ -39,9 +39,41 @@ new_config_ <- function(encode_padding, decode_padding_trailing_bits, decode_pad 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) +#' 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 +#' 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 +#' @rdname utils +b64_chunk <- function(encoded, width) .Call(wrap__b64_chunk, encoded, width) + +#' @param chunks a character vector of base64 encoded strings. +#' @param newline a character scalar defining the newline character. +#' @export +#' @rdname utils +b64_wrap <- function(chunks, newline) .Call(wrap__b64_wrap, chunks, newline) # nolint end diff --git a/man/b64-package.Rd b/man/b64-package.Rd index cd798c0..18ddd4e 100644 --- a/man/b64-package.Rd +++ b/man/b64-package.Rd @@ -4,9 +4,9 @@ \name{b64-package} \alias{b64} \alias{b64-package} -\title{b64: What the Package Does (One Line, Title Case)} +\title{b64: Fast and Vectorized Base 64 Engine} \description{ -What the package does (one paragraph). +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}) diff --git a/man/utils.Rd b/man/utils.Rd new file mode 100644 index 0000000..a13cd5e --- /dev/null +++ b/man/utils.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/extendr-wrappers.R +\name{b64_chunk} +\alias{b64_chunk} +\alias{b64_wrap} +\title{Utility Functions} +\usage{ +b64_chunk(encoded, width) + +b64_wrap(chunks, newline) +} +\arguments{ +\item{encoded}{a character vector of base64 encoded strings.} + +\item{width}{a numeric scalar defining the width of the chunks. Must be divisible by 4.} + +\item{chunks}{a character vector of base64 encoded strings.} + +\item{newline}{a character scalar defining the newline character.} +} +\value{ +\itemize{ +\item \code{b64_chunk()} returns a list of character vectors. +\item \code{b64_wrap()} returns a scalar character vector. +} +} +\description{ +Functions to perform common tasks when working with base64 encoded strings. +} +\details{ +\code{b64_chunk()} splits a character vector of base64 encoded strings into chunks of a +specified width. + +\code{b64_wrap()} wraps a character vector of base64 encoded strings with a newline character. +} +\examples{ +encoded <- encode("Hello, world!") +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 0f5f845..22e0257 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -63,22 +63,86 @@ fn encode_file_(path: &str, engine: Robj) -> String { encoder.into_inner() } -#[extendr] -fn chunk_b64(encoded: String, size: i32) -> Strings { - if size % 4 != 0 { + +/// 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 +/// 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 +/// @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, + }; + + if width % 4 != 0 { extendr_api::throw_r_error("Chunk size must be a multiple of 4."); } - encoded - .chars() - .chunks(size as usize) .into_iter() - .map(|chunk| chunk.collect::()) - .collect::() + .map(|s| { + if s.is_na() { + Strings::new(0) + } else { + s.chars() + .chunks(width as usize) + .into_iter() + .map(|chunk| chunk.collect::()) + .collect::() + } + }) + .collect::() } -#[extendr] -fn line_wrap(chunks: Strings, newline: &str) -> String { + +/// @param chunks a character vector of base64 encoded strings. +/// @param newline a character scalar defining the newline character. +/// @export +/// @rdname utils +#[extendr(use_try_from = true)] +fn b64_wrap(chunks: Either, newline: &str) -> Strings { + match chunks { + Left(l) => l + .into_iter() + .map(|(_, s)| { + if s.is_na() { + Rstr::na() + } else { + let s = Strings::try_from(s).unwrap(); + Rstr::from(b64_wrap_(s, newline)) + } + }) + .collect::(), + Right(r) => { + b64_wrap_(r, newline).into() + } + } +} + +fn b64_wrap_(chunks: Strings, newline: &str) -> String { chunks.into_iter().join(newline) } @@ -287,6 +351,6 @@ extendr_module! { fn print_config_; // helpers - fn chunk_b64; - fn line_wrap; + fn b64_chunk; + fn b64_wrap; }