Skip to content

Commit e0abdfc

Browse files
philippechataignonben-schwenMichaelChirico
authored
Fix fwrite length for gzip output (#6393)
* fwrite with correct file length * gzip length and crc are manually computed in each thread and then added/combined * gzip header is minimal * remove some old debug code * Escape with NOZLIB for compilation succeed without zlib * Move zlib check at start to avoid oufile deletion * Indent and add comments * Buffers unification * Restore schedule(dynamic) more efficient and progress * Use alloc_size to see allocation when verbose * Test if stream init succeded * Add cast to avoid warnings on Windows * More explicit timing messages * Free stream structs * Add option to control compression level for fwrite with gzip * Rework namings and default value * Rename gzipLevel to compressLevel * compressLevel param documentation * Put zlib initialization together * Refact buffSize, numBatchs and numBatches * Add missing NOZLIB * Increase outputs in last message when verbose * No real init for stream_thread when is_gzip false * Minor corrections * Uses %zu format for size_t * Last verbose msg was not printed when not is_gzip * minor operator ws change * Add test for compressLevel=1 * Add url link in compressLevel documentation * Add 2 lines in NEWS for fwrite fix and compressLevel * tidy-up, expand NEWS for compressLevel * Use match.arg() for arg validation * add a test for the other extreme compressLevel=9 * partial test fix * fix updated test errors * confirmed NEWS wording, fix typo * fix order * weak ordering * place in 1.17.0 NEWS * Add parenthesis to be more explicit * Add comment for DeflateInit2 * typo * Add parenthesis to be more explicit (2) Co-authored-by: Michael Chirico <[email protected]> * Try to emphasize that '-' is "command flag hyphen", not "negative" * Convert Toby'd comment to atime_test() * Remove INTERNAL_STOP * Increase coverage * add // # nocov for STOP, like previous version * add a test when naLen > width * remove test of buffMB done in fwrite.R * Try to fix nocov error * Another attempt to increase coverage * Add more nocov * More judicious #nocov, keep INTERNAL_STOP * eol='' coverage * buffMB<line width * Similar for buffMB vs. header width * 0-row table verbose output --------- Co-authored-by: Benjamin Schwendinger <[email protected]> Co-authored-by: Michael Chirico <[email protected]> Co-authored-by: Michael Chirico <[email protected]>
1 parent d032af7 commit e0abdfc

File tree

9 files changed

+423
-274
lines changed

9 files changed

+423
-274
lines changed

.ci/atime/tests.R

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,20 @@ test.list <- atime::atime_test_list(
231231
},
232232
expr = data.table:::melt(DT, measure.vars = measure.vars),
233233
Slow = "fd24a3105953f7785ea7414678ed8e04524e6955", # Parent of the merge commit (https://github.com/Rdatatable/data.table/commit/ed72e398df76a0fcfd134a4ad92356690e4210ea) of the PR (https://github.com/Rdatatable/data.table/pull/5054) that fixes the issue
234-
Fast = "ed72e398df76a0fcfd134a4ad92356690e4210ea"), # Merge commit of the PR (https://github.com/Rdatatable/data.table/pull/5054) that fixes the issue
234+
Fast = "ed72e398df76a0fcfd134a4ad92356690e4210ea"), # Merge commit of the PR (https://github.com/Rdatatable/data.table/pull/5054) that fixes the issue # Test case created directly using the atime code below (not adapted from any other benchmark), based on the issue/fix PR https://github.com/Rdatatable/data.table/pull/5054#issue-930603663 "melt should be more efficient when there are missing input columns."
235+
236+
# Test case created from @tdhock's comment https://github.com/Rdatatable/data.table/pull/6393#issuecomment-2327396833, in turn adapted from @philippechataignon's comment https://github.com/Rdatatable/data.table/pull/6393#issuecomment-2326714012
237+
"fwrite refactored in #6393" = atime::atime_test(
238+
setup = {
239+
set.seed(1)
240+
NC = 10L
241+
L <- data.table(i=1:N)
242+
L[, paste0("V", 1:NC) := replicate(NC, rnorm(N), simplify=FALSE)]
243+
out.csv <- tempfile()
244+
},
245+
expr = data.table::fwrite(L, out.csv, compress="gzip"),
246+
Before = "f339aa64c426a9cd7cf2fcb13d91fc4ed353cd31", # Parent of the first commit https://github.com/Rdatatable/data.table/commit/fcc10d73a20837d0f1ad3278ee9168473afa5ff1 in the PR https://github.com/Rdatatable/data.table/pull/6393/commits with major change to fwrite with gzip.
247+
PR = "3630413ae493a5a61b06c50e80d166924d2ef89a"), # Close-to-last merge commit in the PR.
235248

236249
tests=extra.test.list)
237250
# nolint end: undesirable_operator_linter.

NEWS.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ rowwiseDT(
6969

7070
6. `fread()` gains `logicalYN` argument to read columns consisting only of strings `Y`, `N` as `logical` (as opposed to character), [#4563](https://github.com/Rdatatable/data.table/issues/4563). The default is controlled by option `datatable.logicalYN`, itself defaulting to `FALSE`, for back-compatibility -- some smaller tables (especially sharded tables) might inadvertently read a "true" string column as `logical` and cause bugs. This is particularly important for tables with a column named `y` or `n` -- automatic header detection under `logicalYN=TRUE` will see these values in the first row as being "data" as opposed to column names. A parallel option was not included for `fwrite()` at this time -- users looking for a compact representation of logical columns can still use `fwrite(logical01=TRUE)`. We also opted for now to check only `Y`, `N` and not `Yes`/`No`/`YES`/`NO`.
7171

72+
7. `fwrite()` with `compress="gzip"` produces compatible gz files when composed of multiple independent chunks owing to parallelization, [#6356](https://github.com/Rdatatable/data.table/issues/6356). Earlier `fwrite()` versions could have issues with HTTP upload using `Content-Encoding: gzip` and `Transfer-Encoding: chunked`. Thanks to @oliverfoster for report and @philippechataignon for the fix.
73+
74+
8. `fwrite()` gains a new parameter `compressLevel` to control compression level for gzip, [#5506](https://github.com/Rdatatable/data.table/issues/5506). This parameter balances compression speed and total compression, and corresponds directly to the analogous command-line parameter, e.g. `compressLevel=4` corresponds to passing `-4`; the default, `6`, matches the command-line default, i.e. equivalent to passing `-6`. Thanks @mgarbuzov for the request and @philippechataignon for implementing.
75+
7276
## BUG FIXES
7377

7478
1. `fwrite()` respects `dec=','` for timestamp columns (`POSIXct` or `nanotime`) with sub-second accuracy, [#6446](https://github.com/Rdatatable/data.table/issues/6446). Thanks @kav2k for pointing out the inconsistency and @MichaelChirico for the PR.
@@ -304,7 +308,7 @@ rowwiseDT(
304308

305309
5. Input files are now kept open during `mmap()` when running under Emscripten, [emscripten-core/emscripten#20459](https://github.com/emscripten-core/emscripten/issues/20459). This avoids an error in `fread()` when running in WebAssembly, [#5969](https://github.com/Rdatatable/data.table/issues/5969). Thanks to @maek-ies for the report and @georgestagg for the PR.
306310

307-
6. `dcast()` improves behavior for the situation that the `fun.aggregate` value of `length()` is used but not provided by the user.
311+
6. `dcast()` improves behavior for the situation that the `fun.aggregate` value of `length()` is used but not provided by the user.
308312

309313
a. This now triggers a warning, not a message, since relying on this default often signals unexpected duplicates in the data, [#5386](https://github.com/Rdatatable/data.table/issues/5386). The warning is classed as `dt_missing_fun_aggregate_warning`, allowing for more targeted handling in user code. Thanks @MichaelChirico for the suggestion and @Nj221102 for the fix.
310314

@@ -1019,7 +1023,7 @@ rowwiseDT(
10191023
10201024
14. The options `datatable.print.class` and `datatable.print.keys` are now `TRUE` by default. They have been available since v1.9.8 (Nov 2016) and v1.11.0 (May 2018) respectively.
10211025
1022-
15. Thanks to @ssh352, Václav Tlapák, Cole Miller, András Svraka and Toby Dylan Hocking for reporting and bisecting a significant performance regression in dev. This was fixed before release thanks to a PR by Jan Gorecki, [#5463](https://github.com/Rdatatable/data.table/pull/5463).
1026+
15. Thanks to @ssh352, Václav Tlapák, Cole Miller, András Svraka and Toby Dylan Hocking for reporting and bisecting a significant performance regression in dev. This was fixed before release thanks to a PR by Jan Gorecki, [#5463](https://github.com/Rdatatable/data.table/pull/5463).
10231027
10241028
16. `key(x) <- value` is now fully deprecated (from warning to error). Use `setkey()` to set a table's key. We started warning not to use this approach in 2012, with a stronger warning starting in 2019 (1.12.2). This function will be removed in the next release.
10251029

R/fwrite.R

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ fwrite = function(x, file="", append=FALSE, quote="auto",
1010
buffMB=8, nThread=getDTthreads(verbose),
1111
showProgress=getOption("datatable.showProgress", interactive()),
1212
compress = c("auto", "none", "gzip"),
13+
compressLevel = 6L,
1314
yaml = FALSE,
1415
bom = FALSE,
1516
verbose=getOption("datatable.verbose", FALSE),
@@ -18,18 +19,17 @@ fwrite = function(x, file="", append=FALSE, quote="auto",
1819
if (length(encoding) != 1L || !encoding %chin% c("", "UTF-8", "native")) {
1920
stopf("Argument 'encoding' must be '', 'UTF-8' or 'native'.")
2021
}
21-
if (missing(qmethod)) qmethod = qmethod[1L]
22-
if (missing(compress)) compress = compress[1L]
23-
if (missing(dateTimeAs)) { dateTimeAs = dateTimeAs[1L] }
24-
else if (length(dateTimeAs)>1L) stopf("dateTimeAs must be a single string")
25-
dateTimeAs = chmatch(dateTimeAs, c("ISO","squash","epoch","write.csv"))-1L
26-
if (is.na(dateTimeAs)) stopf("dateTimeAs must be 'ISO','squash','epoch' or 'write.csv'")
22+
qmethod = match.arg(qmethod)
23+
compress = match.arg(compress)
24+
dateTimeAs = match.arg(dateTimeAs)
25+
dateTimeAs = chmatch(dateTimeAs, c("ISO", "squash", "epoch", "write.csv")) - 1L
2726
if (!is.null(logicalAsInt)) {
2827
stopf("logicalAsInt has been renamed logical01 for consistency with fread.")
2928
}
3029
scipen = if (is.numeric(scipen)) as.integer(scipen) else 0L
3130
buffMB = as.integer(buffMB)
3231
nThread = as.integer(nThread)
32+
compressLevel = as.integer(compressLevel)
3333
# write.csv default is 'double' so fwrite follows suit. write.table's default is 'escape'
3434
# validate arguments
3535
if (is.matrix(x)) { # coerce to data.table if input object is matrix
@@ -42,7 +42,8 @@ fwrite = function(x, file="", append=FALSE, quote="auto",
4242
x = as.data.table(x)
4343
}
4444
}
45-
stopifnot(is.list(x),
45+
stopifnot(
46+
is.list(x),
4647
identical(quote,"auto") || isTRUEorFALSE(quote),
4748
is.character(sep) && length(sep)==1L && (nchar(sep) == 1L || identical(sep, "")),
4849
is.character(sep2) && length(sep2)==3L && nchar(sep2[2L])==1L,
@@ -51,14 +52,15 @@ fwrite = function(x, file="", append=FALSE, quote="auto",
5152
is.character(eol) && length(eol)==1L,
5253
length(qmethod) == 1L && qmethod %chin% c("double", "escape"),
5354
length(compress) == 1L && compress %chin% c("auto", "none", "gzip"),
55+
length(compressLevel) == 1L && 0 <= compressLevel && compressLevel <= 9,
5456
isTRUEorFALSE(col.names), isTRUEorFALSE(append), isTRUEorFALSE(row.names),
5557
isTRUEorFALSE(verbose), isTRUEorFALSE(showProgress), isTRUEorFALSE(logical01),
5658
isTRUEorFALSE(bom),
5759
length(na) == 1L, #1725, handles NULL or character(0) input
5860
is.character(file) && length(file)==1L && !is.na(file),
5961
length(buffMB)==1L && !is.na(buffMB) && 1L<=buffMB && buffMB<=1024L,
6062
length(nThread)==1L && !is.na(nThread) && nThread>=1L
61-
)
63+
)
6264

6365
is_gzip = compress == "gzip" || (compress == "auto" && endsWithAny(file, ".gz"))
6466

@@ -115,7 +117,7 @@ fwrite = function(x, file="", append=FALSE, quote="auto",
115117
file = enc2native(file) # CfwriteR cannot handle UTF-8 if that is not the native encoding, see #3078.
116118
.Call(CfwriteR, x, file, sep, sep2, eol, na, dec, quote, qmethod=="escape", append,
117119
row.names, col.names, logical01, scipen, dateTimeAs, buffMB, nThread,
118-
showProgress, is_gzip, bom, yaml, verbose, encoding)
120+
showProgress, is_gzip, compressLevel, bom, yaml, verbose, encoding)
119121
invisible()
120122
}
121123

inst/tests/tests.Rraw

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ TZnotUTC = !identical(tt,"") && !is_utc(tt)
159159
# (3) function factory for matching messages exactly by substituting anything between delimiters [delim, fmt=TRUE]
160160
# (4) function factory for matching messages exactly by substituting a generic string [fmt=string]
161161
get_msg = function(e, delim, fmt=FALSE) {
162+
ufq = options(useFancyQuotes = FALSE) # otherwise we get angled quotes, hard to match robustly
163+
on.exit(options(ufq))
164+
162165
condition = tryCatch({e; NULL}, error=identity, warning=identity)
163166
if (is.null(condition)) return(condition)
164167
msg = condition$message
@@ -170,17 +173,13 @@ get_msg = function(e, delim, fmt=FALSE) {
170173
sprintf("%s%s%s", delim[1L], if (fmt) "%s" else ".+", delim[2L]),
171174
msg
172175
)
173-
if (fmt) return(function(x) sprintf(msg, x))
176+
if (fmt) return(function(...) sprintf(msg, ...))
174177
return(msg)
175178
}
176179
base_messages = list(
177180
missing_object = get_msg(`__dt_test_missing_` + 1, "'", fmt=TRUE),
178181
missing_function = get_msg(`__dt_test_missing_`(), '"', fmt=TRUE),
179-
missing_coerce_method = get_msg(delim = '"', {
180-
old = options(useFancyQuotes = FALSE) # otherwise we get angled quotes, hard to match robustly
181-
on.exit(options(old))
182-
methods::as(TRUE, 'foo')
183-
}),
182+
missing_coerce_method = get_msg(methods::as(TRUE, 'foo'), delim = '"'),
184183
missing_dispatch_method = get_msg(conditionMessage(structure(1, class="foo")), '[\'"]'),
185184
invalid_arg_unary_operator = get_msg(-'a'),
186185
invalid_arg_binary_operator = get_msg(1 + 'a'),
@@ -199,6 +198,8 @@ base_messages = list(
199198
stopifnot = get_msg(stopifnot(FALSE), fmt="FALSE"),
200199
not_yet_used = get_msg(.NotYetUsed("abc"), "'", fmt=TRUE), # NB: need fmt= because the English message has '(yet)' --> parens in regex
201200
ambiguous_date_fmt = get_msg(as.Date('xxx')),
201+
match_arg_length = get_msg(match.arg(c('a', 'b'), letters)),
202+
match_arg_4_choices = get_msg(match.arg('e', letters[1:4]), delim='"', fmt=TRUE),
202203
NULL
203204
)
204205

@@ -10006,7 +10007,7 @@ test(1658.27, fwrite(DT, na="NA", verbose=TRUE), output='Writing bom .false., ya
1000610007
test(1658.28, fwrite(ok_dt, 1), error=base_messages$stopifnot("is.character(file) && length(file) == 1L && !is.na(file)"))
1000710008
test(1658.29, fwrite(ok_dt, quote=123), error="identical\\(quote.*auto.*FALSE.*TRUE")
1000810009
test(1658.30, fwrite(ok_dt, sep="..."), error="nchar(sep)")
10009-
test(1658.31, fwrite(ok_dt, qmethod=c("double", "double")), error="length(qmethod)")
10010+
test(1658.31, fwrite(ok_dt, qmethod=c("double", "double")), error=base_messages$match_arg_length)
1001010011
test(1658.32, fwrite(ok_dt, col.names="foobar"), error="isTRUEorFALSE(col.names)")
1001110012

1001210013
# null data table (no columns)
@@ -10048,8 +10049,12 @@ if (!haszlib()) {
1004810049
test(1658.423, file.info(f1)$size < file.info(f2)$size) # 74 < 804 (file.size() isn't available in R 3.1.0)
1004910050
if (test_R.utils) test(1658.43, fread(f1), DT) # use fread to decompress gz (works cross-platform)
1005010051
fwrite(DT, file=f3<-tempfile(), compress="gzip") # compress to filename not ending .gz
10052+
fwrite(DT, file=f4<-tempfile(), compress="gzip", compressLevel=1) # test compressLevel
10053+
fwrite(DT, file=f5<-tempfile(), compress="gzip", compressLevel=9)
1005110054
test(1658.441, file.info(f3)$size, file.info(f1)$size)
10052-
unlink(c(f1,f2,f3))
10055+
test(1658.442, file.info(f4)$size >= file.info(f1)$size)
10056+
test(1658.443, file.info(f1)$size >= file.info(f5)$size)
10057+
unlink(c(f1,f2,f3,f4,f5))
1005310058
}
1005410059
DT = data.table(a=1:3, b=list(1:4, c(3.14, 100e10), c("foo", "bar", "baz")))
1005510060
test(1658.45, fwrite(DT), output=c("a,b","1,1|2|3|4","2,3.14|1e+12","3,foo|bar|baz"))
@@ -10098,6 +10103,23 @@ test(1658.58, fwrite(DT), output='a,b\n1,0\\+1i\n2,-1-1i\n3,$')
1009810103
test(1658.59, fwrite(data.table(a=list('a')), verbose=TRUE),
1009910104
output='fields will be quoted if the field contains either sep.*sep2.*list column')
1010010105
test(1658.60, fwrite(data.table(r=as.raw(0))), error = "'raw' - not yet implemented")
10106+
## naLen is bigger than col width
10107+
test(1658.61, fwrite(data.table(a="a"), na="VERY LONG MISSING VALUE STRING !", quote=FALSE, verbose=TRUE),
10108+
output="maxLineLen=66")
10109+
## eol="" error
10110+
test(1658.62, fwrite(data.table(a=1), tempfile(), eol=''), error='eol must be 1 or more bytes')
10111+
10112+
## buffMB < single line width and < header width
10113+
f = tempfile()
10114+
test(1658.63, fwrite(data.table(a=strrep('x', 2**21)), f, buffMB=1.0), NULL)
10115+
test(1658.64, file.size(f) > 2**20) # almost exactly 2**21, but exact number will vary by platform. we just care we didn't truncate at 1MiB.
10116+
DT=data.table(1L)
10117+
setnames(DT, strrep('y', 2**21))
10118+
test(1658.65, fwrite(DT, f, buffMB=1.0, nThread=1L), NULL)
10119+
test(1658.66, file.size(f) > 2**20)
10120+
unlink(f)
10121+
10122+
test(1658.67, fwrite(data.table(a=numeric()), verbose=TRUE), output='No data rows present')
1010110123

1010210124
options(oldverbose)
1010310125
## End fwrite tests
@@ -10969,9 +10991,9 @@ DT = data.table(
1096910991
D = as.POSIXct(dt<-paste(d,t), tz="UTC"),
1097010992
E = as.POSIXct(paste0(dt,c(".999",".0",".5",".111112",".123456",".023",".0",".999999",".99",".0009")), tz="UTC"))
1097110993

10972-
test(1740.0, fwrite(DT,dateTimeAs="iso"), error="dateTimeAs must be 'ISO','squash','epoch' or 'write.csv'")
10973-
test(1740.1, fwrite(DT,dateTimeAs=c("ISO","squash")), error="dateTimeAs must be a single string")
10974-
test(1740.2, capture.output(fwrite(DT,dateTimeAs="ISO")), c(
10994+
test(1740.1, fwrite(DT,dateTimeAs="iso"), error=base_messages$match_arg_4_choices("ISO", "squash", "epoch", "write.csv"))
10995+
test(1740.2, fwrite(DT,dateTimeAs=c("ISO","squash")), error=base_messages$match_arg_length)
10996+
test(1740.3, capture.output(fwrite(DT,dateTimeAs="ISO")), c(
1097510997
"A,B,C,D,E",
1097610998
"1907-10-21,1907-10-21,23:59:59,1907-10-21T23:59:59Z,1907-10-21T23:59:59.999Z",
1097710999
"1907-10-22,1907-10-22,00:00:00,1907-10-22T00:00:00Z,1907-10-22T00:00:00Z",
@@ -10983,7 +11005,7 @@ test(1740.2, capture.output(fwrite(DT,dateTimeAs="ISO")), c(
1098311005
"1999-12-31,1999-12-31,01:23:45,1999-12-31T01:23:45Z,1999-12-31T01:23:45.999999Z",
1098411006
"2000-02-29,2000-02-29,23:59:59,2000-02-29T23:59:59Z,2000-02-29T23:59:59.990Z",
1098511007
"2016-09-12,2016-09-12,01:30:30,2016-09-12T01:30:30Z,2016-09-12T01:30:30.000900Z"))
10986-
test(1740.3, capture.output(fwrite(DT,dateTimeAs="squash")), c(
11008+
test(1740.4, capture.output(fwrite(DT,dateTimeAs="squash")), c(
1098711009
"A,B,C,D,E",
1098811010
"19071021,19071021,235959,19071021235959000,19071021235959999",
1098911011
"19071022,19071022,000000,19071022000000000,19071022000000000",
@@ -10995,7 +11017,7 @@ test(1740.3, capture.output(fwrite(DT,dateTimeAs="squash")), c(
1099511017
"19991231,19991231,012345,19991231012345000,19991231012345999",
1099611018
"20000229,20000229,235959,20000229235959000,20000229235959990",
1099711019
"20160912,20160912,013030,20160912013030000,20160912013030000"))
10998-
test(1740.4, capture.output(fwrite(DT,dateTimeAs="epoch")), c(
11020+
test(1740.5, capture.output(fwrite(DT,dateTimeAs="epoch")), c(
1099911021
"A,B,C,D,E",
1100011022
"-22718,-22718,86399,-1962748801,-1962748800.001",
1100111023
"-22717,-22717,0,-1962748800,-1962748800",

man/fwrite.Rd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ fwrite(x, file = "", append = FALSE, quote = "auto",
1818
buffMB = 8L, nThread = getDTthreads(verbose),
1919
showProgress = getOption("datatable.showProgress", interactive()),
2020
compress = c("auto", "none", "gzip"),
21+
compressLevel = 6L,
2122
yaml = FALSE,
2223
bom = FALSE,
2324
verbose = getOption("datatable.verbose", FALSE),
@@ -58,6 +59,7 @@ fwrite(x, file = "", append = FALSE, quote = "auto",
5859
\item{nThread}{The number of threads to use. Experiment to see what works best for your data on your hardware.}
5960
\item{showProgress}{ Display a progress meter on the console? Ignored when \code{file==""}. }
6061
\item{compress}{If \code{compress = "auto"} and if \code{file} ends in \code{.gz} then output format is gzipped csv else csv. If \code{compress = "none"}, output format is always csv. If \code{compress = "gzip"} then format is gzipped csv. Output to the console is never gzipped even if \code{compress = "gzip"}. By default, \code{compress = "auto"}.}
62+
\item{compressLevel}{Level of compression between 1 and 9, 6 by default. See \url{https://linux.die.net/man/1/gzip} for details.}
6163
\item{yaml}{If \code{TRUE}, \code{fwrite} will output a CSVY file, that is, a CSV file with metadata stored as a YAML header, using \code{\link[yaml]{as.yaml}}. See \code{Details}. }
6264
\item{bom}{If \code{TRUE} a BOM (Byte Order Mark) sequence (EF BB BF) is added at the beginning of the file; format 'UTF-8 with BOM'.}
6365
\item{verbose}{Be chatty and report timings?}

src/data.table.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ SEXP chmatch_R(SEXP, SEXP, SEXP);
296296
SEXP chmatchdup_R(SEXP, SEXP, SEXP);
297297
SEXP chin_R(SEXP, SEXP);
298298
SEXP freadR(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
299-
SEXP fwriteR(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
299+
SEXP fwriteR(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
300300
SEXP rbindlist(SEXP, SEXP, SEXP, SEXP, SEXP);
301301
SEXP setlistelt(SEXP, SEXP, SEXP);
302302
SEXP setS4elt(SEXP, SEXP, SEXP);

0 commit comments

Comments
 (0)