Skip to content

Commit

Permalink
add 'fast_numerics' option for numeric vectors without NA/NaN/Inf values
Browse files Browse the repository at this point in the history
  • Loading branch information
coolbutuseless committed Jan 25, 2024
1 parent f14b8f5 commit fa191d8
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 27 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: yyjsonr
Type: Package
Title: Fast JSON Parser and Generator
Version: 0.1.18
Version: 0.1.18.9000
Authors@R: c(
person("Mike", "Cheng", role = c("aut", "cre", 'cph'),
email = "[email protected]"),
Expand Down
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@


# yyjsonr 0.1.18.9000 2024-01-25

* New `fast_numerics` flag when writing.
* Default `FALSE`
* If `TRUE` the user is guaranteeing that there are no NA, NaN or Inf values
in the numeric and integer vectors, and thus a faster method for writing
these vectors to JSON can be used.
* Changed writing of `raw` vectors to always use the `fast_numerics` method,
as raw R vectors (by definition) will not have NA, NaN or Inf values.

# yyjsonr 0.1.18 2024-01-22

* Fixes for CRAN
Expand Down
11 changes: 11 additions & 0 deletions R/json-opts.R
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,15 @@ opts_read_json <- function(
#' @param num_specials Should special numeric values (i.e. NA, NaN, Inf) be
#' converted to a JSON \code{null} value or converted to a string
#' representation e.g. "NA"/"NaN" etc. Default: 'null'
#' @param fast_numerics Does the user guarantee that there are no NA, NaN or Inf
#' values in the numeric vectors? Default: FALSE. If \code{TRUE} then
#' numeric and integer vectors will be written to JSON using a faster method.
#' Note: if there are NA, NaN or Inf values, an error will be thrown.
#' Expert users are invited to also consider the
#' \code{YYJSON_WRITE_ALLOW_INF_AND_NAN} and
#' \code{YYJSON_WRITE_INF_AND_NAN_AS_NULL} options for \code{yyjson_write_flags}
#' and should consult the \code{yyjson} API documentation for
#' further details.
#' @param yyjson_write_flag integer vector corresponding to internal \code{yyjson}
#' options. See \code{yyjson_write_flag} in this package, and read
#' the yyjson API documentation for more information. This is considered
Expand All @@ -277,6 +286,7 @@ opts_write_json <- function(
name_repair = c('none', 'minimal'),
num_specials = c('null', 'string'),
str_specials = c('null', 'string'),
fast_numerics = FALSE,
yyjson_write_flag = 0L) {

structure(
Expand All @@ -289,6 +299,7 @@ opts_write_json <- function(
name_repair = match.arg(name_repair),
str_specials = match.arg(str_specials),
num_specials = match.arg(num_specials),
fast_numerics = isTRUE(fast_numerics),
yyjson_write_flag = as.integer(yyjson_write_flag)
),
class = "opts_write_json"
Expand Down
11 changes: 11 additions & 0 deletions man/opts_write_json.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 29 additions & 26 deletions src/R-yyjson-serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ serialize_options parse_serialize_options(SEXP serialize_opts_) {
.name_repair = NAME_REPAIR_NONE,
.num_specials = NUM_SPECIALS_AS_NULL,
.str_specials = STR_SPECIALS_AS_NULL,
.fast_numerics = FALSE,
.yyjson_write_flag = 0,
};

Expand Down Expand Up @@ -85,6 +86,8 @@ serialize_options parse_serialize_options(SEXP serialize_opts_) {
} else if (strcmp(opt_name, "num_specials") == 0) {
const char *val = CHAR(STRING_ELT(val_, 0));
opt.num_specials = strcmp(val, "string") == 0 ? NUM_SPECIALS_AS_STRING : NUM_SPECIALS_AS_NULL;
} else if (strcmp(opt_name, "fast_numerics") == 0) {
opt.fast_numerics = asLogical(val_);
} else {
warning("Unknown option ignored: '%s'\n", opt_name);
}
Expand Down Expand Up @@ -390,14 +393,9 @@ yyjson_mut_val *vector_factor_to_json_array(SEXP vec_, yyjson_mut_doc *doc, seri
//===========================================================================
yyjson_mut_val *vector_rawsxp_to_json_array(SEXP vec_, yyjson_mut_doc *doc, serialize_options *opt) {

yyjson_mut_val *arr = yyjson_mut_arr(doc);

unsigned char *ptr = RAW(vec_);
for (int i = 0; i < length(vec_); i++) {
yyjson_mut_arr_append(arr, yyjson_mut_uint(doc, *ptr++));
}

return arr;
// Raw vectors can't have NA, so can use the fast method
return yyjson_mut_arr_with_uint8(doc, RAW(vec_), (size_t)length(vec_));
}


Expand Down Expand Up @@ -455,16 +453,19 @@ yyjson_mut_val *vector_intsxp_to_json_array(SEXP vec_, yyjson_mut_doc *doc, seri
return vector_date_to_json_array(vec_, doc, opt);
} else if (inherits(vec_, "POSIXct")) {
return vector_posixct_to_json_array(vec_, doc, opt);
}

yyjson_mut_val *arr = yyjson_mut_arr(doc);

int32_t *ptr = INTEGER(vec_);
for (int i = 0; i < length(vec_); i++) {
yyjson_mut_arr_append(arr, scalar_integer_to_json_val(*ptr++, doc, opt));
} else if (opt->fast_numerics) {
return yyjson_mut_arr_with_sint32(doc, INTEGER(vec_), (size_t)length(vec_));
} else {

yyjson_mut_val *arr = yyjson_mut_arr(doc);

int32_t *ptr = INTEGER(vec_);
for (int i = 0; i < length(vec_); i++) {
yyjson_mut_arr_append(arr, scalar_integer_to_json_val(*ptr++, doc, opt));
}

return arr;
}

return arr;
}


Expand All @@ -480,17 +481,19 @@ yyjson_mut_val *vector_realsxp_to_json_array(SEXP vec_, yyjson_mut_doc *doc, ser
return vector_posixct_to_json_array(vec_, doc, opt);
} else if (inherits(vec_, "integer64")) {
return vector_integer64_to_json_array(vec_, doc, opt);
} else if (opt->fast_numerics) {
return yyjson_mut_arr_with_double(doc, REAL(vec_), (size_t)length(vec_));
} else {

yyjson_mut_val *arr = yyjson_mut_arr(doc);

double *ptr = REAL(vec_);
for (int i = 0; i < length(vec_); i++) {
yyjson_mut_arr_append(arr, scalar_double_to_json_val(*ptr++, doc, opt));
}

return arr;
}


yyjson_mut_val *arr = yyjson_mut_arr(doc);

double *ptr = REAL(vec_);
for (int i = 0; i < length(vec_); i++) {
yyjson_mut_arr_append(arr, scalar_double_to_json_val(*ptr++, doc, opt));
}

return arr;
}


Expand Down
1 change: 1 addition & 0 deletions src/R-yyjson-serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ typedef struct {
unsigned int str_specials;
unsigned int num_specials;
unsigned int yyjson_write_flag;
bool fast_numerics;
} serialize_options;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
12 changes: 12 additions & 0 deletions tests/testthat/test-fast-numerics.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

test_that("fast-numerics works", {
expect_equal(
write_json_str(mtcars, dataframe = 'columns', fast_numerics = FALSE),
write_json_str(mtcars, dataframe = 'columns', fast_numerics = TRUE)
)

expect_equal(
write_json_str(iris, dataframe = 'columns', fast_numerics = FALSE),
write_json_str(iris, dataframe = 'columns', fast_numerics = TRUE)
)
})

0 comments on commit fa191d8

Please sign in to comment.