vroom/0000755000176200001440000000000014151431426011415 5ustar liggesusersvroom/NAMESPACE0000644000176200001440000000521014151154316012632 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(as.col_spec,"NULL") S3method(as.col_spec,character) S3method(as.col_spec,col_spec) S3method(as.col_spec,default) S3method(as.col_spec,list) S3method(collector_value,collector_character) S3method(collector_value,collector_date) S3method(collector_value,collector_datetime) S3method(collector_value,collector_double) S3method(collector_value,collector_factor) S3method(collector_value,collector_guess) S3method(collector_value,collector_integer) S3method(collector_value,collector_logical) S3method(collector_value,collector_numeric) S3method(collector_value,collector_time) S3method(output_column,POSIXt) S3method(output_column,character) S3method(output_column,default) S3method(output_column,double) S3method(output_column,factor) S3method(print,vroom_altrep) S3method(summary,col_spec) S3method(vroom_str,data.frame) S3method(vroom_str,default) export(as.col_spec) export(col_big_integer) export(col_character) export(col_date) export(col_datetime) export(col_double) export(col_factor) export(col_guess) export(col_integer) export(col_logical) export(col_number) export(col_skip) export(col_time) export(cols) export(cols_condense) export(cols_only) export(contains) export(date_names) export(date_names_lang) export(date_names_langs) export(default_locale) export(ends_with) export(everything) export(fwf_cols) export(fwf_empty) export(fwf_positions) export(fwf_widths) export(gen_character) export(gen_date) export(gen_datetime) export(gen_double) export(gen_factor) export(gen_integer) export(gen_logical) export(gen_name) export(gen_number) export(gen_tbl) export(gen_time) export(guess_type) export(last_col) export(locale) export(matches) export(num_range) export(one_of) export(output_column) export(problems) export(spec) export(starts_with) export(vroom) export(vroom_altrep) export(vroom_altrep_opts) export(vroom_example) export(vroom_examples) export(vroom_format) export(vroom_fwf) export(vroom_lines) export(vroom_progress) export(vroom_str) export(vroom_write) export(vroom_write_lines) importFrom(bit64,integer64) importFrom(crayon,blue) importFrom(crayon,bold) importFrom(crayon,col_nchar) importFrom(crayon,cyan) importFrom(crayon,green) importFrom(crayon,reset) importFrom(crayon,silver) importFrom(glue,double_quote) importFrom(lifecycle,deprecate_warn) importFrom(lifecycle,deprecated) importFrom(methods,setOldClass) importFrom(tidyselect,contains) importFrom(tidyselect,ends_with) importFrom(tidyselect,everything) importFrom(tidyselect,last_col) importFrom(tidyselect,matches) importFrom(tidyselect,num_range) importFrom(tidyselect,one_of) importFrom(tidyselect,starts_with) useDynLib(vroom, .registration = TRUE) vroom/LICENSE0000644000176200001440000000005314142515724012424 0ustar liggesusersYEAR: 2021 COPYRIGHT HOLDER: vroom authors vroom/README.md0000644000176200001440000002341414151154260012676 0ustar liggesusers # πŸŽπŸ’¨vroom [![R-CMD-check](https://github.com/r-lib/vroom/workflows/R-CMD-check/badge.svg)](https://github.com/r-lib/vroom/actions) [![Codecov test coverage](https://codecov.io/gh/r-lib/vroom/branch/main/graph/badge.svg)](https://app.codecov.io/gh/r-lib/vroom?branch=main) [![CRAN status](https://www.r-pkg.org/badges/version/vroom)](https://cran.r-project.org/package=vroom) [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) The fastest delimited reader for R, **1.23 GB/sec**. But that’s impossible! How can it be [so fast](https://vroom.r-lib.org/articles/benchmarks.html)? vroom doesn’t stop to actually *read* all of your data, it simply indexes where each record is located so it can be read later. The vectors returned use the [Altrep framework](https://svn.r-project.org/R/branches/ALTREP/ALTREP.html) to lazily load the data on-demand when it is accessed, so you only pay for what you use. This lazy access is done automatically, so no changes to your R data-manipulation code are needed. vroom also uses multiple threads for indexing, materializing non-character columns, and when writing to further improve performance. | package | version | time (sec) | speedup | throughput | |:-----------|--------:|-----------:|--------:|--------------:| | vroom | 1.5.1 | 1.36 | 53.30 | 1.23 GB/sec | | data.table | 1.14.0 | 5.83 | 12.40 | 281.65 MB/sec | | readr | 1.4.0 | 37.30 | 1.94 | 44.02 MB/sec | | read.delim | 4.1.0 | 72.31 | 1.00 | 22.71 MB/sec | ## Features vroom has nearly all of the parsing features of [readr](https://readr.tidyverse.org) for delimited and fixed width files, including - delimiter guessing\* - custom delimiters (including multi-byte\* and Unicode\* delimiters) - specification of column types (including type guessing) - numeric types (double, integer, big integer\*, number) - logical types - datetime types (datetime, date, time) - categorical types (characters, factors) - column selection, like `dplyr::select()`\* - skipping headers, comments and blank lines - quoted fields - double and backslashed escapes - whitespace trimming - windows newlines - [reading from multiple files or connections\*](#reading-multiple-files) - embedded newlines in headers and fields\*\* - writing delimited files with as-needed quoting. - robust to invalid inputs (vroom has been extensively tested with the [afl](https://lcamtuf.coredump.cx/afl/) fuzz tester)\*. \* *these are additional features not in readr.* \*\* *requires `num_threads = 1`.* ## Installation Install vroom from CRAN with: ``` r install.packages("vroom") ``` Alternatively, if you need the development version from [GitHub](https://github.com/) install it with: ``` r # install.packages("devtools") devtools::install_dev("vroom") ``` ## Usage See [getting started](https://vroom.r-lib.org/articles/vroom.html) to jump start your use of vroom! vroom uses the same interface as readr to specify column types. ``` r vroom::vroom("mtcars.tsv", col_types = list(cyl = "i", gear = "f",hp = "i", disp = "_", drat = "_", vs = "l", am = "l", carb = "i") ) #> # A tibble: 32 Γ— 10 #> model mpg cyl hp wt qsec vs am gear carb #> #> 1 Mazda RX4 21 6 110 2.62 16.5 FALSE TRUE 4 4 #> 2 Mazda RX4 Wag 21 6 110 2.88 17.0 FALSE TRUE 4 4 #> 3 Datsun 710 22.8 4 93 2.32 18.6 TRUE TRUE 4 1 #> # … with 29 more rows ``` ## Reading multiple files vroom natively supports reading from multiple files (or even multiple connections!). First we generate some files to read by splitting the nycflights dataset by airline. ``` r library(nycflights13) purrr::iwalk( split(flights, flights$carrier), ~ { .x$carrier[[1]]; vroom::vroom_write(.x, glue::glue("flights_{.y}.tsv"), delim = "\t") } ) ``` Then we can efficiently read them into one tibble by passing the filenames directly to vroom. ``` r files <- fs::dir_ls(glob = "flights*tsv") files #> flights_9E.tsv flights_AA.tsv flights_AS.tsv flights_B6.tsv flights_DL.tsv #> flights_EV.tsv flights_F9.tsv flights_FL.tsv flights_HA.tsv flights_MQ.tsv #> flights_OO.tsv flights_UA.tsv flights_US.tsv flights_VX.tsv flights_WN.tsv #> flights_YV.tsv vroom::vroom(files) #> Rows: 336776 Columns: 19 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "\t" #> chr (4): carrier, tailnum, origin, dest #> dbl (14): year, month, day, dep_time, sched_dep_time, dep_delay, arr_time, ... #> dttm (1): time_hour #> #> β„Ή Use `spec()` to retrieve the full column specification for this data. #> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 336,776 Γ— 19 #> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time #> #> 1 2013 1 1 810 810 0 1048 1037 #> 2 2013 1 1 1451 1500 -9 1634 1636 #> 3 2013 1 1 1452 1455 -3 1637 1639 #> # … with 336,773 more rows, and 11 more variables: arr_delay , #> # carrier , flight , tailnum , origin , dest , #> # air_time , distance , hour , minute , time_hour ``` ## Learning more - [Getting started with vroom](https://vroom.r-lib.org/articles/vroom.html) - [πŸ“½ vroom: Because Life is too short to read slow](https://www.youtube.com/watch?v=RA9AjqZXxMU&t=10s) - Presentation at UseR!2019 ([slides](https://speakerdeck.com/jimhester/vroom)) - [πŸ“Ή vroom: Read and write rectangular data quickly](https://www.youtube.com/watch?v=ZP_y5eaAc60) - a video tour of the vroom features. ## Benchmarks The speed quoted above is from a real 1.53G dataset with 14,388,451 rows and 11 columns, see the [benchmark article](https://vroom.r-lib.org/articles/benchmarks.html) for full details of the dataset and [bench/](https://github.com/r-lib/vroom/tree/main/inst/bench) for the code used to retrieve the data and perform the benchmarks. # Environment variables In addition to the arguments to the `vroom()` function, you can control the behavior of vroom with a few environment variables. Generally these will not need to be set by most users. - `VROOM_TEMP_PATH` - Path to the directory used to store temporary files when reading from a R connection. If unset defaults to the R session’s temporary directory (`tempdir()`). - `VROOM_THREADS` - The number of processor threads to use when indexing and parsing. If unset defaults to `parallel::detectCores()`. - `VROOM_SHOW_PROGRESS` - Whether to show the progress bar when indexing. Regardless of this setting the progress bar is disabled in non-interactive settings, R notebooks, when running tests with testthat and when knitting documents. - `VROOM_CONNECTION_SIZE` - The size (in bytes) of the connection buffer when reading from connections (default is 128 KiB). - `VROOM_WRITE_BUFFER_LINES` - The number of lines to use for each buffer when writing files (default: 1000). There are also a family of variables to control use of the Altrep framework. For versions of R where the Altrep framework is unavailable (R < 3.5.0) they are automatically turned off and the variables have no effect. The variables can take one of `true`, `false`, `TRUE`, `FALSE`, `1`, or `0`. - `VROOM_USE_ALTREP_NUMERICS` - If set use Altrep for *all* numeric types (default `false`). There are also individual variables for each type. Currently only `VROOM_USE_ALTREP_CHR` defaults to `true`. - `VROOM_USE_ALTREP_CHR` - `VROOM_USE_ALTREP_FCT` - `VROOM_USE_ALTREP_INT` - `VROOM_USE_ALTREP_BIG_INT` - `VROOM_USE_ALTREP_DBL` - `VROOM_USE_ALTREP_NUM` - `VROOM_USE_ALTREP_LGL` - `VROOM_USE_ALTREP_DTTM` - `VROOM_USE_ALTREP_DATE` - `VROOM_USE_ALTREP_TIME` ## RStudio caveats RStudio’s environment pane calls `object.size()` when it refreshes the pane, which for Altrep objects can be extremely slow. RStudio 1.2.1335+ includes the fixes ([RStudio\#4210](https://github.com/rstudio/rstudio/pull/4210), [RStudio\#4292](https://github.com/rstudio/rstudio/pull/4292)) for this issue, so it is recommended you use at least that version. ## Thanks - [Gabe Becker](https://twitter.com/groundwalkergmb), [Luke Tierney](https://homepage.divms.uiowa.edu/~luke/) and [Tomas Kalibera](https://github.com/kalibera) for conceiving, Implementing and maintaining the [Altrep framework](https://svn.r-project.org/R/branches/ALTREP/ALTREP.html) - [Romain FranΓ§ois](https://twitter.com/romain_francois), whose [Altrepisode](https://web.archive.org/web/20200315075838/https://purrple.cat/blog/2018/10/14/altrep-and-cpp/) package and [related blog-posts](https://web.archive.org/web/20200315075838/https://purrple.cat/blog/2018/10/14/altrep-and-cpp/) were a great guide for creating new Altrep objects in C++. - [Matt Dowle](https://twitter.com/mattdowle) and the rest of the [Rdatatable](https://github.com/Rdatatable) team, `data.table::fread()` is blazing fast and great motivation to see how fast we could go faster! vroom/man/0000755000176200001440000000000014151152772012174 5ustar liggesusersvroom/man/cols.Rd0000644000176200001440000001026614142515724013430 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{cols} \alias{cols} \alias{col_types} \alias{cols_only} \alias{col_logical} \alias{col_integer} \alias{col_big_integer} \alias{col_double} \alias{col_character} \alias{col_skip} \alias{col_number} \alias{col_guess} \alias{col_factor} \alias{col_datetime} \alias{col_date} \alias{col_time} \title{Create column specification} \usage{ cols(..., .default = col_guess(), .delim = NULL) cols_only(...) col_logical(...) col_integer(...) col_big_integer(...) col_double(...) col_character(...) col_skip(...) col_number(...) col_guess(...) col_factor(levels = NULL, ordered = FALSE, include_na = FALSE, ...) col_datetime(format = "", ...) col_date(format = "", ...) col_time(format = "", ...) } \arguments{ \item{...}{Either column objects created by \verb{col_*()}, or their abbreviated character names (as described in the \code{col_types} argument of \code{\link[=vroom]{vroom()}}). If you're only overriding a few columns, it's best to refer to columns by name. If not named, the column types must match the column names exactly. In \verb{col_*()} functions these are stored in the object.} \item{.default}{Any named columns not explicitly overridden in \code{...} will be read with this column type.} \item{.delim}{The delimiter to use when parsing. If the \code{delim} argument used in the call to \code{vroom()} it takes precedence over the one specified in \code{col_types}.} \item{levels}{Character vector providing set of allowed levels. if \code{NULL}, will generate levels based on the unique values of \code{x}, ordered by order of appearance in \code{x}.} \item{ordered}{Is it an ordered factor?} \item{include_na}{If \code{NA} are present, include as an explicit factor to level?} \item{format}{A format specification, as described below. If set to "", date times are parsed as ISO8601, dates and times used the date and time formats specified in the \code{\link[readr:locale]{locale()}}. Unlike \code{\link[=strptime]{strptime()}}, the format specification must match the complete string.} } \description{ \code{cols()} includes all columns in the input data, guessing the column types as the default. \code{cols_only()} includes only the columns you explicitly specify, skipping the rest. } \details{ The available specifications are: (long names in quotes and string abbreviations in brackets)\tabular{llll}{ function \tab long name \tab short name \tab description \cr \code{col_logical()} \tab "logical" \tab "l" \tab Logical values containing only \code{T}, \code{F}, \code{TRUE} or \code{FALSE}. \cr \code{col_integer()} \tab "integer" \tab "i" \tab Integer numbers. \cr \code{col_big_integer()} \tab "big_integer" \tab "I" \tab Big Integers (64bit), requires the \code{bit64} package. \cr \code{col_double()} \tab "double", "numeric" \tab "d" \tab 64-bit double floating point numbers. \cr \code{col_character()} \tab "character" \tab "c" \tab Character string data. \cr \code{col_factor(levels, ordered)} \tab "factor" \tab "f" \tab A fixed set of values. \cr \code{col_date(format = "")} \tab "date" \tab "D" \tab Calendar dates formatted with the locale's \code{date_format}. \cr \code{col_time(format = "")} \tab "time" \tab "t" \tab Times formatted with the locale's \code{time_format}. \cr \code{col_datetime(format = "")} \tab "datetime", "POSIXct" \tab "T" \tab ISO8601 date times. \cr \code{col_number()} \tab "number" \tab "n" \tab Human readable numbers containing the \code{grouping_mark} \cr \code{col_skip()} \tab "skip", "NULL" \tab "_", "-" \tab Skip and don't import this column. \cr \code{col_guess()} \tab "guess", "NA" \tab "?" \tab Parse using the "best" guessed type based on the input. \cr } } \examples{ cols(a = col_integer()) cols_only(a = col_integer()) # You can also use the standard abbreviations cols(a = "i") cols(a = "i", b = "d", c = "_") # Or long names (like utils::read.csv) cols(a = "integer", b = "double", c = "skip") # You can also use multiple sets of column definitions by combining # them like so: t1 <- cols( column_one = col_integer(), column_two = col_number()) t2 <- cols( column_three = col_character()) t3 <- t1 t3$cols <- c(t1$cols, t2$cols) t3 } vroom/man/vroom_write_lines.Rd0000644000176200001440000000200614142515724016227 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{vroom_write_lines} \alias{vroom_write_lines} \title{Write lines to a file} \usage{ vroom_write_lines( x, file, eol = "\\n", na = "NA", append = FALSE, num_threads = vroom_threads() ) } \arguments{ \item{x}{A data frame or tibble to write to disk.} \item{file}{File or connection to write to.} \item{eol}{The end of line character to use. Most commonly either \code{"\n"} for Unix style newlines, or \code{"\r\n"} for Windows style newlines.} \item{na}{String used for missing values. Defaults to 'NA'.} \item{append}{If \code{FALSE}, will overwrite existing file. If \code{TRUE}, will append to existing file. In both cases, if the file does not exist a new file is created.} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} } \description{ Write lines to a file } vroom/man/problems.Rd0000644000176200001440000000165314142515724014313 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/problems.R \name{problems} \alias{problems} \title{Retrieve parsing problems} \usage{ problems(x, lazy = FALSE) } \arguments{ \item{x}{A data frame from \code{vroom::vroom()}.} \item{lazy}{If \code{TRUE}, just the problems found so far are returned. If \code{FALSE} (the default) the lazy data is first read completely and all problems are returned.} } \value{ A data frame with one row for each problem and four columns: \itemize{ \item row,col - Row and column of problem \item expected - What vroom expected to find \item actual - What it actually found \item file - The file with the problem } } \description{ vroom will only fail to parse a file if the file is invalid in a way that is unrecoverable. However there are a number of non-fatal problems that you might want to know about. You can retrieve a data frame of these problems with this function. } vroom/man/vroom_format.Rd0000644000176200001440000000410414142515724015174 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{vroom_format} \alias{vroom_format} \title{Convert a data frame to a delimited string} \usage{ vroom_format( x, delim = "\\t", eol = "\\n", na = "NA", col_names = TRUE, escape = c("double", "backslash", "none"), quote = c("needed", "all", "none"), bom = FALSE, num_threads = vroom_threads() ) } \arguments{ \item{x}{A data frame or tibble to write to disk.} \item{delim}{Delimiter used to separate values. Defaults to \verb{\\t} to write tab separated value (TSV) files.} \item{eol}{The end of line character to use. Most commonly either \code{"\n"} for Unix style newlines, or \code{"\r\n"} for Windows style newlines.} \item{na}{String used for missing values. Defaults to 'NA'.} \item{col_names}{If \code{FALSE}, column names will not be included at the top of the file. If \code{TRUE}, column names will be included. If not specified, \code{col_names} will take the opposite value given to \code{append}.} \item{escape}{The type of escape to use when quotes are in the data. \itemize{ \item \code{double} - quotes are escaped by doubling them. \item \code{backslash} - quotes are escaped by a preceding backslash. \item \code{none} - quotes are not escaped. }} \item{quote}{How to handle fields which contain characters that need to be quoted. \itemize{ \item \code{needed} - Only quote fields which need them. \item \code{all} - Quote all fields. \item \code{none} - Never quote fields. }} \item{bom}{If \code{TRUE} add a UTF-8 BOM at the beginning of the file. This is recommended when saving data for consumption by excel, as it will force excel to read the data with the correct encoding (UTF-8)} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} } \description{ This is equivalent to \code{\link[=vroom_write]{vroom_write()}}, but instead of writing to disk, it returns a string. It is primarily useful for examples and for testing. } vroom/man/date_names.Rd0000644000176200001440000000213014142515724014557 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_names} \alias{date_names} \alias{date_names_lang} \alias{date_names_langs} \title{Create or retrieve date names} \usage{ date_names(mon, mon_ab = mon, day, day_ab = day, am_pm = c("AM", "PM")) date_names_lang(language) date_names_langs() } \arguments{ \item{mon, mon_ab}{Full and abbreviated month names.} \item{day, day_ab}{Full and abbreviated week day names. Starts with Sunday.} \item{am_pm}{Names used for AM and PM.} \item{language}{A BCP 47 locale, made up of a language and a region, e.g. \code{"en_US"} for American English. See \code{date_names_langs()} for a complete list of available locales.} } \description{ When parsing dates, you often need to know how weekdays of the week and months are represented as text. This pair of functions allows you to either create your own, or retrieve from a standard list. The standard list is derived from ICU (\verb{https://site.icu-project.org}) via the \emph{stringi} package. } \examples{ date_names_lang("en") date_names_lang("ko") date_names_lang("fr") } vroom/man/vroom_fwf.Rd0000644000176200001440000002070514151153626014472 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_fwf.R \name{vroom_fwf} \alias{vroom_fwf} \alias{fwf_empty} \alias{fwf_widths} \alias{fwf_positions} \alias{fwf_cols} \title{Read a fixed width file into a tibble} \usage{ vroom_fwf( file, col_positions = fwf_empty(file, skip, n = guess_max), col_types = NULL, col_select = NULL, id = NULL, locale = default_locale(), na = c("", "NA"), comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, skip = 0, n_max = Inf, guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique" ) fwf_empty(file, skip = 0, col_names = NULL, comment = "", n = 100L) fwf_widths(widths, col_names = NULL) fwf_positions(start, end = NULL, col_names = NULL) fwf_cols(...) } \arguments{ \item{file}{Either a path to a file, a connection, or literal data (either a single string or a raw vector). Files ending in \code{.gz}, \code{.bz2}, \code{.xz}, or \code{.zip} will be automatically uncompressed. Files starting with \verb{http://}, \verb{https://}, \verb{ftp://}, or \verb{ftps://} will be automatically downloaded. Remote gz files can also be automatically downloaded and decompressed. Literal data is most useful for examples and tests. To be recognised as a path, it must be wrapped with \code{I()}, be a string containing at least one new line, or be a vector containing at least one string with a new line. Using a value of \code{\link[readr:clipboard]{clipboard()}} will read from the system clipboard.} \item{col_positions}{Column positions, as created by \code{\link[readr:read_fwf]{fwf_empty()}}, \code{\link[readr:read_fwf]{fwf_widths()}} or \code{\link[readr:read_fwf]{fwf_positions()}}. To read in only selected fields, use \code{\link[readr:read_fwf]{fwf_positions()}}. If the width of the last column is variable (a ragged fwf file), supply the last end position as NA.} \item{col_types}{One of \code{NULL}, a \code{\link[readr:cols]{cols()}} specification, or a string. See \code{vignette("readr")} for more details. If \code{NULL}, all column types will be imputed from \code{guess_max} rows on the input interspersed throughout the file. This is convenient (and fast), but not robust. If the imputation fails, you'll need to increase the \code{guess_max} or supply the correct types yourself. Column specifications created by \code{\link[=list]{list()}} or \code{\link[readr:cols]{cols()}} must contain one column specification for each column. If you only want to read a subset of the columns, use \code{\link[readr:cols]{cols_only()}}. Alternatively, you can use a compact string representation where each character represents one column: \itemize{ \item c = character \item i = integer \item n = number \item d = double \item l = logical \item f = factor \item D = date \item T = date time \item t = time \item ? = guess \item _ or - = skip By default, reading a file without a column specification will print a message showing what \code{readr} guessed they were. To remove this message, set \code{show_col_types = FALSE} or set `options(readr.show_col_types = FALSE). }} \item{col_select}{<\code{\link[tidyselect:language]{tidy-select}}> Columns to include in the results, either by name or by numeric index. Use \code{\link[=c]{c()}} or \code{\link[=list]{list()}} to select with more than one expression and \code{\link[tidyselect:language]{?tidyselect::language}} for full details on the selection language.} \item{id}{The name of a column in which to store the file path. This is useful when reading multiple input files and there is data in the file paths, such as the data collection date. If \code{NULL} (the default) no extra column is created.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[readr:locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{comment}{A string used to identify comments. Any text after the comment characters will be silently ignored.} \item{skip_empty_rows}{Should blank rows be ignored altogether? i.e. If this option is \code{TRUE} then blank rows will not be represented at all. If it is \code{FALSE} then they will be represented by \code{NA} values in all the columns.} \item{trim_ws}{Should leading and trailing whitespace (ASCII spaces and tabs) be trimmed from each field before parsing it?} \item{skip}{Number of lines to skip before reading data.} \item{n_max}{Maximum number of lines to read.} \item{guess_max}{Maximum number of lines to use for guessing column types.} \item{altrep}{Control which column types use Altrep representations, either a character vector of types, \code{TRUE} or \code{FALSE}. See \code{\link[=vroom_altrep]{vroom_altrep()}} for for full details.} \item{altrep_opts}{\Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")}} \item{num_threads}{The number of processing threads to use for initial parsing and lazy reading of data. If your data contains newlines within fields the parser should automatically detect this and fall back to using one thread only. However if you know your file has newlines within quoted fields it is safest to set \code{num_threads = 1} explicitly.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} \item{show_col_types}{If \code{FALSE}, do not show the guessed column types. If \code{TRUE} always show the column types, even if they are supplied. If \code{NULL} (the default) only show the column types if they are not explicitly supplied by the \code{col_types} argument.} \item{.name_repair}{Treatment of problematic column names: \itemize{ \item \code{"minimal"}: No name repair or checks, beyond basic existence of names \item \code{"unique"}: Make sure names are unique and not empty \item \code{"check_unique"}: (default value), no name repair, but check they are \code{unique} \item \code{"universal"}: Make the names \code{unique} and syntactic \item a function: apply custom name repair (e.g., \code{.name_repair = make.names} for names in the style of base R) \item A purrr-style anonymous function, see \code{\link[rlang:as_function]{rlang::as_function()}} } This argument is passed on as \code{repair} to \code{\link[vctrs:vec_as_names]{vctrs::vec_as_names()}}. See there for more details on these terms and the strategies used to enforce them.} \item{col_names}{Either NULL, or a character vector column names.} \item{n}{Number of lines the tokenizer will read to determine file structure. By default it is set to 100.} \item{widths}{Width of each field. Use NA as width of last field when reading a ragged fwf file.} \item{start, end}{Starting and ending (inclusive) positions of each field. Use NA as last end field when reading a ragged fwf file.} \item{...}{If the first element is a data frame, then it must have all numeric columns and either one or two rows. The column names are the variable names. The column values are the variable widths if a length one vector, and if length two, variable start and end positions. The elements of \code{...} are used to construct a data frame with or or two rows as above.} } \description{ Read a fixed width file into a tibble } \details{ \emph{Note}: \code{fwf_empty()} cannot take a R connection such as a URL as input, as this would result in reading from the connection twice. In these cases it is better to download the file first before reading. } \examples{ fwf_sample <- vroom_example("fwf-sample.txt") cat(readLines(fwf_sample)) # You can specify column positions in several ways: # 1. Guess based on position of empty columns vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) # 2. A vector of field widths vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) # 3. Paired vectors of start and end positions vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) # 4. Named arguments with start and end positions vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) # 5. Named arguments with column widths vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) } vroom/man/vroom_str.Rd0000644000176200001440000000073114142512075014512 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/altrep.R \name{vroom_str} \alias{vroom_str} \title{Structure of objects} \usage{ vroom_str(x) } \arguments{ \item{x}{a vector} } \description{ Similar to \code{str()} but with more information for Altrep objects. } \examples{ # when used on non-altrep objects altrep will always be false vroom_str(mtcars) mt <- vroom(vroom_example("mtcars.csv"), ",", altrep = c("chr", "dbl")) vroom_str(mt) } vroom/man/generators.Rd0000644000176200001440000000400714142512075014631 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/generator.R \name{generators} \alias{generators} \alias{gen_character} \alias{gen_double} \alias{gen_number} \alias{gen_integer} \alias{gen_factor} \alias{gen_time} \alias{gen_date} \alias{gen_datetime} \alias{gen_logical} \alias{gen_name} \title{Generate individual vectors of the types supported by vroom} \usage{ gen_character(n, min = 5, max = 25, values = c(letters, LETTERS, 0:9), ...) gen_double(n, f = stats::rnorm, ...) gen_number(n, f = stats::rnorm, ...) gen_integer(n, min = 1L, max = .Machine$integer.max, prob = NULL, ...) gen_factor( n, levels = NULL, ordered = FALSE, num_levels = gen_integer(1L, 1L, 25L), ... ) gen_time(n, min = 0, max = hms::hms(days = 1), fractional = FALSE, ...) gen_date(n, min = as.Date("2001-01-01"), max = as.Date("2021-01-01"), ...) gen_datetime( n, min = as.POSIXct("2001-01-01"), max = as.POSIXct("2021-01-01"), tz = "UTC", ... ) gen_logical(n, ...) gen_name(n) } \arguments{ \item{n}{The size of the vector to generate} \item{min}{The minimum range for the vector} \item{max}{The maximum range for the vector} \item{values}{The explicit values to use.} \item{...}{Additional arguments passed to internal generation functions} \item{f}{The random function to use.} \item{prob}{a vector of probability weights for obtaining the elements of the vector being sampled.} \item{levels}{The explicit levels to use, if \code{NULL} random levels are generated using \code{\link[=gen_name]{gen_name()}}.} \item{ordered}{Should the factors be ordered factors?} \item{num_levels}{The number of factor levels to generate} \item{fractional}{Whether to generate times with fractional seconds} \item{tz}{The timezone to use for dates} } \description{ Generate individual vectors of the types supported by vroom } \examples{ # characters gen_character(4) # factors gen_factor(4) # logical gen_logical(4) # numbers gen_double(4) gen_integer(4) # temporal data gen_time(4) gen_date(4) gen_datetime(4) } vroom/man/vroom.Rd0000644000176200001440000001743614142515724013640 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom} \alias{vroom} \title{Read a delimited file into a tibble} \usage{ vroom( file, delim = NULL, col_names = TRUE, col_types = NULL, col_select = NULL, id = NULL, skip = 0, n_max = Inf, na = c("", "NA"), quote = "\\"", comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, escape_double = TRUE, escape_backslash = FALSE, locale = default_locale(), guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique" ) } \arguments{ \item{file}{path to a local file.} \item{delim}{One or more characters used to delimit fields within a file. If \code{NULL} the delimiter is guessed from the set of \code{c(",", "\\t", " ", "|", ":", ";")}.} \item{col_names}{Either \code{TRUE}, \code{FALSE} or a character vector of column names. If \code{TRUE}, the first row of the input will be used as the column names, and will not be included in the data frame. If \code{FALSE}, column names will be generated automatically: X1, X2, X3 etc. If \code{col_names} is a character vector, the values will be used as the names of the columns, and the first row of the input will be read into the first row of the output data frame. Missing (\code{NA}) column names will generate a warning, and be filled in with dummy names \code{...1}, \code{...2} etc. Duplicate column names will generate a warning and be made unique, see \code{name_repair} to control how this is done.} \item{col_types}{One of \code{NULL}, a \code{\link[readr:cols]{cols()}} specification, or a string. See \code{vignette("readr")} for more details. If \code{NULL}, all column types will be imputed from \code{guess_max} rows on the input interspersed throughout the file. This is convenient (and fast), but not robust. If the imputation fails, you'll need to increase the \code{guess_max} or supply the correct types yourself. Column specifications created by \code{\link[=list]{list()}} or \code{\link[readr:cols]{cols()}} must contain one column specification for each column. If you only want to read a subset of the columns, use \code{\link[readr:cols]{cols_only()}}. Alternatively, you can use a compact string representation where each character represents one column: \itemize{ \item c = character \item i = integer \item n = number \item d = double \item l = logical \item f = factor \item D = date \item T = date time \item t = time \item ? = guess \item _ or - = skip By default, reading a file without a column specification will print a message showing what \code{readr} guessed they were. To remove this message, set \code{show_col_types = FALSE} or set `options(readr.show_col_types = FALSE). }} \item{col_select}{One or more selection expressions, like in \code{dplyr::select()}. Use \code{c()} or \code{list()} to use more than one expression. See \code{?dplyr::select} for details on available selection options.} \item{id}{Either a string or 'NULL'. If a string, the output will contain a variable with that name with the filename(s) as the value. If 'NULL', the default, no variable will be created.} \item{skip}{Number of lines to skip before reading data. If \code{comment} is supplied any commented lines are ignored \emph{after} skipping.} \item{n_max}{Maximum number of lines to read.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{quote}{Single character used to quote strings.} \item{comment}{A string used to identify comments. Any text after the comment characters will be silently ignored.} \item{skip_empty_rows}{Should blank rows be ignored altogether? i.e. If this option is \code{TRUE} then blank rows will not be represented at all. If it is \code{FALSE} then they will be represented by \code{NA} values in all the columns.} \item{trim_ws}{Should leading and trailing whitespace (ASCII spaces and tabs) be trimmed from each field before parsing it?} \item{escape_double}{Does the file escape quotes by doubling them? i.e. If this option is \code{TRUE}, the value '""' represents a single quote, '"'.} \item{escape_backslash}{Does the file use backslashes to escape special characters? This is more general than \code{escape_double} as backslashes can be used to escape the delimiter character, the quote character, or to add special characters like \verb{\\\\n}.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[readr:locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{guess_max}{Maximum number of lines to use for guessing column types.} \item{altrep}{Control which column types use Altrep representations, either a character vector of types, \code{TRUE} or \code{FALSE}. See \code{\link[=vroom_altrep]{vroom_altrep()}} for for full details.} \item{altrep_opts}{\Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")}} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} \item{show_col_types}{Control showing the column specifications. If \code{TRUE} column specifications are always show, if \code{FALSE} they are never shown. If \code{NULL} (the default) they are shown only if an explicit specification is not given to \code{col_types}.} \item{.name_repair}{Handling of column names. By default, vroom ensures column names are not empty and unique. See \code{.name_repair} as documented in \code{\link[tibble:tibble]{tibble::tibble()}} for additional options including supplying user defined name repair functions.} } \description{ Read a delimited file into a tibble } \examples{ # get path to example file input_file <- vroom_example("mtcars.csv") input_file # Read from a path # Input sources ------------------------------------------------------------- # Read from a path vroom(input_file) # You can also use paths directly # vroom("mtcars.csv") \dontrun{ # Including remote paths vroom("https://github.com/r-lib/vroom/raw/main/inst/extdata/mtcars.csv") } # Or directly from a string with `I()` vroom(I("x,y\n1,2\n3,4\n")) # Column selection ---------------------------------------------------------- # Pass column names or indexes directly to select them vroom(input_file, col_select = c(model, cyl, gear)) vroom(input_file, col_select = c(1, 3, 11)) # Or use the selection helpers vroom(input_file, col_select = starts_with("d")) # You can also rename specific columns vroom(input_file, col_select = list(car = model, everything())) # Column types -------------------------------------------------------------- # By default, vroom guesses the columns types, looking at 1000 rows # throughout the dataset. # You can specify them explicitly with a compact specification: vroom(I("x,y\n1,2\n3,4\n"), col_types = "dc") # Or with a list of column types: vroom(I("x,y\n1,2\n3,4\n"), col_types = list(col_double(), col_character())) # File types ---------------------------------------------------------------- # csv vroom(I("a,b\n1.0,2.0\n"), delim = ",") # tsv vroom(I("a\tb\n1.0\t2.0\n")) # Other delimiters vroom(I("a|b\n1.0|2.0\n"), delim = "|") # Read datasets across multiple files --------------------------------------- mtcars_by_cyl <- vroom_example(vroom_examples("mtcars-")) mtcars_by_cyl # Pass the filenames directly to vroom, they are efficiently combined vroom(mtcars_by_cyl) } vroom/man/spec.Rd0000644000176200001440000000132114142512075013406 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{cols_condense} \alias{cols_condense} \alias{spec} \title{Examine the column specifications for a data frame} \usage{ cols_condense(x) spec(x) } \arguments{ \item{x}{The data frame object to extract from} } \value{ A col_spec object. } \description{ \code{cols_condense()} takes a spec object and condenses its definition by setting the default column type to the most frequent type and only listing columns with a different type. \code{spec()} extracts the full column specification from a tibble created by readr. } \examples{ df <- vroom(vroom_example("mtcars.csv")) s <- spec(df) s cols_condense(s) } \concept{parsers} vroom/man/output_column.Rd0000644000176200001440000000111414142512075015371 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{output_column} \alias{output_column} \title{Preprocess column for output} \usage{ output_column(x) } \arguments{ \item{x}{A vector} } \description{ This is a generic function that applied to each column before it is saved to disk. It provides a hook for S3 classes that need special handling. } \examples{ # Most types are returned unchanged output_column(1) output_column("x") # datetimes are formatted in ISO 8601 output_column(Sys.Date()) output_column(Sys.time()) } \keyword{internal} vroom/man/vroom_lines.Rd0000644000176200001440000000470714142515724015027 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_lines.R \name{vroom_lines} \alias{vroom_lines} \title{Read lines from a file} \usage{ vroom_lines( file, n_max = Inf, skip = 0, na = character(), skip_empty_rows = FALSE, locale = default_locale(), altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress() ) } \arguments{ \item{file}{path to a local file.} \item{n_max}{Maximum number of lines to read.} \item{skip}{Number of lines to skip before reading data. If \code{comment} is supplied any commented lines are ignored \emph{after} skipping.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{skip_empty_rows}{Should blank rows be ignored altogether? i.e. If this option is \code{TRUE} then blank rows will not be represented at all. If it is \code{FALSE} then they will be represented by \code{NA} values in all the columns.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[readr:locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{altrep}{Control which column types use Altrep representations, either a character vector of types, \code{TRUE} or \code{FALSE}. See \code{\link[=vroom_altrep]{vroom_altrep()}} for for full details.} \item{altrep_opts}{\Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")}} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} } \description{ \code{vroom_lines()} is similar to \code{readLines()}, however it reads the lines lazily like \code{\link[=vroom]{vroom()}}, so operations like \code{length()}, \code{head()}, \code{tail()} and \code{sample()} can be done much more efficiently without reading all the data into R. } \examples{ lines <- vroom_lines(vroom_example("mtcars.csv")) length(lines) head(lines, n = 2) tail(lines, n = 2) sample(lines, size = 2) } vroom/man/vroom_example.Rd0000644000176200001440000000135714142512075015342 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/example.R \name{vroom_example} \alias{vroom_example} \alias{vroom_examples} \title{Get path to vroom examples} \usage{ vroom_example(path) vroom_examples(pattern = NULL) } \arguments{ \item{path}{Name of file.} \item{pattern}{A regular expression of filenames to match. If \code{NULL} all available files are returned. listed.} } \description{ vroom comes bundled with a number of sample files in its 'inst/extdata' directory. Use \code{vroom_examples()} to list all the available examples and \code{vroom_example()} to retrieve the path to one example. } \examples{ # List all available examples vroom_examples() # Get path to one example vroom_example("mtcars.csv") } vroom/man/vroom_altrep.Rd0000644000176200001440000000322614142512075015173 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom_altrep} \alias{vroom_altrep} \title{Show which column types are using Altrep} \usage{ vroom_altrep(which = NULL) } \arguments{ \item{which}{A character vector of column types to use Altrep for. Can also take \code{TRUE} or \code{FALSE} to use Altrep for all possible or none of the types} } \description{ \code{vroom_altrep()} can be used directly as input to the \code{altrep} argument of \code{\link[=vroom]{vroom()}}. } \details{ Alternatively there is also a family of environment variables to control use of the Altrep framework. These can then be set in your \code{.Renviron} file, e.g. with \code{usethis::edit_r_environ()}. For versions of R where the Altrep framework is unavailable (R < 3.5.0) they are automatically turned off and the variables have no effect. The variables can take one of \code{true}, \code{false}, \code{TRUE}, \code{FALSE}, \code{1}, or \code{0}. \itemize{ \item \code{VROOM_USE_ALTREP_NUMERICS} - If set use Altrep for \emph{all} numeric types (default \code{false}). } There are also individual variables for each type. Currently only \code{VROOM_USE_ALTREP_CHR} defaults to \code{true}. \itemize{ \item \code{VROOM_USE_ALTREP_CHR} \item \code{VROOM_USE_ALTREP_FCT} \item \code{VROOM_USE_ALTREP_INT} \item \code{VROOM_USE_ALTREP_BIG_INT} \item \code{VROOM_USE_ALTREP_DBL} \item \code{VROOM_USE_ALTREP_NUM} \item \code{VROOM_USE_ALTREP_LGL} \item \code{VROOM_USE_ALTREP_DTTM} \item \code{VROOM_USE_ALTREP_DATE} \item \code{VROOM_USE_ALTREP_TIME} } } \examples{ vroom_altrep() vroom_altrep(c("chr", "fct", "int")) vroom_altrep(TRUE) vroom_altrep(FALSE) } vroom/man/vroom_altrep_opts.Rd0000644000176200001440000000104414142512075016234 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom_altrep_opts} \alias{vroom_altrep_opts} \title{Show which column types are using Altrep} \usage{ vroom_altrep_opts(which = NULL) } \arguments{ \item{which}{A character vector of column types to use Altrep for. Can also take \code{TRUE} or \code{FALSE} to use Altrep for all possible or none of the types} } \description{ \Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")} This function is deprecated in favor of \code{vroom_altrep()}. } vroom/man/figures/0000755000176200001440000000000014142512075013634 5ustar liggesusersvroom/man/figures/lifecycle-defunct.svg0000644000176200001440000000170414142512075017744 0ustar liggesuserslifecyclelifecycledefunctdefunct vroom/man/figures/lifecycle-maturing.svg0000644000176200001440000000170614142512075020144 0ustar liggesuserslifecyclelifecyclematuringmaturing vroom/man/figures/logo.png0000644000176200001440000005427114142512075015313 0ustar liggesusers‰PNG  IHDRπ·ΊšOgAMA± όa cHRMz&€„ϊ€θu0κ`:˜pœΊQ<bKGD ½§“ pHYsgŸRtIMEδ 08£ΩŽγW¨IDATxΪνw|[ΥωΖΏη^I–δmΗ±γμ½χ ‹°WX J₯@)-ΠB)-έΏ–Q(£Πe”QfΒ„$d|αm˚χόώ8’,Ϋ²-Ϋ’-Ωχω|”ΘΥηήηœχ9ο8`Β„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ IΡΥ'`"9qγz‰>2χŽ3•d„yWL4ΑMdθ­ _ά3Ϊ|\’ ζ1Fqu`6π ΈXϊ$rςΐΌ&"‰ 0ΈψΠ+ψΩNΰΰ `hC“Θ]στ`4"noΰΫΐ΅ΐπ(›K`9pπ P `χ™Dξ2˜-ίCA^'pp#0ΠZω©xΈψπƒ9wΜVοaˆ’soζŽ6ξͺxxSwΜΦξ!ˆAη6€ˆx2€€%μώ <Ž©;f+wsάΈAFήδΞύ0¬Ήίhjλ`Λ6dš † F€YΑhžΘQυ±4ΰή±ζ#–H˜­ΫΡHηž‰2—’+ψό°crεj8P‚Π449yτν£ώnaDφοχ`κγNΩ²έmΥΉB(R8ˆ\± vμBψύΤ?μv9 9q"?/ψqσDλckC›™DŽ?ΜνFh―Ξ-―€Υλ6#κκˆώTwγΗ ΗŒBd8[4«ΑΤΗ ‡Ω’έΝψscΉ7#W―C”WΏh퉐Šψ…½‘“Ηw@χšDξ0ΜLaάτ…„œπŸνΣΉ νO‚έƒϊ›ϊΈ+aΆ^Š’έ:·ΉbuΫ^tD Φ†Ά3‰ά>˜­–bH¨Ξ A_‚ΨžSwΜΦJtŠΞ · †ΐ‘ZΨzŒXMμ}6 ΈΟτΗ³•RρπηΚЈΪ ΘrΐΉ“ΰ;GA–ͺέπΕNψγ{PZKμOKP쏜bκγ„Βl‘$F{p-θ\CBI[tV 7~8Ζυ—ς‘(*κΰ†—ΰιe*σ¨M0υqΒaΆL"ŠΞύ>p ρΦΉΐ„bΈώh8q$xύΰ΄AΏπ°r/μ*ƒ’,ψΩk°t­η*5sS'f‹$β s)―ήΣVtnί\e*_4¬ΊΪΌ8Jͺ`Λa¨¬ƒ>Y0}$όψexδsΪΔMψ’δ€ νΧΗn7<4Ω|lC0["IΠY:7ΣηN„kfCa&xόPœ­Fί§Ύ†‡>ƒΓ5πό•pά0ψt;|²rpχΨ~„Ž=5¦>Ž+ΜVθbt–Ξ΅XΰΨaJηN(κ\'d€Α[αž%jΒΚ έοΞ‚ΡE`·ΐΩγ!Ϋ<o£νft44ΤΗδη©+hE?܏`-¦>Lw: sεκuН;ΎŸ«t/v«Ήϋ+α§―Α’΅ΰςPOL ½2ΰΏ—Γ1ΓΰσπίPγΧΦB₯;NΠ~}ό(πoL}lΈ³ΡˆΈ…ΤΧ‘ŠIηZ1κάβΈς(Έx*ΨtυqΏlpΨ`Λ!xgάω©iΊ/«wœ nŸς_: †υ‚3―wŸQ8ς|¨°Μ6θγΏ/ςΰΎVΏΊg]m#‚ΌιΤΧ‘ŠΏΞ΅ΓΒ‰pνl5εκάl»š€ϊr7X4˜ήnω4ΚH5Jωl8c,¬Ω~k¨—ΏΝ>₯`κγ6£η\i’‘Νƒ"n«:χ@‰"n[tξ1C•ΝΨWιάόt₯sίΫΛvΓ¨B˜3ηΓΖƒpοΗπο₯ΰ 4Ϊ_Ќ~π|ψj7,ί£f¬'υ…s {ΚHάΣΣ}άΣόΗέ »ΜεQ¨Έεψκ\”‰=¦Ύ4œΤΉfΐ7ϋΰžΰυ΅Κ~φ 8v(ΌΊVEW3 ξxήέ@“QXύsΰ²Κ?Rwοl€²Ί7^½>–γΗΐ˜‘ˆŒtS7Fχ½².DδΊB$Jη† !Χ OFτ†€‘ΜεƒΥΚ%τŸep° EN ³‡ΐ)£`φ ˜7Ljάτ2,^εXFΒΏ.†ΧΦ¨ΙγGͺYμ›_jO'4fϋτρ ”ΈΫλγξwE]ŒF:χLΰβ­sA~~όδxε―ύz\ω ¬ AϋΚHƒ7±EπΤrψf―2‹ούvDρρZ4θ›­ΆΉαεzΊxjY³;‘0υqTt―«ιBtŠΞ ’ρ67žΎLιΪέεπ·%°z/QΝβγΥ¨=\2U€=Λw7έf ‡.€Eλΰ‰/axo |‘FόNEϋυρB°¦»ιγξq]ˆNΡΉΑCδ₯ƒΓ %Υ4όMΊn>Ξ™ ˆω›·α7ouvΉ0 ^Ύ FφVΡW―­…ήπΖz¨ŠβγΝ²Clε?ώώ\ΫG™ηO|Uί§t*‚Ν κγ±±ιγ]¨ψκn₯Sϋμ»ΡΉ‚qΛ1ι\вα²ι0,Τyam <ϊl:ρ[™9>άn…gΎVa‘χο°Βus`ΛΘwͺd« ƒ₯DᏠ8>Ϊ  šiVεRκ2ΔKKΈoLjR!5ΟΊ‹EηήΜ Ξώάt;œ=›«t¨Η―2ƒrπόJΈκY…€ϊξ’i0΅œ< ~ς*<±”¨£pq6όύ<˜5^Y -Un§ΝG’˜ΕδAA:μ«„yCΥ¨όή&Ψ™HwR¬ λA}<1f}όJ/!…υ±₯«O •EηήœJK:Χ€ύΡtn QTΊs‡Γ σ`JE,G0ό±Ξ§FX‹ߞΜΣPR©Θ}ΞXΌTͺ\__j ΄ςƒŸͺNΰGΗ*Ώο%OBYγδ}»KU ΘΉαλέ°|/ψtyEΔ„”)‘€ TΖΤφ]ˆ=aδπz}¬D³σΣ€ΣQ±ηa}ΊΏ©DδΤ9Σ.Dϋ}ΰbbΠΉ«ΦΑΖ6θάΡ…jΔ=mŒŠv²[TbύšJwΦωΰΖcTšί—»ΰτ‡‘άEψαžΨfRD^0~ό |±¨£pQό`œ?QΝ\ίχ1l>εΝψx{₯«¨š:βJΩ ρ"ΪI‚Νη&ΓSF―κέτ/[OΏ² d»b x14 ~ΝJ@³βΆ¦γJΛΖeΝΒ§§‘žc“C'gΓρGγνέG΄R €φ…Ž}O ˜ΥΙ†]ˆ›ΦΛΘ ιάοC›ϋM{una– ˜ψφ4„aHe6‡?W³Ώ* ? ^ύ"νΚ}πΩvx{CύώJoΌΏ~Ή(¨S£θΪ‰ύTΌσγ_Αϊpιt5;}ΛkΚ\o‚6Ί·Ϊ! Ύ2|³g ‡%&}όwΰ%Rd}§δ=³.F‡tξͺ`½εXtnœ5^Žኲ”~{n%<π1¬+ Dνξ˜apυ,8} ΌΏEΉ€Βf²TιyN•„pς(ψσϋ°§ΌιΉθ 2ΰ΄Ρ𝙰·B₯.ί› Xη¨ ύJΧ³πλ?S\±‰άΪ2λŽ`χΥ Χ_RrΆπδ±ΗΚ7ΛiγΫ¬?|Όf΅©‘{`Ά}»t.jΦwΦψα10-BηfΒ[TςόΗΫΐο§A—! e5k°ši~uΖαΩc‹ΧΓm§ΐοΞPz΅ΉQ'€1EpώδΰρΆΒδώ0{°:v§@ΐψ½K8aΓMFΠxΕ‰Ÿ;%ϋaΔ0δ€–ύΗ!}<‡`|΅–ΔϊΨ$pΝθάK€όhΫGΡΉΤΥ!bͺ₯lΐ‚Iπϋ3Ԍ―!UrΑϊeΎΎΌjά(β6ο5ΨvΎχLξ??QEIέόrύΓo &¦~ΉHiΫΉCΰυuͺ“hΌ―₯;ας§ΤθΑΨY KχvbΓKΘvT&o‚Π¬ ©r<«Χ"vξFŽƒ;2Νtp9ΐ5ΐ)† ΗWο»iƒL*}άγ άhύάBΰ2”?·5+7l†˜un#μ(Su§FφVfτ½ΑŸίS3ΗZœ$ςϊ”‰{Λ πΩx{£Ν.Ÿϊ>`ΐί?VΩCO| φTΐ»›‚IϋΞΡε…‘…κ<^Z₯f²6AΥ œžΚ„i9pΩ²ΤΑƒTU!>ϋΆnGNž€lΕ<ψ °€>Tί΄A"%άΫΕDξ±ΎqDΤ_}ΫtξΈu…Ϊ3±£ΑΊJ£τ«šSίμUΉΊ1‘G¨,£+ŸQd½fΆAύΌώχšG RΡR――SίWΦ)wKγ}­= *P"c<~ νΣΐ―N‹ŸTΑ­B*½š>XιάZ―Ήa(sΑυ/Bfš2§w”*ίqrA’ΛΔε%4+^‹#LZ«ΟCAυ.ϊ•m€wΥ2έ₯θ†·5ƒΓ™ƒΨ•7V,/ΖζrΚxδ°ΨτρB‚ρΥBPuΣIž”x"wkί°^’Υ·a:p*?·eλSώά«‘$F# Ξ£ͺb ΜSΥ/rυώΨΥϋUdΤζCυ †Ÿξ€ΫOQΫ>ώ₯r%Υy[8^p)”F©πΗ>ΩπΨ*£hb_•©΄·Ό~Rͺς°_ξRqΜΙ™H[πZμ8=•Lέ±˜Ω›_`ΨΑe*šΛπ…c „F=“ψbΘB±|χB6κ#§N€ΎΕJG™*ΐ”>Ύ€`|΅#­sτqrΜ…'όΉs©[nџ{ΰ`Ϋςs5M%όpΜ¨ˆ›γ€l‡ς«.^―κP4RΉŠ.žYFΈϋΘHƒͺΤLpyόρέ(ΟIπr&υU‘”s‚‘G?W…λ~x dΨΰƒM%υϋιχDFQuΊαγ–7Ξε¨ν―ΗΝο‚Κ‹ωΟά?1wσ™Έϋ]l/Ρã룑ȼ΅pΟOΏ £N•ΣŠ0ctώAƒU+6/y+ΖWk«CuΆEδ$Ό₯CŠ[n»ΞmΑC /PTgŽS&–MWdΪxHΉ…^ZU΅π³ST΅Œ77υθ eς†φ5ΆόηREπϋ>†ίΌ1[$_Ώ\Έz6\8YUˆΌ{‰ςυ^;G™ί/¬„g–«I³}•1šαI‹αε–Eη0cΗq'0€OOΓcM'Λ]Φf?³TΪσY1x>ύެ£½J:ο;<η <…}„h©'PρΥa}ϊ0ήDξ6n”Ÿ[„ΉΧ‹ΞέΤΉ•±ιά^™ͺNςε3”Ύτ*nΉΌNειώki½ 0(.œ’π¦€sŸl%<ΩΤ7n=ήΫ¬Fδ*7ΌΎFœY8’Jp¨σ©PΗ•{α‚ΙpΞDˆρΐ'*vωΈαͺcx}IΰH„Lδφώ6x›ΤKjΖN•ϋ.»‘F’εŒ―‚ψΖWw 7Š[> εϝNY*οΆΕπβJαΤZHŒMWξžωcT–Ρ]οΐξˆjΊ€ώΉΚέtΙΤ`Ζ΅οkζΐQαΕoT5Θ‚ Uvo…J:¨t΅~όd…&άΌψζl})ε‚ά}Ι\«>ލǒƒϊ8₯ά™:7Σ‘Κ¦zύυŸ]2ž ΖΏ΅n_}]‘h¨j•€Τ@UP=œŒoΐόqπ“γT•Œε{ΰΌIκυε.₯sέ>8vΈάn}±;™‘Ι7ΏysΆΌ˜nοd½ BϋJOZˆ?#C΄YχΆΘzΜ[v1nΪ&o:pJO\δEΫ^•Š·eς£Ο`ύF„ΫέJ•‘’•ΞΏ;v•Γˆ"oe.ΘqͺΩίc†)ξςέΔ|Ηw–©ͺŽ7[¨ΕΕ<©‡{ΚΥLυΠ^pη|΅,ΚoίR³ΩSϊ+?οπΑζ`υΘ'/ΑK˜Ήν%–λν.P$°Φΰ+ў쨴CϋEήΗ‹EΖϊ•ψς‹πχRΧ‚q·MΠ˜ς#φ0fώΰΧ,}ΰȎ—τνΥΉϋK+VΑΞέ±εη `Ζ UΌ|Φ • Φ8΅΄¦ίPμ+W©|WΝT+XuθρNϋ₯ϊύΥU§Ž†­Φ–B°V‹ϊό_KΥ(?ΊΟ’&ΗήX‘s»yAZ>=­Σ+Pυ²Q€Υ Θΰv fŸΪt=€πzΘ_²Hd­ψLž‘lEΫP$žMPλ:«cΝ?NΪΗ ½:·¬ΉΊ:WC…?ήz’2ΉuMιΡƒUpμύ«0m ͺ†qΈf „ίΎK6ΣΊXςσŠ£T™ΨΆΒ/5ŒO¨ŽΑλ‡ }•ήύzwjλάΦpέϋΧpΚΪvͺ ­‘tW.M«} ΨDpΊΈ¨ΧΗWspα•xŠbΦΗφ-ΖW'έά(nΉHΚpάςζ~Σ.‚„a½Υβ]šPεl²μj„]Ή¦φ@ƒ{`ί5c\Z«όΐ­"Έδη9U¨₯?w,VΙ #z«0ˈΣΑ¬φΈf0Šͺ•όΰ”†P pp’FΰH„²*­¨Έ#£œμϋvŠAχάF―w_–ϋΎ}ƒ,=q!ώΜfυρε‡β«£9©4πMΒδMGε]ήMΌul”*&ατq*0c@·mƒ½•0s *9σβ*eή"”ΎOΤxUμςΔΎ°΅48₯pœEW·P­³ϋΜΧπ»wTΰΗoη«`Ι–:䀡‘βwΏΛ¨’₯ύ(–ε;‹:55³'„υρ†6ιγ©(}Ό[ˆ¦ϊ8)FΰF:χh”Ξ=…xκά qgV/­ͺ€_¬FΘO·«0Δ ΕJ{ξ­P«ςω"λkπς*ΐρΫωŠx/―Vu“€¨‘ό΄?œΗ‡·6ΒΟ_…άtψγYκχό\Mde;š/εΪΰΣνΝΉΟγ^fG’ϊL”ωœM󝧝ŽEqE;vS}|=αγZΗ§³κc‹΅‘>ξώ½‘ΞƒΉo¬9uΝlUτόχοͺ2­‘ΉoMS™Bύr”Ι\ηƒΎ FVνrn=Y%β?³\ωˆ?Ψ Uuj}²U•ΗoMSΩGw¨f¬―ž­ Ζ½²ZUΚΘuͺ(ͺe»”Ϋ¨ΣοFk“a2ΖΟ:xs6?ΗΙkiΰ΅8pΩ²pΫ2Θ―ήΓ€]oΓδڏ IC%ςζ’Lg½…Λ Ύ[h_'XS7w wίAςΐωW+qQq›υq—ψΗ›$†AΚLΎ†Φt+"?7F« ΅‚ΑηACͺίyΆ&<15sΌφέϊ ’GΎP$k6² eϊž4RΝZeΒEOΐΦC°p’ ΅”ξϋHΉ}NTI+φΒύ«ΥφσαεWN8"Θ* I†·ŠΒšύτ­ήMΆ§έπγΧ¬Α—…:k:5ΆLκ,NšΏΠ hͺr¨LΛνΠ©4Ή·2€Επ”šN@XΊΖ‰kεΊχnχ~%Š€N”ΛF‘8–QU Β¦ΆΖ°mδρ4Τ°i Ύ t΄Υˆ(Η‘BP3fJ(Ύffkώ㕨όγW»Τ„6 F ’QfΣ ƒqΛrϋ.XΩ†ΈεP‹e« *§U‘Ξ€ύUΏϊ_?Ϊ¦\<½3T€UKδ5JŸ6φWΒ­―AοtψΝΚ‡ϋΔ—*jjjxτ¨vΓ­―«ΥfV#τgΫUœvΥΤj IΊ Γ[EQΝ>UleXω†–mdhω&ϊVο"Ϋ]Μ‹ .U‚@ €¦γΥπι6Œΰί†°°=g8?;ρQφdŠΫhl―ήt:Ζα­&z n‹·PPY(βf’ΜfΩh›VΟ« ΗΥPD>ͺΓih‰"o-PŠΥ#/€$sέr1βφ«)σyΉος›dετc„am6Ύz2π0pEWkΰΎΐ4’<Βa{ ·£?·‚ΙμύrTF/ %TgjΕƒηV¨:ΚƒσΌψ Έν ΅ο+R!–οn‚_Όςp¦"κ??‡7Χ«σΈpŠšY~k#x|mΌ–X! ΈzGο~Α›PΉ•Ϋ(ήCΆ»›α gΧ4ΆΦDθ―ΰˆhΗέxΧΤ`Hω&φδ JhΦ„fθU½;ζνCύ e"ηίGf΅FŒΏ³‘δ\ššε!Σݎ2ߏ fΏ»§šθγΣ.ϋ/ύAkϊψ˜&p³σeεͺής¦XκP5‘ΑΉ)Χ—ΐ²=ͺφTc*έΚ-τπg*%otl(iω˜5^ψωI*4ςšͺ9ߝ­κ0ΏΆFΥ«ΚΆ«pΘ’jxϊk(―₯aΔ@pɚGΈρ«ίΗΣ†d5ˆ}d‘Q–Ψ‰q³^N]ύŽ_x« ™ΙιΤ›Ι6κGΏŽτ1±΄“UW'§•γ…ΜλήΑίμ"Έ$b”ν,Uε’ψΉ‡Ιϋδ-yΰ‚«εΑsΔ[Xu}§&pT¬ί„ρΥrD›όΉQZ"?C―­U‰ξCς‘"ΚΦhJϋώξ•w;o¨ZšsC΄5…"ΰς©βι†T.’‹‚SίyFΉ•Ž΄ϋβυj%ΐΠ± !%ύͺv’!ITύ:™ΐή'ΝοbαΧβμ%ΝWΫμ<š ‘™œAϋΜδζ`5NJςυ΄4“ Ή(]Ό—– ŸΆ—tοντzχeΉγζ?Κς9'ŠΖ&uΨ0`ΛvDE9’E/u Ϊ1Ο©&Œζ U~ΧΏ Gͺ£ΞΜννŸ?]Ύ9ιPαjώ8RͺYζΗ…_-V©}3©$ύΞ5:±œΕπ“ε©HΨώΒBΕ™˜ θ[Ύ‰“Χ<•Ό‘Q,LΆΣ13Ή9HZ£”¨Ξ#?Άέ5A/Τ$Y«jMJ²Φ-ΉŸΏg”Ο=±Ι¦IG`Ί‹ZρηŽ,TZ΅ͺ…e;/˜ OQ³ΌX½š%S0)Σ!8},όψXU_κ­u­΄°PEεωLΆΗPIο|YόΡ™sύΊαΗι―Mˆ<€WO£:-;aϊΧπb x|ςέf FΫ,Τθ3Ή9h¨Ρ5/βoP³Κ‡Q‘–Ή4υΫtΑ}WΗ°m¨HsϋIH•Ψ~ΓVΉ6c>‡!ΤDΣ…« ­ΞW“Y±–0κ3—Ί š4°‰)ˆ%·Εέ„uX‘ΩΪӁ 5ΒšŽ5³;„"SΘTξ,Θ(/Pn’4Ϊ?ϊ†φJ_μΘ5₯Φ\Sh}‰"―V±—¦‰hFΐΟN€t›2ϊa %g„šlΊσ4e~gYΔbc)M -”ξΡνxCιΑvIσ»Ι«;Bnέ2|5)©΅¦sΔYH©³ŸΕΦ²­${†§Š©ΏfxΐC }©ΙRηK’fΉC&|G#-΅Œšlzg“2‰χWΑ)£TTUδ ΆYaαEτgW¨bteΨφ/Q«ύ½ΌZύξGΗΒ½ΓαΦFο$*”ž˜…:CΨ«§!€Αˆ² ·c1³φ.apΕf²έeΨžΰv6Κν½ΨΤk8™žΒžμΑυΪ!Π§z/Ην\ΜY›ŸcRΙ—8#&°’…Έ‘° Lω#PθΪBKΛύ[τoSŽΐε˝?LP³ΛO.k8™e³ΐ+k  ]εξΊ}πψW±bιNψιρΚϋʚ຺)@^Dύœ¨ί«§QX»Ÿο/go|†>5{£Ξ;}΅δΊΛV±…SΆ½ΒŽœ<3ξjž{•Žτ€ŸGΦ1λ8mλK -ۈUbžθJθ¨Y䎀ς  –ΙφŒΎ@3 4Γ@“~4TG,€& ά֌¨ “’p©u~>ί‘ͺBN*ŽXQ^*οE“•½b/όφν–g«£cG™ͺœq€¦τS€φ%jκΈ6OβF`(¬έΟέο\ΖΈC+C‘δΝ&⇣t€dhω&~ριϘzΰsήq>σv½Γ±;ί’°φ@xίΙP+VdΡ±ŽF^}9wυK„!Ρ€$pMΒΔΥdaŒΧ‹q²ι1Sΐ(WΠύŸ¨ϊUΧΜV₯UΏΨ©¦ Έ~ΚγύΕ"΅ŽξΪmΫΐP±Μ'ŒPu˜?έ¦>K ΘfΝ­ŽοŠjφΡ§f_›GJ2ΐi[_β€ν―a3όmŠ K6t€…p ½˜Ζ~‡ŠŒ’ΦS— θ•}_)I`„εΑΖƒjιΞή*—Χ‘¦FζηVͺ<ά_ —=ΥφCXt΅^Ρ'Ϋ[Yl,ι  L ::‘$QΎκTmγΠlώOqΖζηΓ>^·ΕA­5“Zk:.[΅Φ κ,ιΤιЇQά€ρS“ΐ(]{λ"8qΝl•τΛE`Ρ”ώΫBpXΰΟΔΰ>Š_%€Z95ώ¦–w°§ΑVnγΪkς]8^]hΑΞ€fay―Ÿ°μ€_5yS–ΐ .ͺ|―_쫦ς~Ώ?ž]&6jvmΧΦ‘BΔ@oΌ¦₯ά©χ4HZΦόZP[‚Ωcvτ’Τ%°P 7Ύ€j5Ÿ?QΕ>WΦΑ%O(~wάσμU•μ ζά“wΪ0”ξύύι0JΜw£ ³|μ52§Z FG‘βf»ϊ,T*§ΠAXΤ=ιt&Rw*@γW‹Υ* 'Ž€{ΟUU2AΥ]φτ°ΩCθψ4kΗwΤF μ…`ΛέΊ-Hήΰα)ƒΚυ •γΨC‘ΪFΝ―?^€ŠΔέ³DΥ™:jd6\§·' τϊPΗN„fƒœq`Ν ~ΠΘ5"4¨Ι€‘jHi:_Ε;_τΈͺŠρΠ*ΑΎΪέα]§šή%K•ƒΘvΐ–Γpη[ͺ GudαΊChψτ΄ξλΘzσ 1aX³y-ΝD  Τ(mΪΡρCχ °Pλ ν«€“FAY­Z‚³'" t₯Q‘–Cπι6άΊ―%,OωCq;–f…΄^ΰμ§F]έό’3 Β›Ί $δ₯«QxΥ^΅”ggB Ι6*Ι7JI—΅$tBΗ‡·°γv|X   H‹kθ£Ÿ~ό»¨OΐχZψΑWΏη;«ξοp£%}qm9jΆ9tZ<7λΫDm[‹κ,„¦4Άα55uέ‡ΐ"˜Ψ|ŸHhd•φ38°ƒ‘ΎMŒχ―a΄o}pΚΊ 5 ‘αΗ‚[Ψq‰tκ„Ώ°ΰΕ†WΨψΚ6ƒ?fώœZ‘ήρ bVφgυoςΉΥπ΅»i„f˜ύΐΩ'h*·1ΫΎM.$Ά\pφ…΄|ΠƒΥλ€ό5PwκφC‚+ά&=Ί!aΔΝ5ΚιίΔ`†Ά3Ϊ·Qώτ μ%Χ(Η&ΫΊ t¦zWπ¬γbΦ[ΗΔο„›!TΊ/–j ‘₯½@M@Ω ”»(|Œ6ަFŒΦl5‰ΫΦ,exCΥFpνkϋΉtt/'ί­}”Ϋͺξ"CΦ’Ε3F€²&‘η/„δ΄-cφž%1Ÿ½%3ΒLΞFSuB₯t- r'Az?Z]ΔΘ–yS•φnΛ"Fέ&c@žQF–ŒηJ±υ Μί°^n|ŠŸv+½\‡ZδΠƒfkpΕI܊RΙ΄jh= ϋΗxL šU=F¨Χή„5cΒ$p Hd~­?8Α•˜ΆS~ρΙOΙs—5K^Νf‘Σϋ+½Ω3ΉΉσ0Όΰ­hX«q‰n{2›Ϋ)Ρ¬5 <₯=O›ξbΤ {\'°@@οΪdz«š’W€5Sωn}ΑšŒ[N„³XͺYδτAΰ―mXΈίπ‚»Dιγτj»6_*3ίQ 5ΫΪψΫ‡Iΰ.†K8q‰-U‚Z %²F–°@Z0*ΚQzθΠ έ*tΘωAπ°”­χA5ϊ·š²"jwτ,“Iΰ.FΘΐ-ίQ3πθv 4¬v{‘"nZ~p€ƒ›ψ Wθκ|όΥj«#•­ ¬ ·0EΑ$pΓ₯9ρЦιiCΊ¬Ε.UF†K8©Ρ2π·α–ι2@AZ)Ω£!³XΉ_f&·AβιφϊΤΓφ"œ{άƒ`Έ‹αFEg‡™ιύ‚yžOη[KQ „tY @•–Ε}0Km3ωΠ~k-γπ [Τ}¦ΛZfz—r‘λΏΜΧ“?&θ€M&βF €atΔ©PT:Ξ0 άΕ¨²dη»^ΰŠΪΗο[uc*Λ9―ξEUχζ=ϋ‰<q=_Ψf…gΙ NvΏΛΕg™γύŒ,#1―ΈB*Ϋ ι0ω|n o@a„]sBΦ»ι'w4~O Ÿ·r)Ϊ¦₯•šƒIΰ.F㏕_Εiuob‰qΊήΖ!.q=Γqžωsζ-|˜vσέ‹9ίυγόk±ΚΔ,n–( d μΰN$<έϋrž?u‘Z’TͺιÏnψ‚βόͺH\pυ ‹αW…ΤQΕγ,†]ЌVΓ‡ρ^—ποtΓ‡nαύkΑο,nψΓΩ-Αχκ7ώp±v‹τ‡‹Φ… ΉkΘπΉ)Γί t)ρX’»Mw1¦xW2•ενϊmŸΐώTω3*΄zβ—e”ŠΨ§χεή‚³6m\ϋFς‡F΅ς…"Τς' όΡ‘εP‚Ϋ 4)Γ„Arκ‘•ΒK©Β„EͺƒiRΦwF€‘c ή]VfθN\›ΐ&½=žΌ;-ƒ˜ΰ]ΝhίT« ꄃZ‘ŽK8©κ„·°γ6ΌΨπ +~,„N Ζ•Ž$’αbΫm™5kΕ.Ο2ͺθΨKΐ Œ#€I7Υd`u€‡Έέ&Mt Μς|Α,ψJf‚ω„/6<" O0­Σ%Έ„3Lπ-ƒ‘A΅Θ€F ώ/2¨Ρ2¨ιΤ g0›ΜN υΎˆŽΐ/,j&β4Z\‘TͺΒ‰i£^ϊΏŽ™‰v`―ޏίfέΞ<οΗό¨ϊn¬q2ŸL:Θ`ε*_Π`Γ‡-^Ι§!’Z\ P€i›ƒŽΩ§O9/₯D/β‘ςkβZ_-) vΙKUQΡ*|αΘ”™RW€Š6:ΕΩ¬|Ρy›­#jE:U" ·°skυ9Ν½Έc;m{£ˆ›Cύiiπ7ˆ©c“γ…Ϊ¨qΑ:ΣOι<㼘3λ^gšχλΈξ;ι,€!ΆŒΝZΛηFΩ7R`9ŒŸŠ†‘V|J­‘Ne ›ͺ@^iΓ+mxd{}ύX\5Ÿ##nη΄:™•ΦɍΞSβ­ι€ύ*Q#hŠ΄…@[‹₯bά.t< Θς€ Τέ΅@PόΫ$r\ρ₯ν(φκύωΏΊŸ Η³,1IH`]ψmΡνLK{›ζmσΓTppξΞρfΥi }­έoΧBd³ω(Q>ΚΗϊΎ-r;΄”g,ΗΝ† FyMΫΖΆε‰k·žˆmσ(μg²χ›Έο;ι ’l½›π†ώlZΕΦύ ?Λyޏƒ™/1]’"L:j€νƒ ;Z,=VςφΖ’Fέζλ-Τ›ο} oΒ―°±Ζ:ž1ώυδeqί€μΨΠi‰ O pΊϋ ξ­Έ‰~V*‰‡ΜδlΤhΫ₯uγT,-“X’&ΔΖ‘: ₯γ@‘=ΐ4§;ˆZ‘N‰^Δ$χ7q7Ÿ!I άQψdβ|+'Ίίγ‘ςk( ŒΎAˆ$i@/qσPfkθϋxx₯  4ψc’…ώΆΓ‰Ό‘ηoFU(}l’Έέπ³–1ϊB7$p@κT²ΆIΎUΡΙ2“3©7“³PG‰(&'PD Γ, TœtՁτjΗ±eπ:ϊ=tεψA5Ύ‘ ^°ΫΨ‡•J#;aϋor#$ͺsP£mΚ —™ά†ƒƒο#Γ-·;QIHg·½ƒϋ1C6›…OΨT½¬fΜc{°<π­ !ΗοvφΆ„Žΐ‘ΦΠL. ήLŽ,–ήΑ[–ΰ1#₯‘FΤ(Ϊ^H”ιν@%N˜ftΌβXΐcιWQ8Θ±žΘ’•H:SΌ+(μΓ)k)μg›e(~,1.Œݎΐ™F­Ρh­‘Θ\DωΌ §G€)b‘ˆ’IβΜδX £όν@‘―£S:υΪέDψ±π_ΗE,rœώ~%Ί …@Γ`’wwTέɩζ]Ξ³Ξ‹8¬Π'p ηΡνμ“V<2θX•c©`tΪΖ:Φ1ΨΆƒ|½]¨d²έ;„UuYλGy ·ωJpκ.Žr~Ιι}ήP€ν,3Ή­(βvd™­β°^ΐjΫ„phUι—Ω¦sYή“œSχ™²šz–Ω¦sVέkq=nI`·a§Ÿm/δ<Ο9Ω/1ΞΎ–l½2jVO΅‘Ι:χX^\Θsε²ΛΧ°@q‘ε 'fΎΗΕ9Οrtϊ'dYͺΒΏMZ‚θ¨Ι­Žz-ό‡„™n‰m–‘μΡϋ·ΈM™–Η£ιίE'€ΖsŽ 9Υύ6ΏFνv sFΦ"ΚŒ)Ž!›^2΅jf¦/e¦s)η<Λ]oγͺΣhΫΕ‚μW8?η&ΨWcΥ|ΙMΪHΤδVΘ΄oη>Άy‡ςœυBD–Δ‚VιΓ‚ͺWlΕ§Š”c`Α~/X4x―,€|―Λ@ψ½&ϊχΑοBϋΣ~ͺ§ŽŠ@—ανDψ;Q£Ήρ Έ‘Φ[ΖΔΌ,l¨dν›φΣψ$νhNpΏ·[ένάΧΊΏ”t½6vΒ·™δό†φΏšΟjη0ΦLξ€4“cAﬔ‚Ηkΰ.ΗmJ.4ƒzR„,xވ8‘Δ ‘0τyψ;)ΓΘpAΉΠv‘B'€x{ ώ0Ω­ψΒο#; MαŽG' :Ÿ`§d‘ώ†ί…:)κm—&=θxΞya›~/ΧrωCζ­Lπ¦ΐ8œ ·9ω&<€ O»«σηκεœ‘½¨ΕQ»'`―?ŸΤM_λΎπƒκ—Ό†OZρK•RiΘΰXΨZτ\, %2υj2΅jμB­9ε‘iT™T2;υΧV|˜vweέΖ+ŽCΦuxݎΐqA%m$zιGψWο„ ΰ•6܎ΛpRk€Sk€SΘ ΖΘ ΪΘ€ΖΘzΉ '.ΓIα N:pv<2 Ÿ΄β•6ό’j/‡C‘υφo‰sυZ&ΨWstΖ'Μp~Ε0ΫVς-₯ͺc^GY mž‘,uΝdIΝ±¬u£6žπΞΕ@γατkɐ5άZυ2dM‡φgΨDT85ClΫ›~ΡΪ1+λ—R“5”ςYg8št΅FΊκ™υB°3}ο2œΈ€·aΗ-νxŒ΄°Eΰ“Vœš‹ωY‹Ή<χ f¦/Ε©Ή8μ/`‡w0[]Γ¨ d£ag)£Ÿu/σ2>fAφ+TY|Q;‹'Λ.cQΥ*Ž Dφˆ4ώ’ωSJ΄"n«Ύ‹Αώνή—I`mCŒΦ‰†‘2Κ8ˆb*Ά‘#HΏTΕΠ}ŠΧ¨οꀃ:ΓA‘AΆ^Ι4ΗΧTΩΌ^y&―T.`yέT*9hΒΐ*TH™_Z τΓΜL_Κ9Ω/q\Ζ‡œω>οVŸΔϊ _Ήf¨œrΓΉΆ>¬ό+ύ;¬°Mα‡5ηΜΊΧ[ΤΕ’™†οΉnάf€Qη’ Aˆ|Ꭰr=±ˆϋζ6μΓΚ^_?–ΧMεÚγψ΄f.›=#pφ„°Η)]δedΚj¬‡OX©$‹‘Σςi³μZγ {ΖΚ€ΚΗ΅QOΰ›h° s$N5XαšΒu{䈿χυ½ssώ‡C«kήύρYž^Ζ99/q|Ζ‰η+.ΰ Χ¬ΔΝbΛζwΫ3[J~ίGύΝm|ƒΣ0GίCi Ÿ›χ·aη±ώWΥ“·HΞΜ~ϋ]ΗrΧTξ,Ή·ŒRΧWͺmΩvru―ςΰsx~ΰ\ϋ<9zE§Ί!{%*w?Ρs[%*%0Σœ"0ΠxθΘχψΚ5ƒΏίΜdηΚψά; §e½Ι― Γ³σJε‚VύΞ™Z5σ³σδ€Λψί sΉ χy΅ΪNy–Ί7C ˜ͺΘ8ŠζEƒ ₯M€6ΊGρpι΅\‘χ8§d½W²$—η=Α±ιKψλα›9μ‹!_ͺ(ΐγ3?ΰρWπδ€Λ˜κ\žπvθž“λ…*Π6 Uγ)‡ζ{ӎΔ;Kš7ΝMΔΑSε—’ ƒkσNH±Έt½– ξe»goT»Ό’ΰuœ“σΟΊ€‹rώ›σ ‘ϋ8²JF?`rπ՟z'•lεχmiηΘ₯P²©―?•…εM2' ‡ύΌ^u&gd-bXΪΦΔDΒLηR¦;—ρBεωxŒ΄6~ˆm;φ»Ž ξ ‡qΖ©? Zq T%£7ͺE[«d΄…ΐ‘r3‘5°B³Ϋ~ΐB•e­Γœ‹'¬qgΏ―˜Σ3ί@FΒ:J‡^Ηό¬ΕόωΠ-μςdDZΫ+όεθάΥη62΄ώ|θ–θ“b@jΈρB`E¨Ρ―½+„~+ϋΓPΪ:ς|@iι4”ΉήΨΔ'sΜD+\SΘ«k_—p+gΊcnΓΞχhFΨ7·λxQΗ­…@"ψΣ‘Ÿα•ρ«S”ZΕ₯:P#_¨tk8A4Œ3Ρ>HUΌί&ΌJ&Ίΰ“±ΪΫzˆ»Šn£ŸmoΗwF²8΄Ψ i'’Μe‰™έ ­!4eE™ΎcQ³ΨΥi΄g•ƒΠώ{Ρpq1ΡΉΚDhΒΐ©Ή¨62ρΛΔ+@―΄α6μ8΅6DΩ$Lw.γ½ξ‹{)95π@ΤΓΟ…ΐZC*Ω!52—£LΰŽΤYΆ’$@e‚―#…qΠ_¨|ΐœτΟΘΦ+H$‚\½œ4αA ιgέΛzχjtlzbΛf–ς¨0rθkίrΙyσbΕy|νšΦ!/EςXPΏΚAgϋR£I·’\T‘I{―+΄M!ΰνκSΈzΟ?(²–₯U… <Ιρ ·τώ3“+™ΰXΝΚΏΝ_r-ε‰{FlφŒΐc€12mS\w]h=ΘUω±²nrΈje{|!YLM eΞΗCΏšΈYH)ψΌvv85oΏ―˜ύ‡ΏίθΕΧiάXp/ύ¬{1Πψ’v«zNΦG‘΅D8žΟ€„s²_βεΚ…ΌS}r»w“œ8Ωΰ„Έ,i’<’ΡΛ•lVΦMn~[½ΓΈqί½\ΏοͺYΌRΉW qμ|}x«κTNΞ|‡Kόω½-‡x€5|;χ?ν^3)yGΰdD™τn:V \ 6•Ž€’&‹ζGΑΪΕ’ώ}hΖS ΡD}‘s!"κ'GΌΧ0@Π°˜Ή¨―Α „?‹,t]Υ‘]„ϋ}Εμτjυ< 4φϋΤΘόiν\>¨9ΎaΰxAΐ‹•η±Λ7Iφo¨“œ"NYhΫΕύύ~@­‘ΞKη΄ω>˜ŽvΪξ>Š„€’š".[ύ$BΧ"V5I-’~₯ƒΙ?ψ; ώίGώΩAX…/ό»P)αΗ&ΌM>ΧE@ύ&Έ?‹π‡ΟIΑβθGžƒš¨ο”BϋGθοΠω}XseΌ65kM ƒΏΎ™£œ_Ζw„ΚdΰΘυΤ~vΰOμχσƒ^χ‡7‘R„Λΰ†‚2Bυ±ƒλE¨υ#"–θ WμDGJA^–#νκDMNJ­Δ!xyσBΎ>4 iˆΨC6“ΑΌŽ\wΈQM)!Τ‘βθšf4θˆ"WfΒ$}κ(jŒŒΆ»…|Rs49όSξ*ΊMUΑŒ*ό9άQr'›=#5;ώΧC7σzε™ κɍ˜‘οθ²~˜€ΤΓο ©5ψή@kχ’Έ&;€Nσ0w}‰Ο°RγΝ Ζ—A7ƒZ_zψες9qϋνΈv<ώ4<4|†Ÿa%`¨"θ†ΤZŸ₯GCΠ ΐy˜y›•Ώ‘Y»Θs”‘‹>ΓJi]>;*³ώΘ6— Κ“ΥΎc΅§mΡyΰΘυτΆβΖ^χ†KΗΆΥF&Ώ*ω «8·Αη₯|Ύ¨ϋŽ:©σ5 ά °h~ΞυBΤΥ‘ͺGHΏaΑoXπ6|+ž@nΏ—ίIίΛη€Φ—&|΅7“jofΈˆμ \>gψwnΏκΌ† oΐ>NƒŽ t>H³z˜Ρο+Ξρ' |Ÿ‘ΉΫ°λnU—ΩŸ¦"’t/½] PεΝbΝαρΌΉύ4^έr6뎌U£s‚f—αδΧ%Ώ¦*Ε ώFŽ₯’ν“€}ήΎάQr'O”]έ΅“ Q#˜ξ,΄0Z !•ΎΤ‚3‘m9wRΤ°β ΨΒAίΛοTδφ9›ΏΖ›A΅Ou CgώΕœ5ό5돌εŸί\ΝWf°£r0Uή,ό†‡₯ŽηaFηo`NίϘΣο3ξšwΧM~gΦ_ΒΓί\ΛΆ²‘υא Τιόαΰ­¬ͺ›ΘO{…™Ξ₯ͺ=cX¦₯Ξpπ^Υ‰όιΠΟψ¬vNbN0A0 œŒ1~ΦᎿς9[iJ˜Φ:TG2σίέyόζj>ί?‹ζgXξVΖτZO‘σ VέG­/=ΥύωxΟ<ž^χ-ςμe,ρ WMxŒŸΞό σ‡,ζΏ.β‹ύ³ΨP:šƒ΅…zBΘμΗΒk•gρ…kgd-βΌμ™β\AΎ^Ϊΐ΄6ΠpNJόE|νšΖ ησnυIT2“r”m ΙWΨ?W:Ο•γ,―₯XSv#Ψ\6‚ίώ ^ίz&£{mΰ[cŸζΈ2 k7N‹«ž€ C£Β“Γϊ1Ό²yΫt.š0Έiϊ=|wΒ£8luΈΌNvUdωΑ©|Ό{KχΟdkΕ0꼎ČΜR%δΆν`xΪŠ,%85~i‘,ΗnοvzqΐίΏaInβ0}*Ζά™4)μnΨDCψ`Χρόθ½»©υ₯σΣ™α‚QΟ“λ(oy,DB6”ζήe7ςόΖ 8{ψ«όα˜[)Κ( o' Α‘Ϊή¬<4™wwžΔ{;OdSΩH<Ύ΄„‘ΉΩsN΄@`Σ„6Qoo?…kήz„‘9ΫxςΜ˘Ψ{•ϊ΅Μ½rΞίΐ}'έΐQΕ_ς‹~O…'‡OΉŽ’τT쉀0γ §fΎΕ©ƒίβ «ΟχΝζεΝ yΧ μ―.ŸOΌ«»’K£sgώΰΧƒ€o‘η#0˜l}žήΪ¦nάμI+J¦pεβΗ™·™ΗNΏŠ‘ων+! ‹“‹ΎaxξVZω=vUβΔAο7Μ ξ;ΓVΛθ^9sΨλœ:ψm œ‡9TΧ›Ί^Π ³ΨI }‹‘ϊ5m “ΐ&@@y].׽󾀍Ÿ~%CrvΔ%JδQ˜qˆγ.጑‹θ—Ήύ5ΕrυNΨ2Ÿ)l&3˜ΰ?λΎΝ²’ιόnή/žΏ%~±ΕΎ5φi.υ,[φc6•ŽŒΙ%60{7Οψ+oœ:Ώ=ϊvϊgνIž ΅$‚Iΰžϋ«‹ydΥ5,ώ '~'ξΛ¬Ϊt/?š~7R ώ΅ϊ;*x€5‰< k7·ΝΎ‹œΟρ?0IάΙI`z‚’στΊΌ½γJj‹ΈjΒcX- EŒ #ς7sήΘyyσBφUυmΣJH8ͺο—όηŒosΕψΗ±ˆxδvv$!C8Ώ9@ξ3σ0€ίΦi βWOΧ„‚ΟoeΡΆ3˜X°ŠΙ…+ΆΘΉ’…#^¦άΛηϋg·+&»8s?χžx#?œϊwlZbKι€ ’ύ$tp\Τt‘1γ0Έί„Ϊ»ΑχMWŸeχ€€ΓVšΐeγžΔis%ŒΐH“ΏžώY{ψlο.ύ\»φ‘•VΕoηݎEσsΟΧ7α3:R¨,υ‘„#0ΤWYxi½ΐyδ>φ³ιΉS’q„€=Υύ©πδ0‘wβJΣ !;­’ΡωΨP:Ώk Eμ'έZΛsοδšI„Σ{*’”ΐΡ$΅e8dίiΗuυ u ͺν @qϊώ„KΣ ϊgξαp]u~Gϋw$ρsοΰŒa ¨Ζ‘BH!‡`€ή2ZaWŸLΚ£Ξοΐ’ωqZ]‰'‚€Μ΄jΌ›2};bDIΘw”ς»yΏdD^ϋNR)H`lGγœ>‘δG+$ΡECj* Ώΰ XΓU9: γ Φrλ¬?`·Έ;εό“ )J` ΨΟΡS¬›£“ΝΎͺΎΈvu§#_AbηΪΛ H2w^β§$t’i«V„‹Η¨)αό‘/pΚΰ·7—ΔHΎYθ6ύ(eFvvυ™$ͺΌY\ϋφΓ,?0•ρ½Χ0Ά`iΊΏa‘ΐy˜Γ_‘oφ>ϊfξ#Mχ°₯|8Η ϊ0‘ηδφΩΩR>œAΩ;qXκβΆίτ΄ZΎ?ε|°ϋxͺ½™‰nΪ€B X‚Θ-'φ"q=ΆW αݝ'QζΚckω0^ή΄0œΏ+4Ι3λ/αΞΉw0₯pC²·σωΎΩ|w⣉›Υ°·Ί›ΛF0Θb4݈߈iΐΡύ>α”Αoσβ†σRΪl+R˜ΐ&š…€•'SαΞiϊ0 U%σσ½³ΉπΥη8~ΰΤ|Ίw.{ͺϊ30{Wb&„,Ωs,nΏyύ?Žϋ1Φ:ξœ{ΥήLήΩqrΈδkwG χUd5@΅*λQ§7τuΤ—–ΰ—θΐ+N†`yΙT £…Ϋ+ ¬.7œΗς’©μ¬Δk[ΟJ˜rgρτΊo1£ΟWL(X&ΗτZΟ§_ΞΕ£Ÿν1ώα%_μΚαήw†F­**X5_ΈΘΈUWο5a`ΥƒEΘ…ͺ]lΣΌαο,š*(zΪGγύιZ Ό.κί‡χ!κχ‘kϊν„_kΊ±ςBπψ‘ο4Q_μ½Αj ’ακ ‘uq#?Cώίεu²φΘΈΦ›0’γ:\u5g {-ώ£°/o^Θηϋf3¦ΧzώΉκjτpϋγeσŸfδ¦iχπζŽΣ(―Λνφρ>)K`Ώ!y|YΟ­>+ͺ™Ψ*D˟G’’Ας$Ν«CΕΝƒŸ…ˆΪψeΡόˆPsαG4!ƒJ=b»Θ}Y5_xu‡P§’klš›ξΕεs²ςΠδΆΩWΦǟΏΌ…ΏctO|n”€ ‡Gση/oΑ°ρMΙ$~Uυͺ½™8«βζA * υπ+τYθ{ΏaiπήgXΓ…ΡύKψ{Ώ΄°·Ί5ތnO^Hao>+φBΏ^υ²/ή€ϊίo¨—a€ŒVΛ)Z/±|hΘ›εm‘ZΫ6β|4+d₯AfΨ‚wΓνj7TΉQ‹ͺ΅ΗΔn‡8’Bπο5W2py./Τx‘Ζ£^΅¨ΎjΫ “ϊΒΌ‘’ι`pΎ$Χ iΊΪΔγ‡ZΨrDπωvψh›`} Έ½$όΑ¬σ9ψυ§ΏFΧN~XΔν1§5ΨU1›?ψ+‹·Οo½ΌmG¬¦Œ”%pfpԊŽΦŸ8C†Θ)ρGn_½άA2»Ό‚Z/ :ƒPPμj<κ;—Wm_ηSˆΗž@}gΰ7 Ϋ ΖK.&™:l:”TΑŽRΑΊ5κZ4ΘO‡Ή’GHΞ›ε.ΙΗۏοlΈ<$ŽΘB€άϊΡΨV1”›gό•Ω»c_l]¨2³KvΛ―>ω Ÿνc/HYwšP²…  GElA 8Jϋ_dGΰ‡:o½UΠ+C2©/¬†gΎΌΆV°zΏ"+ς‚ω‚’,˜3XrΞD8u΄δΤΡπϊZψΫΑͺ}ͺΣ׊Š'IΈόNξϋϊ>Ωs4ΧNz˜3†-’oΖΎϊŽ£ΡBg_뎌εΙ΅—ρτϊoqΔΥΛ$o‚‘|u‘\ΥWΚ1²Ϋέϊ*7<ρ•ΰαΟ‡j`r?8f˜"uίl‰Σ¦:„*7l;"XΊ–lμ)‡ΩƒαGΗIŽ.)­…Uϋ_ξ‚Οw VοƒύUΰo―^n t-ΐΘΌM;` GΙ€¬έdZ«BRλK§€Άˆ₯£ψκΐ Ύ:0#œαd’7N0`ΪŒ£g₯@aw€yΉRž/EV7²VοάϊΊΰ˝pΦxΙwgΑδ~G+ωθ‡jΰݍ‚|"Ψr›+Ήω8I–]}ορÞrXΆ[πΑψl‡`Ϋπϊˆ/™ƒ&΄Π%K6Ν‹oΐ†Ϋo'`艱z2‚dΝΙF=† F€Πߎœ—'—!EZŠΟ(.Ω*ψώσ]ƒ»N—Μ#±Ά± ο‘xΰcΑŸN#ωλIοFaΏRͺ‘xιNΑλΰΓ­‚]eΑc)ή†==9a,δdGηjWψ8ΰM Ιt”ΨŒL—ςΨ<ΙBKΑž}ιNΑ₯τˁœo0¦¨ύϋ πμ ΑO^œ8Rry’GσΫξ(…·6žF°lwηΜb›θ $X,0x rΚ(*RΟ½lf:¦« »;c&™ωΑev8θb]­~dž‘‘BfυžrψΞ³Nψ ˜7T}mΫΌt˜1LLι§&ΥφUŠzσΪDς@‚Pά9wL›ŒhnΤ ΒVu5o^`Ρx!ΐ'a—[ˆ΅ΏDφ²‘τf΅?Ώ~K°t§ΰ_—H&Ηoίc‹ΤLυύŸ¦φƒ‘½Zήήa…ΡEpφx˜6@M’ν.ψ˜Dξj„tnς¨©0η(DŸήΡς}Ω όΈ΅«MθΠ[ ˜άœ €7w­!}|L’λγΟv<*ψΙq’[NŒnLi-,xLΓi…Ώc΄ΰoŠΌ²Zπϋχ`šΥ]…F:77G=&²yοεΰΰA`#$IAd;pŠΘsiΖΔιγQιR“C2©τ±ί€ο='ψr·ΰλ ϊd%ζ8/|#Έζ9Α.•œ1ΆναRkΐΟ^ΌΉAτΤ’R]ƒ;yτiEηu¨Ή’{€Ο fΐί3Z$nάΠ`”Κ.ΎŒjξ7†„t LΙ’rnŽ€(-9gΫ8ώοΞ”ά~Jβ¨Qξ‚“ΤΧGςθE½#ιΑjψΕ"Α_ &‹‹ ΞνS€ˆ;x Βji‘Έπ%p/π:ΰEά’fJθήΰIGγRΰο(Σ΅ΐeD™θΈπIΉj³s€œžέυώγe»U|υ)£:Ύ―–λ„“GJ^\%8\#)jΗH_˜ [(Ι°Α?>ψMΗΑ6ΝΝQ¦ς¨ˆt‡€Z οVΰaΰ?ΐΑΠ‡‘δ…$"pγ yπsΰ%ΰΰ,ιγPά@©bU΅θr}όυn(Ξ†a‰gΓΜAππη*r«(«}ΗΛΆΓ]gHϊT˜Šβ‰ftΡΊΞύ°)τac←tn|Β7m°ψ†τqθςvΉ= ™Ž<Ά τ±ί€­GƒςGK%ƒσ%]°«ζt`?™iπΫω*LσΏ+ΜE΅;Œ8κά–΄αžΡ"4»—Qϊψ:`tγν5ΐ/aM΅Ϋλ`Jͺ}(―ƒα2œœHdΫ•›¨ΜΥρ}ε:αgJΆ• –νĜnB:·OSΫ y[ΥΉ-!%nΡ=£Eδ…τρ™ΐ_ˆΠ .,Bs―&ή+²ͺV₯”Αμ€Ά†JΆΊ¦^ώ8•€”wΝ—δgΠcW;h‚±βΉ9Θy³‘gžŠ9aΡ[ΥΉ· €ηh#y!FΰHtDΏqXˆΥ u μV•"(ƒ½q"QηS Ξ8.wΒΙ•GΑ_?0έK1‘‘ΞΉ9κγVtξΣ(n«:·%€_h{τρ³£Ϊ©w—Γ›λNΜ$UπD° O~Ίum:gIφT<~‰=Α«_QEβιkΦ5ψήΙ«k[cκαζΠΖΈeΪ©s[BJ8„fτρΕ(qT}ˆΤΗ™ΘΉΉ±λγηV nyE [ O– έVlΟ,ΉεΙΘή*^ω“ν*ƒh@nbΫ`ν°j0€W|ΗΚ!½ΰι’;›μm B‹ξΟm‹Ξ5|pί„Ž΅oJš˜Υ₯ΐύ¨^ξZΰΫ@“όŸ°>bC­³r€œΡŠΨ€/v¨χφ–7ό~c‰ΰ«]‚«ό±•uπυΑ€άΔ‘~>Ψ’b‘ΖΉ£ΐeΣ%‹Χ ΎάAŠΜ–$υώ\&ŒEΆΑŸϋʟ{(τaGFέHt»ξ΅Q|υt”YέD‡k|υΎJ8ώ~Ν‡Zh5 ]Gͺ‚ §JώuqΫscΕΖƒpƒΧΟ•όόΔΔtΑΟ_ΌΎVτRιΝ +;AηΆ„nGΰΕWŸ άD+ρΥֈψꑍτρ§Ϋ§=,¨‰₯\rπΠ½2ΰΥοJfŽ?Ή ·Ώ!xΰSΑ“—JN%±'Ȟ:T=ραfzήHάHηφ)R™B©s[BΚ›ΠΝ‘‘>~ψ„Άθγ,δ»Δp#tnQU(cBπ^©†ϋž(¦MΩB±`ΩnΑγ_ ͺέpέσ‚_ —L­―±+ j2šx‘Ο YyδϋΠφ~#ψΉ‘$C~zΘVι!ˆ“ΞMqCθw$b4 \ƒŠ―ŽZ#΄΅ζCnέ7#φ—ͺZmm1›Ώ9]ς“γΪ—l ͺΰ²§4ήۈκy$eΑΰόΰω‡I= HRKΥY" άhΏQ]@*M/_sR£…ΈεPΫzCh«>uγFZΫv |m­f!!Η] Ήβ(IGΓ:Τΐ^<ύ΅ AέΞXλ6‡ΠΦσθ OJϋςss[BOΈ-MΠV},„ͺ°±krε*ΨwaΔήzR…)ώςdΙχζHmν;ο-‡αΦΧ/―ξαJρFPηŒ[.ŒIη.F™Λ ΧΉ-‘GΰΖυ22J*Ÿτqš€:7lڊ\΅JΛ‚νK+J΅φΡΉUYΨΙύˆy4φΐ’΅‚?½― Ίχά»g„tn‘š [~ξRqΡI:·%τψG‘­ϊ8DϊΚ*δšυ°~’Ά–Ψ[€β8{Όδœ 0‘―$ΟIƒΔ‡€Tfϋ+ዝ‚ηW >ΪͺVyθq³ΐ‰@Šιά–Πγ B‘υυΉšΥΗ!λΰaδΚΥmΤΗA½šn‡!ω0¬—€0SΕPϋp€VΫY¦*fLβΖ )¨s[BrœE‘ΣυqKbΦ\ε ~HaΫ’λl’Qκs%V›HιάΑ–ΣΉ-!9Ο*IΠH‘Ύ>W¬ϊ˜ΪZ’¨t`B€Ξ£‡#œ­λά-ΤΧ‘:ŠθχŽIή˜Όg–Dˆ’oΞ&}όΝjΨΪ±‰φC‚Ӂ5&ŒƒάμΤΦΉ-!ωΟ0‰EίM+ϊxχδŠΥ°oυ±‰ΆA‚Ε CΆYηή|A’κά–:gš$θx›Šθ2υqœΡΝunKH½3N΄Y1υqΡTηβt bΠΉO‘Dώάφ"5Ο:‰Π^}|θ0r嚠>nG’DGHηŽ„ ccΉ‡Q:χ!RLηΆ„Τ>ϋ$B#}| Κlκγx£κά–Π="Ipγ:‰¨˜Κ.Bιγ1ΝύFPηAnή ί˜ϊΈyunq!rςD< ηθά–Π½&IE_\N ϊxνX·ΡΤΗaDθά‰γ`T{u„{’ΨŸΫ^tΏ+J"„‰,ˆωΗΡΆ7υq#uξθ ΞΝ‰]η>l}ΨέFέHtί+K"4£ηLI#! Œ―ξ‘ϊΈ}:χ ”ΉάνtnKθώW˜$Έa}ƒjyΤϋM}Bϋuξ=(wKΫzΞ•& Ϊ««ͺ•Έ[κγ8ιάd[NzΦΥ&BD–!Ϊ‘·ν―·Y‚Σ‰=ΒΤΉνAΟΌκ$BΥΗΣΉŸΜ€ξ©Δ ‘g_}’ QώqχΦΗ!ͺ·›ΞύEά©s[‚Ω I„n­C:79ql›tξƒ({8΄›{Mς†aΆD"Š>ΕW·¬„λs%—>nŸΞ} 5IΥγunK0[$‰E‡ς[ΦΗ{‘+V%>–`΅F¬ŸΫΊΞuΡ0nΩΤΉ­ΐl™$G£όγn¬sG€ΣήͺΞέL½?WιάθΟm/ΜVJ1$­>6un—ΐl­E#}|2υωΗ­κγ•«`oΌτ±ΉrΘ ˜<>f«όΉ’/¦ΞνΜVKa4Κ?ιγοc›ϋM€>^΅Ž΄W·_ηήƒš¨2un`Ά^7@#q[υ±\ΏQ«>ŽΤΉ‘ΈeSηvΜμFh€§‘άN1ιγ`ύκ–υq½Ξ•Ζ"LΫυ0[²"bDN£a}υρξ Έ‰>ŽΤΉ ¨wtιΟM(Μν¦ΈaD‹ƒ>n‡Ξύœzn˜ΔM$Μ–νζθˆ>^Ώ ™–†hƒΞ}•κgΖ-wΜΦν!ˆ’oΠ‚>ŽD £ξ!κuξ–Π‡ζ¨Ϋ90[Ή‡!Š>Ύ˜G3ϊΈ„tξ=¨²6¦Ξν˜­έE_ˆŠ―ΓΟ4Œ[6unΒlυŒ(ϊψjΰ šΡΗDΡΉέ΅ήrͺΐlyυρTκγ«³‚›” Hϋ0¦ΞM*˜wΐDβ«ΏJίK0unαr'asyYΉ%tEXtdate:create2020-01-07T11:01:42-05:00{ŽAξ%tEXtdate:modify2020-01-06T15:48:56-05:00+~IENDB`‚vroom/man/figures/lifecycle-archived.svg0000644000176200001440000000170714142512075020104 0ustar liggesusers lifecyclelifecyclearchivedarchived vroom/man/figures/lifecycle-soft-deprecated.svg0000644000176200001440000000172614142512075021371 0ustar liggesuserslifecyclelifecyclesoft-deprecatedsoft-deprecated vroom/man/figures/lifecycle-questioning.svg0000644000176200001440000000171414142512075020662 0ustar liggesuserslifecyclelifecyclequestioningquestioning vroom/man/figures/lifecycle-stable.svg0000644000176200001440000000167414142512075017574 0ustar liggesuserslifecyclelifecyclestablestable vroom/man/figures/lifecycle-experimental.svg0000644000176200001440000000171614142512075021014 0ustar liggesuserslifecyclelifecycleexperimentalexperimental vroom/man/figures/lifecycle-deprecated.svg0000644000176200001440000000171214142512075020413 0ustar liggesuserslifecyclelifecycledeprecateddeprecated vroom/man/figures/lifecycle-retired.svg0000644000176200001440000000170514142512075017753 0ustar liggesusers lifecyclelifecycleretiredretired vroom/man/reexports.Rd0000644000176200001440000000154614142512075014520 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tidyselect.R \docType{import} \name{reexports} \alias{reexports} \alias{contains} \alias{select_helpers} \alias{ends_with} \alias{everything} \alias{matches} \alias{num_range} \alias{one_of} \alias{starts_with} \alias{last_col} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{tidyselect}{\code{\link[tidyselect:starts_with]{contains}}, \code{\link[tidyselect:starts_with]{ends_with}}, \code{\link[tidyselect]{everything}}, \code{\link[tidyselect:everything]{last_col}}, \code{\link[tidyselect:starts_with]{matches}}, \code{\link[tidyselect:starts_with]{num_range}}, \code{\link[tidyselect]{one_of}}, \code{\link[tidyselect]{starts_with}}} }} vroom/man/vroom_write.Rd0000644000176200001440000000642314142515724015044 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{vroom_write} \alias{vroom_write} \title{Write a data frame to a delimited file} \usage{ vroom_write( x, file, delim = "\\t", eol = "\\n", na = "NA", col_names = !append, append = FALSE, quote = c("needed", "all", "none"), escape = c("double", "backslash", "none"), bom = FALSE, num_threads = vroom_threads(), progress = vroom_progress(), path = deprecated() ) } \arguments{ \item{x}{A data frame or tibble to write to disk.} \item{file}{File or connection to write to.} \item{delim}{Delimiter used to separate values. Defaults to \verb{\\t} to write tab separated value (TSV) files.} \item{eol}{The end of line character to use. Most commonly either \code{"\n"} for Unix style newlines, or \code{"\r\n"} for Windows style newlines.} \item{na}{String used for missing values. Defaults to 'NA'.} \item{col_names}{If \code{FALSE}, column names will not be included at the top of the file. If \code{TRUE}, column names will be included. If not specified, \code{col_names} will take the opposite value given to \code{append}.} \item{append}{If \code{FALSE}, will overwrite existing file. If \code{TRUE}, will append to existing file. In both cases, if the file does not exist a new file is created.} \item{quote}{How to handle fields which contain characters that need to be quoted. \itemize{ \item \code{needed} - Only quote fields which need them. \item \code{all} - Quote all fields. \item \code{none} - Never quote fields. }} \item{escape}{The type of escape to use when quotes are in the data. \itemize{ \item \code{double} - quotes are escaped by doubling them. \item \code{backslash} - quotes are escaped by a preceding backslash. \item \code{none} - quotes are not escaped. }} \item{bom}{If \code{TRUE} add a UTF-8 BOM at the beginning of the file. This is recommended when saving data for consumption by excel, as it will force excel to read the data with the correct encoding (UTF-8)} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The display is updated every 50,000 values and will only display if estimated reading time is 5 seconds or more. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} \item{path}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} is no longer supported, use \code{file} instead.} } \description{ Write a data frame to a delimited file } \examples{ # If you only specify a file name, vroom_write() will write # the file to your current working directory. out_file <- tempfile(fileext = "csv") vroom_write(mtcars, out_file, ",") # You can also use a literal filename # vroom_write(mtcars, "mtcars.tsv") # If you add an extension to the file name, write_()* will # automatically compress the output. # vroom_write(mtcars, "mtcars.tsv.gz") # vroom_write(mtcars, "mtcars.tsv.bz2") # vroom_write(mtcars, "mtcars.tsv.xz") } vroom/man/vroom_progress.Rd0000644000176200001440000000130014142512075015537 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom_progress} \alias{vroom_progress} \title{Determine if progress bars should be shown} \usage{ vroom_progress() } \description{ Progress bars are shown \emph{unless} one of the following is \code{TRUE} \itemize{ \item The bar is explicitly disabled by setting \code{Sys.getenv("VROOM_SHOW_PROGRESS"="false")} \item The code is run in a non-interactive session (\code{interactive()} is \code{FALSE}). \item The code is run in an RStudio notebook chunk. \item The code is run by knitr / rmarkdown. \item The code is run by testthat (the \code{TESTTHAT} envvar is \code{true}). } } \examples{ vroom_progress() } vroom/man/locale.Rd0000644000176200001440000000377714142512075013734 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/locale.R \name{locale} \alias{locale} \alias{default_locale} \title{Create locales} \usage{ locale( date_names = "en", date_format = "\%AD", time_format = "\%AT", decimal_mark = ".", grouping_mark = ",", tz = "UTC", encoding = "UTF-8" ) default_locale() } \arguments{ \item{date_names}{Character representations of day and month names. Either the language code as string (passed on to \code{\link[=date_names_lang]{date_names_lang()}}) or an object created by \code{\link[=date_names]{date_names()}}.} \item{date_format, time_format}{Default date and time formats.} \item{decimal_mark, grouping_mark}{Symbols used to indicate the decimal place, and to chunk larger numbers. Decimal mark can only be \verb{,} or \code{.}.} \item{tz}{Default tz. This is used both for input (if the time zone isn't present in individual strings), and for output (to control the default display). The default is to use "UTC", a time zone that does not use daylight savings time (DST) and hence is typically most useful for data. The absence of time zones makes it approximately 50x faster to generate UTC times than any other time zone. Use \code{""} to use the system default time zone, but beware that this will not be reproducible across systems. For a complete list of possible time zones, see \code{\link[=OlsonNames]{OlsonNames()}}. Americans, note that "EST" is a Canadian time zone that does not have DST. It is \emph{not} Eastern Standard Time. It's better to use "US/Eastern", "US/Central" etc.} \item{encoding}{Default encoding.} } \description{ A locale object tries to capture all the defaults that can vary between countries. You set the locale in once, and the details are automatically passed on down to the columns parsers. The defaults have been chosen to match R (i.e. US English) as closely as possible. See \code{vignette("locales")} for more details. } \examples{ locale() locale("fr") # South American locale locale("es", decimal_mark = ",") } vroom/man/as.col_spec.Rd0000644000176200001440000000061714142512075014654 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{as.col_spec} \alias{as.col_spec} \title{Coerce to a column specification} \usage{ as.col_spec(x) } \arguments{ \item{x}{Input object} } \description{ This is most useful for generating a specification using the short form or coercing from a list. } \examples{ as.col_spec("cccnnn") } \keyword{internal} vroom/man/vroom-package.Rd0000644000176200001440000000271414151153626015221 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom-package.R \docType{package} \name{vroom-package} \alias{vroom-package} \alias{_PACKAGE} \title{vroom: Read and Write Rectangular Text Data Quickly} \description{ \if{html}{\figure{logo.png}{options: align='right' alt='logo' width='120'}} The goal of 'vroom' is to read and write data (like 'csv', 'tsv' and 'fwf') quickly. When reading it uses a quick initial indexing step, then reads the values lazily , so only the data you actually use needs to be read. The writer formats the data in parallel and writes to disk asynchronously from formatting. } \seealso{ Useful links: \itemize{ \item \url{https://vroom.r-lib.org} \item \url{https://github.com/r-lib/vroom} \item Report bugs at \url{https://github.com/r-lib/vroom/issues} } } \author{ \strong{Maintainer}: Jennifer Bryan \email{jenny@rstudio.com} (\href{https://orcid.org/0000-0002-6983-2759}{ORCID}) Authors: \itemize{ \item Jim Hester \email{jim.hester@rstudio.com} (\href{https://orcid.org/0000-0002-2739-7082}{ORCID}) \item Hadley Wickham \email{hadley@rstudio.com} (\href{https://orcid.org/0000-0003-4757-117X}{ORCID}) } Other contributors: \itemize{ \item https://github.com/mandreyel/ (mio library) [copyright holder] \item Jukka JylΓ€nki (grisu3 implementation) [copyright holder] \item Mikkel JΓΈrgensen (grisu3 implementation) [copyright holder] \item RStudio [copyright holder, funder] } } \keyword{internal} vroom/man/gen_tbl.Rd0000644000176200001440000000515614142515724014104 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/generator.R \name{gen_tbl} \alias{gen_tbl} \title{Generate a random tibble} \usage{ gen_tbl( rows, cols = NULL, col_types = NULL, locale = default_locale(), missing = 0 ) } \arguments{ \item{rows}{Number of rows to generate} \item{cols}{Number of columns to generate, if \code{NULL} this is derived from \code{col_types}.} \item{col_types}{One of \code{NULL}, a \code{\link[readr:cols]{cols()}} specification, or a string. See \code{vignette("readr")} for more details. If \code{NULL}, all column types will be imputed from \code{guess_max} rows on the input interspersed throughout the file. This is convenient (and fast), but not robust. If the imputation fails, you'll need to increase the \code{guess_max} or supply the correct types yourself. Column specifications created by \code{\link[=list]{list()}} or \code{\link[readr:cols]{cols()}} must contain one column specification for each column. If you only want to read a subset of the columns, use \code{\link[readr:cols]{cols_only()}}. Alternatively, you can use a compact string representation where each character represents one column: \itemize{ \item c = character \item i = integer \item n = number \item d = double \item l = logical \item f = factor \item D = date \item T = date time \item t = time \item ? = guess \item _ or - = skip By default, reading a file without a column specification will print a message showing what \code{readr} guessed they were. To remove this message, set \code{show_col_types = FALSE} or set `options(readr.show_col_types = FALSE). }} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[readr:locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{missing}{The percentage (from 0 to 1) of missing data to use} } \description{ This is useful for benchmarking, but also for bug reports when you cannot share the real dataset. } \details{ There is also a family of functions to generate individual vectors of each type. } \examples{ # random 10 x 5 table with random column types rand_tbl <- gen_tbl(10, 5) rand_tbl # all double 25 x 4 table dbl_tbl <- gen_tbl(25, 4, col_types = "dddd") dbl_tbl # Use the dots in long form column types to change the random function and options types <- rep(times = 4, list(col_double(f = stats::runif, min = -10, max = 25))) types dbl_tbl2 <- gen_tbl(25, 4, col_types = types) dbl_tbl2 } \seealso{ \link{generators} to generate individual vectors. } vroom/man/guess_type.Rd0000644000176200001440000000240214142512075014644 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{guess_type} \alias{guess_type} \title{Guess the type of a vector} \usage{ guess_type( x, na = c("", "NA"), locale = default_locale(), guess_integer = FALSE ) } \arguments{ \item{x}{Character vector of values to parse.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[readr:locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{guess_integer}{If \code{TRUE}, guess integer types for whole numbers, if \code{FALSE} guess numeric type for all numbers.} } \description{ Guess the type of a vector } \examples{ # Logical vectors guess_type(c("FALSE", "TRUE", "F", "T")) # Integers and doubles guess_type(c("1","2","3")) guess_type(c("1.6","2.6","3.4")) # Numbers containing grouping mark guess_type("1,234,566") # ISO 8601 date times guess_type(c("2010-10-10")) guess_type(c("2010-10-10 01:02:03")) guess_type(c("01:02:03 AM")) } vroom/DESCRIPTION0000644000176200001440000000551114151431426013125 0ustar liggesusersPackage: vroom Title: Read and Write Rectangular Text Data Quickly Version: 1.5.7 Authors@R: c(person(given = "Jim", family = "Hester", role = "aut", email = "jim.hester@rstudio.com", comment = c(ORCID = "0000-0002-2739-7082")), person(given = "Hadley", family = "Wickham", role = "aut", email = "hadley@rstudio.com", comment = c(ORCID = "0000-0003-4757-117X")), person(given = "Jennifer", family = "Bryan", role = c("aut", "cre"), email = "jenny@rstudio.com", comment = c(ORCID = "0000-0002-6983-2759")), person(given = "https://github.com/mandreyel/", role = "cph", comment = "mio library"), person(given = "Jukka", family = "JylΓ€nki", role = "cph", comment = "grisu3 implementation"), person(given = "Mikkel", family = "JΓΈrgensen", role = "cph", comment = "grisu3 implementation"), person(given = "RStudio", role = c("cph", "fnd"))) Description: The goal of 'vroom' is to read and write data (like 'csv', 'tsv' and 'fwf') quickly. When reading it uses a quick initial indexing step, then reads the values lazily , so only the data you actually use needs to be read. The writer formats the data in parallel and writes to disk asynchronously from formatting. License: MIT + file LICENSE URL: https://vroom.r-lib.org, https://github.com/r-lib/vroom BugReports: https://github.com/r-lib/vroom/issues Depends: R (>= 3.1) Imports: bit64, crayon, cli, glue, hms, lifecycle, methods, rlang (>= 0.4.2), stats, tibble (>= 2.0.0), tzdb (>= 0.1.1), vctrs (>= 0.2.0), tidyselect, withr Suggests: archive, bench (>= 1.1.0), covr, curl, dplyr, forcats, fs, ggplot2, knitr, patchwork, prettyunits, purrr, rmarkdown, rstudioapi, scales, spelling, testthat (>= 2.1.0), tidyr, utils, waldo, xml2 LinkingTo: progress (>= 1.2.1), cpp11 (>= 0.2.0), tzdb (>= 0.1.1) VignetteBuilder: knitr Config/testthat/edition: 3 Config/testthat/parallel: false Config/Needs/website: nycflights13 Copyright: file COPYRIGHTS Encoding: UTF-8 Language: en-US RoxygenNote: 7.1.2 SystemRequirements: C++11 NeedsCompilation: yes Packaged: 2021-11-29 22:18:11 UTC; jhester Author: Jim Hester [aut] (), Hadley Wickham [aut] (), Jennifer Bryan [aut, cre] (), https://github.com/mandreyel/ [cph] (mio library), Jukka JylΓ€nki [cph] (grisu3 implementation), Mikkel JΓΈrgensen [cph] (grisu3 implementation), RStudio [cph, fnd] Maintainer: Jennifer Bryan Repository: CRAN Date/Publication: 2021-11-30 14:20:06 UTC vroom/build/0000755000176200001440000000000014151250641012512 5ustar liggesusersvroom/build/vignette.rds0000644000176200001440000000035314151250641015052 0ustar liggesusers‹uM ‚@†Χ,₯!θμ/πGDΨ‘KDDΧM7”\uIΊυΛ΅QgEƒ»3σΜΌ;/{u!:15θ€Ζ. ŽΫqbC\ήXƜŠGαŸx„Τ~‰<η-»—¦αm{ς͞I―THye"c―}αG½,‹%O;έΆ–Œσ‘;dSτιΊν$dp³a>3Ι(g6-„f€L œΩΖq`ͺυFvμΙ²H}Αμΐήe. /²E^ϊjΩ’qφ«λκΧQ˜B9RΠ‰¨€ώ]€ͺκ °(ήΰΤvroom/build/vroom.pdf0000644000176200001440000044451614151250631014364 0ustar liggesusers%PDF-1.5 %ΠΤΕΨ 88 0 obj << /Length 989 /Filter /FlateDecode >> stream xΪ•VM“γ&½Ο―Π-vΕΖ€Ύ¬T%‡ΝNR“šέΚzδMU°„eΚ²pyβύυ Ι–Νxf.ƒϊυλΧM ΚΏήΑgΖwΛ»Ω/a`’ΗΑrΰ'A‚S y°,‚ΏGΏ‘|KJ:žβŽώ=ˆ1š8ί}7ώgω›‰„@ΗΈIΒQ ?qh!>ς1ŠGσCw+*,Ξ&n12H=΅–W2ΓE#NQ<Ԙ(ΩάρZLUτ4:3ΕHqͺwZ“%…uIj7ωK0εβZΠ\‘Ίl*β.Η)QΓϊeWήEμμSΓςmuΌIΫ9Ÿ’@£‘π§VSƒ Ιx}‹x¨UΜPG€7}Ήο­/d­ήS™ ΆW/ΈŠ#€Γ€s΅ά89JN*;γk;~az:οfb—˜΄£βvΧ ?.zωΎΐVlkJ«ΝŸ+!Ξtm%Y.ΖO.f˜œ((»rαΖ,­ŸΦfΠΰΘ.ώη²”Δ#ΰς½‘υ‰*«K‡Kp#©‹ˆœ!Έoj¦˜ΦΔΠ…C’¬.lτ€RΡ½γ¬.eΏh'‡1ŽG€j:ΏωΚ4γvξ€—ΧέFo}RτΘ›³Δ2#ΉjHΥΩιπ쀦΄ΈH݊žHj±b]¦}-΄9t§bΝŎ(ω,VϋΪ‘iΠΚWD &βDλ|#xΝΩ…°|wNDiΙΑKΗ0ƒιπh<²œΦ’ΎpαΌ?–Φλχ]ΉA\ΉΨ~Ύψωώ₯σiΠ¦ΊΫ’Θu°?L§‚Πέtaπ9ΦQ °£΅Qj/˜ΝΪc ΔΤδmT±ΰ’ΌB1€ΙΌƒ™Ψo_γ_‹9Η—ξK¦6Ν δ|7;q°€†>Χΰ#UεyΧ” ΊηBΧΪ›UŠc½δŒIiNΰkΈΆΠ}vOλBή;Eύ=tj‚?ύhη!@mΫΊUCdpŸ<μΊ,δ ΰ¬―˜SI4±=E œ'Γƒš rδυΔw†σŠyΧKέ½Ό›œψšRΕΦ47ύς˜W~ΛU^HHΧeΟ„@ίcFC―‘TΊgyw[­Ί“|އπΟ… Ύ+dσθυtΘ•>#μsΤΣ+Ž’Vϊmβέ~E-^ίώ\Υ|nΚ’Jυ†’%"ί0sUΩW\W@ HZμ3F+ͺ{φu”Z˜Qζφ…(ό›¨ΌΕΎ:ϊMτݐ_%ά₯nν―ƒ²άW\aοήV_ύ~G{’ςΝ“yΗp±υ!¨RΗF#ψύξ!Δd‰₯ϊΑ›ΪG4†Ν ›ΓύςξB“μ@ endstream endobj 118 0 obj << /Length 1033 /Filter /FlateDecode >> stream xΪνZ[s£6~Ο―ΰf‹¬+‚Ξ΄›IνtΆ―Σ™ΞnΗCŒβe‚rχΧW2ΰ‡%ΈρΆε%²Ν9ϊtΎs“P ³t ssρvv1Ή&Τ‰@ΰΐ™έ;B@hΰπF°3Kœ.φ~Ÿύ8Ή¦δ@` ΔfšΘ΄9œ+AH-‹tQz>fΠMδb½ΉΙ·VνΦΛ9Z‹O` sϋc5‘ZΕκ!‘fξ&Ζκ]!1VK”z€2.φγ z\.βL”še!²,Ν—΅(΅ώλΚ†OΑοΏ«>c€4? n½4ΩͺΞ'kfeη7‚nœ%²Sοq•αcβ}γίD₯„°Vϊ)ΝŒi3/Β죝F€rΪψ­Pr©DY>·cχ“Υ”Ό½EQ τ\ΥΒώJξΊtPw²εΏz„»ι2Z‹·λ4K„κ³Ÿc·Ό±!O΅κ…¬ε[—2!^Nš ™ˆ$Υ©Μϋ€mξ‘§Œ!½¨pKΏ[Δ*Ξ2‘υγΐ jpο«Ψ+Ε ψ•R·Ωο…HΚΙFά•©}Ψœ5ΘΉ‡˜»]˜)HΊό¬KΤOA₯{΄€b«¬no€‡€Γ½ƒν‚3QΕΨεΟΏό6}wσΓμC/p=A ω*_ΘΔδW/0ϋΌΊ]ϋa?Š•n§pœ/Χρ²—PΚd{‘ϋ·/ΨRΙ·Βh*·KΡ«Τ$p°―ώYZj›«‡₯Ήb΄NήΩΤ£Π½½z1λy[fΧΛy/ϋC)0%Δ4‹zIά”‹ώ"YΛ· >lK-VSρΗ:U ]aΫTωB›pmP/ίΌAhί-΅½7Αjε|˜΅š&iI""Κ΄Ν£…`£‡ €oʯ̌nH"x>ƒpΎ<Πθ9K‡~‡ζ/‘£μΧκγτζΒωθ#BΰQ±nΖ]gΓ»vΊχΗ|!σDδ¦ΌšXGςŸ“?œu6ˆυ$ΦbžΗ+arγ1ώ=ζMΥ*ΦRζa#εη+8AΏΘήs}—y¦ύŒ΄Ÿί |X¬Νωi·Εn—ŒΜŸΟΡ dωΝFα‘υ―ΰŠΠxΑA(”ΌΛΔΚτ³χy?ύhύ*)Wfςpά}5Oΰαž˜Η™V’+Σkφ£;ΩΙ€Οe‘να8b#ΙIN YΨwyρͺ°—Α׈r~εχR­μυΕddϋ5ό$7χφt…Ζς}βΓˆΟάΎΠ‘γΑφΤγφσ§;Aϊ?όΧρMNΰ»Τjά³‡όo”½ΕτqHGκΟ@=τκς€ϊ¦Φ{υα$wˆŽο@ !³`ƒkβίε‰x¬΅;(Dΰι’wο–šρjvρ7mΓV endstream endobj 143 0 obj << /Length 1230 /Filter /FlateDecode >> stream xڝWIΫ6ΎϋWΉT.b†‹¨εΠΓ΄) ΄qNI`Θνa£ΕΥ2}Ι’¬‰=Αΐ£'ςρργχ6 ;{;,~]/ή=xž‘Θ§Ύ³ή9cΔ<ί |Œ8£Ξ:u>»I™ΥΛ―λοΨPΧΓ> | –΄S* lMί―.ˆΨ!g«”’ π$_|ώŠ&?80…ΞQ«ζŽG <rζ|\όΥΫ›>5tΚ†Π FΎGœsΔH8†ή―ƒσ£EE!ιΞπ[΅d+βF,W”c΅ΌΝ #Χ‘Θ/Σ$ndYh«³§τ#†xΕ)υi›q„Ο#δqfΰύ.κ€’‡~ϋ))SFBgE"ΰθLΖΜ1όȌ?`:S!‹$kSQ/Αvγ,3‚aĎΚΒ<›'Ρ ΪƈiάΔo9Lέ}+κZϋ‰rG<ϊ­κ‰^*vKγmΦ {tJ.δŒwΈΥ!7e‘Ύsσ<ˆθ!(β|zbεneΒHΘ9μό©l –„»2™Θ¦[’cewZϊά}kGΎΙΓAs02Y‰Ί;ΤΘύ+ε;!Ž=ξSοΕ¬Σ0Bcμ" X‘kή~1PΫhΏu4ž•3™TότψΨ V”‘ˆoΐ–z½Ϊ&+χ2‰³λвhΔ^T/)4·rMϋ¬œ–ν6Χυ’§ΈŠ“ζ&ΚλΧ-mΎ½aο+»ξ^©νeβY@œzΛϊ²¬RQ‰t4ύpχψρήΞΫ„Ψρ‹*W(…BΪΘ\“Ί+«<ΗΨ›77[ω ―q-ο*Uφm.ЦώnR^IFΨsFu*π‘9υ φ^BΩ¨ nDώΉ1Eά-·ˆ€©νD₯ΊWj^Ά'£‘(ψyΘ ΰ|£6₯γνΆKΚέgΩΩ€΅]Έ"ΞEέΡ<¬žΚΌ*έ”„P±UŸΪjπͺΪ%‘z—υ;‘G†ε{cšΑΜ…#@f N­Μ>εΞ’ (tͺ½Σ‰_x’G½­ηͺ,σaΓxΙ#ΜC‘uΛ”6RΌDξϋ9#΄…Ÿ–Ί°[L¦(Iυ‰gυOT•LU/P4Βtϊ0³ G‘yμ΄;ŽζΕΆΥPόΘ• μΓέΪΜm‘©)Ν³;ν³ΑΠΉω66`P9Ξΐΐ–>ƒ*›σ\jχ3-«·2Ά‰3 #oλ%qϋ7Ι“΅Έ υ¬MLιaΣL!ή2έ<Xΐ}χ}Ρ/:Η±Y»k‹Dέ‡μz°_ 3Wbd ₯Q&7χ—Q;6©4ΝΙQΰ¬lRD#΅‘o‘7'σ]‘Nͺ}ΐ ΟJ€C±ΞτςŽ““xJEate1“d$ς‘Χ‡»-;€p ξw*G©n|jŸ­0v‘°€fδ…ΘŒ5OBά«.ž=„Ω˜τώ­Bδτ²ž^uιΈ™ά΅Ž2ΈΙλuR/υͺ±Πmk;|R¬)ιWκŠ Ηͺ3A/˜­X$€φBϋέzpL4DΤ‘\)γ€"5’, ¦ΓθϋΆ…| @έͺ £1ΙElZ»ΔzΚΖ:$ώ¦zYwλ=T"JI—ƒ ›ζJ!&Fƒ΄š#-€o?rS‡"ΛzzΡlƒ…/―uα»Β endstream endobj 168 0 obj << /Length 1861 /Filter /FlateDecode >> stream xΪ­YiΫ6ύž_a8"kE’¨+hlŽm·X4Aβ-šb!Λ΄—ˆ,)΅ΗΏο Ω²ΉŽέA š"‡oޜΤz£ΥΘύφδυμΙ‹‹€ŽR7H4š-GΎηΉFqδΉa@F³Εθ‡Nώύρβ‚n/ ЍΔΘ%yU΄Έκ‰§E›η‹ lm›q,χMIŒ“jwΑnΩΞώ\qδ¦ 1§½ΉΙš,¬™L’8·?tX.*ύ»n*œΉε ^ΤTΛ„TKυ̊’šΠΉc 5Q0ό©D­;™†4tψR©>ЁψΔ₯‘Αςηη«+΅hH$ a€©3ξxQ¨ΡŠ•¬Ι³€Ÿ€ΈA¦Ύο¦a¨δΜ³ρ’ΠsͺR=Ε Sƒδί:=ΎEyYΡ±V/Άια°uŒχ-θ6λgZV³`A1ؚμΟ’Ο¬˜©Μ5&^Ϊ0PβϊIτ= ΎYΰξ8ɎsΡΤ ύ8 έ”ϊj‹Α{΄w]jΈΠŠ”Ε—ή Ο½:I‹Vˆ”|^ζE·`Χev&›SΧ'?<·y!z*5K²F›’nXΛJqf #ρheΫ‘ ςΎ.xnθΨh°Rρ&%Mά0ς€,«f‰γ 9‡Θ <³MŽΫšεό‹η‘<Ό*1ζh*Β· Φζ ŸΛp‡Ÿs¦γ…F;hpΉΤ‚˜–ˆͺαs<ΦΒ*dρ_3-YŠƒ:kZεh€r·D8—Ÿή'‘ηƒ4κQ)MΏΘΚ…Ή1u:λrcϊόέ j½βB μΙ02 όzYŠeψη&£ΙVÏ{–ΪQ£¨ς¬`_ΌΠƒώ!c©›„ƒ …θ”sS?£'κΝη²ΰ_џNΐD Ζ½ΤΘnES#Η€‚μψΑN:λσ§q%οΈ’š\w­~ λς›νy΅ &τ/€΅ΗΥ!9p~?¦nΰQΘπ‘β‚yΛDΖ-‘ØΖό–Έ‘¦pfdb?/²yΑ¬ͺ˜πnΨKπ`‰+*¬“8_fkS3ΈVϋ[W Φg…ΕΆ~zr>oTμσν#Μώ9”jecΡ ΔυδEJk²μΚ\±νϋA;CT“iδ§ΰΧ7U#Άg‘Š™ΰεnKΑviκ=ϊ•λ’ZqπηmΟΩ₯š‚―φΕi¬7ŒJΙ`Œ#β\©φΪ›W%ΨΆμιͺΚβΑ2‘n²ρλΩχ*α™EΖ \_ό_ ύ‚ΩΗΟο,BΠd~Ο ”KYχάΈWεβόκ“UζEƒ$±[(B/R’}ΫρR°kŽ·nΐ›€ύ<°7φ»άΌΤΥ­η¬i­˜΄`)Ϊ@šσΥι°Μ¦ ΄Π |g|‰Π(Αkέf £v,<%’s.π(Νφ­γq=λχ(Δ^Ϊϋ ˆ¨Ν<±€}wXCg+fe„FKiΊo₯EΥΝ‡υβ0jύXλ2+°†η@!a―‘”Β=’ΣΉiGΤÈTƒL¨Υp{’E·ΰηζšqΌύ΄dκΕ0#Aό­KΛn…²ž„l)ϋ/™ΊεΥiؚuΣΗ˜`B[―;οω$χ²‘3iŸ‰a“ΏIv'αΗ>ΡoΧά_΅ΥΗGϊ ˆ@ΰ©:γ·ά‹η τ'ε"kz^M(¨“„QδŽ‹έš›η“0tZ‹.τIΤ'5}½iZw°†ς_x8΅¨α„ξηΣΝό7(Ή‰‘`Ž…Ιr³I7ΪυOc„€!ΦĜЇώ#4₯. »·όVŒ˜>Ώ|xις―ω‘MΔx†LωIjΪτibΌ“ό\eγΣ‡Z*τ!α•&αύή­Ν…¬aΩbΣέι΄fο4μι? 4Πύ₯pΥT] [ΧYσUυf\Ρ}uΪ―Ό>^\έsŸI@› G_χΣSLξ ¬σ οτ˜‹ͺ|>ρG[›―λΎυ7ΌΧΉθΦεIFYAΆjWC.ίθAtŽvΰuΖ―Lv€iοϊcL;°‚Ϊ;g-:Ž₯Pzβ‘ΦK}ίαeέ ϋ"„ŽŒΐ΅€ϊ2Θ$ξwχ^HμW λWΊ$‚ΥΝ#vΊIK’° Ϋwψ°τa}GhO7±ρ=Y γωϊ‚πTνψ»κτVŒ dE[©Q‡ΐ€ ­Θ° ,τJΌ«τχ”Gξƒ„VρjΦ gj4Ξ/Μ|>œΏοi΄~ί¨§ΊαHίΥp(;ώU«ά ΈJΎ|‰™ΒΝΫΫS‘+c<†ΏoΡ,JȈΦ[w…ΰuaLΘ„ΦΫν4υjΌ`KΘyϊβ)?˜EλΉΚ†εŸφn²ΦΌφτ΅ΥK~α«ΧΏL{ ’>ΉVΖΞ½€Όž}ΟίΟ±nwΥήφaUΩά©5ˆmηδh Wοΐ~κMΓφaουΓ›£΅¨_ΑπhauB<“;’Δ„/ίhOδ™Ζώˆkž{7{ς>FJΓ endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 800 /Length 1700 /Filter /FlateDecode >> stream xΪνYKOGΎο―¨#ΣΥ%l‹ΔR"YΰHI0‡ΌΚ²ƒf‡˜όϋ|5;ϋ€}€π˜“%–ή©©ͺώκΡΥΥ½LšΑнI^Ά> qα³s$  ΰ=όΛp]°βm8OL·›”Ÿœ5|Η^Β{F΄ΒUΦ*< Ο<Ω θ›X€ΰμ’ΨΏ'-„Δ2ƒΐωŒψ:ƒΨ§kaF’Νζ,μ²d”ΊŒPσ,X°Τ!o8B‘±°0”z‹€Ι³€ŠΔ́%V.ω %K¬Αv (PιC &ˆ’‚° ,Bή€…‚ ³—·Γ1ˆ4# >‹ΐ4’2G‹ ȏ ¦q›+­  ψ“ ψ–‡4Cr2/‰Μ ‘ύΰΰ€Š*~©>UTΌ§½γ«‰Ί¨ΖSΕϋτζΝ`OΎοoη:ΎγΛ䲜LΛU‘Οl㜾YόrΨ”­ψdxS.¦ͺΘΆΔΝ‚Χ倬‡MU/„–”­νLΝωxED¦e‹Θ]9YΧόw»°¬₯Άr nWΓρB`φ΄™σΆΞΗεΝŠωσfξλͺΊ™³Ά»ψψpάΤεν>#?SXΎV·Νt‹ω&―ŸVήon—^YhιθO‰_UυΝ°Y“ž‘Ÿώz΅.ωυκ)±ρhR›έRŸE ―kIŸΗσO)˜6υš,hO‰}­GΝΊ[κσD·ΫέΎία€Vϊ½3©ς°ΖsŠνFΣ1G£ζLψΕ'¬#*>―ΛAρš4ε€™bcΖAq\N«»ϊ’œΚŽΣR~//GΓ·Υ=jΩΦ³WΨV°±Jωl=5jsΛ}8™TPw*œΜœόlνp6x€‘eo«ϊ²¬[υϊ¬ψ΅ψPΌΓΆƒ3ΑsΡΠ)―,J­ ˜•ί8£œΑ½J6‚οδξ\κCρΫhςOqxpΠΞP^4£jRœΟή—¦Ήώ\³(Τ?Gηͺͺ―χλeȌσΚρ™ΝQYg_ŽμzΤ|Ή;G}Ώ)Zx3¨/ˆ NaλBk€φkμςͺν‹Ό Ή/tΕh:E•^Ή’zώυ7š8-S΄ =MξΖγ³-¬θ[fΌκΡ½˜χ©M²ŽΠ.fΣ­τŒyΎ&ΠdδY–B€ψXW'%<†΅ρώˆŠOε}σ8a-ζ΅U#MΝ·,›<[(9Ξ†4r;HK7ΉM7Ϊntέθ»1tcμΖԍ>ξτq§;}άιγNϋ«·Λ jShsFrϋ0ΟH―ƒ’)Ρ΅*ιK=[₯Q'l *:°γRw Ώ+ΫVΊ~!`Hσ9Gœ2!l‡ΰΏδn–vŽΑ³ŠΩΌ.«•‹+~°X―œΆcίG•‘ο &(΄κΫ1Δο€A#τJF²S6ςv ΉO ¬°ξLΆRŸ΄Τ‹Έλώ!$­B\f$ˆX$vξCHΚθeF’vb•δLd4ˌ4¨nWF²οƒ³ͺ=«Ξ1xlίzGFrθͺCXqŠƒ7»R2φ%VRD¬ϋΊP"Z‰w₯dκ£:ΘΨƒ6¨;RτΏ4ε!¦e‰b”‡`w€€±ύc@y°(Ψ 1b™ΔΧΕ€ςν2'ε!€9i\PlXζ$£<˜]‘θΏB±yPΨ :Ψ]‘x°2ΡΠˍζs xηέΏc­4ϊζmέJΗοZΌσΉ‚εωΉ`qFxιYΐΩ΅³€γo: Θuh}· Y%Ο(Vš^‡¦'’γ—:fΝζΎ{qaρ8fΞ> stream xΪ΅VێΫ6}χWϋ$k.EκZ΄Ϋ4) @Š:νC΄DΩκκ‘φξώ}‡ένf4††ΓΫΜ™Γ3ΖΞΑΑΞο«_·«›7Ύο$( IθlsΗΓQ?t’£€g›9έ΄)Ε.mκŒΧ‚―?oίήΌ‘ΣM>Q8R/Τ’ΆwΌήΎ¬<0±γΗŠ0μH«ΥΗΟΨΙ`ς­SIμάλ₯•γ{Š| vιό΅ϊs8ος«s tšƒ‡Qθ{N„&Ι9 Ρl7AIμυΙΌ~`UQσυ†Ψ•Gkΐi§ͺ6ΆhyZ|˜€LM-Œ7o:c0σɘ΄Vή­½ΐe•‰bž0‘(ψp4”.2T… L.Ώq‘vE«’œ!0"1Η1v6^‚-@ψ ~ή’aJ{%»[{Ψε€ ₯TΑA)Ρ G³—§xYg‹0ώBΪέW@Χ…NAOν͌ΰRυΑ8uΜϊ\]ΞN₯μΦ…ƒ sX&=Dς±νkέ\ΤΌj„μ+ΘΏœxmGγΎ2šΊ|4VYΦΘKϋBη )ς5 ܜw³£‘AxV A‡(†ΰuΠ ΜgκA ’QΧƒ+φ=Ȏ₯R\d˜ŸΚς›όξ!hͺYτ²ΨοΛώutœIn±Ψ[$ΐ—uλ pmB3Άn¬cܐ%‘}²;πE’Ύ”ΈρΔ}θ‘RH‚%ήΘ‡ ’³0ΥΙΣo;E―Γ©‚’‰gCύFˆ’4P!( ―ΫΎ\SIQrb¨gŸΣ”Β“’Oͺ·^βλ‹f)ώ½Nΰa–'ώ„dΜ"½”ŒΫN;σβΗΡsψΖ£ψΆ%/'μ C%†&fk›εk`vޘοΉkšJ[;nRŽ«J¦¬(η+E„)Y ΅$EFΜμΙ“εOoθ)Šγ=₯ͺ’ίΎά¨GQG?Άu’$D>±š<γ»&Ύ£oΎκΦ4r•Ψ’Ϋvhά²+4)Ο#•­5ή³˜=‰δ‡αΣI@Pΰ…/mŽΛL·TύηΘ•4BN-pΗH> Tbβl⹏ΝΙ8›\φ‹ku,ύX=ί½«Υξχ8άs~—±Gџb6ωΆΣΖ2Œͺ©εQ˜u¬³λ:ήv\€bΑΥ=K'-ιΆYΩ@Π9!†ν±°-+:cιΰ›ŸκtψοΧ—₯ ێMβΝ1]^¬=wg†¦_ kϋΓυυ΅YΣXogΈSgžέΥ·£ΐ΄£ΐ0`]fFͺ £₯\-7]ΫX…ν‘οŠρμ² ώρκƒ1Τ>JيŸnnD!9*Σ¦ν­xƒ»ύΎυκsΑΖ¬εpφ€HBPD†Ξ- σϊP,txΜΐώq΅,½ƒΞΉ(°”&ˆψδ5ΪQ)@Ύk#`νΨήΨΏ .; Œ­Λeγ$«vm5›Σj{uϋξΚΈzξ+½ž4χ1Έ]ΙjU¬Ύ'Θύe;Δτ?ΦWΠ€\ύΨ[ endstream endobj 193 0 obj << /Length 1043 /Filter /FlateDecode >> stream xΪ΅V[Ϊ8~ηWDh@Ϊxl'NΘHϋ0ma΄+¦FZ©[!“ˆš › ½όϊγ$3a¦ͺΔγ|ηψ;ίΉx°±5°q;x΅\Ν,ΫπηPΗXn ‚1²lΗpŒ˜Ee`|9γOΛfvhΉQΫ7GΘV$"γEšε;ΐΥΰέj™΅•I]Ή©lo²1Α£m‹€ΠΝ»Ο«m»#Γ$δL”Ÿ8Mώ›”aΉZρu—I›Ύλ oBkφ³2Š”%O‚j±^gbLΩθςBίb§– EŽ:GhμΜκ “0δΡ*Φ€«8Βκ·pό"Δη捫iΜ‹‚g ²‚†u4‹2‘h‡žQΔxΌΪΗ—ΗρV’Q§–yMz“fjqsΧ‰ρώξω"G<Ω–|+.'u£{υϊ^.ΘΘvΥF”ϊ<uUρ@¨Χε^ν€›Š­ΪnΞν€I½…T6Ϊ†M• ΄Eͺ³΄¨ε FXMm(’ΥΓb¨€–ή‚ΘrμχSΒXd‘)ρ²Ν,4q‘ίAc ισD!§Ι6 σ]]Bτ°qΜη…Έα²%Ÿ#@V₯ί4ΐ³žt­έHD”χœΩΖ: ›fΘd·ΐ³›ωbΪλD_υxο¨Χ¨Τ,(›«Jιρ ¨»΄'Ύsι*ΒXœΛ>‘ͺ] e_ώ'Σ#!­‚ήΘΆ‚q#'Δcu.©y8ης½© CŠ11Ÿa‹B—»nA{,žfTλvaMUuΓύώέβού’Ÿ~O’{‚Ρ]Π']ί5Γ‡ελα‹‹κΡ¬…ν‹o^ώ endstream endobj 212 0 obj << /Length 1039 /Filter /FlateDecode >> stream xΪ΅WێΫ6}χWθ‹ Δ\RԍOEŠdA6ς%Q]\]v7ϋυŠ”lΙφΖvPψA&5_ζ Δ|œ!r;ϊ|9ΘN7˜­-Ιxή©Ϋ­”gΊαάτ'ΓδΎGιd§χΒf}ΉΨ½f³Γ¦(=ρMF¨ƒ<βkŠoD7r74H§γB&–Ÿ1Ϋ\ΑŸ  i_,δ!Uœ—Όωτ•™Z;`lš]^΄υrΙπ>3ρŠcσω˜ ΣΜ}―{ύ'ζUU¬6κχ¬5bTO•Η­θΠΙΝBμ™― endstream endobj 226 0 obj << /Length 1765 /Filter /FlateDecode >> stream xΪ₯XێΫ6}ί―0ϊRˆΉ)Jr Hšl‘"HΔyJŠ-W¦m!ΊΊdλ~}g8”,ΪΪΝ ΓE ‡g.Ÿmgώμχ«Χ««λΞ–lρhΆΪΜίg"Œfqδ3)ψl΅ž}ρ’ω_«?oΒ± H–H 0Fd«ΛΫφ.GΑ+ί’΄XτC<ΖNψΉQ[ν {θy}ΓΗpA2[KGŽ_}ι#š °%LiDκκΎya>ϊʛωBϊΎχ’>Ώδm{Ψλ‰[₯ς*UΉvdΧz£ΊΌ½₯O¨.όƒΙɊ¬i²rλ §Ε-&V‡0d'—uδ‰/Y$’{UΟίΫv….ΫζQβ \.tžΪ{μ!qΔ– οδCWάιzΎΰχͺ =λjΞ₯(ζ­­θ –Τ΅jυ Ά£ΛΒ‚/‚˜ωq<2γRΊ’|@Ÿτšmˆe—Δπpΐ‡°Ά+ΐ9 ƒ¨jw™©u!#ίηττš:7uULΜ/!–z¨£[žO†,ΓͺΩ£¬ŠH°ΔΟh=‚?Ϋ?KpzΎHμΉκΒg±H~ΐxΪ’ |ApΚͺ?–ΜjΤ„šΟ½•ƒ§F#‚š>Φ[†ΰ°ώA΅f―Σμ«οσT΅YU‚Β½ͺξ•1¦­!HΩ|†ΎχIλ)οΰΑΰί³m©ΫΦO΅Vλϊ§QπŽΥIbωΓΈ Μl’_ψ’‘<ΙU­{7jU–7lΘ)!“ΝHο¦ ‚­gΫCι©<ΗFh‡:m~ΔξϋŒ€w§©'+φ]‹Έv3EƒνΆnšΫBύ3εά ’A«c>Aτͺ€)۝¦Ž¬„Ή‰AJζσΠe0+[]ƒΝλΖΔ^Δah]uΫ]Γΰ]~@§Θ5]„±·’@†ξώ™Βάΰ Ηe™–Ζ£ΕUiΑ7˜‡UΣRώ‡.`iΞ}―Ÿ¬¬Z¬+κoZ;!ρt» εxBΕχΨN³ξͺξgμBΫ |©Νz‘eRŒΘΚό²±xό Ÿ’Z–rLΓΣ'„μ»ύσSΩRΉο‘₯‚oBz(‰„ 5υΎˆx$rις~Β!X MB2 =<•ξθ… MπnC`,jT₯±:΄ξ[šΠΉτb>€–’GΣέ5Ϊ Uf˜«™,ΎΊF?ƒcΙ`ΏQ|‹Š>…g²x)ύΚ¨ž#i]Rψ*‡τR}Η-¨€¨Η° ε‘’T•΄άΡiUμUjΉ }§'m_λΜΘ8χ;m6ΥGΫμT Ίž"l@±fœγXύβ°'H½}fΎr.]©/¦jM η₯…+q=‰ΰ‡°2 s6RΉ½­tΠJͺ /Z;@λͺ»ΛυE@Ή”WΫ  !m$ΪΆΊl}oάυΦηO…YΓP±—ΆΰΕ0ΏBΊ ³™]„skCΗ,ŽvΝ·lo/aat²±½>`Βδx>4†‚3"YeDιψYα#°υuέg-U(ψAQίPAϋ,u£œ­Μΰσˆ;ΈΠΝbͺΐ0'u8ΌΓBvTiQŠn§Ϊ©CJΒ’c!gJέ©cTΉNaΚ˜Σ<ΔφΑΞω whζ pLB΅.*”’ŒJ’φh_a-¨6¦Ψ8ςp#9Χrψγ §»Ϊ]u;:ομxσκύ§·S52ΰΔ‘tJPŠΆ/°δίΥή Έ­Rz¬'Σ™ †½€A7σ8τpB܈?ΎqŸ3!cχψfο ž|v[™½5ΓεΆ±h¨Ν- Ύ|Ττ`‘¬ZϊφΧ’κ½™ ί΄φΉJ-˜Ωκϋ.cΡδ8λϋ\¬™ςΛϟ)μZu–bOUvž}CM=mν‘ΖΖ³ηbΓμΉΨxfΥ0Ξλήιy+wškNnγVΆά]'©Ό±*¨q Pd¦-`Σ•©=M^ΔΩJkM•kΆξ”-R(ηbMΡΈˆΆβD0HhlrAoWWxηœH endstream endobj 237 0 obj << /Length 1265 /Filter /FlateDecode >> stream xΪ₯WYoγ6~χ―²/6+"u/Ϊ‡΄M]6qžE@K΄-¬—’rτΧw†‡"ze'έn°ΰp4~srμ9[Ηs>Ν~YΝ.ƒΐIέ4’‘³Ϊ8Δσ\?ˆœ8ςάΠ§Ξ*wξη۞·νCχ²η‹―«ΟΧώψDΰEnQΠ'eS™yϊυG²K#Ό€12Υ‘[ΞKzσΛ²m¬γ―+όΉ‰#p«Θ©ά‚ž€Zύ’D£ζ5¬kDkιtξε]]£ξΤRAQηΕ‚†σΗ"οY©x Ξy†Š\mΏeήλS—Ζ©Ίχκ™Uϋ’·G,±Χ‹kκ; (‹cIέ(&JΧ‡Ε2τΌΉ`uήTŠ&žZŸΥͺ₯cλ’+ς©θvί˚²―j- ‘”πΒ’xnjθxΰ‘[—Jμ§₯ZΑEΘόΛ =βλkaGNͺΠί(qS’XΦ°R_7ύ€š†–YΑΨ¬‰kςuy( 5Πΰ|°AΩ-·?«ε,‡gΗl1—7εΥπ»76uϊŠB{»lκ­’68 E6ϊσŽΥΫCΝγhnϊ:λŠFk€ŠhφȜ νθγ.Αχθͺ¨Όb|Vm‡θ;+άm,ΩΆc]ϋρ£θλb£UΖr-²²¦bΟΦ*ΣΘό·`'cΨΗcCoHΞ @e)_­fψl@dθ“A}2φ¬šέυœ>‚ΧOηIŠVN@ˆ>Π₯s;ϋr΄ΘfLΗ­…–( NQ7"S½xΤG‚Ψ:Iέ4!¦)ΒCͺ›Ιt’ͺT³Q+ΣύNφΊ&‡ž 6ύ“₯iVΧ”!PcΫoΌΝD!sx²ykψρX9tυS=]gα]ΛΆόύύό8ΔΧθaŠΚ”Can„ζ†Jκω|HFδFŠ[3+q3Τpv¦“ϋμΟKΩΖμ“Z_ΩdΜτY}:ηΦ—έƒϊ„ͺŽžV˜‹Ίγ[.,%Χ—ά^Ι#Λ +LQ½υ^^ЁΚν+^w§ΝcΞΥQz>=ΖΙGPΓ”ςλŽ –uhΟA2XΉςˆ³+{nJ {&Zξ\ga[κϋΐZΧ3sDπGϊIl!Δ="Δ΅νDQo[άD21Vb/x§Ά¬UkU΄mo‘RhLs˘z0…iρnWθκ9ΡάFΕ²ς C3ƒΩδΣΔΈΖPU‘±8ηl˜ΒjΝΫ±8νxβ.…z³<―+ΰέή_ΙώϋCι 5u'š²U;¨ Μ[¬"Ι‘ΰ8Φ©o «xQ»ΐg©}Ι2­LZnX?N^oιώFΡNUιέν2ƒEΊ|«Ο+‹o¨†+΍ͺpI―ρΎS›—¦WDΖjEτ­ρΥτάm—aHΰ Bjw”S.|—@νŽRA‚\&‚F&Υ GŸ4<γ €₯Σe "i Λ-―1σύS[ ΠcηΔκΑ@ΆεzyΖ#EύΣΤύ&s^gMΊτ6ηYQαο«˜ψ¦ωλb;ζM4u9Ίazημε’°;]¬z+Ρ§:Œέ«ίξΏo&Š~•x(ˆ›»«‰j†ί*)BVSš*ŠD$ΨΉδˁ =€!Ήi4οiΧΘά²ξ«5-*ςƒy1‹Δ09Π!m†7θόΐ$Ij€Fΰ .«ΔΐyE#ӈΘυa΄Hώd‘qQέΙw Ζ₯šΥ» endstream endobj 249 0 obj << /Length 1634 /Filter /FlateDecode >> stream xΪ•WYoΫF~Χ― T₯‹Ω]ry胛ΨEŠ΄Ecω‘HcE­%6Ϋ {δ;©Κ$ςB˜_VςΎͺγ0'…ΗE4τΏ¨jΫu6wžU8±Xη"„†²R·Z뗏֧†> stream xڝΩnγ6πέ_!Έ•€X‘¨{>€›d»EΡ’±Σ’Ψ, F’cu%*₯€ϋυrHΩrœ£‹ ΰh8œϋ2qnβ|˜ύΈšŸG‘“ϋyBg΅vBό0Jœ4!~RgU:Ÿά[Ω^ΧΌιΌΟ«ŸΟΓ]ϊˆ~š3MŠdF {g’ΔOσΜYΠT=Aͺˎ#'ξJΞύ Ο-§ωό€° τ“4°$}λ-Β”Ί°Σ@ΏΡ@θv^ΰ>v=oπ³δk/ .κήVyς΅όΘ[D$t―=JάΑP\sΖξ½~&GφΜΎίTBχU]£ΡnŸ‚ζ`:υ³ΤYŸΗ1j,9x²Š Όι-h Μ ΩvVΉσυs/#0<Η—η^»­T„Τeκέ’mnkήsDΦUΧ#ΤρΌΞVRhlVxesw„ΨŽs1ψσ3Gͺΐ!xρaΆ‘,ρIYVw­ψ•5Ό»"1iπχbη~š&φ­Ύ Rχ€α²*˜°κ€χhCβLέg&ξόlΉš+0uu$ΕπxΟ++&ππΚh„,—Τ-[ncπΨ0•wΚΫ‰N—+/΄ΒpΡΠU¦’tΗDš?‘c~*¦r8ρ)‚‘9cs)ŒΝΰί, §F/{&J&KΜ‘•Ζ.₯ύG@ο½8vM]σ˜!¬ŠCΊ80Ώ\iσ£-κ=½dυ1Ό/ό½ N2`‘&~žQΠ0է֐‹’-+qσRτΝ;cυιnAjΉ†…o6‘™G( ’έφΐTξw‘ϝچ ψ%f1¨ŸC BδU·«ωn λβ#‚|ŸbΎ–σ]*  R€ϊΞ[Δ„ΈΛvθ7ΪμΖ/δς ή©ΰ(ς’UΓκΏ&Ώ ζ<ζG£ ³³ΥμίY K6[wL‰Ÿ‰S4³OŸ‰SΒ%ˆτChΓχš΄q"Θ°4 εμχg]§ηΓ΄€βI81‘PΟωΣω°γφIq…0ŽΕqΑ{YqUgw&Ao™TIΡ©LB„τ"hz[Ξ­rT Θ°V[5Ι±‘YϊvθςΥm_΅β`šνΧ΅rΰ›ΐψδNΆ-ΜJ33$(MέVԏˆΓ¨ ^W+`ΑΊ^ΕsE­ ͺZ#NΟ9…Ψ^fΊj"αEͺƒΑψbuUšK1aŠsνΡΘΦύp—Ε $/Zέ%ƒ¨ŽSς§VF=Ν­eΓε6»UKΠ8?i;4bhΥ#«Q‚dъz€g5^9₯ΏLΣθ±hͺ›Α‘=’Gfz/쁺ιΧP˜`D₯ξ_€±kι U%ζδhΩDνT›˜gϊσZ²Ζd0^’\a»ξΦ υu_©Φ€$•Α­Q¨;ΨϊTR©Άζvsb7όνMοivξ-tͺη<˜†S³―“Fs~ςΛςΜΆšΧ4;ΡΕ{340O^nΛ/kφπφ)r‚ԁAWnC±–P|OwL`Ζ‘e Kτέ;}μφ=© l.aΆέ\^š”†>IC3 Σί•gίlΦΗυΕa^ΩϋΥΕεΩ‘½@["5ασΜύgΠKažšž¨Œ„―u;ˆ)ΊΦ pιE€.^……Ϊ€ΰ%T4ΙΓJ0©³lάuώΠ~ dιhrό¨άΞ¦ƒ1ͺŽϋQL} N΄LΫi"ΨΪRύQvfǐœ™%Κ.Ξ΅yΘ„Ή`ΊO?)[t‚faπʚb«β/‡u½ψ·L—ς{ΫIτ^«•³έN“Άfδ¬ΨLlΪ3@Z―ΤC#ΊwΣ_pπ“+ŠΥ:ξbτΊ’4ž}ύΒ*Δξ0C <.v•…£Θiσ΄jNiA=§ή'nyΡσr"ώOœqΩ±’ΆK³ΚΠρ  τeXΡ¬~FxΥΫ_}ŠΘ¦Φΰ·ηVΨjΓ·6Τόιάαϻٞ°gύ/uώ endstream endobj 269 0 obj << /Length 1445 /Filter /FlateDecode >> stream xΪ­X[oΫ6~χ―όd1KRχ{ΘΠthΡuX›ΪΒ %:ͺ‹«K³ϋρ;‡€dQQgŠ€‡βΗΓsωx]κά8Τω}ρΫυβΕkΧsb ]zζg]UΒΤ(ΏΊ^όXΰ:uΨI/χˆΟΈ“‹/ί¨“Ββ[–βΘΉUΠΒρ#‘η‚œ; ϊ¦£2ž»cγ%Ηœ†Δƒ‡ GBk'q48ςAŠt½α>] =€2ϊ¬•ζλWJy.΅œ•meaΫl·ƒEE] ζΒ:#±oΪgΉΌP‹Τ^P^­7>₯«_υπώΣ»w³Π€Κ·₯(dcΑ―?|Ί²αΑ ήή'πG΅72—I{>KΟΓ5ί³£…€³°r[ˆΏ-ά›r?,Α°/—zΆ|Ή„l6"?Ίͺ•ΦζεWζzΈy6 E!K;@ΡΗ­,ŽνέΆnŸΚΩΤΦY±= WIε6­:δυsΆμDς½ΙEs°v½Ύ|χqž6y•ˆΙ©ά‹.o·z γ}?ΔζΠ›N6Ν½TBy˜E‹Ό­ερΔ—ΖOfψ@ΓqAωΰΘζI‡’‰ Τ›{#KYcsΣ.²0" &±k«B΄ΤΞόξ%μƒ’ώz"Ζgή e›‘Υ<ΙίL·ΪΔI/$‘ουHUgbWΡ9φz:―ΩJ1ΎŒνQκ™ςΫ~"DήI£FGχν Β”b<’Ρ+ΓfMΥ=ΧTτ|ΈtŒέ{φB&UœΕšUœ1£„β8(Ž₯žρΤPEc2ΣΌS[υΟSύγCψά!#hΥ΅xΚά/ E]]uο'ω¬i²ςf½a1vXύse’sƒ4°ϊύεLΆυsƒŽJaOj”MPΤ±@©'³ž ³Œ/Q—`&r\ΪΙήR蜹4_³²Χ‹―”(ξtЁˆ\= ¬ iνz‚xj>„°Tο}haαH  g»H]AΈ¦ΑκUwΜ³D…„ϋ|TΡω@d ›@:EgΒ,Ÿ"hΎ+ƒ°3Δα'>ΨcE₯©iχϊ)yΡχ~9γcȈ{jjγGι}_}LΑΠ2ϊ^R•m]™Ηέa Έzvτο’~L«rςςύΞΏ„‘£ endstream endobj 175 0 obj << /Type /ObjStm /N 100 /First 867 /Length 1506 /Filter /FlateDecode >> stream xΪνYΛn[7έλ+ΈL6ΌδpfHF€<ΰΆ@“E[' ΥΉ ‚ͺR!Λ@ςχ=s₯(²-Ε΄%)Π…uω8$‡σ:ζΰ‚‹9:|ΘUτT]LΦ͎mb—(£_]2;Ž-Α±F|£“ £XΘ‰X?9©6ΟNΩϊβ΄X_]Nθcƒlέμ ΩtqΕ,ΥUΜEPΫ& ¬ ‡μ/`Έ²u@h΅ϋ±σc턃cΉL όqNhTΜΖ2’`0MŽls  œ b#ΐi΅l‘ΩFŒ ΕFŒΔd#Ωh·۝lg#R±3ΘΗ4α§:ZR©ŽRbτ°2ΩͺˆcTb΅=‰Mƒ§$bΰHA αΒ”CΖΖ-ΰ5Έ\Šΰ&…‰ΐ?"r‰mH*‰€ \?™$ˆ 7%¬2YIΒQR— 8ΉTm¨ε`WΒ>l{P*hΨ΅Ruœ"”œζ€6β˜ƒ @ΨV3‘QA2˜Η2œ)hΨ™Œm%Ζ …¬©j²Α)mcU’]Š$Ζs!Φ!l.TuD“˜¨I (˜ζ„“ν0gAC ­`°“lr €9b [₯8ͺgpRΝ<"«ΑŒ‹iPΘ J’Ρd‰ŽFœG’hT)N“© ΞU6-10–γ ΩCrJUG''£ξ…;šaigϋυ·ίM³}qJψBzΣ«ΙδνθΙ“―‚%‹W¨B8Υδ+ΘΉ>MξδΔu§rMΛU§lŠWˆΔ/ΫPwŸ΅±Ύ{9Ÿ]Όκξάu/_œΊξuqαΦ[ΏώτO‰ρϋ~Τ=Η1ύtqi¦cΛGέY9»š_τ6€Λ‘ŸϊwΖΟfέΉy ©β S‘/υ-NΟ±\Α€ήΈlήΈl­Γ]sNžαOn1&oaL¦δMΦ‡kaΟ©6‚3yή4ξΔF2„ΘgΘ° Μ™=΅²ŽI|.dΐ=ωΪΊσ½ΐΠρVH07^Ξία]vΝͺ6ΝmΣΒΨΒ”Μά–g\3·χ3·EMζΆ‰][³‡okͺή"r±S=>Ή |ύώΟ‚cτ K˜U}Μrο@ωΕZwšτ͘"έ4cΛ οaΖσώt:aΗσ!%5’nœ<̏ΊWW,†ώ¦Ίg³ω»~>œήvίw?tΟΟγΠ1š.pΦΰ-#7°H>!“”$ž™z:0ο•λΎ›½ž9πώΡٟSΎŸϊ³«‰ŸŽξwΎˆΖ’[«ŸΉ†]»Žο4‚όR‰m`TˆMz 0o 7χŒ ›jxΥ#½₯zΔ{ͺ^’ΐ‘xΎVίΈϊ!UΙΏ‡9‹°J”‚b9Ύ‚^՝Ίy1›\Ύyτζρ¦^ξGHJ¦d–be]Kš`2θ–β9ξ6’Ι‡ΛΕ‰@Ν€ZxEj5Ϊη.Ο ψ/Qf£$τΑͺ_8έ‚ŠŸ‘p₯πuJ—1›N>X< ΞX1oS‘χ€2θQZjέ-žΩΕxί$Ε,œ€έƒmbοΚ”Ά‚…‰=t4?£‡ qu-)ίr-Iφt-΄€Ϋ-θBbΜ€εP|F μ ¨ΟDΧΒ[?/fσΛ#h)ΌWύ¬₯e?@G“Άλθ&φNέΦ½δζ|ή’³Œ$₯Fm§Ν_6aIͺ§›)GCψ>bζΘυ–qήΣΖxI­H;T˜ΞδνΝnξZ±G7ΞΎnϋnΌθΧIδ26LΖΣχ‡³8$³”Φ„IDΘB}?Βn—vΓΫΔήixΫΐο`™Ηΐ©ψڊ₯Ίυνl+˜ΑέΊ3B1ΘίRΙ&·:ν­xOΓΣC\*+M „Hbχ0ΐȈjΘ$jή©ΧΏL.gӟ·ι΄΄ΏΔ^Γγ##nΔZύ_£ΜVδFpBζ¬AΑ(‡ 7^$ω(ρzM$½LτAΑdγκz?¨Ϋό ‚yήςΎ?XQ"Ω?†ΦΰJ“― endstream endobj 279 0 obj << /Length 2385 /Filter /FlateDecode >> stream xڝY[oΫ8~ο―0ϊ¨Y‰u)0(2vΡE· LΣ§ι £Ψ΄-T―$7ΝΏίs!uq”Τ ‚@Ežϋων/v ρ―Ώ_½xύ! ©H#-Ά‹ΐχ… £EωB+ΉΈΪ,ώς~4u].ΎϊχλjΌ8τ'@‰V —Όπ-mχ|ύAͺў•JnZΙηxλΊ.»»ƒi'NN‹#‘&φίΚ,W2U^½eΞ&§(_ΔͺημσΧOŸfΨ`(΅[τŠΙe–ψΙ’ANxψη=Q)bΉύ CϋΝΧ>ό‰‘€Hόž΅φ`Φω7ί—λ¬Λλ ˜P~θՍc†mΧδΥN,Waθ{_Œ™8PAθΘώΘw•ι:ƒ ½lLΆi^:ΎNΨIbωύΎ-œ kΐJΎA¨« ©Φό΅¬TΊφ½ι²Όh-φa•Ϋ$ΌκγœApτd{(₯½¬(p’~eΕ“μ+4}›σνέžΙΛΓ±3žά6Ξs§κŠΐ™’ήv»£iΫλ2ϋ9ηδ‰’ž«¦^JνέΆL½ψΘnox"―ΰlΦ  ΅πe8Υ`^u¦›7-2(# [›ϊΈΫΧ° ή“Βθ…£«0φφyΛΣξΉ†³Α~,ν™*7•ݏΟ*K|» |/k;΄=ϊwZZJίs‡UuΗ ›šηΫΞΘF$ӎΨgζdΒ:&Εχ؁§(ιέΥΗ œBΫΰϊʐΌ0κjή‘WkπΛΦCΒ3VJ@χ:9ΛJ±ά:Š Ϊ‡βŽλ9_ΧMcΦΞFJ L…dηB71ΐRΕvΦΟί9‡τγΣ0Fχπα,=–άά=!³¨X(ΩGe‘“ ΟI-©–#5œ dH>#•Ξ+Α{XTπMH+‘ΖDΝ³€/V| Nυ~’C°@RB’Y^Άήσ “|Έ*M‚±xPWduέ’[Rh„Š]f1ς(γG{Όi]T[jμ0ύYΰΨ3ρplΝt¬EU|Œž£gŠ8νS;γ*†ι©K^^*P&)N   ˆz ΛP*Zg‹{lΝDλΊ‘…)5Ή{ˆZς8΅jB­:–7Οdk3!΄©7…yGΕ„PQοςuV<‹₯턁uW?OQLεƒΜω,žξ“‘Q——ΟSW7!ψ0™_πυ²UΠ“‘Zφ,vmδ4ό\MΈkΏηŠ»D„ΡI]ϋύσ₯μΖ†:b¬€":~ΞπXψΑS·yΗ?d<ΧΓ1ίΛάΈΞ3ψ|€,2έ\šv5—Ϊl‡ΦŠA=-Ξ?1Ϋgέ Dˆ 8ސξ :€^cŠυΐ1˜ε!ΆομΞ°¨ΐπj™€ŽyQcΚWqBε₯2ψ ² ۘaγΘΓ:rŸΛUΰGBŸSiχυνυΠϊŒνψαςΣ—χsθΔ‘ž `Š«XςŸϊ@ψ« +CkO8eNƒmΏρ¦Λ8τπ@¬Cβ€M吾JΗ“”ŒD$YPυ„ξ-T)I€] ·8Àȍπ•μσ«F‹‚šΓ$Dτ}ΨnΙ«Yπ¨ #κΝΎ(ΨΌyΓΤΗEχ”O¨΅r(΅«$NΌ―ν:UόήλGhBc£d0ΕeχΪbCVN=δψβ/%όD9Ίoΰ.ι8`*‰nŽυηΑ`ͺWTKωPo©Α钞·UΞ©`‘γ–’N΄=#sT; A;$ρ– {‘θ²°!Γ2ΗΙmπ Κ€μκ;¬u;ςΧ ΅MΨζ|G}©Ο`”)J^ψθQτ9΅ύtA-m*½ ΑJέiχPs€-΄„SԊ)—.qΤ£ηΡ~VF“[eΠϊnΟ£ΞfΖ>™Z-VYiΈη³k#w4 8½γτ―–Œ>dΤ_†ΓJ>Ί8Rf„lB’Αό ς«ΪΣ##hκ1­^ b­(ζ*ΑΓ…άBod›”\!<Οʟ•qΏ²εv₯Θ+cϋ8Š,xQݘ-,UΟρF†5‰šΏ Rh|‚‘ (KμάgB;†κ“Wn‘Ž6w%Δ4Χ.κ5λrεšZ)Βπ€Φΰ1Ζ"H+3w‘“ο*h܄ρɍ`τ,g[ «OKl ΄Jϋk-Πԁ[¦Έr ώy–ϋOφ3/ε\svΨή–ϊ?ν“Αžž5ͺμ|ήυύΗJA΅bΰ@ΐ˜ήΙΕΰΙYm›DμdIΜWD—;~ΝZ~–9$jς2"hc²ί*–x)h—3.‘C‹αl=γŠ'ι’ο™)8:2Ργ‚Γ:‡³+;Ϋ3Œ/c†υNЧšί±>ιUώ8Υ•Œ‘€ΧcλbΐρΞ΄ΉΌ°5žœjFA|ƒ—xžꈎzΖ΄eLC‘šωφŽΏΫ3ΠάJFή₯‹|ΪΑ₯Όγ•Ÿό2 ‡LΚ©€εΏΚΐmu’£•!Oœ©$΅=2Β΅)έέuSߞsΡΏ πz)°F…€@,Ώ)²κ;‡λXϊ`ψι’ ςΌ«w«8t`!ΆχKލ”%Ύσ aŠeΰ1Δ@BνLάHΙiΤίδ\ύωu©Kΐ5Α€Νφ¦3Ο‰°ζi“6((€ίΤΒ€d„g“Br¦]vAVτK½ƒ™ά~™•&΄6πωPί T’Γi€δ¨š°;Η%Ɍ)?MNnΕωjτώ―+‘ξϋ|9‡«'K†γ’­q…½π°ρΰ.›~•Šό@$I2 uˆΨςϊφ ΏR9·U)Š' •*;Ϋ5€qϋιΫ}ή™φ­ Ε\|ωεέǏόJ_ΪS ٍΕoΚjžιζτ›ΉΘ¦ίDHφΦFˆMa“£C78>d͐ΖσξνS³£iΧΩΑ\Ο\W=ͺΈ?j’SΪDpΠίHΐ˜©ς˜R8-Ρ“hŽŽc­2‰ς-v8© ~atΰ[1aY_1a<ωq2Ύπ~ ςοŒe₯ΰ O‘ΔρΤ"½λ²š/^ΎΌpPez_šΉœ<”8•Ϋx!f„}υβ*v£ endstream endobj 287 0 obj << /Length 2121 /Filter /FlateDecode >> stream xΪ₯XYΫΆ~Ο―0ΣΪ—ΰEΫ$mŠ4·H&mαgLD‹«%3ξ―Ώg‘dΙΡxώ/Ε‰§L}mp•@„vΒ’ηU*ΥGΜΐ{MjE^θ΄*ΫΊΚžeκe“]ފ‹fnω·ΟK7°d}ΰΩM QBΤ>—©aF@τKmoρώR πΐ±·γ|ό°JΘ§SτJ"!°παΚ{Xr˜ή.]ΫκZžͺŽ φ 0t+ψρ’FȘ|ΰNΰˆ {θXTΌο;Ήο 'Œϊs” D­d«Ιj¦*DτΈף4Ξ.›ψ"ςO}v°™η…°Y—·†ƒυ$œBOgta~ϊ§*βΔ–*Σ*^fšœΎH쁏`Ρ>¨'„Όλˆ8ˆ'mΐ† vIάGpέAΚΑ…ŽD—›z©VκόοqΓΰ!χΩTϋΆ9Ε{Π‡~ °t ΌSHAٟ—;mΜ |6νXΦ\ξΉοϊ˜‹]v”³3‘EŠ:ΩgctoUς>άΕQ˜K€(@ϊŠͺώΗόζόΠ\ωvh½1WqŽΕc™lεœχb•ΊDiΗ*₯β>+ΐʝƼΚ46`*ΟΜ/ά±—uC:&6l'σΒͺμΪ dՐΝσ/mΝp†ϋβRρΘϊγΉΉv ³Oϊ1i¬ήa`‰'[s_W·ž`Κ—ΊzNeί‘\Γΐ…f[Y‡Εή³~8pΧp,8PZ^Σ-ο6PEZ•ο Ψρ²Ÿƒε‘ƒh#Ά9Π`zΚ9ΰΨm‘²j™€τIHω©ΤmΛ^†€,fUڐ&±m ΣΆΐϊ`OžŽ4‡³ 9/S—Δ֍$τΌΩœοm¦MT½h}kt91cu8dτZ4»κn36剓H8^0κBf³ͺλ»OM«gsVΓ{ȟϊI υjΓuκι₯Οs}dBρI`y@qgΏτ•va׍±šr©Kž9aFϋŠ@ΰ=Vœ ™ †fξά],–¬·mγ (}S—Μ1$ξ(.pΐIβA1Œe¬Μvlι9qCWΐφGχψφBŒ V|ŠF§:£z*E‰e3qηKB$C5χρνΫ9  †&`X7ΙRxΙ¨₯ZJ‹Θ8dΠ„w[ϊ'ν'ˆmiš3byΏΟuJ)§­eŽ™ώ3Ά{£S½αŒοΔπޏÑ[ϋτ5ΰ‹ψψiα|ψxv(Ό$™–|=σͺΎΤυεΑσ3€ΏœƒΕN¨ώzΰYƒΒšiΕW~RΖ΅ρ+ΑAwDGηΟ!t^•MWΣ“6ΑŒχ i­jηRŸ*φ-^ηŜ’ρ™•ϊοŸ‹TΣ²:_"μΨp<ετ£D­—L>*ΐu}~WζV]^ήFhΖγ£No!³ΏxΑγ%ΝαΚυ"itΕΠΚ,Σθ–ύΖ;u™ζ]6T‰¦ΫοσΓ0νšώa“)τοReΗ§S&Σ„–©I ΫΔα©Iν€/2IλΥ½,φωIβ~h$Τb`±Jq G"Γ―oΐΒ6„™j™ΨK|"…ΝŽŠoγɍζδB6ΰfBB—ϋέΠ―΄ο?+Ήc7,Π*Ο‹6…M€Νηη½…Ξ03ΏB Kœh"ρ{κ»H&ϊꁔι0>ONΎAφL6Π’¦&8Y}ΝΏ9=ž&ιψ$Α†p‘x,sΙπΣ ΤτΠyc,HνlQ6Σ54鐭f9ρΝ&ؚCο*mέ•/Ξ0s !Ϊ­ ϊͺw”ς*Οwm»o^¬Χ·π θΆ"­Šu½ΚυvM[Φ΅Ό[πXΓ[’]«ϋλsZDS-^•ή3VζAΕyΗλ\η ή΄υ 3Ύf8δ{‘~$¨d ™χΈΕ~<΄IoΖΙξ”—'|?9Ηjεΐ£”y€‰ωσϋ«}u\½+ώ̏¦&‘φ*W)ϋ― Έ3Žυ›lŒ‹§£›M5D²2–e¦ξU3gΆ>υ±Όfm§Š CυjΈ3ζπ­YΖΝE•©Όίwθ©[%λSHΏώ.ΗμρΜθ8γ;Ύ(―Ÿύnb«Π endstream endobj 296 0 obj << /Length 1715 /Filter /FlateDecode >> stream xΪ΅XmoΫ6ώξ_!d_lΐf%R―Eχ!m“‘CΪtŽ3`h ƒ±ιX¨,y’œΔ@όŽ<’–ΕNΡAΑ#EήλsΗ£]ηΞq?zo'½WηΎο$$ iθLŽηΊ„ω‘…. u&sηKΎ,ŠΥ”gu)Φƒ―“?_³ζίυHC΅Ω δ–ž«EΌ:§Μ‰aoθΛ½#3ΉyD#yό6ΫΏ,qάT‰z©‰JdbV§EŽΣ₯ΘΦ’¬€1ς\’ICΥ7pΣ|½©§‹4C<4+²)ςΑωοšuΝΛΊš>€υR;™ŸΐΰΙ†=υHβΕ-M)6š)Χ*ρ¬**EΞWF₯‹tf5Ψ¬ς_£v–V΅ά?γek}UΜE¦Š{QnλešίɝƬ#¦½SJjoΧ’BrτSΊLΦςήnqœ‹ίd΅V]ωΙ»¨*£†E„ρεNO}2+Šo`±ŽŠv ΪΥΡ)ͺΪΤΛ²Ψά-‹M½'oΞk^‰š΄Ξ†kYi‹Δγ:Kgiι/{Z_cάjΝMΐ †ΈΔλƒΰω ό8άήxΜΟ½!U#ϊjTΐn ͺ] ž“ωμδΡY©bΡ Q#B―ς6#ΰλΌΨάfΒ ΎqlΆδ%xV”?琌Ώ8fΔ¬Ί£ψπEά!%ξ'AHWνθ[ΑνBžδΈ[²”U[ιjΦΰˆ”KΘ„²‘dZ«εZξw+σϋ -ώ~ ΞcΑη­Χαε³²¨4½‚²” diώ1 t™Άͺ‘pWΣΫνtΆΝΫ›Q£τMΕ#_­Β­…JΉz»-βy|ζΥ~q•6ΚKL―ΟΣS­κ’‘ηОΤ_y©yˆΤ¬Tδφ΅Ϋ4σƒ‘n)m,λMzφ<8δ:žνPXβCα9³UοΛWΧ™ΓGΰKX;jλΚρ=hL|tζ\υώ²=Ιώ¨Ϊ hSmhϊžΓ’˜ΈnΠέ5z?j¦$‰=Σ]-‹‡ΑˆP4—Π xA‰SS %­K‹$αFgQ_ΰdS©ΛL’§ X}YpJτύ§(γλθ HbŒ/ͺY™UΦ΄½Υη5x@‹ηΕ$ :|gjoG+QB™qœΊG₯ε·ΦEν ©<¦=§š'νΟB€i%¦­έmh#τlgΛ(aQb·•/†~d¨FξpΥ‘/ΰΔNyηrόΔ#K"jyYΔ[οΩVΑ'qh­d“ρˆωΰθPφΤ!DΦǝΧΏ‘ϊΙΘI/[eφΣυΕE#ž-έ<£Cn§ΰCWϋ°:¨ή΅PŽ‹BHBjw:± θΫϋ§χ2H…žΛΦ…‘ΝLΉ¦3S‘Žκ"‰SŽ:³&°D£ύw<Ηež Ό~Uΰ€ζί€ω]€1”³Δz2Ύ>λ$eΔm‚€Δ§ŒF̍awA±7ξ:?½ΈκdAqˆ C“5Κ@Ss€’–bΊekΈ#ΣΫLο5Ÿσ"6=l6ͺ\L(fZ +οEΝΣ¬z¦ά΄ΜΨGθ-ΚZζ5QVΕf²ϋH¦ŽψxS %ΎJΝeŒ"ψpσ₯e‘λ" ΨΛƒeΚoUg@©~„ΥY‘Χe‘αΪΰ†n‘;)Ρ₯|=ρ u(Κo»'G#œ“₯PττkCΐNQžͺ—rϊ$R½e[lΊ #½'-2c‘+»ήϊπlίUθΧ₯κ₯Κ€sΘ€ίO<|<Δ$πmF‚;ΰmZ½~-ζi=-§ZζΛΑ£> ΫΥB%μ\FW%0ˆ‘.Ί3ε7H;Ά1νΎVp‹ΙP…ƒ²ώ§@bxΰ“ ,> .+$ΑΦMΞ₯l„DšqLΨ Ν"ήΰVF‚–Zi ϋgΌΤ²ω¦.VΗ:Sx„ΥzSζκ*Db;PΒσω^ξc––Zcι8“ΐ8 δ υ‘ w'ΨΨΛΪΦ8,ZΠQτΰRduΉ]a’Zp;ΨψΝvkΩάΕΐ“ΈΡ‹Ω> stream xΪ½VKSγ8ΎηWΈ8ΩU–dζ °lfœΉ•ς: v­cgύ€Ώ-ΛρΔΑ0™**‡Δ­–Τ/υγΓΦ“…­ΛΡi0ϊtΑ\ΛGΎ Β VΑ1WXR`Δ΅‚₯uoα<}ΊpwO2&+ΘiΞ<yΎ^„iU¨Ν"ίT₯Ύ2Β­’=-cFΌζς˜JΝ4"‚XΚSΑν°#27T’-‡rϋ9YΦajxϚIψwͺJΓZε…!TΕ†ͺ~lrƌsϋ¬. •U鳓g@5QΆcη1γ[ΧΎί~ωr½˜ίM“Yp;ύΊ8ϋσΦάΪ ETtYͺ•C°Φ©‰8-(b˜XcBΟΉ9VεŒ¨K·’ͺ’VCϊ€€‘¨oΠ!$,χ|sζRΎχ,=΅π&Βϋ•ΧoΎ©Ή=³¨€‘πβ,8Β«›#+<½Ί<ΎσΣΩqήΜ―VΘ>BαμςΘžΑυ‘5N‚ιq5WΧΣΆ ±^+š€&Ζ‘π7σΩlΗ…žϋMfRθ°=Υktοwš_ΨfΜx§™JΥΠΑ±‰’QaT©Β,ŸuY¨¨ΚΫ5<|EW"šΧ–HCζζ[—Κ“&4ζŽΖ°ΐΜ*ߞ…™aκ%v™›Eώ£έWCπΥs‘οw―­[Χ@JPνžmΖΧ‚ 'τL }ΔΪ΄·!ͺTv 5ί–ΏϊYόΪΑ&α·)¦©!6yY&€βΫΜo·³> stream xΪ΅WKoΫ8ΎϋWΩCe bH‰zφέMŠA€mSSŒEΫBυp(Κi}‡)b¬€)Ά‹ E ηωΝ72φΆφή-ώZ-N/(υr”'aβ­6ΑE4ρ£8 ½Uα}φ’mλ[Ύ$±ΥϋŠ/Ώ¬>œ^DΣ‹”f Uί ©Y`kη|΅Έ_Ψb<š)Βiκ­λΕη/Ψ+ΰε^ε™χ EkPJ#ΨWή§ΕΏ£Ύ§«Ž#Œ¦qŒJΌ§Η‘Ηc £ΘBκάQž‘!šw\.ƒ0ΖώžΙΩΙΦ¬±€±jΝ#_†Ψζ¨Σf#Orв˜ώJδ:Β(š©T’PD©πή­EΉ—eΫ8ρ=Ζιd^@2”eρ$AΛ€δΨ_·5„ ·w*¨Ύ)*^˜ƒ‡R₯Aν˜YšΎΎγΒμۍY;“e½ΏΑ8¬uecWiή”M'O ΌdΑ${cΞ‹Rπ΅lΕχeϋ gםŸSν(Κ…„β™:w78ΖπOf@ HwΫ, ΅­ΚΞ֝U•-ϋŽΫ•“Γ2Œ}VVμβCύΗι¬`SΜ8LhYΟf~Αί4EαˆΚΑMΑ₯(Ήrε ¬σ'žcΆm޽EΦΪΤΙ €!ΒΚ L¬h³ΧΫςYpƒΝy}&hει8(Βε乊‚°δ cμi–«λΛΛIζœXΘ“0ΞΔ’`ΫΧΌ‘έ‹α<†E‹Ξ―Λ%nΝ ’p(Ϊ«mςUŸ¨Υτz’Β1XI‰νκΌΪτΩΰž\TΥ·}Ε„90(Ψ ήuŠ6ΰ,ΥΞ©wΖΉ†i.P Cj­™\ο 'γ4ςίoζ—ŽQCafΰ σAFχ˜R=ΣZtΖ?&ψM α„$Eyώ€‘{ΡπΌŒˆιfυ0ƒ pU§wŠσ)‹Ώ ι(Lθ‘T7ΈΦυ‡η₯ζ΅Σ±κΝaŒQ=ς‰QΥ0Δς8©ι^°‰kNO-΅1  vͺzjΥ 01ψS{ΚάI-ΧLthέNΣσσ-J ?HώNφ( ρtpέnZΈό…Αώ7ΰΨΈˆή,j™έF¨ξ›v ΠAŽWe]J5ΥcDάl_Θ L›8N~Ο܏pŽ₯mξΫλ«]©ΖN}³&>ΏοK3RTHσ`τΓΚ<±υ†νΗ#ΪΚŠβȝq5οs.ΒWMς¬CΜ—ˆuE}5pV˜Ν¦ΰ·―j08«Χ’μΎZ ₯½ CΘΑΝΜ]S>ύ‘‘ψο₯›½(k&Κκ»‘5ΐ ±t“'ϋŽozϋ‘P|ι‹ΐ•‘Ό“ΪςKs+ύ]γw¦eTAt\J> stream xΪ΅XKsΫ6ΎϋWp| 5c!ΐ瑇d·ι$Ξ$–ΫCœΡ@$eqΒ‡JR±έ_ί],H‹ eΛI{,€o—»ί.ΰX7–cύvςzqςς\ΊVΔ"_ψΦbmqΗaυ­ΐw˜'…΅H¬Ο6g_Ό–Η*FQ4ό­₯:ώ―^ΆuVήΠ±:p;€EΦ4ύό $x~»Όm†ρϋβb&= ΞΉύβωŽWω²ξhŽWδνzβgEL²“8υξςΝ”λΈ,rόN ½ο!‚]QRŸ@Χ΅o³<7ƒUKUJmVΖω.ΡAefۍ™n«-u0jφf\ΎhRΟ›VCΈ‚9²‡Έψt5₯‡˜ΠcnΌ>ςwωKŒŽN―£υ::€Œtͺ!F!F=lL»Mγ ΥH“³ ψ<”Μwϋ0όά‘nΐ\ήGΧ’¨U}EgKΝΐΖtͺνΆjPIIΙ™γBϋM΅oήdψύmΖ= ²ήm'"=„ΠκS±Ϊn‘ψ&ά'baΨC~*\=Ζ}>bα&VΫτ™D¬Qίo»μΊ¦Φl5Eˆj“f7Ίοͺ65«j3uΆθΆΗ<>T €?ŠΥk!ΌΗ”™λχζIͺ݊hxŠ){±ω#8IGCT«{ƒχν‰ T(FΘηΓB(Αζ\²‹€ΎRρΧ&WΝfΪm₯?Œή”KΫ:Σ€Χ’?p¬Šη± ϊ MΚͺœϊB@μωΗ(A ΝNΥ€#‚‘Χθ}Ÿτω>ϊ~§T9τμ*“.ejφΙ“¦sυ,ήP7ΚVuΎέΧ']ΊΪ¨ΆKΉέOιv_₯{Hώλ@ΐσδΙ’Αό…ΊPκΡ<‘ςž.‡!ψ χQy>•}€Ή₯7Βύρ­TΎ{ŒKx†₯ΑΕg·ΎHr@}Π²ΟρλΘ°DUοΥj– τžHυPν:A/€,ΘΉ<‚ΝΥβ|b7°_xo¦Zj5»cά”ΏΙΚRιzυ2}eβ9!€¬‘yj}θ ŠxȊδΘp,τ‡ΉΧ$ξڍš ¨!‰ήΈμ‰0CΥ&t V›]±m³Κ¬Ρ ²λ]œζXΥ(šΟZjM‘’°Wœ`Ώˆ>u\C[§*!YΚt0€‘μί΄:πp+%ΕΈͺAνŽώΚΈzΰkΌ—iΣλ›Ω“%ο¨ (wΕ²έ ΈζxΊΨ+νΏ‘  D­h=¨•†–Šθ˜r(EΨaXα;\BλLεΩ?fNš(‰ΫͺΖΊίu|*aζΎΪ™ΓYΠ°/’‰ψΰ^C#haMΞΠίγ3ψ"‹C.ƍΦQ‹γ;§΅k+€›Ε@-χ4΄2k΄$f£ŠZ2›Š·œώ]bc|++d^¨£‰ ή,Nώ>Aλ;ο_u nš3²ββδσΗJ`Άg2 ­[-ZX.ΈxΙ­Λ“ŸbτΣΡ°v†LγCΥ.}ΞxGΧ‡ž…€ΦχWŠ}WωΤ«€HξK·YΉ:}WvΩP žoΫΒ YΔεslAΟerβΉLβ&π»Ηš&3↩׬q:ΎbMk―ϋ‡-0~†Γ#…Ο\HbϊΜ«Fέ€ΎΌχΧm$ν»(*‡ώ»Ξςτlκ΅o[ϊ††kΙϋj`Ηe l{ϋz=ή|ΝΆ¦[άΐΝΏYκ ξl:άσ%ήJ†]\½{wPΌIsΝ§ΗΘgΙqryœd“tΟKšB*QͺΑΚ…OOEN/^~Ώ²·5¦Γ‘2ΈrŠoΐΘdύe]έν…IrQ[gΕςXa> stream xΪ₯XݏΫ8ο_ΜΛ:@£‘%Ψ‡ξ]{θ‘ΨzΣ§ν"pb%1Ξ±|ώh:ύλ—eΗΚxf2³ €HI‘ό‘”ωbΏΰ‹½ωνξΝνΗ X€,D΄ΈΫ-|Ξ™ ’EqJ±ΈΛxί­λέi·όσξί·ετ@ΐ}'ΐΝPϊ)’Όα–νG!'΄+K–ψρb%b\€3ϋ^΅νϊ˜ύXBΞ½_iEή"/ΰ³ς}–†!QgeΧ¨Ϊ!½ϋςυƒKMiΧΊξZη@κFm³NείxΘαߟUυΗuwhT–»ΗΙvηIu£χ άnζψ°υδωφ Oλ­.Χέ}­\.Ώύόyφ «²£ZΓ½³’qNάτUρ^ݘC+τŠtΝ…j …d©OΛΰχ΅:Φέ=ͺΊ+Jυ–ψ΅+\?p» £s [‚γQU«έέ¬.ΓΰσzŠΌ;μY πš0«u[t… ΏΆΛšΞ²SUώψU–t‡Œ ΖΨ@¨rβ'p*bΡpμ}³τ9@νΦ:»/0‡Œό„Ε2ΆςΑuπG,MΔ€ηEwPFBΖ^†CβΥYw …NO6b.Lpˆ€Δ[]Uj‹FΕu™zΊ‘²θT“•t2ΟΊ‘GΘΥ(3X·E΅΅iή5π‹ζΪ%k²₯z§9T|ΗΠD7hq6’€c {’`p£ tΈ‘σ’"8V °ۜɌ~ΐRΘ­–ζνζ‡#ΝO1Η¦"|†IŽ\~\£ΙJFΖv™ω1Kβ‘’ύ„?dΡ,G•NEY’‘6ŠΖ¬οτ1λŠmV–χΈ$½ΎΘטξTNΆ_ωˆ cΧKƒ d—₯ο!Ι°p*0ξpvθΊϊέν-ήCFζW;όŒ½έyβOμˆ~ψι 1,aAD³οa2Ρ6s½TΥjΒ”:ΛρJ«8‰Ό/κ¨;`πΤƒˆ€1± hiq›U4ΙΚVΣώΖ_ —Α$ƒΘ³*·› ,Ίš―93_„ψηua8 fEKγQ·ΝϊVνzK·3(ƒ‰B ύȎ΅ΉR·:Υv-Cƒ…ήέ2αhL\ΗλαεVο«’U–>³­˜S0A„‘Wts—:φF³8"Žq읚¬ ;X4±ρ0šSΊ1—}Jν5‰΄’"£dό6ςaδΓ”δΓdΤ;@dNΠpVOƒr³qσ΅=K’α;ΛΚήΚΠ;[Sΰ%‹K MΏ<¨HόsήΫ–E½ΡY“O½σXq c&’‹Τƒβ±£ΩΡGšA±χΏo;eGiμΙ>UpΑ8TAΘRVK(σc{p}ωό‡.ϋ#`ήOΉ7Gχsa €λ[PΎΓ@Ζ›ϋλνθCν§crΪ΄g °8uΛΚuB‘ξq9yξΐž“ω,ŠΖJEεηΚ›rΈ¨˜JuZ΅ηΗpY\" fRJΖA'©KςΠΖ”δvfEW¦&ΐJ«J£ΚisΌ*st, ȝ/ΈZ Y½»‰σΥV$’O; 4>’5½£%» †±ˆ€61H…›xΆ6‚²Α…aΧO•»Ω8ϊΙγKcxΐƒ`μδEέƒŒ™#*ΛΒΈπšΤ’†βUE²G$^‘Κ&ςθ€W΅ 0Α΄Ί΄jμeΛτ*ιΕχ4Χ†ψ©"°ο|υgπψΫθ±b$+*.‚³hB­ΞN&, 4ΰ‘kBVmCŠ―aΪ~Σ*K€-7 ˜QV;ϋ9οeέDΝΔΤΔkTτε ‘ν‘bΞΒ(tCς} ι₯σa’’Τ¦Τ›?αΩD汋Χν[ε˜?d[k‹ρqbŒ†oWH3ηœΚ UΩθ›CΦΥΜlδ^ΌƒΘΚο+˜Φ2š΄ ί„]Š?θ;ρψΥ²š*p&½@οcΌ ‡ζk4γώ•ά*‡[Υ7―d”;Œrέo.Ύ^Λ¨t•z_5^Εiηp’ `Α³Μβ§™ύΣ½€ΝΩο¨ξήόM‹Wδ endstream endobj 361 0 obj << /Length 2881 /Filter /FlateDecode >> stream xΪ΅ΩnGςέ_Aδ‰Μφά‡±YCqβŽ,ωΙΈΝaSlhffh™ωϊ­£{.$Κ‹…uMŸUΥu7ΕΝΒYόλΕ/Χ/^½σƒE*Θ‹Χ»…λ8Β’E9"τ½Ευvρeι9^ϋΥ»`8ӏ"‘z.μCsΎΥUU¬ww;œϊΒ1ΨvrΚΚ‹\Η11vς_=/­^|Y…޳ΌΎXy‘³ό™›­lC­.ΤxΎ³XΉ>lœ>Ύa;Ϊπ‡·ys±ςέn››£jšΪgΝT5·«vΝ­>ΰ’E"‚( ]‘†oψΛ 0pΌεVν.yΜΫ—ˆS°¬•άκς†‡%6ξς«γxΉβ;έξ«cΛ’ϋ²*?%ΓΝAed²ΥUΙσξtžσπ‘Φεxq‘š‘ιCF±‘7x[1²―.Ό6A΄ΌΆΫΛ–εΚσ²'"Š"+VHHΝ³FrΣΝ!Ξ«-ΣξΥ….Oζ U+x}‘yR­Š g}ΓЧΆ{έπ(Π‚hΏΔ―hΩ¨9,W πS»―ξΦΐΗu{:¨ftο.?\ύ6Cξ‘₯…„ΒS–©xΝW't˜aΈ–™£Γ`ΩΟΌθέE,ρ@XεŠ‘ΜMιπOψ!h’ŠΘρ ά΅QΉΚΪΙΪκ1€Δ³˜ΓL…?‘,j\Αΰ§{‡ΒInjΧ΅z{Z=}Xˆ8 쒝Fξς-‰lƒή²­ΈS—Y~ά*ϋΑ-ŠΝͺUj ž$ιR¨š§lNά–²0‹«šΧt#G–ρΗΐυΗw_¨Zg0ΟσαΠ- αw½Πw—Ÿu>sβ@ψIl Νπήρ㍛lC/Bg„^w…ΉnΪsΞσ}ẝε'Ύ{Αά!qν χU­Έ―έΛ’ϋͺt1—p' [”εφ|όΑΎΖΒM’ρUΌA™bt^ΏΞeysD+τX8iΗΑ5Ε»#Z<ς:ͺ•:7*¨’ηΨOΔgiΫo|\σά$N2V<½=_αιt°½,°U;l}4Κήΐ’#¬M{·ΧٞAΌ8œέ΄tGΤeχ΄Ξαƒlχ Ι^ΐ™d$qΏ†\²{+sŠυ?3‘υΣΪ,™.Η–A>²α”P?ν\ 0vu7iΈÈ6κΉ‚?ΡΈq3Yομ²Χ8ΔΫ8[Œ;{όχ»ΰ»‘½œŸ?|˜s[ŽΞq‘u24pβ€ΤYVά²’΄΅δΟξbΦF(3ΰy«ΆOΘœοˆ‰­Ο«LζκΉbϋv!ΑYUΆuEϊ_R¨εJΆ<φ ύ–¬Oό΅««‚‘C.3³Ι₯ν"οτ§φΎ†nζ$ξσΥ*S€™gί'Άηϊ·Qάσ 9ώ’αΝ…η,)4‚Sud #Ϋε“Ό?ΓΌ‚χ γξΎΥs,l6ŠG‘ +ξ0«βHΐ gΉpk,ΰF ή3˜8¬ώΞ|?Βπ§Ό1π9±qΠ=b=­Ρ…ϊL:ς/L–ͺΜ*Τ}σΉ…@²9O+d}kϊ7ϊfΨ7smΖ`ΔzU²{£`ξšΗΕάHμHΞKyΎŒΏέΛZf-EIlΒΔ¬­Μ7šYl)fZρ%A'DΖͺ—Φς§lΈ-4ψ8ŠΘiCT‚όˆt¬b·+;£P:δ`"oμ­f Ξ„]™Εy(_Σ *^Ŋ,HΉΥY—UY“Σ!ŒC„·/DΕχ’Ι’ύ;Ÿύ—l™½lΐΰ£αŒνίžzwβqsςΣχ’εei“\a¬'Ο”;ΊΧ‘°ςV–‘ΖSPΪ3#Ÿλόukσ“aζM ήt{¦|ϊ{sξ‰ήZ‡φ΄«»ζ n­\°qyωδs9²²œ  · Φl3 Έ5H’ΡLαZΫκF‘Ÿ…„6€|T ΄Ί½ίρ|“auh/ ͺ¦H'qŒΑηΥK{λuύισoσ0†ΪΚ"‡ςdΆ<ˆ…£Il7 (LJŠPY΅ ΠΦ γJΈ’3ρ5dž#yAΒδA6#³ΤΈ±'z<Jγ MΒ`BM ΣΡΊsXS ›&ΑDΤNsˆA,vˆ}ΌœK.GSz%ΆzΟ­΄An‹p|ρ”;H’I *[¬Ο’^g,·~/σjΧTš^ˆztήuCόΪͺζ@ΑŽ’΅»Όzϋώ=H3έAnŽ©|ΓzήWƒΦoηTΫ„%xΙΜΈ -Un\Fν8fψ λήPκφΝ³=“-ρœΗ°Ηb£L~Bρ>&Ί΄—j:mzΧ.d*iςό躐ίΟGχω]GΓΝς<άΛη#Fe ?rrrh³D:Έγκ06§ΚΜσ)‹ vαα GrXϋι<ΧνQξHδ  ο’Ο‘ΞΞQΑς&·―ΰlΙ ΉGπaΟ <šΡ%ΛJBΰε\β‰ΔqŸφƒΔ©ͺημ!€6ι“v:©Σν#΅+υŒ˜ή«&γ²7_Ψ9=8ΟKο¬8oψ"I;*Α›€šά‘œšϋˆ₯„η¬A"g&₯6ƒ ΎDq΄ςπ΅½ͺ˜Q4‘φ½ŠσFό’΅y‘jŽ0,X^XE‘ΈΑS•h_„ή½B4žq―MOad…žnƒ'Σν„ΒχƒyΊ»ψΟΨζ]ŸoΪ2ΓDrͺV½;KΚΚh8`r›SΏΣŒEwcαυ‚8οiΑ5~oΒ%EB7ΖΠΎyέ΄ͺΜԘDξ˜ΪUθ ‰ίZ> stream xΪ΅Yλo#7žΏΒHΏŒ΅v€y·ZάnΡΓήβ.›EqΨ-Ε–“AΗ3ξ<β¦ύ‘"5{œ&=όA‘(ς'’"eqΏπ?^όpsρφC.2‘Ε*^άlχEΖ‹$φE¨ΕΝfρΥ{¬«jw[δ₯i–Ώάόγν‡`Ό$τ₯HRΰgi•D’ Ÿwpνb₯²XΔA —υ7₯’ ύΫ*ρNEχ¬/»24u£‹Λ1’P$Yθh―–«@ϊή?υ―KhΜr₯"ίkΈSκSe²]ˆ(μyΐvΏuff«@‰`ΠX—βΪ<•­^·ωz’Ρb%γLΘ4„N ²$:£ωβλ*ς}OƒΰΎτΆ] œͺτeΰιύΎxΒ/Κ[wM[νˆ υ ^mφ:―‰β›ωFά‹73Κ4~˜8ΩrΈu‹Q‰wΤμτ―FŒ`šθΏ’I*’$ƒŽYΔm«zŒνζεςMϋTp·ΪR{§žΉΙεxd]ψCͺυΤφp0x(©δŒενͺšΟ{cZ|Υ`…ΞΤZSοg›#­ukΠ\ξsgΙŽd΅¦„}ΧChΩ‰η"Ω*‰E–*>λ€_WΕmοdηΐα…ŒΝϋΡ M?}ωψ‘­Χ! ©Y?θB‘#|D]ΜΊuT°o·+Gήϊ ΡΩLΛ—‹ό©ΫέYIŒœ>L=ΊLμΑsmEΑΊΜpδ‡Ό(θcmτ†ι+š#†C>4ύΝχUΑœΰ»uΫΥFΠ5πΓΡlΜwΠ]±”^Ksyk-˟šTήΈ¨ΠNO/ΘWƒuΘ7νΓ+ωηe±Φ ψ‚†­ΡkžAeM±AGΙBο Z5NZ†AμC<°να”O‘›vΒΚ;<˜’¦μΌΌgF<§οοΝ†ϊΫΓv4:‚Χ»AΣκΊ}γΌjσrœ>γB+¨Švδ8Dύ$†ŸΌ\]“/UĞ`γ‘%έWMŽρΊ!jkžΘΑ"MλA$ iœvH#±ζΕ+ 0Y=+f›°h£Κ=Ϊ#vhΫ‹κΐ7ε_ΕWρrLB#ρSrΛΐ'Ηͺ­Αΐ¬) ]G8@'A MÍn5Mlk*p RΘ§€Ωœ—ν:β–yοi:ϊ¦Ρί±SΒ΅WC*eι(^5LRnζŒΟτ1†€oΈΓνύΎ’A]‘%PŸT·ϊ˜Α½+ι]dΰ0^Κ Yp*ΑŒύZ§%Rβ μΙξ“‘[!&©dLΰΫOC£j;—³ΔH†άŠχ8cHDœΘ>―™=ίΠ³ρDZƒφ6`Q©!SŞ5UΚ-d*E†Σ£:δžΡλΪ‘Mω½M±»φΒS £pjLά5 Ι]G EηάeŽ‚r-™€σ(ΆHΨΡ?Uν\½ƒ_ΝΥ="Q=„›[³Ϋ·OγΜνΈΨJ…’}Φ·ΦeY‘K‡€{_qa`#L^ΣΞ’46=¦ι¦³χ54£iψεϊ#šΘΛ}‡7D'=mkeόJΡΩ{ >Τ¦ιŠΦ-€³M‘:Œ²©·\†! ‹ͺΠη4§&βfθω3Š$J½ŸΚžΈqδPΗ4ΤΕH‡_sί™Φζb–KEί6d1eQabcΏΈ]*ƒdCτ΅|ΆUmζ" λ2kch$cϋz»ήν 3o`§–’³8μ Μ%ci,»₯­ΑώΆ’–ž 턆t ”+’νον₯3,«ˆŠdΞ–Z$G}>bŽˆƒa\5^©ΙόŽvώOΥQ¬’:Νή¬σνΟrΗώθςΗaξθΝ£©uAƒƒ~jζεν€ φGζΜ Ϊ u«£xvK­υ²±XΝάF%`0…½!¦ήzςu(0μπρδ`W—Lx‰ΙK?€ΨޚaΤ”—ψsΗΖh(Fγ{Ά9ΖJos›pZlϋ΄ψε:[0Akρ³ΥFωόMφ…²ς€4F$.yfΠ‘FΟι85°€uό”ξœIΡfͺ)ߐφU§dBγ:ƒνύ%υϋε=NΥΐ?Φ»Η&ό3H^~Π!ƒπ Φ³FΊ¦§ŠΖ.^œoΐΐ-τ†³οFκ9]ΥΠς”2˜bτ’W 0:‹ΡŸΥΆ?bkΥ“O½Cλ-ΥXΛ‹χ7Ώ]Hϋ#ϋg`%ρ…*]¬w_ρψb‹ KKΊ[„p %!¦kΕβσΕΏΟ^$φ­yšθςq›e pŒfߚΟ&>RAβΤ'€Χτ:)—{XΐΊ 2ίΘΓk}TXρέzVc™FB‚0―ΠΨj3―θ2Š`eŸΦ5λ:ίΫk`>΅;z©εΤNžσLbγέ;Xθ½0σš|—Ί¦½ξνIαΟx“Ϋχ\ ˆον‘[ρ†(©1C©Ν›ζ-΅Θ™…’Z:ξΘ°«Θρ‘›¦9“|ω[$Θ«¬ŒDΔ^ςό„"³#UšŠΔ¨φ+J!¨K|όcRuΦΪ=Dj"ς)ΐ‘μ†ΚgΠ 2‘Fri†‘πeOπ°>ΓM·4ύ3n}€εΗs²)ϊΑψ"Nd~/^ŸΌc*Δρ¨ž <οl%@L½ςvΆdΐ9χΦ«<³EsCΗ^η}­ύ†ίͺkωέXα w:›4s|°/γg`.νίξωοZΜϊ.„’8Qς9 endstream endobj 382 0 obj << /Length 1780 /Filter /FlateDecode >> stream xڝXYoΫ8~Ο―0ϊ$1#R7°‹EΊM]μφ‘IŸΪΒ eΪ&’Ðδ¦ξ―ί%KΆβ8‹ ΠπrΞo†v'λ‰;ωλκύγΥΝ½ηO–„"œ<&άu™η‡“(tYΰ‰ΙγrςΥbϊύρο›{ΏΏΣ 9K<8ΖlωQ•e>Οt‘jά{εΪΰx―Η4³\3αρ~©εZ Έ^ϊήά‹ώi<žΜxΒxp"Γ77pρD³γpk`·­t¦Ν’;\(ζΉό9λόNŸΕjtgύ€·ƒξψr°)έΘJ¦ͺP:ψηC¦πpφ\εΫf?―ΚηzpΒύν?w£Wee*35ΨΌT+ΉΛš9-ίiΉeΦTj¨Ργη/wgφΞΛmS]·­T*΅<{U±ΛηΝ¦Rr9d'ηΩ•³ΪVεΊRυ{»ΤςφΩH ΄‹ΗቑΗ>ν»­¦άuΦ»\M}6DΟ‡&Ζάq^τ“) Y‹6™Ά²ΩLg"p¦€―€q%‘ί\WdŠ:afOρωžίς‹EωWώΤω.§;Αw U]¬H&εΒ’ ί X?§.–λ“Εsc#ŠηF­(8…’ΰ—©…Z••’( .Φ΄°”dΣ™{ΞΗΕΔΠ“‰Οήή›–9Ζ‚’ya»O[Aκέv›i΅€‹e1偳§%8jF*<Α|έAˆβ5Θwdd‰J ‘Χ(΄΄ΘaxΔ;‘ε gD`Αα“vZj F9ο8ž$ €z`ξbΏύΩb Ψ Žœh•6₯WΒ·n*m‡δL˜Τ` @¦‘‘¬ι›λΊ&ošE΅S5ψ4σΠno6Ϊ2tι²°³εˆΛ λ·GŒDLΔA«e› ΊXj„D›>vΆ}ΟZήσ™# |ΖγxΌVΌξ† 'φ‡MΉΛ0ΔbΧYd²x"²*Q&,=fAΡ·7ΐΗ‹@ΉVΝFU@κώj¦QW΄Ÿμ›[{›Sκ{‹¬ ΊYλaι‹WQέ5pwΡ—œ)ςY½ΧΣ& geDeC„QΎ°³nʝ%ΝI»GfͺηΗ€Μh»2ͺ;ˆjͺφ˜BPhH!8( C‰O]’Ϊ˜• ”¬ή‹ύ˜`4A'Ψ§Ϋ©†[αΩF΄…‘ΜΦ!Φi™νςβ΅(v9ΧxˆΆwΉCΝ•‘Χ5=H§eΡTeVΣz,άؘr_Β©Tνi΄ͺʜ¨m&S{˜Ιίv οEραΦήΩ§bP8#Θ—‡Y ώͺt gyΆn¦ŸπE3Ÿ©έ1τb Έ΅kh°/wD€² bW·Ά‚?O*4‘ŸOϋH­ κ€iΨ ž3Ήο1©Τ4#”ΞFu’UDQ†YρZk mŒnCΤg‘Τ…|ζyφςξ'’Γιι Ξν―²PhΏ vT‘–Xίνp©RcΫ„ΫrY=Ωω…^χηΖZέΒΦί₯άίδ ¬νΚ ™ΏΧcŽν²/―•dΈ“ ηy£– (وnφ[“œ›Π0s·Ά‘ΗΙ.$Βp}³‰£4’7ν–φPΈΞη0.[₯q‘W₯G¬Τv‚FŽλΠρB»όutχyΧ>•ΥxA[“Ό ͺ>KάξF’=¨7€ τEqΆδΊKς_ˆNΚUiΫε±k‘s©©³WβH„ΑKDO°3νa4}ύq0εζ‘φύν/‡ήΛνΝ}ΊˆJŒ¨`Ÿ8i ΎΊ1:vοΠ¦γ4ε"9H_i™ι_vΝλΕgmšΠ6!-F!ΆωcΡ‹$uaΪξΚ@YΫoΓΜ³F\"ί\*[Ϊͺ|@leU·«ΦD»¦Y‘Λ²=M-,Cj:)<₯€/ι|£?8€™j+.ΖΎΒ 8; œ·ƒQχŠΎΨ•t υΠ”Mn >‡·8ŽΫAΚΰϋ=U]ΨΌ«•‚Ϊ¦ˆ·vΚ¨`¨ecsΌ¨hΗΰvΐ$"‡ΣB Χc{ω"~ƒy¨ΧPԘΘ§B7 …P}`rY¦ζ­5?lΝ‡ωΞ™4μ© Ό 6M›ΔΒ²Fr‘Ω6Ν=ιό kՊώ€VωϋΒ„ω‡rŒP±zS>Οϋ~> stream xΪΥZ[o7~Χ―ΰcσB‘<’FΠ’xw] pςΠΦΝƒ"OZaɐδ€ωχύΞΜΨqδΚ[γfΧ€ΐΛ’ηž;rtΑ₯œ‹Kš])kΒP‘ΩqΖgό4T΄κ²—ΰŠΑεμ*ι$u1$,(Ιňթ‹ ”]d‰θ`o©Ψ€T• Aǎ­θdΫ―bUXAX;WλΤΰ #1@2:Ωα‡?tπΙ z(Ϊ ΰH@Cΐ€5`?tDfπIƒNZάx"Z VήˆN6F،D6cθƒvŠΡΞ6`blLŽγπh'Φ'2X6buGœl?Ξ6±Q,θ¨Ν`? ε€V”pH‹VΒhMp:†VΒΖ₯QθT°°9;3etp¦ˆγhGβN8Ϊ‘©:Nv$tμH2ڍΰ “ Z˜Ϊ3ΡacγώNd‚`¬ μ\ 4F’#„2°Φ–X'ΥΥ +Oˆƒ ΆPΚΤςΑI1|X!QF>ψΒ±!.B#x> S3˜± ;p‡@—bjb—«YΑ4ΑςjΜ’βrccr²ƒ™ŠcŸ\6€HM|³‘]V»:Εͺ\MΰΔAλ„@[Ÿπ+-†P‚”φ+1Ψ„ #ΆΪf S°Ό˜°α‚\a²>‰ EΑyςόωdϊƒ;O81Έ37ύιη_ 3Α·œ δ3P^]-—―&_}/0Δάλ§ §λΥΞ=Έ'θP»ζ—SμΈRΩΘ¦’ύ\€Y›Mά¬η/š;wΣ8uΣ—Ν;wsΞΛ— >Μ~k&Σοqf³Ϊm‘+Υ–O¦gΝv}΅™7ΫΦ.΄Si.³οΦΈσ€ ©βΝ„ŽΎΤW8mΆΑЭ؁»Z­±γyk€ %3@]+}ΫΝοaΤ›L_\½ή΅γ/VLΏ[o.šM{rx5ύητ_ΣοΟc;0\η 2IφΨ9§κ‘%f™ΌBΘ3ƒΣ)μΫ–«/άτλ—k‡+ωκμΝΚΟΧΛν―_ύϊμ™±lD Œ^M¦…ΌšΖ+y†©²—$1Y.Ά»±Θβa^―‘ΰ}‘Οβ0>7R ތ«Z³ωͺΎΐk(ξ…γύ˜ψ³«₯_―–φQ2uΚ·Τ©ΦV›r&ƒvHŸ>Ρ‘ΫΚu@Ÿ«C%ίΡ‘"GκPιΤΪ\qΧς˜ΊC aΉV-žZΨκ’fπ5€Γ»žΟ–ΝΘΪ#δ…Μ‘ψΦΫ–δ .Kcς)ΦƒΈΌΫ¬Χo[™-w›ζr<ŒϊLΥόjυ‚‹”H> e—Γν―_/›““ύ+.:L„χa―½Gυζ"ρ]ΖτTsΥp”μW½#ϋέQGΘ~OΔH²ΞΈΎΔς‘―@Χ1ZPΕώ3΅}U†_ίmΨηD}X†­ ]:Œ¨Σ0ΰX͏e‹Gˆm(ΠΎlZ2πΩόHΉΕ‰CeΰΨ`)hŒxΦWy`XέBipφ–¨ Ž 0«Žf£ŽχΟ–UξΛA ΗΩ(Λ!G΄QΘCCXV½₯QHn=‹%ΓΕ‡8Αχ›ΕΩ3X–ρΦΫ°Χw‰ Μ[:0>°υ¬u pΦ6J‹η};tXΈ <Έ3gΆv0Ko θ§θCΘΊ„\$rϊ_²ΚIξhc’#­rβα‚~φFvΉM“†S.ή’ϋ'₯VjϋΏF΄\)FT οWμn;„q“7β»βΘGβ²ο¨ΓΟκm]«}›ϋΆτmηD¬ΤΦ΅±oΣ¨…M°!eh`‚…¬VDΖu8ž/—―Χ³ΝΕΘι¬—$«Πq‡ τΓJ“ )DζΝϋ7­—kή^ξ>ŒŒP ΎtuΪ.=ΰ„¬―ΕθύβbχϋΘ%x~―ε#J’`*Eιr½]μλΥθe‚Ψ„ΫςŽμ‹Υ€agkωbXυU@¨Z›£_WΫΗ=ΉϊΣU“X ₯άTS>ΕτχV―‘θ«€ŸΓαιͺ€ χbΪu]L¬VνytΠ5{zώ $b{8& :>xΗιH=ιH‡ͺ½·tmο|€w>;)γf.Y§=υx΅6Aφ7§œ½άSνέ-.>ψ³‹ΩφwΏm–ΨiDΡSδR±:E>*$¬f[ž‡(Š–Ό1@ΦΖα#DP?+ίΑΈ– VφφLhuΤlΟxP€ŠŒ_y ‡oζ»šξNNN–³ΥoWέ T_y΄šκuεY 7BτE*ΟOfl_xF‚βIωQ…g³:ϊNρΐšΘcNΎktτX£Σ?υΩ›l׎j\°W΅jA…5!dΰ¦Hβ2d™%Ό’ΙνΙΙ¬s%oVs‹ NΠ^‘όΐQ³·p'T%Y-w+™%©Βύ»*εϊ/ŒΨΦϋŒΞΞ4Ήζόeμ|Bl’zΚ^οΑεs†Ύ ¨ώ [M endstream endobj 390 0 obj << /Length 1044 /Filter /FlateDecode >> stream xΪ΅W[s£6~χ―`ά˜ Š.ά4Σ}H§Ξξμ΄“­ΝΆٝ ΖΔ¦δ‚°“_IHΔΨ¬7Ÿ$„Ξα|η|η΄Φ΄ήO~‰'Χ·žgQ@Xρ£… Δ ¬0€ΐ'؊WΦ½½«+Ά5[ΧYΣ8_γΧ·δPΚƒ„‘P©c"―L ώΘ,žό3Ab -τͺc„ΔJΛΙύWh­Δˏ–xE#k―––‡„Rˆ}a-&τϊŽW“C‚ΐCV}€’oaθ„i h„ š_3žΥe^eŽ‹}hηέΊ­Ο·™ƒ ½ΪR«z³LjyͺŸš k‹•~“υgϋJ™0ꛀvόί(2Θΐ§ϊ‘Α€uΎε9«xυΔ ¬Βw.’ ½Nό“ρžΑ©wIύ ΜΑΎέ;r+@H©ρj[γTΒ‘škΜΈi·σ>xdEΡ},―Φ:4Zί€ Β—žΎxώyvϋΑΧ],)#ν ~bL;©/ϋ)λήυ!΄ccŒ Ή1A­™ƒ|ϋy[δiΞ‹—ξl•7Ι²Θ τi“q.œZŽ‚PΠ ΟͺΕKΦ‚‹Υξ τατΟωέέο‹w=|šί½ŸΟ‹ι»ιcR4ΩT\@gQΥ. €ψ(SΆΚ†0λΆzMΊ₯b•›W"o’”η2H;ιCxI@υ ±ŒaΑ{―k—Iƒο–…½ž:γlˆζΖνΝo‹Ω=@a―G~ ύββ(~»„τ:_πv•3γMž-{Z6mυtdόq–^>U9Χ€ΎΦWΚ€~ZιΏΌ‘CA’C«Ξuή¨‡ΓϋΌ(Μι>yΡ7δπ,W5βH“UӁ€ϊt$b%OΕxjΒ&―b¨ω(y§λg·[•¨λtdcԜΥ)i³ScΥU'0½š^ώ[R%˜njsgΊZJϊО3¦ŸΜoΏR΅²Wθ endstream endobj 402 0 obj << /Length 1515 /Filter /FlateDecode >> stream xΪ½XKsΫ6ΎλW°Ύ„š‰<ψ<τΰNμ6™6Τrsˆ3Š„LN(R!©Ψώχ]βb9ϋ:#π‰²ίzˆΟI7³Ÿ°“Αδ[¦βΘΉ“¦Η#…ƒοΉš½φ›ώJ ”)Œ8! β§α©ΠXKQ‘žΤiΏ >vυ“%ώZ7sβ»ΙFΟw΅iΗΛb‹3υη Ζ΄T'[Δ>"α)ώPWΗ,W>b>Q ^σ6mŠmWΤ•Α{ΟίΌώΘY…Μ?Ζ'“Ÿ ^ˆγh€‚ˆ©γΫδ–[qβ6οΌΗν\χ φ±ΨRZ30#(φevRΞ`=ͺI―‹’['$―ωΒΗΨύYύœέζugVk^—‡Ά•έΆJLΣwη; 8­ΛΟΈ½5ΜJΆ[^e֝Քa~yώϋΥ…ΥψλΧ=²M…ϋΞ*Ξ3ž$….)Λα»ͺ+~FΔξƒ6MΆΆ³z·*ω°Λ*IΏ΄eζGξ»ͺ7GRͺv›Ο]ήπ$3}¦"DΟH‡giŸo›ϊΆα­my?e_―l“.7Φf|Ϋπ4όθΧΙe‹q€κ΅ύδs‰sb„έΫέ†W]ϋd=–8ϊΐϋ‰:κ Ž΄Ησ'‘n΄0+ΈfS$ξφšeEϋMN6`.τΡ β£8χyz<άΛ’œ€Kλͺβ©”ΖΗž+«ΔΡΈ^k±P, ξ²)Ύxdέ–o“&ˆΔθ·9…W§άρ́Η`ύzNˆ›μJΈωΡBβJm2‡45±m—¬ΤXΏ69ΐΛ"`—WΛ°i{{Ί—„:ν£e.@{‘+₯Œy‘[―Υ@YT\€9°H•aB2†aπ§p¦ξuΫiΣz³©«ςAοYt9¬’Yfΰ¦,@8ξ1hωΦΩθvˆEao·5‚λͺΈWη΅έƒŠTHZ?F^N4Š Ÿί .νΛ!FωΕ,25’b±06P‰?Μ=μUVΛ#[5¨ΚΟ1–§o•ˆ0ŽΝk…‡μθ[½κš’ΊUΗͺ°—*#έm;ΜaΟ@λdΨγ>μΗιϋβݜω N²βΕιͺ²b&ςfmΉ¬ͺc6ˆxŸl‘γ‘5΄Έϋΐv›J}λǞž{W”₯¬;υ±βκ·¨r—I‚a’g»\OwυV}ˆ¬Νx:}…K}ίNƒza6@\ώumγAΰ“Zx,tΤΗ"P’Q―9⁣±’#=5‘Ε£Φ.€Πές΄4Έͺή7ΝΐΖεN8x!ςȐ]{$έΤυ‹6rύQo·u«; ΙΒAh{5•±y[ˆΏΏ‰Ξο_K¦GZC’k8KψΔ(ŠΘΟ₯+΄1ƒ}ΏσEzΗGEz8ŠF#νdFC·.Q~iϊG ΈΉ/ΪN "Φ‡·ο \ aE'Η¬‰¨/œΕ¨|L,pΒ œJE‰|δΗtZ²Š2PΦI;θ|±ΦQs£YReQΝ΅Ά©pχρΡpiΡ>ά‘ΠλS(s;‘D§>Σͺ#8:>~«G@zAΞ“*+Gτx™iXwy‘ζC1Φ%…Ξ‰α-ο₯=OΊžiNτ»―τΞι„!τΔσJ7”ϊOω!B^0δ”κ|{^³…Βπ§,*4ΟPqΙωf‚|αAC7Ώ ΕaπΠ‘K³E=¨ ˜ΈίοΡ&RφF°§Έ(ΈΣΐ%:;›@Pί`μίλeσ¨gŸ>θo0&ε§jLO«@εU=lϋ6B§nίγŽƒΚ™ώΊ{—Έu$MŸŸΥ$ύE7υoǰꡏa+NΕQGλκ‘—'Ψw¨Πώ“ώ/`―₯ߍ^K§hΕy6°œRρ}ΖSΤ[IμŸΕYΫό‹εμ)ώY= endstream endobj 408 0 obj << /Length 1456 /Filter /FlateDecode >> stream xΪ­َΫ6πέ_!l_δbΝπΠe E‘ Ω’A›’ƒ’H‚-ΡΆITuμυυr(―δΥn³mŸ8η>Hκν=κύΈx΅YΌΈoM֏ΌΝΞc”D^Q ξm2ο£Υh]^^7y§.‹ΌRνςσζν‹ 1ΎPFβΈΪ<4$ κδ λ‹ .FwV"ζŠΗ‡W·Ίœά=‘GdπAΞO;TeΒv <Ábσϋ‡73κrAh|$’YΆ\ ω—›‹UbΐΨυλ/ξ¨Γ΅;(ΆjΙBŸWU^ν₯wxιHσ‰R^(²\…4ρ7‡ΌΕσ|Ι|6*Υe©ͺLeFMπ 'Iδ­#λ0DυͺZ8 όV.9υ―¬@Ξ„ŸΙNβΙN7€Ίjϋ²ξrνξlo‘Φͺ{“ͺβφœω²ΕσΌΓυ:/ €^©Bδρn;k£d†΄ΦTƒ²ͺ ¨S>rΚηέ(BκHHufwΈQUͺ34vŸhH­χ`'y0Ν—+–ˆ:YU_^v£\ϋυ9τ/·Κ8/α6€<1V!‹΄FΓΪ·  3T¨; e•!PΚN5Ή,ς;w&ό+γG°Y7-dC@#“ΊφδVχNψc„ˆv2―Œ6kζWjΙCλΠbŒ‡σ a“pͺΘά z€Z6­΅qM‡8Vφ]σTΕ-’Άξ‚MΜqΡΈ’ύζήDK—€-ΨZ¨!.AΦπŽBŸ<;šu£χjŸΚΧy[˜-νrδbw[Ωό… •%―«έ’QπEEΩ!Ξ–DΜWd­°P6–aέndUΓ"/Σ.71˜γA ˜Šœ /¦ H­τ φ[OξK•wfμœI™N{h1–„ΠWρT΅‘} y₯œu―WγθBzN)Εγ+s,‹;όi„QΡ(άEƒ[w/Πω‘ͺνςIβQ΅ œ(D[Ma ρ …ŽXeg» Kݘ6*"κΜ…3“Ύ«9EmN‹‚Iτ}„~Š 0ίaυεΆ@7Ή– k«ŽΞ}=υαΌα ;bHBceCΪƒΎΎgπIβc"βxΈ%φοJΠ5‘ljΫΕ˟ίΟ΄€¬‘pΏ§‹Α¬N’ΔU[μͺMBŸ>©4ρh₯}|­κfΙθi&ΎŸg4 γ±Wl‘TΧBW{Ϋ‘nϋΊΦ °9Η½i4έΑΐαύcgjdΖœμθθš„ƒ8Β‰x€υkp³ I˜ΰ…77²¬ ΥΞ>_fŸ3 pŒ« yΙPΒ™σθ7fμSΫθΝ ,΅΅JσΫH\¬]ͺd©Ξ½ΐΜ„΄ΓΡ`)ZΘ«BΖ¬'jΨY0`»Σ¬A(νa2Wγ¨›/6ϋΝ&Λ;Ύnɜ έw—χ|Ώ[9ώͺ¬ Φ¨lVuγ8ΛYΪ^ ƒώ”ε‰Ιe—Β sξΔΉνΩω„ ‡raΙΔψ?ίΫΒ·ή.Zg½k£ΉιαΕ½³LžpμSŠžαŽt'†Z!Mμ£ΤNepŸͺμ™Do&°£ΜΑWϋ4ߎ²ζq‹NήΦoΊ¬±NDB κΎ#έ;dχh&<“ΣφŽ_¬nξ•ZΌΩ,ώZ0`H=vό&‰€“8Ό΄\|όL½ A&λΔ»Ά€₯@χŽΣ ούβ·G›‰ύ‹MhΜΠΫC<ρu€ žpΰΠ΄ΩΠ °-Ϋ―{>†˜Gf•Γ ά|Yž0x >Ηdkš3ίLxt˜3χzSmΪδΗ);Σn'ήΆ[νVό+λNǁa7žZΉW_?ΧλAΔLΚΩδ4wΔtΌίœΟ=h°ΧΝΌ•.¦υAu6Λ€’Sw/ηιd]«*›ΠΪGΗ<Σяk|v'Η‘e?Έ3V?ψμ +dΰίέLρ endstream endobj 416 0 obj << /Length 791 /Filter /FlateDecode >> stream xΪ•UΙnΫ0½η+„\"5ΓE))Ϊ=4E@KTLT"‰Š“~}ΉΩ°'p.ζx8ΛγΜ›Œ"}=ωΌ89Ÿ“4’€ζ8M„ $Ν£"‡ #8ZΤΡοηɟŷσyΊkIH"7qœΝS―TwΏξ…ζχ­|°.'0$2YȎοlγ<Γ…Uϊ}‚`ό0v\κ©ϋώy>Η»αPΝP ςΗyήO½ »Θ-ρυE2ΓŒk¦™—šžuά‹ͺχ§ΛetZωΣ½sͺͺΕπμež œ…Τ3”šϊό0‘F;νΆJIΙ+-”|ίP!Ÿˆ«φxT‹•ΙD"ζ²ΆB«Ζ+,Ό¦Z±žUšχώΒΒ΄κqΰ ™εΗ?Τ ƒ©κ:%Ϋ—Sθ•ρrτ›ΰΖ†An0œή!’ΚSo7ΑŠ e±±kT@p+Ε³Ο7θίCƌV¦Π,σ.ζ 8‹ΧŽΣŸΆΥ (ƒ€’r¨Α€t‚Κώ•€0²V.εΰ• w±ΌίUdζS:!›dΗ7υFχB>ψ¬¦Ku˜Ž ΠN ΓφώΙ‚bνh1͈α/Ό±“ΜΖVS^ž]'$3S‡ŠΟ><-μρΡrμθG\5ϊDAI·•Ÿ_|ΏΉ<ĚPΈei;ΑeΌmk₯"V ΚΜ³ΝοΓ¨Yn5ΟbΠ4Vs!n-Η³”ΔW Š!B‚r»H?o!BFΔω›ˆBiœΦMΧ8ΕιYΚ dOYΏTzV ΆΔMhη* PρΠm©΄Άό_6aρλ"ψW=gšΧί[rμξυΚxΧΓρ,Ή»₯]LΈΔnsα’Δ› NιΘkN3^X―ΈτvΦΚρQ3Ϋ+t}/X+ώ…;ΘRiΥΫ!IanΙιn^Τ’»―m œφΒ,yΝ„΄h(šμ―Y›)€—m9y[‡ί'#<²~po€0ΖjΩ¨•Α**ΦΪ}kUΛΰ`&½rSo£(ϊχ[ΏC(ν>Ψ~&]ωΒ²΄»<Οbpπ‹~Ή8ω³¨#‡ endstream endobj 512 0 obj << /Length 1548 /Filter /FlateDecode >> stream xΪν[KΫ6Ύο―Π1Ί\‘βΣ—ΊA{hboI!hmΩ*[†€έ$Ύ#KΆE‹¦%‹vφQ…43ΏαΜp†φ½…η{oόƒΟ_nξξƒΐ# )°χ0χ°ο£€r †8…?ΝΌO~_Νβo?ύσπΗξΝ»{L=…'ΌzΛχn™pσψgίͺ§7²χ DG7Ο¬£Όˆσ’~Œ΄ΓQF½[ψτ±ͺŸžfiN30cUčθΆ~˜ΨΚώΉ1ώ!ιε•ΕυΧΏ?ήxŸn σ?°4š·’".ƒΖY ύ₯Lfί‹8§εd’F«ΕS΄0™‚}xΩ—}lΉ»§’υ&EJβν{Δ·Zˆ}…˜Tή- ϊλUϊμ3ώc“Y 3­"}­‚οπU§.M֏Y”Ο,Ζ1 O±±K†•Υ8¦\'ό*|La²*γEœΧ¦i„@οTT . οΌ―ςUƒ fmφ‚L ‘ καΖΑ ' 2ξl₯pϊ%Κ£iiΜ0•=Λ>€Yΐ` ι«.Ε³¨Œ R‘ŽΈβφΊPΉDΚ'f¨e²4Αε.^§+K‰$]OžeO© ,n}ϊ:έXXΞΊάΞaΟfω;»xŠ “‰T $ΌJ/ŸvuEΛ‹§ΡXςΧ’$ ₯†„›f‹d₯ονκiωΏιMΫrδβίdύNν‘$ϋf ΆX-Ώ―γ7Šε‘uϊΡI³n|Uή>$΅4[ΞGD±rμαM˜₯©ΠNRgŸΐ›Šκ[pΝCޏi4β:ό†Ω*ύώNvΡλΕόshί‘ΙhͺW+jΣηaυ+Υ/\EΛΨ­(Δ 8σζFœΖπ^Ώβz'Žέξώΰ½Ά·.¬ΪtNz:‡+¬­Έ£LmgβLιqξΈ…›Ζ©.ˍTˆsjεΖ΄š’ ¬Θ²Ψ 9Β!9u€Ησθ)-αμ'Sέ("D }T”^-8‚ŒOυ£%μD’Η©ωΧyx$WmΚd<ξsžeΛDš€Q$β±<έk”:ΛΰxΉ.έ€y'ˆ₯CΔ@±:ψE5χΥήΈuV$e’­ 'ξ“Œΰλ2‚1P‚Y—“l /x!¦E->ξΪχ5™•_ 'M‘Η‰iθ²Η|ωŠIυͺ˜ΪsΓEΌr<‰q•Y~΅”Μν€\έlΫd_±t†Έ¬•ŽΧΩxθ₯a6†Γ jwS’νΩ‡Ÿ‡—'0EŒQΑˆΦ 8½Έ½½-=1FŠρ=ζSφš·7ƐeΪ€f{Π'†+otuψ{ΣQά€ΧRHτoΩSΒ^pΧU}Χ4wγ—hZ˜ΛΗΤ€ŠOh·/x#εls¨7θϋš‰γŸΊϊa;EΏNΧ―δ֐T·6ƒ5HT―.ύΫͺ(MŠςR§ώύyςϊNύ9qOrΧ­hyD»w&ύZu<ƒr‹Cπ=€ΨΘ3ΘR6«Fέ›έ;½ΈŸv\ξ*Θ‰όXί|ΦΝ_i‘­ώΌΞ¬χλ|΄¬[η —Ε(ίn;»ϊFž¦>―† “IT„σ§Υ΄jΨYI)Δ‰Ν!ΐ‘¨ί)ΦρΤ4g ˆb>τΣΑ$^oAh“ψ« kvmόVYU”ωΊ*«.^bΣ^%v›Š2y‡LκO[w·ξΌŽNΣ~Γι–O?OΛΌ˜Lžγi~}jJμκΧΈί―%Ϊ9oΣ86ε[μΩΓb#Oσ°n[Ίν}v°±\ώΒ»|Αƒ‹λz$₯e―Ge›-Αlp5Χ6ΑΒ3‡Wυu1€Ί8ΆΜaΆ.‹Q—Άtσ~?λΨ‹–λΤΙ3,ΞτϊΖ†ΒΙ¬σς΅tΣκ bˆ§α<Λ—QιdΗ‹3wόn©ΗZmZ?~ΖΨΘ3θO“•ρVX!όφ‹·j  €^仟nœνΆFœΈ―\Λ3œ Nθ?NΓΧ<)c'4Πώ4πŽΆΜΣ;"YλCq&AσŽzλΰM˜qHVe |&pŒoVˆθVφpσY‘α³ endstream endobj 387 0 obj << /Type /ObjStm /N 100 /First 881 /Length 2258 /Filter /FlateDecode >> stream xΪ½ZKάΖΎΟ―θcrιικzu‚? $@–N:(ς"0bμzΞΏΟWœšYI+͎$ЇE“άβΗbΥW―ζπ -’X¬Μ8k…8Ngι`~>‘¨—ΡouG‡ΛΓ­ƒΩvb­ ‰w‡‡Ξ8θexΌ;Θ9FΌ»IUpx2)ήέΐξT3gΠ. ¦†/l–i2wβ­Lw\ιζ fAj Ζ¨IBY`i]ψO-¬ _ΰH=δ‚ΐ Γ rx Τ¦έ£G»ύεŠαοV~*ϋŸω―ΐ― ‹ΊHUΠβζΝoΏ=Ϋ}σΝYa„ZxΉH4­oςŽπγΫ›ΧεΡ£² ζ ˆ—»KΔVB<·ε‡c0xΚQh‰²ε`ϋ_ήΎxrύΊ\•ύ?<.ϋ§ΧΌ.§η<ύίοΧψΗσ\οφίγ™Χ7―_ρ-nίνΊ~uϋζε‹λWK&X.ύγϊ—_ŸwϋGΉ ²vXwΥ1ŸαiΟ_quϋΥηέ«Οy°©sωΎ™ζΠϋ}3­"lN΅!΄/Vnu"Œ.–AxΑ Υ@z© ]_˜uΦ~ι r›Υ±^$ά­ΑZ£S«ƒ>JτwΙύλ?Bξ·#ΰ3‰.­ΏOτ¨V_Dτ¨•—ύΩ;ξ"Σ|ΐ[_.lξ•›](ΜxSDύEΒ(³΅ΝρΙ9μkΊvάw­}Šk…ψžkύ\λHˆM/fιu:α>€‚  χYν}?1jί.U_^ž„μžkIΎΠ΅€—»φmΩ³ύΑgWρΟ΄Œ½|¦Τ>Η2ϋoonnx΅tΠ‘R4Π‡•rνΉr’«ζjΉz#ΧΔγΔγΔγΔγΔγΔγΔγΔγΔγΔγΔ“Δ“Δ“Δ“Δ“ΔKςE›|XOOOOOOOOOOOOΟΟΟΟΟΟΟΟΟΟΟΟΟΟΟΟΟΟΟΟo$^φ¨Ρ·ΦΔ‰7o$ήHΌ‘xYϊe&ήLΌ€ΉdHR]fβΝΔ›‰7oπτP˜±R=WΞUrΥ\-WΟuδzΐ{/’ΎοφOήόϋυrώχ_oώ»Ϋwϋς—λ—KΔ΄gϋΏξΆώŠ–“ˆ±N¨|Θ>Ά4d˜3f΄ώ#†³Ϊ!χν’ž”ύ_nŸή$?ύލ­ϊηˆςUtΐμ[c,Γ΄U4ΒŽ †NhεΊθΗuθm=%-@ ’κZcCϋΠΪ: ½ηF* ΌΖ4©ΖΥ— ψtV—J³\ šλ:ƒΐMUB‘θzu!†Z―δςq%xEC€ΰ€2J7*xΣ[ͺ­’κ6 vvŽΝ'―†ŒLƒ"Qš±odΖϋ”@­†*ΥQͺ UF»Αst@fC–ZΊ(šΠf 0rS†+bρ€@{߈ ) Ά\fUT1ΰclρ@•v.EjπΞ—ύ₯ͺΊdiΨ΅‰n«‚jUdΖ“f΅uί– ΒΛؘl@/S[›fτ}5j13 o«sn«JΦ²7xT£Ξτƒ’yVρδuκFAΡ₯Ζ¦+ƒ‰±O|¬Ω‚‚1y£¨`θ›τ`f΄ψ qΗ—‹:ιLTŒuΐμFhwO:¨E‘zXβυ›—“ΩΌ<¬ΔŠΝ ΊY4πŒ‡ϋςεΉΙΒ21&ŽMΊΩcdΔ%ΗG§Œ †nc£uτ…x={BІ'Τλ0Ϊ†’GG5€šΘΓ:¬JɁ²€e‰θx•Η6:dhŠe‹lΙΠΎΰ^΅o£A¦kŠ·x8R«Άmt@K_γ«ΈΑ >8c¬η6HΧΤ΅}ό¬Ž˜σdTŽŸ u\οΆ©±W=άξth3o£C·Ÿ₯bo:~(ΔWmΕi3°ΗΨؚž#~lVγšbΦ‘s΅―ΟΘΨ-ot"€:#(6²‚!Δ°ΖΗ“eΒ“…@έΘG* c‘;& §Τ©Ϋ2ν Z™;*pμœo£Ββσ Sΐ P…ω¬€~)~Ox²BώI·Ρcm|Β΄O˜&@G耉+φξݢيbƒ–½ρΤK|Α~P‡5η~½.Ώa=*qδΓƒJ¬ι dcŽ‘JHˆψ ψk~Q¦ Ϊhΰ=QΒζbFɈΔη”q”υΓσ€C†ηƒ:œBγM7ύ endstream endobj 526 0 obj << /Length1 2835 /Length2 24727 /Length3 0 /Length 26135 /Filter /FlateDecode >> stream xڜvT][Ά%ξξ.ξά]‚; ξά‹[pχΰ‚[p‡ΰχΰΑέ]ƒKίWυλ%UέwŒq9sν%s―=Χ>‡š\EYΜ db. Ί0³³°ρ䀦  3ΘΞΨΕΨ˚“YΝάΥΞΨ ^cηB’¦Φ°v±30¨΅Μœ­A@ώΏύ%œΜ]ΐIcp˜Ί+ lκΰ°sπsρςsr8ΨΨyώεrβ(Z›Z›ΫΤ­ŒΜ‘¨%@žNΦ–V.ό€Ώt¦τΰ@6nfp4@ΝΨΑ  `ξfmό·: k'g ±½9‹ρ?D-퍭νXLAφτL[Bvφ&όχͺž`jv`όg9YώŸ"ί2Κύ/ϊ–αŸ[ΠΩ;γAΤΥΤٌΕάΜ•žείά¬ḾΞζfW ™Ή@]N`>€έ?ν`g€Œ9Πά άI3€‰'ΰ―3“Σ1GΩxή²θ¬\\ψYYгψk‰ΕΩ‚hξΒJn±ΠLdootqFbg˜YƒΒΔάˆΔϊW* O0Gv€™ΉΕ?±’±‹“΅@…ΐφΧίίOΰV˜€vžΏέ•ΐΝ°J*Jhkι0ώ7"ϊΫY\δψΘ`fηαp±ρxίr|ώ=©Š±υ‘bϋ(΄ώEήΜΥα_pϋ§ξtΠ=ΰίS)\ΐ=Πύ>5}6.6Sπϋ#£8όOκω+φOό™π_2ϊ—tώ±φ·zώΗȐѿ€σ—Οͺη―}p›nώςωŽP?ΝχόόΝΜ8'χι`•v΅³ϋ‡ ιώ¬?'ΐ π?u!mlomηωΏΕ€CώΣSΫόŸΔ»Dκ UΞΕΜJ hiχ·ΙΪYΪΪΓάLΕΪΕΤ `alηlώO»ζ_›±³š«€œ­Ί'Α#ΑΕφkVΦ¦Ά@sggΐ-™Νώƒ€˜½™5ΠΐΑΕ 0vr2φDb3ΰ#;ΐœΙ`ξΝΚΉ€C.>ΰ:!ύ5Eά\V±ΏLDάVρ߈ΐ*ρρ‚‡ό7β°JύxΨ¬Ώ;€Uζ7β°ΚώFo¬rΏΈžΒo§ψλ)ύFΰzΚ#^p=•ί\Aν7WP8¬Ώx·šΏΈΊΦoύ«λόFΰκΊ#>°§ρ߈\<"ζNΦΞΆΏ]ΐα&ΏΨΕΔΨΤΦ+O+sΰ`›υLΤζnΉνά›ί„ΉΑM°ϋkH~―ƒ;ω{πkƒυw).p. xΈ~o\θjoςΧ›ΔςJμΰΦ€~“ηύΕΞή΄Γοep c's ΉΕοξr²—ΥιߚώLΑΑά |ύαΚύ›5Θμΰ.:ώήη_ΘΥάω·Εί©82‚ΐ_&v‚­œ;x‡ΏΫΓή‰³Ή½υΏ«‹ β ~νΝΜρ? Ύ±YΏ·X]¬œΜΈ.ξ ?ΐ9\λΜβwΈ³)ΘιΟF‚OΣν¦ηώΗ”€“zόΑU=€ΰCπϊΝœΙΛάιŸ ώνž4uuŸ‚Λ?Ύΐ—θΏ°…5ψ¦67χ07EZψ 2Ά© n»«#vgήωΑn!™i]+>’¦ιOxϊyόΰ!μ‘K^sί¨Α‚_H-zΪCyΘΜ‹εΣE•{Κ;6g'¦ΖμB”Ύ…±N -“aγπΊΞ―ζŸ΅Νs&ΥvΡg7V~8|ŠΏum¨ρ>ΠQ‘ήΕ‘όΎψ‘π²αˆάρώS.†–ξΫοΕ¨}“xώ@v}=*š•ͺ7–‘σA,Σ΄#Υ‚|]yFTŽ= )ί¬°p•ΡАi”[fΤW0.&jκLσΖ)ΣYN_‘Θ‡Qšu•ZS_ucύGBκpq ΣW_"rL”u;l τ3-z› K‰­–|άΥE>…IXΙΪ¦ϋΛζw›•ϋΝσqˆΌΣψ;μό4φ(τ¬?υX¨g {‹‘3Ώ^—Σhy.”γΪ‹iφ΄a7nβέos6Β|€Jδͺ.ΞϊB«Ε’ήsΨάλξηv|*‘UήhaϋSσΧ8γw§BLΤ4m»9i˜ίW=ά',‘:\sΝΚΈq«η[”bšMYΕ3œ°˜eˆ"ω&G(9:Λ]8ό›4Ύ3NΛRώ±+³ ²ϋΥ*?8uΠJiHo>QŸœJ$*’2ΙοΌsC#EΝ S*CkE YUu ³Ύ τ-π¬¬ΩΗαv Ž Κvƒ‰ς΄Ν7q5Υέ¬|ˆ(fŸθ\+‘ζnrk¨0δ`5%α|–BN‘gΐrΖ‡ψΖ?Νώ šΘί6Ύ₯=/<:§“kC΄“›ή=|ž†Œž…R:„ί=-™>t±΄šι&³§‹Τ@PT 'dayςTΆš «pbΓΟΠ‚ΒΡ―±/Ζ2oΟ’t™J6™Ρί˜λQΉ΅ Bg›’’βXΟΣi­άTε:ΉYŸΏ Ρ})‹Ίt[Ž]Άƒ_ ™GKϊζ"°Η‘zΉwkbΊτ‹‡†ϊS¦gzq΄[7·ΒΩž±§Ω«Ή(υVΜ“Ž»[0Q”ορͺ9HδΫY6!°ύ̞`oBe 0:ˆ₯ε7© Ν‡xH¨]ϋkϊΓεͺXQοLλ0§ο±½Μ¦άWδμR3D“Α«χ4·v–»(ΥKΧ7݁Τ=c-R#ΗΤ«ΗQD™ΖκΦ'Ζ7\©χjΠα_ΠχτD¬7©οe°D|€T­%$4,šψQ]ήAίΟ!{&ρΪΪ”[yΞ•)ψ‹cπFƒΔΘ…††aξΎ™xβeeάΩώ­Œ0;°KGδ3E4θζ!Τͺ-JoE]΄ί{cŒ§ϊϋmδΟC·ο|"KzcPΜ–S_ΟύM'?)ΣτΈB@₯ΓΉ½‘εzR=.ς+0Ξ}βΦ!”ˆΘˁ­ξ/’!6―δn’ώηΥK>ΛΖ‰}'/y/6.‘…MpŠ+E–Η¨8*ΈΎ&J†S'~ώΌ―«ˆ=PݍφL§zύ€-žw„lΐι²~}Ψπ³iΑ\lΗ«Š΄"L+Χ8υ₯mΖ_›…°έ9¦¨Φ£W:zΫ5ŸkΧSmͺuΨΗUS€y'Y‘_voΔ~A–…2ΜH”UŸbDrΠϊ)ξξ MN{ό)dNΙݐΰL­½ƒ6όύ±6q‡βš–peνͺŒΥ ]ΐΟά8g$§’£P ί½‚ dι™)ο ΅Ϋ’Θ47IΚ­±xžώ”Y€φ™n ₯iϋLωe)­}–_SfΈ`\‚Ω@œ»K—FˆΙf,wν¦‹ΊΚκ:s_x­άLV“–CsΊiΠVΛiHδΆζεt6?fs*cvkΥΐ‡J3]'0GŸvTΐΦ›ΡΕ_}3`xVΑΥ/”> y+8‚7%ΪdΥ™­N8'§F²μοΠμ°ΔΊ66"›€[?J€ΥΫ―wή–Ώ‹υk\Ύioσ~O?Χδ5»‘…JϊˆζRp‡ξW•]ΐR€ Φz‘υ”$DgΦ嫈ŸcOͺΚ]²έ€Χ‘ήά΅½}a^vœ’ρ4sύΕ³ΎA“”έN1UτΘ“ΦΊŽ:ΎΘ‘‰–Φf"Pƒϋσ£%ΣJΠ ’€¨ΎuaΩ³&ν^ oyφΚb‰βJΞΞ–½WΩλ«{ = •X‹L―&°ξiMw°υ)»%h“ƒ)e <žq°£Β%kκνξJ>KΐKŒ οΖQKξ‚/νωτϊέΕЇυ‘ [ ¦J7#αiή“ΪτΣ9τν¦ώR‚›…έNξ_ν~”X*ςy„TΒΓ¬)•$Ερ"ΚΞ΅$·Δ¦κI €Wψ’Πβ˜„6O;σΪΝ½pX#ΧΤ)όKϊHόU²ι±@ESΊ]‘»Ά ?«•¬V,!ΓΉMΓn (~₯·ŽnβμSl :}ι;U·/΄|} Άω(Bl¬"έά=ΞΑ2 Άž}›°Α=MωιBb€„όλΧ΅^βUΝΆΜC‚ξˆΦŽΞ΅4(ΰ!v ζt{€1[1}>αm•}žγΖ₯°iκΌ.εLe3ΉͺˆζεΤNε.›yOŸy?›^ŒΞ.4νωuτ“πŒNύgύύhγ΄·βYάA4ށr‰HLΕl<Θ§χβVl+©sΒM0ΌΒΛd.ΩQ—XІbυ£qβ›DIsfφΊοjIljr«ς-₯ ΩrαΤ\>&C”±\„Ύ+«dM; sξω(K΅Π―^{&ο˜^0B ”<° ²q^ΞGπy„¦χ#ΐ`eωb0pΑ%¦5‘l6o3β83•-δίυκΛοόx1ΓP[,₯'l< žLΞ g7ΦΊB¦«φœο7ώ\ι]σ;ψ΄Ί‡ΥΦόΐV ·–χˏx886ΌΠΈF‚Ζ*ή ψσFό€lέ₯-φ\ύ#ηŒ‘ŠΆεH₯ω+ΡvUDΜΆ²·ώ ­ 5TΓ9pΩJσlFEχzO%f΅ϋ“OεB=/^RI\2 ΈυΊΛώA“ή©Dϝ¨ΉlUI€R%wKω Ÿ,9€οΰww„*oΟvβ—Ί€Α-±ΘQ@κ7a°}IΙbB4ŸΝj!wιˆNeΫ֞p,avEV¦λ’:»r«WΟΒήdψCΦ4Ξ…`ιLΨπ €uΨX+k0ΚΗΘο*oΊψΉαtΠnr_˜…j5’«dΫdzυ6ηΥ•&“^ΛTΤ£3›α-šϋ€¦_Ρ€ί A’tRpΐι0½!E-ΉσAšΤb |’‘θω‡κ£hϊ$(†–Œ»Μ€θΈχšΆHίƒΠΪεμ²η…qs‘Ρΰ»Σβ©λτζ–μδ+vό ½άΠzo!› 4zgϋιVΌζŠά y9@Yn•m?ο‘Ϋ“Π±ΰΥμMπΡ ‚zν*?Ι'±`4a^NUHΉ3hιlδ,θώ²WKqi…φύž—K`ςέπb‚BΟ}R’ Ώ―‹uxš„Γ—Ωο„³Ζ7!ŸPϊQ`ΣψRέ°’·Ι«>6Yc;9)5ΌoŽVo†ΟKhΕλ9ΡΩ°ΒrI%“7?εCθΜ5₯ˆ¦Jϊ;ΰk1Yq7)[u`}ΙσŸP' Ό‘q;€ε>γll"Ρ@D~„Qπ­C€ENΈosέόšnΝJˆΤ4@]†&r:ψumšrŒό’ χρ΄Τ› Z‘ΐ“ ήΦ1©mρL†ν@―И’ΨN;΄ΨŒOΛ™ρ nyy:5δw,ž«³Ά'βη£λƌΨμϋc2.-η‰BƊ’9―ΛΰŠ9pΕvή, 1Γ끲°]aΙ}άΡπζ“ίρMBΠ{ŒψS2_sόΒz©ή9ϋ°―‡ –&Š„ϋ»Q½”'@@w]ΜμyvμnlSΕm0`S„MDvΥ/ ‡χqΗόΒ· h†ΐb+Α“zZŠΙ+”[pŸΗ~;A ’O/ΡZ΅1™N€Σ§Τ½°{ω“M›ψœA! λ—HZ›/έία}ΞΪ²xfŽoXΕ2ώGΧxaΊYΦΙ¦|© …hSvUΔ%δλ]ZΛΤόW[Τή©ι2€ ζL|W,rlήφθυγm£¦XGy8‘RΙ€…‹1o±~ΖUξMV鐩³FιΑΑ£–nZφ³MΠKbemM,F@ΓΪ95}ύA(WIκσ‘7ΖW¦ZΛrΣQvφRε\ΫH\Lη• Ζά“^;+ρ)Ζ³ϋή|,zGSά ŽΒOTξ9L˜γ†>±=/‡vƒ5ρƒ—4TΑήgΩζnpbMt Œ*ˆΦ"jι!UΒΔOψΆ}Ε7ϊ.Ϋ‹U½ ΦΞzπΧk!BΩ’«ή‰w0Œ πfΥ€jF;aŒ]DΟυάpύm(SCΧΦ"b–σΫέιί₯.["ΛyG΄δσ§3…DŒBλO\ήk‡½υž‹υPe1swZΈX΅[€·9§Ζ~υ™Ϊ^‡BxoG*DαnœΚΏ©Ζδ9γώfKD퓨wV‰ά魐'κΒΉ‹E9Žχ=+ŠuΘe-g όδώKαi}έϊϊ[“Ξ`ϊv…³ΪΠ=ŽδXα²_Ÿ4f_«[2sά€ΰxŒ`ί—½«ς£Ίf]K½}Ή™½ν7Σj sξΣ^-ρΛu^μr\Ÿ@΄έΟ'ͺ¦΅| ‘ΧPβl6*~s΅–°O½ImRxpJAIΛγƒ1j—‹·{±ιωέUΖ―΄§b€θφ§Σ„[άIuυ½šσŽ6Όή΄ Φω«HΔ'ρlԌΗΜΰέ5=Ά”2M€œ5ΗΦƒώΗ.₯ŠC·o±mΆΙτ‘Ο!‘h’πclb»Q-ραςί†«„q{xΰΰί$‰Z$ϋ8WΘ£ΗΒ/πuy9΄Ύ* 6MhTϊ“­Όβy6 J¨Rw‚Δ³$όπ!Δ{b3|Ϋ‘Uύ•e#GΈ°κ§Ω]ΰ”}G¬XšΡ³1&ΦΝd΅€œ5BTMγX~ˆΤlν„Ήάkf‡ nφ/r^½κόAΐ@SXωε—α—*εΠΘύ€;8'²>«Ÿ¨O0Ζ‹+1ieΔ5εTςuΆ†‚UηK[ΑΩfˆΙ΅~Ϊ\š²(#«>™_›Ÿ^#χ wŒ(Ϊ…β‘<‚“ςΕ¬πΖvΖPˆΓc” C,ήΧ¦βΣ§ωβΒαaκ‘ŒθμeΠP$>«)ž%ςkΖm©Ρ%rΑ3ΪXcξ“ͺΡΟƒ§Ξ#νD|vJ†V©Ιέxf €+ΐΜJΒ―žLT$ Ο₯χ;ο`oΝ[θ[Τυ†αΥ Œ.άηΒN©΅˜:5ΪΨ`­―~ϋπ}’\ΡA‘%Ό=GC‘—‘$oy:Τt}oΊξΠ6.šϋρ‹Ž€Θω·ι‡ ‘― M ΄™…χπ4“Κgcq{sΑffωc‘ωGVjw 3SxΕ Ή¦‘Ϋd±cmL¦=F( Ή_}ƒΎ€:|&|j·βΡV܎Ώ"Ώlξ„6LP„žξ(6˜sWO92Œh‰Ίe|^#φ”₯η}oοK"šqŠΉΆ{ϊ,3 ~ϋtςUΧk³ΰΔa‚” Θ‡0ύ§]”Une˜A)βαŒ ˆ·²(‘ƒ2—}œ`ΉJlœ}•ΐσ퇳@Ζ¦ζΞ+uΔV¬·R/–Β˜χJi NO©žά1?α"Π°Jr &S;0ήΕ DL]ΌFHŠΗHŸŸΒφΧW;λί•τΙv-Θ$UN‰uPχ—K \nά€<+ ΑyΑΨνΑˆ"sιQ₯i‘θ upƒ< Ϋaξ۞Šή}cV!xPnˆYΎΓ;·Ρ=?:ƒ*hαδƒΐ4Œ£e)‹ΥΝ²Žn΅Ω|₯tW}vΒμΚ ulςx0,4,‰Κ\γΚy$%,PΞTι ’TΏΊ~‰s~§"εΌ°²‚0Ÿ ΅*–%™ί€υδY‡2Δl—;°$ο΅VΜk&Qξτ’2ΉΦt«υ­h†Δw!ŸTe3ΣώΜ€!"Γ %€f"b€˜χ¦!η²ΥΪ`­ύΪά½xΤP=ΰ)ΤύΟf %υtSε8Ω/sΝ†f\bΘΓ™U$£žtƒρπ”ŽX“MIκx•e|0<πΰOiέοhί*Ρ5υ™IΟ±ςiYΣΔΜ%t³ε,ωh’j,²‹€†½Ώή‘Οψd—›ΊΐΥϋžŠ¦j_ξ³pΰΥ…~\7[0MΤνvφ¦zk`ΒoσΎοαEΨ^’ΕΌ¨ΕφB.»ΐK|Κ:Ϋ'zˆMU†Ργ»"PdYΨy$Φq΄ε²2¬©l&=)ahη~š)θSβ’Ÿ}ό²Ή·Ž₯οߍ!‹aε«cΣκdΣΉμn‚-ΖB~kMrJ™©τ4΅ƒσ$ζS9>@ΜΑ&_ CΤ°`΄τφ‹w±,3ΪkΣ ±š6Τ䧬¦—«4Td[;.}·€ϋ ’θAτiΩNkτRQU./’Ί¬@ΊIσ¦§~N…Έ―Ώ>WΑΚΆYq½RΌFl‰0›ZΚΆbι‘©Hπ«/ϋΟ·tα‹Ω―μEε .F9GPα¨ά#έhf@¦ο=Λθλκ1ι”KΕfXu,Χ©(4Vœ8ΠJhρ€“ΙYίηυXtΎa4ήΦΪr–YΒ–L‹ΈG½BΨ=1‡π1N̊…I+R½rΨ.zαmDφkμlU•x±9 ·a9X&'šεέ•¨ƒUm2η‰η3£9΄ScWΚ†ζ_~~€lυθΫ· ’sέ΅+6P(q[h…ΖR3—”ή Q9±ζζΘ₯ Δ`_ΪΊ%ŸC·« ¦QΓkm˜΄JD¬Μ5˜+Tμe}?£`ΖgQ9|ιΟ‚ΗΗΖ§Ο=— kΚ*_Vψ=a¨ηU[6™Y! pkΗRhƒ½deŸ‘-²B;θ Ώ}΄ k½˜Ό) uΉ#sΩ‰¦ο‹›ΩŸ 6MUξηϊZW00d`eΌπληH*4bݟ…E=ι»XΉζ2|Ώ[Ÿ+KaiΎ‘α•~σΏ₯nŒWb3L(ΨAάνBFΖmμ–hvφε❳ΊΏ†Γi’„·c;μJί‘ƒb}rOΘ –«ΡbhlυδΕΒ_©yεFύBj•ΌpuΚ‡Ll7#πΰήRpANΌω‘ώfαΗΟΥέ"ώ£2G/ΓΎwγπΰ;α³IV° Œ©ψ$W ‘αΦ‘έτέ|ΉUBJDAΏφΰi7p†z)3yvΚwIφ†ΰw[‘0-$θδN]“ψΛ‡άμΡεΈAAK—Ό².=&ΐωΦΉ| ϊ\ΧB½ΥΉΫ$=χιr‚wsξ3 δζη4΄΅`ΙΡ ‹ͺ”οˆΈ,TφEσx…]Έ€Β+ΆΆ†°™Ψ7Φ‹#έpς‘?&…΄€?»‰š€SΟl©«ΒΆ+ek‘Ϋ‘dη(FN›£ ZŸ*Τ?mςΊcl2ε4³‘$ Œ=oϊΙΝ νΘ ΊάͺΨ2Ο% Ξ γOΨ‘A‚²Ά"©ύ¨ΘΙ΄αŒ‚‹ι–½†s―›Ϋ ™Vΰ•…$ δm—GΥ—)‡΄}ΝιD{θrš’~Έl{ΠώόφδXΨά°dι€Κ4J-ΐ ΐ»ΪŒθe)ƒ―Ύί±κhh†1˜£!ϋζΒ8Μ Γ˜΅oθΫτΒ%“[ΩkvHΎ6a;C ΠcxΛΝΠς¦­z㦡ǴΊ tΰφΣ`ε=Ε°ŠOSK―ΎΝWή8Γ‘\…Ύf±βS:€ž^ΡTn±œ¬ό•͚Ύ~ΆΪτ]η)d:x*ψΫλέό“δΓ{™’ο—Kρ©–^.$1Θλi›3ύ ΈQv—€ρszHΟΒΘΰγγυ”7kS]#—¨oV<“Ν%ߌ2=OΗΉ‹}&BΛ\̜F.„hμu³‹¨FCaΩr»Ήχw:3ΘΦΠυ{ΧγΏB΅T–ΚͺΓJS|{c΅βΐ(iiA,:wRο-²η+°εδ|ύNΏΌ–yψγK=fIΘΥΜdN‘YuίΪ³LuUbΗ\9ρwͺОωΨlS΄;,M³Ή@Ν.έ y½1ιΩS²Ή―.½t”*xλu)oο‰qrΦ_νΎ»(d΅1]›_='H"¦Ž —ΏΫjΟΞ πθvΌ#ίκ+ζξεςγ-škxO)ό •>Œ E©Δ₯ΈΥ{*Φck€‡±5ΑIώ»Φj5˜y›§ηΧ+“ˆυVh''E 0oIp¦“eΰc€"1*}ˆi·Όj§–ΚCkj²‰\ RμpKȐρβτΐFΤwt‡@έ«΄@¨ŸΤYφΪ½½'[XτFΐc™Φ3I©JΜ*—½κzιν"šœ‘:ͺH–¦Ρ ηΛW-„€<ͺ>‡‘,uŽsŒKΐ–dδΪ•€³Ζ£φπ78δϋ'εЊA …~ό#ό‚Ϊ½Λ>cζžIμfKœš™;Έ—΄ž]?ŒgK„·E|ϊͺ-HΪΧ—δST†b=κ.λƒΐ±ru œκω³ˆθ@v€6lU~<“Fε^#–ΜΕ‡ƒY%Ζ΅BŒΌ‹ΐΌ'­€²+sΗΕ#“Œ¦ex©Η#J$†/dQ6ΌΫΫn‹ςήό³r£iˆ;χ'™}ΪΥXμxύYuϋπδ˘–<β₯E}―mφηˆRSηΑέΈFuυςUθiεύ΄S”γŒ_ƒf ²ybτΈƒ†:ω€AL'o&†Σ‘K#fωΝxfάΝΒΌ‰βφ#~Ά§JRψΪγυ#σ„Ζϋ#}{’ Še.ίφέLD™`-^a·ρ,hœ udJ:©³j°ϊ:œ>}[jΪ¦|`•¬“a!XͺΨΔ1ϋ"‘ƒ8 w λΜ[[nYθΛλ:Ώ/MJχj‘ί`\ϋΦ±k„ΆsZΉcJmdšΆ­™LƒΥeΩρΔfˆ©ΪٌόvmsqGŽ"oζΕseο3=δ$9Ά'¦8₯ˆ›tΛUN9M–νΦΎML rt>Ÿ K―θ(Γ$Ϊͺχ ’,쁼~Τb­_³‚’WF/wΓ4’Ξ‘(u|MβΚHδ1π φ{0䊟ŒFΘ#•FQχeΚNt­4f{Ζ…vaΫζ3δ―lœύΔdε…Δ²ε₯Z‘qΒh}έRί|ϋ5C6yν.xRώe!†ΝTώ[J`ΐ&ωΫ1ϋ³Οά„•uΨ3v%<H¬k€kToΕ.΄~Ω«yX7΄™ΰ‡ΦW°/ ¨ΟpΘz“Š“}X†»Άw/g€ΐΒΟ#qa΅Ά{±_ͺs|>W 5βΜφ|ΊsλkBŒ&¨°ΙΡ†°l}Ξ™Že G˜ύ³ ’gΦ0†ixϋ~_”n€6ΟΡϋσ+Ί‚Ψφ<[bT3Ώ]€—ŠεΧ>³NŠΫ¦0Δf]4 ɍ녽SOύOΤ}½qƒΠO‡1ƝΡHΌ-r sϊh65 β;έ  SτΨrΒ>’Η½ πs¨35žΦCέ%A¨†Ό]t―“ΣΜ{qΖSχ βώεβ†ό†#`¨mΓΑΤԘϋ=-zέΎPΨU~οnše BK7'Ώ‘ά¦zς2kΒaςΞρNNμ Ž_J3Ξ·%c(‰«Ίξx€ΒΧx€ξΡύnΖόΌ•Λ ΦB ΚΐwΏ|2Ly½Ϊ,ΑΙmΖ½©˜{cΡ­’M«Υ1$hΖ1‹!Ν4 h4ΝPx9VΫnA+χόAZƒB{τ5t5rŒ{i”»ϋ^NΔξ»ΈΑ'…>²\#τΫ‚t1˜6ϊ|+ψ{γ›έŽ™\sΓeφn)d€)ΝΐφύΩmβ( sό'X†Όs»Kό˜4oω²xψG›VτtΟ8/žϊΒ;„φRξ4θΤ Μyγx3iP7‘IυkmS(β~Θήwθ§ΕgtΎs\ŠNΦ[Ί>ΪΊ–Έ!Μ^ls¦Ž*(cΧx άϋ3²υν;« ήΎΤyΫ—ΉKVl8]n†9ΆCΎΆ ”0)ΈoS>WGyN’§kŒ:xœ{…]Θ]œαΛέΞ8ΒBϊ΅ͺo—ψξ’zΙG Λδ ]‘W—z©n ΓjΊˆψ oμ)ΏΰŒΕnc!AΏN—`Ί'=’ž/μ˜υ* qˆ+ΐ/ο"ίοΪ:ζςk]o’±Ϋ'·U|™sbΈ†Ώ)Zr1Μ‡Α‰€tΜ\OσΰΑΦmcYΊΜβλ8ΖΞάp"U²k¬°#έV'τ%γΎεΜ£.Ώψv!qC»*«š #x%©[‚τtθ»1Vμfv€XsΫ‘Σ φtμ#±σΉΜYϋGέ=g΄F1t΅ΐ«·xίhkφœrΛ:¬’ί’­3Ÿ(Ÿ1Zώ |Ώμ„άgΝ›\b*±_–κ΅x°ΎΊζίΡ4]HΝ‘«YΌ–BΧ6i*Ώ Ογέ½ϋŽθcρ-!rF… ‘―>Ϋ§&>ΤsoK£p±3Β'#φWrρ*»‚}‘F©Υ¬ϋœKώL„ >ŽγξΦ‹jϊ* ՊԾFΏY₯Ίzv•7 u!ΡO«Qm’ {5Θi¬P΄fΦΩΐφ‰&‰A+WK€μeϐ¨ν­νjι°Μ„”τΩ3QO(Œ?χτΈe¦šD] {Ηγι΅γˆ8PέΒ•€€H“«8“Γ9σbΊΣ{nΰθήΠUoή‹Ζͺ;Τp«§αρΝb—•£ς‘₯μΦπWΐ*"Μ5εω{T„3‘Šzh‚~]wJΠΑŽ&Υf»ΉΞπϋBωτ£O1}L+vj-“Ή*΄žΌk‰"ςIϊFjίωa‘“EΫΏnŠε?U3”φAΐνοΖχ“z„tߎáϊχ’΄φ3Π¦ο•Ώ>ςSΨloͺ^<‘‹€Ϊ "ΝF1@Žs/v• Λaͺ½ΌŠKsΰΥ3ο¬ίWΈ·ωΪΩ=Nί L¬¬3₯  Μn0³ίƒΎ§€‹ψŸΧ²ΝUΩtš4ΑφςVtDn‰ΖΆZν·p+“¨:νNΜ)_+•A†HσαeΟ~˜Ψ4;‰·βς³iJ­ύπ„Τq-3q΄ΆμWaΣpFkυΞtξ1εPj>9ΓΐD½β-YΜhBI{Θv\ΐέ”ˆ³_‰Xs‘τ…uŠ”KLFqQ%΄Β΅ΗgԈΘ1Έθ |½ϋ|ΛYƒŽ„|Ώτω²F(ZF£Χ5ωHOb=ƒ…¨ΩΊb[€Wώ²˜š²‡— {Bw“\1R›²΄λκvwžςϋ+‘bg>Ηί’°Ρ™y;Gϊh SJνqΥe„δ Lεei«yή(—FDrάΥxΌ<Χ,2|ή³Χ΄Z(ΘaψϊEΧ΅K΄΄…ΨHͺVύΤ©I9—@ΠΒΕ"–X ΄'ukΔ΅4ϊayCQk:–†`ƒ—Ϋ£θA¦0ν¬ =ΊΫ=εέd%§―ΝσΏΟ*αvΑQΛ%9¬άίA_kMxΗ59‘©Zλ©.$ηBBq’”)ΈŒΓm0·ͺc7_ώOgqˆ(»n¦±ΓJJ…I’rY˜Ώΰv=žC9σ+¬VSΫω5 /I«Up₯2ͺvύȜEpΙ[›~’mg—±aŒθŒV˳Ӂ­¬Φe2[S"’m>ž«=?FvΕF€ψλŒΠvίK οEuj^N"{QyMv8Ρμƒΰ=KΩΰš!#)€@O^Že ίYμηL,¦τƒ ΄φ[œU >Eζτ3ΞΌŠμΉ{rζΛ€˜eK6m‚X4¬Ίe`ޘ\…q5H‘hqzΈχbίq~ΖCΤHԜώ‘Μ Ÿ^™=`ΎυλΕωΫδͺ`yΡyΨ₯2'ΑRf¨ύz†‘±qoΞ§αVkδ¬CžUc"Ζ1άΆ_”b’B‘”. ­˜‘Θ†ς‘‘{  C?Χν]΅/!O¦‘VΉΎƒΨJύh“yO:\fαr\‹y=Ρ½Ž +ίˆ,€(ύ#φ`R,κΜ³υk§ {α πE‘²θΨθ™Σx?¨Ξρw'{Γι‘ ‚pY–4]ΡΌ«.“ »π«±jρƒ tΝ’έΡ5ΦP*ΖlTR¦ aw9fb@σΆΟΒΗΨΎMjΓ~–οͺo™ͺ Χψ¬ρ§dw‘ωλ\wL’‘e‚α /³>L `Až9WΏχœ΄ JoEΤ&εbΫl†Ž,θƒ’ΣΧ¦ο]ι G―?„°Ip\²Υ»ϊ ₯)…όs™ϊΝFΑiςΔ½QΨΥLoˆ ιQ*ί&ΞΨ―™ν5ΑΓEkyq\£λvρewΑ²HΒ„+ΨJCt;O `”}›KSzπ7‹v!θΞŠ¦ΘΚnJqίυ«t[ή_}ΕΨc*¬ύ0±΄°Š‘Ε]θCβό =τWšατ"Pi%ζΪΐsΏφ¨ά€Ώ~Ο’l‹‡+ΛF››πέ«eϊ–ϊ«Cd#%] L4 “RȞ›ϋyΜ±±ΡŠύ‡ψΛ2΅[TN ΘΟ%Uo!λέ·ϊ’ήΘέPΰ}ϋ|« _N+Α‡ ½œ˜2n|΅b³Nsξ)ΦφMσΕEXΓmOΘQv=»2‹kr}~`¦Ξ;ιι΅GBΗQΓp³ρJ·Ι‘ŒuΫ©*@ΝJHrβF0°š3ΰd©ƒlZΣ(Tμ‘tI/Js$ΟΘ’Acώ`€Hl]‚ρψΒ 0kaMΐύ…os»ŽΗ:τ4Η)Ώ+š»ζΈœλcΏΓ4T;b~ψϋ(-ε‰S]MΎμχαw-ΚΝ Aδ„zΆ6VΗ+u4½μ’αˆΔ•φWD’za˜? ²Ό7MZζέ½@3Yo§ΟΧ@R‚ c:Ϋ]!±™cύπ$ΕςΪfgd ¬_O†·‘ϋάBγ—_π~ͺ„JΙͺftΙΔΚψ΅χΖΏKΐ4†RΑ<”mΓlP-6”ψOZyS%Ωdβμ¬`R1CY!{M$z¨ #AΡΝΊm)²Ξ‡P•Ι:ΆC<;ψ˜€4T΅G#9;ψ)<$@½ ΤΡςΤ.Ώ ^ΊΨ4ρ©‚ υόχΤ<4xPζœ«{+ΊB…ω―3ΥZ#Ϋ–¦³Ϊ3v¦Ο?ΉY ™yΔεβ ?Θΰh …HLTpgΪβρž{ι ͺιΐC²x"Ό+ Ζ7GάΠ|΅"°€’tuύςKHW—κ'E~εc…—d€Ρ¨;–ΤtYΪ€g-₯7GΦ(gvςYP’Έ.Δ›eρΙέήa,Ft–υη£Φm+“j³•B{r‰O΄˜ώB-€j/χΟÊ}αγΒz! τ£šˆr°o2;r ŸεBβ*}·α©<ϋςf―g¨ΧͺZζhl0Ξ~ηΨaΞ3I1a:’ΰJΈεΥ2RHŸΖe¬3ο/~ηœz…ϋ—Μ`mυETr… ΒΟ1?~κ ζ όοΑd1άqως.ΙK9<λX•–'υ«–AϋΜT‘ I_dZ7_ιν§{TAšη7z°δnLJ$³jέπΝΟΦ miΏ'―όbϋ€―/θ&₯XtQ_²‡6Σ’Ž$φa!Ψχ™$’T ߏ.†ΧŠΰπ£Ro˜W`τTΑuΌπmBA¨έύΐΧ­λΐˆΊΗΙj偌o‘•|ZΉΛ“:EιBισΑͺx[@G·˜]Ο(―€s£o―μ7iΖχyΔ¨G4Μ Z{Š―υD©ΤȟƒrQU‘±Bœ<{ύγ(€ω\1ε ‚—]δί£N―΄Έ†Μ©PέQθθ@s6½±V³yπΌ€kυ5bγSrηΝe±±<Χ[wžƒ0΅Ž&_V"[’θ‘m]p¦.‰5Ύš$!­k ˜I(ρόBΩ@θΙW²_ίίR¨λzx|Οڏkžήτζ€*<ςq³΄=‹*fλ ίΖ”a9.TΫΉΰΞֈR7λόFφεAˆ₯—±KwrEΫPB;Ά"&]ξΈ±%Ρ³σΩS»GDΕΏ,,/’λ˜vF³·ήE–…CwQ#4ώ70@55‰ΦΰοŒœέ<“)nc[>13Ό‡Mλ γΨύu₯ζ[œ‚<{Ω$4zϋsΐΊΎ.Θ[QΖ3εΧ ήυ αJjŽ.°m²ΖΩ•³ig³mΉ$.Α ˜Χlβj«ΘXΪMΙ.Oυ€€H όνν]ωŠ$EΎ$Ϊ J„—χNtͺ&0))ΊπΡδ“Β·oFh§³ΈQŸ1Ωm¦Ω>OTΘγΤξAΉ*o΄‰[…’8«Ίqc]ΠU–η3#£½MwŒyΠΘ†Ph—J’/Ωρ©#˜xŒ§pj™ –œ¬Ήέ}dψ tE-`,…$L™ή¬η‰/gΤEο5αζˆ‚ΠάρέΊΘ@*ΪN+Ο› 4&‰λƒδ¬/΅7ό=˜” _Τ>&I¦^~EZl ”l –`kΏ/4؏k»φT ’qψ­ΟΈ ΘυFgυ+Ρ‡ŒP–SE¨Qα™‘‰€όFΆYn ₯5ΠιΛε[ϊhγŸm ³₯/ž-WΎ›u/~7Φώ’έόqx= _i«εmΨ>%?WΘ,”χ φ['Γ_VΓ0Γ?έyp…]"PC;@Ο:ˆn―bV‘ζ­τ€ΜΖ½pJΗηεΝ£ΟάΑk‘e¨fxχjΑ“Ύ;•ž7ΎP6•VΤ‰KΦLό-°Ml,kύωˆΞgNτXLzž26H£&ΰ ˆΪπ²αEfΗρ~O ™ΪΕ8w„υΕv LI叨۠XPΜ @έε>ΈςΧ₯MΈσopήDEƒˆζ5ΊφbύڊβBžξΎΫαY‰Τo―’+ώ¦Σ†δEKΊ“Τ£vΠ΄€§sΧεςΌkύuΔφΙ―ξHa7ΝΕξ€ŽλΠέ| +d¬ΈΘ+_JΑ±tό…™m'ξκ!bϊΘeΒhπ]¦ΨΦΤ»Ÿ>μ…Ϋōԩ’W²Ημ,ž Kd‡wA0JȌxJ˟ψΎΗσ™Y-Ύp%ΙΝCvIΨD­κ&ΙΓ@0Ο~RŽQ±i3φOΡƒ[M<‰ώ©–ό‚*w^X9*Π‹$Ή₯F~b`Ύ ¨hλAdYθΞΛέ'ρM`Ζ#TΧψΪ3Υκ:fΤ•έ>Τ°AΖ@ˆιχžρ^oάGΠ*|[xη&-dŠzήΠΦ―‘γΉSdENKŸDαNγ(€€Xαφή{χ0‰ΨψΡ΅ΛFΤγ]ρπ³ayΐ»ΗΟ³γ‡@ώΝ>uδ紁ɈLω vB²’Οb’ΞβΞ6ΧςxšΌδGo³2ίxΝΚώ`!%ε͐ ™:&ώ‘ ½aL@1”V²φ–ϊŠicΦe$Β°Yausb-T=’a£³%dUΩ²…½vZq³ήελΥ/f•έΘ–J46ξŒρ‡½ΐͺLZY‰ £q”ΕKiΫGBŠΖΆ‚ρŽ!O•ƒk͞”΅šΐ–)A£˜;?ΕβuvΛ€ϊ₯”—IbƒξhŸprϊΒΕEp*ΨΔ―œδ6‘ψŒξΪ‹ŠLΨ=c »ΞΘΨe?»­O|˜F”ƒάƒεaZ“\έv8Lˆ)ήW @k*fCJ¨dήλΔV‡c–΅‡T½Κθͺ3T§‚‚Ψ„w4cβ"»_°Π@!b&K6{XSΤkͺpζm@ $eBΧ§œS}ήG0䱀ݏmq—eΜAϋ)6ηύŸӐζυ>(JΒTΕ­Ÿ2ν©°¦>UΑŠ8ΪrM%ψ‘ϋcς΅’ωϊΘ_YΏ,£νϋŽΦ8Nφ}ζΚE/Υ^μ2g_™zΞ”γ½{bTCΞg©Ϊ‡/„Ϋ0mZΉ`ACΩ;]―œ₯ ύ& ±‚G‹ΰ,“ KiT°Οφ²t7Κi:ddN}ΗΙU‰m\ˆ Ύ˜h&ψiό|˜π(ΐmδ› ΝΞ ασd½ ¨e»ΛMŸƒ€ψˆpJŽΘD6ήUœž/H.Οƒq1Η’±jr‘ιΐ,Ώ1Eΰ-*Β›—‘ΉCKΟ²‹f*δΉ τCƒ&ΑλŒ ΆθšM'²ή Dή·£<ΚΝΜ‘žτi2F,Ϋ»JA"ƒ(§³OmνQςρœ|’x/ψΒυˆA―-Ο.kΌέZ(©-ΉR°lGΣ~τόδαe•)=H²ζYŠρ«!­‰wΫ°£™ZΤ=±,0λsΓ„τΤTw₯…-δσ°υŠ}¬½GqIΏν,rΆόά0@]l!6Ϋ§Τ—6υ•χέaΖχK₯κώ+Ώή³μΜ'F„)]€ Ÿ‰q,¬Ό0ΌbΦv­¬8“~H^^ςNλΦθTkž]ُhr€Ρή-˜Άζž]X’H¨ͺ‘PsfUΔ¨Ω'τ/=ρWv8jη„R½#z2OΞΣ¦‡΄ε­΄F[α]­ΘκRrVΠ…vz4N=ΓHηΈ?RSyΜtΜ*΅h“ίm€DΧΜO9yEDœDeς &–dx!v Gd3}’Ο•Χθ 3c“!±*Ψ‘L“1uΏv!«i9?Ά[Κ·s|%;!2\4ŒfhZC©ΞBΙζf{w[•w€ΓJ­…’Rγ‹=‚ϊ#ˊ@™χώ9ψ4O>ξ(ήΗi₯KX? π}¨φ6G†lψ |ΰΑV‹©ΓŒςζ<'wλAV+y„ίη@f)μ₯DmεX˜4'΅{ΠI*’=nMΧjOξCƒΧAιOjΦAΨώ†―Φ‰€XJ[;²±pΌ©Ώrη–φΎA†ΰ ₯r{σB1‰L£Γ³8‚mmΣΙFsΆAΞlxΞ@‹z„}³MJΖ…e|ήb*GώΝ%ay©΅=œ³Λέ3#ΪώΎμ³L~k‹Σ…DgŚ<.ηc†U° *,2bΈ„Ÿ‰†n>ӜÝωGΥθΒWKθ·]R,©>¬vΦΨZG…ˆy­}σς6‰χΛ3 Xxς~γY-[(<¦‡σwνκ‹Ÿί―r§œdV2¦Ϊ’Σ>n©6ηinx Χ•Ύ'g/qw™ηςΈβη`ƁΦs(‘)’„Α0ρmϊ’‘¨A{’WŸgΆ―Υ}ιZ΄Α”7]u=‰ΥΧθωΕθ—ΛšνGUΐΗ‡]·φ½šκΛ-θa-j>^Ξοb cΒk­ω€yŸ>Žδγ³K?²‘t¬&ο-*άΑjπz΅Xf΅(ΎΫψΕγ;ίΟ{˜2QΑ(ύ’»ΩKΓeΌϋκχLΣ‘I€I₯–{ο&GˆεŒΒΑӘ~ΨB£ΉήU=e'][ŽΪBζߐYΙ?ΨΠζ%Ίk~·=B*I; ΞΕ‘r##ŸΣΝš8΅|ξ0Α™φH³­@€’§~ΊJΡΤυξ0$ΟΫe<€^ϊώΔFvz#ΏΈt”›" ­7$…Íϋ·5οdatΆboν{ˆ»Ί–| ό]4ͺœnˆΩ­dIhδΣΛbB”ž6…g±r©€…TTΠ¬­ S¨§Ά/ς}G<UŠ 69‘5 κς@‡/€GtΖIι8Χθ9έιMΨ=cޝ#6€½αΔ%0vŠΖ₯Έ΄Μ·p@ ?¬Όo_ξFΗΌœζN™©ύ`»霈‹m<Φ gΙ©°€Θ&ψτγŸvΨ£CμόΤ3€;ώσSQ½ίK+˜ΛC““1>d³š"Sρ™Ξc3•O‘έ:©'ˆψp¨"Ή:εyΆΗ©x£ΐ—+ΙΔeΟόώiχ*δꝇFΌΐ rR‰ΖΗΔκΆΙ(qϋŸͺBΉ+.¦Ϊ…ώ€t.γM?ψ0ν}Fγr†Ϋ°ψ ϋ."ρ‹`<¦½X²:€_޽ηβ­?Ελ›γΧτ'ξΉ§s<Υ*Y·³\R˜…GEcΈF%»+PΝD¨γ’οΑΠHaCΒΕ₯Dώΰ7Φr ˆ#₯ιEj7bl Θ₯uζY¨ΰ‰ΪA5šϋgk±ώ-VQ³\«Θ: „"Εδq—φ†ςΧQ—+b/–B,σ6Μ ΄η}«lρ{6©wΔξ‹eδ^l.οvι Λ|#xQXX’]ίΆ°<ΞΡ?»Vш=..Œ€Ϊ›°3I€γ+ΓH¦0_Βԏm©ž*ΚΏρo £ˆΉFΨcƒΥπ†)ΊωόώgF}ι@ϋ–αX‰Q§L© “ΐ³Y™ή…Δy[K·sσΟPž†4ΠVN©°9O(ΥqΔ½ΜΓ‘"†ωƒώaŠAEYP‡Ε`j³½ΨΞHΟubUΩΑGΥ‘Ν΄ԏͺ([Ύ©ϋY$Mλ ΌΌή\­οŸ/ž1ŽΟνΈ^X#bΟF«–]ξf7ξͺ{κ=γΉ›+vά©ΆkυςP©’ΤΜάHKCΰρυ²=X”MΏΓΡ”Ώ_΄WvΧα9R‘ͺi=―κμe0g¨θ76Q ­–ΐ6ϊeΨ΄ΞlλwC‹‹ΰRτΎ΅e£ωx―σ3Š]ωjVύ1_it,K$ΡͺŠg}iΊθ,ΞJύA Gjϊgήτ‘BΗ™]₯ŝ› *Ofπ€—vA-θA΄kΥ|§bτγϋε€šμ5w—ό: %%ίϊŒ^ΔΦCΤa‰P;tΠgΫΘ)©¦¨φiŸ˜ΜFgb(n_Χ²Šά y_*ΩξTޟH6έN,€Rχχ!$’Š>- ή G‚XΉδŽΤΧ„²«ύ³uϋχz8ψΟΝbkΏ_αE•ψ›ˆ7ΆτΜ]qψtϊ£H’\™Ώ¦EΛhϋξΖχΎŸW€wθo‹—€[4₯Wœ5ΜͺFά1„υLΝzςμ‘ϊUŸΨ9»‹‘GeOΔΗφ|ˆ8z&2*“ γς4VΏͺM―ΏN sΔ± „FΑ4Σ-ά¦T#₯z3 ΕmhΎτŸΊš[Ξ!%ΖD¬Ι\(S&s+ΞY!; BY<Ιΐ2Kψ³Σ͌ΝB΄^¦ΕQOWΓΗα2†‰Ψ ΪL₯’97E+Yš©W&d‘ž”φύυ“΄΄\₯|ΐIJηΟ•‘€{€j!qK’Ζh‡pγΒ2%aεsΎD~μ­Τ]uMdΞΒκ7Ύί{dβbh_ΕϊdφΐςWΑmt?σ"²Υ`Dχ’Ίθόs°ŠœΈΫQuηs>&ŒώΦfΎ―]7½ΜR/<ΫTr4bό8Αέ€ŽπΰΏnW.σ„ ―!χG™L’›Μ½‘$ͺX4b.¬Œόβώ¬Ύ“„ Μ‘5%υ ΚΈόρ6up£Α2ΫρmŸ™›„.Γ_ΏoώnF°ιφΗ • έrΜ  ΰh΅o0wΥΓšwz’ζ‡·šύ™ξEW™ό3:=Jρθ'ΎζΧMςωϊΔσYX”Pμθ ;ΰ ’©ˆBwΨΆPp“7²γ,ΞA’T6}—τ οHC΅„ΣΛZΙlΨηœ Ÿ Ώ έ8Ο¦Nd*Opρ—5‹ν@vΙS>~ exιΧ~½rKIΆZx"ό άζΆZ¬xΔοUυ› OVAn€Σ”ͺθΘ77r]Fp’ ‰΄e›6°`IΧ€Ÿ-ψtκ]μ`ŽmbτΫέ1½mBž }M¨Β˜©©"K“L=x4K η\κugxι60Τ ϋD—ΉOΧΩEcδΪF<ςAdί«μŽ.O`ϋΚΙcLΣ†)#‘Ξ/Β¦Cxίh…šisΔΙζ;}?‚Χx’Ζ΅5&Ϊ)%ή°ž£ŸQB’―)K|*{δs4± ΫΏ&iAKά–)²³" ϊŽvΐnχ!φt/γ]’6p: @HΣs!|t9ώβό\ˆ;EzΙq‰yΓT²M9οί°γπ­g²ς71|߈εX΅<ΰμ¬x2㉩¨KžζL˜υτ…hτ³)¦{{«Y++Ž„a²p W")΄ΎηϋΠ«^ƒNμL¦Zκb)˜Άίΰ\Ά;>a?ΠS)fTθΈ·<-ϊΧ+Cfο£n–f›έ«JZΖCΐœΛσZgΦ•ΆwšήΨ;Eω ΗέΛΰFΟΧΏqΚή3Σ*ώT―j=ί’μ+°ωλ•΅ΩŠŸ—Ύ…qb°Ά…ά Ÿuh ™c=}ΗK~Όξετ…“θjPς> vε-dtς_ΈwύB`HΪ"‰Θ„jšΠ’₯wƒΪ5φΞ3HΕwη‡ϋ φn±Ÿξ<ισ›NηξΎ¬V|ΘtξœAφΏΉψρy‚-—™VXμ[Κ-BηΪk½[EY―φϋΦ,UΌ»~ghόώ+YΞP?uʍΠ)ΟήΪ rE•6 ρ°|KX˜ή€ͺγοG9*Ύ$‡‘‘žu¦euyίqm/5χΎbΜ>¬1ωΝμ©<²•UO, /ΩWυXΥϋΞ…ΜΖ–Π“ž]vΒ“ΎpmβπL%ηω£Z+œμH–*LSΣc_ XYHμšΖͺ,3!o~»=ςΑΚYΜZ/š₯ρNkSlΘ\Ύ‡XMΠUΪ”%L`ΊbϋΕ΄ώX₯“A—γ@ ˆ±~,Cλ`Ό:†^7mˆœž—2s«‹‘€Oβl’§Ώ79:ΔΧ¬bŽ-RO»±Ή••Ψ56*x9J_“Έx©©ΝIζvΊq£TΏ$V/fa~οΝVDν ’">Ÿ‹»€]$DΔY/ά£‰ςc0%’Ν ŒΙš«1?nWώ›`Δ‡ˆΠΐ¦±q,€‰ΫzΨ‘PK‚ 1€ukИšsy 4q₯.ρwJΞ¨8zσX”‘ζLΕK:Σ»‘e€c} %]ΜcψΤhϋy-α:χΆ—X°ΊšΫŸυςΖΠ9ίΘώτjψ²νM§ίœ9Ζ0@žI‘βQΉ Ρ―z‚ΟΰΑž§ˆ »θΜ0PιaΥ}Ν―+‰})Α ΠυΟ:<±b±P pΨ΅λΫΆv@‰Χ+kΣ ο§›J},υu\}SΉε„B[αΞ₯ΒAzη¨Rͺ±Τ¦J O7Oί 3“}X™’Ή8οn•ψέ?[7‡Οp¨ΪT¨I#‘γŽPαλ+ΥOπl‘Κe¦άd-rlΔ9˜βE]λ.U”f"αυ₯RΤ}χρ•Ζ“8₯β”ά.Κpb‡¬σŠ^&Έό‘“5μΏ˜~ρeD=œυ +Ν°dγΓϊΕK~―uSδ.ύΎ³™{gn†%δ—šX˜<0L‡uY|λ½fΓ%7`Oίo#–ΔζΏ(βM: œ}`; 5"Έξ±2ώ§ώ{zΚ!]ΚςΌΎ‹(΅“¬ύΰβό4Λ¨‰AoΣ~Κ+@[ΨW„ΜΈŒΉMφTΜ΄ύ7‰‚ΑΨ–Ε†ώ€gΆ#'ΪΛ―w © ~4VΩΟ\ήΰyι>ζ3$΄ƒβ˜όLΊ,blγ(ξ,εΥ©ς Y0Y₯s#’ϊ ΆMόm,FN‘Κ½Ξ_˜Πΐ£)N΄)˜>¦λ½.φ†λάp΄3&ΚΧB/@Οu’ν.™¬z’š}ZρŸ‹~X΅£ŠrST x #Ό_€ΐΥvΝτθρeΩσ‡ˆ7·Τ«œοK˜£ γ[pΟ‰KQΚ9“˜™‘έ@ ά3R|gžΆ >θΗ°‹œ0nσς―ngξ±£_6•θΩ‹Wgψ­~b‘Σ²8Μ =€ζιw₯$ύͺ ›‹ μ8Qη΅tͺΛ]„f ·~±υ5ˆσLjQM+0 –ez†ύΒΛπn J―œΝmŒ|κ ˆJܚdz£RΊγ;\_lς’±τu»Λ…ιϊ šIi„lσNχΥPτθbΨQQv_vvWλcΖD…8>$*,Fμ3ϋYZb1zβ{a»¦πvH6HσoyΞmUΦ³k]Uw?fM­˜ ΐ¦¬Κ"ΑΪκνΟή`ˆ˜DLg&Rξ·X8±†ƒ0ΎRάωΫω%w5­ADORqτ& rSeτM₯HΨ‰yY³Mƒφ]Eκανo5ΎΛŸψa4±n½ηTU}Ζ,aΫ€’0™β μδuεAΣ%₯hQ²DR΅³[’m‘Ό΅_^ϋω?7Μ²ˆƒžβžΐοbΙaκάφC@§Ew sΈ#’Ϋη!ΐ2wΓŠRν¬Ϊ‘!Σκx:‘qν¦N±Qϊ.ζ<ΫϋΘ‘R€W1Ό\dλ–ν{K@ŒJcpd– i`kfΣ»%΅us€(ŠŽ=ϋqη«Ζp&Κ—~Ω-l^χ­yΌήιMΟΛφ©Ά μLΪε`NίΈήښίξRΤGs‰’fφΩ*‚©Iφά\{Ϊ`£2ΈG»¦$f~-ΐ‰ε΄,Τ0›»yJ_χΩo`CζwΤK_χ;ZuΟ?XO1T… K4J―R tB͚SH{5κRρ5ώ»ΘŸ[~„ZSYφ-u‹…Qt7άlv‰8ΌΦqψ™ά]#ι:Z©“nιΛI ˆ1NΥΏRhχΌψ±ύM$‡p ΰΕJ“‰΅ςCΠΜGζνa,ππ­`&ξμ,ΐ“Ÿ±‹(㈍IΊ{FΣ5ΒγiˆS‘%ŽΘZV™Κˆ·eίήj:狉.ϋb1Ώaή1Dμ?@†lžX, `H^ξοψ19½–[ΫΔw&²§T+½n\έ2nXUΥƒvΩn―}ΚŠε“ ε“¬r9Ι–Ξ[ύ5ύΓ‘Ρ|#Ϊ\μΌΖ‰jΦlϊ OΚΐΓ|’WΞ«†L½Š‘δh‘}νΣ%s©¬ςFΛ1%̘φο耐aτΒ€Š―^0ζ{8χ_΅ZΛΙHW1Šω AzΫ—ΣwΡ;`ŸEٝ›ώΫΨέ βΏ"·-GΧFz ­54κΏiΆ8QͺT:tL‚°ZKΓȚˆ… Β"ΟVνo,@΅'>ΰ©›8]’χ§™ςγ>~ί&w³2Ω<ύLLΡ`ζ ’z ΖU$tj¬?V#^Yd5’PT4[Μ*ήeŠ™² ―χLwλw š‡e£όˆκδiν$GMyφυ΅xH/’ ΥJBRjlύ^Ψ[ϊ»ψIˆdΞΠ₯H.¨g¨χLgΑŸŸh¨Ή M4ŠxHΝL•Œͺ@{μ^ψ/Aώuζόr_fxg ϊ ­‡jrΠx2μ,•[€Ι2l|©εh2¬,Ά0g)ΌjΚK­Sx9TPΣ †›WEpŸmYJ2-β”—5d2UVU¦ΚΆWsτG)λΏAuB砍|V쩊##lSωQ­ΖΆ1ΉOrΪwσ?…ž›=p†ρzHΘσShX&ζ­2ο“xb#sC=|ÐYν€υ€νgΨLαv}±χ‘`‰Λ&`n*έ.΅ΜIηL )F? AHΛ –f—‚­ε2Δ]-μ…ŽNΑιό™ί…n‹#κ/ι ΐ€Q± CκΡοβΟΕ–'Δ©ρΥvŸΓY(~4ƒVl’ϋ5έΛβ`,5lΝ)†Π™η\ψΛ.]ΰ&Ο»ΡόŽ;tϊ3k:…JύΪc|•SBzbή{Τξ₯Χ΄ HΧb/zŽ_§‚ °ΫNiYQ1dgίVטЦ—2C [>YΚΛea¬T²αχ<ίΩasΈ#Κ!κΝg«ŸψYφ·σωΠξcA.₯OγCNΰZΛƒς7όγ}ƒv†šˆΜΆQC-,FSΓ€ŸΓ2nK¦»ΕπιέŽ*εH w ±"Ζ£s†€;ύύΦ„΄d™›|Α₯ψj{.%άPž*Α–TΡΚΘ~4›Ύδήρ·š/G€ƒξΓ±Ιγ$υD̊3ž‰₯πXΰΨώΕυμξ•_φΝέ8ĐkΈΈΩ7>PΨo,y6cΙ:Ή¬q`›½ώτeG,Ω―ny6έ!­}p"„ι―sλ–ΐΞΎΒρŽuη²ΦsvޚΌ1eΨ–wb™ƒ(T φ·>όZQΔΓ,%Ρ+ΌζA"νΓσ α°όέ»ΘmΓCCfδžLƒ)Xώ!@'€ΛΖW¦ƒ57=Šψsψb6KR{ξuŠx;ίρ·Β:ΒΎ0‰Ž„δƒΟώ/ ECΕπ~ΥοD&‡mάdΑΠ/Α΄' qφJ”@sk·ώ8n•–,ϊ °XέβΔρ™ιfE›;֟OΏι²οJΈBΕΞ΄AI , RcIγa¨ka0Ήkˆ¨5;M1φηά³–=η|+rlDuΆMβΙMώξ}+œ+v9KΟ•’νTΩιΎΝHνBΜ2-\ͺ€©CGύtτα'¬J¦"#δ["žo=Βe”δ¨ρAΨ₯νβχIΎ€”cΈl|O*³JΩο©΅ κŝZ±›η‘ΦγwΊP©Ο_/Ζ%_/KΡcQ4«αEΟ׎gƒš9Ÿͺ½“GδΣ_ϋŽFβ~4K€'JΫͺ9ΎηœŠY xγKε§BΧυ©ύ>Μ· aχ }s5.ƒhΈν€Ψkˆ !kΆ±Φη²Ζ­Š— §AYΕ|n$ŠŠ^‘‰γόΊΰu$›» ΐή x€P.›±uxPφ žπ{¦ σz΅Λ›Β!kψ­+L£ΌΌ :FήΗ' Ώ:ό¦Z±y!Aš’VV^ι³ΕΔΌ·žPΗπ?+ΤοTΉ+Y4‚B 8q­‘Θ9€‘Ÿ­€fgwŽ6 ΆY‚ŸΨλimŸyo @Ν‘ͺh5Ό§ζεό|0) H˜]$˜P@9δ.qPπ‘;+άV€3ΝPu‡eŸθ…Ώ|΅!Ό;Ί€IΣΦ§(Oͺ1Ϊz•d€ž0Μϋ£RΆF  •Ύο€ϊs•ζ…ήηDppLΐ6―™B„›ββΛk=F_ΏsbI1.38žJζΪL{t½vΨ§ͺ¨ΛδοX6,τχϋ]6ά §Χ£ώπyλZγσͺΉΝ«ω—z˜žν‹;²ς€–mΔΙ/α‘KΠd^ς«yΘΧ”IZΜλΘςν `†γ:'ƒΒ=+Ό³k> £(«;Ϊ€OFη!° »Θ”W”“c€KΡ³ZPΘΓUΕ¦Μ½Θ %wοM†ν"ΝαΕBκδMΔΦ0ωξz:θδώMj—J}œήΤe›΅ͺt^Xznέ΅ΑjΟ*τ§9PVWΞ‘eγ‹Ϋ_Έo!"ξYΓ*Οb‡Ο11.-ͺ2ΡNŸη²ƒΉ5]«OΣfšG-[eU³†VφGΈ]o­γ ΐ4@KκΆΤΦ…Πk©%v’§ύ&±L©D0Οͺ}kλΣW£ΚΨΤtb€Ζγ1S™K₯ΓZ[”žλα X­―R|©Ž»§rρpάίλ–ΙθεnΔ«pƒKŸςŠΡμΤ°\ϋπΌϊO EΧΚ΄‹fT#Υ ΆXΏΖTπςejή9Ό2Ε+:ͺŽb[Γvˆ’„–0fͺΙ‹s4ΪP δθ ωyN9\άΕ™)Dωζiη0ε!g€δ¨Ϋ%ΓφΞ)W!4ήΔωAέ€««~μCϊΝ—1«1α§έRΣ…r/$+ίΪž)€‚EϊYpŽ[φ“Θ―­rVΨ«¬“ΞC•ZΠΛA™|iΗρ·ΐ/EΥ‚L±‘ΣrτόΑν˜[:# š«4‹rƦԘ³Yo-©I( ΪPŸ"Ε&ΣeωgΊΗ]MΧϋ ½D0 ₯n j² ¦lε‰NšΨΜEiρΤΨ;‡‹rΗ ΰζU΅υϋΐ>{Έ-΄ΗaOπΑ ‘ς€ŠεΠ}όΰ;#―bΜωHAv€>υΓα4T-…ΝUiΤΕ:{Θٌl Ÿ¦7Λ΅ρ©-#…ζΆ3&Ο„€Ό³ό—@*Ί˜]›M&ρ»ψοœΟΎ$tζ+jτl’Κ¬ϊ ‘…½έ…gCθάςZίΚ:š‘1‹Χp'΄½₯Ž?i4ΝzUΏκΧλ$ΫhaMC3Slι;ρ¦­λω¬F’ΥC lLKc τ Ϋώ­œmΠAANό‡)¨LS-ΖΞ<Ϋ©…Γx„αοΗ­Y'Κ?δ*Υγ~1ϊ!`ύbS+Xν8ΰNu³ΒΤ7Φw2ΫgۍτΊαχθΙξ²·Ÿ6iϊ]ΠC:cΰ”π,ΰ bΏφƒ φ9“.WΒD‘˜T›«xΚΧ ƒξ’Pœδe]›x©Xψ՚―^χή—!»›£Φ’ρκΩ; θ±©¨ ψkg²kœr—LB‚ύώΈόΑŠΊs=ebΕα±™a„ξœΚΖ™}Δ§sC€‘qΏ¬Π¦?ΜPΏαxVUωR2­ΜΚ iΈ³8eA{wHš™Ν€³;£UΔCξό½Δ\hψMπδ›’σρƒ~Θϋos1φ’Ίχ%֏0½όLeϊž&ˆ‚d΅…ό}hώ’ό³­ψγ*΄ώΞA«(εšv<‘°λΑ'­Vωmͺβ£n§Ρ³ίΆ!σΗΠΧ€Ξ'PSΦ¦bεΊaCyΉΪWg§7ΰ11θ9—o™ζlX‹)˜Κvεœ°ί %λŠ)ψ’6i‘qqζ‘wόj†«έξΥ†Z¦q8Csώˆ›(;ΟtΕυcςγL‡"t‚ήFρTσfΚg@ΛKαRa“ ΫS%Qζse€N―υΡ³Rβηοš51-κΊ"͞ˆκ#Πw`·’ήz·|9Ώ/΅Ž‘šβχ­­>ˆ‰£tιΝW8MΘτsyΞ’2αqφd—ΥBršš"_tR‚oΙΫ Όβy[Σέ+ιξ/M’ΈΦ’ Α}ŠŽί8\ρrΑ1―.ΙLΔPε)Ν /£_„Ϊ’nvΡΩΔBΙ ²teXi;›Τυ;/bα°B±X§Ά‡#ΡΤdgŒAJQ UΧZιώhξκδͺΙμΦWQC>ΒckQo…†Ίˆj€_Žœ?…ψίρNc”j‹TΥ² ―γܘ¦j~'t’[Ηζy°@α―;R0>±:n¨l3Ÿc΄#RλzrDK^EΓc«°Eόε|™^pΎπ,hΧgC›f±VΒW@ΜιCqό}dμA°,V«7f’B‘/θ΅ρι˜\+P šgμͺeV«»πU‹Pσ4i5 ΰ :Έ`kΑ²E Ά7»Ώ‡ψ•ϊ+ΉΒ΄‰~žrίΝiΙΘΑtlΤOΟS/έΗnο™ΒbD3;žo΄ς±st#Π!Γpdεž„ŒJΉlR»ξύΖ·Ν GUΗ}š ’9ͺα ζγΥU’υ1Όe™›Σ‘½ωΦιC9¨όId(lςqψΥΛήψ-> Mώ¦‘H04†³…F›+ΫκM#Ο„9όGCw†uκ#‹„R{Dνe.ŒΰN ΰΗ·τgιύ‘^cbιy½f |y؜ξh€'Ή ΝΎΦ3Β:­Κ¨κ E+t;c­’8Άa)-56ΎτS:ξσι*F$>λrν‘1ΫΝjvκv};σ7gA'·«GΉΏ½rhΚΚΕvΚ½υv•ή¬μ€ΑT8ΑΤ"ˆ μΚ~’έχRΖdΕCTαθΘ†;ΕΥΫ"!¬†τ²ϋwnxH„˜B"yE«cFΈS¦¨’ϋœ 6–ύΨ UΟxŽ h Ώ”hwizRWŒ!Τ¨Ωω<ω©ž)ΊΨ=‹Σ[iόHI²a~‘TΝ#ϊΗωjd2“ΠQΟDχεoHlk0@επ‡έ>%=!;Ι™Λά 7‚ο ‘όŠ#z‹λφ+¦Δαp6 |8Z₯8{ψ9Τ< ~w?ξ­ \³»’:Ψ'Eο₯ΪV-slC!Œ―ΑŽ ςμεΠ1w^$πTώ£Φ›ΈN΄}‰/s–ϋƒy]ΪΜαψŒ‘Ϋ0Ξ2Γ’μΟͺΰλ!bήB; γ‚ lZ€ΨΖ=\ MPΨ~o°„ϋε•$γlΜ«°ω>Ρψ.ι @Τ1συΐΖΘΗέ ΰŠΚZρΆB/K³mVbZnΣʝ0Kl§^¦†.Ώβΐ§Ρ4#ώεB yΏτN(H2?˜7$»BUα½°…Ρ-ώήz€m±WOœ­RΆυ;kϊΞ.tΕƒθ2jΕs© endstream endobj 528 0 obj << /Length1 1386 /Length2 6039 /Length3 0 /Length 6990 /Filter /FlateDecode >> stream xڍxTSΫΊ5€#H7 ½& ½wήAjHB‚$t^₯7ιJι]©€(]zQA^τxξ½ηώ‘μυ}σkkΝΉχαd30T‚’`κ($F$”¨θ[‚€ PT!αδ4c°Ών$œf04…”ώ„Š ŒΑΪTΑ,P…άσD@’Έ4HBˆRQU° ΠάC!ahN”»―άΙƒ­σχ%€Β IIIό(ΉΑ<ΰ0  Ζ8Γά°!`ΐΓ0ΎHΑ#λŒΑΈK {{{ έΠB('y^€7γ 0‚‘a^0(ΰΧΘ=°μΟhB$œg8ϊ/‡1Κγ φ€°C’±!žH(Μ€­0Φθ»Γuώώl$ϊWΊ?ΡΏΑ‘ΏƒΑΚΝŒτ…#Žp  ―#„ρΑΐHθ/ FaγΑ^`8μ€όn PW2€±ώ™ ρ€»cΠBh8β׌¿`·Y UAΉΉΑ4Ι―ώTα0vί}…+ετ{εGBυt6EΒxΒ΄T`°&’ۜ`€PJ\\ €=ΐ| ΞΒΏ ˜ψΊΓ~;›±3ϊ»£άŽΨ1`pGφ‡Δ φ‚0ž°@tόsE pΰs‚#Iώk†9ώ΅ΖžΏά`ΔώϊόλΚΛ0( ‰πύ7όχ ›ͺiέ·4η3ςΏœΚΚ(€Ώ ˆ@PJ@β 1@ΰ?σ€αϊψX-€# υW»Ψ}ϊ»e―?ΰω#^ΐ?sι‘°Μ…xώMtk ‚ύύŸιώ;δΗς_YώW’wGκžΔo?Ο_€Ηvƒ#| °ΜυΔ`U ‹ΒjωίPsΨ_Υ…AαžnνΥΒ€±jPB:a-Ί+Όϋ—ŽV‡ϋΐ p Δω/Φόe7ύ₯7 3@‘αΏξ0Ψ( πΏ|X‘A\±w4–šΏ]0¬†ώYW AA‰MDLφπϋ’`Ο»ψƒ°ͺ„Β|~“ ,„Da°!쌁G”Ι―ƒƒΡΨ™αhWμ98r’ό£ΔΣΓ«ΆίLΐV{ύ[Ϊ0˜ B2?‹‚Θ„»Όο8―UbςόζP欁βœgξՈ" zριXυ]ο―t§Q₯eg–:Έ Mq„ΓDmΏLПΫΗg'ƒDlε’ƒŸž*‹ XέGΝ.dΣ4Œœ4Zxzl.˞#w„NžφΓ+-n§"7Z^w χDν£8Ύ²ΠN…$Ytfoχm%7k‰Ύ₯2γSΗΤiΏCuΫρ&ζ'Nδγ΄wiΘάW”š›`O4₯(4‘zg’³Ž“©Gl)‘Γ°†τ {x1’²)Q¬MεγmŒ©ΥX§γΈ…Θ£ηcΣρ7RΥ™Μέ΅wۍF½=χ”UΤάsR’«ΥΊΑ\Rώψ€ΡfAͺd'’Τdΰ‰ΖP‘Y€޳cΙBA{hۊγQK,Uύw χύΣ^ζ4muΜΒχύ gxΕ‘κ’?Ό D?|ΆΠν«„κϋϊp{έΖό“j¨n+A΄εέ₯£Θͺ"ΛΔ™7Ejϋ:—"«v"­7ͺ[ΛQšιώ$ [>S 7‡ΆΈ»;<ΪQdη§ίΝυn³e«₯–μf΅&NγΰJ[ΝDάVΏ’³‘Β5r=gUΟw8(©δB©œΫJŠίχπ…3{9Ξ ΒψΗιsu·woΧž!˜!|‹Ρμ_«mƒŒͺΎΨTΈEQΎ…—†kWθM%°i݈{1ύβΗ:ύ‰ΥO;Μ΄ΉLVχ›ƒΜAOEž;Ύ7₯4Σ7LŸδϊ¬žœ‚E?«—!π»Ž$}π«˜ŸMaR4Ν•ΚzͺWdκΩΜ'~’©΄ ί 3CΥ?~οΥ–Ώ“’ΠSŸv[&-·NnδƒΌ@ψΐjie—5{ε·¦τμ[“F»Χ½ŒTηŠρs UIΘ§νςιFr):]©ιβλJZηΆΈυYήσΩ4%P­!M?WΘ™h˜Ο$ΦΨ‘γaS­zGν±ςQ4cιΧQ˚ŽΒ]α―W‰γV…­?ζX[t8υ ·³4Γ"S©e Έξ=yδ•<#0ΐΡΐξlZͺ«p\Ώ­°œκΞ7.αEώξ–ά{:pUƒ"Uρ™^Š˜νΎhzŒΙηzIΗΆH£―aITX«>oxYPbϊΛ'yqΈι³ο)F~OiŽŽ7ύ£&l©T?Λωge­(§lΝ~90ŸqδV9ς]\©΅|>ύ\ΡΒ*™Z‡d”xΐΖπεšvτΕ]W}Ή[»?+gM)e Pjo}qΕ}›ΕG.AτΟj`ημ{σΨάΖ΄5ͺ=ΤφG3Wάη£C*IDzβZ3Ί•+<ό•pʊ-o6›ΙηΎ`0ΩξrΥΧω‰ϊeΊNcφB™Δτ O[γ%±Γ,qe9ͺχE^ΘχY&«DΥY‰uγμ“gε46Β£‚AώΡ“CŠΣό˜³αδΧ.Λ!ΰ1wδΠϋΥ†ιO3ώdzε‘8{Μθo›ϋωœΦwΛzΟθBΧ>W™-¬ †uƒΛ³mŽ7fHqw0…ˆΤLgJρδ+ύh―»R£Ν7υRIΖ[<ƒ]6Cχ3βWILg­βg―dŠλοglt¬yΝ±JΉ’ϊR%Π5j¦ψΊΛ0ͺ½[€Ί0μ‚r'€χΒmͺ>ζ8ψi¨(°s†ΐ>{³mΤeŒŠΗlp|in•Ύκ|Λ;Τ™ vgn£Κ]ΥϋΉ›I°΅‰τ΅Ž0S?€σ !0jΎ›)nξΓ-R·‘ˆšϋŒ}E:δ’γ/ηγ!#Gγ¨›σΈU±9:šΕoΫ΄?5εσfž>Θηχbι›ή?Ϊ^\s„ΖNχMά₯½ b=!ΪŒΰ’‰ϋ8wΌnβ΄c\6τŒͺπ±Ξ‚Ÿ†'Μ2,’οΜUΟΌΡςεϊœμ†r`}Ζ¦kύϋ^Β%£]ž…™q[‘9NJ ΕεŽ[x;NάΕ&"ΐ- 5ύz§ό.ƒαη6ΪκπBί<š€ΤΔ―ΫΤ{ό“ž°φ₯©5BήΎ˜ΗKΡΨ~'\}ΚB·™‹ΐ»Π„eGΞ¨4lzι}]gΧ$-Ω!ϋJXoΣ*TΊ•τ2.?`½–g­Υlζ`)V£¬ !ͺνΘd~ΣoΡ£ΐnW?wΕέ‘H ]@ O7}oz]δƒŸπ‡ϊ¦yΏ)ήϋα­1ͺΧXξ R|[ͺνΊ7Ε2ή7ώήr€4ΈUE]z™aEi-­υU'ΡU7¦yYh…cβƒΣ-b‰0ˆαkxƒ•'–8–tαx».DΡ³žk‚x%ˆΚαμρ½{‘@–ΉŸ! •½f πnjκΏ£uɁώ€ξbyθ•‹―Iv|Ho£™Ϋΰ—ΗΛ Jί8© 3$%οΝ½lΛΎ&εw½ύ³I΄Κbpζa[rfδυR ίξc©Έρ­G…(—Σ]νS—Β6!b‚Ρs~Pνμ‹^­„ͺΞα}<ѐ³&A$κΈγ°“[vΒ²s‰&Έ>'+‰γS»σu𠕐αo£R!ΟρOΨΟ‰m"šο)ͺ gϊΊKμ[A½!»£Ε£Υ¨CΙΔ~moC§ρξΉ| [ςP‘ΩΨθΌ±žν:R³˜‡Η――.ΞέΘn"ΐcd6œ7w„ΨKμ“ΪΛνž6Γ™ƒΧά_žφ'°’SpΕ|…,Βρ˜§FŸσ|a.2)άΕ)9…‚΄ Š\++ίΔΊ| ͺ,"bBύnUhMοEλœΕ3Ζ’QŒ–/°―λ©ώτ˜~«Τ;ΟXΒT悔 Mqw―Qη«,;©[ΜτΪσП†Ž!%7žQMα9J…ρΥ0νXHŸt­ΛvdKσ.κ°8βŽΖΣφJ₯pS\dψYόiہQΔΌ J)N½΅Ϊ|[!Ϊ=Έο͚Έύζο½QbYΰ%F~=ΏQ?c‘FΥ›•τ^gl‰όήφήα¦­*Όμͺd_-›χ‘E€‡νΰζ’iΤχ€œŒ;ίΔΜΒ·'Mc·„ž]LΘ]ΕecίgzΔτ z 6ΠΩR₯¬ kŸSωHXψά•‚j^ΙΈTŠˆQ© …JδœΜ¦μeΤ4>žc¦ ΪV/cbje`Œr—Ϋbq…Ψ™½a€ΞŒ έδΑO`k³nξ‡_οE‘kλV2Bμφ·”¦‚DKW i7»•Y͎rμK©΅Ρ%ΉΘ‘<²QTnδω{_―±£=+Vκ4έ]7O9kc-gG©¬’zΉσΑ‹ρύŠΫK/'ο6Ζ²F=Kšψ znΰH–ΈκH`ΦΈΏ@όx΅γ:σg₯VΙ_yΩ"νh¬ϋλΈ;ΘηLƒγͺ€)Όxk\(°°uμΖ›΄ζ²[Ζ»ΤG³ ΅uΩΙ‘rτΥ―Ωn]»Ν7Y!D ΌξMΠc$ΩΖ£8?(ۏ ©Ψ«Η₯εφ8γSy ΆΧnι°ƒaΕ1Ήωϋ”™ŠζΖyžδ[Χ[3ΙΧe}Οsυ<Ιΐi„[ΕeύB%{Έ½†βΎh„Θ ΚW%-›(ή[ΊηAMΖE_,3摍Œ‘ρY‰3\e|Ά ,ƒΙάEŠϊ ζ‘Ω₯±ώgΟΊX'wk©―Άζ+ψΣ δΥΑM²…}ΉέvΠ²Y¦›!Rξlv]Έ₯† 'kά%z9 {ΛΧ3εΙqϋΛF_ΊΨ4P#D-δοϊ)Μ«ξΛιG\λt@’±Δϋ?Έε`Ι#Ο>/Ι·ρ¨khΤ΅ΜWτ΅{Žσ|£ΉΪC–¨κ½znσ,˜΄“)‰vϊΔ_-…vΈΈχ‘wΘ<Γr•­ΩςΊ3#&χΤΟ•ΓΥ•Γζξv -ώ>Δ±₯π{ e» Ύy†βαΘΡΡΌΣΗξ·†5OR ΏdΕΰ;,—Ρ »]kAˆΫαͺ\βΝ8]ΓvξΣn>ΊΛΑ&א‡Y¦™8ΧCa"…r7ήό‘qΗγ֚έτε•’sί;<«5φœ ώL”₯l†¬ΉΒ@.τ’Oλ±γΊr%Ռǣ=΅₯=+Μδ‚“Ύ6sΌαSŽο/δn2~ }—«°U…πRڈV0foΝ0Οΰpςιj2χ2fmηΛ¨@Ÿ.g^ςpοd¨έtΉ,P§b€μކD Y0ι»άg+*ΦmξΌψΥΌ§?sνngSρΦΝπ΄~Ο)nΌ‘«ΫFXNφ`ΐ‹«©fψLe鳨N“Ξ}t½·‰§2Ί£mΌφά Ώ`^uy‘ι£Ηuκ'¦ημνΪc‘‘ό•S–]0 `%Oϊ)ΚΔ”­Σ γδJ(υŠšΗRK0)aό䫌ύ νϊ₯ "Mι†ηO–-5Y@©+νšƒώ-ήaƒFΆ–“Ω δ$O8ΤΛήfhͺΞš1*ΈϊN>n½i°Θ©.ΧΪ38 Ep:Z¨‘=gΰ\P―Αͺ_kΨn+:X…hί„ΞoqΚ‘xXv:#-ξ"δšύ]μSY 4{»γrω#‡} 1E(αBuY0Պžˆ«cψεyOB4/ˆr«αΠky«ϋ8Hϋ½Ήξτ’¨Β»ΔψrέC›oζ 27©Όn'EPf^·X‘Ή|;…8¦Τ‚&QΘ`YKF‘€ΝδY4@θF3šnοΤΤfνyXŠά€EμΟ—)b /cƒ=‚u1ςΎr5‘|«!*x]mΙ:1ύLJu…ξ¦σώ’τ‹£kΦΗgsCΜ:!ΈΫa…©\ ξ©§†ŸŒΆΣέ…£ΙxV™fO^φΝz™οŒΣ3zΌ:Gφ/NTΦ+tί kNQg7ͺΚ―ΰσΠ6œ2°OWNΚm‡ 7wΓ|Pχl–ίU°½ρΎΧ(ψ(?³=$F΅_dΆ2R^Ο_EUά\Uύς‚E"Άήšύ|Ά|ΐwύσp_*‘IAœŠΨ…†‡ΣŠ)A₯ΨŽΔ¨Ηq\΅‰έ±DΣ?jT¬ΝIζ?ή"+¬!ΠυϊΌΔγr SΆΊ ;/Bμζ،ΩτβΎ1§ΠŸKfŒv#Š{šP½OldΥuk"₯'λrη ΡOξΠυΎP5ΝωK™ΐΫϋΦΊAyYι9XbΒiDδ*Νύ¬»¬ΪΡNQψεύΟzΚΧ)hγrMͺ3άΰSvΞν{CνΐOE―W=U#sScȏΈ‚/ϋ$₯„.gKΫΧ½”!όAjέό €Cb%Ρ\©c‹Vω €Σ1±–ϊΊ΄B&m.‰ΐΨT 2@ξ"’βfUό©R_B>ΔΥό»kqQy'ξˆE wΨ‹―,%tΉ΄=²¦₯/ήι½—ΤAA]ή£ί‘‹RFΙ“fέab<ȘΔp[€Ci₯ψΥ$κφυΦˆ² q’ώύ6‡qn­yΒQ 7(σΫί%ΠC½YβFXfœώΌνr§9ήΜbΛRλ3Θ“PW@яχΉPH­V³ηΌrJUŽΝ‹λŸ7ψšp,£lk˜¨_ž*ϊ­›Oω†h}˜'²£yIk€|λͺΐNŸφΣώ-ΙνLŽ—Kφ‘‚R΄†Β}şuλaΑω s―‡Ά¦j―±Rξ8―αΩΔ–8μw_nΆoUΪm‰ΘNžf πγSξ`˜{*j­Ζsλ’ξώ,ΧW|Ζ©›¬ΊΈI†)iœσ"‹œ±flvXμ=Τ5S§Š]ρ»j}1—’wΜ,oPNœχ5‹b’š*―Ϊ ]*"KzΨKΎMš%)²’Φ‰u.ΐM°€¦­ηC‰IΈ.ύL½ΓD¨™b#Pε3pŒAϋμkζΛͺkS­EΝ]έu.½Ρz_|‚>ΗM`qΩXΠό>u"9ήΨ=zξζΪ³aΉ²΅ήzŸσκΑˆ ιŽs}±%ƒ΅pο^»‰5`,›φ’hΜΔ»νηΙoN~JλΥxdάΩε~;Bή εΈj wgTFΆCVc‰υΏlΫS•Ί§dϊ·,•i‰ίRΙΠΎTΑφΝs»IXωaΨψ-ΗsΓ¬“*:ΗEGΙ-t>™ήğΊJX"Τ[ss‘=d_SK• h’±Η§θ'υϊΑy~{‘ΖΔj2K`³ ÍeΧxlΚθΏTIρς™‘φ„&Σyήχ΅»ωΒʞZρԁύ•~€Ν’Ζη·ΈχαͺΈΕ ΚnUmV}B†WQŽ9MΟDτΗ`ŒΟΎτq²„μςn» ž/ΞΏ¬`§iτ$ΙTƒ₯ˆσχΝΧ’Kοr3έ¬k-=Ιό΄ξmψ‚ρxΏΦA]ΰ Hb`τ#τbΐ‘\ ^yΘ)D£νg­w³06|ύbυŒNm‹P`f&2E%§{ EΓ{S0δdΠμΣ3ι)FΣy‘Ιω!Pτ»Χ©μέ†ζmOΒτ/O&“οςιϋύh‚΄Ή@*-Ή.–>Μ͍Σ$ξlΙmKP§Yg5œžΏήίPCk-©Η¦ η*\Ό‰ΝZ&³£…τψΦ_˜΄Υ&₯’F›δLX§?±oΈ-X«œ=ρ¨8τ‚~ΤσΑτ­ρ8 ¨.ζεάμ±+λΩπ"υ=`¦YΞ΄ηߜ7W@αCe‚+Ÿ37qεγΌTΚΣκ΅w₯ͺ;?Fϊzϋδ0| λολξ‹/™|;‰ Α«υΝϋ™ωτܘϊ:Γύo›) ƒDsώ =μK-υaή鴨\ΕgWρ„Eα <ΥΛέΡo4Ž'ΡAuλ@ ŸΜUw[’₯Ž%R±Σ*γ·:Ζρf a¦‡£€ά±@4ΚωH«Φ₯φUφ΅¨#–O³›Ωv5*©₯Ξ“?$ΞhΦt½*ϊ’²όc‰’‘Ό¦ψ ˆΦ[ξO[ρ“νMγ―;/:׈/ŽΨŠ ύˍ+a΄κ“F•Ρʍόm8’λΌ5w8ΰπdSΏiMfTφν]!ΧI―ηX6›œκΖJθΒΣx#ό»DϊΚWGœjoL6Sssf#S"˜Έ ©Ž‘ R{:D’qYυdό•_’Σ(Ϋό0WλTμΤ6„εTΦv™A©ΈκΨaΈμ#I5ΦDφΠ1HΌ€_mʎ—AŒΖ‡ΰ°Ÿk߁w鄍XΑDΓg]τ?ΗK‹ endstream endobj 530 0 obj << /Length1 1144 /Length2 1528 /Length3 0 /Length 2250 /Filter /FlateDecode >> stream xΪuSy‚QΚaˆ"AXHρx\dϊƒDΔg€"ΐB+ϋ1+ΰ„πΔ|ˆΝ&όW­Y³φ‚#]ό…œAΔ†#t rήt&TΒ„A>Z4s:–Β’­gBΒιvŽPΘ#š™ρX4ΕL,Sš­B ]3Θiβτ̜!>Θ@››Ν[ŒΔΐq?ΐ,fΞ΄ΔρΜ6ΐP”twώ'…ΏalPΰ±ζXΦ£p+ƒc62@ΜgHά4Lƒ™Ϋγx`ΡΈp;Δџbœ€ BΎάχΏΔχ;E`B !@Ωθ5|SGa5»χ¦ ωΠV kŠΕβμτχu‚^(Ήβoα>΄H0σΪθξαξ²fnο_£Tƒ³Ά0ζ6xΤ)¨"o1W‘Bƒώ©ϋν°;ΜBΒlαθΔΎ ς¨ “ΓΎWςA„LΎ™d3Eύώp?5ΟwόO-47‡‹ˆΛιίdΆqν\xΣ½siόΒi‘Wό“sΑYŸw! 1Θ0›ϋuLΐΪ 2)Α™5Ζ,ΎfΞΌ8‚ ι7 `pxά.€1"`P @έ7C0sNΚυ0aB0  QΡψΜ―ΐ4Νρωθxf.=ϋeΟ‚ΠAp+ΘPΌ/AΆIαg“κ'ϐ΅c0ΟnY΄Xm˜ψ,Zn©Έ—+ϋΔt^³fD©Άερ·6‡r)mϋΚ`9o9ΩδLνυ{­c"Ž ¦ jκ»ΤζΉ₯i½0=gηCT~΅Π€σ5ΆEkΝμcϝΔWάFW”OΪ;έT£&#ρΊ“›€ƒQΎςΪzε‹|%«ΏΎί1βλΖύ͏(ίu½ΡΎ#%[Ιγ…ΫSΖ›”.Όx§^Ρ Ά₯[ꨂοJvUž}EΧ*΅&6ήΌdώžΣΰιΠ(Ϋ΄φdzΎ±tΜ¬]ιΣ£λ«»”ε5μΧνS^¬©Ω‘ˆχŠ΄½ΟX¨}€ϋDkΓm“λΚ6χ0dxΛ0ύtŒ~zli^Ύ¦ΘKΑυΡΐƒΙšvσΆž†{kŒ²'ικϊšΦ©#%‘Λϊξ„΄ύνΤάθωψI»LΥf=?xδτΡ$δΒΠ6wjΨε‚ΓVu–αr‘έhυu(Ύ±237kας‘<]iu4τM¨Ρ‚΅”ς‰ΦΈ­'©°" ^…υύ&Η?»Ήφψΐ’κS^PZo“#fΉχn=q-—ήž'ΒI©S£ 6Ζ‰g'v5+ΒΑ:+E-%F#/7σΛμ‚―Oω—$1ΧθwίΤι_θHϋ\έεΏδWΟ8™Ά·PξAΎ›λσξέ“¨ύ©ηγŠ»ϋΨ@BώžT9εΖ>2…§hZJ?UΪ7Η[qΛf€Ά*νLΠ&½\ΆϋκΊͺ#žo£ΥΞXlρ-‘Aςiχ‘Ϋh\•υρ―FΡΉw‘φ)Ό΅}Κ­ΤDΨ‘x5{bαΩΛ Ίœ2+ξ:£ M―Ά°κ%wδϋ:~ίuxιώΪe[Ψ€Ει=ΓjΧΜ*/ή§ ήš¨z:…¬žŸV]ωq[ΣeΕ"Y)ΉsaΪ@šύΈΤό&YΤδDtd[Ÿ–ψ«―­Σ~³L²wρp[Ν:e…MY1¦ušX|ΖΉΪͺ~9qluL,a$+άo”[{‘$έmš—r>[š4ψΙηα|½x~βέp7>QεiνŠΟΎ\ΓξΘXZρ¨T< 0ε‹\ž8Ψeλ@<2}ll’DωφΣU§ή­\™QΓ½=πD-£)p#1Νve©9Θk|Uίή\ά΄3)¨J)΄Ί}©AؾގΔWάχuˆΠ‰<τΪ―4kη©lΧi3[΅}!ςFW½Ώ7ύε=ΗΫ81μ&A¦ς―[Ή%ξϊE΅ Rˆ9e§tIΠΩηŠ“Šš%?Hdφ)‰―gΧ֍{}:Ζτ†dr—±ήˆ‚λζσ>~Όs’Ψ@žhReQ?©δέό βύ– {Α₯Μσϊ#Ώ†‡θn»q69WζxKλοKΤ‡‘Άnα7ϋr§κ²œΚp=ν*VψmI.xžΥu$ #c|₯œ?΅­M>―σΥ™e–†:ιΛοΘY©`Ž{Yt2φιCƒ eΊΝΊχiۍΤ{6iΑ8Uζ捞5 ΨKίΛΥΦ­^τ]παͺ‰Ϋ%΄«+Ν Ϊ©#‰υVEσ–\ξ~Eω₯"P•½kωμ£ΟΞ~%²lžχΈLΝs+Δ™yŽΥoj† UVΙHFŽͺ`εΨiέιΝΆϋΘσ8QΏοŠO ³½6²ͺk“κƒΥK½Z$ΦM‡ξžŒ αίτΰsS—ƒC]·‘Υσ χΧΔ…h„vφϊ~ΈBω1‰Jμa:Β™¬ΐœΌν`:Ψφ>°LcœKRΝaΏΊƒ-β–†4&€w(Ω[πˆnRη·ΧΛ(UΏγ’KΪΣ}γ5*ρςa㧬槐'RΚ4₯ο―>o R:`¬4€VΰρΜ·χχ(Φ2λθͺžrn­ηΗκx¨‰κεj¦ΟΧo \sšΏΜΝ“ςτTΦ…Š Ψ§P²΄ΣPΛΫhy£Ψ`#qRΓ£ΘvEjΖΑA fRπ[SiNΏσλuC%ώeΝ³Ny՝Υ©ΘsξαGƒ©έΕ9ήΔή·h{άΟάΆcΈdE>!₯Gm,)hiώ|κί-MŸθξ7ΒQ21dՈαœέΐ²Η±Γͺh½ήEΝm μ©’\hŽ endstream endobj 532 0 obj << /Length1 1626 /Length2 12094 /Length3 0 /Length 12925 /Filter /FlateDecode >> stream xΪ­yUTœέ–-$Έkp(ά‚»ww (άέ%Έ»» ξξ Έ»»½όιΣ§ΗΉ}_ϊφCρν%sΝ΅ζΪϋ₯Θ‰”ι…Ll€βΆ6NτΜ Lά9΅‘³£’­΅œ-— ½,Πψ°³Γ‘“‹8 @Ά6’†N@n€:Π 4°°˜ΉΈΈΰΘ"Άvξ 3s'•ͺ’:5--έΏ,…ŒάιωΘt™Ω(>>\€VΆvΦ@§ˆq’2p2LAV@€ˆΌ‚¦”œ€JBN ΄:Zœ¬@Ɛ1ΠΖH 0΅uXύγ0Ά΅1ύ՚#Γ–#ΐΰh4}€݌vΉθv@k£γΗ7δ0s0΄qϊ˜“-dclεlς»©νί„μl?"¬?|` ΆŽNŽΖ ;'ΐGUQρπt27tϊ«Ά#θΓ °5ύˆ4±5vώ«₯Ώ}0^'C#ΐ θζτW-# ΐδhgeθώQϋΜΞτ7 gGΩΏΠ€f†&V@GΗ˜μΏ¦σ―>₯{C;;+χΏ³mŽϊO 'G •)3ΛGMc§Ϊf 8ΖΏvEΚΖΤΐΜτ»‰³έ?}.@‡ΏDυΧΞP04±΅±r˜Mαεl>J¨ώg*3όο‰όΏ ρŠΐ+ςώ‰ϋού—Kό{ŸZάΩΚJΞΠϊcώρΖ>CΐΗ;όυΠ8[_)†Φ +χWΏG«ΑVΨΦΚδί}RN†#²1ϋ…‰ιF£8Θ h’r26˜Z}Μλo»ͺ ΠΑ dόΠυο‘θ™™˜ώΝ§b2Ά΄ωKφΈ€6&NCͺΏΙ3Κ*Θ ‰‰Ρώ7λ߁ Kΰ€βnχΑν?Z‘΅5ωΟΓ_0ΒΒΆnOz毜zV6揻χAˆλ+“χSςo ζe @n폾™˜ξώ?~:ιώŒ˜±­Ι_k£μdhcς±iiψΛmμμΰπ!πί—£λžήy Π h ·4okΜd‘š‘ζTƒ•38.ͺέΫΝόy0Ψδ§JaΎ_•m—ojθ&W…ΑKu0Cύ$χ[‹ϋά±έλξ7š½αξ/V”]Ιΐσ<|oRκž|Τ5Š6Ϊ½F½Δ΄υHΟ‹Y™ ­―Lj{[γŠJzΕ/P“m¬0Τ~€.ω~dχvH>Ζ)u1˜ν(υ`h5Η' ‡χ”ύ#Cƒ]W=»x΄Ω1°δ<†X>IΗΔ‰Nξ·?ί Ÿ\8μΐTGœν΄Ύ-‰0Δ™4΄¦ΛšVΡψf0φΡVj0lΝUWέίXΛ&©θF [ί‹*:WΞΤJΨC4υχΟ€ύŒM'.ƒ|Rν~ˆjp‚ΩΏ‡›β`bg¬όΖϊ£¦™*Μ¬=μŒž’Tڍ-vΔƒ…=ιW‘­‘»ά ΏQϊ^°γΆηœ[ψ;Χ„fΊΗβ₯0²Τr‘F9/ΜQ”X}Ϋ‚3φ₯oHj6·L.Χ²Υεϊ°ΡPκη\4(Α[žΈxκθΚM#n¬α’{’‚—υnϊͺ©Γe Xw8¬IΠςΌχ…@†±?ΰψ³EΣ2΄gDoβΘ"ΐ –ύ;¦g1>‚r[DE»1.³v€6' Z‚ž½tΤWCVίΪ—lπ-EΑPJjξYJκα,C¦DΣ³,›ά ―Δ~jf½^ή† 3όtί’‘G°Ωλ«― ωmύs?gτ˜Μ0•3|ιI±/q΄‚3J‚ž IV½3X½΅ΨT«©χi]Iχ¦ώš@žX‹ίχβ&’ΈŒΆRΨ>³Wzαύ"^6€2ϊ‰βJλΚ₯žΫ‹5…Ωκ†—½ΨlKm@|VΚκoΗ'@Sγπ°ŒK&Ψά%¬ΌΜxδ˜mWβΫI|'ΒεΟz¦ γ· ›nk³– )Y¬ΝϊͺύTχy>αυ}§»KοΛF‘‰w½ΑΝ$―Z Σηχs§o“%{ΆπeόέΠ‹~—nGhw!₯FKψ »΅ mΥn“`-‘²ά€ff΄˜t=ζ?[b!ΐ ηHَ~Ϋ,½kΌ²ςvgΖc ΖΨK~Σ—αοΨΎ¨–ŽJ?5γwQίΎ’„Ώψ fF” ›΄ΦFΦȎ´)τΓjη•`\€G΅γ,ΑΌ>ζΎΝ§νn‘ΖΖ••©K‘hŸΛG]αχ[tΥ}——p­‰x;ATόΓύΙ’§SB₯Š‹:•§(M Y70u±›3£f˜p$–μ¨έ^¦…·j³αΔ~³φl³\ UΕZ†?¨n™œ"ζcλΞFΐο½I€πηη<*WU­λp<ˆ<Ξ—sγ5bξ@-ΉjH«»| •Ι3O|8ƒβQ„S;f°e\>Ω'˜=:C罝aε:›‹g£_< O’Έ72ρSnVhsςNΐΧόσ·CJ€[¬±Χ¦L>δ94\₯EvPθε(‘ΥŠΦs_oΝ~0„ΎT»ηα7ςΡπM+νΜYN˜νD³ƒž*ΤkΣ"}ΎιψIαΘ£yρΥ {–XEx†KHΘΉ`Ѝ2YD 8Έ/T!›υ―q.!Š3ۜgΞΎΆ?ƒύ_τ‚g…ηο'ݎΡP W€WSΒ#vm4°.6θβb {ΫpπI5 ‘% `½¨΅N˜¬8ŠiδΦ'Uήz²Μ‚ύ ’{ov/μ ŸJͺS«7œΑΨ3€’Ϊ‰Έ8 ”+Ÿ`RΆ£ {·Ώυsp> ƒ³oCυ€URγΟ^n²Šκ}? Ζχ”5.ήM?{6TιLΪmSξn«(•΄ŠΒν2ότQ’&&g‚΅]ΟΈt«'š•‘%ͺψω΅γ'ΊjϋLώΛύϊυ@g·vtψ±ιϋgg\Y‡Ό€[gsδϋr8μώdΐ[-XPKζR΄¬˜\Ikgi__TuθŒ‰γΒ0žbVΌ}jNΔ΅F‚•£Ύ«qŸš-7m=l9Όή!{Ν“ΎƒΈ\`Λ™χY@»4;‘pΦM§#νΤΏ‚ΜςΐZKVΒΙ‘xϊΆΫΦΤώ²ΰ"©¨ΰη„½ΰΘηψ‡ηRξ ω=0S—J*Tς“1Γ«yˆc LιYκs$kΚ†… {ϋ#eJ‰?ή&kΟΜςΨiuΧE46$0΅Η†~Bω?|gΝ>ήυιAφ|]'ΆΑgqΗHΏ"ΓdI‘™ΣΛF2§Ζίr¬ήζhΊož θξλIy'{ ^ξΡvŒΒ}•Ϊžψδh$ΌŒxxmΟ@›#r@ό56Ζ±χϋΠΦ¨§›Κ|-Υ¨›9³»l ΚAΜ“VάQΙažζֈ)+_1k†.’PŸhςQpϋIοθή(Έϋj­€Εs±4Ξ£ΔψΣρ›ΈΒ1ƒH@r—L±Γ›τ΅Ρϊ:=Εδ―γ5`&#‡ηbΜIΘτ†ν™˜=ΌYϊŒ°σ‹θΣ%₯2U;―T-^Fμν'oXom§ΝMkš Y}Λ―θ<9OŸL1ˆδ Ώςlf!ωωΨZ#Ηe)Δ^^£σ9K})œ'>–lj‘kxϊ™ρY–†]ws#^…ιdΕδ:oλr_ξφ Γ,υφ1«ϊόZ9o.ΚZ>)τܭ駉y]’ή;VυυβM,œό<„ά†Υύ&ψuιEbcY;fϊͺ_ D`―NΏG…kM‚ ±?8}ΣπY\¬t΄ΐoπΥ•ΔRe΅t ΓϊΣ=ΦρΠΔΈ-~’³‘z1ϊ.aKQ]9ΕηQMνϋΟΊ<ίΧτ=ɘΠh‡ίΩΕα9βQΫRB<ΰ|°7Φ7ΰ½νA§gJ8U_ΖW₯±μOXw}.1\,˜7MŽKІZZΣΒΉάΪ}‹T ΐ=vZ²?oaε !­­ιΎ¨γ]ϊ…YνIδ“3ΟaΔΑΏ„ϋ~V<Π‹πΐ"αOΕ %†·¦pΑΠ4k°@aV‘ΩFσŒ5ΜΒ,—Τ¦G‘£2_“ΎaϋΉ $³›} c„`Μ«2<₯M•Έzί²IsΓΉΚ[΅Ž3œ|ΨσφΞωKD-Fγώ;&œEδιR%<>ωιξjš62P’Ω°\«ν‘χθ'œ§‹/Ztυ‹•ΘIΘ³ŸΛ‘έHφDύ:€΅=$ΑM&΄>²Z^₯$£―3z€°ά«’ωtπωlΩ½†vOq·{y‹r]λu…ώζpΎF›9~ΗΊxΤy+Ÿθy`Œϊ6¬ΰΊ±J86ςΝ>Gˆ…yFš"ΖΙ€μυ‡ή~Mxa:/ΐ7ΟNNχe8³<Š’fθQ¬ΌΰΞNρώηό–šΕ!Ό»Υ77βώ(χβ}Χ3En]`*¨΅Tο7MώλΤΪeavσeƒagC ?ΔS1q“VDšεX^-;ϋΤ§‡Nj4)@nnΚλ}ƒ$wYl‚ϋOϋβŽxwƒΜ.˜G₯σWΙΖγ‘α(ύj° -tswΖR½ε5°Š¨šAΆίΈΔ»3ΨwξKΤ«™ύgΕ8ΈŠ¬`Η©Ž‰ρ#„Ξ›6€λ€ζώ-ϊœμ”½•r*{S½ :„P$N•§ ΄†Α°•,ΗΦΘLX+°Π² qΡx³”ξw.cγΊ*%§ωί yο _δΏ ±a’ΟΤΘ_ΡbΎδ―]gU3¦wˆŽΆ\dD"ψθΓ:I$€FoγŒ‚ωΎzγF2³άά Λ1fœg άΐ“γͺq–ͺ4jŽ †’~Žγsmς=c*£EΆΠΞμ/FJJ5όa―€«‰ι {1π%…Ž:SŸ1·{ι]¬qςTFΫψ[΍« 1»Μ’­9Ξ ¦~„Ρ –ο··λœ‡ψE쓆gtDGlΖίH αG‡QK…λυθ”„ Ix­'ƒ;ΥhŒŠ’^C-Ί\s΄0ΏΌNgϋTMΐϊ—ΎzNT½•PŸ θΛm˜Βƒƒ\vF:„a]πμίˆΧiΐ<Mۊ{Ё<Θξ 1ˆ ΐθλxΫiŽ'8W@*'ιώ<όΖZq³t{0.\D|ft…V±>ξ» ₯ΠΥ>­δ¦Dš½$ 5λΟX_Γ??ζ Βw·Ι-‰¦ψA½ΉƒS K\ζ^ _VB©d¨+¦ΉήŠ­Ny€1,\³j]·'?ڎγQΒK)³δΥ#ΪΙ[—p™(”m» 4Εj—4Ψί0o·nX›+Σ«’$ν@œλ˜twε„Ÿ€m›w*χ*ͺ₯mvH͞­γ μ^,Y덞'ω‚–ΗdΎ(‚ >¦Ζ…ε?­Ί~ˆDΫ^8c9’#ήΑ»Λ!φE°[t§ n6‰Χ“ΓE΄!!Ζ¬½«λ#n»zŠr₯‰Tλ^·ταΦΖσiI…'…$P?ΰu"$ϊŽΎmΑΡωλ²€ΙN²΅Ÿ—3|ξ">Κ…1 HͺΉuM!€B…F]uέήΝχ3| =Υs(ίsκ:ΨBΌ §‘Τ©‹9κΟt0…HŽ₯΄u½ιΆχS D‰R—J…©’ρ™Y—ηCΕ$³04KkπνΚ\ibΟςψτL%tφ³}ν‚BgSόγ»Ή†ΗZΗoœcJ’}#Šα¦©…_βφ2zΧϋόΌeσD 85nο8_ω¬4B©ω››~ψ€%¨ΰς‡(Έ”­έKˆ>‹_z6nRΎ$xά(€JNπ2ξ&D;QMŒ#¬“Θ’Χέ¬^h ½ήdm΄]λ9M„3“ζ_’ϊSΔ,ΑwU•:ΛίΎώΞxdΠΖIζ‹))cΒΔVv^Χq˜!$ια!£).#2:’Mj pάkζhpykη!|ςm‡cπP'ˆC­“…,ΞωAΎΓŠsμύkρπ»?)iτ%– ΈΪΕ#ž¨Ωo$άΨ3[WξTζυρxIBίs29Ί~₯ιΨΪ%5ΗΟV.#Î&ΙIΕεΓSΧ4pΔαΈϋ +p/Φ¦O’/wbΪ΅ˆέ"£<Ζ„GςƒΘ]ž»β(pΨΌ°θ Ϋnν5&zπύκζ-t[xΥΚ힍ω € +%φ}%<½ρ«ε¦Rϋf#asAGsφWO‹_{“~Ά$8˜ 3>¨Œ!9ΧeΖjDεz£=„ήN,φσ#LI1 χ*bΎ―`πU–ϋύ4€U₯ΈD$(πk&Π]†…Ρ‚XΊq¦‡ωΦ· ηO«& --ΌΛΟԜuΒ&48)H‹΅ΫΌΚψi1L·ΧŸ]vQίΑYκΜζ₯-Τ;ŠW~ΤΣ\§₯ν{Zph~PЌ Ε‚δK‡Zΐ”β]™~–›ήΕΧΨQ§κKvLή½ch·±κ )dZ”–uo•Œ‘ƒΛW Υ+όΆβ‰_ς Λxx―!)η'ΤA–ΦΝ²SCΧ·†ι![–}ˆjΉu0Λ* ΟΝΰrBΜ^ ^+”Η᳇KXϋt©5‚!ώSυ(όmρΰXžλk†Ϋ‘G£έΘ„Ή[E›ΨZφU₯oΖΕRR%f’όŒ6ξΌΜ'wΓiΐκ91†O P΅xƒξΞωΛζ]•J(Ÿ³ 5£'Υ& rϋ;³žΔ„ψjΫϋ–c΄ψpdP‚θcg-;b<“JZ'/ο²<ΎΟγΌ!·yeκ₯vpΦΚϋmTόμR‹ΗPR₯€œžΐ~xΣ/FŽ‘ά6wun祖±D8Θ8)σΨυ―δ59τŽ5 FgPΜΡΙTψ.ƒ’½€†^ΉνπώυUΌ|Π¨ρ¬B7^^3dΌ™‡m_κό GGSυœ€ΎάΈ0m‹ρϊ»[νΔ ZGΖ<»D>Έc7Ρaύt/’ve=?K΄ι0’·Ζ}%ϋO±ϊο5ΒIίWLQ^½οp ,E6:κj£η©ΤΔ«k¦I5 β†ΰn’;€C²””ςZ^ήΨΔE₯@θ™Ασ)mΏ·™9T>ΧnΣ΄="iyΐΤ–―'ϋI%$Coμυa±“;Ž€»>ΉΦ°Ϋζz-¨@Gfπ΅|W„q($ Ϊ^―Λ§Χσ5gI":”s&ž%ͺΌ‰IaYΙu_Μ«2)8μΗ=—β9.ώ:+bνσN6Ž•Ξζoˆ/bε†ψrVE4fΧ­}=)Ψqί¬2+Ω@twœkŸnΈ!©» ¦œP Αp C7¨H&υδߜY:Œ₯F)gšς@ΐα<\¨¦WΉ?`0κ.oUψυΫ|·¬”skϊΜ„Rˆμ© EΖ?€½Ύͺ±‰‘Iά‘ŽM@ιΒ%8₯³Φ5SΟί]9Pΐj|2Ό’›ŠžH6υE=«τΖmοΆ ,18aΙj@ύ³J€ŒTA3Ι><Φ@ΞγΠΓ[―†ωFϊ²ViΞo·ΨΦc”Ušl΄'¬ ρΐ«1}„~FπJ 8*ooΈŒΓη.!™Ν>ΡϊLi²n©œZCτί,}[;»•Έ΄πW-“ωXκ(EΚh8<Θ/Β(·bΓ"πύN+" u.Ψ¬ ‘tOΊ`Φv΅E΄qΟό#γτ„.δ3†Vδφ΅1Ο–ςž8μ½ρQa‘.Ν)>Ω_yG:λΥki\Bͺ΅¦ξdf%hΏO-ΥίυΔοLrΩΤδGmE‚¨Σ—L˜~;!±ΐεž$Η…l_:t„Δw|»aL‘IˆΊ’[α ξ[]ΩIΞε”)ΐDσLt%«Ξi 9λθšθά,ΐώι…Ι€\GΛƒN.ωόπ ΊY …~&S5ηαθ±±αΑ„δέjάφη\“.ιoM―7…m'&h8DVBY5zνά‰†―λ aΌ7όˆζ½ΫΊ;»dxΙAˆη±|Φ•Hδ:WεbŠΨ—„§ δZΔJΛ\κjQϊ«[¦f@%C³Ÿ$y"›qbœŽΐΡkL©x]¨¬PθΙSŸ@:ς«6Υη±ΜέBΒrη˜ύ:†"žRΛÝYλ6yΎE£-Όmw‘–„ z ΫΊŽ©©—ι υκδN=&wΓ[MxΈmcV3C:¨&\nΊcω{J­nα1¦ ξΊΑ2\Ρkβ r9#a‡θ‹~,½ ΨA»+ςQD@H~Hώλ7“ϊD`€άL€…FbBΘ’.OeΊˆέΨΈ9ϊ]Λc‘ΘZ=ϋ9«άGLˆΔ’zŒWφξήΈϊΕ1j>αάCΟu.Q©mΉΒΉ,N΅€l«2 #VμΑΫV1TΉλ>7œ;’„Ž”έ©ΥWΘ“tf¨Α€Ν₯‚6F;žFΈ6Α9ιg'—RΡ€?]()οV»kJ‚hGΠwΪ(MˆT¬Θ$Β:°Ώ₯» °Ώ\/(0Δ½Ί )ο}ΧμšYHl‰]ΕxσΟ‘άq¦¬«3;ͺ AhNΒηφ–w LΒqAΈ›·ςb…“JΉ—MsSΠ„-Ύ ^ΒR ΎΪθ[½νβτYf.ΰΎΔ£ŒKΪ•cύ…τν&β€›΄€2yb4£₯t΅€rȏX΅ Ηάi”~Ό°* +ˆVΉΗØ·Ξ’πν9Ϋ䡁—'π~¦„56XΛ>y«Χx ˆŠ‰έUΫ,άNB3;%£εbΰΉˆM,¨ΤΗΨ]\Έ¨πΠkbΠ §¬ώλζΣέw« Ό?υο@ΟN4]W‰ ]ΒΚε°=fχ‰°Ž g@ΜΑfΒύπEΑψO<γuzM€{£Υσ«#o}JTU'„>&8J ΖλGŠ-%ϊπ΅Β§`eΞ•[AθΨξˆΩϋ°L}gΥ((ΰϋrΏεLJ^sftΚ3Ρ²Ι ˜΅₯wμyNΗXγ`TCο@&JξΌσA7,p|&4βκnσέ±αƒtE™Oά‡jξ)C —/x'₯εϋύ}ݟy„+4ΰ3Ub7±%_V–lΉ€™ϋφπ[„XT­Y@›8qγ֚‘jκ|Θ'4lfΕ• ¨Υ`RΠIƒσ“8+<Μ쉋IlMj³+_ώΎ•Ζ,_ol€Hθ °ͺ*πΐ˜ΝqJx«€λŽƒ q΄<©ϋU†Ι@šyί’mΤσ½ό8ψΧtIŠχ , .Α8MΪΥ(\b[tλVͺ2Ε–υ—M=†μfzΈχb­8™ξ‚ψγA]•±Œ=‘* εb‘zdT­ϋΐsV|^ζތq’‘•7½Ω™Vrcό„S!MΫΣ± ώοf{iEΫΌ½m’"‘Nxξ*–Ά £ΘNuηOψ©„5ht옴q•aLFŠX SC€-Φ]!ΏO1λ*³θΞ’[ΨέiσVΦΊο•BP,Gbεα»6ߊ 'Oψ;ξ\ΑΓϊ|A ;}ϊ$:ύ±2 w«‹iΜ’?μΔRQ‰ΌΤ¨ƒί[ξ}κ·M«+叭eΠ³·΄p‡uœΤ6πˆ”Ή‹€a3°1Φε#sάΓ όXχ/p™Χ„΅Ιg—Tf‹"¦JFm—a7£”Ϊη•IršνŒ…›Β [οΪφ"!ΉzšV§`ζhp`Σ2WfmΪpc²έCΗ5N)κƒ |c,ΠuF‘βή ω5bȚΊι€,5ΉDύΰš£x›FtΘ2f¦3ηθΤΪ:[€W›=`jζ琱δ‡εeΓρŽ΅ΘίDσDt?ΣHXŠoυm*Γκ€F.ΉGΐ{ήΙTΙ'X`8-0ι D?ΛΊ%&’Q―n7½b‰ )‚F\uζ—²α£θ§ˆδ«θ0<4ΏfLΒ’6χΠ@c ˜‡ϊ@ηKE5©¬›?u©tv%MͺHZύM„ήHiΊ]3B ƒN…#TξΦkz°…“ί«ŠΝΙ‘‡ψεβ7€™ξ&”ώ$ρI>w`EΠΒ.ϋٞ`”A[†:άsΚΫ―Ξ#S­ƒzΡIΊτΦ_Υ‹€HL]γtœ>ΘCΈάBvfk‚E2†·ξΤ₯†Yζί(Ά€|S˜¨kJ§ ζβœεhύ¦…ό*:¨ΤSΔEGΘJώO―-"άΧ„QA1Ψ5”#<Ϋm}άΗfš¦iiζ.ςe"Z·\χπ˜Δ~}f2§ρ.ΘΰNfΒe^Kδf@ž©O ςλΦ‘YQ•™θ}b1ψΛ¨+τί$S ‡UUœ}Y†βFcύvIΓΔ§XzSyŒ\*_8’V OGςŸš£ΒςΰΟt8Ώφ!R¬-»ΛΆϋ>~Η‘°š¬»ΟKΜ6Mδ!­k υNa|εΘύΕ[½E|Έ:ybLˆ6hΫΡζ ΰωo6ͺ½¬‘¨ΜωI(θhΛtΏˆχ©Iίyœͺˆ-π³"ψŸγ4Œ6ΒϊJΥ sO_4)ΐ­αχ©ϋTΗμVŽΤyε ‹όΒ,Τ…o…ΰ₯HŸxBIΘ{Šy~ΔOr=D΄ςpEIŸ­Γw⻈O”~sΉΟχ§ΉΤλ/2‘»N½rΦκSb\ξ@ŽίχbΨwe vεiΎ‚>Πa™Aܟ}Ί³ΨYV¨―ΛCΗΛ³$ΜΤνt{Ÿ½ιΎ/ηs€έΪφM}ˆ }dcTψ=EŸζ5ŽΉC>©Cί:7βAKF?Ž?ˆΔc`aΤP؞Άy|Εησj'κO±Ν\™ϊ*ΦW1Πΐ~aSM6ύC„t@ΏέI7k‰e~Όρ­ΈΥΞ»*¨ε[‘'+—ίώ;™έdt/Η€j…>ΆύΤdβϋuέφZβλ"θgVlδ€ς†©œκ>²Ωψ@Z„¦kΜ£ψed¨¬Ρœ7g@δS₯_c›Ν+ΔC=Nο-χ·bη'˜/ΙnΪWΊ.wbn¦H~LeL‡œ‹]?π`γŒ^^4Φ{LoGήO™p†|μngξnΕMΊαmΨΎ†=Ή©"dQ}*DϘ€žra²ρή()_‡T•¬ξΈΗGɍDπ±θWμXυv€yδ₯NΊΞ;ρ¨"³§GζkŽρ‡ ‘zΟΩαkuιS[Ό‚ eε ,knHaΩcWΌ‘~B9λί*PbXo‘|!S䀏Y ΦNά£D=₯Žψaeζ5Σ΄Κ;βj•Ι¦`$ΆHGτZŸNΏ²ύ,W‘Œ΅<Χh,‡ήB™ {–NŒ3YP’₯ΞSγ £ΐdF˜+€Dm~·?P­—f½r" _-ΑοΧχGτ/Ά²~>έIŒ›“Φ§uqσνWe+ΣΟΖDΌι{^ωL\ [’ΘΙcJ8}Ώ‚xχ§5"£Κr,±ͺyΒn&+u 1‰‹kζŸtŒB.ΐ†˜ΰ8Ίεΰς ΣωWfΙ!γsΣaw5ΗnΕ?9₯8 +’‰f#ποŒ'όΟpζžωu΅°ϋ½z¬u΅ /ΦΦo΄3‘–Ό.Si†½kŸXr¨²\fτΛŠœΡ@ˆτdGΏBΝ€¬ύ„umΉ4ΈωZXy,NΞ ΆBπ†VΚσxΫBDYsϊΒhpŒκ9q€^―ΈΗλΨ“2c1 Ÿ.ιω=™ΧΨθ†ζd„­ω• !Šj^ύrθ>ΩemvkβΫq“6•?΄+ˆ )„qko,΅W1lΧZžΪΛΚΎ› όcΐαHπψ£yΚ―ΔYΑb_Τ€7™BΌTUΊώDγΰkΰgc)0ΛφΊΕ’q±—;,ΗεΚΗri3wγ1n]ϋm bJδUΔΆ+Eζ‘7Υ]v©#'F­Θd•οό*/›\έ_ΡUΪFBγoegΒφ~amΝƒ7άι.œ@\ϊΗ†(@ύΚ4GΫI¦Ψwιz”πΠ ?β8“JΗξ>@rΒwά!›Xξbμ₯«%Π;2:ΧάμMx΄• β”,€ΞY£Eγ\ - 5Ωyaxr‹\g¬,₯ΨT†DZ¨MΕί­·₯i~$[Θ΄¬’‰$‚ qΪv ‚˜ LΉ g1F ή\x°Μ,ΪΒ%σήmqΆe`-#Ωe$.[yΨLd³Ψ^+ί+|χΣΫ^ΪrΦ¬ΐI"Ԛ{ω–ΗΩ υϋ$Σ…pΌ?i»›ρμ―Εiv†υu»‘ +’Ϋ^y•a=ΟηaπŠ:kφΟƒ•δ*lIοΨaJε€Όά4ψψSUm{f ωυβ„YˆΉΎ£°ŸΤ.jTυ<ͺS­) }”ŒFϊ>‰@‡$·(:ΪζMj"’›ςή-² yB’ɍ ξ*6bΆL©θ 9’λύέ{ž!Y'‘q;Ο Ν™υ¦Α“C_₯ ΣΔ5ΠλΞ1·—λχ»₯«ΘΈΔž;K`»ewdΤZƒ±§ϋΙ81fheώ>πίΔqw5™ι**ΆTίLrz4‘0_±•±—T%†—’’ΡΪέΥβ-Έ3|ΔA4LˆDυ‘xJ?RΑϋΜiΨκ%’^/UΧΎζβΜ)‡Ÿƒ†–Ϊ)1š*ΥΚεήφζ#O>ƒΝΰ*ΞIfd“©“β{HΝε$ώήF,·%U+Έ` Oj&%crαŒ. ZC73‰&Ο&\0– yΚ‘4l&™cΞX«}§½(’Υ I½³θτ Υμ“€Ο±WΚΖ\QR\šΚl¬(ΝΝΣκωJψmFΏ.fn]ƒ Ÿ? w qZ!{LAψ~Z ΈœSίήΚν^ΒT§[š!ω)|nŒΡ“ί~OΛOΑΆϊSRΎ)θΰΆT” ·^qŸϊςΘ₯Ζƒlι²hIν>CšA;sρ]Υψ'ΚcFΤ’1χ³ί{dxLΩΑDʝ(Žg)=J3Cη³~Ή”1žηΖ/ƒό«-„*σσΛξœ uΊφtΦw)ˆuωJˊ.=₯Ύ§Ζ4q¬ °Ω€?»Β8η3±a Ο0΅#2LOWDΞ4_#‘`a½X Ώ3n’YsQςZ+Σ»'έΝH6 ΙIq wΆ|οrc}G1‡}Ι‹°ΰ†'j˜„hcΡ£ Κή—o݁ϊd4ΧψσΉέŒ¬kᰎŸτu5ς>8Ιڊ¬qω­ˆ‹J²πr\GΩξ‘1ό±ά­QWmO^œν.cκα{2§_11: ‹7{½εAlKκK`Ύ?‹ ΘeΧaž -¨;ΆoΔq3\΅E:Δ³ΖJήΝT5QžέΘό¨ΕkΞ…^ϋ³6(”^o ˚ɢ!―εGύμ„ε3ΥξΦ₯ςRήΧξζέI<%ΆsΙΧΐ½[–Μίγ@¦Ή:¦?λ€Bωήώ{UPϋ΄šοͺΨ€pƒΑ:ͺϊΗοΈΘ>­Μ]­‡zΓη‘0ΏΌ)FΫ)Ϋ ΓΆϊιΛ¨8©6+‚†—ΡζŒ1Ώ$Š ^’*Μ*&JAΤO«˜SΏ~§ ˆ!½Κ³YΏΦνCŒMΊοΗ«=ώΩ;Ζ₯kIΚ"όz‚‘2ί.ΓΕsc™₯θ-\i[JΝ’ηύ”€έΪ8A‡iωX5Tί=­xSF‰—ϊ6ΜCΜΚΊΌ―AΦΌJBφΩ8©βV•2±V­₯>Λ«ΣΡ‘’ΰG|‹DJωΉ­ΌVε©a`’Ζ7†šwμ”UmμΦkWp“„… Μ‚^<χ¬κ[oΜ[}R|Ο0~ύξΕ¬ΏLd„ ΠΪν%VΏ³D_? σ ™‹{T„0ŽB.bΜ !Μ ϋ§$=EΏΏΏ5ŠΪ‹(mRvθ„κE~ΣO=–πω|―…‰ΚxίΥ@‘Φ,ΓyΛeœΨ uw‹}n=Ψ}9nicψ‘*#ΕΫ•Ϊ˜Ύ”γ/€š"jS+“ΉΧHΗ―W/œ£™@ΣIω>Zόšψƒ_η΅‘ν'πΩF3#Ώ!θ•6Ζ^Ήω!Μw8Μ|εM}ΛώχWšM°WNΔΝi‰κH3έB ±ΉgQy9°dΕρΖΆv€Ί³w΄₯›σβp£μΧΐWΣΑΫF¬Ue‘‡²Φ|5dΧNΓGfΗό;ήΩυ=ΘΉ«π…σ'n²!·έyπMΖ΄‚}ρŒB¨>T6)Ϋψ8ߟr„ %­ϋώΒ©υC‘ ‘Ÿιpͺσ2“E±•ζHa2q"lSxjXΎ£jH_˜mΈͺ§Ψhp TηΖ7σ%Χήϊ$σ„~f ’Ύ ΧΝ"ε―6]Cl€Κ¦˜#άOt¦c[°‹½όΞΒtΥ8:iq8ιρ±Ψ²>¨NtGυΫ*΅‡xE ‘Ζ Οvιτυ+-Η¬tΪε–Β™NYM1E kͺηh(ά숊v ο<©Ϋͺœ +ώ‚Jκζ/^ΙDFˆϋ{_UοΌ@›JαχDΏr)xœ~£ž[xuJ,τ`?oΈetvi ‹%ιžσQ&R"bΖXΦ옧„»ϊΑHa !ƒη9{·ω ¦}ΩΡΙnSτLcl’4•†_’I<>„t–LητŸ *¨γ5A°_H±/vΔ{k9¦,™ΊΗψ?―ͺυ±Γ4P„?Qͺρ/Šy;$5ψ•ˆbUŽΘž!_šΟ’τ†ΐTIΤΔύΔΒ₯·\Υ:a˜Ω0„o7j(ί$΄ ©΅ϊ€˜dwΧ‚†ρvΖ„'―δ£ίiι;ΞOOΫYK’‚QΏΝŒΕ“VΠθαN³Q¦`Μe°¨΄SeΖΛΤp©ΐMψb¦°Ό ΖΞn 'ρ›¦(OΪ ¨δr7haΧΥ’ΙΧΪ~ ΌͺόεžΝ·§ižΝ’{€w ϊ(‘<©OB!θΰ,­š00νD›yFΏKΥλ{•Δ―£2Π‰vμVbY7š>{’dfΓW²ŠdXԈώι€WΆΓGƒ―„εΗVψRΊχžšριhΊ_Ύή Ήš[Œcϊϋmv ϋπ …ΏΝύδ=θ₯α/!C±t&Δ"[žͺ^ZήpˆΕ.1ΚΧ‡XΰKlqv­Έ@<›Ρ8‚ ΙΕΛSΞ%I4ρΥƒ§8*›][Φ›ͺ m€Σm[n‡MHΑD―bΛϊ=ƒn:•½lΤΙ›Ήά΅Ό]>εLTλΥbΌζ˜ω3ή’S"ύ%t jδ­h’Ea[]lσ2ώχ¬_‚θ΄Šbψƒr€‹ 6Y=―a²( sψX–l¦EŠΑιj; ΗΪ.̚Ÿ{Tχo¬­=Ω £hFΕάσG―©W|ΙXϋ΅0£³ƒΘ“=ΦψE’©ΓžΓήςεhΫΘ_€i΅ΪΊa*T(λ{ΑΒ ΛΰΫήθΩ€μˆ™³9de \²ιΞ§ehέΌSΦπΉ+’ς'ηq“YΞ₯Pσš(,Σάζ*H³R‹ί½&.;9Š}ωΉ(·ˆ7”(O±–¬‘˜PŸq˜θΪ47„ΟΎΕm]«‹?αo'ΧΣAΕFiΤWγbΪα]I”ΎΔ Fp_ΙΌ»Ί½F9*cΚiΊΒ_}A―€qtΤ<š} K₯f(°h²ΣLΘΝςϋ\ m~lΆa Ω Mν·Ϋ–F’6wr xbZe"“`8M|b€‰σL) @‰JΕρ|‡UΈ4lτ>%aΣ!‰ @ͺZΩϊIώ3ΆΡ/v‘ˆv9‚Žgύ!Rr3ωŠΡ΅jΔ:.oμΌfι"lΪ’6¦mQ§[!`<ςζ"¨i…“Σμ~όmπ‡.:\db;Ÿοxςδι φ“Θ΄;…—©βDv&•Κn‘’6nLζœίB₯iΎT\φoφ^σ ›ΉΒ+ZΝPӝ0³gψ깘ƒ]^’7•‚Y±7Οuβ³BˆΚ?WΔΖ;οΨεW#Ρ•duλŒ*sQƒφ\+(9‡ ͺ€Ηul@·]LeqΆ`75ΉC³$΄βtωΣ οIχœeSŒsπΜMΦ₯ %순Ζ1p"<Ξ·‚φKχ²–¨bϊ­5Θ‰Ω‘ΉAζ§§ττ=»Ι|w>κx𔀂o?š°ΰΠ²Ιuͺk”$W€QΩ}ί{ [!IΦ€Mx=Α\jc6χ΅L•ΓΤ GΉeχηωW#’χΡΦΏG/Ρε³ ΅*ΐy^zƒ·'ubθˆΩN9ΖG€ΰ\ΟΉ‚ϊΧJ(%F6»΅X͘R—€bMlͺ3 ¨<«#yΜ±ΜχΏ]QQά‘³’³H.fkςVκ‘BΥ}ΨΆJθc­5F/|r5rόKΣΛmy!TQά§˜Ί‘^;nύŽy6Nbό’~σ~­QTβλ]έ![ρ*‚LάlΔpΒ6_φP’n™kP[Η·Ξ1 †€ μ:Uω|}6<{A}•»ΤiK²pjΨN§°>)‘Έ,njCΖ^p`X:(84ΪΦ―3CΛμ·My ‘―~m«n‰.λtΕ«νwΟΠ‰i„λ9SI<δ.Šs„¬+DV8Όβˆ± δ$₯ν°α~"©ςΔBw܈Π]ͺ gk―‰n­W0ΑοΡ›γ:0Qσ\:4Ί|…‘˜¬ƒ% ²Σ~¨zV[OMΝά$'vχ€υ+δϊΒ]œέr 6€¨œo³ΗΔΐM©ΒqΆ0lΑi}ΨΖ;ΥiΊφΟ"/`˜ί@;Ώί endstream endobj 534 0 obj << /Length1 1630 /Length2 17799 /Length3 0 /Length 18642 /Filter /FlateDecode >> stream xΪ¬Ίctem·&ΫvvlΫΆmgWl'•TP±m«bΫΆm'~υΌoŸ>=ΞΧύ§ϋόΨc¬{⚸ζ=ΗZcl e5sS €ƒ½+ #3/@ΡΚΞΤΝEΥΑNсGžAhαψ+η€£ sšΈZ9Ψ‹›ΈyZ@s€8Π ΐΚ `ααၣˆ98z9[YXΊ¨5T΅hθθθSς ΐΤλ?4=]¬,μ”܁ΆŽv@{ΧΏ׎j@ ΐΥψfe ˆ))λΘ(J¨₯5R@{ ³‰-@ΩΝΤΦΚ oe΄wΎ98l}˜9Ψ›[ύSš γ_,€ ΐΕhfυΧ θitόGEp:ΫYΉΈό}XΉ,œMμ]φΐΥ`eofλfώOείώ•£³Γ_ »ΏΊΏ`Κ..fΞVŽ€ΏQ•Ε%§«₯‰λ?±]¬ώͺίώZš;˜ΉύSΏtaώj]M¬μ]@OΧb™ζV.ŽΆ&^cstΆϊWn.Vφ™=ΐhaβln tqω σϋŸξόg€₯zGG[―y;όΛκζ`εκ΄ύΖΗΒϊ7¦™λίΨVφpL̊Œύ7 σΏεζnŽ‘s:«AΤΜ Νί$LΜμm½ζΐopLŠC¨οXfόο#ωΏβ‚[θύ#χΏrτΏ\βΧϋό_‘%έlmMμώΐΏw ΰο’1±όέ3yΐ?‹ΖΦΔωηcbgeλυςϊ―ΦZΐ§ϋ“q5ωΫ{‹ΏΤ032[hε"iε 4WΆr5³|3±ύΫ³Ι5μ́΢VφΐΏάώ«­fζ’S·΄2³±‡Ž«€φζ΅‚Ώtύ+&eQqIUΊΝ‚ύ—‘ςίApUχrό›Ϋ¨FΑΑόώuπψ0°prXΉYώήΏΏ ρ°²ϋώoBώ ˆε?Ο &ΞVž½Ώu3³ό«ϊρϋΟ“Α‘°7s0gtΤ\MμΝNΫό£6ssvώKςΏΐίͺγό―Ή=fp«Kf|?­Σ2Σ]k±s‡&ΔυϊzXΐ‡‚KΤ‹ ό«~H Ϋα©0~― flœβύlσZi]97΄ -”ΦΣ…Zγ!w΅OΑφŠŠ*½iΧHΜ%“Kqύ>Ω°ε·^WwšG ⼐ρτq_£\Ι.ŒWЎtχ%Θ/9XΑΕ[ΌŠΓΕ. δ—Q°g‹&Κ“»g#Λc2›ˆ2 ™ηAEω^δ²Hλ₯Š2‚Ν Τ\ΗθgΊ0˜yžβšDkΝεS‚C^Υρƒ+v{6#{O+,φέ(―»Ψu Α^k:XΑ •"ζ―έ…ή| {Α*H|ΠQEƒœSω`ύ{όΦCρ£Π»½²ά•έΕ61€ωŒž4CY}nF¬rϋ~>‡Ssς ν[°σ+§Χ[2}A›βl΅rEg“— ΗσΟ=ٚΊζh•Μο1έa$eΰ8ΚZ™`SŸ&ϊ1ίIMJύ\Ή΅«±@Ċ·ήK₯Jθβv(‘ΛΛΌΥkγiqΌ§h*­·‹²|8&+«X5XΈ8ω n6Ϊ?„_c_=€Ξ3›'Aiπ{\Ψ0YΦ7Ξ|λε3ΐΑFΓ­ θgpρV"Ωfj9θ8lΣόVMΈΣtϋuζCE…œπΉ¨ΧωίꦑG‡ΉR^'5Ηθ-ΘΟOη,—‡BΫK–˜ͺεφϊsW(ς}Γμ!Έ9†iόΧ£κ«?’οlG:姨σΈ5T’ψ<Ϋϊ^λ iΩor¦±w3#eγ»Z6―·–•ωβdCSΒ‰{·Ώ°[k6Ÿ}\ͺgΧβ ό>][)§UάφύŠΖγ§ƒΥ Ι*a,™ΓΨ_ςxΝχηmˆLΓ6“f<_jXω ΅3qD»”|n ’ΈΧŠό %#PR‹Q§ζ>ο:ΎβνέFήΑ+"u‡Ξ±ϊdŠVS’# «ντ缘¦ˆ(ƒ–β½lZαŸΠ’LΑ¦ρ)€‹ΐ}ƒ!ŒΫ0]ΑΤ|^έ(f2άλ9ΐΏŒϊ]3AΔ<³έf“=zdδ‹<πα…°ΙnHͺ_SM­₯@ }χΒC™ψΪ/ΠhuΔ_ο Ψ@ ΨκΩ¦Υ―ύjIΪ7ιŒ4YEP]Σ̏k!Ύ₯ΖG΄X˜Ÿδχ2|΅‡aΖαΑΡβ«Lρ­Υ ήέΗσ+l)s:˜…ˆ"‰3qπh@G)‡‹•ΐ@:x½T ÝQg7Έw!œc£CώaψsΐϊΙ‡Ά'o.m2ξ|!yOZλtιΑBΘΟx†7Oϊ€Ί#{ΰ“π2^,ΠκŽHͺULα₯pΪf,Δ€?ΐ₯ΊEw¨'BSΎ’₯{!8U¬fήwvŠ"!σd#G•¬–zΧzŸΝM %τ?l…z{‚P9Ϊ7dμΫΚX7H«ΚŽΊLύIŸΖPΘFμ] ψδ,™ŽΗ-›}°‘WT €˜* Œˆ―“ηΜqz%ڈ›ΪOƒ…Cθ†έρΞ=\|Αθϋ€2ebτ Šs%`αΙΩŸ(–«Lb0k‰i•UΆŸ°ΓΞκ3ΦYB€‡ο%ς}Οu4π ς3QY”aπΠε.―*qo λd²^EOB“5‘s0ξΫh{’~‹La»yΰt„/t†:>YTΝ·ΥΎaN3ζˆpΰ κM†O:pŽηq»’l΄αƒkN5ΏI€°₯ Λsωh™θž˜Ηꇲ( + χεΓI Ψz8ψΙΜ°ύŒ²ΏOZΰΧθΖωΘΦΣ³$5)ͺW—ϋŠκΕaοβxαΪώ€³½ίΪλoχRΰc Τ…S₯c†ζKjQd$ρΧ!Υo,+^Π­›4Άv°ΩŽvφ±4ώr†=_$ΏΊc.?¬ΌΕBΰωޘq+{π#f3˜E ΰ¨ ͺΌΧh€1Τ,‚ρ›ω™yΌDεψ3 ^‘˜Tο*'ξχχ’TΈ-lj’sξ.~;! Iο₯ρ‰eF‰ef7 '–ζP8Z7§kF‘[·U’ύ―Ε §-³‚σ Ό,ξ\²Ύ,o+λΫ›GŠj‚χ]'ŽŠœβ(:qd”EβwȐiΉΘΝZ#:±1΅Iά<‡Œ€3X[Qχ‰Π“Σ³’Υ±™Δ_OκŠΦŒ ̏τδBjQΙmΝ_Y݊G+˜Wvψ¨Ξ.€ςED?.W1€βΩ=½Ž•ΖΔzςΐ#Ω|¨48Ke ±l±UΘ‚F•:š…CxΕ>’‡+ʌaŽ‚©Ζ§*_ρ}nˆΚ¬ΐ !$‘ςΡ\fαHlVM@fxC9μΎ%sB‚½ΎdΠΠe¨ήΞ}”Άͺ8άΣh™ Xrυt™΄ΫažB€Sϋ{=OlΰŽοZΉtJδž“t~ιΖi Ϋ?\–Ο=·l_9χ8](‰fίΑ|–¨Υ/γ} ΊjG_αρμέvHo°TγάX]*ΛσǞk±nϊŽXoζwˆfeH—› ίΑyRΉwζΘ—Σ¬ZΥrπN^4έΟ]PY횘vά(Ώ˜-2’8ΤڈΚ˜ΰυ€ΊxΓ;F,”Tlρ·-šπ^ΉwAέ;}WIšT4{cž΄κ@xqh‡oJΛ%Ίώ Λ3ά4ہ ˆΨ5¨]†ώδΙΪ±ΉδΗσvŒOvMͺ?T\α5©ςlΗp‘j–΅Πέ΅$ίόΩ,ι—ζ7ΘϋHηςudeHPΉίκ~6U ώj}’ΨƒŽΰ―ύζΔnΛΤΆΥR(»e/κjαƒΒξQMRVN  ;’">?šΗ"ώ`ΚxZΉ2=Ά\9Νy+Ί©‘ΚmXx’f‚―+pΪΣ?>Mνy αBQ΅LH|!ΐ‰ ^ h­²Ήs‘Ξ–ΜNϊ<ΗΰΟΪ»ιŽV9rηq-2ΛδΈJS⃄6¬|6jΪΤΰ„ž―Ι)°¨kΚ”»UΓΟšΡ“λhN:Ϋƒ³σΖ[vΔυ0ΦkΊφΎυYδV= `¨2e•'­ŸΦx:zʘNg2aTΒόxF廀Ž$gξ‘ώlƒxώsήO@];ˆΉΟB_ɍy(4αΧΠΤTΎΌ]₯ϋΚ+νu€?~υ}$½u/JmΉΐ$qς¨νS†ΤŒY†8k&'ις Ϊ[¨¨^½ >&ύ€UΊ‘9bΙ"ΉIkβγύ`ΐ²z«φΧ¦pλ΅Ζό`"ρη*¨Š&kb‚½Ώ*–{38φ₯d°¦]V<νd£χΕώO{)ž°ψiÌPL –Ÿ„'8―i$\|™Νˆ(ρΪbΞ3κ v–v—*…½P·x5u6Ω·ξxο€λ}pr!oaχ,HhwXa-ςnšΎ^cΥzΫ0†T ^ΗΟΕ―₯Ÿ &Oό‡΅n~‡xsΧΈi άzO‹D&Ά‹νjT;Vc²%'KŽodKIqΪ:9jgΆ2gΛ_·μΒ³ς«ή½ϋ'›qΆ`ΏŽk_QfερΔpωΟΰσ“‡šxhΙ+₯χn›c­ΩŒΞ XiFJ―o89d“-Ίq…ύ\*Ό¬].Y)X)@©,ίΑξς92|tύqσΉ‘΄ί3„Ÿ‹0¬š…” ΚN6ͺ•oΝL‚D¨ϋžλQοB™_ œxœΠΒv–½#²g~ΐΌ£0ρΙ8Θθ#ƒ 6Ÿc-…7\= ¬iχ}MΆ݊Κ/w mΨ½ΪΕλŒΒΘ`ζ-(χXŸ‡¬ΐ³ώ;O&'ŒZ©‚=Κ;–Ξ;Ω“‡6Uς$xZΏ‘J˜‘"tOΟλ,Vh£Πlη°΄π”ZPϋ.o§?kΨ:œΤs§’Tߍ.ޜΗT/:ΰn+qnmDΥF9T‡γ0–"gηH„<*£‰J…zμzΈdύ”!‹PΧ$νΡ;™Ξd,‘MH—Ό*] а1Οk?«ζoٜΡ /~tΥUλŒp^+@Dπu>ώ^Α7εl;« κX 5«3φμΖjπ2Ϋ”Σs'œsΪSXpΛΓΧ=>³p½+ΙΒΑύސŸΈn 3 KΈΣT3Μ”pΛΏf>lU_‹•<—OWΑŸhεƒ|W΄™±,|2₯Ζ Iέƒ^ψ;Xί„Vf`lˆ~¦NV“ς€Λ7βXηεΑŽŒ;ό{ώSB›iΣ ΖσΎ[š­•T©εζyΠ†* • SMnΗI71u5X•-―RwHSέ «ΫB°€^IQΜ ‰΄–αCa)eŽ—Yg\X|_=‚|–wθX4ω+"—|ΜaZζ­³ΘBP(ύ™?TL$†a"iνao―Μ|Œ„oq;ψ0<ΰ²ΑKM‚ιΘ¦‘/m5=γy ΜYtΜσφ0 •z2)y »‘3Λd309…άςc²Κ7]΅έ:Ε>:Ί#j•€’ ΤύWΥo8s™’ϋΗ‘b±ρ ΏKρ*ƒ‡ΣbΩͺEv—~Ϋi Άv‘gΗ₯O@±5ΏΤχζE€$PΤΌΠ²^μ-k€ ϊ£₯–smφ±Ij*ΊLΔ9»ΔΚtΤY¬Υ‰`'/²VxPœ^‘gΣ‘jL\ύžR³9\5XΫD«]H<ε‘Vλtθ,_Ίι~δޞb‹"ο:x5"ωωS;Rψ~ˆ@U(ΕcΒoP§ζΉ}mžΘι) © ΄Οš ΦΔώΙΌΌχΑΦ=Ÿφ9tΡςΡfΒή3Q.½VMuŸΐ  ©Oƒ”`΅Ζτϊ*ΒΒBCpΊB€y;<γH‘g¨@⬏‘D‘viΎ©EŽj0 -€ƒν~RxˆθΧN-’«Ÿ"[썊I²£ΤΪχƊΥF”ξ')‘F4“„OΝێ*ΚΊˆλDΙ½Μy|SFΘ#GJκ|/Πn§\»ΉQι­oΝgPPU€’hO0v!ϊ2DUέ$ΧΥΕfλΠφwPϋλ³Θyܚψ NrΎ`fc§₯M—&!Z0‚Ηό\ SUƒ”“ΕƒaΠ΄ό9œbX¬U5š‹­ƒ©₯»nM‚RœφΪsAήbβ+θ†ΪvΑ₯ˆΛφΡζŠΠ+ΡKμž?v5š%ιߝ]iπΰ΅Xδ=εΗ“•“© £›ŽΠgVγγΧ{τ‘%_nY β½8ώ€Τ‹=²ίDˆovjΕ΅€συΡͺŽ"ŒεˆΓ…X—υγ:žύtƒ‘Β^l\θλΫ uˆ]ΊςaώΒ3OΝα<ΰSsu²ΣϊώβVs8ƒœΣ’Γk%r:zƒΏhπΈ1€€±±ά³Πَξ½νγό₯IΈx¦ Υ$蘽΅Π°{‚#‚‹FΟΊ—0~3vClt³›–‡Ϊ°q³‘#xq1ϋ);c1FWlMώ’2΅ZŠ₯ςέ1X$θVcGόOΤX¨Ω7hΫΌΫΈυ2'p{ΰψb›ΰ«Ύ ¦œHΑp¬O?€l2/pΖwŠ7zyƒ\ഇ†‚ŠAΙƒ~?/,Œ9j˜•(ψ΅Νφ­χ+ηšΫaχ3§³ε³ /£bζ΅Pœ;―ΙdpΖψj΄O²†€— Jκ‹τζ˜3\Λ' ‹ύwtό‘<φZˆ‘ˆΟί<gμIϋ:h>-б―€4Ά]ύ‚ΤΧ,εό›Π'±ΡΫΫ…V‘vΓ5g]ύύxξ$˜„—oωQΙ}³ευύά-o½›f΄©•倁ϊݟYεc]b΅#‡EήU¨‰kTΨ―ωΖωάR·5S”mGΡ YΥ pq4Z:Uδϊ―²f>Xv=³tSfΠΩ/qG5λx†@†–τv+τ€N Y‡c’UηyΜt=δ:p:š!ܚLfΙAυ£ScϋθHʟ œ₯5%ΕB‚#˜ςθ[6¦ 5Ξρgς£yδ·}–›~Φ³{Y²ψΩ^n‘ΈφΊζ_φϊBcΐ… fƒ‰Ÿ©OF-@oTgCdϊžΔœ$ε†ΘΜ~Fͺη(wΥ-mβ2q}!3Υ<6Œΐσ’ΐ=Ϊζp4¦š½-^8ΨΔ0.Q₯^³ί«ŠQ\κά‘‘­·j0ŠΓ0j’gφΥtΌΓή^Ÿh`Jšͺ„θŠ=yΚυΊKΪίβV₯ηωn0@ΫL―rZOα£EΑ±¨Gμ1>νφLΐ\kΛφLΕ»F‘¨r©·ώΙ ±@jU.?°¬AξΘ;Η.ύ‚W‘ηπlώ{ΪH”/ΉΟθxβqΖσΧ‹ώžλτ“œg{0Χ¦n³όzv^e’ν „ͺ>»Ϋΰ·Γ;k*h€G`°Pδ­ρΓπm†SςiBC?tAθΙ θ²ΐl3υΡ`qˆY懑4xRXβ-d½ώ^ΠΪ1»BAО–SIžW+Ι‡o@¬τQ†p΅wVτn^Šwv“ΐ.Ϊvγ e ΡJ“`1ό ˆQΜ8˜ΘΐŠO(# NŒŸK]ΣO—‘π•‰?2ς‡ΊΨuβ\w<«vώ»,)†€aΕΐxξ ‰ Œ,oδκ€»βV‰9_Pϋ>”’ΝΠιšθ©6θϊK)ƒ{’>]ΝgΟWϊTυ\Τ€ψ“33Σ7-THβη­iληxF"Z»‰R¬TqƒdO ΠΙ‘|Ηt υ—Α뀍Α…RΜώ‘0Tqφόj_α>Œ*&.4ΔA$±Ϋ±}Z@›ΞφxS*ψΔ !ίpψ§$$Ήμ…›Uš1Ξ&S€8ύKΌ‡―8ψŠ£Έˆ† ΫΤi°‚Hx εθζ·ΐYM>;ΓŸή½"ξηXF΄…£τΪyŽbO ΞlΗ?*ƒ’ε5T6‹»+«ͺF9…™Έlb]Π(ΝwΟ†{w‹ΈsΣ…έΠHÊ/X¬3Ϊ’Ž£.“Ξ&£5N΅ΨnΙ«ψoŒΌ!π̎Ώ\q€Ψ–κ)x杴=°π Ÿ’iT΅ΓTŒBάςlΠοB& ,‹½Δ;·YbZfgΙ9Μ–v6Κ²ψmzϋ[MvzXΤΟ³ΛcBu©Νͺa@έUξwμ’Qσβ‘ΖDδAΆ•ΐΛύ΄#Ϋ=lK>-^ΒPT14#ΏΗ΅»ˆχ„#η6₯#Q βμ3cBΐ¨Έό†5„(' (껊ρΧšYΆΧν1p―ϊ‚Ή”iϋGΨϋά7Ψ±.ώΙ ΰfΓ―θ«ΕZPl’½φUG°{‡Χ―WWw–ιΗθΉ2{Νύˆ*|Υ+Jjυ[LbΠτ±[σ%œ£˜l‘ξ³³‰θqš6@©zςS˜όŒFh+΄¨£‹=g“©mq!QWπύs7g{Ζ &gδyT¦bςLώ 6·Ϊ2Σ*bΙTiА”Ώ/I”5&Ψχu99—Αβt4ΪΫ"‚;-ϊF›bs”,xεΥΎλvvϊ4yAh­Ό"αό‡τ$`XΠT΅Œ@ώό‡5–qϋG†\A[λ]ΌΤ9v`5vDG9Η~gυ³ωUvΌ'N‘+$α¬SόPΗfzς7£Gγ`SL₯ΌIΪυ#£Gε?<”€ό,/ξέ; Μ4Ε†Ώw§βE=ρ½₯³6ΔP| Š(;­­zXΌώI(;ώ™(™εθΰΏpγΩκΕ\²ΨsλζD>#]WqΒΓfΔM›•Ωϊ†O—βoƒS¬(¦}χΞQeΘy”ΏG‘^Γj·.‹H%ϋ½©’UΉhAΡ*iΘy“­žΗφ>§a€Tl%hGEf­‰·₯[ΩЈ@»΄›ΜΥI«rΑ)ΓεˆΜ$1Ÿrγφ£Žn\ΫnΚ`βηή2ΜΟϊI(g1Υϊmc8=ϋ0z/Ωƒ6?–)O…5FYE#sύFΈφٜoΚL΅ψ –ΡΔφ£ΟW˜dΥ ΦŒ "h΄ΨšDLrτ­+>οΑό5œΈCνD½$κη½Ή~ΰΒ‘tάΟκ©ΥmκΈ6”C,_DCR.QG n-qO+Kπς¬ιχζΞA?:©·\΄ ‘u#U5€ΤεΜεΡξ‚‘ή†ωk9Oχ‘0νΗoS:w―˜₯ΫΎ_Ψΰ_¨Γι‘ψΪλδ}ιTvΕΘ&{χΒΊWρbΣ’ƒή" ώXl“Qtς61Κd»-…Ÿͺ ‚" @Τλ%ŸςύšV3όΩ‚ΚQ‰7ζ[΄².m$«ΊσjP>cg7ζύε/ϝΨvΓόέΓ€οFβnœ‡>χώΕ9aέIžp$πAλ°ΈH΄Υ€‘TΏhqd³9Δv χϋΪ<†&nβ6T-Τϊ«¦΄ϋ vΓD©l;GΙ)ϋ­ΜΤ¬ΝΥ-’ˆΖFΕDn\δΩμΞd­ά)m~R]$ΐ»f­^¬¬0]^ͺ©™¨AΈ*gA™‡CήJ3ωΞ‰„‰\‹RΑ§ϊ:“³JF¦4WωzΛsήJD:…‡qP9+s˜―«₯l\ΜocN–μΡYΆ_Πiu*J`°Μ±rWKΙ$π\Œ/ΤΕšimδΥH0ΣΣX―18‹α ΘHΨMEΌΒ8Žv¬γDN&Χq&; αŠXW,=ŽήV’Ϊ@yΔrψ-L7rβ¦.όνj5,"‘86—\\­t—δω~@Ο]O(Ήη˜:¨€ρ'κpΠ³˜ιΑq—Ό9/ϋVhΕsΕκp©AόpN€πV»Οi\U'"³­_oΟRΣγ»ώ˜ ΈŒοπžμΉ~α˜ί}ŠηqPΝ”YgCά‡©Œ·€Ο³™‚xχΑEAΔ£bΩ^cψήζ¬γάω$ekΉ ίX"?pJ?};’<*iyo’)SΪ°fΛΑ¨}_ˈbΒξΙlx.Χ±ώ‘ΒN„ˆς£±ΐyΊΕ@”Μ ΌΞ?ώEβˆYωΠAΓΔγb˜θΗΥΐa|ί 7Ω\Ί~mDΝZ8ΚτΝ"a ·•FντŒ³α=»+Cφ-βœlc€―2=Ω†―OΖ»`gΝ΄―™‚ΌFDΜkφW…>λΨμλΔ`²‘±RR»C#Ξ“\Αz»O€«­‰¬΄QΦ‘»θ-8ΝέΦN°ž»nΏ7’‹ŽA“δΖυ.QžΊ–ΒžmFgo§A~ϊ &HΪZ"rV3Pέ) ΆτpN“*mW;D£Rͺ+ˆτGSε·ΙA“υΪM3Ӝ½υ)ξLΟ φ_~πݞνzϊπΥLμDθS\qΦΘ―Γ?ν•π†Τ‘;ᧃ εΏrΦΒν§ίΕΟύΞΆ†»@yΩ8ŠΦ·’4[χُ²,6Uψ*1€“*Ο`ό φϋš‰Lς@Aοp ~ΦQ6XΣ6=ΰΧΓπ2πΣ *₯ΆjΤΟ άT³ΦΥ„X5’|΅˜ŸB`ΊUε*>GΐuKšΌ§2ΙβΉ^©LŒΖNΛ•TΓFUr φŒ}Ρˆ†šŸxQω!™Μj"£erbρN3Q9R }ΥvΦFβJ θ΄0 T\`KμΦΊ)Ι ­ΚΙ―Λ<΄ΑoΕάtOc°ΔF‰ήΊQ&Ϊ5γ‹‘Λ ‹Ό`ΗΒ(^σΘ΅Ά‘«H0kβΐμΦ5ψΣ6gώF§[ Η|Πΐς½u”feu˜1. mδٞ[MšΑœ^!ύYζ\³ε*srςέ ‹8+vέ_ΊΨNTχˆ·\ Yώ ¦Τ†«XϋœŸlψ'ΘΨΑ@ϋΝζγΕΊyιF·cIGΥC‚%¦&Φτ'5Άα³Ρμe–ΛHτBfΪαNOI\Ρ\~“‚ΖkrvWCαb6ŽΧΟΙό·¨kJ(u’cΘ°–γfsqxsxKΤΓlGpιβΰ…4*5Üϼ½¨ΔδFB½:–χ΅7ύfΟ5οBΊž[nƒxmΆ‡ ίO%c_ΗNυͺπŒ^.ωΊ«SΨεε]Yχ9&KχžDFVšεAΉθ‚—Σ‡°•¬5Νγ6žο- •TΎ4~ζ,6ΔΟmC{ΏΓκ3ϋχΏπŒ0DD*»‹”OΡKžbΈu^‰Λ χ‰j ’-ΪVΡoΗjkχ―:ηήZ2ϋs U€5v„=9ς€Ίκ.0ΏΡ!}¬;ί( Ί•Tg†rΟW "H€Œw°ͺαrΈμΤ©p‡I{Μ’j{CuςΠΰΝAcŒUnj(ϊBΧ0x©Κgž›γχKσ !*\ΚE€-Ω΅Άg }mQμ½ygˆaε{5nS +IΨjv«7y?•0ΪϊύΡˆY­KrιΉ°ILj 9!%ε ›Γ0΅CΑWDύnvw\E΅!<žQg,4’«Z.8γWΗY?$ώtΖ.cF¬$‘{š&^[%έNJχsΊΙΪx6ηsyΙ „>2Ο΄ΪΟS³β"Ύu’°OΆwgPθΝ!NxίΊ”Pu\Dφ‘?‹0Οf2ΈΏύσmδtΗOEσΤ c1(Ήx,ΛΏΐ΄]v(½&wK€{άo† ώρs·ΟΏ@—+ΕΐΎ‹Ξ>b+αέ/A/.₯αΗŒ‰‘ΘzqxŸω²Ρ tWι YvΙn6Ž)Κν“φ‹#{υϋ+“ρπ)Ϋ<-4g+aμΥXέd«UΠzΎrψ??Ή+‚ή „Rν')‹š–ΣΰΒPΌta[¦Z‰ώωφΓX ο0L*V΅‘Έ\Ά§›1m±xέ$SmΥ&­ι·ρ¬Vf’½dΘΛ§½ώήkHGλαΕ;ωΈΟ$mš62q#ƒ«—3#ψzή\De ߝ |›μŸΎ w±)Γ Ϊ¦^μυή§§ήΉ χkW[Κΐ δΩuH°ν&Ρ­μ υζ φuJWuΩ/wCχ2‘e=0£4ž§gœ„ΞeΚ- ] hέ‡§ΫVςFD@E$>‰΄˜λ:+}ί~ΠΥΟbνioŒ:}JςΑKΛ°Φ™T`6r!š.?ζ£{βΤΝΒ,@&Eƒο³Γψ•>β‘Lπx9ςΕ! Ο•€s”’~lλς»8{…”ΕΫ Ν˜ ΣŸr]Ν΅Ί—~MΘ²$9ΐαy#pœΊYš£„Ο,ύD˜$xG4’ΌWz.,Έ”n(΅s Oόkx ’qιdr™Ρ$Οs€j $Ά΄©ΣuΗZΡΊκnFψ6__VȜA^Sσ«ϊwΑΰ€˜!k hqlA•_+?ΒΖPpuΚcŸβ8LfƒΒ·:ΟkΪyΒφ9_ΖΆrιkN°ϊš|cπ­΄e'ΗόΔUf¦,OLΊχς cΪsψφτ›@s2θύ‡rύΙθ6š:>$}N(鏊§¦ΰ gμJΉF^&Ÿv#s-Ϊ›%ŒT–SώˆΆΐΖ―w—΅ΛPzz½<ΝQ+˜ΔΆΨσqŸ@„$1Ga<Ϊ5»-λ=KZrχ.+4C38YC35ΡλyEˆΠ€ΗŒ!φ~qQ'―iιπ£ύn­δ=X‘3q#¬Ω‹S>^"%_Y0ю·:,0O4dΝIΨΥΊ ιE ‹VlcgkΌάxκŸXib(ˆtTΨάiτ©§€ΰ•ΟQwa{#b2 μςs§ΉŽ&΅πφ½—ΓQ‹³;Zk'0FPαη ‡ΞŸβL”s›rΕτŸ#ς¦ΘQ43{²yͺΆ+A ΨŸΕAςKΐλύŒ!Ψ|’…αd1η– h*˜έ8J³LοaϋκΎ΅C}ΫMu= Θ)β<Η„žB+ι2Τ䝾flSρXνK{ςΈdή½ΰ@MΞ ‡Vμr΅ ΌQ|δs8/vF;φ;χœSpϊbΠ;Β­υΦ‰Ξ²Π"]ΛτZι‘χ£β6 δηŸ„ΚGXΜzUΓq”˜Πλ·Χί ξy•Ύ‘?XδυHω8E86~‡^©§hβ ωυ°νͺŸH 5ΜνΈΚ=©J˜‡z7Ωeυy;έόH ΕCͺ–ΙΒbβV-Ž^Ώ%=υψi^ΩΊνΔι<Θή2€…Z§‘ΚrƒςS~ˆ±u¦%ΉŽϊΚΚ‘ΩΑς#YF ύt{B»mΞΞ[ω‘Uκ5:†±―³¦») ²1^²ρ#™ΉΖ†Οoˆ=3KΪ­T­»wj;Ωˈ½z)‹…wΘa.QۘaξΔ°š§Ν2(€•‘Λu»ΖκCΪ²ž·6Ελύο΄-“d]υ;t$Z˜ΝUΧΗ7κBπut]Λ`©‘nNΕ­Y˜fρ–ΩBŽ₯šΪ#9Σχιπ}φπ8X°ƒΨNQXKYζI›M JbkBBш―!}°ςοΡ)S²ΜO$:iό»IŒY ύeij$8ͺ-Gκ©(–{YΉ£ΚV"ΆπKΠ"͝dΛ“&ΑdˆižΒN@»O ,~‰Ν―^ξΜτ@¨r:5‹Οcz³„»‡i$βΎΣΒe£κ<{ΪΫL“ύ&ϋ€£22Υώ°ΞΛU.zΕ6€…Πά4^l―’]΅±ŠPŒxœΞLh)UΓj"π‰ ͺ@Ι―ωE‡ͺΤχ Cω²_Π«θξ¬ΏΥX©hζωθΙσKyXVth룄δC’|3tζXSq§mzΧcΆΚDΫϊ§ έπφΨήτJU•›Γ’ΌάL…Ζ¬Td[Vΐ^ϊ¦]!>΄ΨόπGΌŒU>0ΕBν·0xΆκKže‡k4Ή˜waάNΚ1ο»BοΕ:υ`ΟΉβ4 ά&„θΛ^”Μή΄τ›Χ 6½ͺWΨΦ–S²±RΖΖ‰vyΜ6§ Ϋ:­ŽΪioŏΆ}ƒώβέ½±φS‚6pχt;#ΒZ!lΡ/l υ+,ΊωŸ'ϊ3š0‘ndΥ·ςLJ€_=Ώ—ψΙΝ/foη~wξbK‹)/#ΐ§5ρthDΚΏ ,§2‚Yά{—›ΏΣ Φ κU ›­O±œ±AπVGbZΔ[Ÿ{£‘‘Ω‚–;ε™4a ·IΆΒ€d«¦ ŒcΔg~ή©ρ6K``Σ%•VPχ-ˆ~sοΠ9τBb ψ fcο·ΰ»ίE$Ρg£wœUκ―‘κL¨s83Ž£­[ϋ$Ρd^σ‹€-ψMk)ϊFΠi3F:$[ΌuƒFX¨‚6SaœZuAHλΎΏroπFFΥ΅ͺΙrμ\˜ίu՝0nfY›Xhטθ‘ΐκΓλ°X<τ—,ρNΰNr{΅Dΐυ†ΈΥ%Ι—ρŒH%•ΙΪ&>,W{°€΅{ΠΠaΦΜ‘…Χˆk"°ϋB€~ϊΆ Κζ£ΆsΰO4@dΉ0 u¬ΉŽύ‹T[5†γαλ χΈΨI‘š!~γ³>A=†Λΐ,j | %ƚί9"•|₯ ΈDπο[³-Ψ―KF4NΡφΰ ΔΔG7ibž(μD²ίοfΕ566lΡ6MΘ=‘ΩΰιηήK§,Έ‰R’ψ–―œWΓΚ†xΤΕМkq8 Ώ?y±₯-Š (_ƒkπ~†―#νΪVZ«¬fkΡEu€“'ΡΖυv‘BώIˆ0w;?Ε²"&ΆŒ•›+Μƒ$f6΅HP "2́‘2xΫ%^7€3!¬1Ν4’ηοΐe§Γ|_Κ½έ“Υ•Cy=#πŸCΑΌφ%¬Κ±Y¦~θΧB”}sώ}·&,_g C„§@ρD>έΝά΄'C‘~%ΆΘ½ΦH[F[Ajb¬XΙJΏSzjβ}«·η³ΈŸν³Q}1€xXsΧiƒ+ΧΫ…š‹FbX‰P@6ΏWθ…`Wά;#Υk—˘V“ŒΏτVHβο4ƒνΚ’ ©Τ%ίh¦νKy—#²ΑΡ·½Ωt5a3Ξ~1—5ƒμXŸχK5/»E‘·ͺ‘P˜ΈlΩ "ϋMšικ7eU―”Η*”ΫVa€†DƒΆ"Œπ|q­Υ\+•ρSτ jυ|`€»!Γ'ϋ‰ˆ3Φ…ω_?βαΛj(q›ψϊAMΒƒ‘,‚ΣНœ~9dέ.“Εtƒ#A\θpτ/Λ'PιoQ`#Γ€έa‡’ΚIκMR‚pω-ι†ΒΘΔ:σψ9γVΊ|’HšιXωαςσdΚfc< υ»C„.27§Ϊοηh’)ΕHl„ΘΒΥ—=ύη†IEρ›„6β朡ŸΓ}°%w•ι;ή«˜)ΟΡ6Υ/εJιφ―g^ ΛMRWλήγ= Κ(IŒΰΒ»/λy)DΈ£>Gn3ΰ=oφO­σΫ}SΙμ3“ς@Ά’΅{ΙUYŠKŠ!½Œ:]μ꾘£0)όΉqX5κΥδqΝ[ΚW“ής“‘y`P«k~OZwϋr†}Yk™›œZΤQ\εI<±υΠγ;J™χ‰žl63ν‚ž‚K?QψψVoο*o›OΌDAo{‚ω™Ό%1Ν‹Υ‘ΰ}sψΪrΖN0‰ώމΟ;όωQgΞΏ„€‰γΎΘ_ϊˆΡύ{‘6X,FΛέ.‚ΜΜΑ₯Εfsηω7D SA½E[πV4ƒύα&¦ wp³/Σ¦ψ’ŸUΫP2MgΦ†Λφώ‰cˆ¦y·§w ς›FR΅΄οhήd©zIF/{ΉŠ^+ΊcΧKGCtEΟzJAλ–ΈA%ΎΏδͺΪ;Εp—¬œΆ£]7«ΎJι‹δΖ>΄c6c%o³§Τγ·ΤbhΓnτAΖGJq/μΈ꣎FΞ~Φα©ψ TΛc`³o¨EΧΨlYQ‚&‘ΏZ­‘ΏΊβυPc yΕo4{±αVαΣΒεΙΊM*φΒMYO|π)iς‘$8 ρ•ηψΗο•τyν^CΨκ!μΪ/”d™ΌBΏ΅•l™_Ά—·XΤ’Ύ84BΦΐσ’fGŒγμ[m3OJΎη v3Φ©~<τs₯A°%œΉ©LPιm”Yq°σΛ―€ˆE―Ή½΄ΪΠ8}€ΐdΟ7τŽ@ ζ”]s‘j€—F₯Ί0+m ―ΫΩ£’²!84 e.ψϊΚlΦΕs:ݐ)ζŒβΡμnUχŠ•šΕG/φ‡SΟ3Η5ٜ?Hk$Eρύ€‚†PζpΚΒD9ώuVΒΖGCΖvυ)φύ.Ίš„χ­²`άφ{K|#ρΒ·V°„…·seaeΝ^;¬˜Π¨ΐ'aή -6Ίήb~nΗ]ƒζCVg/91»Λޝqΰν5‘ρΖ(΄“Σ-ξΆpμ Φ±›ΒNψ«βΑ.₯ο<nLœΖZ:Hαu₯;ΫPsDΩX‹φΩόΧ±œ©› h­Ϊ@,λκτZŒ‹Nρ’>}€šλύ»ΜΆ_mΚ™‰‡ͺ}Œσϊ₯ΣDα„΅¬6φ»‰‹ψιΠ™Ÿ'Κ”ΉœuψΩFΤ Χ$¦ϋ₯Μ†)©cVΐbj=ζΜγŽ4s| Ÿ{Γ]fΙO°KΪ€πΛΉϊ!‘ς·¨ytκΟψ~pV)’― lΰ/uΙλ˜BΫ½μ© &–€«Q?G™ιH<γ‰f―+8‡π€j¬,T,ͺβdαz–žΑϋέ}Ώ?S4k&ΰ:–Νžͺ[EpΚνh-ςίOΑΝ_UDγv£BΌσ‡HfξgΫ΄Έ“ z8L8¨3 ΅‹Sσ~wή5Vy‡5όVαe*5Ά­ž³Œ e»ΩΫF£Χ½ζ₯V:€B‰ϋHΘ(δ#Q έwΦ‘hVšζ€Πpγ+}}Έ€‘‰Έ¦˜LŒΡ›£ςΎ8Ιr+5ΌΓΣm­V“ɝ|έϊ†αnή„ΑΜ€ΏΟT„(Ήγhπ*uS=4'φ¨:¬ ΅N(\꩘&q)Ιπ³w“g\TܐlT1Υ‹Dς‰Ύ7ΘCϋKuΫE—{‹’•wΝj¨Ms(ΦtA`  ^¬ΝIΗ|{Ai&"οό\ΑΫ© = υ~ΩΰT-n…ΉΆ’nλ>${NŠ\•`"uΪϋΓΟΔߐjZ₯žsFd¦W†΄»Π°₯»63θάIŽNžžβ‹! ΅³υλΣΪUτΕΆ=Κq” ζp―ɚμΔ`ΗƒxΟ9gΪ«ρΩo|₯ϊcXΫKT ¨™gόtsΓΒ€λ_>Dƒ|c}π£¬νjΐU§,U­!οŽ\.‡%ƒ=D~q$V™V(ΒΡ,Dh@Q΄ ³χ Λg^JlΕ”C0dT1.f1Ε Φ€Šj ¦οαNΨg U›oA’iΎ£+ Ω+θ[|χkΕͺ)BEΦ1ή»‚@IΓl}ϊήdŠ+φ€P)εΓφξ΄>wσ£­€·-Αr΅kΥ ΑvΝΉ`–„=,Iς·FΛfqJ₯]Η³/4•Ÿ'υσΤ†¦ΈιαQJL‡xΓ[±w3+G=ΓΔ·eUjR…0φ½Ÿnΰ1u~‘cθΗΛ<ߟ΅œs"i―μ‘q-#ΪΡ4Ψc•e!|βΈΪ1xω TtΆžJ#=ΠδΟ—C΅%Ε`δΛ„;;ύZ.Ί.O¦<ƒυ”yy†šΠF! ζΕeϋυcΖ΄²¦†πκ₯e ih°C°rΑqαq°bω‘ΛEŒρSe/1Ω>\Œεc{IΪ»+Iχ}έΟG'ϊϋ†gͺ‡#R9hη__Ι6ψhΆMΕkγngbG I―`Θ`ͺ,Τ»θφγ|œdΧ2dpκγ7 9e•χΝͺ…‚ U Y˜λεσΣΒPΥ>γΆΨζωγfβ9L$ •Λ«Ε€ξΠDj―« 4"5_Ÿ‚₯ΥFx±SXΖ!ηΝGζ7r[σJ¬ΦLΔαϊΨ»QR θ₯œ‹”#PHe,Όυlκ8.eΗΊΉ 6OBo?€‚©™j,†—Τ [xkHϋš>άSψ4ΌR77όσΊλ"ΎΒ!Jμƒ Gμq€ΪLq›1a ‘ŠΛζHΧξηΒn°γlΙιfWη΅ζ¦Ο Λ |¦φ6l"D½›rΚ 7―yJ1ƒUDs?B-{lώηxΓtŠ₯2ΥBμX'ρ€€9SΡJu=mς₯spD Œρ²¦α ·ν,HρδΏ•ξκO!•οκXWGBBΖ€PΗ±₯™ΐͺ@5*ΥΖ―“Zw¨‹΅ν6—c`½…ϋ}uN¦Κš@CgΠxΝ¨±2˜6ΜaεYtμΉ›7gΟ½Β`ΔχΫoοhˆωμ@εΜJ\Xϋ`φΤϊ4Ey2H[•!ΐeΆ=Ξζο‡AΦ?‘,|7qς©mρTd€]Ζ°ϊώφΠg/ μVŸŒŠγ΄>&h½Ξ¦ώbΑŸhμΥ’S{Λ(ΞJhkΪϋj6X‚γ(F“–μΏdͺwοoΑΎK𳔁Ψ[›n„“[Ÿd(Ρ΄ΩΈq‚ q>o—\ξλwί3^Kφ˜nψ;sZ(ΣΈ™πέ–I^ΔΊP3V4ΖB2nj7`ε ˜0όg$?γ9³;r\U”BeΏ¬&ڏ©Τ$‡Υeώ²Ž{ό䚡£ι„¦Kΰ”ΦQ]N“‡`Z3 ­Ύ|Λ;2štLΚ!Λ7›Ίκ²€uΘ²“)ν&¬ώυzΜ$>NΒ‰ϊ–·d(³¨8ΌΜ#ήΊθ Y{0Κ°GΪ•ΣušΌΖΟ‚+σ+žz΅α©ε›S­‹•ο^΅Δ$ί―p!D»i5#>'zΫ‰ήΧ΄‰φ/ΚA)YnκΣβ EKΞΟ‘wΫ †bΧ=Dβ>K‰€΅TΜH‘#ςgτ\Z$ε"sŽMΚ—x”)>DΘ‘Jj‡₯’SηR ?‰Ύ•tΩ.¬²ζΖήΕO]ΩXœqB6ZΨί’…ŽE#ιοdό@₯;{€νy»σ > stream xΪ­xeT\²-‚»K:8!Έwwwm ‘Fwwwww  Α5Έ ά]σΘχ½™Ήkξ»ξ›_έ§φ©]²λΤκΥTο•T„ΝμL€v`'FζO­‰3DΕΞVΑŽGŽAhα x΅s QQ‰:@v`1c'ΰ'€&Π 4°²Xxxx¨’vφξŽ K'­ΊŠ&=ύΗYώ\˜Έyυ„€,ΐκΧ/.@;{[ Ψι•βν¨ œ,s  ͺ¨€-­  •TPHΑ@Gc€’³‰ Θ 2‚!@:€Ή#ΐζοΐΤlϊS„ρ•K0@쁦 W7 ›)Πώτ`t΄A ―ί ΐΒΡμτΪ';ljγlφ'W»Ήέ_ Ω;Ϊ½ή°}Ε^Ι”μ NSG½ΰ5ͺ’˜Δίy:Y;ύ‰ ½Β;σΧ›fv¦ΞJϊ {₯yEŒA`ΐ θζτ'– `‚ΨΫ»ΏΖ~%³wύ•†3ΆψWŽ@ cG3 ςJσΚύ§;ͺπ_ͺ7Ά··qΛΫξ―[Μδژ3"±°ΎΖ4uzm#1ύ™i°Ή€…ωo»™³ύ?0 γ_ ’ύ33t―I›Ωmάf@s$&;§ΧΪΚŒ9‘GώΘϋ'ξΏkτ_ρο{ώwj gcΫΧψ{Η^—Œ1πΊgr€?‹ΖΖΨρΏωΫ‚lά'―Ώ­ ό;݁LΪΙψ΅-Β`‹WixΩΉ™yώ@ ΠL δdj 07ΆyνΫ_vu°ΠΡΎκϋWk ,ΜΜ†©Y‚L­Α„ΰψ‚Νώ½ŠWΙώͺIB][[SώΏ/YU›Χa3dαδϊΫEιu,œΤά큀OSήΞμŸ‡?„""vnONn+7Λλk|M‡•έϋό/"–εAn]fFff–W?ŸΜbΣΠ7q°©ΩŸARu2›½Ξή? `SgGΗWΙZ―υγόΧ+έ€¦HKσv¦ΌAVi™ιNuψΉίΕt{ΊX`‚νKՊ όjμΎω¦…ύδ©4zͺ flτζ>w`Ό-σag¨ Ο†ζ[ π$ŸΔ›‚»s•Ί‹~'€Ι 5ύP3ΚσtVnN‡“Ycgγ»²ŠAΙΣρv6Η·§·t~.~8”7φh>¦© ±ΈMPXu…‡Τ‰{·74}ΓƒύίΞα»·‰ιsb©xρ}’ή'9Ή9^5šΎΐ?ΈpAͺζ²Z4k0Υ]=ˆηίQ^ΗόΔfI?f#³ψ- ½Ε,4o+λz‚ΓZ” πρέˆθ„šŒA}ΗΌτα"珣OiΦ™‹<1 δ_M‘σ’iEk/˜΄BR}³™:!ρfRΜάιΚΪ‘ό\μυD‚Tβh‘Bu~ΓIBΕΡνNήdoωΏBU«₯υΦg*1 πƒ`’Y~ΉF\9π1 i[…|8ŒΊAxP)gάδ#‚IYmΒ©³ͺšX‚GLkŽιΔ¬u•=Ω’b{ Ε”Z΅mzχ)J:—κλ2Ω ε›nηΧ4qŽΤA£‰έž&Ω’·ξΪR_‘Κdbς|Ψ“uŸΡfMjϋ›š>κFξιύ¬$Ξϊ"’’ω-‚s3‰—4psʎ·Iν"¨žŸ2‚βa6Σ_‡%3 }mΘdΑl³/S£…ΨΰmΧ‰$Π5oΈΛ·…‰5Yάπ•{[A}„Ρπ…Iͺt]Ιk³€¦²ίBτrqɜ‹ΛΛ­Ζ₯Ψκ$›{rξjφL†Ζמ*Φ³oL·_™py“΅œgYdb=dZCͺ™­6&Π©ά,C\ξ–λβ¦f½v#—₯.Ό«&Η†`A›©εl* ½1ΛSψαΧZ{|“ ΘW}/Ά}Βfƒ‚έ@xμQΒξ±ΛέVτˆGΚEΖΜΏ7f»σΕΑΥπΔΠΓ ϊ9ϋrΑz—Δ­;bΏB/6ΛsCtfΪθDΡf2Ί dτΈρ*ΐ½Ό£˜FD!ڐa%¬³ϋiΘ}ƒΗ .Πakvφ6ŸϋL-?σΫΠ™…―€}θ.mR<{δδrxDο‡pΠό`wκl^E=A:ύΝ•jωFŸΤzΌ<y΅;ο$ΏΝΫ±zt7πXOάιψU·μb§μŸž?Δ<ΝΊˆΡΏKΥ4σ›ΑΚoπ„ι=M·ίW} b~Ζε U‹E_¬•YβԚΕ3KΉS]d¨H4’γΰ}υά₯τί‰ ˜ GΎΆ/’Μ ϋμAk—UvΛ―vq}tOH6¦©ξ,‡|¨i‡›s{˜…x`ϊ9ώΨ|*Ώ=Πα²ς‹iδkϋΟI°΄SβΘr₯ŠK?VU<†=Οn–©¦AυvΜ}]ϊWZ+ίlκω–b‰hgΜΠ’²γ2Όϋ-―rΌZrqyLYΦ]Θ|nŒ^ͺRίπ.PdSε'‚mΑœ-Λğ,ιΫΝΈ”š~hΡ”!‘SΘkΰId%Δ7¬υEΙΝxwDΤ9j&ITf­ Α·$C2—'ιžGρ!*p!ΊΠ$QΒA\A”φ{α1Έ6οώΜ“»`ˆΕ›­•'†θp“ΈύΒj.Μn~-βλO’χΐIΔηΦK»–,Ȟ4š> Θ-χό4€ρͺΚT0ΪΙγ7$M[“¬T^u…–££ xcͺ’oΫΌ(€_pš,GZ±ΤΩC¬[Hƒϊ’DΤyΠΖκCg!tΆš)R€ε6ϋr(‰:J‘s?νJΆϊAv•όŽρϋoPκ6hwd.Ώ1±€-ΙΉ‘u—\ΞIμφ¦{βg$«°4NǢ͌>w$₯„5’#+ ΞԏδΙΈƒuCSrϊ*ΙγΔΈ;«7NΒϋ”ΫAƒκΒ#„ž„Θ(σ)]4­ 8IςnIf‚jΥ/ϊ²J}Ή…χ°……2—,„?!Λή' +2p)‘ Ϋ3Ε²:Vγf·iMΡ°ΩXόηoAM°Ÿ‚[ “?Ζς￈ΆψFϋŒ₯ˆmοa'ΘΣT_Ύ‘θŽΈt{ƒμEκ­%³©ΓΗlYφI™CcRU»0ρσ@BfώαΦU;ŠΒ.J₯—~ιpΗΚ£ΕωgZ{¬t:Ntό¬†ϊ7ΑI+ΈŒZA, [Ϋx’H–:ΕX({|ηŽ—KΊ‹PS¦ ¨M—šώ¦Iˆύ;y•;ςM|1°‚—λθ+ΎŠνΧ|υp€?ΰ3;H¦˜ux‹‰ιVͺEφBωrlTΌ'>RŸ”4ΓrPu/„< έ~PΓ0πΠdΈPγPεPz›zAvoέΪ‡½oPφyυΣ―EπαR„%χβ[$Ι3q„MτηoΡΝόBB8Εƒνρ@κьbœν&B.’ υ›wŸSt‡XΛ‚XΔNχ,οΤDθ₯‹ΠΏ!>=Ψ΄5#•n)”FGό†Χ:εΖΑn0θ[-R7w{΅‹{qνtΖΛ;7V2΅iΣ»Ρέ‘ŸŸή •φ#ρ ²DξF—XΜr]u§y:<α™Lu;ͺœΈΡβΊpΰŒvl›–ŒΫ0φ»πš›gPŸ;―UφΝpθά€ΝšΧκG Τ&ςΪΰΣtϊ­ΒΣΕ3.ζΦΎώ«b’:ξΗι(ΟήφΔΊ―œt5r…ŒFΔΒχ£Ο’œS6LPεΤjžν—†pρϊΩΤA(ckΕUΪ_‘SHΨ—΅“αg RΣα~ω$΄Ο_ύtχH‡&|›:Υ„§o [9“₯ςSΨ½ΉΪЁ1ΑχότqΦIΩH•¬³0Nn©όΆTΎςη•Q²Λ1ί›ZΨ}6ΔξΞ”–άΈΜΘ’ΪΗ₯ϋ#w)ν²Ώ³¨O+σά΅€/v†&λ1¬ ©w”°RώχeΖφt|,qΙχνcŠtγYε)Ρ:’νθA²λߞ]ιψύmζΧ‘Ih›ωcz’ΏIΝΆƒ>ΓˎDv•X,΅DN³δšΘ \eUš€FΑΤTW§Ÿ6Q§€”2{B•πzθϋUλ5πΑXI2yy<Σ―Ž«s―Ω€Fmœζ4ώUtŠκfΟ48——e&ΞΤGΤΩί ¨r?s—u^9jθ΄Rψ»qΦν”΅!d cΤε4ΠG’˜ΞΈ`*&—-•i†’b*α.hΑJ;‡e‰δκLΒ±φ0IΠRj;•2ΎžΑ[±‚z#oοκ;ΥΖ½—φζ;ζύ ŽIηνΙ φIO³`α„IŒŸ˜Ήϊ(p­$θ—­­ƒΚ|ζΟ€Ω‹™Fަ­š| †/菣Yϋμ3ς―ΏΠIYΠ~Lγˆsz¨R_Ϋ Y²¬"N¬λΪΘ·A*ZΣ½«ΫΚiέκ Ή5¦Dήv)‡@1n}»1ͺ?xω–ŒώM썏JΖέαΑjάΛ4gΪWζ³ΟλRL5>~ΖQRΘmC(eδ’θ\ί’Γάό€RO‘G…»βVb^X@GՍy€…[\)$ξΥ•—†'G•+³΅τp…”²UΙ-PΐήάΎυ%R₯)†²κ^- >y|Ώ…ZߘΧΕ5λ–ς2DδͺMȟ’μ½(G!QšMŸC€l{Šϋ1jθ Θέ”hšœζΔμω’…»ϋO6a΄'ήAγ{eo†ντεφόV†-©«Έ»έC/†Ρ= Z9λΪ=Q9‰@j+v‚ΝΠEΞo₯}XεωίPή‹ ΅γŸ(­ωzΡ=Λ’3qΥΏ₯πΖ½.UT΅V4=ΡpgD;~κ‘Ο‚lZE5œψ₯a”ΙΦo΄‰keŠ)¦Bμ±*Ϊμ—Ÿ‘ +‚³]\ί zΖΫΜA~½΅Esƒ†MHΓRκν\π…ιIIλΛψ©›™·°šΟΠ-φΤϊ BσiΎ8yΥŽση7ƒ¨qΎ„ΰtcuίs‹;η&GT}¬OεΌiŸςμgΔO&ΛI'ͺΌ³vε­r!Oφ,MΚtˆƒΡ³₯ŸΣDS°"Qp€Q?«‹½>§ΌλΛώbDθ;UΥΉN«/7 θ²,Kn|gί$ZΓΦ΅·c"ΊE*sΓΰœ΅­‰nΊ_β½βΏ’κA( ±~ίΆ„fܝ³Κ8Θέ7Β{…_ρ΅šλ EyCΉ°\άΡ8iοΙ}H—Ÿ‘>χήr ]ύΝ9)?Sώ{ ΫΌxχˆΙ`σCds\rθςΐp0Rωrβg»JΛλEΙ‘™8ŽΆ„oŽΗPe3ΒΩ‡cκφΖφjδؚ~K²•Ϋ'žΈγ₯ZσΟεU…ΝΊ?tροήOZόF6*‰‘>²oΜ ΘΘΒΓ”CW’Ÿ4€Ω#Δs¨YΈ·ό> –θ5‘(¬wΟοœ|w›ΧψΥy?μΰnBΡBS6i3__Y>nO€`b­ϊ³ωσώρ‹ΆWrsˆ@ϋσ;ŽθΦμ―›‰ -‘Φ8³GŞηƒΚΈ:Ί7ΕνΝTΜHGDΓ;{T¨μo5O-?τΏυHl$ XŠ4’n°9©vDΏαΓ”σ'ˆ Ι­ŽΎε—mΣ»…  Ήu>Ά|Ά:¬Ο…*IΙ—ιΚ»c\€Š1ϋύ+ EΏ‹χ–λC\ιO’ςΈ Šg ,8”ΑtRVψ– 5€©ΰΦ/ΘQ‹ο+vβό&ΎϋG©ώΜ£P5J΄?ΠίΊΎΤ©HRx;8θVΓεFΠ`ey»ef–‚E~΅Αi‹½/Έ§—Cρ`«“Ψ°Έή+‘ΞΥ|—7Βb/Ϋ£-_ωYφ{ε:–¦*ώ†Nο›ωΠ°w¦·γΨ’ ŒΙo»™l¦%I δ 5ςΠΡ¦³βgCΌG3Η΄°Uμυ’J[~Ε>Έθ|ΩσΓΉ‚sGΖϊJ―•Η;ή§kΖκίΞw™Ή1ϋž3;―cR°%ΉZa@Π<~υžήΐICκr=κEλΓ, 5z½sd•Ε†ϋ΄¦δω―ΞgΓ§Z%$βƒlZ%Ή μwgVΓUr­°φj`gPΩsΰΙJžήM-ςJ#§ΖΤΨΈ&lΕωsŒ±‚ Ε*M,‘Tޟ“Γ/Δ~%Μy„H†ϊŒH)bC²§DœiD}YιΈKiŸΗ*a>έzeΓKά—„oΙE$»΄Ρs“%ˆͺ" Ρ»`CΪ8sό²=>ΞςΞΤ2@ΥxZC+ΤHτφ¬±\₯ςΛΖα6΄Xρ{Ε@Ξ!π‡v’Œ‚φ5αφπΒ2‘mΎ¦cZ%«rŸχσ²ΏψδΓύ²ϋ’6‰ŠiΥpΩ\m—z°©K‰#Λ(ίmτ&xpz·§n‰©Θν2 Ϋ'Šn6œΕ’‡μμD~ΏqαmΓ6akκ΅^o§y«@罋QO˜^ζΙ‘Ρ'ω‚s΅=τύ4Δ >i2NyΨ42Δ ³Τψ<Όdj{ς[ΚgQbΚΎΖFΞζΪŒγΥAγ(9ƒ9šό;C(‘HΒ¬A lC[kkρφ‘^|€„]ΝGυ[{oί$u›,NŽέNυΦε₯Z4ˆ "Ί7Œ XΞN:g ί‘» ρι?Ρ>‹l334*7{lˍ8·/~φξRH6?ά›΄RΧ5c°<²ΝDσΡaΩVϋPΩΪνkυσζ:G±“5…ζ·lοΌ97LΨε>šX1˜½‚r:5x·pj³Ι²Ν—Σ¨\{΅3ΑNdΒξ@=ΓΣCψ ͺΑ‹?’|ΣΚ[ζ^zοo³ΗόiwύΨp±λΨ(£9 kΙι7 ž_”„”8έhx»†ϊ½άΝ‰–B0Πu›ož6κΰpτ}s~D›CiSR£~Œ–Π "‹š€†ίΉ_²dφΤ9Η8†J&JŒΨQΚmXύq»-‡+IόΖ[υθ.ͺ ιB7C“9„–½Nί΄ˆΠβj=κN 0<œžeŠjRk\–€>©ψM’½.]48Ψ8 X$Τ»Όδ/λ) ψ’ΉTούΗ;ήδΚuE½ίN,―“ς+Ή°uέj8¬₯/L!€ΕΓ|2ΐ³Dξκθε7uμVWX™q-šد»Γ‘΄ϋV*κ‘Ξ”D&εcCΓ²Ζ›ΣΕl¦#£ ₯BΤq a*­œ“θυ ‚ώHλ™‚'7OΖ]ΉV(KΓ°εhjkΦίKš“ε§žλLιh3ΤσΌ•—[4”ƒh-¬ƒ‘,@ί’;JTLοVˆKjχΨ•r€¬ Nyp[p{ιŸ73š”V_?p€ (‘ΰΒ㩐zdχ‰Π%C…—SŒ·―πb’b/ΙΦ AocCΐq¨ΒΊχF—γν5' β Δ9˜p)ϊŠ·’H©3ϋtξδΒωπh0ηŒT$¨§€ά±$ξnλt» ͺHf΄G›ωf―€ή 8£P£)Μ+ ͺCI$Α‹4λ;Urm‹ε)R•ηί{Κ«na¨©–0aσ±Ξ–ˆ0°‹•ΫΛΆIϋ›gv„\€*»TpUq•όιφn6š…7X=}#ί0]Λϋ°Έ/8wΣνΜΝδvyΪ2Ό,“ؐžθΫ·›ά/μΘΘ9ϊΛccγ΄|° Nz rŒ§vRˆVετΏ7@Ο‚# ΡGy­iX֚σ1•+Ί0¬¨}vΙοπχD ±aΦO­MΪδIΓu*vDMυb!g οKG9υw1ς6ΡpΌ―7εlH=G΄PŒ3Άq#ΏΜ,›»Ωs™qI–ΗkΈΡ(ΆΩ‘9w=¦ζΕ¦ Ν‡ΒtπάΚtN•<ωͺB yq½ 7 HpΏξ™ΕΊ›(m<’‚wΈΜŸ·sDv?[.™Όθ‹kΏˆΑ~ΕJaΌv£ψnO)œί‘pηH~3]Ε~ΙHš˜hΙα–Ξρ§κ°:’•}§”ΛMxώ©₯֊Κaύ= r(ΛΆ?y”rΰd4¬q¬εa‚\WacδaζξςEλ31y\8χk™–…α,Ξa΄!ιΓ2ΰxί %Τ'!ρ'bϊ’(=β(Πfb3„u;ξ%‰””Ÿ~abl*ΈεΗΠ*#ΜΤDύvν ¬~―Γ$gπθf֚ΜnVˆ·qΈ{xσΖ‹ŠΫJΧπ#myJ;ΏπrΚ)^¦ƒωΑjO§QΝΓP&΄Θ„ͺrζ›C1lΫϋΆ΄‰Ω(.S¨Κ)knΕΫ)ηξύŽ-©Ώv“žάŒ‡ΠJ'qοaqΝΡnω15 ήο8dcŽΘ@6ζ1ρ-Bάvaυ9΄4ϋς\­~ύ’’έ½RŠr\m+3β;=“‡0 ΉU4=‘’g††„%yΈ²YΣΈhOν:)ι₯B4i¬ ύ1 O6νΣu?œκσΗ”QέesUΙ_πnώ«έ53x {N_ˆRŽo΅5LϋI#E‘~³Δ[6ΊψZž(‘©y'Zώ–6βGD…5¬©ζγΩ&χ–nά%ο·&;fٌƒFςΞπτ#ΪΓ 8c7Ίώš›όΐ kW&ΚFzΆέ%^ή¨GΣ ›ˆ{q•!Ω9ϋR±ΑΕ{­Έp€ώ&€7Qφ«<7»*&aΥ³§j¨N©αL_6’ΎU?ρ:‚§­Π<€ή›€#ž=ΰ,žυs>}ΙZΦϊΞΒ q?#Λ&uUΟ†Iμ[ £ΌZr-΅ŒK˜δ‘™ΕΊŸNσΊ— w’Κ%Ό/°²0θγm3Π·q!Me,Ÿ‘n pFβ†Œhκ0rmώ α‡ d—ω±Ιες₯ίR{(“Hό‹ϋ|Y4q~Ώ«‰©: 9zΖ‘ώ°KΟυάƒ–;]ΰ0@Δ²Cž;2³`Ρν‘JΈΩΈ¦»Ά±“ΪσVK‡;ΩuŠk–Ψ™1gZ=ΈQΠ QUΓ‹ςΜ 3by©όΝ žΎ/ ”Ra*A‘ YanHΕΟ™ΘΰQΐχ?Žθ*2ή¬΄aύV„_NΫw³Πb6γ«μKF³*W}G+%ʞΰrWp’žαBΣJuλΤυ»_ȊuR$—/ήσk³φ—΄Ϋ/ϊaΎ{¬λn¬ƒA$Ÿ'WφٟΏyαwΡδμΈi8ρYA{ΰΝNΟ‹^ΐ—Ά+ˆuPή¦χόΓd#’‘_fέθω¨y3Wσ―‹Ό{½f-R¨Ÿο[IbΣβa·oξ tXφΜ€$+/»΅„άΩ²°aβπ3ΘΈd<`Άήσ5ϊ…΅Yq^™Λπο‹YΌ•ιΰΆ‚mbΞϊΪΖσώ¦έœ1gΎΟβLgq£ΨJ•RυΨYω,BŸιkσΜυŒ:™ PΣ¬–{˜ Κr’ͺu„Ω);OabuwXk$ ‘Πtσ±txΜo€ή§XgόniLn£΅Ά!W;¬ZΑwΧΖy.&·‚-ͺp7γ§Cϊ|β-k²|ΕH€RAwM;”πlό|c™ŽQ„Τ<{D{σθ΄Υ½π@ΜΣC2ς]ΐ±’ΞΊQ\s«`P’σώv1oΙΨHf5ftή|ΩHζ0s.Ÿ„Ν3BΠ“ψ`2[Rε†Έ–kŠΘΖπϊN‹p\_nm ―§6°–ŸUi"Κ+Ρߚωdwl“ι4L™δ¬ω–B8ΪY)›Š-ί γbœαCΟ迏ήε¦ψalvΨΘ‚Ÿύ₯dl’X ΥΈPλΕυΙd’Ή*ͺbH¦9P„μόθαΛ‡XIΙfξj™_XlfH$%?ηάω½Bcπi-~f‡Mc©›γψeo—Ϋ~ΜΒ@•ΒlDTίΆo~^ΑεMYΊ­ρšΫTŠ7Υέ‚JE„7βΠ# :Ÿk0aύZ°Εέόη˜ώ·žbΊώd#LOΗw"žύp‚²’λ o-ν&Lρ;SO΅λ9«έ— L¬fΙ— œE3C=,δςΌ™˜/c·n²·’¨²‘γΞPΊ8&qχGŸ{l"Q΄O Ί²ίΥo%΅ŒΓwŽυοŠΡμP€BPzβh`”§Œ1 U™N‘„ϊ ͺeΧο;Ρ₯Vή ϋ}WόνfΨƒΛ΅θΛ³€₯ŸΩ°+ΏXΆD‹N '¦³δο΄Ϋ;ΙΛ#-|₯“ΥΡ6½>‘γ &γ*vžS&™»Qλώ,Ξ8–―Qb±€•'y½­ΆEb5ό“¬αΡ‘―Ρέ΅!ψHπΧ0[5‘\Ζ@#{Κ{―p– e¨?©άaWαήίS]ώ‡Ξǘ+Šΰ#b@Ν7e0Θ)0•οΉλ˜~Ϋ …>ˆ`ϊ!€_$Ά›žL* 0²‹ΏmY΄AβC\εό•ΚTθ‡ˆΫΣ₯8·Sλγσ ?ω§˜h<&ώ_υ΄οš<š(8Ρi όΏ«ͺΨ φκΡΖ|ωpe|ΌΞΝΕε΄= w{“Δ\‰œΙy%ƒmbHt―o0anΡ«η—~ΖO©ρ Uιc)!Σ§Β“‡ψtΣj‡-…πΝ€{°ZYξu`€ΨT:yeΪΖΆU4=ΝK‡ηŸo-ω‚#Θv¦ά~†$χΚΠ•Ιym§ξΗ=fσ[@ωV΅°1Ο±Cw0n«φc\ "`°aΥ{γ½υ~BOέΩ ΆF­λ ‰o¦σF‹?ο^PŒήMm8Ο=Z™IZ!ρžgT_ΑDžω[Ζp=}εΙhο”“­fVβ%Ε“~!τρ#€~+‡fΩŒ@_­±½›=B§Gκ@ϊ0Χzΐ‚ΛUˆ6_·<’=>Εd»Ρ=¨=iςΟ­lφγΙ22uΛΈώϋΔ\k²ΟPc"ζf0ΌMd \ P΄ Ψ_qΕ•…ράϋςΆWίs7―SD§Qn˜kςDλχωΐjbk .η&7}χ[ή;Ω}9Ecψ2pΞCκΈόΆnxxΪ\έώ“C½wo_e:τˆ†+k2γΕ±k:7&mE a¨r4n;—'ΤbG{9ƒ΄~’_Ρ{b1β>cηz”Y•XYB?2³ήΫ±τγšτζV]„’Θ?œα‡GΰŠΘzm%σn'ΐ‚v£ΙS—ξqSΞ Ο FΧ‚B3龝.E.^Γ4sԁ{MHiΤ§³3€’UΨΝ² ΰέW„#΅uZ]Χf†Zπj‘Θϊ¦γΐS;¬†C”Ή lU3Œ%WdΎεg6Hq)€ZwΉŸ™φΤgf^€%b'€©p½ŸUτΎ8π1ΓΟU†ΠAN£[WΏεέ ;eΨκj{až&[ ²ρpŒεGžΊˆ²g»8E}aNΤ_»ξΔk†lΉ_§|Ω »υ+ή(ΫW¦Όξλ"‰–έΗ4±Μvζ#•³*φΖ’"(φ›₯m ‰τK^ύνŒTž‘\’`‘B―·!RO]­=`βΉ!:ccBυ*¨ ²uzS£ΦBPeρ₯Θ,μF·ρ’yΥAO‚ί5ȞΕWΡ!½Π;;sΑ9nωΥΞ²ΓωBΈΠP€Hφθγ‰(₯ζδ˜SLέ΄­Š‡Λm₯ΑhϋrΒ1^JιuτU1RͺΔF›*K}Mƒ£Εj !Iθc@ΝΓ/Σ™ΣhοŸ˜ Q¬™> Ν¦2TݜδκΡ}ΩZ―š“> bg°|oη%jτώεΫΛΚK5’Ii΅€zgpεΆφ# 5 |mϋl Πθ‘” χάSr|mCdοYuPΠΡΚ©ƒzliUΘeο²¬Όύ$+dγg€σž• Š”ˆ"Ηπ(!³–ΫΧγ ω«ρ­ιl@gkgΕ―Aλ44Σ•Ί…žW„o{χlΦτΰν1fe΅ƒ@όEΦ«Ζ<μiΰftt£ ƒŸŒί‡ 6†“„r:κ ₯7<$₯ϋ‹ΦΪ endstream endobj 538 0 obj << /Length1 1647 /Length2 12497 /Length3 0 /Length 13353 /Filter /FlateDecode >> stream xΪ­weTά’-Nά₯±@pwww‚†Π@4ξξwwΑ]w‡ΰξξx|ߝ;wΦ}oώΜ›έ«Oνͺ]UgΧ©΅šš\EQΤΔΖ$eqddebα(­œΤl¬•lxΥ@fN²Ž@+ΐΖ‰LM-n:‚m @G@ dΨΨ¬ΌΌΌΘΤq[7{°™Ή#€VSMλ==ΓΏ,ΉŒάώ‰ΌE:€Ν €o?œAV6ΆΦ ˆγΕ8P8šƒ¦`+@\YEGVI@+­€ A@φoM¨8Y `cΔτ `jc°ϊΗ`l1՚Σ—¨p°ƒίΒ@Ζ ΫΏ €-Θήμΰπφv˜Ω!ŽowΰhCŒ­œLώ*ΰΝnjσwAΆφ6oΦoΨ™Šƒ£ƒ±=ΨΦπ–UEBκu:šΚν~ƒ6¦ož&6ΖN΅τ7φFσ†:Α€#ΘΥρ―\F € ΨΑΦ θφ–ϋΜΦόwN`ˆΩΏ*`ΨƒΜ€φ&V ‡7š7ξΏnη_}ώKχ@[[+·Ώ£mώφϊΟΐŽ +S&dVΆ·œΖŽoΉΝΐdζΏζEbj`eω‡έΔΙφŸ˜3Θώο ’ύkf>½4±XΉL@¦ΘΜJ6Žo)΄3•™ώχDώ_ψEΰyΔύwώΛ#ώ}ΟN-εde₯΄~€μΐΫ’Bo» ψkΩXν-°ρ ΄[ΉύwΑξ­ϊGΥΑωοπ?RˆBΜήbdεdβό‡μ v™¨€Ν¦@«·ΛϋΫ 1Ω[! 7‘Ύί· –Γ4ΜΑΖ–ΏΤΰό‚˜ό{oΊύέ³Ά΄–Š*ύ³mvVy› G 7[ΰ?2i)ژόηα/*11W€#+/€‘›εν1Ύ=G^6―GΪΏ‰XuV:Ϊƒ]z,L,,¬€·ο~ώuϊςo4’c“ΏζHέ1y½4ό;ΩΫΏ)ώχ6xλόŸηΏδ 2F^˜³1ζ΄HIOu¬ΒΛξ‘ΠϋΥΙ Ϋd[\«Qη[aΣα“ΊΞϋΓπΉ2ˆ©nŒο₯ΩmφΠφΟΆέΞ@'MGθ4—Ψ‹ςSWΖΚΗVnϊfƒβχ©GZg3 kpΊ\,Ÿw6FTΥ ŠžHΖZΩνߝέ}ς₯tΞσΕ¦Ί΅Eσ6N‰ΖiC―ƒΒ¬Κ?<ϊΏwKΣ3ΨίΧΫqί΅MDŸDΝΔσN<$Opt3΄Ώ5~tζvBqyϊΗ‰B‘b#^ν>υΐζKΫZ|0¦/–μP»`^Ηβ~Φwm..H !zNγφτ“ͺ’Œ2Ύ$V₯*ΏΉuckP2~4Ϋƒ·₯w‹―"%f,2Ÿςτ–ItΔ},TΚW7ώφ~ΰ Ty)$ϊ_xžϋι»SˆΟي{z ݏ,η)ΡW1/x‰δαύγμ§YM©Ϋ3c€SΰvΡ€‹H\‘yk³‰ΧρA+l[ μκœ€JΥQΔ«ΗψͺG_ω]7θσ’oyυ›L’)|§ΈχiœΪι#’§g ω²T+Ϋ_ϊRδύ•ά"?Ξ ΥrΘΆ‚ΊΨΔ Ρ—ο~o:ιZ`Ήη­1έ…[pφΗΘKέάΫ0…:Ϋ³=«χ*TQKIΘ4΄7Φ-s5οΧ‰λΝ^2‡/Zη-–»βKŸ“΄6Γ‘G7οΨ~ώ¬;]’xλh-€zΜ aΩμ@!0’UίEυ- iΤ― _2šΌs-b—r40―§d²E2Ψ/4ϋΛς―ŠxψY)ΧΞθiφ-;Aηn7h­dQd+Α=β±n\ΕE•h‰ΤπΰIΙ¦ΘΉžΤyۏ_ΪwΘtλδΉΚξΊœŽ‘ς΅υp`9)<-_Ϋ©―ΠcΝΣ gˆ_Χ` η)$ΛWk¬2ππΧ՝-[ΟiΏTξŠέ^αω ™ιλΣ.₯RΡ°¬TL‡.Ή£Θ¬,ΏFhΰ‘²»m”XBΉR7Y κ‹9X―β.2ΚnσΔΐΠΗIVQςθZ¦J»Cγ).՘΄Πv[Ύ]ϋšΑΟΧw~3« tύ“†ΝGb‹NΪ}|ί›‘ηΓΈb=‹ΞΫΠE@¬=oŽΗΉ΅bΏiuρΎ‹\‘ŒΕh{Nζ™βΫς*ΐ΄Ε_(q2u+Ϊ2δ„t8œWBϋ&9δYΊ)Q7-ψ2ΒZQΒKb§ψΥ“ΦΐζK.5V ~“$ςPi"`£P>LΏ€nμh³ώ’οΫΦ cΥΕiπΔ_γ/ΨIΏsΉ Ζ· Že@λΨ‚φ½`{Ϊr領8Θl–˜―kβ|ϊ=4.η2ݝΘεiΝ&VLΌ’,w7h°˜Θ-Puίzυ•‘!©Χ Μ(ξŠθQ.μ:λΚ‰§FλtΠψ‘Vτš4ύAn‘ιdγζΝν8$Y&όY¨σΗވΑuΥxΞM–νmQκ₯Ε°–έW†’ΛŸ³ΖΙώεδκ%š&„Mβy’“ήωsΠu΄…ή@wΩr"Εtcγ‰οΟoΝ¨Mr <U΄Ήƒ»J•”$/Φ/7’K¦‘ϋj3tν‡iuG¦užΞ ±a*Pͺ΅π*μ ηͺΊ(£―Ζχ ΐ@Ϋ@ b΄ŽΚ)iΘ%ψ“ίžΒ»Ζ-‘ίρA„s ΄) nl(ƒΝy˜/ ₯θj†γ ‹§g©iΜw~S:Y|~΅šk: <.΄‹\yŒ¨•Θ αφRƁΧ;yΥX*~Δ,ŠΛ˜ #q~έ©[εj§+ΌNSߞn°˜§•ŏΧ$‡&1Σ ΦΖΔ[ΝՎΏctνϋΤdΡΝιΖ -‡ώ­τJVΞͺ’εΆν*–Χ=έΆΞύΈαΕΜ)”M/\Χ %―vzVΝ·ŽΝxά£Ο”‹Χޱη™φ>=θΊ'NΜ&ί8”Ω~FL¬`Οϊ«1°’ξaθΈ4εŽOͺ9w0(§΄»Ρ%υ8N‰FΈ₯ΥJζ>A*ρ―t”ώΰ Žψ“ΤωuW[αYύFΟ©}Λλιig‰θΉ·\ži….…]όcVΨ¨΅έMΖΒaUνΟ93Šw†‹ΠI+ςhaG”iτZΙ€j‚—¨±9SPώφ Ο}{>ΞδΎΚλιΘlΪ'#‹l° ei’κλl”,Ϋλt£4J‚‘$¦Œ>Ιg(Ά`ϊώgΕ/φXΝK±`ν‹οΞhƒ°%΄Œc[AVΟQΖυ]†€ žο£Ο h£?ΌάlMζ&© «šΠιΐθK+a€ΆGBJrκΟfό'Ÿueή3EPM;ˆ–mτCΦΎM{~_ŠήoΠͺq\_φ™„Vηxπ₯f4힝߿Zؖk+—λι~ Δδ\ποί=χ&܏‰p Fͺ’*₯ι˜ƒpoώ2½ΧπžΖDΉNŒUξγvνbύj£΄6wUd‹ω)ϊΛ6T5Άθ γȏJΊMΏπ((„ˆ „©•­ϊeΣΘi•oc –ξΟr͚‹ˆέŽ7™Β·΄Ή+Ÿ&Q=@υ5ι'~Έlxβ«"]¬¦)Ό‘^&x?”Ε“™΄€6y5b„Ώή\π/4H(C§³γΙνΎƒ΄²6„ΰ"gœSŽyζ§ΦBΈqχΌ₯Ιv›=ΓΕ}±oOO뼐΄π Εi‡š3žͺεξ8@ΎSe»Pέα3vކ€’ #Η·ŠΓ¬βΤςήwHΜ% αΓΝ†›TbS‰Qάϊ9ΕΑβ8rΫ²&mΝ‚Ϋ+ΐΗ0Œ±p‹VDՊΤ2μ°ΰ‰SKJ»*ηBŽθ!/;£όέE&#€Ίψΰ•"ηŒάžXΨ°;T0šΓM"Ι :ŽIω¨žN$yF*”%?—X»lm¨ˆgŠέΔλ―2†ςr[ΈΞ(P&pόQ€5=SΤζfqΨ?kΩΙ»nV¦Rδ³νακn“ _ž›Λ@ΟYͺ‰zΖ]‚ΰ$"δ¬Ο=,ŒˆΒς“ΰx>sw­―žM"’,•—ρ°mΏΚZoškgRkXmEρ(u^ηΊ#B―w9·{ O¬Ρ [γ4xΈ-πΥ¨8ΊcάnaB΄r]ΤF™ΠΙ ω‚Ρφ#Χ…φC?ΙPΌŒ#κΕTέι`Ύ a§u`πeίήigk…c–ςWΜSαe_,}gpZ΅ ¦v 'π£Ήότw\6ŒšΕŠΠηΏΎίZΉ¨CgΓD½9°P Β­>0π•ϋΰdό²Κ~ξHψ~ώBΨΏ‰ύ,oS'j\έή³:“Ύ _ÐUΗ~v°RXrƒIΐε ‘u–'„ιNpoσ ͺ›lΈΩyA•ήΌΓ–#œ\] ’[Lόƒ-Ψ©¨g ώ+)ΔΚ]Μgpόι„“>Ύ5N3EΑΒκb½Ό˜ƒε‚(~“@r.ΈŽ˜Κciώ6‹ιŒΘ!2&Υ:¬BιuόΌ™μTΥ΅‹›XnRaGΏτ’ιKMΟv„ ΚCΠΗ'ΊŸΧV&¬N›rN_ν9},.Ψ±κΗfw?/ 05γβ8ς‘Μ}΄›ο·pP{όΞ()fDi3³,ε„Ψ΄!Y} δ‡ωS°‘Ζk”.β›m[©μ”Y†ΕψŠS’Τπ=mq υΪy₯‰―xη‰΅±zH†8SΚvŒΐZoD2ΩΠ :7}ΡH*₯χI{Γ⧝Yj”ΣER€§—mΐ|¨λ)€λhϊ~灐jΕ[Η)I[ό4όρ Œ˜J˜+T/jŒ C. q‚μ„X΄ΰfsƒΟό[ƒ¨Ν’ιΓp΄JΑ‰5r\g”Ό›‘.£΄fW%9 aC·¦ƒςŽ#Θ[c°uι|ψΞ0Uu―菇ΘξŠ"]!vζ%~’Ωy―αg|d>φy†nρΨ΄?‘”ΔΝzV%ΠόŒ©e†ζpm‹Φ;λωsI°5Ί¬·ΚΘ"ƒ€.Y«œΝSΣ\σJύε‘―€h§{[0ΛMnUœ>XE Œ²Rzf φοΟθ~ώ’ Β'ΡΈΈ²‘²κšς<Ά±ΡΡ qe$VفΓv”…Š x/β·/Γ8^X'…=3\b^F/8νZπ=ΎfьUπe#V@m^ύΞbBL0ΣΜωμΩ' ͺ „οΚ€]Ν΅έ!Τψ{κofΈJZaQkK}φχ5ψ»ΙPG»Φb5όŠΟ0)“p'Έ₯Τ4γηW“²\Γ α ,.NV #n7.β‹DξxΝ‰PjRΘΞ½ , ΈΡx©·n-έgΘΉΎιΞψX{ ²Σ2~)tޘ²Ξ-½[l"κί쏴޼DpwΟIν| ˆ Φ„‘5ˆϊΠΟω½”€²Ο<ρ8›RnK’αht^°*”σšEμžξ8χ WλSΆμ N9Μ0<Τ‚±|Ύ&bΪ`δnύ•fs½φ’―ρbΟΛaW+uί†Ύhbϊ»°«¬zNŽb°DμͺΙμ%)#`ƒ;t§oό@΅ηkOlJλΈΛ€ΌfYέΦ&όπώΤ(EuκW|¬©’eΔ2ΚΥ&$MaΠ>&«7³?ΕΓVήρθΊW, £IwΘ™•{N’¨SAshύA'΅¬I&mx€1»ΏΐG1ŸΛ‚uLΤjώ΄#t*J¨%–Wc0ƒγπ,ΩKΗ©Α ~ΆOΎ~Χρ­P±§U4GίQ‘·ΘΖ‡QQˆξf1xΎ&e ¦€.ΔyΥ Ϋ‘%ύΦ(žStcζΩΧTόy€]žιΠλ3y Ήt‰ε"ά ϊιYNηhΕݞ¨«JJ"sΘ Θ½Δuϊακ(γ±κ!ν{pTEџλŒIZXLΝ¨ώςή„€­ο ΉΥσdO7ƒ΄ŸbS₯οLεγΧβz™^ϊiΙbA”IΩN„§Υ;P+ζ@)—ΌH–ο²φŸ‹EΣΉjπΛ―`ΊΫΫe ΰn;Ğ'dΔwj—HDp΅‡E’%Ÿ•ο?FcXΥΨ’ΙΉμusΞψΦησ€ ΕH—:ξΖ‘ξe°ƒτϊ±y 3©ϋ5m†λ~ΰ}_†ƒ±m ‹uHύΰΪΛ|ΏμjΰηfŸΕ§εw&_j½φΌxά3Eλ ω…Ω΄4}ήMΝHP‰›ZiΙiψŽL6aΩrΎVx΄g] άΛ8ι2ΡτΞ­?—ΫΞŒε|ΟΎA~™kEkH>K5*€/w§TDπ#ωτΌfΆews\Œ:₯ayŽe.νRb]Ζφ³νœΚ‘‡Φ1hfp£epκ ’9Πζž~΄δ:ήb¨'Ι§Ϋ.Ζ‘ΒΫ˟ˆβΤΈ0ŸVήW”¬Œ³νε7(ΌΎσŽμ£ε”šχ“gX°0hLψΥLw› )μϊ”™ΰ]VB~–±K@χ4ξ˜g3~ ήτ3Π7fςCΈ¦°ƒχϊμ«j§Bͺ'Χφlμ ‹λ͜Bΐ‰Μθ’Q’`ŒώΣCayKΙάΈθŽ/hVGuήκ#½Ξ»ΖAϊΣ±xn|S#³`₯X‘vV  O7KΈ•I*GF,sͺΉdQμΩuπ₯ΤIH$\;g¦§‘ώΩzeιΦraΨοF‚ωΤ z=]ρ‚D”ζΆ¨©šΆϋyŽW€“―:;fhϊώsRΝ[Ψΰcβ{%LΓΓξ—B(ς6LαƏ[Ύ_βNNŒξϋΐ”…ω„Ί€°χζ₯Έή₯4ηšf!u,Lτu…PM“ 9·+‘_’ΞΞ‰‹9JGηλ}ΠNXσΘV읙EΏ)Μ:Ώ?μF ?N•˜YO‡€\s°‰§Π‚‚Έ eσί»0šΕΩ‹«CΘ[ψφμς.:‰ 5ύaΓKV%nΠπΜΆ΅' «Π²ί+ͺαQή:”dTΊ …»ψπmYπu}Ν€α~ _y¬h΄²£BΒ‹U<Τγ?¨'Ϊ吝I;lP¬qφέ•Μ‘VΧΧΩΣ_ϊ§Γβ™Y­ΔpCmΎ=ޞ2ΐΚ}1ΐ \3ν" [ΓmfΪl)ϋΥ¦Zr‚}—ϋ(jiPT·v·΅MT‹“pνr ~ϊόΟΊ0 Š!ˆΈΒ“AHΕC²1 ³ρ²Zφ¨λ¨Š]ΪΣύέ4 °ΐΰwΏ§αό,3QŸVλH& bBΎW­ͺΛ΅Π/ ‚Gˆέw»Vœϋ cˆώ£“{αϊΨ*9Ι?[§–±n?δaρc;•υ*+υΎ[άτ εΫ.Ο"ZmΨ0ΧΎχ‰₯Υ_Aε9Ά t_‘όRƎ‚ςΙΛΛΰ§»μΫΊ$κaφuμ;p$WkάOΠU@akK)έwBνjM—u€9Ι†ώžΠΜϋ›μΰDΘ’ϊΰ…l„‘o–»˜”oΏCØU̝Ψϋš:J.ε ͺ0:«]ν)‘•Q’N.9Ϊ8Šέj’kRxόο©XμώΛΗ±Ϊ| :7,˟QJ4(₯XJCΚOΈbήΝ’m=©Σgσ’ν7¨Ά¬ΉΧϋptE4~ζVε+6*)‰`rΫσnA™€Fl’lI Ρ‡’d›KˆΑIˆωJ>vλMά ά§₯ΦΏd$˝y?aαnu)B…ͺbδYέ/ΈiκHΪ;.;tΗeΡ- GWΌωφξCτΗ`™ASvdΑ|cv’%9rWψ‰αέE§ο%«6 ¬Zζ>» ϋ'Β±5ΔμwΛ}PθαžpD€$<:όvω s $¦Ύ*5ϋ&‚OB"nΓ΅4±μKρ‹Jώi,Υ[l6­πJT£ζJNU'‘^ͺςDΓ1Όa'+vͺn·ˆšεS“ςέ:οϋΜύžζ–ζ §Ύ/ΥρΟΗγe‡κDΣβξ^­M¬f(»ΡάCι:t‘ΦΈό{θϊ.ΰ=έ„Η#FΖGΞΩΔ?) ]τ 6‡F"0Ÿ,H_ŽΎ0 ηiΑςˆ: kΘ­m›1Κ%OZšGŒοΣs%Ι†ϊ©“Ψ— >‡JΝS«†dbŸΈͺ±υΛ{$‘ύxκδν-­'l €Μ%ΎœΆœM~E<·υψNhΩχϊΘ2čκϋΑ0±λλ/'x2Ον`{Ÿ /ΖSζ|Θέ5F?a«Ν™ϋΊWϊ+ Ύ$'΄C&₯`Υ#+S`‘r tn¬~"φΈεš ΅ qBη ͺβ€o„ψλKΣe|qW!BƒAΎz7%Ρ‰ ΄œΗυHAGΉ·βγͺ·S΅I.τΉ;,ΰ*θΎΧ=k ›έ΅‘vϊΔΌ6Η<²1ή΄;htJηΧ™±ϊΡJgΌd_GΩh’”aΟΰΐTT²α„6ul£˜‘ργΜΫ…3‚'Υ Τέ›$ΛfεO~S_P2 wΜVΛ uQοΨ<4`Υa=ω­υK…IΔιpRω ύ₯̝tΐxΩ¬€φΑΔ*™ο/S5Λγ7κE)"όςŽuœ°ΣΌ₯£\HHI―θΝa__‘Šόρ{ΪΩγ Sx Υ:ON£Ξmυ°xζeΔ^iψΞsΰ©x”«:+yΫ‡fLQDl.v­Ύ)’7ύΨ―ΫΙvγΓπ0΄N Ε&9! ψ†γ’pΗ±*dώ> zϊ'–C όψlΞ M傦Ž^ΑΘ'7—³τ—Ψ %}hΝC#”¨ ξvgͺ@fΖ‘ΗW™Ž%}9ΨόΒθν+νήst·c{=gmhTΓ 6Ξ-‚š[V4ϋƒSρ(„όΠ+-vρ°£¦ΧEδY@jμκ™Ύ»lύΓ1Οe ~rVϊήc)θYT4δn]+œκ†~θTW%£9ΏR h€y:ΜΜΜΣ~Ψq SŸQψσw”!l/θΗOΓ¬ώα_Γε j!ΙωΚ…ΫŠθζϊ Οn«v2ύ,ƒόΕ νŌmOi~ώ«ώΐ« †­ΒWsςΎGί"―މB† c.ϊWυΛƒλ‰$wώ°οεͺlϊQσŸTlT­œ„Z}[K?Ό{Νp€Εu΅ώ•QFJ}ωbu`Λβ™ΰW«zkUzŽΖϊi2ϊqν7TšςτΞoM”C.ύΫ‰T1JrύQ9Φ±#άNΆΡ£ψ$Κ‚Sρσ,B›1’χσι9―΅¦“™©Χ„_ΫΞ΄Οœž@Ι»Γg@j˜qŒ“ΥΎ”ar'–=²n―KωΌΐ_ό$5ύaή2‚ς—qbf»zUΏS3σ Ž‘¬%Šώ³ŒΎ{ όΆA­σ̍ΑΧy,ڝ―§ηM›Έ΄Q7nήiύΉΉΞΩc/υΤ-—'Ί¦ZΧ½χm^Β —g¬δ2+λΟLΘϊJϋΡψTϊ°q«^jdΦ(e_Τ‹|ΧnΆΜΥt„¬άΞ %°wH·Ξh† TΑΑ>υe¦£½@ηφŒ ˆN3βο$EΟr jΑ(υζz}&ZDˆlεjΉρ^‘Νμ„·=XφΈE΅•5Ά*ήη!+QήΉΏœ|\SYΗΈmΆ’²2€ω'ίΉ¦τB*zIΉΉ\y»κ§½/›ΧŒ3φž•_.BΩK…ˆTmΫρ‘ζΈΰeΰί` χPΎ zηŒvUm₯υ^^¬K„i/Ÿ)ήR#"'¦g)³ΑΞ ₯ά”"γΰ:-&MάψHX¨;όU§vΔσan1.εφωl0 šΨ㘡 7²§#hŠͺ=d.ϋYα §§΅y#ΌΨ4Ή-ˆ“ώ£›K|&‘ξ\ά ΕιAγΨ+ΑŽr c™$'^Ÿ—ymuZ·Λ; οΉ©ξDKΪυ‘Ϊr…!%ƒΑ«ο”@όΜͺσ`ηbYDήŸ/uz’ίήΜδςΉq[B¬ΡMΑέ”UΜ=*ΰ*†΅Cmυ»XV§ &Ώ4—”!‘ŠαΊΩZ[5D™Φq¬NjΆ ?,ωό'άw½…‚3Υ­θ4Σ]>–j ίΒρΤ9Fχ"Ηΐασ<¬ΐΐή=AτΖD3F³{9;Ο“ΪιΟdΖ<ΤΈτ˜Θ?uœΗ–[ψΈ/(ͺΡ†ϊs΅tˆb†ŸG%;up{ΕšξΕ”ΆE•]HB)φ­g1S_…L|μGE6ΡvZΎ`’Ί –Ÿ²ΰΕϋ”Oτ¬Ÿkq~Άp?ΰ7Y˜ α£ΰeI^-:bΎCΏΛΤ?«-ύž§«2ΰλΫφξΤΉ{ρ\MόβQΒ€F“c8GCΑ(T‡œϋψ²AJΪΑa>χρL'1‘θΧϋ~/ΩƒΩ5 Ε‹Gν iߐ£Λd$'g~³:uˆ˜±αΆdhbAzfωͺZ-όΔΤ=Gΐœoτ(#ψΓ]GNŸ|t°P>IQ~*J§j9½"Ν ‹)¨fΊ-ΩN bC{1ζ·μ’Κ%"·I~Θu@§biyτˆDmη™kω1Σ,™!WnΫV–xŸώΫΠρΓr@ΙvΒ°ΈπŸj—»gΌ‡U76tl‰Q„w­›O±ά…zY4όΌR†ΎF|όRb>ϋŠQŽχΣ'λžŒ,/ μz¦ΈςœΆ^ΑÍDžςΫ“AΉmP/wŒbέc4T\εΑoQ³1t8τΔ%ylF@ϋ|8cΚe[_9C‘0Γ'ϊ܏ΥAn™ΤEž΄ε4yιγ5ν0wΐ›7»ψψžΧϋ$‘)β8\ΞέΪΜΒ0Ψ9eQOψλ ΌΨ―'Κd,7­Λ ©°ufσqVŸjdεΥΆ΄{1>Tƒ—#!ΎΊf0vK(bQύ³ζ[te%Ζα5? ha›<(k —*ό`Lžί²t•|Α·{$)Ÿ„_Uω!Ψ.ζΡuΉΚ Ω§χ˚zά•χ"λ%RՐrΰώzγΒΤ›I$£ς½°΅IvjόΝ^―7%±0->νšωTM™bΖgabΛξ |νΡ—(ΕtΙ 1κ¦oa„œ+rΦ³"“τ0ŠίLϋοœ‹€“ˆ₯½ž{ašΏ.YΐΝS7ζǟΥ:>gΖκ8QΡ‹ηIΊqνu¨£Ϊ+Άέΰr2}cjo‹[ήFAήΟ”άZa/ρχف؟A ΚΙΗσœ―2#Ώ’6»DεΜδ =žX„‘PZ ƒ:›:ςδΫ΅eY½Ϋ"œ³dHC>?3“έSab―4_ ]3α—x2Ί ΙDΰχ•±ζΏpυd"κH°ίώH‘8΄Ίσξ’"Ι™›‚“0ΖH2έ nUΏ"iR„YΊΜΚ3ΙbwφΉ…PS΄Η1ΫΉαΗF—ή±’ωΨ—Ρ3~Me #R'³κΦg†AΕΑΪ9¬˜-‘Π¦ͺ… hPQWͺx+KIΕy» Ξ₯},‚†φA(ΰ°Χ€#-Θ’ͺΤ,HδOdΔάP‚.Ό_ˎgŸϊvf&>9γΐL~₯)”•“φ‘œ<.1%KkŠLŽς qπ§^Ρι~<$CΰΗΐ‰ψ©ηΡέAΖΒ½οΑη¨,δk§όBΤΙͺ,Η’gD«χβxΏϊα$₯λŸ!’OΉƒ·Fω_ωέΧά ˜΅χΖΰ² ΝB©ƒA‰1j?0†œŒΘ΄δP§φ”ΤRΏ-FοΦρœΎήιN«xœXυPίPšΝsΩχοh‚}Z-©`>£Fw¨“™:ΐ”ΜgΨμs%ζ5Zw^χ‚ή-ŒD";ΕJυΤ=Gamσήl‰ ιΗ²/£>rόAPoέf§I k‘ήJ΅|qφD°¨‘V!Ν59ΐ,nn\VΣh°υΒGFΉ₯>ͺ&-3zό΄¬ΠΦΘ’gSg*Α3ΩέΧfψԈw²“ΥH2ο›$u_“I΄ΘΆ°•ΙDF6€ΣΨ±‚Σ‰X›\ ""οΚobeΧ―rΆΑˆ[y^Λνœ. ₯qΊ`ψƒzŠβOjνDΧ_6gΛPTΡ,ΉΔ³Ϊ6—βn‰¦μΞ₯Λτ»5Ό†wΓ# Φ³B°έρ+PmξA¬Τ‡5Ψd³_ežW’§”¨ͺϊς~+S†€‘Ή΄BΏΞε%iNBς]’*Σ&Υόρ\ mΤΒwtzό‘ΞUJF *l&΅vaΨί#ΎsaŒ˜[FΣφο½ϊρͺN¦ΗΚ©­β0wάΒM*e%¬ρz₯ΠΐkRRΗθ½°Ε*΄ί»°³ΠύΉ§Φqζ–ώӍH«hgz|Ήητ°ΔΦαΘx”°8052©Λ†τ4Ηβ57NΓ©’ί’΄Gl.–γH'­‚’O`‘€`Ωλ=”ϋ+«„Α-Ωξ"#ΎqΫδLVαΦύχ―nœ¦•躐$³Ή-œΝ‰b’L΅‚R~F΄Ϊ@—a\εxƒOΑ›J•υ‚Αθϊ©n\SOΐαΎ†±^ώ‰ώ“Έέ_SE6F ΊΡEΨ₯RvO*kιk&ΦΠNoNnR όψή©ΡφՁΰΰ‘ΛΉαΨZ]ϊœΫz‚•^FiΘ½]«Okt#ν*ʌAr\\Ρ³ΦΏx3ˆ΅?>Ζ1b·ΤΥόzμŒBΫ£³–£φž)«RΘ_υ)žx^zάCΕb5²έ @:β“Ν¨}7†)ά3₯Tώ݁‡ή΄Τϊϗ’Gn7Ξ‡3₯Nδί*Ξ]‘,ΘtW­Q­μ#«œ§,αυ(v텝½―ίw½y^aΪ«1kœzΦΨoY•‘΅%…,<|L=K Ÿ ΰ¨R£PU΅«λύΆΒύ/ΒςΌϊ›Sϊηη‘’ΪέΛ„lνZ] υ ŠIνω6h§g„ξ«nΦ¬-ΎΨ|3v©™ζ=Ζα&‘Uΰ„πΡ)(jΉΟ[2λΓJΈc嘻gBάΧ‘'α‡& βe=˜RJάK.ͺMq°σύNκ ώkMv]?3σΉR΄μ Τ@ΙΞ?Ν’dν„^ό3JA6t#9Ρ%ήξk]δxφ^Ÿ,&΅ƒ±7-ζάΌό…vΆbP ΥΩD–ZΩĞξc Λa+v5c֊{œΚ %jjU> ―?Ώ9‚ΥdyPeς― θ^έZκΖΦHΘέwW—"`ΈΫό.šκlήλM7<<6όxFω“¨ΐς;l ³0󠞿… endstream endobj 514 0 obj << /Type /ObjStm /N 100 /First 898 /Length 3238 /Filter /FlateDecode >> stream xΪνZkoΫFύ_1·(ΜyΏ€ @ΗivΧ΅Σ8©,›±΅‘%G’Σ΄Ώ~Ο>4’iΚNU/°»Hθ’3wΞ=χ1—€LL0$“Z‘UL9‹V3=ZΓ¬¦ϋ–9§Ρ:ζ#έχ,ͺ€60)lD'2)C˜(HDEˆ4s’bB€‰šI/:†Ι`0 ’d 4Ψ1%%ρL)Ι10₯ΞΔΘ”•z`…6c™’)ο=:@₯@p…Ρθ¦₯wθX¦΅ˆθ8¦Qθx¦­§ιi?·u0b u$mTcF …L#œΔ8p Aˆ†L#q_;šhg˜Q$”xQ ƒ-Qƒ­DΗāΆ£p_“²ΐ Έ4Τ$–5P*ιZBIβ_γLiLΧΖƒ,,‘CzCΨ‰" š­0ι $4³*™ WΕJ 2”¦8P°"YΘ*˜Qhκxζˆ}«"σBbυJω‰Έb™‚:žf138¬le`!’`θ¨α¬EοheΝb€@ <1B™_ˆd~‘Œ!·@Ο@΄VψΓP\^CΈŒα‘s`όKJ)Αˆ"?• =λP} Wθ@ '"΅ ³p@—œυ ”Ιϋ`gt ί‘„€΅aIΠϊ!π!λ¨C~€£ ZN)βΨP‡ΜHƒΙ 4 †˜¦Αδ^Š[,­h°ƒEƒ=`ι/ώ ψλί―JΖO&ΣΕ€]X€σ—£Ι§2•³ SρžΘ_π§'2 ψayΊ`':ψ‚X0.δ¨:šטρ±ΦcάcφθγGŒ?ŸΎž2ΎΛώv5.%άΒ C Ppφ‡qT …5T¦`i›κˆ‚jdeL ΫL¨tͺ νŽΌΜ€¦ƒή¦Cb-C ‘ρΘM€έ~šlAΤir#ˆ­:D …Rί2Wk! ”gΔD½}+ B­ΩΎU…υζA·οC½}oΔπWlί-ˆzΓΨ BmŸη [Xα6>pΫτEIΟ^Už2E₯₯'°ΒΉ>‡π[δΑK¬ ς 6•3A£…qC½ˆ’OxtΎOeDα£~ uΊT2ΐT›.j‰ Cˆ°M&‚όRΐνς9CIW„‡*¦šL%±8jŠe¦‚β©±Δ_°{I0βSeW™C"h’Φ’6‡τ䈲5‡DQ‡‡σfϋiB’Œ³F5iB’’J=Pš¨₯4pΓ Ϋt)ρ°#\+θ.;±’2ώφέ―ŒΒ¦υ^τΊkr=ΏΏm¬Δ^MƒΆ½ζZΌ7,€=­ιmHš΅‡4FοJͺ<™ ϊΪ°Ψ\§—@(WιΒψΑlzzTBcΖvχ]~]°΄ΞIh¦ŒοΟ¬ω_F‡€lLο₯l*\RŸ‘ώ:OΔΡ=bΊBcΔ-ύ΄΅T}£ͺ5θτJ,“ŸΦΔuZ‘O-Ιoζl:9$£9θzΔΊ•θη•jΟ©OGΒ‡GΊOϊΎ|lΣ¦΅¬kΟΣ8ΊŽk /‰·@₯ͺΕΥ`€5š6׍ϊt4Χ}=wE?βΌ–ίΘ ŒΤzΞ9i€XgΪƒξFUk€ΡΉV& $½Υ¬ξ'$Ζ5;9sς›>]oΌ"1κbΛ@yΣζΜ΄ΨλΎο°|ƒ1g.c xJ'wυ˜[9ˆρΪ›όAžp3?V±ΨXSώτΖ·Ι"lΜ΄Β·ζρθ©ςρM}΄τΖi‰fφεU‘ΥΚ}8!½sΦΉ‰iΫι%h;ΌΔΣΜ&³έi—!νk«[k[x ZΘ\€₯Μ‹?½ΝŒφ mF‹ΦΜ­IΙeΘά΅IˆώD;ϊt―5?7γj—Ιε$Er‘ΉŸΨ WχLN~R·ωυvNέuΏΉίΞ©χχζήJ–]–—T‡ν–σΣΩθj1UuΩώπwv_==~σφϋ“Σιd>Γ?Ffη°<Ώiΰxx>g¦šρδΙτ+C5Ί#ΣΘTιΝαό΄D™'όιπκΗrt~‘Nh½ϊϊ‹Εp<:}<9—tz΄(/ίκ€Ώ­‡λ1ϋb8£οoό1Ÿς]ώŒορη©ξ}Ι_ρ}ώ?ΰ‡όˆΏζΏπ7ό˜Ώεοψη‹r6šβψ‡αι§ωx8Ώ@oΖOωιt<ΰοε吟ρ³ιJρ’§Eyωωz8ζεΧΣρπ’δG_Jώqz=γηό|6όRo0ψ‚_ό~uQNψˆ‹βc>.ηs~Ι'|2š”|r}ω‘œΝGη>εS\ΈβWΓY9—Uo–ΦΊ*gD΅£ιΜ?_—σΕΰ>_OεΩ‡1Ÿρ9Ÿ——£ σ|τ•Wš,ψβbV–|ρΫ”_σλ žζ§ΣYΙΏπίψWώ;ƒQΞ¦ίU6ځc««·ϋΐ/Ο^όϊξψϋ§―ŽήIΡiσΩN€o„’>#ϊΜμήή½4ύžTW­Ώ#Mc#2ϋk™ΫΏ±θεpq±ͺWΈ^/ίΌψϋ‹½οχG—ηGΓΙΛδΧέ ηθ묐,Zi(Ua:m5TRίβίΑ.υ³Jηϊ(₯Ε}”zu°ψΩ³Z©Γιεώ4ΎάyUžnΡΛΆ£ι›0₯&˜p©—‹«–‹"ΧKΔ[τ’+†s²;p)dχWΒuHρ9C€–‹uΏŠ&TϝGˆΐ …EΪQυΉ ”43υͺyUΠ|A|ΆΑ’Β€ŽUžΥ}x>x²»wψjη!žΣ7ϊΔ³2Ο>¬πl劸;ψIAv§όψσ3$ΤΛō$Ήš)-Φ™qΌLŽες6₯ΖMωςΰΥψzή™ ;ΊσΡ”ώOeJmξγ{ΏΌ{wΌία;GγαdρOL±{μΔ;ψ…ΪJDΑj£,˜ˆΌE'E«άάkyϋόψΰן;Έ!ψ·±‚mEy*ε:+.°B§ΩΆ’Χ·Ϋ–!£Ε¬e§*hž#T(HŽ[’V]?sϊΚγ/’‹οθΫKΧ\uΘΦ oψ`Ηφ Ω\Z>~,!ό΄œ³• ™–y40mΪΠa:ζ1Σ‰°Ν)ώ*υλX³ (Bš4%*U#‰›wI͊΅DLς9ζD• ͺδ‹&Καβ73σ/‰±(W7eΛΆμΫΉO/“Heη&.ڌ΅ά]šh„}.Φ9ςύL£uSKΆY,«λΪ³Qότ[ϋ'μ‚GβFΙjΛ₯m˜Σχ0 κ¦]Τ«τ³θΏwe¨zΏιΡ-kυχ73UφF."<ΞΛτVϊFΥ»’Ψ―)(ωήh6_PaΘ9/‡Λώρθlq1OΏKμΜ’}kίZ™ΠrDP :i`¨zhCTϊ₯⽁υU—λΨτ:ΆšT2Γf·­―"[Ηf{±™˜a3ΫΐΆiσ[Ηο̝ŒΫΐw·Βe₯_GiΓ­&Ϋ€ΉιmΔzŒή¨υ @“Τλ•ϊŒ.H'­ύ ;Κ―ƒαy9GM3½¦i€σm'τσί4ϋ£3dΕϊϋ‘¬dΒί«/oτ:/΅Ύn«/rοΏe UTUl3U‹Œ~ΐžZ[­‘\ψφ5|-+Τ²c΅–•>ΊΖ «ώ¦5΄©>>j'ͺΦΧm¨e§₯‡τγψMkθΫΦ oυIF΅U°ϊϋiŸ,ε›Ι΅ΞͺΆ₯jpΥ<›κCꚬŸcμόσΪσXγΘρX¨±Υ ‰vζ₯ υ6ς˜Uδg:ΥwfεϊIyξ›iͺχνTΣ7΅ ;ΎO_pƒθ£[9’GŽ ™œ.<.6rœλ“cr9ͺCŽmε¨>9"“γB‡œ–ZΫG­uΉsSŽmyΆ}<ۜgΧΑ³myΆ}<›œgΫΑ³iy6}<›œgΫΑ³iy6}<›œgΣΑ³iyΦ}<λœgΣΑ³nyΦ}<λœgΣΑ³nyΦ}<«œgέΑ³jyV}<«œgέΑ³jyV}<«œgΥΑ³jy–}<˜gΥΑ³ly–}<˜gΥΑ³ly–}<η4/·Ψ–² endstream endobj 585 0 obj << /Author(Jim Hester; Hadley Wickham; Jennifer Bryan)/Title(vroom: Read and Write Rectangular Text Data Quickly)/Subject()/Creator(LaTeX with hyperref)/Producer(pdfTeX-1.40.20)/Keywords() /CreationDate (D:20211129171801-05'00') /ModDate (D:20211129171801-05'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) kpathsea version 6.3.1) >> endobj 542 0 obj << /Type /ObjStm /N 44 /First 383 /Length 2035 /Filter /FlateDecode >> stream xڍYMγ6½χ―ΰq έΙβG#Θ"Ψάv“,&Ιi“ƒΪV{„uΫ†₯ž™ώχΛGQ6ERκ9’«ŠΕWΕ*J2¬b’)ΛH VsrWΙIwuχrŌΠξͺY]Ή?$ «―ݍe5ιŠU¬6œίՌWdœ„3Ξα•γΊQΞ%Χ΅σCΔΈΥΈQLp•fBZψ1Lh‰eΒhw£ΊŠλ;R5“΅q6Š3)Έ›B &₯ξF2©4$Δ€qzRndεπ’r?.0Κ©e c`²ξpkγ0k‰…‘ζLΥ.‚)`Ϊ1C†Δ”!“ςSΈΤ8Κ0]8΄L׈ԑΤά:Ο¦fZXgl8Σ Œ`‘ξƈ»~Έ{ψ£-³n!>ή=όΔΤxσŸζ /νg·RώώΧφλΰΛωρΗΘ MNκ²?ΠΔγ~m^ڞύχΓΟ§νχΏΝeΨ0·Ξ€}ψψ|ΌΗΠνήϊφΠn‡ΗΗCsάΏ6ϋvΓ„’›Νo‡ώtτ~6ŒΗŠ- eτНŸNΝeχΧ‡Ώ6¬y€;ξ?Ύ½ΏvΗ‘έ·— «έ::«Ώοώέ½tC‚uuμίi±ρφSsiΆΓ8…Ιμš‘…Κ–UCχ5Uυιυιΰ•u|vsž0'ρ\Ήmϋ:‘†Ό€Ό0z5θ«$σι§}·mΠR=ΎΎ<CUμם‘Ήjbͺΐρπvn}Όv-ή+θΒΨ₯hαUΞCμΗ¬γbΎ*ύ}νR1yšOΗ]{μ]Ϋ=Ζλ‚ώt<Ό…Zˆ­ί”,t5™Βˆ0#!Η5wamΤ’hλzQλΣΐmsψΠοZω©’uέ΅ΟΝλaΙδRΙ‘ΗΗD/„΄δ’HΙσ—η°  αŠbR΅/ηα :»  e(ήΓ“ώ|κ»‘;α[¬ι'<·ων†OήA]b`Aiδjΰ£Ν4}΄’ϋφ˜8«s}(pΦ”UaΫZ[P‡Η«*WNŽW«QΗπKγ‹±O6׊ΐ+ž#Έ–1^‰\‹ †JT‘ΒΉ#Lžΰ±.kό*± raθj¬γ:πJΝ&n/c ϋΟPŒ½αZ7?Y-)1”GΔΊ~€„―…7’ΌΩCΊ•„(]Gα˜΅©Β£‰²χ|9Ήά{ρG*ϋbyΏ L=>6cxΟ―Η-vmΨ5ΊXš'”ο/†ΨŸΫ-Šb­.η°™t”tCχδ?>ŽΧPΤ- voχwMι~<€α4ρσy;\ϊΗΗΟνΦc }žβ‹xϋ|9^UγΕΣΐδ?`W{8ψS(zϊ,­ΆyηŽλC˜#T2 / HΣϊž]Ξ-ο·π vΌΤαQ“L½–[?·½3#3²ϊvnΩΓ?›‘9œφψ€Ίw›Ζχ§wΏ½ώΑ<ϊΖκw')£ύ/§]ϋπgίNΖnΨΉ=ώδ£e·Ωώ8Ÿ° endstream endobj 586 0 obj << /Type /XRef /Index [0 587] /Size 587 /W [1 3 1] /Root 584 0 R /Info 585 0 R /ID [ ] /Length 1322 /Filter /FlateDecode >> stream xΪ–»SUW‡χ:€ ˆxP7oAδ‘(" Θϋ! β›d&M&3™tΙ̚I•6)2)2“€°Z•3I•I—Β"AΚLΚT)“ϋύšoφZηœ}χ]ϋΫλœ”Rϊ―H©H–šΧ£Ν"Ω EQMNW+@%Ή†rxCXΐk%wJX jȍ;!< jΙ-’{MXΞ‘Ϋ"χаœ'wD\ wJξ%a 4–—^j&χ‚π"h"ΧAξ9a3h!7@ξα%p™άMrG„W@+Ή;δžΆvr/Ι^ΉœsκRςJΒabHeΡCβ΅₯ϊwϊqD0 NO¬eά΄΄Ϊ’ M Rϋ±BΞGθ|p*B§‚³: ’ε£ z`I4[κjΣTH—*ξF;ΐΨ `§Ρ °3ΊNF/ΐΔθX£`ΐRίηšλBΒm0Β°ΐ°ΐ°ΐ°ΐ°ΐ‘@©(»1ψ§fΑΊΐΊΐΊ˜fυˆ@Σc]°ƒ1ci¬Yα_ΰ_ΰ_ ^ ^,ƒU€ Α†ϋ+–&υ¬Ή !!!!΄«ϋ–fku3   „ΐ:V5ΖW–;υΨrτΡ’Yϊξ{0ΆtTƒ³ Βφ‡ΊZΓ|›Œκΐ9P.€ZKGuΊου«’₯“Ÿt³Ϊά% ζvτ€6KΌΥ-Wν«DΏ*uΊT©Œƒ^K}«›―A ŸΤί7ΐcœKυέϋ`Βǟj‚Ϋ`άS`ά3`<σ`,‚‡` ,ƒG`¬ƒ9KŸύ ίXkΤE/‘A V ¨¨Ξ[`μ€]πμƒ K_ΌΧ|{<Φzκχ‚§ΰ8°τΥ±žxžƒΰ˜ΗΊͺ{N©P£r·ψϊΉCgδ KΏΆ(,rψ1βΧςZ€4Ž/Ž ή0ΗΩso΄τΆIΟb„c„c„³ o΅ς)ΠU­c„w€NΦL…ΌΫ»]έG \ο-Œπ€ŽŽŽŽŽ~ΰ†Λ Ός @Gw΄p΄π;777777₯|`‰c‰c‰c‰c‰c‰c‰c‰c‰£…sf_|ΰγλ_βγγοdπΐž;{ξμΉλ-ΔN;;νμ΄ΏΗ#œ7˜³έν~C90 ·A¨Uΰ Πw„ϊΑYP κ€>&κΑy .p”@#ΈΤτ•Ρ.Λΰ Πg™^šνΰ*Π·JΏ₯ŸΡϊ­nΦh ,€qπL€[ΰ6˜“@Jw-ύ~€g§ΜFώΦhΪl-4ΊgΆχF3f_ώ₯Ρ¬Ωγέ7ϋχζΐΨ΅bϊ7r™‚e –)X¦`™‚e}R°LΑ2Λ,S°LΑ2Λ,S°LΑ2Λ,S°LΑ2Λ,S°LΑ2Λ,S°LΑ2ίk™΄Μ!Ι]€#ž{"Σ22r™Ζ“iA™f”―ƒ!+¦†“‹½ιψKN endstream endobj startxref 148260 %%EOF vroom/tests/0000755000176200001440000000000014142512075012557 5ustar liggesusersvroom/tests/spelling.R0000644000176200001440000000024114142512075014514 0ustar liggesusersif(requireNamespace('spelling', quietly = TRUE)) spelling::spell_check_test(vignettes = TRUE, error = FALSE, skip_on_cran = TRUE) vroom/tests/testthat/0000755000176200001440000000000014151431426014417 5ustar liggesusersvroom/tests/testthat/fwf-trailing-crlf.txt0000644000176200001440000000002214142512075020467 0ustar liggesusers123 123 123 123 vroom/tests/testthat/test-connection.R0000644000176200001440000000521214142515724017662 0ustar liggesuserstest_that("reading from connection is consistent with reading directly from a file", { skip_if(is_windows() && on_github_actions()) expected <- vroom(vroom_example("mtcars.csv"), col_types = list()) # This needs to be small enough to have a few blocks in the file, but big # enough to fit on the first line (until #47 is fixed) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 100), { actual <- vroom(file(vroom_example("mtcars.csv"), ""), delim = ",", col_types = list()) }) expect_equal(actual, expected) }) test_that("reading from connection is consistent with reading directly from a file with quoted fields", { ir <- iris ir$Species <- as.character(ir$Species) # add some commas locs <- vapply(nchar(ir$Species), sample.int, integer(1), 1) substr(ir$Species, locs, locs) <- "," out <- tempfile() vroom_write(ir, out, delim = ",") expected <- vroom(out, col_types = list()) # This needs to be small enough to have a few blocks in the file, but big # enough to fit on the first line (until #47 is fixed) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 100), { actual <- vroom(file(out), col_types = list()) }) expect_equal(actual, expected) }) test_that("vroom errors when the connection buffer is too small", { withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 32), { expect_error(vroom(file(vroom_example("mtcars.csv")), col_types = list()), "not large enough") }) }) test_that("vroom can read files with only a single line and no newlines", { f <- tempfile() on.exit(unlink(f)) writeChar("a,b,c", eos = NULL, f) # with a header expect_named(vroom(f, delim = ",", col_types = list()), c("a", "b", "c")) expect_named(vroom(f, col_types = list()), c("a", "b", "c")) # without a header expect_equal(vroom(f, col_names = FALSE, delim = ",", col_types = list()), tibble::tibble(X1 = "a", X2 = "b", X3 = "c")) expect_equal(vroom(f, col_names = FALSE, col_types = list()), tibble::tibble(X1 = "a", X2 = "b", X3 = "c")) }) test_that("vroom works with file connections and quoted fields", { f <- tempfile() on.exit(unlink(f)) writeLines('a,b,c\n"1","2","3"\n"4","5","6"', f) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 8), { x <- vroom(file(f), delim = ",", col_types = list()) }) expect_equal(x, tibble::tibble(a = c(1, 4), b = c(2, 5), c = c(3, 6))) }) test_that("vroom works with windows newlines and a connection size that lies directly on the newline", { tf <- tempfile() on.exit(unlink(tf)) writeChar("1,2\r\na,bbb\r\ne,f\r\n", tf, eos = NULL) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 12), { x <- vroom(file(tf), col_types = "cc") }) expect_equal(x[[1]], c("a", "e")) }) vroom/tests/testthat/test-col_types.R0000644000176200001440000000115514142515724017526 0ustar liggesuserstest_that("You can use !! in cols and cols_only", { var <- "xyz" expect_equal( cols(!!var := col_character()), cols(xyz = col_character()) ) expect_equal( cols_only(!!var := col_character()), cols_only(xyz = col_character()) ) }) test_that("format(col_spec) contains the delimiter if specified", { expect_match(fixed = TRUE, format(cols(.delim = "\t")), '.delim = "\\t"' ) }) test_that("col_types are truncated if you pass too many (#355)", { res <- vroom(I("a,b,c,d\n1,2,3,4"), col_types = "cccccccc") expect_equal(res, tibble::tibble(a = "1", b = "2", c = "3", d = "4")) }) vroom/tests/testthat/test-factor.R0000644000176200001440000001302514142515724017002 0ustar liggesuserstest_that("strings mapped to levels", { test_vroom("a\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"))), equals = tibble::tibble(X1 = factor(c("a", "b"))) ) }) test_that("can generate ordered factor", { res <- test_vroom("a\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"), ordered = TRUE)), equals = tibble::tibble(X1 = ordered(c("a", "b"))) ) expect_true(is.ordered(res$X1)) }) test_that("NA if value not in levels", { test_vroom("a\nb\nc\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"))), equals = tibble::tibble(X1 = factor(c("a", "b", NA))) ) }) test_that("NAs silently passed along", { test_vroom("a\nb\nNA\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"), include_na = FALSE)), equals = tibble::tibble(X1 = factor(c("a", "b", NA))) ) }) test_that("levels = NULL", { test_vroom("a\nb\nc\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL)), equals = tibble::tibble(X1 = factor(c("a", "b", "c", "b"))) ) }) test_that("levels = NULL orders by data", { test_vroom("b\na\nc\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL)), equals = tibble::tibble(X1 = factor(c("b", "a", "c", "b"), levels = c("b", "a", "c"))) ) }) test_that("levels = NULL is the default", { test_vroom("a\nb\nc\nd\n", col_names = FALSE, col_types = list(X1 = "f"), equals = tibble::tibble(X1 = factor(c("a", "b", "c", "d"))) ) }) test_that("NAs included in levels if desired", { test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b", NA))), equals = tibble::tibble(X1 = factor(c(NA, "b", "a"), levels = c("a", "b", NA), exclude = NULL)) ) test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b", NA), include_na = TRUE)), equals = tibble::tibble(X1 = factor(c(NA, "b", "a"), levels = c("a", "b", NA), exclude = NULL)) ) test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL, include_na = FALSE)), equals = tibble::tibble(X1 = factor(c("NA", "b", "a"), levels = c("NA", "b", "a"))) ) test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL, include_na = TRUE)), equals = tibble::tibble(X1 = factor(c(NA, "b", "a"), levels = c(NA, "b", "a"), exclude = NULL)) ) }) test_that("Factors handle encodings properly (#615)", { encoded <- function(x, encoding) { Encoding(x) <- encoding x } f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw(encoded("test\nA\n\xC4\n", "latin1")), f) x <- test_vroom(f, delim = ",", col_types = cols(col_factor(c("A", "\uC4"))), locale = locale(encoding = "latin1"), equals = tibble::tibble(test = factor(c("A", "\uC4"), levels = c("A", "\uC4"))) ) }) test_that("factors parse like factor if trim_ws = FALSE", { test_vroom("a\na \n", col_names = FALSE, trim_ws = FALSE, col_types = list(X1 = col_factor(levels = "a")), equals = tibble::tibble(X1 = factor(c("a", "a "), levels = c("a"))) ) test_vroom("a\na \n", col_names = FALSE, trim_ws = FALSE, col_types = list(X1 = col_factor(levels = "a ")), equals = tibble::tibble(X1 = factor(c("a", "a "), levels = c("a "))) ) test_vroom("a\na \n", col_names = FALSE, trim_ws = FALSE, col_types = list(X1 = col_factor(levels = c("a ", "a"))), equals = tibble::tibble(X1 = factor(c("a", "a "), levels = c("a ", "a"))) ) }) test_that("Can parse a factor with levels of NA and empty string", { x <- c("NC", "NC", "NC", "", "", "NB", "NA", "", "", "NB", "NA", "NA", "NC", "NB", "NB", "NC", "NB", "NA", "NA") x_in <- paste0(paste(x, collapse = "\n"), "\n") test_vroom(x_in, col_names = FALSE, col_types = list(X1 = col_factor(levels = c("NA", "NB", "NC", ""))), na = character(), skip_empty_rows = FALSE, equals = tibble::tibble(X1 = factor(x, levels = c("NA", "NB", "NC", ""))) ) }) test_that("encodings are respected", { loc <- locale(encoding = "ISO-8859-1") expected <- c("fran\u00e7ais", "\u00e9l\u00e8ve") x <- vroom(test_path("enc-iso-8859-1.txt"), delim = "\n", locale = loc, col_types = c(X1 = "f"), col_names = FALSE) expect_equal(x[[1]], factor(expected, levels = expected)) y <- vroom( test_path("enc-iso-8859-1.txt"), delim = "\n", locale = loc, col_types = list(X1 = col_factor(levels = expected)), col_names = FALSE ) expect_equal(y[[1]], factor(expected, levels = expected)) }) test_that("Results are correct with backslash escapes", { obj <- vroom(I("A,T\nB,F\n"), col_names = FALSE, col_types = list("f", "f"), escape_backslash = TRUE) exp <- tibble::tibble(X1 = factor(c("A", "B")), X2 = factor(c("T", "F"), levels = c("T", "F"))) expect_equal(obj, exp) obj2 <- vroom(I("A,T\nB,F\n"), col_names = FALSE, col_types = list("f", "f"), escape_backslash = FALSE) expect_equal(obj2, exp) }) test_that("subsetting works with both double and integer indexes", { x <- vroom(I("X1\nfoo"), delim = ",", col_types = "f") expect_equal(x$X1[1L], factor("foo")) expect_equal(x$X1[1], factor("foo")) expect_equal(x$X1[NA_integer_], factor(NA_character_, levels = "foo")) expect_equal(x$X1[NA_real_], factor(NA_character_, levels = "foo")) }) test_that("results are correct even with quoted values", { expect_equal( vroom(I('day\n"Sun"\n"Sat"\n"Sat"'), altrep = FALSE, col_types = "f", delim = ",")$day, factor(c("Sun", "Sat", "Sat"), levels = c("Sun", "Sat")) ) }) vroom/tests/testthat/test-big-int.R0000644000176200001440000000364514142515724017064 0ustar liggesusersas.integer64 <- bit64::as.integer64 test_that("integers are returned correctly", { # empty fields are returned as NAs test_vroom("foo,bar,baz\n1,,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) # numbers which are not integers are returned as NAs test_vroom("foo,bar,baz\n1,1.5,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) # fields with non-digits are returned as NAs test_vroom("foo,bar,baz\n1,32xyz,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) # 2^31 - 1 is the maximum representable integer with 32 bit ints test_vroom("foo,bar,baz\n1,2147483647,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64("2147483647"), baz = as.integer64(3)) ) # But 2^31 should also work test_vroom("foo,bar,baz\n1,2147483648,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64("2147483648"), baz = as.integer64(3)) ) # As well as -2^31 test_vroom("foo,bar,baz\n1,9223372036854775807,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64("9223372036854775807"), baz = as.integer64(3)) ) # But 2^63 should be NA test_vroom("foo,bar,baz\n1,9223372036854775808,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) }) test_that("NA can be a big int value", { test_vroom(I("x\n1\n2\n"), delim = ",", col_types = "I", na = "1", equals = tibble::tibble(x = as.integer64(c(NA_integer_, 2L)))) }) vroom/tests/testthat/empty-file0000644000176200001440000000000014142512075016403 0ustar liggesusersvroom/tests/testthat/multi-byte-ascii.txt0000644000176200001440000000006313746062417020351 0ustar liggesusersid||name||age 1||ed||36 2||leigh||NA 3||nathan||14 vroom/tests/testthat/test-multi-byte.R0000644000176200001440000000101414142512075017606 0ustar liggesuserstest_that("multi-byte reading works with unicode delimiters and UTF-8 encoding", { test_vroom(test_path("multi-byte-ascii.txt"), delim = "||", equals = tibble::tibble(id = 1:3, name = c("ed", "leigh", "nathan"), age = c(36, NA, 14)) ) }) test_that("multi-byte reading works with unicode delimiters and UTF-8 encoding", { skip_on_os("solaris") test_vroom(test_path("multi-byte-unicode.txt"), delim = "\U2764", equals = tibble::tibble(id = 1:3, name = c("ed", "leigh", "nathan"), age = c(36, NA, 14)) ) }) vroom/tests/testthat/test-logical.R0000644000176200001440000000205014142515724017132 0ustar liggesuserstest_that("TRUE and FALSE parsed", { test_vroom("TRUE\nFALSE\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("true and false parsed", { test_vroom("true\nfalse\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("True and False parsed", { test_vroom("True\nFalse\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("T and F parsed", { test_vroom("T\nF\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("t and f parsed", { test_vroom("t\nf\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("1 and 0 parsed", { # 1 and 0 are never guessed as logical, but they can be parsed as such if you # explicitly set the column type. test_vroom("1\n0\n", col_types = "l", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("NA can be a logical value", { test_vroom("1\n0\n", col_types = "l", col_names = FALSE, na = "1", equals = tibble::tibble(X1 = c(NA, FALSE))) }) vroom/tests/testthat/test-int.R0000644000176200001440000000230514142515724016315 0ustar liggesuserstest_that("integers are returned correctly", { # empty fields are returned as NAs test_vroom("foo,bar,baz\n1,,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) # numbers which are not integers are returned as NAs test_vroom("foo,bar,baz\n1,1.5,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) # fields with non-digits are returned as NAs test_vroom("foo,bar,baz\n1,32xyz,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) # 2^31 - 1 is the maximum representable integer with 32 bit ints test_vroom("foo,bar,baz\n1,2147483647,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = 2147483647L, baz = 3L) ) test_vroom("foo,bar,baz\n1,2147483648,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) }) test_that("NA can be a int value", { test_vroom(I("x\n1\n2\n"), delim = ",", col_types = "i", na = "1", equals = tibble::tibble(x = c(NA_integer_, 2L))) }) vroom/tests/testthat/test-problems.R0000644000176200001440000001035214142515724017347 0ustar liggesuserstest_that("problems with data parsing works for single files", { expect_warning( x <- vroom(I("x,y\n1,2\n1,1.x\n"), col_types = "dd", altrep = FALSE) ) probs <- problems(x) expect_equal(probs$row, 3) expect_equal(probs$col, 2) expect_equal(probs$expected, "a double") expect_equal(probs$actual, "1.x") }) test_that("problems works for multiple files", { out1 <- file.path(tempdir(), "out1.txt") out2 <- file.path(tempdir(), "out2.txt") on.exit(unlink(c(out1, out2))) writeLines("x,y\n1,2\n1,1.x\n2,2", out1) writeLines("x,y\n3.x,4\n1,2\n2,2", out2) expect_warning( x <- vroom(c(out1, out2), delim = ",", col_types = "dd", altrep=F) ) probs <- problems(x) expect_equal(probs$row, c(3, 2)) expect_equal(probs$col, c(2, 1)) expect_equal(probs$expected, c("a double", "a double")) expect_equal(probs$actual, c("1.x", "3.x")) expect_equal(basename(probs$file), basename(c(out1, out2))) }) test_that("problems with number of columns works for single files", { expect_warning(probs3 <- problems(vroom(I("x,y,z\n1,2\n"), col_names = TRUE, col_types = "ddd", altrep = FALSE))) expect_equal(probs3$row, 2) expect_equal(probs3$col, 2) expect_equal(probs3$expected, "3 columns") expect_equal(probs3$actual, "2 columns") expect_warning(probs3 <- problems(vroom(I("x,y,z\n1,2\n"), col_names = FALSE, col_types = "ddd", altrep = FALSE))) expect_equal(probs3$row[[4]], 2) expect_equal(probs3$col[[4]], 2) expect_equal(probs3$expected[[4]], "3 columns") expect_equal(probs3$actual[[4]], "2 columns") expect_warning(probs4 <- problems(vroom(I("x,y\n1,2,3,4\n"), col_names = TRUE, col_types = "dd", altrep = FALSE))) expect_equal(probs4$row[[2]], 2) expect_equal(probs4$col[[2]], 4) expect_equal(probs4$expected[[2]], "2 columns") expect_equal(probs4$actual[[2]], "4 columns") expect_warning(probs2 <- problems(vroom(I("x,y\n1,2,3,4\n"), col_names = FALSE, col_types = "dd", altrep = FALSE))) expect_equal(probs2$row[[4]], 2) expect_equal(probs2$col[[4]], 4) expect_equal(probs2$expected[[4]], "2 columns") expect_equal(probs2$actual[[4]], "4 columns") }) test_that("parsing problems are shown for all datatypes", { skip_if(getRversion() < "3.5") types <- list( "an integer" = col_integer(), "a big integer" = col_big_integer(), "a double" = col_double(), "a number" = col_number(), "value in level set" = col_factor(levels = "foo"), "date in ISO8601" = col_date(), "date in ISO8601" = col_datetime(), "time in ISO8601" = col_time() ) for (i in seq_along(types)) { type <- types[[i]] expected <- names(types)[[i]] res <- vroom(I("x\nxyz\n"), delim = ",", col_types = list(type), altrep = TRUE) # This calls the type_Elt function expect_warning(res[[1]][[1]], "One or more parsing issues") expect_equal(problems(res)$expected, expected) res <- vroom(I("x\nxyz\n"), delim = ",", col_types = list(type), altrep = TRUE) # This calls the read_type function expect_warning(vroom_materialize(res, replace = FALSE), "One or more parsing issues") expect_equal(problems(res)$expected, expected) } expect_warning(res <- vroom(I("x\nxyz\n"), delim = ",", col_types = list(col_logical())), "One or more parsing issues") }) test_that("problems that are generated more than once are not duplicated", { # On versions of R without ALTREP the warnings will happen at different times, # so we skip this test in those cases skip_if(getRversion() < "3.5") res <- vroom(I("x\n1\n2\n3\n4\n5\na"), col_types = "i", delim = ",") # generate first problem expect_warning(res[[1]][[6]], "One or more parsing issues") # generate the same problem again res[[1]][[6]] probs <- problems(res) expect_equal(probs$row, 7) expect_equal(probs$col, 1) expect_equal(probs$expected, "an integer") }) test_that("problems return the proper row number", { expect_warning( x <- vroom(I("a,b,c\nx,y,z,,"), altrep = FALSE, col_types = "ccc") ) expect_equal(problems(x)$row, 2) expect_warning( y <- vroom(I("a,b,c\nx,y,z\nx,y,z,,"), altrep = FALSE, col_types = "ccc") ) expect_equal(problems(y)$row, 3) expect_warning( z <- vroom(I("a,b,c\nx,y,z,,\nx,y,z,,\n"), altrep = FALSE, col_types = "ccc") ) expect_equal(problems(z)$row, c(2, 3)) }) vroom/tests/testthat/test-vroom.R0000644000176200001440000006446414142515724016703 0ustar liggesuserstest_that("vroom can read a tsv", { test_vroom("a\tb\tc\n1\t2\t3\n", delim = "\t", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom can read a csv", { test_vroom("a,b,c\n1,2,3\n", delim = ",", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom guesses columns with NAs", { test_vroom("a,b,c\nNA,2,3\n4,5,6\n", delim = ",", equals = tibble::tibble(a = c(NA, 4), b = c(2, 5), c = c(3, 6)) ) test_vroom("a,b,c\nfoo,2,3\n4,5,6\n", delim = ",", na = "foo", equals = tibble::tibble(a = c(NA, 4), b = c(2, 5), c = c(3, 6)) ) test_vroom("a,b,c\nfoo,2,3\n4.0,5,6\n", delim = ",", na = "foo", equals = tibble::tibble(a = c(NA, 4), b = c(2, 5), c = c(3, 6)) ) test_vroom("a,b,c\nfoo,2,3\nbar,5,6\n", delim = ",", na = "foo", equals = tibble::tibble(a = c(NA, "bar"), b = c(2, 5), c = c(3, 6)) ) }) test_that("vroom can trim whitespace", { test_vroom('a,b,c\n foo , bar ,baz\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) test_vroom('a,b,c\n\tfoo\t,\t\tbar\t\t,baz\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) # whitespace trimmed before quotes test_vroom('a,b,c\n "foo" , "bar" ,"baz"\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) # whitespace trimmed inside quotes test_vroom('a,b,c\n"foo "," bar","\t\tbaz"\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) }) test_that("vroom can read files with quotes", { test_vroom('"a","b","c"\n"foo","bar","baz"\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) test_vroom('"a","b","c"\n",foo","bar","baz"\n', delim = ",", equals = tibble::tibble(a = ",foo", b = "bar", c = "baz") ) test_vroom("'a','b','c'\n',foo','bar','baz'\n", delim = ",", quote = "'", equals = tibble::tibble(a = ",foo", b = "bar", c = "baz") ) }) test_that("vroom escapes double quotes", { test_vroom('"a","b","c"\n"""fo""o","b""""ar","baz"""\n', delim = ",", equals = tibble::tibble(a = "\"fo\"o", b = "b\"\"ar", c = "baz\"") ) }) test_that("vroom escapes backslashes", { test_vroom('a,b,c\n\\,foo,\\"ba\\"r,baz\\"\n', delim = ",", escape_backslash = TRUE, equals = tibble::tibble(a = ",foo", b = "\"ba\"r", c = "baz\"") ) }) test_that("vroom ignores leading whitespace", { test_vroom('\n\n \t \t\n \n\na,b,c\n1,2,3\n', delim = ",", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom ignores comments", { test_vroom('\n\n \t #a,b,c\na,b,c\n1,2,3\n', delim = ",", comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom respects skip", { test_vroom('#a,b,c\na,b,c\n1,2,3\n', delim = ",", skip = 1, equals = tibble::tibble(a = 1, b = 2, c = 3) ) test_vroom('#a,b,c\na,b,c\n1,2,3\n', delim = ",", skip = 1, comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) test_vroom('#a,b,c\nasdfasdf\na,b,c\n1,2,3\n', delim = ",", skip = 2, comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) test_vroom('\n\n#a,b,c\nasdfasdf\na,b,c\n1,2,3\n', delim = ",", skip = 4, comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom respects col_types", { test_vroom('a,b,c\n1,2,3\n', delim = ",", col_types = "idc", equals = tibble::tibble(a = 1L, b = 2, c = "3") ) test_vroom('a,b,c,d\nT,2,3,4\n', delim = ",", col_types = "lfc_", equals = tibble::tibble(a = TRUE, b = factor(2), c = "3") ) }) test_that("vroom handles UTF byte order marks", { # UTF-8 expect_equal( vroom(as.raw(c(0xef, 0xbb, 0xbf, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-16 Big Endian expect_equal( vroom(as.raw(c(0xfe, 0xff, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-16 Little Endian expect_equal( vroom(as.raw(c(0xff, 0xfe, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-32 Big Endian expect_equal( vroom(as.raw(c(0x00, 0x00, 0xfe, 0xff, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-32 Little Endian expect_equal( vroom(as.raw(c(0xff, 0xfe, 0x00, 0x00, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") }) test_that("vroom handles vectors shorter than the UTF byte order marks", { skip_on_os("solaris") expect_equal( charToRaw(vroom(as.raw(c(0xef, 0xbb, 0x0A)), delim = "\n", col_names = FALSE, col_types = list())[[1]]), as.raw(c(0xef, 0xbb)) ) expect_equal( charToRaw(vroom(as.raw(c(0xfe, 0x0A)), delim = "\n", col_names = FALSE, col_types = list())[[1]]), as.raw(c(0xfe)) ) expect_equal( charToRaw(vroom(as.raw(c(0xff, 0x0A)), delim = "\n", col_names = FALSE, col_types = list())[[1]]), as.raw(c(0xff)) ) }) test_that("vroom handles windows newlines", { expect_equal( vroom(I("a\tb\r\n1\t2\r\n"), trim_ws = FALSE, col_types = list())[[1]], 1 ) }) test_that("vroom can read a file with only headers", { test_vroom("a\n", equals = tibble::tibble(a = character()) ) test_vroom("a,b,c\n", delim = ",", equals = tibble::tibble(a = character(), b = character(), c = character()) ) }) test_that("vroom can read an empty file", { test_vroom("\n", equals = tibble::tibble() ) f <- tempfile() file.create(f) on.exit(unlink(f)) capture.output(type = "message", expect_equal(vroom(f, col_types = list()), tibble::tibble()) ) capture.output(type = "message", expect_equal(vroom(f, col_names = FALSE, col_types = list()), tibble::tibble()) ) expect_equal(vroom(character(), col_types = list()), tibble::tibble()) }) test_that("vroom_examples() returns the example files", { expect_equal(vroom_examples(), list.files(system.file("extdata", package = "vroom"))) }) test_that("vroom_example() returns a single example files", { expect_equal(vroom_example("mtcars.csv"), system.file("extdata", "mtcars.csv", package = "vroom")) }) test_that("subsets work", { res <- vroom(I("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"), delim = "\t", col_names = FALSE, col_types = list()) expect_equal(head(res[[1]]), c(1:6)) expect_equal(tail(res[[1]]), c(9:14)) expect_equal(tail(res[[1]][3:8]), c(3:8)) }) test_that("n_max works with normal files", { expect_equal( NROW(vroom(vroom_example("mtcars.csv"), n_max = 2, col_types = list())), 2 ) # headers don't count expect_equal( NROW(vroom(vroom_example("mtcars.csv"), n_max = 2, col_names = FALSE, col_types = list())), 2 ) # Zero rows with headers should just have the headers expect_equal( dim(vroom(vroom_example("mtcars.csv"), n_max = 0, col_types = list())), c(0, 12) ) # If you don't read the header or any rows it must be empty expect_equal( dim(vroom(vroom_example("mtcars.csv"), n_max = 0, col_names = FALSE, col_types = list())), c(0, 0) ) }) test_that("n_max works with connections files", { expect_equal( NROW(vroom(vroom_example("mtcars.csv.gz"), n_max = 2, col_types = list())), 2 ) # headers don't count expect_equal( NROW(vroom(vroom_example("mtcars.csv.gz"), n_max = 2, col_names = FALSE, col_types = list())), 2 ) # Zero rows with headers should just have the headers expect_equal( dim(vroom(vroom_example("mtcars.csv.gz"), n_max = 0, col_types = list())), c(0, 12) ) # If you don't read the header or any rows it must be empty expect_equal( dim(vroom(vroom_example("mtcars.csv.gz"), n_max = 0, col_names = FALSE, col_types = list())), c(0, 0) ) }) test_that("vroom truncates col_names if it is too long", { test_vroom("1\n2\n", col_names = c("a", "b"), equals = tibble::tibble(a = c(1, 2)) ) }) test_that("vroom makes additional col_names if it is too short", { test_vroom("1,2,3\n4,5,6\n", col_names = c("a", "b"), delim = ",", equals = tibble::tibble(a = c(1, 4), b = c(2, 5), X3 = c(3, 6)) ) }) test_that("vroom reads newlines in data", { test_vroom('a\n"1\n2"\n', equals = tibble::tibble(a = "1\n2")) }) test_that("vroom reads headers with embedded newlines", { test_vroom("\"Header\nLine Two\"\nValue\n", delim = ",", equals = tibble::tibble("Header\nLine Two" = "Value") ) test_vroom("\"Header\",\"Second header\nLine Two\"\nValue,Value2\n", delim = ",", equals = tibble::tibble("Header" = "Value", "Second header\nLine Two" = "Value2") ) }) test_that("vroom reads headers with embedded newlines 2", { test_vroom("\"Header\nLine Two\"\n\"Another line\nto\nskip\"\nValue,Value2\n", skip = 2, col_names = FALSE, delim = ",", equals = tibble::tibble("X1" = "Value", "X2" = "Value2") ) }) test_that("vroom uses the number of rows when guess_max = Inf", { tf <- tempfile() df <- tibble::tibble(x = c(1:1000, "foo", 1001)) vroom_write(df, tf, delim = "\t") # The type should be guessed wrong, because the character comes at the end expect_warning(res <- vroom(tf, delim = "\t", col_types = list(), altrep = FALSE)) expect_type(res[["x"]], "double") expect_true(is.na(res[["x"]][[NROW(res) - 1]])) # The value should exist with guess_max = Inf res <- vroom(tf, delim = "\t", guess_max = Inf, col_types = list()) expect_type(res[["x"]], "character") expect_equal(res[["x"]][[NROW(res) - 1]], "foo") }) test_that("vroom adds columns if a row is too short", { test_vroom("a,b,c,d\n1,2\n3,4,5,6\n", delim = ",", equals = tibble::tibble("a" = c(1,3), "b" = c(2,4), "c" = c(NA, 5), "d" = c(NA, 6)) ) }) test_that("vroom removes columns if a row is too long", { test_vroom("a,b,c,d\n1,2,3,4,5,6,7\n8,9,10,11\n", delim = ",", col_types = c(d = "c"), equals = tibble::tibble("a" = c(1,8), "b" = c(2,9), "c" = c(3, 10), "d" = c("4,5,6,7", "11")) ) }) # Figure out a better way to test progress bars... #test_that("progress bars work", { #withr::with_options(c("vroom.show_after" = 0), { #expect_output_file(vroom(vroom_example("mtcars.csv"), progress = TRUE), "mtcars-progress") #}) #}) test_that("guess_type works with long strings (#74)", { expect_s3_class( guess_type("https://www.bing.com/search?q=mr+popper%27s+penguins+worksheets+free&FORM=QSRE1"), "collector_character" ) }) test_that("vroom guesses types if unnamed column types do not match the number of columns", { test_vroom(I("a,b\n1,2\n"), delim = ",", col_types = "i", equals = tibble::tibble(a = 1L, b = 2L)) }) test_that("column names are properly encoded", { skip_on_os("solaris") nms <- vroom(I("f\U00F6\U00F6\nbar\n"), delim = "\n", col_types = list()) expect_equal(Encoding(colnames(nms)), "UTF-8") }) test_that("Files with windows newlines and missing fields work", { test_vroom("a,b,c,d\r\nm,\r\n\r\n", delim = ",", skip_empty_rows = FALSE, equals = tibble::tibble(a = c("m", NA), b = c(NA, NA), c = c(NA, NA), d = c(NA, NA)) ) }) test_that("vroom can read files with no trailing newline", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("foo\nbar"), f) expect_equal(vroom(f, col_names = FALSE, delim = ",", col_types = list())[[1]], c("foo", "bar")) f2 <- tempfile() on.exit(unlink(f2), add = TRUE) writeBin(charToRaw("foo,bar\n1,2"), f2) expect_equal(vroom(f2, delim = ",", col_types = list()), tibble::tibble(foo = 1, bar = 2)) }) test_that("Missing files error with a nice error message", { f <- tempfile() expect_error(vroom(f, col_types = list()), "does not exist") expect_error(vroom("foo", col_types = list()), "does not exist in current working directory") }) test_that("Can return the spec object", { x <- vroom(I("foo,bar\n1,c\n"), col_types = list()) obj <- spec(x) expect_s3_class(obj, "col_spec") exp <- as.col_spec(list(foo = "d", bar = "c")) exp$delim <- "," expect_equal(obj, exp) }) test_that("vroom handles files with trailing commas, windows newlines, missing a final newline and not null terminated", { f <- tempfile() on.exit(unlink(f)) writeChar(paste(collapse = "\r\n", c('foo,bar,', '1,2,')), con = f, eos = NULL) expect_message(regexp = "New names", expect_equal( vroom(f, col_types = list()), tibble::tibble(foo = 1, bar = 2, "...3" = NA) ) ) }) test_that("vroom uses the delim if it is specified in the col_types", { # if we give a tab delim in the spec there should only be one column expect_equal( ncol(vroom(I("a,b,c\n1,2,3\n"), col_types = list(.delim = "\t"))), 1 ) # But specifying an explicit delim overrides the spec expect_equal( ncol(vroom(I("a,b,c\n1,2,3\n"), col_types = list(.delim = "\t"), delim = ",")), 3 ) expect_equal( ncol(vroom(I("a,b,c\n1,2,3\n"), col_types = list(.delim = ","), delim = "\t")), 1 ) }) test_that("vroom supports NA and NA_integer_ indices", { data <- vroom(vroom_example("mtcars.csv"), col_types = list()) expect_equal(data[NA, 1, drop = TRUE], rep(NA_character_, nrow(data))) expect_equal(data[NA_integer_, 1, drop = TRUE], NA_character_) }) test_that("vroom supports NA and NA_integer_ indices with factors and datetimes", { data <- vroom(I("x\ty\nfoo\t2020-01-01 12:00:01"), col_types = "fT") expect_equal(data[NA, 1, drop = TRUE], factor(NA, levels = "foo")) expect_equal(data[NA, 2, drop = TRUE], .POSIXct(NA_real_, tz = "UTC")) expect_equal(data[NA_integer_, 1, drop = TRUE], factor(NA, levels = "foo")) expect_equal(data[NA_integer_, 2, drop = TRUE], .POSIXct(NA_real_, tz = "UTC")) }) test_that("vroom works with windows newlines and files without a trailing newline (#219)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("X,Y\r\n1,12/08/2016\r\n2,05/01/2018"), f) res <- vroom(f, col_types = cols(Y = "c")) expect_equal(res$Y[[2]], "05/01/2018") }) test_that("vroom works with `id` and skipped columns", { data <- vroom(vroom_example("mtcars.csv"), col_types = c(mpg = "_"), id = "File") expect_true(ncol(data) == 12) expect_true(names(data)[[1]] == "File") expect_false("mpg" %in% names(data)) }) test_that("vroom works with n_max, windows newlines and files larger than the connection buffer", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("X,Y\r\n1,2\r\n3342343242312312,442342432423432432\r\n432424324,532432324"), f) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 25), res <- vroom(f, delim = ",", n_max = 1, col_types = list()) ) expect_equal(res$X, 1) expect_equal(res$Y, 2) }) test_that("subsetting works with both double and integer indexes", { x <- vroom(I("X1\nfoo"), delim = ",", col_types = list()) expect_equal(x$X1[1L], "foo") expect_equal(x$X1[1], "foo") expect_equal(x$X1[NA_integer_], NA_character_) expect_equal(x$X1[NA_real_], NA_character_) }) test_that("quotes inside fields are ignored", { x <- vroom(I("x\nfoo\"bar\nbaz\n"), delim = ",", quote = "\"", col_types = list()) expect_equal(x$x[[1]], "foo\"bar") expect_equal(x$x[[2]], "baz") }) test_that("quotes at the beginning and end of lines are used", { y <- vroom(I("x\n\"foo\"\"bar\"\nbaz\n"), delim = ",", quote = "\"", col_types = list()) expect_equal(y$x[[1]], "foo\"bar") expect_equal(y$x[[2]], "baz") }) test_that("quotes at delimiters are used", { z <- vroom(I("x,y,z\n1,\"foo\"\"bar\",2\n3,baz,4"), delim = ",", quote = "\"", col_types = list()) expect_equal(z$y[[1]], "foo\"bar") expect_equal(z$y[[2]], "baz") }) test_that("vroom reads files with embedded newlines even when num_threads > 1", { tf <- tempfile() con <- file(tf, "wb") on.exit({ unlink(tf) }) writeLines(c("x", rep("foo", 1000), '"bar\nbaz"', rep("qux", 1000)), con, sep = "\n") close(con) res <- vroom(tf, delim = ",", num_threads = 5, col_types = list()) expect_equal(nrow(res), 1000 + 1 + 1000) expect_equal(res$x[[1001]], "bar\nbaz") }) test_that("multi-character comments are supported", { res <- vroom(I("## this is a comment\n# this is not"), delim = "\t", comment = "##", col_names = FALSE, col_types = list()) expect_equal(res[[1]], "# this is not") }) test_that("vroom works with quoted fields at the end of a windows newline", { f <- tempfile() on.exit(unlink(f)) con <- file(f, "wb") writeLines(c('"x"', 1), con, sep = "\r\n") close(con) res <- vroom(f, delim = ",", col_names = FALSE, col_types = list()) expect_equal(res[[1]], c("x", 1)) }) test_that("vroom can handle NUL characters in strings", { test_vroom(test_path("raw.csv"), delim = ",", progress = FALSE, equals = tibble::tibble(abc = "ab", def = "def") ) }) test_that("n_max is respected in all cases", { expect_equal(dim(vroom(I("x\ty\tz\n1\t2\t3\n4\t5\t6\n"), n_max = 1, col_types = list())), c(1, 3)) }) test_that("comments are ignored regardless of where they appear", { out1 <- vroom(I('x\n1#comment'), comment = "#", col_types = "d", delim = ",") out2 <- vroom(I('x\n1#comment\n#comment'), comment = "#", col_types = "d", delim = ",") out3 <- vroom(I('x\n"1"#comment'), comment = "#", col_types = "d", delim = ",") expect_equal(out1$x, 1) expect_equal(out2$x, 1) expect_equal(out3$x, 1) out4 <- vroom(I('x,y\n1,#comment'), comment = "#", delim = ",", col_types = "cc", progress = FALSE, altrep = FALSE) expect_equal(out4$y, NA_character_) expect_warning(out5 <- vroom(I("x1,x2,x3\nA2,B2,C2\nA3#,B2,C2\nA4,A5,A6"), comment = "#", delim = ",", col_types = "ccc", altrep = FALSE, progress = FALSE)) expect_warning(out6 <- vroom(I("x1,x2,x3\nA2,B2,C2\nA3,#B2,C2\nA4,A5,A6"), comment = "#", delim = ",", col_types = "ccc", altrep = FALSE, progress = FALSE)) expect_warning(out7 <- vroom(I("x1,x2,x3\nA2,B2,C2\nA3,#B2,C2\n#comment\nA4,A5,A6"), comment = "#", delim = ",", col_types = "ccc", altrep = FALSE, progress = FALSE)) chk <- tibble::tibble( x1 = c("A2", "A3", "A4"), x2 = c("B2", NA_character_, "A5"), x3 = c("C2", NA_character_, "A6")) expect_true(all.equal(chk, out5, check.attributes = FALSE)) expect_true(all.equal(chk, out6, check.attributes = FALSE)) expect_true(all.equal(chk, out7, check.attributes = FALSE)) }) test_that("escaped/quoted comments are ignored", { out1 <- vroom(I('x\n\\#'), comment = "#", delim = ",", escape_backslash = TRUE, escape_double = FALSE, progress = FALSE, col_types = "c") out2 <- vroom(I('x\n"#"'), comment = "#", progress = FALSE, delim = ",", col_types = "c") expect_equal(out1$x, "#") expect_equal(out2$x, "#") }) test_that("name repair with custom functions works", { add_y <- function(x) { paste(x, "y", sep = "_") } out <- vroom(I("x,y,z\n1,2,3"), col_types = "iii", .name_repair = add_y) expect_equal(colnames(out), c("x_y", "y_y", "z_y")) }) test_that("col_types are based on the final (possibly repaired) column names (#311)", { suppressMessages( out <- vroom(I("x,\n1,2\n3,4"), delim = ",", col_types = list(x = col_double(), "...2" = col_double())) ) expect_equal(out[["...2"]], c(2, 4)) }) test_that("mismatched column names throw a classed warning", { expect_warning( vroom( I("x,y\n1,2\n3,4\n"), col_types = list( x = col_double(), y = col_double(), z = col_double() ) ), class = "vroom_mismatched_column_name" ) }) test_that("empty files still generate the correct column width and types", { out <- vroom(I(""), col_names = c("foo", "bar"), col_types = list()) expect_equal(nrow(out), 0) expect_equal(ncol(out), 2) expect_equal(names(out), c("foo", "bar")) expect_type(out[[1]], "character") expect_type(out[[2]], "character") out <- vroom(I(""), col_types = "ii") expect_equal(nrow(out), 0) expect_equal(ncol(out), 2) expect_equal(names(out), c("X1", "X2")) expect_type(out[[1]], "integer") expect_type(out[[2]], "integer") }) test_that("leading whitespace effects guessing", { out <- vroom(I('a,b,c\n 1,2,3\n'), delim = ",", trim_ws = FALSE, progress = FALSE, col_types = list()) expect_type(out[[1]], "character") out <- vroom(I('a,b,c\n 1,2,3\n'), delim = ",", trim_ws = TRUE, progress = FALSE, col_types = list()) expect_type(out[[1]], "double") }) test_that("UTF-16LE encodings can be read", { bom <- as.raw(c(255, 254)) # This is the text. text <- "x,y\n\U104371,2\n" # This is a 4 byte UTF-16 character from https://en.wikipedia.org/wiki/UTF-16 # Converted to UTF-16LE text_utf16 <- iconv(text,from="UTF-8", to="UTF-16LE", toRaw = TRUE)[[1]] # Write the BOM and the text to a file tmp_file_name <- tempfile() fd <- file(tmp_file_name, "wb") writeBin(bom, fd) writeBin(text_utf16, fd) close(fd) # Whether LE or BE is determined automatically by the BOM out <- vroom(tmp_file_name, locale = locale(encoding = "UTF-16"), col_types = "ci") expect_equal(out$x, "\U104371") expect_equal(out$y, 2) }) test_that("supports unicode grouping and decimal marks (https://github.com/tidyverse/readr/issues/796)", { test_vroom(I("1\u00A0234\u02D95"), locale = locale(grouping_mark = "\u00A0", decimal_mark = "\u02D9"), col_types = "n", col_names = FALSE, delim = ",", equals = tibble::tibble(X1 = 1234.5) ) }) test_that("handles quotes within skips", { data <- I(paste0(collapse = "\n", c("a\tb\tc", "1a\t1b\t1c", "2a\t2b\t2c\"", "3a\t3b\t3c\"", "4a\t4b\t4c" ))) test_vroom(data, col_names = c("a", "b", "c"), skip = 2, quote = "", delim = "\t", equals = tibble::tibble( a = c("2a", "3a", "4a"), b = c("2b", "3b", "4b"), c = c("2c\"", "3c\"", "4c") ) ) test_vroom(data, col_names = c("a", "b", "c"), skip = 3, quote = "", delim = "\t", equals = tibble::tibble( a = c("3a", "4a"), b = c("3b", "4b"), c = c("3c\"", "4c") ) ) test_vroom(data, col_names = c("a", "b", "c"), skip = 4, quote = "", delim = "\t", equals = tibble::tibble( a = c("4a"), b = c("4b"), c = c("4c") ) ) }) test_that("skipped columns retain their name", { test_vroom(I("1,2,3\n4,5,6"), col_names = "x", col_types = "i__", equals = tibble::tibble( x = c(1L, 4L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "y", col_types = "_i_", equals = tibble::tibble( y = c(2L, 5L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "z", col_types = "__i", equals = tibble::tibble( z = c(3L, 6L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = c("x", "z"), col_types = "i_i", equals = tibble::tibble( x = c(1L, 4L), z = c(3L, 6L) )) }) test_that("skipped columns retain their name", { test_vroom(I("1,2,3\n4,5,6"), col_names = "x", col_types = "i__", equals = tibble::tibble( x = c(1L, 4L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "y", col_types = "_i_", equals = tibble::tibble( y = c(2L, 5L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "z", col_types = "__i", equals = tibble::tibble( z = c(3L, 6L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = c("x", "z"), col_types = "i_i", equals = tibble::tibble( x = c(1L, 4L), z = c(3L, 6L) )) }) test_that("unnamed column types can be less than the number of columns", { test_vroom("x,y\n1,2\n", col_types = "i", equals = tibble::tibble( x = 1L, y = 2L )) }) test_that("always include the last row when guessing (#352)", { f <- tempfile() on.exit(unlink(f)) vroom_write(data.frame("x" = c(rep(NA, 10), 5)), delim = ",", file = f) x <- vroom(f, col_types = "?", guess_max = 5, delim = ",") expect_type(x[[1]], "double") }) test_that("vroom works with quote even in the first two lines (#1262)", { text <- c("1,'I am sam' 2,'sam I am'") test_vroom(text, col_names = FALSE, quote = "'", delim = ",", equals = tibble::tibble(X1 = c(1, 2), X2 = c("I\nam\nsam", "sam\nI\nam"))) }) test_that("vroom works when grouping_mark is empty (#1241)", { x <- vroom(I("foo\nbar"), locale = locale(grouping_mark = ""), delim = ",", col_names = FALSE, col_types = "c") expect_equal(x[[1]], c("foo", "bar")) }) test_that("vroom works if given col_names and col_types less than the number of columns (https://github.com/tidyverse/readr/issues/1271)", { x <- vroom( I("a\tb\n"), delim = "\t", col_names = c("x"), col_types = list("x" = "c") ) expect_equal(x[["x"]], "a") expect_equal(x[["X2"]], "b") }) test_that("vroom works with CR line endings only", { test_vroom(I("a,b\r1,2\r3,4\r"), delim = ",", equals = tibble::tibble(a = c(1, 3), b = c(2, 4)) ) }) test_that("vroom works with quotes in comments", { test_vroom(I("a,b\n#bar \" xyz\n1,2"), delim = ",", comment = "#", equals = tibble::tibble(a = 1, b = 2) ) test_vroom(I("#foo \" \na,b\n#bar \" xyz\n1,2"), delim = ",", comment = "#", equals = tibble::tibble(a = 1, b = 2) ) }) test_that("vroom works with comments at end of lines (https://github.com/tidyverse/readr/issues/1309)", { test_vroom(I("foo,bar#\n1,#\n2#\n#\n3\n"), delim = ",", comment = "#", equals = tibble::tibble(foo = c(1,2,3), bar = c(NA, NA, NA)) ) }) test_that("vroom does not erronously warn for problems when there are embedded newlines and parsing needs to be restarted (https://github.com/tidyverse/readr/issues/1313))", { withr::local_seed(1) sample_values <- function(n, p_safe) { sample(c("safe", "UNSAFE\n"), n, replace = TRUE, prob = c(p_safe, 1 - p_safe)) } n <- 300 df <- tibble::tibble( a = sample_values(n, p_safe = .99), b = sample_values(n, p_safe = .01), c = sample_values(n, p_safe = .01) ) # write to temp file path <- tempfile(pattern = "quoted_newlines_", fileext = ".csv") withr::defer(unlink(path)) vroom_write(df, path, delim = ",") x <- vroom(path, delim = ",", col_types = list()) y <- utils::read.csv(path, stringsAsFactors = FALSE) expect_warning(expect_equal(as.data.frame(x), y), NA) }) test_that("n_max works with files without a trailing newline for file connections (https://github.com/tidyverse/readr/issues/1321)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("foo,bar 1,2 3,4 5,6"), f) x <- vroom(f, n_max = Inf, delim = ",", col_types = list()) y <- vroom(f, n_max = 4, delim = ",", col_types = list()) z <- vroom(f, n_max = 5, delim = ",", col_types = list()) expect_equal(y, x) expect_equal(z, x) }) vroom/tests/testthat/test-path.R0000644000176200001440000000506414142515724016464 0ustar liggesusersmt <- vroom(vroom_example("mtcars.csv"), col_types = list()) test_that("vroom errors if the file does not exist", { tf <- tempfile() expect_error(vroom(tf, col_types = list()), "does not exist") }) test_that("vroom works with compressed files", { expect_equal(vroom(vroom_example("mtcars.csv.gz"), col_types = list()), mt) expect_equal(vroom(vroom_example("mtcars.csv.bz2"), col_types = list()), mt) expect_equal(vroom(vroom_example("mtcars.csv.xz"), col_types = list()), mt) expect_equal(vroom(vroom_example("mtcars.csv.zip"), col_types = list()), mt) }) test_that("read_file works via https", { skip_on_cran() url <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv" expect_equal(vroom(url, col_types = list()), mt) }) test_that("vroom works via https on gz file", { skip_on_cran() url <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv.gz" expect_equal(vroom(url, col_types = list()), mt) }) test_that("vroom errors via https on non-gz file", { skip_on_cran() url <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv.bz2" expect_error(vroom(url, col_types = list()), "Reading from remote `bz2` compressed files is not supported") }) test_that("split_path_ext works", { expect_equal( split_path_ext(character()), list(path = character(), extension = "") ) expect_equal( split_path_ext(""), list(path = "", extension = "") ) expect_equal( split_path_ext("foo"), list(path = "foo", extension = "") ) expect_equal( split_path_ext("foo/bar.baz"), list(path = "foo/bar", extension = "baz") ) expect_equal( split_path_ext("foo/bar.tar.gz"), list(path = "foo/bar", extension = "tar.gz") ) }) test_that("can write to a zip file if the archive package is available", { skip_on_cran() skip_if(!rlang::is_installed("archive")) tempfile <- file.path(tempdir(), "mtcars.zip") on.exit(unlink(tempfile)) vroom_write(mtcars, tempfile) # PK is the zip magic number expect_equal( readBin(tempfile, raw(), n = 2), as.raw(c(0x50, 0x4b)) ) }) test_that("can write to a tar.gz file if the archive package is available", { skip_on_cran() skip_if(!rlang::is_installed("archive")) tempfile <- file.path(tempdir(), "mtcars.tar.gz") on.exit(unlink(tempfile)) vroom_write(mtcars, tempfile) # 1F 8B is the gz magic number expect_equal( readBin(tempfile, raw(), n = 2), as.raw(c(0x1f, 0x8b)) ) res <- archive::archive(tempfile) expect_equal(res$path, "mtcars") expect_equal(res$size, 1281) }) vroom/tests/testthat/test-chr.R0000644000176200001440000000167214142515724016305 0ustar liggesusers# Encoding ---------------------------------------------------------------- test_that("locale encoding affects parsing", { x <- c("ao\u00FBt", "\u00E9l\u00E8ve", "\u00E7a va") #expect_equal(Encoding(x), rep("UTF-8", 3)) y <- iconv(paste0(x, collapse = "\n"), "UTF-8", "latin1") #expect_equal(Encoding(y), "latin1") fr <- locale("fr", encoding = "latin1") z <- vroom(I(y), delim = ",", locale = fr, col_names = FALSE, col_types = list()) # expect_equal(Encoding(z[[1]]), rep("UTF-8", 3)) # identical coerces encodings to match, so need to compare raw values as_raw <- function(x) lapply(x, charToRaw) expect_identical(as_raw(x), as_raw(z[[1]])) }) test_that("encodings are respected", { loc <- locale(encoding = "ISO-8859-1") expected <- c("fran\u00e7ais", "\u00e9l\u00e8ve") x <- vroom(test_path("enc-iso-8859-1.txt"), delim = "\n", locale = loc, col_names = FALSE, col_types = list()) expect_equal(x[[1]], expected) }) vroom/tests/testthat/fwf-trailing.txt0000644000176200001440000000002014142512075017541 0ustar liggesusers123 123 123 123 vroom/tests/testthat/raw.csv0000644000176200001440000000002114142515724015722 0ustar liggesusersabc,def abc,def vroom/tests/testthat/test-vroom_fwf.R0000644000176200001440000002630614142515724017536 0ustar liggesuserstest_that("trailing spaces omitted", { spec <- fwf_empty(test_path("fwf-trailing.txt")) expect_equal(spec$begin, c(0, 4)) expect_equal(spec$end, c(3, NA)) df <- vroom_fwf(test_path("fwf-trailing.txt"), spec, col_types = list()) expect_equal(df$X1, df$X2) }) test_that("dos newlines handles", { spec <- fwf_empty(test_path("fwf-trailing.txt")) x <- vroom_fwf(test_path("fwf-trailing.txt"), spec, col_types = list()) y <- vroom_fwf(test_path("fwf-trailing-crlf.txt"), spec, col_types = list()) expect_equal(x, y) z <- vroom_fwf(file(test_path("fwf-trailing-crlf.txt")), spec, col_types = list()) expect_equal(z, x) }) test_that("connections and normal files produce identical output", { spec <- fwf_empty(test_path("fwf-trailing.txt")) y <- vroom_fwf(test_path("fwf-trailing-crlf.txt"), spec, col_types = list()) x <- vroom_fwf(file(test_path("fwf-trailing-crlf.txt")), spec, col_types = list()) expect_equal(x, y) }) test_that("respects the trim_ws argument", { x <- I("a11 b22 c33\nd e f ") out1 <- vroom_fwf(x, fwf_empty(x), trim_ws = FALSE, col_types = list()) expect_equal(out1$X1, c("a11", "d ")) expect_equal(out1$X2, c("b22", "e ")) expect_equal(out1$X3, c("c33", "f ")) out2 <- vroom_fwf(x, fwf_empty(x), trim_ws = TRUE, col_types = list()) expect_equal(out2$X1, c("a11", "d")) expect_equal(out2$X2, c("b22", "e")) expect_equal(out2$X3, c("c33", "f")) }) test_that("respects the trim_ws argument with empty fields", { x <- I("a11 b22 c33\nd f ") out1 <- vroom_fwf(x, fwf_empty(x), trim_ws = FALSE, col_types = list()) expect_equal(out1$X1, c("a11", "d ")) expect_equal(out1$X2, c("b22", " ")) expect_equal(out1$X3, c("c33", "f ")) out1 <- vroom_fwf(x, fwf_empty(x), trim_ws = TRUE, na = "NA", col_types = list()) }) test_that("skipping column doesn't pad col_names", { x <- I("1 2 3\n4 5 6") out1 <- vroom_fwf(x, fwf_empty(x), col_types = 'd-d') expect_named(out1, c("X1", "X3")) names <- c("a", "b", "c") out2 <- vroom_fwf(x, fwf_empty(x, col_names = names), col_types = 'd-d') expect_named(out2, c("a", "c")) }) test_that("fwf_empty can skip comments", { x <- I("COMMENT\n1 2 3\n4 5 6") out1 <- vroom_fwf(x, fwf_empty(x, comment = "COMMENT"), comment = "COMMENT", col_types = list()) expect_equal(dim(out1), c(2, 3)) }) test_that("fwf_empty can skip lines", { x <- I("foo\nbar\baz\n1 2 3\n4 5 6\n") obj <- fwf_empty(x, skip = 3) exp <- list(begin = c(0L, 2L, 4L), end = c(1L, 3L, NA_integer_), col_names = c("X1", "X2", "X3")) expect_equal(obj, exp) }) test_that("passing \"\" to vroom_fwf's 'na' option", { expect_equal(vroom_fwf(I("foobar\nfoo "), fwf_widths(c(3, 3)), na = "", col_types = list())[[2]], c("bar", NA)) }) test_that("ragged last column expanded with NA", { x <- vroom_fwf(I("1a\n2ab\n3abc"), fwf_widths(c(1, NA)), col_types = list()) expect_equal(x$X2, c("a", "ab", "abc")) }) #test_that("ragged last column shrunk with warning", { #expect_warning(x <- vroom_fwf(I("1a\n2ab\n3abc"), fwf_widths(c(1, 3)))) #expect_equal(x$X2, c("a", "ab", "abc")) #}) test_that("read all columns with positions, non ragged", { col_pos <- fwf_positions(c(1,3,6),c(2,5,6)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X3, c("A", "B", "C")) }) test_that("read subset columns with positions", { col_pos <- fwf_positions(c(1,3),c(2,5)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X1, c(12, 67, 54)) expect_equal(x$X2, c(345, 890, 321)) }) test_that("read columns with positions, ragged", { col_pos <- fwf_positions(c(1,3,6),c(2,5,NA)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X1, c(12, 67, 54)) expect_equal(x$X2, c(345, 890, 321)) expect_equal(x$X3, c('A', 'BBBBBBBBB', 'C')) }) test_that("read columns with width, ragged", { col_pos <- fwf_widths(c(2,3,NA)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X1, c(12, 67, 54)) expect_equal(x$X2, c(345, 890, 321)) expect_equal(x$X3, c('A', 'BBBBBBBBB', 'C')) }) #test_that("vroom_fwf returns an empty data.frame on an empty file", { ##expect_true(all.equal(vroom_fwf(test_path("empty-file")), tibble::data_frame())) #}) #test_that("check for line breaks in between widths", { #txt1 <- paste( #"1 1", #"2", #"1 1 ", #sep = "\n" #) #expect_warning(out1 <- vroom_fwf(txt1, fwf_empty(txt1))) #expect_equal(n_problems(out1), 2) #txt2 <- paste( #" 1 1", #" 2", #" 1 1 ", #sep = "\n" #) #expect_warning(out2 <- vroom_fwf(txt2, fwf_empty(txt2))) #expect_equal(n_problems(out2), 2) #exp <- tibble::tibble(X1 = c(1L, 2L, 1L), X2 = c(1L, NA, 1L)) #expect_true(all.equal(out1, exp)) #expect_true(all.equal(out2, exp)) #}) #test_that("error on empty spec (#511, #519)", { #txt = "foo\n" #pos = fwf_positions(start = numeric(0), end = numeric(0)) #expect_error(vroom_fwf(txt, pos), "Zero-length.*specifications not supported") #}) #test_that("error on negatives in fwf spec", { #txt = "foo\n" #pos = fwf_positions(start = c(1, -1), end = c(2, 3)) #expect_error(vroom_fwf(txt, pos), ".*offset.*greater than 0") #}) test_that("fwf spec can overlap", { x <- vroom_fwf(I("2015a\n2016b"), fwf_positions(c(1, 3, 5), c(4, 4, 5)), col_types = list()) expect_equal(x$X1, c(2015, 2016)) expect_equal(x$X2, c(15, 16)) expect_equal(x$X3, c("a", "b")) }) # fwf_cols test_that("fwf_cols produces correct fwf_positions object with elements of length 2", { expected <- fwf_positions(c(1L, 9L, 4L), c(2L, 12L, 6L), c("a", "b", "d")) expect_equal(fwf_cols(a = c(1, 2), b = c(9, 12), d = c(4, 6)), expected, ignore_attr = TRUE) }) test_that("fwf_cols produces correct fwf_positions object with elements of length 1", { expected <- fwf_widths(c(2L, 4L, 3L), c("a", "b", "c")) expect_equal(fwf_cols(a = 2, b = 4, c = 3), expected, ignore_attr = TRUE) }) test_that("fwf_cols throws error when arguments are not length 1 or 2", { expect_error(fwf_cols(a = 1:3, b = 4:5)) expect_error(fwf_cols(a = c(), b = 4:5)) }) test_that("fwf_cols works with unnamed columns", { expect_equal(ignore_attr = TRUE, fwf_cols(c(1, 2), c(9, 12), c(4, 6)), fwf_positions(c(1L, 9L, 4L), c(2L, 12L, 6L), c("X1", "X2", "X3")) ) expect_equal(ignore_attr = TRUE, fwf_cols(a = c(1, 2), c(9, 12), c(4, 6)), fwf_positions(c(1L, 9L, 4L), c(2L, 12L, 6L), c("a", "X2", "X3")) ) }) # fwf_positions --------------------------------------------------------------- test_that("fwf_positions always returns col_names as character (#797)", { begin <- c(1, 2, 4, 8) end <- c(1, 3, 7, 15) # Input a factor, should return a character nms <- factor(letters[1:4]) info <- fwf_positions(begin, end, nms) expect_type(info$begin, "double") expect_type(info$end, "double") expect_type(info$col_names, "character") }) # Robustness test_that("vroom_fwf() is robust to improper inputs", { expect_error_free( vroom_fwf(I("foo bar baz\n1 2\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 6\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("A\n a\n"), col_types = list()) ) }) test_that("Errors if begin is greater than end", { positions <- fwf_positions( start = c(1, 3, 5), end = c(3, 1, NA), col_names = c("foo", "bar", "baz") ) expect_error( vroom_fwf(I("1 2 3\n"), positions, col_types = list()), "`col_positions` must have begin less than end" ) }) test_that("vroom_fwf respects n_max (#334)", { out <- vroom_fwf(I("foo 1\nbar 2\nbaz 3\nqux 4"), n_max = 0, col_types = list()) expect_named(out, c("X1", "X2")) expect_equal(out[[1]], character()) expect_equal(out[[2]], character()) out <- vroom_fwf(I("foo 1\nbar 2\nbaz 3\nqux 4"), n_max = 1, col_types = list()) expect_named(out, c("X1", "X2")) expect_equal(out[[1]], c("foo")) expect_equal(out[[2]], c(1)) out <- vroom_fwf(I("foo 1\nbar 2\nbaz 3\nqux 4"), n_max = 2, col_types = list()) expect_named(out, c("X1", "X2")) expect_equal(out[[1]], c("foo", "bar")) expect_equal(out[[2]], c(1, 2)) }) test_that("vroom_fwf respects n_max when reading from a connection", { f <- tempfile() on.exit(unlink(f)) writeLines(rep("00010002", 1000), f) out1 <- vroom_fwf(file(f), col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out1), c(1000, 2)) out2 <- vroom_fwf(file(f), n_max = 900, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out2), c(900, 2)) out3 <- vroom_fwf(file(f), n_max = 100, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out3), c(100, 2)) out4 <- vroom_fwf(file(f), n_max = 10, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out4), c(10, 2)) out5 <- vroom_fwf(file(f), n_max = 1, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out5), c(1, 2)) }) test_that("vroom_fwf works when skip_empty_rows is false (https://github.com/tidyverse/readr/issues/1211)", { f <- tempfile() on.exit(unlink(f)) writeLines(rep(" ", 10), f) out <- vroom_fwf(f, fwf_cols(A = c(1, NA)), col_types = "c", na = " ", trim_ws = FALSE, skip_empty_rows = FALSE) expect_equal(out[[1]], rep(NA_character_, 10)) }) test_that("vroom_fwf correctly reads files with no trailing newline (https://github.com/tidyverse/readr/issues/1293)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("111222\n333444"), f) out <- vroom_fwf(f, fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, tibble::tibble(X1 = c(111, 333), X2 = c(222, 444))) out2 <- vroom_fwf(file(f), fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, out2) }) test_that("vroom_fwf correctly reads DOS files with no trailing newline (https://github.com/tidyverse/readr/issues/1293)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("111222\r\n333444"), f) out <- vroom_fwf(f, fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, tibble::tibble(X1 = c(111, 333), X2 = c(222, 444))) out2 <- vroom_fwf(file(f), fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, out2) }) vroom/tests/testthat/helper.R0000644000176200001440000000436214142515724016032 0ustar liggesuserstest_vroom <- function(content, delim = ",", col_types = list(), ..., equals) { if(any(grepl("\n", content))) { content <- I(content) } suppressWarnings({ # with altrep withr::with_envvar(c("VROOM_USE_ALTREP_CHR" = "true", "VROOM_USE_ALTREP_NUMERICS" = "true"), { expect_equal( vroom(content, delim = delim, col_types = col_types, ...), equals ) }) # without altrep withr::with_envvar(c("VROOM_USE_ALTREP_CHR" = "false", "VROOM_USE_ALTREP_NUMERICS" = "false"), { expect_equal( vroom(content, delim = delim, col_types = col_types, ...), equals ) }) if (!file.exists(content)) { tf <- tempfile() on.exit(unlink(tf)) out_con <- file(tf, "wb", encoding = "UTF-8") writeBin(charToRaw(content), out_con) close(out_con) con <- file(tf, "rb") } else { con <- file(content, "rb") } on.exit(close(con), add = TRUE) res <- vroom(con, delim = delim, col_types = col_types, ...) expect_equal(res, equals) for (i in seq_along(res)) { force_materialization(res[[i]]) } expect_equal(res, equals) }) invisible(res) } test_parse_number <- function(x, expected, ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = "n", ..., equals = tibble::tibble(X1 = expected) ) } test_parse_datetime <- function(x, expected, format = "", ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = cols(X1 = col_datetime(format = format)), ..., equals = tibble::tibble(X1 = expected) ) } test_parse_date <- function(x, expected, format = "", ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = cols(X1 = col_date(format = format)), ..., equals = tibble::tibble(X1 = expected) ) } test_parse_time <- function(x, expected, format = "", ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = cols(X1 = col_time(format = format)), ..., equals = tibble::tibble(X1 = expected) ) } expect_error_free <- function(object, ...) { expect_error(object, regexp = NA) } on_github_actions <- function() { identical(Sys.getenv("GITHUB_ACTIONS"), "true") } vroom/tests/testthat/test-vroom_lines.R0000644000176200001440000000364614142515724020070 0ustar liggesuserstest_that("vroom_lines works with normal files", { infile <- vroom_example("mtcars.csv") expected <- readLines(infile) actual <- vroom_lines(infile) expect_equal(length(actual), length(expected)) expect_equal(head(actual), head(expected)) expect_equal(tail(actual), tail(expected)) expect_equal(actual, expected) }) test_that("vroom_lines works with connections files", { infile <- vroom_example("mtcars.csv") con <- file(infile) expected <- readLines(con) close(con) actual <- vroom_lines(file(infile)) expect_equal(length(actual), length(expected)) expect_equal(head(actual), head(expected)) expect_equal(tail(actual), tail(expected)) expect_equal(actual, expected) }) test_that("vroom_lines works with files with no trailing newline", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("foo"), f) expect_equal(vroom_lines(f), "foo") f2 <- tempfile() on.exit(unlink(f2), add = TRUE) writeBin(charToRaw("foo\nbar"), f2) expect_equal(vroom_lines(f2), c("foo", "bar")) }) test_that("vroom_lines respects n_max", { infile <- vroom_example("mtcars.csv") expect_equal(vroom_lines(infile, n_max = 2), readLines(infile, n = 2)) }) test_that("vroom_lines works with empty files", { f <- tempfile() file.create(f) on.exit(unlink(f)) expect_equal(vroom_lines(f), character()) }) test_that("vroom_lines uses na argument", { expect_equal(vroom_lines(I("abc\n123"), progress = FALSE), c("abc", "123")) expect_equal(vroom_lines(I("abc\n123"), na = "abc", progress = FALSE), c(NA_character_, "123")) expect_equal(vroom_lines(I("abc\n123"), na = "123", progress = FALSE), c("abc", NA_character_)) expect_equal(vroom_lines(I("abc\n123"), na = c("abc", "123"), progress = FALSE), c(NA_character_, NA_character_)) }) test_that("vroom_lines works with files with mixed line endings", { expect_equal(vroom_lines(I("foo\r\n\nbar\n\r\nbaz\r\n")), c("foo", "", "bar", "", "baz")) }) vroom/tests/testthat/mtcars.zip0000644000176200001440000000133114064431075016435 0ustar liggesusersPKόvΦR mtcarsUT =2`=2`=2`ux υu”Ϋn1 DŸιΔ›.Ÿγ:AZ \;hΡΏοΪK¦/›φhHŽF~ΏΏΡνχwzωφΌΣΧ;½<τλƒ~<_oτσIΧwz{½>θv}|ΉS#n•˜+i™$₯ Š5Β"ΩgΜθNάK•Β"^Έš j80…Τ(‰“bHƒ˜ψΨΔ€k†Ϊ,z »:αSE)-Ά’I`Ρ›φΧ(Ψ[`X©EδT³’›š˜gA<Ό{Ά©aCs(a Εα‚–6£4©e(ΉΟigˆMYύαΛ<0Œ#ιZ/xŠξ\Œ0ΠΝβμ‚yΖΉyr8Œ γKZΧΙβ‰} =ύ—Σ5ΈvrŽώ>γβΏ“ͺYΥΊΐ‚πG‹yφsΖνXψΖ$ΟjΘ Y© zœZŠΩΙ©δ)τH@k1"ΪD³~Iλ’” (tΖHš‡m.'Ό‚γŠƒ‹X”‘Έωw.=^¦™F ¨aV*ŸIrpH›©fuΏ yiΖrΗ²ΛIhcΦyψΖ°Εχ m“ΆmN₯‰4…)Ψb€ž—ΩΓ΅εξtΈΑMuX_―ΰ–˜―9Gΐ$Ν–»¬ίΤ²Ο†£ά-IE?¨­t 7SƒqΫ¨•Ά1cύ|Dϋh β\ s(ό•9ΞόPK=MΝPKόvΦR=MΝ €mtcarsUT =2`=2`=2`ux υPKTovroom/tests/testthat/multi-file/0000755000176200001440000000000014142515724016472 5ustar liggesusersvroom/tests/testthat/multi-file/baz0000644000176200001440000000001414142512075017160 0ustar liggesusersA,B,C 1,2,3 vroom/tests/testthat/multi-file/foo0000644000176200001440000000001014142512075017163 0ustar liggesusersA,B 1,2 vroom/tests/testthat/multi-file/qux0000644000176200001440000000000414142515724017224 0ustar liggesusersA,B vroom/tests/testthat/multi-file/bar0000644000176200001440000000001014142512075017144 0ustar liggesusersC,D 3,4 vroom/tests/testthat/test-select.R0000644000176200001440000000530014142515724017000 0ustar liggesuserstest_that("col_select works", { expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = 1, col_types = list())), "model") expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = 1:3, col_types = list())), c("model", "mpg", "cyl")) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = list(1, 5, 7), col_types = list())), c("model", "hp", "wt")) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = c("model", "hp", "wt"), col_types = list())), c("model", "hp", "wt")) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = c(model:cyl), col_types = list())), c("model", "mpg", "cyl")) }) test_that("col_select with negations works", { expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -1, col_types = list())), c("mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb") ) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -(1:3), col_types = list())), c("disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb") ) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -c(1, 5, 7), col_types = list())), c("mpg", "cyl", "disp", "drat", "qsec", "vs", "am", "gear", "carb") ) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -c(model, hp, wt), col_types = list())), c("mpg", "cyl", "disp", "drat", "qsec", "vs", "am", "gear", "carb") ) }) test_that("col_select with renaming", { expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = list(car = model, everything()), col_types = list())), c("car", "mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb") ) }) test_that("col_select works with vroom_fwf", { spec <- fwf_empty(test_path("fwf-trailing.txt")) expect_equal( colnames( vroom_fwf( test_path("fwf-trailing.txt"), spec, col_select = list(foo = X1, X2), col_types = list() ) ), c("foo", "X2") ) }) test_that("col_select can select the id column", { expect_named( vroom(vroom_example("mtcars.csv"), id = "path", col_select = list(model, mpg, path), col_types = list()), c("model", "mpg", "path") ) expect_named( vroom(vroom_example("mtcars.csv"), id = "path", col_select = list(path, model, mpg), col_types = list()), c("path", "model", "mpg") ) }) test_that("col_select works with col_names = FALSE", { res <- vroom(I("foo\tbar\n1\t2\n"), col_names = FALSE, col_select = 1, col_types = list()) expect_equal(res[[1]], c("foo", "1")) res2 <- vroom(I("foo\tbar\n1\t2\n"), col_names = FALSE, col_select = c(X2), col_types = list()) expect_equal(res2[[1]], c("bar", "2")) }) vroom/tests/testthat/logs/0000755000176200001440000000000014142304304015355 5ustar liggesusersvroom/tests/testthat/logs/fixed_width_index_connection.idx0000644000176200001440000000000014142304316023760 0ustar liggesusersvroom/tests/testthat/logs/index.idx0000644000176200001440000000000014142304304017160 0ustar liggesusersvroom/tests/testthat/logs/index_connection.idx0000644000176200001440000000000014142304322021377 0ustar liggesusersvroom/tests/testthat/logs/fixed_width_index.idx0000644000176200001440000000000014142304316021541 0ustar liggesusersvroom/tests/testthat/test-multi-file.R0000644000176200001440000000663514143524032017575 0ustar liggesuserstest_that("vroom adds the id column from the filename for one file", { res <- vroom(vroom_example("mtcars.csv"), id = "filename", col_types = list()) expect_true(all(res$filename == vroom_example("mtcars.csv"))) }) test_that("vroom adds the id column from the filename for multiple files", { dir <- tempfile() dir.create(dir) splits <- split(mtcars, mtcars$cyl) for (i in seq_along(splits)) { vroom_write(splits[[i]], file.path(dir, paste0("mtcars_", names(splits)[[i]], ".tsv")), delim = "\t") } files <- list.files(dir, full.names = TRUE) res <- vroom(files, id = "filename", col_types = list()) # construct what the filename column should look like filenames <- paste0("mtcars_", rep(names(splits), vapply(splits, nrow, integer(1))), ".tsv") expect_equal(basename(res$filename), filenames) }) test_that("vroom adds the id column from the filename for multiple connections", { dir <- tempfile() dir.create(dir) splits <- split(mtcars, mtcars$cyl) for (i in seq_along(splits)) { # write_tsv will automatically gzip them vroom_write(splits[[i]], file.path(dir, paste0("mtcars_", names(splits)[[i]], ".tsv.gz")), delim = "\t") } files <- list.files(dir, full.names = TRUE) res <- vroom(files, id = "filename", col_types = list()) # construct what the filename column should look like filenames <- paste0("mtcars_", rep(names(splits), vapply(splits, nrow, integer(1))), ".tsv.gz") expect_equal(basename(res$filename), filenames) }) test_that("vroom works with many files", { skip_on_os("solaris") dir <- tempfile() dir.create(dir) on.exit(unlink(dir, recursive = TRUE)) for (i in seq_len(200)) { vroom_write( tibble::tibble( x = rnorm(10), y = rnorm(10), ), file.path(dir, paste0(i, ".csv")), delim = ",") } files <- list.files(dir, pattern = ".*[.]csv", full.names = TRUE) res <- vroom::vroom(files, col_types = list()) expect_equal(colnames(res), c("x", "y")) expect_equal(NROW(res), 2000) }) test_that("vroom works with many connections", { skip_on_os("solaris") dir <- tempfile() dir.create(dir) on.exit(unlink(dir, recursive = TRUE)) for (i in seq_len(200)) { vroom_write( tibble::tibble( x = rnorm(10), y = rnorm(10), ), file.path(dir, paste0(i, ".csv.gz")), delim = ",") } files <- list.files(dir, pattern = ".*[.]csv[.]gz", full.names = TRUE) res <- vroom::vroom(files, col_types = list()) expect_equal(colnames(res), c("x", "y")) expect_equal(NROW(res), 2000) }) test_that("vroom errors if numbers of columns are inconsistent", { files <- test_path("multi-file", c("foo", "baz")) expect_error(vroom::vroom(files, col_types = list()), "must all have") }) test_that("vroom errors if column names are inconsistent", { files <- test_path("multi-file", c("foo", "bar")) expect_error(vroom::vroom(files, col_types = list()), "consistent column names") }) test_that("vroom works if a file contains no data", { files <- test_path("multi-file", c("foo", "qux")) res <- vroom(files, col_types = list()) expect_equal(res, tibble::tibble(A = 1, B = 2)) }) test_that("vroom works for indxes that span file boundries (#383)", { x <- vroom(c(vroom_example("mtcars.csv"), vroom_example("mtcars.csv")), col_types = list()) y <- rbind(mtcars, mtcars) idx <- c(c(34, 33), sample(NROW(x), size = 25, replace = T)) expect_equal(x[idx, 5, drop = TRUE], y[idx, 4]) }) vroom/tests/testthat/enc-iso-8859-1.txt0000644000176200001440000000001713746062417017275 0ustar liggesusersfranηais ιlθve vroom/tests/testthat/test-dbl.R0000644000176200001440000000316714142515724016273 0ustar liggesuserstest_that("Large exponents can parse", { res <- vroom(I("1e-63,1e-64\n"), delim = ",", col_types = "dd", col_names = FALSE) expect_equal(res[[1]], 1e-63) expect_equal(res[[2]], 1e-64) }) test_that("Doubles parse correctly with comma as decimal separator", { res <- vroom(I("23,4\n"), delim='\t', altrep=FALSE, locale=locale(decimal_mark=','), col_types='d', col_names=FALSE) expect_equal(res[[1]], 23.4) res2 <- vroom(I("23,4\n"), delim='\t', altrep=TRUE, locale=locale(decimal_mark=','), col_types='d', col_names=FALSE) expect_equal(res2[[1]], 23.4) }) test_that("NA can be a double value", { test_vroom(I("x\n1\n2\n"), delim = ",", col_types = "d", na = "1", equals = tibble::tibble(x = c(NA_real_, 2))) }) test_that("NaN values are guessed and parsed as doubles (https://github.com/tidyverse/readr/issues/1277)", { test_vroom(I("x\nNaN\n"), delim = ",", col_types = "?", equals = tibble::tibble(x = c(NaN))) }) test_that("Test that values with only a NaN prefix are _not_ parsed as doubles", { test_vroom(I("x\nNaNa\n"), delim = ",", col_types = "?", equals = tibble::tibble(x = c("NaNa"))) }) test_that("Inf and -Inf values are guessed and parsed as doubles (https://github.com/tidyverse/readr/issues/1283)", { test_vroom(I("x\nInf\n-Inf\n+Inf"), delim = ",", col_types = "?", equals = tibble::tibble(x = c(Inf, -Inf, Inf))) }) test_that("Test that values with only a Inf prefix are _not_ parsed as doubles", { test_vroom(I("x\nInfa\n-Infb\n+Infc"), delim = ",", col_types = "?", equals = tibble::tibble(x = c("Infa", "-Infb", "+Infc"))) }) vroom/tests/testthat/test-datetime.R0000644000176200001440000002750014142515724017323 0ustar liggesuserstest_that("datetime parsing works", { test_vroom( "date,time,datetime 2018-01-01,10:01:01 AM,2018-01-01 10:01:01 2019-01-01,05:04:03 AM,2019-01-01 05:04:03 ", delim = ",", equals = tibble::tibble( date = c(as.Date("2018-01-01"), as.Date("2019-01-01")), time = c(hms::hms(1, 1, 10), hms::hms(3, 4, 5)), datetime = vctrs::vec_c(as.POSIXct("2018-01-01 10:01:01", tz = "UTC"), as.POSIXct("2019-01-01 05:04:03", tz = "UTC")) ) ) }) # Parsing ---------------------------------------------------------------------- r_parse <- function(x, fmt) as.POSIXct(strptime(x, fmt, tz = "UTC")) test_that("%d, %m and %y", { target <- utctime(2010, 2, 3, 0, 0, 0, 0) test_parse_datetime("10-02-03", "%y-%m-%d", expected = target) test_parse_datetime("10-03-02", "%y-%d-%m", expected = target) test_parse_datetime("03/02/10", "%d/%m/%y", expected = target) test_parse_datetime("02/03/10", "%m/%d/%y", expected = target) }) test_that("Compound formats work", { target <- utctime(2010, 2, 3, 0, 0, 0, 0) test_parse_datetime("02/03/10", "%D", expected = target) test_parse_datetime("2010-02-03", "%F", expected = target) test_parse_datetime("10/02/03", "%x", expected = target) }) test_that("%y matches R behaviour", { test_parse_datetime("01-01-69", "%d-%m-%y", expected = r_parse("01-01-69", "%d-%m-%y")) test_parse_datetime("01-01-68", "%d-%m-%y", expected = r_parse("01-01-68", "%d-%m-%y")) }) test_that("%e allows leading space", { test_parse_datetime("201010 1", "%Y%m%e", expected = utctime(2010, 10, 1, 0, 0, 0, 0)) }) test_that("%OS captures partial seconds", { test_parse_datetime("2001-01-01 00:00:01.125", "%Y-%m-%d %H:%M:%OS", expected = utctime(2001, 1, 1, 0, 0, 1, .125)) test_parse_datetime("2001-01-01 00:00:01.133", "%Y-%m-%d %H:%M:%OS", expected = utctime(2001, 1, 1, 0, 0, 1, .133)) }) test_that("%Y requires 4 digits", { test_parse_date("03-01-01", "%Y-%m-%d", expected = as.Date(NA)) test_parse_date("003-01-01", "%Y-%m-%d", expected = as.Date(NA)) test_parse_date("0003-01-01", "%Y-%m-%d", expected = as.Date("0003-01-01")) test_parse_date("00003-01-01", "%Y-%m-%d", expected = as.Date(NA)) }) test_that("invalid dates return NA", { test_parse_datetime("2010-02-30", "%Y-%m-%d", expected = .POSIXct(NA_real_, tz = "UTC")) }) test_that("failed parsing returns NA", { test_parse_datetime(c("2010-02-ab", "2010-02", "2010/02/01"), "%Y-%m-%d", expected = .POSIXct(rep(NA_real_, 3), tz = "UTC")) }) test_that("invalid specs returns NA", { test_parse_datetime("2010-02-20", "%Y-%m-%m", expected = .POSIXct(NA_real_, tz = "UTC")) }) test_that("ISO8601 partial dates are not parsed", { test_parse_datetime("20", "", expected = .POSIXct(NA_real_, tz = "UTC")) test_parse_datetime("2001", "", expected = .POSIXct(NA_real_, tz = "UTC")) test_parse_datetime("2001-01", "", expected = .POSIXct(NA_real_, tz = "UTC")) }) test_that("Year only gets parsed", { test_parse_datetime("2010", "%Y", expected = ISOdate(2010, 1, 1, 0, tz = "UTC")) test_parse_datetime("2010-06", "%Y-%m", expected = ISOdate(2010, 6, 1, 0, tz = "UTC")) }) test_that("%p detects AM/PM", { test_parse_datetime(c("2015-01-01 01:00 AM", "2015-01-01 01:00 am"), "%F %I:%M %p", expected = .POSIXct(c(1420074000, 1420074000), "UTC") ) test_parse_datetime(c("2015-01-01 01:00 PM", "2015-01-01 01:00 pm"), "%F %I:%M %p", expected = .POSIXct(c(1420117200, 1420117200), "UTC") ) test_parse_datetime("12/31/1991 12:01 AM", "%m/%d/%Y %I:%M %p", expected = .POSIXct(694137660, "UTC")) test_parse_datetime("12/31/1991 12:01 PM", "%m/%d/%Y %I:%M %p", expected = .POSIXct(694180860, "UTC")) test_parse_datetime("12/31/1991 01:01 AM", "%m/%d/%Y %I:%M %p", expected = .POSIXct(694141260, "UTC")) test_parse_datetime(c("12/31/1991 00:01 PM", "12/31/1991 13:01 PM"), "%m/%d/%Y %I:%M %p", expected = .POSIXct(rep(NA_real_, 2), tz = "UTC")) }) test_that("%b and %B are case insensitive", { ref <- as.Date("2001-01-01") test_parse_date("2001 JAN 01", "%Y %b %d", expected = ref) test_parse_date("2001 JANUARY 01", "%Y %B %d", expected = ref) }) test_that("%. requires a value", { ref <- as.Date("2001-01-01") test_parse_date("2001?01?01", "%Y%.%m%.%d", expected = ref) test_parse_date("20010101", "%Y%.%m%.%d", expected = as.Date(NA)) }) test_that("%Z detects named time zones", { ref <- .POSIXct(1285912800, "America/Chicago") ct <- locale(tz = "America/Chicago") test_parse_datetime("2010-10-01 01:00", "", expected = ref, locale = ct) test_parse_datetime("2010-10-01 01:00 America/Chicago", "%Y-%m-%d %H:%M %Z", locale = ct, expected = ref) }) test_that("%Z detects named time zones", { ref <- .POSIXct(1285912800, "UTC") test_parse_datetime("1285912800", "%s", expected = ref) }) test_that("parse_date returns a double like as.Date()", { ref <- as.Date("2001-01-01") res <- test_parse_date("2001-01-01", "", expected = ref) expect_type(res[[1]], "double") }) test_that("parses NA/empty correctly", { expect_equal( vroom(I("x\n\n"), delim = ",", col_types = list(x = "T"), skip_empty_rows = FALSE), tibble::tibble(x = .POSIXct(NA_real_, tz = "UTC")) ) expect_equal( vroom(I("x\n\n"), delim = ",", col_types = list(x = "D"), skip_empty_rows = FALSE), tibble::tibble(x = as.Date(NA)) ) test_parse_datetime("TeSt", "", na = "TeSt", expected = .POSIXct(NA_real_, tz = "UTC")) test_parse_date("TeSt", "", na = "TeSt", expected = as.Date(NA)) }) ## Locales ----------------------------------------------------------------- test_that("locale affects months", { jan1 <- as.Date("2010-01-01") fr <- locale("fr") test_parse_date("1 janv. 2010", "%d %b %Y", locale = fr, expected = jan1) test_parse_date("1 janvier 2010", "%d %B %Y", locale = fr, expected = jan1) }) test_that("locale affects day of week", { a <- as.POSIXct("2010-01-01", tz = "UTC") b <- .POSIXct(unclass(as.Date("2010-01-01")) * 86400, tz = "UTC") fr <- locale("fr") test_parse_datetime("Ven. 1 janv. 2010", "%a %d %b %Y", locale=fr, expected = a) test_parse_datetime("Ven. 1 janv. 2010", "%a %d %b %Y", locale=fr, expected = b) }) test_that("locale affects am/pm", { skip_on_os("windows") skip_on_os("solaris") skip_if_not(l10n_info()$`UTF-8`) expected <- hms::hms(hours = 13, minutes = 30) test_parse_time("01:30 PM", "%H:%M %p", expected = expected) test_parse_time("\UC624\UD6C4 01\UC2DC 30\UBD84", "%p %H\UC2DC %M\UBD84", expected = expected, locale = locale("ko")) }) test_that("locale affects both guessing and parsing", { out <- vroom(I("01/02/2013\n"), delim = ",", col_names = FALSE, col_types = list(), locale = locale(date_format = "%m/%d/%Y")) expect_equal(out[[1]][[1]], as.Date("2013-01-02")) }) ## Time zones ------------------------------------------------------------------ test_that("same times with different offsets parsed as same time", { # From http://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC same_time <- paste("2010-02-03", c("18:30Z", "22:30+04", "1130-0700", "15:00-03:30")) test_parse_datetime(same_time, format = "", expected = rep(utctime(2010, 2, 3, 18, 30, 0, 0), 4)) }) test_that("offsets can cross date boundaries", { expected <- as.POSIXct("2015-02-01 01:00:00Z", tz = "UTC") test_parse_datetime("2015-01-31T2000-0500", expected = expected) test_parse_datetime("2015-02-01T0100Z", expected = expected) }) test_that("unambiguous times with and without daylight savings", { skip("Not working on CI") skip_on_cran() # need to figure out why this fails melb <- locale(tz = "Australia/Melbourne") # Melbourne had daylight savings in 2015 that ended the morning of 2015-04-05 expected_melb <- .POSIXct(c(1428109200, 1428285600), "Australia/Melbourne") test_parse_datetime(c("2015-04-04 12:00:00", "2015-04-06 12:00:00"), locale = melb, expected = expected_melb) # Japan didn't have daylight savings in 2015 expected_ja <- .POSIXct(c(1428116400, 1428289200), "Japan") ja <- locale(tz = "Japan") test_parse_datetime(c("2015-04-04 12:00:00", "2015-04-06 12:00:00"), locale = ja, expected = expected_ja) }) test_that("ambiguous times always choose the earliest time", { ny <- locale(tz = "America/New_York") format <- "%Y-%m-%d %H:%M:%S%z" expected <- as.POSIXct("1970-10-25 01:30:00-0400", tz = "America/New_York", format = format) test_parse_datetime("1970-10-25 01:30:00", locale = ny, expected = expected) }) test_that("nonexistent times return NA", { ny <- locale(tz = "America/New_York") expected <- .POSIXct(NA_real_, tz = "America/New_York") test_parse_datetime("1970-04-26 02:30:00", locale = ny, expected = expected) }) test_that("can use `tz = ''` for system time zone", { withr::local_timezone("Europe/London") system <- locale(tz = "") expected <- as.POSIXct("1970-01-01 00:00:00", tz = "Europe/London") test_parse_datetime("1970-01-01 00:00:00", locale = system, expected = expected) }) test_that("can catch faulty system time zones", { withr::local_timezone("foo") expect_error(locale(tz = ""), "Unknown TZ foo") }) ## Guessing --------------------------------------------------------------------- test_that("DDDD-DD not parsed as date (i.e. doesn't trigger partial date match)", { expect_type(vroom(I("1989-90\n1990-91\n"), delim = "\n", col_types = list())[[1]], "character") }) test_that("leading zeros don't get parsed as date without explicit separator", { expect_type(vroom(I("00010203\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "double") expect_s3_class(vroom(I("0001-02-03\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "Date") }) test_that("must have either two - or none", { expect_s3_class(vroom(I("2000-10-10\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "Date") expect_type(vroom(I("2000-1010\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "character") expect_type(vroom(I("200010-10\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "character") expect_type(vroom(I("20001010\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "double") }) test_that("times are guessed even without AM / PM", { expect_s3_class(guess_type("01:02:03"), "collector_time") expect_s3_class(guess_type("01:02:03 AM"), "collector_time") expect_s3_class(guess_type("01:02:03 PM"), "collector_time") }) test_that("subsetting works with both double and integer indexes", { x <- vroom(I("X1\n2020-01-01 01:01:01"), delim = ",", col_type = "T") dt <- as.POSIXct("2020-01-01 01:01:01", tz = "UTC") na_dt <- .POSIXct(NA_real_, tz = "UTC") expect_equal(x$X1[1L], dt) expect_equal(x$X1[1], dt) expect_equal(x$X1[NA_integer_], na_dt) expect_equal(x$X1[NA_real_], na_dt) }) test_that("malformed date / datetime formats cause R errors", { expect_error( vroom(I("x\n6/28/2016"), delim = ",", col_types = list(x = col_date("%m/%/%Y")), altrep = FALSE), "Unsupported format" ) expect_error( vroom(I("x\n6/28/2016"), delim = ",", col_types = list(x = col_datetime("%m/%/%Y")), altrep = FALSE), "Unsupported format" ) }) test_that("single digit dates and hours are parsed correctly (https://github.com/tidyverse/readr/issues/1276)", { test_vroom(I("4/9/2021 2:18:25 PM"), col_types = list(col_datetime("%m/%d/%Y %H:%M:%S %p")), col_names = "X", delim = ",", equals = tibble::tibble(X = as.POSIXct("2021-04-09 14:18:25", tz = "UTC")) ) }) test_that("durations", { parse_time <- function(x, format, ...) { vroom(I(x), delim = ",", col_names = FALSE, col_types = list(col_time(format = format)), altrep = FALSE)[[1]] } expect_warning(parse_time("25:00:00", format = "%H:%M:%S")) expect_equal(parse_time("25:00:00", format = "%h:%M:%S"), hms::hms(hours = 25)) expect_equal(parse_time("1000000000:00:00", format = "%h:%M:%S"), hms::hms(hours = 1e9)) expect_equal(parse_time("-1:23:45", format = "%h:%M:%S"), hms::as_hms(-hms::hms(45, 23, 1))) expect_equal(parse_time("-1:23:45.67", format = "%h:%M:%OS"), hms::as_hms(-hms::hms(45.67, 23, 1))) }) vroom/tests/testthat/test-vroom_write.R0000644000176200001440000002042314142515724020100 0ustar liggesuserstest_that("empty columns create an empty file", { out <- tempfile() no_cols <- mtcars[, FALSE] no_rows_or_cols <- mtcars[FALSE, FALSE] expect_equal(vroom_write(no_cols, out), no_cols) expect_true(file.exists(out)) unlink(out) expect_equal(vroom_write(no_rows_or_cols, out), no_rows_or_cols) expect_true(file.exists(out)) }) test_that("empty rows print the headers", { out <- tempfile() on.exit(unlink(out)) no_rows <- mtcars[FALSE, ] expect_equal(vroom_write(no_rows, out), no_rows) expect_equal(strsplit(readLines(out), "\t")[[1]], colnames(mtcars)) }) test_that("strings are only quoted if needed", { x <- c("a", ',') csv <- vroom_format(data.frame(x), delim = ",",col_names = FALSE) expect_equal(csv, 'a\n\",\"\n') ssv <- vroom_format(data.frame(x), delim = " ",col_names = FALSE) expect_equal(ssv, 'a\n,\n') }) test_that("a literal NA is quoted", { expect_equal(vroom_format(data.frame(x = "NA")), "x\n\"NA\"\n") }) test_that("na argument modifies how missing values are written", { df <- data.frame(x = c(NA, "x", "."), y = c(1, 2, NA)) expect_equal(vroom_format(df, ",", na = "."), "x,y\n.,1\nx,2\n\".\",.\n") }) test_that("read_delim/csv/tsv and write_delim round trip special chars", { x <- stats::setNames(list("a", '"', ",", "\n","at\t"), paste0("V", seq_len(5))) output <- tibble::as_tibble(x) output_space <- vroom(I(vroom_format(output, delim = " ")), trim_ws = FALSE, progress = FALSE, col_types = list()) output_csv <- vroom(I(vroom_format(output, delim = ",")), trim_ws = FALSE, progress = FALSE, col_types = list()) output_tsv <- vroom(I(vroom_format(output, delim = "\t")), trim_ws = FALSE, progress = FALSE, col_types = list()) expect_equal(output_space, output) expect_equal(output_csv, output) expect_equal(output_tsv, output) }) test_that("special floating point values translated to text", { df <- data.frame(x = c(NaN, NA, Inf, -Inf)) expect_equal(vroom_format(df), "x\nNA\nNA\nInf\n-Inf\n") }) test_that("NA integer values translated to text", { df <- data.frame(x = c(NA, 1L, 5L, 1234567890L)) expect_equal(vroom_format(df), "x\nNA\n1\n5\n1234567890\n") }) test_that("logical values give long names", { df <- data.frame(x = c(NA, FALSE, TRUE)) expect_equal(vroom_format(df), "x\nNA\nFALSE\nTRUE\n") }) test_that("roundtrip preserved floating point numbers", { input <- data.frame(x = runif(100)) output <- vroom(I(vroom_format(input, delim = " ")), delim = " ", col_types = list()) expect_equal(input$x, output$x) }) test_that("roundtrip preserves dates and datetimes", { x <- as.Date("2010-01-01") + 1:10 y <- as.POSIXct(x) attr(y, "tzone") <- "UTC" input <- data.frame(x, y) output <- vroom(I(vroom_format(input, delim = " ")), delim = " ", col_types = list()) expect_equal(output$x, x) expect_equal(output$y, y) }) test_that("fails to create file in non-existent directory", { expect_error(vroom_write(mtcars, file.path(tempdir(), "/x/y"), "\t"), "Cannot open file for writing") }) test_that("includes a byte order mark if desired", { tmp <- tempfile() on.exit(unlink(tmp)) vroom_write(mtcars, tmp, bom = TRUE) output <- readBin(tmp, "raw", file.info(tmp)$size) # BOM is there expect_equal(output[1:3], charToRaw("\xEF\xBB\xBF")) # Rest of file also there expect_equal(output[4:6], charToRaw("mpg")) }) test_that("does not writes a trailing .0 for whole number doubles", { expect_equal(vroom_format(tibble::tibble(x = 1)), "x\n1\n") expect_equal(vroom_format(tibble::tibble(x = 0)), "x\n0\n") expect_equal(vroom_format(tibble::tibble(x = -1)), "x\n-1\n") expect_equal(vroom_format(tibble::tibble(x = 999)), "x\n999\n") expect_equal(vroom_format(tibble::tibble(x = -999)), "x\n-999\n") expect_equal(vroom_format(tibble::tibble(x = 123456789)), "x\n123456789\n") expect_equal(vroom_format(tibble::tibble(x = -123456789)), "x\n-123456789\n") }) test_that("can write windows eol characters if desired (#263)", { expect_equal(vroom_format(tibble::tibble(x = 1), eol = "\r\n"), "x\r\n1\r\n") }) test_that("write_csv can write to compressed files", { mt <- vroom(vroom_example("mtcars.csv"), col_types = list()) filename <- file.path(tempdir(), "mtcars.csv.bz2") on.exit(unlink(filename)) vroom_write(mt, filename) is_bz2_file <- function(x) { # Magic number for bz2 is "BZh" in ASCII # https://en.wikipedia.org/wiki/Bzip2#File_format identical(charToRaw("BZh"), readBin(x, n = 3, what = "raw")) } expect_true(is_bz2_file(filename)) expect_equal(vroom(filename, col_types = list()), mt) }) test_that("write_csv writes large integers without scientific notation #671", { x <- data.frame(a = c(60150001022000, 60150001022001)) expect_equal(vroom_format(x), "a\n60150001022000\n60150001022001\n") }) test_that("write_csv writes large integers without scientific notation up to 1E15 #671", { x <- data.frame(a = c(1E13, 1E14, 1E15, 1E16)) expect_equal(vroom_format(x), "a\n10000000000000\n100000000000000\n1e15\n1e16\n") }) #test_that("write_csv2 and format_csv2 writes ; sep and , decimal mark", { #df <- tibble::tibble(x = c(0.5, 2, 1.2), y = c("a", "b", "c")) #expect_equal(format_csv2(df), "x;y\n0,5;a\n2,0;b\n1,2;c\n") #filename <- tempfile(pattern = "readr", fileext = ".csv") #on.exit(unlink(filename)) #write_csv2(df, filename) #expect_equivalent(df, suppressMessages(read_csv2(filename))) #}) #test_that("write_csv2 and format_csv2 writes NA appropriately", { #df <- tibble::tibble(x = c(0.5, NA, 1.2), y = c("a", "b", NA)) #expect_equal(format_csv2(df), "x;y\n0,5;a\nNA;b\n1,2;NA\n") #}) test_that("Can change the escape behavior for quotes", { df <- data.frame(x = c("a", '"', ",", "\n")) expect_error(vroom_format(df, "\t", escape = "invalid"), "should be one of") expect_equal(vroom_format(df, "\t"), 'x\na\n""""\n,\n"\n"\n') expect_equal(vroom_format(df, "\t", escape = "double"), "x\na\n\"\"\"\"\n,\n\"\n\"\n") expect_equal(vroom_format(df, "\t", escape = "backslash"), "x\na\n\"\\\"\"\n,\n\"\n\"\n") expect_equal(vroom_format(df, "\t", escape = "none"), "x\na\n\"\"\"\n,\n\"\n\"\n") }) test_that("hms NAs are written without padding (#930)", { df <- data.frame(x = hms::as_hms(c(NA, 34.234))) expect_equal(vroom_format(df), "x\nNA\n00:00:34.234\n") }) test_that("vroom_write equals the same thing as vroom_format", { df <- gen_tbl(100, 8, col_types = c("dilfcDtT"), missing = .1) tf <- tempfile() on.exit(unlink(tf)) # Temporarily run with 2 lines per buffer, to test the multithreading withr::with_envvar(c("VROOM_WRITE_BUFFER_LINES" = "2"), vroom_write(df, tf, "\t") ) expect_equal(readChar(tf, file.info(tf)$size), vroom_format(df)) }) test_that("vroom_write(append = TRUE) works with R connections", { df <- data.frame(x = 1, y = 2) f <- tempfile(, fileext = ".tsv.gz") on.exit(unlink(f)) vroom::vroom_write(df, f) vroom::vroom_write(df, f, append = TRUE) expect_equal(vroom_lines(f), c("x\ty", "1\t2", "1\t2")) }) test_that("vroom_write() works with an empty delimiter", { df <- data.frame(x = "foo", y = "bar") f <- tempfile(, fileext = ".tsv.gz") on.exit(unlink(f)) vroom::vroom_write(df, f, delim = "") expect_equal(vroom_lines(f), c("xy", "foobar")) }) test_that("vroom_write_lines() works with empty", { f <- tempfile(, fileext = ".txt") on.exit(unlink(f)) vroom::vroom_write_lines(character(), f) expect_equal(vroom_lines(f), character()) }) test_that("vroom_write_lines() works with normal input", { f <- tempfile(, fileext = ".txt") on.exit(unlink(f)) vroom::vroom_write_lines(c("foo", "bar"), f) expect_equal(vroom_lines(f), c("foo", "bar")) }) test_that("vroom_write_lines() does not escape or quote lines", { f <- tempfile(, fileext = ".txt") on.exit(unlink(f)) vroom::vroom_write_lines(c('"foo"', "bar"), f) expect_equal(vroom_lines(f), c('"foo"', "bar")) }) test_that("vroom_write() always outputs in UTF-8", { x <- iconv(c("ao\u00FBt", "\u00E9l\u00E8ve", "\u00E7a va"), "UTF-8", "latin1") data <- tibble::tibble(X = x) names(data) <- iconv("fran\u00E7aise", "UTF-8", "latin1") f <- tempfile() vroom_write(data, f, delim = ",") expected_data <- charToRaw( paste0(collapse = "\n", c( enc2utf8(names(data)), enc2utf8(data[[1]]), "") ) ) actual_data <- readBin(f, "raw", file.size(f)) expect_equal(actual_data, expected_data) }) vroom/tests/testthat/multi-byte-unicode.txt0000644000176200001440000000007313746062417020710 0ustar liggesusersid❀name❀age 1❀ed❀36 2❀leigh❀NA 3❀nathan❀14 vroom/tests/testthat/test-num.R0000644000176200001440000000075714142515724016333 0ustar liggesusers# Flexible number parsing ------------------------------------------------- test_that("col_number only takes first number", { test_parse_number("XYZ 123,000 BLAH 456", 123000) }) test_that("col_number helps with currency", { test_parse_number("$1,000,000.00", 1e6) es_MX <- locale("es", decimal_mark = ",") test_parse_number("$1.000.000,00", locale = es_MX, 1e6) }) test_that("invalid numbers don't parse", { test_parse_number(c("..", "--", "3.3.3", "4-1"), c(NA, NA, 3.3, 4.0)) }) vroom/tests/testthat.R0000644000176200001440000000006614073307266014553 0ustar liggesuserslibrary(testthat) library(vroom) test_check("vroom") vroom/src/0000755000176200001440000000000014151250642012203 5ustar liggesusersvroom/src/vroom_types.h0000644000176200001440000000003214142512075014736 0ustar liggesusers#include "vroom_errors.h" vroom/src/vroom_rle.h0000644000176200001440000000566114142512075014371 0ustar liggesusers#pragma once #include "altrep.h" #ifdef HAS_ALTREP class vroom_rle { public: static R_altrep_class_t class_t; static SEXP Make(SEXP input) { SEXP res = R_new_altrep(class_t, input, R_NilValue); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // The length of the object static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } R_xlen_t sz = 0; SEXP rle = R_altrep_data1(vec); int* rle_p = INTEGER(rle); for (R_xlen_t i = 0; i < Rf_xlength(rle); ++i) { sz += rle_p[i]; } return sz; } // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_rle (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTSTRING methods ----------------- // the element at the index `i` static SEXP string_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return STRING_ELT(data2, i); } SEXP rle = R_altrep_data1(vec); int* rle_p = INTEGER(rle); SEXP nms = Rf_getAttrib(rle, Rf_install("names")); R_xlen_t idx = 0; while (i >= 0 && idx < Rf_xlength(rle)) { i -= rle_p[idx++]; } return STRING_ELT(nms, idx - 1); } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } R_xlen_t sz = Length(vec); SEXP rle = R_altrep_data1(vec); int* rle_p = INTEGER(rle); SEXP out = PROTECT(Rf_allocVector(STRSXP, sz)); R_xlen_t idx = 0; SEXP nms = Rf_getAttrib(rle, Rf_install("names")); for (R_xlen_t i = 0; i < Rf_xlength(rle); ++i) { for (R_xlen_t j = 0; j < rle_p[i]; ++j) { SET_STRING_ELT(out, idx++, STRING_ELT(nms, i)); } } UNPROTECT(1); R_set_altrep_data2(vec, out); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } static const void* Dataptr_or_null(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 == R_NilValue) return nullptr; return STDVEC_DATAPTR(data2); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_rle::class_t = R_make_altstring_class("vroom_rle", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); // altstring R_set_altstring_Elt_method(class_t, string_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_rle(DllInfo* dll); vroom/src/vroom_fwf.cc0000644000176200001440000000644714142515724014536 0ustar liggesusers#include #include #include #include #include "LocaleInfo.h" #include "columns.h" #include "fixed_width_index.h" #include "unicode_fopen.h" [[cpp11::register]] cpp11::list vroom_fwf_( const cpp11::list& inputs, const std::vector& col_starts, const std::vector& col_ends, bool trim_ws, cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, size_t skip, const char* comment, bool skip_empty_rows, ptrdiff_t n_max, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep, bool progress) { std::vector filenames; bool add_filename = !Rf_isNull(id); // We need to retrieve filenames now before the connection objects are read, // as they are invalid afterwards. if (add_filename) { filenames = get_filenames(inputs); } auto idx = std::make_shared( inputs, col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); auto errors = new std::shared_ptr(new vroom_errors()); return create_columns( idx, std::move(col_names), std::move(col_types), std::move(col_select), std::move(name_repair), id, filenames, na, locale, altrep, guess_max, errors, num_threads); } template std::vector find_empty_cols(Iterator begin, Iterator end, ptrdiff_t n) { std::vector is_white; size_t row = 0, col = 0; for (Iterator cur = begin; cur != end; ++cur) { if (n > 0 && row > static_cast(n)) { break; } switch (*cur) { case '\n': col = 0; row++; break; case '\r': case ' ': col++; break; default: // Make sure there's enough room if (col >= is_white.size()) is_white.resize(col + 1, true); is_white[col] = false; col++; } } return is_white; } [[cpp11::register]] cpp11::list whitespace_columns_( const std::string& filename, size_t skip, ptrdiff_t n, const std::string& comment) { std::error_code error; auto mmap = make_mmap_source(filename.c_str(), error); if (error) { // We cannot actually portably compare error messages due to a bug in // libstdc++ (https://stackoverflow.com/a/54316671/2055486), so just print // the message on stderr return REprintf("mapping error: %s", error.message().c_str()); return cpp11::list(); } size_t s = find_first_line( mmap, skip, comment.data(), /* skip_empty_rows */ true, /* embedded_nl */ false, /* quote */ '\0'); std::vector empty = find_empty_cols(mmap.begin() + s, mmap.end(), n); std::vector begin, end; bool in_col = false; for (size_t i = 0; i < empty.size(); ++i) { if (in_col && empty[i]) { end.push_back(i); in_col = false; } else if (!in_col && !empty[i]) { begin.push_back(i); in_col = true; } } if (in_col) end.push_back(empty.size()); using namespace cpp11::literals; return cpp11::writable::list({"begin"_nm = begin, "end"_nm = end}); } vroom/src/gen.cc0000644000176200001440000000124614142512075013267 0ustar liggesusers#include #include #include [[cpp11::register]] cpp11::strings gen_character_( int n, int min, int max, std::string values, uint32_t seed, uint32_t seed2) { std::mt19937 gen1(seed); std::mt19937 gen2(seed2); cpp11::writable::strings out(n); std::uniform_int_distribution<> char_dis(0, values.length() - 1); std::uniform_int_distribution<> len_dis(min, max); for (int i = 0; i < n; ++i) { std::string str; auto str_len = len_dis(gen1); for (int j = 0; j < str_len; ++j) { auto c = char_dis(gen2); str.push_back(values[c]); } out[i] = str.c_str(); } return out; } vroom/src/vroom_num.h0000644000176200001440000000532714142515724014411 0ustar liggesusers#pragma once #include #include "altrep.h" #include "vroom_vec.h" #include "parallel.h" using namespace vroom; double parse_num( const char* begin, const char* end, const LocaleInfo& loc, bool strict = false); cpp11::doubles read_num(vroom_vec_info* info); #ifdef HAS_ALTREP /* Vroom number */ class vroom_num : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_num (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTREAL methods ----------------- // the element at the index `i` static double real_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto& info = vroom_vec::Info(vec); double out = parse_value( i, info.column, [&](const char* begin, const char* end) -> double { return parse_num(begin, end, *info.locale); }, info.errors, "a number", *info.na); info.errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_num(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_num::class_t = R_make_altreal_class("vroom_num", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altreal_Elt_method(class_t, real_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_num(DllInfo* dll); vroom/src/guess_type.cc0000644000176200001440000000735514142515724014720 0ustar liggesusers#include #include #include "DateTime.h" #include "DateTimeParser.h" #include "LocaleInfo.h" #include "r_utils.h" #include "vroom_dbl.h" #include "vroom_lgl.h" #include "vroom_num.h" typedef bool (*canParseFun)(const std::string&, LocaleInfo* pLocale); bool canParse( const cpp11::strings& x, const canParseFun& canParse, LocaleInfo* pLocale) { for (auto&& i : x) { if (i == NA_STRING) continue; if (i.size() == 0) continue; if (!canParse(std::string(i), pLocale)) return false; } return true; } bool allMissing(const cpp11::strings& x) { for (auto&& i : x) { if (i != NA_STRING && i.size() > 0) return false; } return true; } bool isLogical(const std::string& x, LocaleInfo* /* pLocale */) { const char* const str = x.data(); int res = parse_logical(str, str + x.size()); return res != NA_LOGICAL; } bool isNumber(const std::string& x, LocaleInfo* pLocale) { // Leading zero not followed by decimal mark if (x[0] == '0' && x.size() > 1 && !matches(x.data() + 1, x.data() + x.size(), pLocale->decimalMark_)) return false; auto str = vroom::string(x); auto num = parse_num(str.begin(), str.end(), *pLocale, true); return !ISNA(num); } bool isInteger(const std::string& x, LocaleInfo* /* pLocale */) { // Leading zero if (x[0] == '0' && x.size() > 1) return false; double res = 0; std::string::const_iterator begin = x.begin(), end = x.end(); return parseInt(begin, end, res) && begin == end; } bool isDouble(const std::string& x, LocaleInfo* pLocale) { // Leading zero not followed by decimal mark if (x[0] == '0' && x.size() > 1 && x[1] != pLocale->decimalMark_[0]) return false; double res = bsd_strtod(x.data(), x.data() + x.size(), pLocale->decimalMark_[0]); return !ISNA(res); } bool isTime(const std::string& x, LocaleInfo* pLocale) { DateTimeParser parser(pLocale); parser.setDate(x.c_str(), x.c_str() + x.size()); return parser.parseLocaleTime(); } bool isDate(const std::string& x, LocaleInfo* pLocale) { DateTimeParser parser(pLocale); parser.setDate(x.c_str(), x.c_str() + x.size()); return parser.parseLocaleDate(); } static bool isDateTime(const std::string& x, LocaleInfo* pLocale) { DateTimeParser parser(pLocale); parser.setDate(x.c_str(), x.c_str() + x.size()); bool ok = parser.parseISO8601(); if (!ok) return false; DateTime dt = parser.makeDateTime(); return dt.validDateTime(); } std::string guess_type__( cpp11::writable::strings& input, const cpp11::strings& na, LocaleInfo* pLocale, bool guess_integer = false) { if (input.size() == 0) { return "character"; } if (allMissing(input)) { return "logical"; } for (R_xlen_t i = 0; i < input.size(); ++i) { for (R_xlen_t j = 0; j < na.size(); ++j) { if (STRING_ELT(input, i) == STRING_ELT(na, j)) { input[i] = NA_STRING; break; } } } // Work from strictest to most flexible if (canParse(input, isLogical, pLocale)) return "logical"; if (guess_integer && canParse(input, isInteger, pLocale)) return "integer"; if (canParse(input, isDouble, pLocale)) return "double"; if (canParse(input, isNumber, pLocale)) return "number"; if (canParse(input, isTime, pLocale)) return "time"; if (canParse(input, isDate, pLocale)) return "date"; if (canParse(input, isDateTime, pLocale)) return "datetime"; // Otherwise can always parse as a character return "character"; } [[cpp11::register]] std::string guess_type_( cpp11::writable::strings input, const cpp11::strings& na, const cpp11::list& locale, bool guess_integer = false) { LocaleInfo locale_(locale); return guess_type__(input, na, &locale_, guess_integer); } vroom/src/vroom_dbl.cc0000644000176200001440000001466414143524032014506 0ustar liggesusers#include "vroom_dbl.h" #include /* for tolower */ /* An STL iterator-based string to floating point number conversion. This function was adapted from the C standard library of RetroBSD, which is based on Berkeley UNIX. This function and this function only is BSD license. https://retrobsd.googlecode.com/svn/stable/vroom_time.h|31 col 32| for (const autosrc str : info->column.slice(start, end)) {/libc/stdlib/strtod.c */ double bsd_strtod(const char* begin, const char* end, const char decimalMark) { if (begin == end) { return NA_REAL; } int sign = 0, expSign = 0, i; double fraction, dblExp; const char* p = begin; char c; /* Exponent read from "EX" field. */ int exp = 0; /* Exponent that derives from the fractional part. Under normal * circumstances, it is the negative of the number of digits in F. * However, if I is very long, the last digits of I get dropped * (otherwise a long I with a large negative exponent could cause an * unnecessary overflow on I alone). In this case, fracExp is * incremented one for each dropped digit. */ int fracExp = 0; /* Number of digits in mantissa. */ int mantSize; /* Number of mantissa digits BEFORE decimal point. */ int decPt; /* Temporarily holds location of exponent in str. */ const char* pExp; /* Largest possible base 10 exponent. * Any exponent larger than this will already * produce underflow or overflow, so there's * no need to worry about additional digits. */ static int maxExponent = 307; /* Table giving binary powers of 10. * Entry is 10^2^i. Used to convert decimal * exponents into floating-point numbers. */ static double powersOf10[] = { 1e1, 1e2, 1e4, 1e8, 1e16, 1e32, 1e64, 1e128, 1e256, }; #if 0 static double powersOf2[] = { 2, 4, 16, 256, 65536, 4.294967296e9, 1.8446744073709551616e19, //3.4028236692093846346e38, 1.1579208923731619542e77, 1.3407807929942597099e154, }; static double powersOf8[] = { 8, 64, 4096, 2.81474976710656e14, 7.9228162514264337593e28, //6.2771017353866807638e57, 3.9402006196394479212e115, 1.5525180923007089351e231, }; static double powersOf16[] = { 16, 256, 65536, 1.8446744073709551616e19, //3.4028236692093846346e38, 1.1579208923731619542e77, 1.3407807929942597099e154, }; #endif /* * check for a sign. */ if (p != end && *p == '-') { sign = 1; ++p; } else if (p != end && *p == '+') ++p; /* NaN */ if (end - p == 3 && tolower(p[0]) == 'n' && tolower(p[1]) == 'a' && tolower(p[2]) == 'n') { return NAN; } /* Inf */ if (end - p == 3 && tolower(p[0]) == 'i' && tolower(p[1]) == 'n' && tolower(p[2]) == 'f') { return sign == 1 ? -HUGE_VAL : HUGE_VAL; } /* If we don't have a digit or decimal point something is wrong, so return * an NA */ if (!(isdigit(*p) || *p == decimalMark)) { return NA_REAL; } /* * Count the number of digits in the mantissa (including the decimal * point), and also locate the decimal point. */ decPt = -1; for (mantSize = 0; p != end; ++mantSize) { c = *p; if (!isdigit(c)) { if (c != decimalMark || decPt >= 0) break; decPt = mantSize; } ++p; } /* * Now suck up the digits in the mantissa. Use two integers to * collect 9 digits each (this is faster than using floating-point). * If the mantissa has more than 18 digits, ignore the extras, since * they can't affect the value anyway. */ pExp = p; p -= mantSize; if (decPt < 0) decPt = mantSize; else --mantSize; /* One of the digits was the point. */ if (mantSize > 2 * 9) mantSize = 2 * 9; fracExp = decPt - mantSize; if (mantSize == 0) { fraction = 0.0; p = begin; goto done; } else { int frac1, frac2; for (frac1 = 0; mantSize > 9 && p != end; --mantSize) { c = *p++; if (c == decimalMark) c = *p++; frac1 = frac1 * 10 + (c - '0'); } for (frac2 = 0; mantSize > 0 && p != end; --mantSize) { c = *p++; if (c == decimalMark) c = *p++; frac2 = frac2 * 10 + (c - '0'); } fraction = (double)1000000000 * frac1 + frac2; } /* * Skim off the exponent. */ p = pExp; if (p != end && (*p == 'E' || *p == 'e' || *p == 'S' || *p == 's' || *p == 'F' || *p == 'f' || *p == 'D' || *p == 'd' || *p == 'L' || *p == 'l')) { ++p; if (p != end && *p == '-') { expSign = 1; ++p; } else if (p != end && *p == '+') ++p; while (p != end && isdigit(*p)) exp = exp * 10 + (*p++ - '0'); } if (expSign) exp = fracExp - exp; else exp = fracExp + exp; /* * Generate a floating-point number that represents the exponent. * Do this by processing the exponent one bit at a time to combine * many powers of 2 of 10. Then combine the exponent with the * fraction. */ if (exp < 0) { expSign = 1; exp = -exp; } else expSign = 0; if (exp > maxExponent) exp = maxExponent; dblExp = 1.0; for (i = 0; exp; exp >>= 1, ++i) if (exp & 01) dblExp *= powersOf10[i]; if (expSign) fraction /= dblExp; else fraction *= dblExp; done: if (p != end) { return NA_REAL; } return sign ? -fraction : fraction; } cpp11::doubles read_dbl(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); const char decimalMark = info->locale->decimalMark_[0]; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return bsd_strtod(begin, end, decimalMark); }, info->errors, "a double", *info->na); } }, info->num_threads); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_dbl::class_t; void init_vroom_dbl(DllInfo* dll) { vroom_dbl::Init(dll); } #else void init_vroom_dbl(DllInfo* dll) {} #endif vroom/src/delimited_index_connection.h0000644000176200001440000000123514142515724017730 0ustar liggesusers#include "delimited_index.h" namespace vroom { class delimited_index_connection : public delimited_index { std::string filename_; public: delimited_index_connection( SEXP in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr errors, const size_t chunk_size, const bool progress); ~delimited_index_connection() { remove(filename_.c_str()); } }; } // namespace vroom vroom/src/vroom_int.h0000644000176200001440000000462114142515724014400 0ustar liggesusers#pragma once #include "altrep.h" #include "vroom_vec.h" int strtoi(const char* begin, const char* end); cpp11::integers read_int(vroom_vec_info* info); #ifdef HAS_ALTREP class vroom_int : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_int (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTINTEGER methods ----------------- static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_int(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } // the element at the index `i` static int int_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return INTEGER(data2)[i]; } auto& info = vroom_vec::Info(vec); int out = parse_value( i, info.column, strtoi, info.errors, "an integer", *info.na); info.errors->warn_for_errors(); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_int::class_t = R_make_altinteger_class("vroom_int", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altinteger_Elt_method(class_t, int_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_int(DllInfo* dll); vroom/src/vroom_fct.cc0000644000176200001440000000462314142515724014522 0ustar liggesusers#include "vroom_fct.h" #include bool matches(const string& needle, const std::vector& haystack) { for (auto& hay : haystack) { if (needle == hay) { return true; } } return false; } cpp11::integers read_fct_explicit( vroom_vec_info* info, const cpp11::strings& levels, bool ordered) { R_xlen_t n = info->column->size(); cpp11::writable::integers out(n); std::unordered_map level_map; for (auto i = 0; i < levels.size(); ++i) { if (levels[i] == NA_STRING) { for (const auto& str : *info->na) { level_map[str] = i + 1; } } else { level_map[levels[i]] = i + 1; } } auto col = info->column; R_xlen_t i = 0; for (auto b = col->begin(), e = col->end(); b != e; ++b) { auto str = *b; out[i++] = parse_factor(b, col, level_map, *info->locale, info->errors, *info->na); } info->errors->warn_for_errors(); out.attr("levels") = static_cast(levels); if (ordered) { out.attr("class") = {"ordered", "factor"}; } else { out.attr("class") = "factor"; } return out; } cpp11::integers read_fct_implicit(vroom_vec_info* info, bool include_na) { R_xlen_t n = info->column->size(); cpp11::writable::integers out(n); cpp11::writable::strings levels; std::unordered_map level_map; auto nas = cpp11::as_cpp>(*info->na); size_t max_level = 1; auto start = 0; auto end = n; auto i = start; auto col = info->column->slice(start, end); int na_level = NA_INTEGER; for (const auto& str : *col) { auto val = level_map.find(str.str()); if (val != level_map.end()) { out[i++] = val->second; } else { if (include_na && matches(str, nas)) { if (na_level == NA_INTEGER) { na_level = max_level++; levels.push_back(NA_STRING); out[i++] = na_level; level_map[str.str()] = na_level; } } else { out[i++] = max_level; level_map[str.str()] = max_level++; levels.push_back( info->locale->encoder_.makeSEXP(str.begin(), str.end(), false)); } } } out.attr("levels") = static_cast(levels); out.attr("class") = "factor"; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_fct::class_t; void init_vroom_fct(DllInfo* dll) { vroom_fct::Init(dll); } #else void init_vroom_fct(DllInfo* dll) {} #endif vroom/src/vroom_date.cc0000644000176200001440000000331114142515724014654 0ustar liggesusers#include "vroom_date.h" using namespace vroom; double parse_date( const char* begin, const char* end, DateTimeParser& parser, const std::string& format) { parser.setDate(begin, end); bool res = (format == "") ? parser.parseLocaleDate() : parser.parse(format); if (res) { DateTime dt = parser.makeDate(); if (dt.validDate()) { return dt.date(); } } return NA_REAL; } cpp11::doubles read_date(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); auto err_msg = info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->format; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; DateTimeParser parser(info->locale.get()); auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_date(begin, end, parser, info->format); }, info->errors, err_msg.c_str(), *info->na); } }, info->num_threads, true); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); out.attr("class") = "Date"; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_date::class_t; void init_vroom_date(DllInfo* dll) { vroom_date::Init(dll); } #else void init_vroom_date(DllInfo* dll) {} #endif vroom/src/unicode_fopen.h0000644000176200001440000000333614142512075015177 0ustar liggesusers#pragma once #include // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" #include # pragma clang diagnostic pop #else #include #endif // clang-format on #ifdef _WIN32 #include #include #endif // This is needed to support wide character paths on windows inline FILE* unicode_fopen(const char* path, const char* mode) { FILE* out; #ifdef _WIN32 // First conver the mode to the wide equivalent // Only usage is 2 characters so max 8 bytes + 2 byte null. wchar_t mode_w[10]; MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_w, 9); // Then convert the path wchar_t* buf; size_t len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); if (len <= 0) { Rf_error("Cannot convert file to Unicode: %s", path); } buf = (wchar_t*)R_alloc(len, sizeof(wchar_t)); if (buf == NULL) { Rf_error("Could not allocate buffer of size: %ll", len); } MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, len); out = _wfopen(buf, mode_w); #else out = fopen(path, mode); #endif return out; } inline mio::mmap_source make_mmap_source(const char* file, std::error_code& error) { #ifdef __WIN32 wchar_t* buf; size_t len = MultiByteToWideChar(CP_UTF8, 0, file, -1, NULL, 0); if (len <= 0) { Rf_error("Cannot convert file to Unicode: %s", file); } buf = (wchar_t*)malloc(len * sizeof(wchar_t)); if (buf == NULL) { Rf_error("Could not allocate buffer of size: %ll", len); } MultiByteToWideChar(CP_UTF8, 0, file, -1, buf, len); mio::mmap_source out = mio::make_mmap_source(buf, error); free(buf); return out; #else return mio::make_mmap_source(file, error); #endif } vroom/src/vroom_time.h0000644000176200001440000000617014142515724014545 0ustar liggesusers#pragma once #include #include "vroom.h" #include "vroom_dttm.h" using namespace vroom; double parse_time( const char* begin, const char* end, DateTimeParser& parser, const std::string& format); cpp11::doubles read_time(vroom_vec_info* info); #ifdef HAS_ALTREP class vroom_time : public vroom_dttm { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { vroom_dttm_info* dttm_info = new vroom_dttm_info; dttm_info->info = info; dttm_info->parser = std::unique_ptr(new DateTimeParser(info->locale.get())); SEXP out = PROTECT(R_MakeExternalPtr(dttm_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_dttm::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"hms", "difftime"}; res.attr("units") = "secs"; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_time (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // the element at the index `i` static double time_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = Info(vec); auto err_msg = info->info->format.size() == 0 ? std::string("time in ISO8601") : std::string("time like ") + info->info->format; double out = parse_value( i, info->info->column, [&](const char* begin, const char* end) -> double { return parse_time(begin, end, *info->parser, info->info->format); }, info->info->errors, err_msg.c_str(), *info->info->na); info->info->errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto inf = Info(vec); auto out = read_time(inf->info); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_time::class_t = R_make_altreal_class("vroom_time", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, time_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_time(DllInfo* dll); vroom/src/vroom_time.cc0000644000176200001440000000334014142515724014677 0ustar liggesusers#include "vroom_time.h" double parse_time( const char* begin, const char* end, DateTimeParser& parser, const std::string& format) { parser.setDate(begin, end); bool res = (format == "") ? parser.parseLocaleTime() : parser.parse(format); if (res) { DateTime dt = parser.makeTime(); if (dt.validDuration()) { return dt.time(); } } return NA_REAL; } cpp11::doubles read_time(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); auto err_msg = info->format.size() == 0 ? std::string("time in ISO8601") : std::string("time like ") + info->format; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; DateTimeParser parser(info->locale.get()); auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_time(begin, end, parser, info->format); }, info->errors, err_msg.c_str(), *info->na); } }, info->num_threads, true); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); out.attr("class") = {"hms", "difftime"}; out.attr("units") = "secs"; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_time::class_t; void init_vroom_time(DllInfo* dll) { vroom_time::Init(dll); } #else void init_vroom_time(DllInfo* dll) {} #endif vroom/src/vroom_dbl.h0000644000176200001440000000522714142515724014352 0ustar liggesusers#include #include "altrep.h" #include "parallel.h" #include "vroom_vec.h" double bsd_strtod(const char* begin, const char* end, const char decimalMark); cpp11::doubles read_dbl(vroom_vec_info* info); #ifdef HAS_ALTREP /* Vroom Dbl */ class vroom_dbl : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_dbl (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTREAL methods ----------------- // the element at the index `i` static double real_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto& info = vroom_vec::Info(vec); double out = parse_value( i, info.column, [&](const char* begin, const char* end) -> double { return bsd_strtod(begin, end, info.locale->decimalMark_[0]); }, info.errors, "a double", *info.na); info.errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_dbl(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_dbl::class_t = R_make_altreal_class("vroom_dbl", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altreal_Elt_method(class_t, real_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_dbl(DllInfo* dll); vroom/src/delimited_index.h0000644000176200001440000003143214143524032015504 0ustar liggesusers#pragma once #include // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" #include # pragma clang diagnostic pop #else #include #endif // clang-format on #include "index.h" #include "utils.h" #include #include "multi_progress.h" #include "vroom_errors.h" namespace vroom { struct cell { const char* begin; const char* end; }; class delimited_index : public index, public std::enable_shared_from_this { class newline_error {}; public: delimited_index( const char* filename, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, size_t n_max, const char* comment, const bool skip_empty_rows, std::shared_ptr errors, const size_t num_threads, const bool progress, const bool use_threads = true); class column_iterator : public base_iterator { std::shared_ptr idx_; size_t column_; bool is_first_; bool is_last_; size_t i_; public: column_iterator(std::shared_ptr idx, size_t column) : idx_(idx), column_(column), is_first_(column == 0), is_last_(column == (idx_->columns_ - 1)), i_((idx_->has_header_ * idx_->columns_) + column_) {} void next() override { i_ += idx_->columns_; } void advance(ptrdiff_t n) override { i_ += idx_->columns_ * n; } bool equal_to(const base_iterator& it) const override { return i_ == static_cast(&it)->i_; } ptrdiff_t distance_to(const base_iterator& it) const override { ptrdiff_t i = i_; ptrdiff_t j = static_cast(&it)->i_; ptrdiff_t columns = idx_->columns_; return (j - i) / columns; } string value() const override { return idx_->get_trimmed_val(i_, is_first_, is_last_); } column_iterator* clone() const override { return new column_iterator(*this); } string at(ptrdiff_t n) const override { size_t i = ((n + idx_->has_header_) * idx_->columns_) + column_; return idx_->get_trimmed_val(i, is_first_, is_last_); } std::string filename() const override { return idx_->filename_; } size_t index() const override { return i_ / idx_->columns_; } size_t position() const override { size_t begin, end; std::tie(begin, end) = idx_->get_cell(i_, is_first_); return begin; } virtual ~column_iterator() = default; }; class row_iterator : public base_iterator { std::shared_ptr idx_; size_t row_; size_t i_; public: row_iterator(std::shared_ptr idx, size_t row) : idx_(idx), row_(row), i_((row_ + idx_->has_header_) * idx_->columns_) {} void next() override { ++i_; } void advance(ptrdiff_t n) override { i_ += n; } bool equal_to(const base_iterator& it) const override { return i_ == static_cast(&it)->i_; } ptrdiff_t distance_to(const base_iterator& it) const override { return ( static_cast(static_cast(&it)->i_) - static_cast(i_)); } string value() const override { return idx_->get_trimmed_val(i_, i_ == 0, i_ == (idx_->columns_ - 1)); } row_iterator* clone() const override { return new row_iterator(*this); } string at(ptrdiff_t n) const override { size_t i = (row_ + idx_->has_header_) * idx_->columns_ + n; return idx_->get_trimmed_val(i, i == 0, i == (idx_->columns_ - 1)); } std::string filename() const override { return idx_->filename_; } size_t index() const override { return i_ - (row_ + idx_->has_header_) * idx_->columns_; } size_t position() const override { size_t begin, end; std::tie(begin, end) = idx_->get_cell(i_, i_ == 0); return begin; } virtual ~row_iterator() = default; }; delimited_index() : rows_(0), columns_(0){}; string get(size_t row, size_t col) const override; size_t num_columns() const override { return columns_; } size_t num_rows() const override { return rows_; } std::string filename() const { return filename_; } std::string get_delim() const override { return delim_; } std::shared_ptr get_column(size_t column) const override { auto begin = new column_iterator(shared_from_this(), column); auto end = new column_iterator(shared_from_this(), column); end->advance(num_rows()); return std::make_shared(begin, end, column); } std::shared_ptr get_row(size_t row) const override { auto begin = new row_iterator(shared_from_this(), row); auto end = new row_iterator(shared_from_this(), row); end->advance(num_columns()); return std::make_shared(begin, end, row); } std::shared_ptr get_header() const override { auto begin = new row_iterator(shared_from_this(), -1); auto end = new row_iterator(shared_from_this(), -1); end->advance(num_columns()); return std::make_shared(begin, end, 0); } public: using idx_t = std::vector; std::string filename_; mio::mmap_source mmap_; std::vector idx_; bool has_header_; char quote_; bool trim_ws_; bool escape_double_; bool escape_backslash_; size_t skip_; const char* comment_; size_t rows_; size_t columns_; bool progress_; size_t delim_len_; std::string delim_; std::locale loc_; void skip_lines(); void trim_quotes(const char*& begin, const char*& end) const; const string get_escaped_string(const char* begin, const char* end, bool has_quote) const; const string get_trimmed_val(size_t i, bool is_first, bool is_last) const; std::pair get_cell(size_t i, bool is_first) const; enum csv_state { RECORD_START, FIELD_START, UNQUOTED_FIELD, QUOTED_FIELD, QUOTED_END }; inline static csv_state quoted_state(csv_state in) { switch (in) { case RECORD_START: return QUOTED_FIELD; case FIELD_START: return QUOTED_FIELD; case UNQUOTED_FIELD: return UNQUOTED_FIELD; // throw std::runtime_error("invalid 1"); case QUOTED_FIELD: return QUOTED_END; case QUOTED_END: return QUOTED_FIELD; } throw std::runtime_error("should never happen"); } inline static csv_state comma_state(csv_state in) { switch (in) { case RECORD_START: return FIELD_START; case FIELD_START: return FIELD_START; case UNQUOTED_FIELD: return FIELD_START; case QUOTED_FIELD: return QUOTED_FIELD; case QUOTED_END: return FIELD_START; } throw std::runtime_error("should never happen"); } inline static csv_state newline_state(csv_state in) { switch (in) { case RECORD_START: return RECORD_START; case FIELD_START: return RECORD_START; case UNQUOTED_FIELD: return RECORD_START; case QUOTED_FIELD: return QUOTED_FIELD; case QUOTED_END: return RECORD_START; } throw std::runtime_error("should never happen"); } inline static csv_state other_state(csv_state in) { switch (in) { case RECORD_START: return UNQUOTED_FIELD; case FIELD_START: return UNQUOTED_FIELD; case UNQUOTED_FIELD: return UNQUOTED_FIELD; case QUOTED_FIELD: return QUOTED_FIELD; case QUOTED_END: return QUOTED_END; } throw std::runtime_error("should never happen"); } void resolve_columns( size_t pos, size_t& cols, size_t num_cols, idx_t& destination, std::shared_ptr errors) { // Remove extra columns if there are too many if (cols >= num_cols) { errors->add_parse_error(pos, cols); while (cols > 0 && cols >= num_cols) { destination.pop_back(); --cols; } } else if (cols < num_cols - 1) { errors->add_parse_error(pos, cols); // Add additional columns if there are too few while (cols < num_cols - 1) { destination.push_back(pos); ++cols; } } } /* * @param source the source to index * @param destination the index to push to * @param delim the delimiter to use * @param quote the quoting character * @param start the start of the region to index * @param end the end of the region to index * @param offset an offset to add to the destination (this is needed when * @param pb the progress bar to use * @param update_size how often to update the progress bar * reading blocks from a connection). */ template size_t index_region( const T& source, idx_t& destination, const char* delim, newline_type nlt, const char quote, const std::string& comment, const bool skip_empty_rows, csv_state& state, const size_t start, const size_t end, const size_t file_offset, const size_t n_max, size_t& cols, const size_t num_cols, std::shared_ptr errors, P& pb, const size_t num_threads, const size_t update_size) { const char newline = nlt == CR ? '\r' : '\n'; // If there are no quotes quote will be '\0', so will just work std::array query = {delim[0], newline, '\\', '\0', '\0', '\0'}; auto query_i = 3; if (quote != '\0') { query[query_i++] = quote; } if (!comment.empty()) { query[query_i] = comment[0]; } auto last_tick = start; auto buf = source.data(); // The actual parsing is here size_t pos = start; size_t lines_read = 0; while (pos < end && lines_read < n_max) { auto c = buf[pos]; if (escape_backslash_ && c == '\\') { ++pos; if (state == RECORD_START) { destination.push_back(pos + file_offset); state = FIELD_START; } ++pos; continue; } else if ( state != QUOTED_FIELD && is_comment(buf + pos, buf + end, comment)) { if (state != RECORD_START) { if (num_cols > 0 && pos > start) { resolve_columns( pos + file_offset, cols, num_cols, destination, errors); } destination.push_back(pos + file_offset); } cols = 0; pos = skip_rest_of_line(source, pos); ++pos; state = newline_state(state); continue; } if (state == RECORD_START) { if (is_empty_line(buf + pos, buf + end, skip_empty_rows)) { pos = skip_rest_of_line(source, pos); ++pos; continue; } // REprintf("RS: %i\n", pos); destination.push_back(pos + file_offset); } if (state != QUOTED_FIELD && strncmp(delim, buf + pos, delim_len_) == 0) { state = comma_state(state); destination.push_back(pos + file_offset); ++cols; } else if (c == newline) { if (state == QUOTED_FIELD) { // This will work as long as num_threads = 1 if (num_threads != 1) { if (progress_ && pb) { pb->finish(); } throw newline_error(); } ++pos; continue; } if (num_cols > 0 && pos > start) { resolve_columns( pos + file_offset, cols, num_cols, destination, errors); } state = newline_state(state); cols = 0; destination.push_back(pos + file_offset); ++lines_read; if (lines_read >= n_max) { if (progress_ && pb) { pb->finish(); } return lines_read; } if (progress_ && pb) { size_t tick_size = pos - last_tick; if (tick_size > update_size) { pb->tick(pos - last_tick); last_tick = pos; } } } else if (c == quote) { state = quoted_state(state); } else { state = other_state(state); ++pos; size_t buf_offset; if (pos < end) { buf_offset = strcspn(buf + pos, query.data()); pos = pos + buf_offset; } continue; } // REprintf( //"%i\t'%c'\t%c\n", // pos, // c, // state == RECORD_START //? 'R' //: state == FIELD_START //? 'F' //: state == UNQUOTED_FIELD //? 'U' //: state == QUOTED_FIELD //? 'Q' //: state == QUOTED_END ? 'E' : 'X'); ++pos; } if (progress_ && pb) { pb->tick(end - last_tick); } return lines_read; } }; } // namespace vroom vroom/src/LocaleInfo.cpp0000644000176200001440000000216214142515724014730 0ustar liggesusers#include "cpp11/as.hpp" #include "cpp11/list.hpp" #include "cpp11/strings.hpp" #include #include #include "LocaleInfo.h" LocaleInfo::LocaleInfo(const cpp11::list& x) : encoding_(cpp11::as_cpp(x["encoding"])), encoder_(Iconv(encoding_)) { std::string klass = cpp11::as_cpp(x.attr("class")); if (klass != "locale") cpp11::stop("Invalid input: must be of class locale"); cpp11::list date_names(x["date_names"]); mon_ = cpp11::as_cpp>(date_names["mon"]); monAb_ = cpp11::as_cpp>(date_names["mon_ab"]); day_ = cpp11::as_cpp>(date_names["day"]); dayAb_ = cpp11::as_cpp>(date_names["day_ab"]); amPm_ = cpp11::as_cpp>(date_names["am_pm"]); decimalMark_ = cpp11::as_cpp(x["decimal_mark"]); groupingMark_ = cpp11::as_cpp(x["grouping_mark"]); dateFormat_ = cpp11::as_cpp(x["date_format"]); timeFormat_ = cpp11::as_cpp(x["time_format"]); tz_ = cpp11::as_cpp(x["tz"]); } vroom/src/vroom_chr.h0000644000176200001440000000621414142515724014362 0ustar liggesusers#pragma once #include #include "altrep.h" #include "vroom_vec.h" cpp11::strings read_chr(vroom_vec_info* info); SEXP check_na(SEXP na, SEXP val); #ifdef HAS_ALTREP struct vroom_chr : vroom_vec { public: static R_altrep_class_t class_t; // Make an altrep object of class `stdvec_double::class_t` static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); // make a new altrep object of class `vroom_chr::class_t` SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_chr (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTSTRING methods ----------------- static SEXP Val(SEXP vec, R_xlen_t i) { auto& info = Info(vec); auto&& col = info.column; auto str = col->at(i); auto val = PROTECT(info.locale->encoder_.makeSEXP(str.begin(), str.end(), true)); if (Rf_xlength(val) < str.end() - str.begin()) { auto&& itr = info.column->begin(); info.errors->add_error( itr.index(), col->get_index(), "", "embedded null", itr.filename()); } val = check_na(*info.na, val); info.errors->warn_for_errors(); UNPROTECT(1); return val; } // the element at the index `i` // // this does not do bounds checking because that's expensive, so // the caller must take care of that static SEXP string_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return STRING_ELT(data2, i); } SPDLOG_TRACE("{0:x}: vroom_chr string_Elt {1}", (size_t)vec, i); return Val(vec, i); } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } SPDLOG_TRACE("{0:x}: vroom_chr Materialize", (size_t)vec); auto out = read_chr(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { class_t = R_make_altstring_class("vroom_chr", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altstring R_set_altstring_Elt_method(class_t, string_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_chr(DllInfo* dll); vroom/src/vroom_num.cc0000644000176200001440000001024014142515724014535 0ustar liggesusers#include "vroom_num.h" #include "utils.h" enum NumberState { STATE_INIT, STATE_LHS, STATE_RHS, STATE_EXP, STATE_FIN }; // First and last are updated to point to first/last successfully parsed // character template bool parseNumber( const std::string& decimalMark, const std::string& groupingMark, Iterator& first, Iterator& last, Attr& res) { Iterator cur = first; // Advance to first non-character for (; cur != last; ++cur) { if (*cur == '-' || matches(cur, last, decimalMark) || (*cur >= '0' && *cur <= '9')) break; } if (cur == last) { return false; } else { // Move first to start of number first = cur; } double sum = 0, denom = 1, exponent = 0; NumberState state = STATE_INIT; bool seenNumber = false, exp_init = true; double sign = 1.0, exp_sign = 1.0; for (; cur < last; ++cur) { if (state == STATE_FIN) break; switch (state) { case STATE_INIT: if (*cur == '-') { state = STATE_LHS; sign = -1.0; } else if (matches(cur, last, decimalMark)) { cur += decimalMark.size() - 1; state = STATE_RHS; } else if (*cur >= '0' && *cur <= '9') { seenNumber = true; state = STATE_LHS; sum = *cur - '0'; } else { goto end; } break; case STATE_LHS: if (matches(cur, last, groupingMark)) { cur += groupingMark.size() - 1; } else if (matches(cur, last, decimalMark)) { cur += decimalMark.size() - 1; state = STATE_RHS; } else if (seenNumber && (*cur == 'e' || *cur == 'E')) { state = STATE_EXP; } else if (*cur >= '0' && *cur <= '9') { seenNumber = true; sum *= 10; sum += *cur - '0'; } else { goto end; } break; case STATE_RHS: if (matches(cur, last, groupingMark)) { cur += groupingMark.size() - 1; } else if (seenNumber && (*cur == 'e' || *cur == 'E')) { state = STATE_EXP; } else if (*cur >= '0' && *cur <= '9') { seenNumber = true; denom *= 10; sum += (*cur - '0') / denom; } else { goto end; } break; case STATE_EXP: // negative/positive sign only allowed immediately after 'e' or 'E' if (*cur == '-' && exp_init) { exp_sign = -1.0; exp_init = false; } else if (*cur == '+' && exp_init) { // sign defaults to positive exp_init = false; } else if (*cur >= '0' && *cur <= '9') { exponent *= 10.0; exponent += *cur - '0'; exp_init = false; } else { goto end; } break; case STATE_FIN: goto end; } } end: // Set last to point to final character used last = cur; res = sign * sum; // If the number was in scientific notation, multiply by 10^exponent if (exponent) { res *= pow(10.0, exp_sign * exponent); } return seenNumber; } double parse_num( const char* start, const char* end, const LocaleInfo& loc, bool strict) { double ret; auto start_p = start; auto end_p = end; bool ok = parseNumber(loc.decimalMark_, loc.groupingMark_, start_p, end_p, ret); if (ok && (!strict || (start_p == start && end_p == end))) { return ret; } return NA_REAL; } cpp11::doubles read_num(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_num(begin, end, *info->locale); }, info->errors, "a number", *info->na); } }, info->num_threads); info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_num::class_t; void init_vroom_num(DllInfo* dll) { vroom_num::Init(dll); } #else void init_vroom_num(DllInfo* dll) {} #endif vroom/src/vroom_dttm.h0000644000176200001440000001321614142515724014556 0ustar liggesusers#pragma once #include #include #include "vroom.h" #include "vroom_vec.h" #include "DateTimeParser.h" #include "parallel.h" #ifdef VROOM_LOG #include "spdlog/spdlog.h" #endif using namespace vroom; double parse_dttm( const char* begin, const char* end, DateTimeParser& parser, const std::string& format); cpp11::doubles read_dttm(vroom_vec_info* info); #ifdef HAS_ALTREP /* Vroom dttm */ struct vroom_dttm_info { vroom_vec_info* info; std::unique_ptr parser; }; class vroom_dttm : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { vroom_dttm_info* dttm_info = new vroom_dttm_info; dttm_info->info = info; dttm_info->parser = std::unique_ptr(new DateTimeParser(info->locale.get())); SEXP out = PROTECT(R_MakeExternalPtr(dttm_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_dttm::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"POSIXct", "POSIXt"}; res.attr("tzone") = info->locale->tz_; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_dttm (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } static void Finalize(SEXP ptr) { if (ptr == nullptr || R_ExternalPtrAddr(ptr) == nullptr) { return; } auto info_p = static_cast(R_ExternalPtrAddr(ptr)); delete info_p->info; delete info_p; info_p = nullptr; R_ClearExternalPtr(ptr); } static inline vroom_dttm_info* Info(SEXP x) { return static_cast(R_ExternalPtrAddr(R_altrep_data1(x))); } static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } auto inf = Info(vec); return inf->info->column->size(); } static inline string Get(SEXP vec, R_xlen_t i) { auto inf = Info(vec); return inf->info->column->at(i); } // ALTREAL methods ----------------- // the element at the index `i` static double dttm_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = Info(vec); auto err_msg = info->info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->info->format; double out = parse_value( i, info->info->column, [&](const char* begin, const char* end) -> double { return parse_dttm(begin, end, *info->parser, info->info->format); }, info->info->errors, err_msg.c_str(), *info->info->na); info->info->errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto inf = Info(vec); auto out = read_dttm(inf->info); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } template static SEXP Extract_subset(SEXP x, SEXP indx, SEXP) { SEXP data2 = R_altrep_data2(x); // If the vector is already materialized, just fall back to the default // implementation if (data2 != R_NilValue) { return nullptr; } // If there are no indices to subset fall back to default implementation. if (Rf_xlength(indx) == 0) { return nullptr; } auto idx = get_subset_index(indx, Rf_xlength(x)); if (idx == nullptr) { return nullptr; } auto inf = Info(x); auto info = new vroom_vec_info{ inf->info->column->subset(idx), inf->info->num_threads, inf->info->na, inf->info->locale, inf->info->errors, inf->info->format}; return T::Make(info); } static SEXP Duplicate(SEXP x, Rboolean deep) { SEXP data2 = R_altrep_data2(x); SPDLOG_TRACE( "Duplicate dttm: deep = {0}, materialized={1}", deep, data2 != R_NilValue); /* If deep or already materialized, do the default behavior */ if (deep || data2 != R_NilValue) { return nullptr; } /* otherwise copy the metadata */ auto inf = Info(x); auto info = new vroom_vec_info{ inf->info->column, inf->info->num_threads, inf->info->na, inf->info->locale, inf->info->errors, inf->info->format}; return Make(info); } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_dttm::class_t = R_make_altreal_class("vroom_dttm", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); R_set_altrep_Duplicate_method(class_t, Duplicate); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, dttm_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_dttm(DllInfo* dll); vroom/src/index.h0000644000176200001440000000614614143524032013470 0ustar liggesusers#pragma once #include "iterator.h" #include "vroom.h" #include #include namespace vroom { class index { public: class subset_iterator : public base_iterator { size_t i_; mutable size_t prev_; mutable iterator it_; iterator start_; std::shared_ptr> indexes_; public: subset_iterator( const iterator& it, const std::shared_ptr>& indexes) : i_(0), prev_(0), it_(it), start_(it), indexes_(indexes) {} void next() override { ++i_; } void advance(ptrdiff_t n) override { i_ += n; } bool equal_to(const base_iterator& other) const override { auto other_ = static_cast(&other); return i_ == other_->i_; }; ptrdiff_t distance_to(const base_iterator& that) const override { auto that_ = static_cast(&that); return that_->i_ - i_; }; string value() const override { size_t cur = (*indexes_)[i_]; ptrdiff_t diff = cur - prev_; if (diff < 0) { it_ = start_ + cur; } else { it_ += diff; } prev_ = cur; return *it_; }; subset_iterator* clone() const override { auto copy = new subset_iterator(*this); return copy; }; string at(ptrdiff_t n) const override { return it_[(*indexes_)[n]]; } std::string filename() const override { return it_.filename(); } size_t index() const override { return it_.index(); } size_t position() const override { return (it_ + (*indexes_)[i_]).position(); } virtual ~subset_iterator() {} }; class range { const iterator begin_; const iterator end_; const size_t index_; public: range(const iterator& begin, const iterator& end, size_t index) : begin_(begin), end_(end), index_(index) {} range(base_iterator* begin, base_iterator* end, size_t index) : begin_(begin), end_(end), index_(index) {} iterator begin() { return begin_; } iterator end() { return end_; } size_t size() const { return end_ - begin_; } string at(size_t i) const { return begin_[i]; } std::shared_ptr subset(const std::shared_ptr>& idx) const { auto begin = new subset_iterator(begin_, idx); auto end = new subset_iterator(begin_, idx); end->advance(idx->size()); return std::make_shared(begin, end, index_); } std::shared_ptr slice(size_t start, size_t end) const { return std::make_shared( begin_ + start, begin_ + end, index_); } size_t get_index() { return index_; } }; using column = range; using row = range; virtual std::shared_ptr get_row(size_t row) const = 0; virtual std::shared_ptr get_header() const = 0; virtual std::shared_ptr get_column(size_t col) const = 0; virtual size_t num_columns() const = 0; virtual size_t num_rows() const = 0; virtual string get(size_t row, size_t col) const = 0; virtual std::string get_delim() const = 0; virtual ~index() {} }; } // namespace vroom vroom/src/mio/0000755000176200001440000000000014151250643012770 5ustar liggesusersvroom/src/mio/LICENSE0000644000176200001440000000207613632437541014011 0ustar liggesusersMIT License Copyright (c) 2018 https://github.com/mandreyel/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. vroom/src/mio/README.md0000644000176200001440000003330614142512075014254 0ustar liggesusers# mio An easy to use header-only cross-platform C++11 memory mapping library with an MIT license. mio has been created with the goal to be easily includable (i.e. no dependencies) in any C++ project that needs memory mapped file IO without the need to pull in Boost. Please feel free to open an issue, I'll try to address any concerns as best I can. ### Why? Because memory mapping is the best thing since sliced bread! More seriously, the primary motivation for writing this library instead of using Boost.Iostreams, was the lack of support for establishing a memory mapping with an already open file handle/descriptor. This is possible with mio. Furthermore, Boost.Iostreams' solution requires that the user pick offsets exactly at page boundaries, which is cumbersome and error prone. mio, on the other hand, manages this internally, accepting any offset and finding the nearest page boundary. Albeit a minor nitpick, Boost.Iostreams implements memory mapped file IO with a `std::shared_ptr` to provide shared semantics, even if not needed, and the overhead of the heap allocation may be unnecessary and/or unwanted. In mio, there are two classes to cover the two use-cases: one that is move-only (basically a zero-cost abstraction over the system specific mmapping functions), and the other that acts just like its Boost.Iostreams counterpart, with shared semantics. ### How to create a mapping NOTE: the file must exist before creating a mapping. There are three ways to map a file into memory: - Using the constructor, which throws a `std::system_error` on failure: ```c++ mio::mmap_source mmap(path, offset, size_to_map); ``` or you can omit the `offset` and `size_to_map` arguments, in which case the entire file is mapped: ```c++ mio::mmap_source mmap(path); ``` - Using the factory function: ```c++ std::error_code error; mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error); ``` or: ```c++ mio::mmap_source mmap = mio::make_mmap_source(path, error); ``` - Using the `map` member function: ```c++ std::error_code error; mio::mmap_source mmap; mmap.map(path, offset, size_to_map, error); ``` or: ```c++ mmap.map(path, error); ``` **NOTE:** The constructors **require** exceptions to be enabled. If you prefer to build your projects with `-fno-exceptions`, you can still use the other ways. Moreover, in each case, you can provide either some string type for the file's path, or you can use an existing, valid file handle. ```c++ #include #include #include #include #include int main() { // NOTE: error handling omitted for brevity. const int fd = open("file.txt", O_RDONLY); mio::mmap_source mmap(fd, 0, mio::map_entire_file); // ... } ``` However, mio does not check whether the provided file descriptor has the same access permissions as the desired mapping, so the mapping may fail. Such errors are reported via the `std::error_code` out parameter that is passed to the mapping function. **WINDOWS USERS**: This library *does* support the use of wide character types for functions where character strings are expected (e.g. path parameters). ### Example ```c++ #include #include // for std::error_code #include // for std::printf #include #include #include int handle_error(const std::error_code& error); void allocate_file(const std::string& path, const int size); int main() { const auto path = "file.txt"; // NOTE: mio does *not* create the file for you if it doesn't exist! You // must ensure that the file exists before establishing a mapping. It // must also be non-empty. So for illustrative purposes the file is // created now. allocate_file(path, 155); // Read-write memory map the whole file by using `map_entire_file` where the // length of the mapping is otherwise expected, with the factory method. std::error_code error; mio::mmap_sink rw_mmap = mio::make_mmap_sink( path, 0, mio::map_entire_file, error); if (error) { return handle_error(error); } // You can use any iterator based function. std::fill(rw_mmap.begin(), rw_mmap.end(), 'a'); // Or manually iterate through the mapped region just as if it were any other // container, and change each byte's value (since this is a read-write mapping). for (auto& b : rw_mmap) { b += 10; } // Or just change one value with the subscript operator. const int answer_index = rw_mmap.size() / 2; rw_mmap[answer_index] = 42; // Don't forget to flush changes to disk before unmapping. However, if // `rw_mmap` were to go out of scope at this point, the destructor would also // automatically invoke `sync` before `unmap`. rw_mmap.sync(error); if (error) { return handle_error(error); } // We can then remove the mapping, after which rw_mmap will be in a default // constructed state, i.e. this and the above call to `sync` have the same // effect as if the destructor had been invoked. rw_mmap.unmap(); // Now create the same mapping, but in read-only mode. Note that calling the // overload without the offset and file length parameters maps the entire // file. mio::mmap_source ro_mmap; ro_mmap.map(path, error); if (error) { return handle_error(error); } const int the_answer_to_everything = ro_mmap[answer_index]; assert(the_answer_to_everything == 42); } int handle_error(const std::error_code& error) { const auto& errmsg = error.message(); std::printf("error mapping file: %s, exiting...\n", errmsg.c_str()); return error.value(); } void allocate_file(const std::string& path, const int size) { std::ofstream file(path); std::string s(size, '0'); file << s; } ``` `mio::basic_mmap` is move-only, but if multiple copies to the same mapping are needed, use `mio::basic_shared_mmap` which has `std::shared_ptr` semantics and has the same interface as `mio::basic_mmap`. ```c++ #include mio::shared_mmap_source shared_mmap1("path", offset, size_to_map); mio::shared_mmap_source shared_mmap2(std::move(mmap1)); // or use operator= mio::shared_mmap_source shared_mmap3(std::make_shared(mmap1)); // or use operator= mio::shared_mmap_source shared_mmap4; shared_mmap4.map("path", offset, size_to_map, error); ``` It's possible to define the type of a byte (which has to be the same width as `char`), though aliases for the most common ones are provided by default: ```c++ using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; ``` But it may be useful to define your own types, say when using the new `std::byte` type in C++17: ```c++ using mmap_source = mio::basic_mmap_source; using mmap_sink = mio::basic_mmap_sink; ``` Though generally not needed, since mio maps users requested offsets to page boundaries, you can query the underlying system's page allocation granularity by invoking `mio::page_size()`, which is located in `mio/page.hpp`. ### Single Header File Mio can be added to your project as a single header file simply by including `\single_include\mio\mio.hpp`. Single header files can be regenerated at any time by running the `amalgamate.py` script within `\third_party`. ``` python amalgamate.py -c config.json -s ../include ``` ## CMake As a header-only library, mio has no compiled components. Nevertheless, a [CMake](https://cmake.org/overview/) build system is provided to allow easy testing, installation, and subproject composition on many platforms and operating systems. ### Testing Mio is distributed with a small suite of tests and examples. When mio is configured as the highest level CMake project, this suite of executables is built by default. Mio's test executables are integrated with the CMake test driver program, [CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html). CMake supports a number of backends for compilation and linking. To use a static configuration build tool, such as GNU Make or Ninja: ```sh cd mkdir build cd build # Configure the build cmake -D CMAKE_BUILD_TYPE= \ -G <"Unix Makefiles" | "Ninja"> .. # build the tests < make | ninja | cmake --build . > # run the tests < make test | ninja test | cmake --build . --target test | ctest > ``` To use a dynamic configuration build tool, such as Visual Studio or Xcode: ```sh cd mkdir build cd build # Configure the build cmake -G <"Visual Studio 14 2015 Win64" | "Xcode"> .. # build the tests cmake --build . --config # run the tests via ctest... ctest --build-config # ... or via CMake build tool mode... cmake --build . --config --target test ``` Of course the **build** and **test** steps can also be executed via the **all** and **test** targets, respectively, from within the IDE after opening the project file generated during the configuration step. Mio's testing is also configured to operate as a client to the [CDash](https://www.cdash.org/) software quality dashboard application. Please see the [Kitware documentation](https://cmake.org/cmake/help/latest/manual/ctest.1.html#dashboard-client) for more information on this mode of operation. ### Installation Mio's build system provides an installation target and support for downstream consumption via CMake's [`find_package`](https://cmake.org/cmake/help/v3.0/command/find_package.html) intrinsic function. CMake allows installation to an arbitrary location, which may be specified by defining `CMAKE_INSTALL_PREFIX` at configure time. In the absense of a user specification, CMake will install mio to conventional location based on the platform operating system. To use a static configuration build tool, such as GNU Make or Ninja: ```sh cd mkdir build cd build # Configure the build cmake [-D CMAKE_INSTALL_PREFIX="path/to/installation"] \ [-D BUILD_TESTING=False] \ -D CMAKE_BUILD_TYPE=Release \ -G <"Unix Makefiles" | "Ninja"> .. # install mio ``` To use a dynamic configuration build tool, such as Visual Studio or Xcode: ```sh cd mkdir build cd build # Configure the project cmake [-D CMAKE_INSTALL_PREFIX="path/to/installation"] \ [-D BUILD_TESTING=False] \ -G <"Visual Studio 14 2015 Win64" | "Xcode"> .. # install mio cmake --build . --config Release --target install ``` Note that the last command of the installation sequence may require administrator privileges (e.g. `sudo`) if the installation root directory lies outside your home directory. This installation + copies the mio header files to the `include/mio` subdirectory of the installation root + generates and copies several CMake configuration files to the `share/cmake/mio` subdirectory of the installation root This latter step allows downstream CMake projects to consume mio via `find_package`, e.g. ```cmake find_package( mio REQUIRED ) target_link_libraries( MyTarget PUBLIC mio::mio ) ``` **WINDOWS USERS**: The `mio::mio` target `#define`s `WIN32_LEAN_AND_MEAN` and `NOMINMAX`. The former ensures the imported surface area of the Win API is minimal, and the latter disables Windows' `min` and `max` macros so they don't intefere with `std::min` and `std::max`. Because *mio* is a header only library, these defintions will leak into downstream CMake builds. If their presence is causing problems with your build then you can use the alternative `mio::mio_full_winapi` target, which adds none of these defintions. If mio was installed to a non-conventional location, it may be necessary for downstream projects to specify the mio installation root directory via either + the `CMAKE_PREFIX_PATH` configuration option, + the `CMAKE_PREFIX_PATH` environment variable, or + `mio_DIR` environment variable. Please see the [Kitware documentation](https://cmake.org/cmake/help/v3.0/command/find_package.html) for more information. In addition, mio supports packaged relocatable installations via [CPack](https://cmake.org/cmake/help/latest/manual/cpack.1.html). Following configuration, from the build directory, invoke cpack as follows to generate a packaged installation: ```sh cpack -G -C Release ``` The list of supported generators varies from platform to platform. See the output of `cpack --help` for a complete list of supported generators on your platform. ### Subproject Composition To use mio as a subproject, copy the mio repository to your project's dependencies/externals folder. If your project is version controlled using git, a git submodule or git subtree can be used to syncronize with the updstream repository. The [use](https://services.github.com/on-demand/downloads/submodule-vs-subtree-cheat-sheet/) and [relative advantages](https://andrey.nering.com.br/2016/git-submodules-vs-subtrees/) of these git facilities is beyond the scope of this document, but in brief, each may be established as follows: ```sh # via git submodule cd git submodule add -b master https://github.com/mandreyel/mio.git # via git subtree cd git subtree add --prefix /mio \ https://github.com/mandreyel/mio.git master --squash ``` Given a mio subdirectory in a project, simply add the following lines to your project's to add mio include directories to your target's include path. ```cmake add_subdirectory( path/to/mio/ ) target_link_libraries( MyTarget PUBLIC ) ``` Note that, as a subproject, mio's tests and examples will not be built and CPack integration is deferred to the host project. vroom/src/mio/include/0000755000176200001440000000000013632437541014422 5ustar liggesusersvroom/src/mio/include/mio/0000755000176200001440000000000014151250642015176 5ustar liggesusersvroom/src/mio/include/mio/detail/0000755000176200001440000000000014151250642016440 5ustar liggesusersvroom/src/mio/include/mio/detail/mmap.ipp0000644000176200001440000003447714142512075020124 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_BASIC_MMAP_IMPL #define MIO_BASIC_MMAP_IMPL #include "mio/mmap.hpp" #include "mio/page.hpp" #include "mio/detail/string_util.hpp" #include #ifndef _WIN32 # include # include # include # include #endif namespace mio { namespace detail { #ifdef _WIN32 namespace win { /** Returns the 4 upper bytes of an 8-byte integer. */ inline DWORD int64_high(int64_t n) noexcept { return n >> 32; } /** Returns the 4 lower bytes of an 8-byte integer. */ inline DWORD int64_low(int64_t n) noexcept { return n & 0xffffffff; } template< typename String, typename = typename std::enable_if< std::is_same::type, char>::value >::type > file_handle_type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileA(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } template typename std::enable_if< std::is_same::type, wchar_t>::value, file_handle_type >::type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileW(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } } // win #endif // _WIN32 /** * Returns the last platform specific system error (errno on POSIX and * GetLastError on Win) as a `std::error_code`. */ inline std::error_code last_error() noexcept { std::error_code error; #ifdef _WIN32 error.assign(GetLastError(), std::system_category()); #else error.assign(errno, std::system_category()); #endif return error; } template file_handle_type open_file(const String& path, const access_mode mode, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return invalid_handle; } #ifdef _WIN32 const auto handle = win::open_file_helper(path, mode); #else // POSIX const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); #endif if(handle == invalid_handle) { error = detail::last_error(); } return handle; } inline size_t query_file_size(file_handle_type handle, std::error_code& error) { error.clear(); #ifdef _WIN32 LARGE_INTEGER file_size; if(::GetFileSizeEx(handle, &file_size) == 0) { error = detail::last_error(); return 0; } return static_cast(file_size.QuadPart); #else // POSIX struct stat sbuf; if(::fstat(handle, &sbuf) == -1) { error = detail::last_error(); return 0; } return sbuf.st_size; #endif } struct mmap_context { char* data; int64_t length; int64_t mapped_length; #ifdef _WIN32 file_handle_type file_mapping_handle; #endif }; inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, const access_mode mode, std::error_code& error) { const int64_t aligned_offset = make_offset_page_aligned(offset); const int64_t length_to_map = offset - aligned_offset + length; #ifdef _WIN32 const int64_t max_file_size = offset + length; const auto file_mapping_handle = ::CreateFileMapping( file_handle, 0, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, win::int64_high(max_file_size), win::int64_low(max_file_size), 0); if(file_mapping_handle == invalid_handle) { error = detail::last_error(); return {}; } char* mapping_start = static_cast(::MapViewOfFile( file_mapping_handle, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, win::int64_high(aligned_offset), win::int64_low(aligned_offset), length_to_map)); if(mapping_start == nullptr) { error = detail::last_error(); return {}; } #else // POSIX char* mapping_start = static_cast(::mmap( 0, // Don't give hint as to where to map. length_to_map, mode == access_mode::read ? PROT_READ : PROT_WRITE, MAP_SHARED, file_handle, aligned_offset)); if(mapping_start == MAP_FAILED) { error = detail::last_error(); return {}; } #endif mmap_context ctx; ctx.data = mapping_start + offset; ctx.length = length; ctx.mapped_length = length_to_map; #ifdef _WIN32 ctx.file_mapping_handle = file_mapping_handle; #endif return ctx; } } // namespace detail // -- basic_mmap -- template basic_mmap::~basic_mmap() { conditional_sync(); unmap(); } template basic_mmap::basic_mmap(basic_mmap&& other) : data_(std::move(other.data_)) , length_(std::move(other.length_)) , mapped_length_(std::move(other.mapped_length_)) , file_handle_(std::move(other.file_handle_)) #ifdef _WIN32 , file_mapping_handle_(std::move(other.file_mapping_handle_)) #endif , is_handle_internal_(std::move(other.is_handle_internal_)) { other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif } template basic_mmap& basic_mmap::operator=(basic_mmap&& other) { if(this != &other) { // First the existing mapping needs to be removed. unmap(); data_ = std::move(other.data_); length_ = std::move(other.length_); mapped_length_ = std::move(other.mapped_length_); file_handle_ = std::move(other.file_handle_); #ifdef _WIN32 file_mapping_handle_ = std::move(other.file_mapping_handle_); #endif is_handle_internal_ = std::move(other.is_handle_internal_); // The moved from basic_mmap's fields need to be reset, because // otherwise other's destructor will unmap the same mapping that was // just moved into this. other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif other.is_handle_internal_ = false; } return *this; } template typename basic_mmap::handle_type basic_mmap::mapping_handle() const noexcept { #ifdef _WIN32 return file_mapping_handle_; #else return file_handle_; #endif } template template void basic_mmap::map(const String& path, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto handle = detail::open_file(path, AccessMode, error); if(error) { return; } map(handle, offset, length, error); // This MUST be after the call to map, as that sets this to true. if(!error) { is_handle_internal_ = true; } } template void basic_mmap::map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(handle == invalid_handle) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } const size_type file_size = detail::query_file_size(handle, error); if(error) { return; } if(offset + length > file_size) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto ctx = detail::memory_map(handle, offset, length == map_entire_file ? (file_size - offset) : length, AccessMode, error); if(!error) { // We must unmap the previous mapping that may have existed prior to this call. // Note that this must only be invoked after a new mapping has been created in // order to provide the strong guarantee that, should the new mapping fail, the // `map` function leaves this instance in a state as though the function had // never been invoked. unmap(); file_handle_ = handle; is_handle_internal_ = false; data_ = reinterpret_cast(ctx.data); length_ = ctx.length; mapped_length_ = ctx.mapped_length; #ifdef _WIN32 file_mapping_handle_ = ctx.file_mapping_handle; #endif } } template template typename std::enable_if::type basic_mmap::sync(std::error_code& error) { error.clear(); if(!is_open()) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } if(data()) { #ifdef _WIN32 if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 || ::FlushFileBuffers(file_handle_) == 0) #else // POSIX if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) #endif { error = detail::last_error(); return; } } #ifdef _WIN32 if(::FlushFileBuffers(file_handle_) == 0) { error = detail::last_error(); } #endif } template void basic_mmap::unmap() { if(!is_open()) { return; } // TODO do we care about errors here? #ifdef _WIN32 if(is_mapped()) { ::UnmapViewOfFile(get_mapping_start()); ::CloseHandle(file_mapping_handle_); } #else // POSIX if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } #endif // If `file_handle_` was obtained by our opening it (when map is called with // a path, rather than an existing file handle), we need to close it, // otherwise it must not be closed as it may still be used outside this // instance. if(is_handle_internal_) { #ifdef _WIN32 ::CloseHandle(file_handle_); #else // POSIX ::close(file_handle_); #endif } // Reset fields to their default values. data_ = nullptr; length_ = mapped_length_ = 0; file_handle_ = invalid_handle; #ifdef _WIN32 file_mapping_handle_ = invalid_handle; #endif } template bool basic_mmap::is_mapped() const noexcept { #ifdef _WIN32 return file_mapping_handle_ != invalid_handle; #else // POSIX return is_open(); #endif } template void basic_mmap::swap(basic_mmap& other) { if(this != &other) { using std::swap; swap(data_, other.data_); swap(file_handle_, other.file_handle_); #ifdef _WIN32 swap(file_mapping_handle_, other.file_mapping_handle_); #endif swap(length_, other.length_); swap(mapped_length_, other.mapped_length_); swap(is_handle_internal_, other.is_handle_internal_); } } template template typename std::enable_if::type basic_mmap::conditional_sync() { // This is invoked from the destructor, so not much we can do about // failures here. std::error_code ec; sync(ec); } template template typename std::enable_if::type basic_mmap::conditional_sync() { // noop } template bool operator==(const basic_mmap& a, const basic_mmap& b) { return a.data() == b.data() && a.size() == b.size(); } template bool operator!=(const basic_mmap& a, const basic_mmap& b) { return !(a == b); } template bool operator<(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() < b.size(); } return a.data() < b.data(); } template bool operator<=(const basic_mmap& a, const basic_mmap& b) { return !(a > b); } template bool operator>(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() > b.size(); } return a.data() > b.data(); } template bool operator>=(const basic_mmap& a, const basic_mmap& b) { return !(a < b); } } // namespace mio #endif // MIO_BASIC_MMAP_IMPL vroom/src/mio/include/mio/detail/string_util.hpp0000644000176200001440000001024713632437541021530 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_STRING_UTIL_HEADER #define MIO_STRING_UTIL_HEADER #include namespace mio { namespace detail { template< typename S, typename C = typename std::decay::type, typename = decltype(std::declval().data()), typename = typename std::enable_if< std::is_same::value #ifdef _WIN32 || std::is_same::value #endif >::type > struct char_type_helper { using type = typename C::value_type; }; template struct char_type { using type = typename char_type_helper::type; }; // TODO: can we avoid this brute force approach? template<> struct char_type { using type = char; }; template<> struct char_type { using type = char; }; template struct char_type { using type = char; }; template struct char_type { using type = char; }; #ifdef _WIN32 template<> struct char_type { using type = wchar_t; }; template<> struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; #endif // _WIN32 template struct is_c_str_helper { static constexpr bool value = std::is_same< CharT*, // TODO: I'm so sorry for this... Can this be made cleaner? typename std::add_pointer< typename std::remove_cv< typename std::remove_pointer< typename std::decay< S >::type >::type >::type >::type >::value; }; template struct is_c_str { static constexpr bool value = is_c_str_helper::value; }; #ifdef _WIN32 template struct is_c_wstr { static constexpr bool value = is_c_str_helper::value; }; #endif // _WIN32 template struct is_c_str_or_c_wstr { static constexpr bool value = is_c_str::value #ifdef _WIN32 || is_c_wstr::value #endif ; }; template< typename String, typename = decltype(std::declval().data()), typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(const String& path) { return path.data(); } template< typename String, typename = decltype(std::declval().empty()), typename = typename std::enable_if::value>::type > bool empty(const String& path) { return path.empty(); } template< typename String, typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(String path) { return path; } template< typename String, typename = typename std::enable_if::value>::type > bool empty(String path) { return !path || (*path == 0); } } // namespace detail } // namespace mio #endif // MIO_STRING_UTIL_HEADER vroom/src/mio/include/mio/shared_mmap.hpp0000644000176200001440000004014313632437541020201 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_SHARED_MMAP_HEADER #define MIO_SHARED_MMAP_HEADER #include "mio/mmap.hpp" #include // std::error_code #include // std::shared_ptr namespace mio { /** * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with * `std::shared_ptr` semantics. * * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if * shared semantics are not required. */ template< access_mode AccessMode, typename ByteT > class basic_shared_mmap { using impl_type = basic_mmap; std::shared_ptr pimpl_; public: using value_type = typename impl_type::value_type; using size_type = typename impl_type::size_type; using reference = typename impl_type::reference; using const_reference = typename impl_type::const_reference; using pointer = typename impl_type::pointer; using const_pointer = typename impl_type::const_pointer; using difference_type = typename impl_type::difference_type; using iterator = typename impl_type::iterator; using const_iterator = typename impl_type::const_iterator; using reverse_iterator = typename impl_type::reverse_iterator; using const_reverse_iterator = typename impl_type::const_reverse_iterator; using iterator_category = typename impl_type::iterator_category; using handle_type = typename impl_type::handle_type; using mmap_type = impl_type; basic_shared_mmap() = default; basic_shared_mmap(const basic_shared_mmap&) = default; basic_shared_mmap& operator=(const basic_shared_mmap&) = default; basic_shared_mmap(basic_shared_mmap&&) = default; basic_shared_mmap& operator=(basic_shared_mmap&&) = default; /** Takes ownership of an existing mmap object. */ basic_shared_mmap(mmap_type&& mmap) : pimpl_(std::make_shared(std::move(mmap))) {} /** Takes ownership of an existing mmap object. */ basic_shared_mmap& operator=(mmap_type&& mmap) { pimpl_ = std::make_shared(std::move(mmap)); return *this; } /** Initializes this object with an already established shared mmap. */ basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} /** Initializes this object with an already established shared mmap. */ basic_shared_mmap& operator=(std::shared_ptr mmap) { pimpl_ = std::move(mmap); return *this; } #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * If this is a read-write mapping and the last reference to the mapping, * the destructor invokes sync. Regardless of the access mode, unmap is * invoked as a final step. */ ~basic_shared_mmap() = default; /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ std::shared_ptr get_shared_ptr() { return pimpl_; } /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return pimpl_ ? pimpl_->file_handle() : invalid_handle; } handle_type mapping_handle() const noexcept { return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; } /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type mapped_length() const noexcept { return pimpl_ ? pimpl_->mapped_length() : 0; } /** * Returns the offset, relative to the file's start, at which the mapping was * requested to be created. */ size_type offset() const noexcept { return pimpl_ ? pimpl_->offset() : 0; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return pimpl_->data(); } const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ iterator begin() noexcept { return pimpl_->begin(); } const_iterator begin() const noexcept { return pimpl_->begin(); } const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return pimpl_->end(); } const_iterator end() const noexcept { return pimpl_->end(); } const_iterator cend() const noexcept { return pimpl_->cend(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return pimpl_->rend(); } const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error) { map_impl(path, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map_impl(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { map_impl(handle, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map_impl(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap() { if(pimpl_) pimpl_->unmap(); } void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } /** All operators compare the underlying `basic_mmap`'s addresses. */ friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ == b.pimpl_; } friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return !(a == b); } friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ < b.pimpl_; } friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ <= b.pimpl_; } friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ > b.pimpl_; } friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ >= b.pimpl_; } private: template void map_impl(const MappingToken& token, const size_type offset, const size_type length, std::error_code& error) { if(!pimpl_) { mmap_type mmap = make_mmap(token, offset, length, error); if(error) { return; } pimpl_ = std::make_shared(std::move(mmap)); } else { pimpl_->map(token, offset, length, error); } } }; /** * This is the basis for all read-only mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_source = basic_shared_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_sink = basic_shared_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using shared_mmap_source = basic_shared_mmap_source; using shared_ummap_source = basic_shared_mmap_source; using shared_mmap_sink = basic_shared_mmap_sink; using shared_ummap_sink = basic_shared_mmap_sink; } // namespace mio #endif // MIO_SHARED_MMAP_HEADER vroom/src/mio/include/mio/page.hpp0000644000176200001440000000474513632437541016645 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_PAGE_HEADER #define MIO_PAGE_HEADER #ifdef _WIN32 # include #else # include #endif namespace mio { /** * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ enum class access_mode { read, write }; /** * Determines the operating system's page allocation granularity. * * On the first call to this function, it invokes the operating system specific syscall * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ inline size_t page_size() { static const size_t page_size = [] { #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo.dwAllocationGranularity; #else return sysconf(_SC_PAGE_SIZE); #endif }(); return page_size; } /** * Alligns `offset` to the operating's system page size such that it subtracts the * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } } // namespace mio #endif // MIO_PAGE_HEADER vroom/src/mio/include/mio/mmap.hpp0000644000176200001440000004530314142512075016647 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_MMAP_HEADER #define MIO_MMAP_HEADER #include "mio/page.hpp" #include #include #include #include #ifdef _WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif // WIN32_LEAN_AND_MEAN # include #else // ifdef _WIN32 # define INVALID_HANDLE_VALUE -1 #endif // ifdef _WIN32 namespace mio { // This value may be provided as the `length` parameter to the constructor or // `map`, in which case a memory mapping of the entire file is created. enum { map_entire_file = 0 }; #ifdef _WIN32 using file_handle_type = HANDLE; #else using file_handle_type = int; #endif // This value represents an invalid file handle type. This can be used to // determine whether `basic_mmap::file_handle` is valid, for example. const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; template struct basic_mmap { using value_type = ByteT; using size_type = size_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; using difference_type = std::ptrdiff_t; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using iterator_category = std::random_access_iterator_tag; using handle_type = file_handle_type; static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); private: // Points to the first requested byte, and not to the actual start of the mapping. pointer data_ = nullptr; // Length--in bytes--requested by user (which may not be the length of the // full mapping) and the length of the full mapping. size_type length_ = 0; size_type mapped_length_ = 0; // Letting user map a file using both an existing file handle and a path // introcudes some complexity (see `is_handle_internal_`). // On POSIX, we only need a file handle to create a mapping, while on // Windows systems the file handle is necessary to retrieve a file mapping // handle, but any subsequent operations on the mapped region must be done // through the latter. handle_type file_handle_ = INVALID_HANDLE_VALUE; #ifdef _WIN32 handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; #endif // Letting user map a file using both an existing file handle and a path // introcudes some complexity in that we must not close the file handle if // user provided it, but we must close it if we obtained it using the // provided path. For this reason, this flag is used to determine when to // close `file_handle_`. bool is_handle_internal_ = false; public: /** * The default constructed mmap object is in a non-mapped state, that is, * any operation that attempts to access nonexistent underlying data will * result in undefined behaviour/segmentation faults. */ basic_mmap() = default; #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * `basic_mmap` has single-ownership semantics, so transferring ownership * may only be accomplished by moving the object. */ basic_mmap(const basic_mmap&) = delete; basic_mmap(basic_mmap&&); basic_mmap& operator=(const basic_mmap&) = delete; basic_mmap& operator=(basic_mmap&&); /** * If this is a read-write mapping, the destructor invokes sync. Regardless * of the access mode, unmap is invoked as a final step. */ ~basic_mmap(); /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return file_handle_; } handle_type mapping_handle() const noexcept; /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return file_handle_ != invalid_handle; } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return length() == 0; } /** Returns true if a mapping was established. */ bool is_mapped() const noexcept; /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return length(); } size_type length() const noexcept { return length_; } size_type mapped_length() const noexcept { return mapped_length_; } /** * Returns the offset, relative to the file's start, at which the mapping was * requested to be created. */ size_type offset() const noexcept { return mapped_length_ - length_; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return data_; } const_pointer data() const noexcept { return data_; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator begin() noexcept { return data(); } const_iterator begin() const noexcept { return data(); } const_iterator cbegin() const noexcept { return data(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return data() + length(); } const_iterator end() const noexcept { return data() + length(); } const_iterator cend() const noexcept { return data() + length(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return data_[i]; } const_reference operator[](const size_type i) const noexcept { return data_[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap(); void swap(basic_mmap& other); /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template typename std::enable_if::type sync(std::error_code& error); /** * All operators compare the address of the first byte and size of the two mapped * regions. */ private: template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer get_mapping_start() noexcept { return !data() ? nullptr : data() - offset(); } const_pointer get_mapping_start() const noexcept { return !data() ? nullptr : data() - offset(); } /** * The destructor syncs changes to disk if `AccessMode` is `write`, but not * if it's `read`, but since the destructor cannot be templated, we need to * do SFINAE in a dedicated function, where one syncs and the other is a noop. */ template typename std::enable_if::type conditional_sync(); template typename std::enable_if::type conditional_sync(); }; template bool operator==(const basic_mmap& a, const basic_mmap& b); template bool operator!=(const basic_mmap& a, const basic_mmap& b); template bool operator<(const basic_mmap& a, const basic_mmap& b); template bool operator<=(const basic_mmap& a, const basic_mmap& b); template bool operator>(const basic_mmap& a, const basic_mmap& b); template bool operator>=(const basic_mmap& a, const basic_mmap& b); /** * This is the basis for all read-only mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_source = basic_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_sink = basic_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; /** * Convenience factory method that constructs a mapping for any `basic_mmap` or * `basic_mmap` type. */ template< typename MMap, typename MappingToken > MMap make_mmap(const MappingToken& token, int64_t offset, int64_t length, std::error_code& error) { MMap mmap; mmap.map(token, offset, length, error); return mmap; } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_source::handle_type`. */ template mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, mmap_source::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) { return make_mmap_source(token, 0, map_entire_file, error); } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_sink::handle_type`. */ template mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, mmap_sink::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) { return make_mmap_sink(token, 0, map_entire_file, error); } } // namespace mio #include "detail/mmap.ipp" #endif // MIO_MMAP_HEADER vroom/src/connection.h0000644000176200001440000000356114142512075014521 0ustar liggesusers#pragma once #include #include #include #include // We need to undefine these here as they may be previously defined in windows headers #undef TRUE #undef FALSE #include #ifdef VROOM_USE_CONNECTIONS_API // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wkeyword-macro" #endif #define class class_name #define private private_ptr #include #undef class #undef private #ifdef __clang__ # pragma clang diagnostic pop #endif // clang-format on #if R_CONNECTIONS_VERSION != 1 #error "Missing or unsupported connection API in R" #endif #if R_VERSION < R_Version(3, 3, 0) /* R before 3.3.0 didn't have R_GetConnection() */ extern "C" { extern Rconnection getConnection(int n); static Rconnection R_GetConnection(SEXP sConn) { return getConnection(Rf_asInteger(sConn)); } } #endif #else #pragma once inline SEXP R_GetConnection(SEXP con) { return con; } inline size_t R_ReadConnection(SEXP con, void* buf, size_t n) { static auto readBin = cpp11::package("base")["readBin"]; cpp11::raws res( readBin(con, cpp11::writable::raws(static_cast(0)), n)); memcpy(buf, RAW(res), res.size()); return res.size(); } inline size_t R_WriteConnection(SEXP con, void* buf, size_t n) { static auto writeBin = cpp11::package("base")["writeBin"]; cpp11::writable::raws payload(n); memcpy(RAW(payload), buf, n); writeBin(payload, con); return n; } #endif inline std::string con_description(SEXP con) { static auto summary_connection = cpp11::package("base")["summary.connection"]; return cpp11::as_cpp(cpp11::list(summary_connection(con))[0]); } inline bool is_open(SEXP con) { static auto isOpen = cpp11::package("base")["isOpen"]; cpp11::logicals res(isOpen(con)); return res[0]; } vroom/src/r_utils.h0000644000176200001440000000304514142515724014044 0ustar liggesusers#pragma once #include "utils.h" #include #include #include #include #include namespace vroom { inline std::string get_pb_format(const std::string& which, const std::string& filename = "") { auto fun_name = std::string("pb_") + which + "_format"; auto fun = cpp11::package("vroom")[fun_name.c_str()]; return cpp11::as_cpp(fun(filename)); } inline int get_pb_width(const std::string& format) { auto pb_width = cpp11::package("vroom")["pb_width"]; return cpp11::as_cpp(pb_width(format)); } template static char guess_delim( const T& source, size_t start, size_t guess_max, size_t end, const char quote) { std::vector lines; if (end == 0) { end = source.size(); } size_t nl; newline_type nlt; std::tie(nl, nlt) = find_next_newline( source, start, /* comment */ "", /* skip_empty_rows */ false, /* embedded_nl */ true, /* quote */ quote); while (nl > start && nl <= end && guess_max > 0) { auto str = std::string(source.data() + start, nl - start); lines.push_back(str); start = nl + 1; std::tie(nl, nlt) = find_next_newline( source, start, /* comment */ "", /* skip_empty_rows */ false, /* embededd_nl */ true, quote); --guess_max; } auto guess_delim = cpp11::package("vroom")["guess_delim"]; char delim; delim = cpp11::as_cpp(guess_delim(lines)); return delim; } } // namespace vroom vroom/src/vroom_fct.h0000644000176200001440000001550614142515724014366 0ustar liggesusers#include #include #include #include "altrep.h" #include "vroom.h" #include "vroom_vec.h" #include using namespace vroom; cpp11::integers read_fct_explicit( vroom_vec_info* info, const cpp11::strings& levels, bool ordered); cpp11::integers read_fct_implicit(vroom_vec_info* info, bool include_na); template int parse_factor( const I& itr, const C& col, const std::unordered_map& level_map, LocaleInfo& locale, std::shared_ptr& errors, SEXP na) { auto str = *itr; SEXP str_sexp = locale.encoder_.makeSEXP(str.begin(), str.end(), false); auto search = level_map.find(str_sexp); if (search != level_map.end()) { return search->second; } else { if (!is_explicit_na(na, str.begin(), str.end())) { errors->add_error( itr.index(), col->get_index(), "value in level set", std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return NA_INTEGER; } } template int parse_factor( R_xlen_t i, const C& col, const std::unordered_map& level_map, LocaleInfo& locale, std::shared_ptr& errors, SEXP na) { auto str = col->at(i); SEXP str_sexp = locale.encoder_.makeSEXP(str.begin(), str.end(), false); auto search = level_map.find(str_sexp); if (search != level_map.end()) { return search->second; } else { if (!is_explicit_na(na, str.begin(), str.end())) { auto&& itr = col->begin() + i; errors->add_error( itr.index(), col->get_index(), "value in level set", std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return NA_INTEGER; } } #ifdef HAS_ALTREP struct vroom_factor_info { vroom_vec_info* info; std::unordered_map levels; }; struct vroom_fct : vroom_vec { public: static R_altrep_class_t class_t; // Make an altrep object of class `vroom_factor::class_t` static SEXP Make(vroom_vec_info* info, cpp11::strings levels, bool ordered) { vroom_factor_info* fct_info = new vroom_factor_info; fct_info->info = info; for (auto i = 0; i < levels.size(); ++i) { if (levels[i] == NA_STRING) { for (const auto& str : *info->na) { fct_info->levels[str] = i + 1; } } else { fct_info->levels[levels[i]] = i + 1; } } SEXP out = PROTECT(R_MakeExternalPtr(fct_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, Finalize, FALSE); // make a new altrep object of class `vroom_factor::class_t` cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("levels") = static_cast(levels); if (ordered) { res.attr("class") = {"ordered", "factor"}; } else { res.attr("class") = "factor"; } UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_factor (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } static void Finalize(SEXP ptr) { if (ptr == nullptr || R_ExternalPtrAddr(ptr) == nullptr) { return; } auto info_p = static_cast(R_ExternalPtrAddr(ptr)); delete info_p->info; delete info_p; info_p = nullptr; R_ClearExternalPtr(ptr); } static inline vroom_factor_info& Info(SEXP x) { return *static_cast( R_ExternalPtrAddr(R_altrep_data1(x))); } static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } auto inf = Info(vec); return inf.info->column->size(); } static inline string Get(SEXP vec, R_xlen_t i) { auto inf = Info(vec); return inf.info->column->at(i); } // ALTSTRING methods ----------------- static int Val(SEXP vec, R_xlen_t i) { auto info = Info(vec); double out = parse_factor( i, info.info->column, info.levels, *info.info->locale, info.info->errors, *info.info->na); info.info->errors->warn_for_errors(); return out; } // the element at the index `i` // // this does not do bounds checking because that's expensive, so // the caller must take care of that static int factor_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return INTEGER(data2)[i]; } return Val(vec, i); } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } // allocate a standard character vector for data2 R_xlen_t n = Length(vec); cpp11::writable::integers out(n); for (R_xlen_t i = 0; i < n; ++i) { out[i] = Val(vec, i); } R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } static SEXP Extract_subset(SEXP x, SEXP indx, SEXP) { SEXP data2 = R_altrep_data2(x); // If the vector is already materialized, just fall back to the default // implementation if (data2 != R_NilValue) { return nullptr; } // If there are no indices to subset fall back to default implementation. if (Rf_xlength(indx) == 0) { return nullptr; } cpp11::sexp x_(x); auto idx = get_subset_index(indx, Rf_xlength(x)); if (idx == nullptr) { return nullptr; } auto inf = Info(x); auto info = new vroom_vec_info{ inf.info->column->subset(idx), inf.info->num_threads, inf.info->na, inf.info->locale, inf.info->errors, inf.info->format}; bool is_ordered = Rf_inherits(x_, "ordered"); return Make(info, cpp11::strings(x_.attr("levels")), is_ordered); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { class_t = R_make_altinteger_class("vroom_fct", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altinteger_Elt_method(class_t, factor_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_fct(DllInfo* dll); vroom/src/index_collection.h0000644000176200001440000000645114143524032015702 0ustar liggesusers#pragma once #include "index.h" #include "iterator.h" #include "vroom_errors.h" #include #include #ifdef VROOM_LOG #include "spdlog/spdlog.h" #endif namespace vroom { class index_collection : public index, public std::enable_shared_from_this { public: // For delimited files index_collection( const cpp11::list& in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr& errors, const size_t num_threads, const bool progress); // For fixed width files index_collection( const cpp11::list& in, const std::vector& col_starts, const std::vector& col_ends, const bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress); string get(size_t row, size_t col) const override; size_t num_columns() const override { return columns_; } size_t num_rows() const override { return rows_; } std::vector row_sizes() const { std::vector out; for (const auto& index : indexes_) { out.push_back(index->num_rows()); } return out; } std::string get_delim() const override { return indexes_[0]->get_delim(); } public: class full_iterator : public base_iterator { size_t i_; std::shared_ptr idx_; size_t column_; size_t end_; iterator it_; iterator it_end_; iterator it_start_; public: full_iterator(std::shared_ptr idx, size_t column); void next() override; void advance(ptrdiff_t n) override; inline bool equal_to(const base_iterator& other) const override { auto other_ = static_cast(&other); return i_ == other_->i_ && it_ == other_->it_; } ptrdiff_t distance_to(const base_iterator& it) const override; string value() const override; full_iterator* clone() const override; string at(ptrdiff_t n) const override; std::string filename() const override { return it_.filename(); } size_t index() const override { return it_.index(); } size_t position() const override { return it_.position(); } virtual ~full_iterator() {} }; std::shared_ptr get_column(size_t column) const override { auto begin = new full_iterator(shared_from_this(), column); auto end = new full_iterator(shared_from_this(), column); end->advance(rows_); return std::make_shared(begin, end, column); } std::shared_ptr get_row(size_t row) const override { for (const auto& idx : indexes_) { auto sz = idx->num_rows(); if (row < sz) { return idx->get_row(row); } row -= sz; } /* should never get here */ return indexes_[0]->get_header(); } std::shared_ptr get_header() const override { return indexes_[0]->get_header(); } private: std::vector> indexes_; size_t rows_; size_t columns_; }; } // namespace vroom vroom/src/vroom_date.h0000644000176200001440000000614214142515724014523 0ustar liggesusers#pragma once #include #include "vroom_dttm.h" using namespace vroom; double parse_date( const char* begin, const char* end, DateTimeParser& parser, const std::string& format); cpp11::doubles read_date(vroom_vec_info* info); #ifdef HAS_ALTREP /* no support for altrep before 3.5 */ class vroom_date : public vroom_dttm { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { vroom_dttm_info* dttm_info = new vroom_dttm_info; dttm_info->info = info; dttm_info->parser = std::unique_ptr(new DateTimeParser(info->locale.get())); SEXP out = PROTECT(R_MakeExternalPtr(dttm_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_dttm::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"Date"}; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_date (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // the element at the index `i` static double date_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = Info(vec); auto err_msg = info->info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->info->format; double out = parse_value( i, info->info->column, [&](const char* begin, const char* end) -> double { return parse_date(begin, end, *info->parser, info->info->format); }, info->info->errors, err_msg.c_str(), *info->info->na); info->info->errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto inf = Info(vec); auto out = read_date(inf->info); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_date::class_t = R_make_altreal_class("vroom_date", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, date_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_date(DllInfo* dll); vroom/src/utils.h0000644000176200001440000001716414151152772013532 0ustar liggesusers#pragma once #include #include #include #include #include namespace vroom { inline bool is_comment(const char* begin, const char* end, const std::string& comment) { if (comment.empty() || comment.size() >= static_cast(end - begin)) { return false; } return strncmp(comment.data(), begin, comment.size()) == 0; } template inline size_t skip_rest_of_line(const T& source, size_t start) { auto out = memchr(source.data() + start, '\n', source.size() - start); if (!out) { return (source.size()); } return static_cast(out) - source.data(); } inline bool is_empty_line(const char* begin, const char* end, const bool skip_empty_rows) { if (!skip_empty_rows) { return false; } if (skip_empty_rows && *begin == '\n') { return true; } while (begin < end && (*begin == ' ' || *begin == '\t' || *begin == '\r')) { ++begin; } return *begin == '\n'; } inline std::pair is_blank_or_comment_line( const char* begin, const char* end, const std::string& comment, const bool skip_empty_rows) { bool should_skip = false; bool is_comment = false; if (!skip_empty_rows && comment.empty()) { return std::pair(should_skip, is_comment); } if (skip_empty_rows && (*begin == '\n' || *begin == '\r')) { should_skip = true; return std::pair(should_skip, is_comment); } while (begin < end && (*begin == ' ' || *begin == '\t')) { ++begin; } if (skip_empty_rows && (*begin == '\n' || *begin == '\r')) { should_skip = true; return std::pair(should_skip, is_comment); } if (!comment.empty() && strncmp(begin, comment.data(), comment.size()) == 0) { should_skip = true; is_comment = true; return std::pair(should_skip, is_comment); } return std::pair(should_skip, is_comment); } inline bool is_crlf(const char* buf, size_t pos, size_t end) { return buf[pos] == '\r' && pos + 1 < end && buf[pos + 1] == '\n'; } enum newline_type { CR /* linux */, CRLF /* windows */, LF /* old macOS */, NA /* unknown */ }; template static std::pair find_next_non_quoted_newline(const T& source, size_t start, const char quote) { if (start > source.size() - 1) { return {source.size() - 1, NA}; } std::array query = {'\r', '\n', quote, '\0'}; auto buf = source.data(); size_t pos = start; size_t end = source.size() - 1; bool in_quote = false; while (pos < end) { size_t buf_offset = strcspn(buf + pos, query.data()); pos = pos + buf_offset; auto c = buf[pos]; if (c == '\n' || c == '\r') { if (in_quote) { ++pos; continue; } if (c == '\n') { return {pos, LF}; } if (is_crlf(buf, pos, end)) { return {pos + 1, CRLF}; } return {pos, CR}; } else if (c == quote) { in_quote = !in_quote; } ++pos; } if (pos > end) { return {end, NA}; } return {pos, NA}; } template static std::pair find_next_newline( const T& source, size_t start, const std::string& comment, const bool skip_empty_rows, bool embedded_nl, const char quote, newline_type type = NA) { if (start >= source.size()) { return {source.size() - 1, NA}; } if (embedded_nl) { size_t value; newline_type nl; std::tie(value, nl) = find_next_non_quoted_newline(source, start, quote); // REprintf("%i\n", value); return {value, nl}; } const char* begin = source.data() + start; const char* end = source.data() + source.size(); std::array query; switch (type) { case NA: query = {'\n', '\r', '\0'}; break; case CR: query = {'\r', '\0'}; break; case CRLF: case LF: query = {'\n', '\0'}; break; } bool should_skip; while (begin && begin < end) { size_t offset = strcspn(begin, query.data()); // REprintf("%i\n", offset); begin += offset; break; if (!(begin && begin + 1 < end)) { std::tie(should_skip, std::ignore) = is_blank_or_comment_line(begin + 1, end, comment, skip_empty_rows); if (should_skip) { break; } } } if (!begin) { return {source.size() - 1, NA}; } size_t pos = begin - source.data(); if (begin[0] == '\n') { return {pos, LF}; } if (begin[0] == '\r') { if (is_crlf(source.data(), pos, end - source.data())) { return {pos + 1, CRLF}; } return {pos, CR}; } return {pos, NA}; } template T get_env(const char* name, T default_value) { char* p; p = getenv(name); if (!p || strlen(p) == 0) { return default_value; } std::stringstream ss(p); double out; ss >> out; return out; } inline bool is_space(const char* c) { return *c == ' ' || *c == '\t' || *c == '\0' || *c == '\r'; } inline void trim_whitespace(const char*& begin, const char*& end) { while (begin != end && is_space(begin)) { ++begin; } while (end != begin && is_space(end - 1)) { --end; } } template size_t skip_bom(const T& source) { /* Skip Unicode Byte Order Marks https://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding 00 00 FE FF: UTF-32BE FF FE 00 00: UTF-32LE FE FF: UTF-16BE FF FE: UTF-16LE EF BB BF: UTF-8 */ auto size = source.size(); auto begin = source.data(); switch (begin[0]) { // UTF-32BE case '\x00': if (size >= 4 && begin[1] == '\x00' && begin[2] == '\xFE' && begin[3] == '\xFF') { return 4; } break; // UTF-8 case '\xEF': if (size >= 3 && begin[1] == '\xBB' && begin[2] == '\xBF') { return 3; } break; // UTF-16BE case '\xfe': if (size >= 2 && begin[1] == '\xff') { return 2; } break; case '\xff': if (size >= 2 && begin[1] == '\xfe') { // UTF-32 LE if (size >= 4 && begin[2] == '\x00' && begin[3] == '\x00') { return 4; } else { // UTF-16 LE return 2; } } break; } return 0; } // This skips leading blank lines and comments (if needed) template size_t find_first_line( const T& source, size_t skip, const char* comment, const bool skip_empty_rows, const bool embedded_nl, const char quote) { auto begin = skip_bom(source); /* Skip skip parameters, comments and blank lines */ bool should_skip, is_comment; std::tie(should_skip, is_comment) = is_blank_or_comment_line( source.data() + begin, source.data() + source.size(), comment, skip_empty_rows); while (begin < source.size() - 1 && (should_skip || skip > 0)) { std::tie(begin, std::ignore) = find_next_newline( source, begin, "", /* skip_empty_rows */ false, embedded_nl, is_comment ? '\0' : quote); /* don't deal with quotes in comment lines*/ ++begin; skip = skip > 0 ? skip - 1 : skip; std::tie(should_skip, is_comment) = is_blank_or_comment_line( source.data() + begin, source.data() + source.size(), comment, skip_empty_rows); } return begin; } inline bool matches(const char* start, const char* end, const std::string& needle) { if (end <= start || needle.empty() || static_cast(end - start) < needle.size()) { return false; } bool res = strncmp(start, needle.data(), needle.size()) == 0; return res; } inline bool has_expected_line_ending(newline_type nl, const char value) { if (nl == CR && value == '\r') { return true; } return value == '\n'; } } // namespace vroom vroom/src/vroom_write.cc0000644000176200001440000003273514142515724015105 0ustar liggesusers#include "grisu3.h" #include #include #include #include #include #include #include #include "RProgress.h" #include "connection.h" #include "r_utils.h" #include "unicode_fopen.h" typedef enum { quote_needed = 1, quote_all = 2, escape_double = 4, escape_backslash = 8, bom = 16 } vroom_write_opt_t; size_t get_buffer_size( const cpp11::list& input, const std::vector& types, size_t start, size_t end) { // First need to determine how big the buffer(s) should be // - For characters we need the total nchar() + 2 (for quotes if needed) // (they are converted to UTF-8 in R) // - For factors we need max(nchar(levels)) (but currently we just convert to // character in R) // - For decimal numbers we need 24 // source: https://stackoverflow.com/a/52045523/2055486 // - For 32 bit integers we need 11 (10 for digits plus the sign) // - For logical we need 5 (FALSE) // // - Currently we convert dates, times and datetimes to character before // output. If we wanted to do it in C it would be // - For dates we need 10 (2019-04-12) // - For times we need 8 (01:00:00) // - For datetimes we need 20 (2019-04-12T20:46:31Z) size_t buf_size = 0; size_t num_rows = end - start; for (int i = 0; i < input.size(); ++i) { switch (types[i]) { case STRSXP: { for (size_t j = start; j < end; ++j) { auto sz = Rf_xlength(STRING_ELT(input[i], j)); buf_size += sz + 2; } break; } case LGLSXP: buf_size += 5 * num_rows; break; case REALSXP: buf_size += 24 * num_rows; break; case INTSXP: buf_size += 11 * num_rows; break; } } // Add size of delimiters + newline buf_size += input.size() * num_rows; return buf_size; } bool needs_quote(const char* str, const char delim, const char* na_str) { if (strncmp(str, na_str, 2) == 0) { return true; } for (const char* cur = str; *cur != '\0'; ++cur) { if (*cur == '\n' || *cur == '\r' || *cur == '"' || *cur == delim) { return true; } } return false; } // adapted from https://stackoverflow.com/a/28110728/2055486 template void append_literal(std::vector& buf, const char (&str)[N]) { std::copy(std::begin(str), std::end(str) - 1, std::back_inserter(buf)); } inline bool is_utf8(cetype_t ce) { switch (ce) { case CE_ANY: case CE_BYTES: case CE_UTF8: return true; default: return false; } } void str_to_buf( SEXP str, std::vector& buf, const char delim, const char* na_str, size_t na_len, size_t options) { if (str == NA_STRING) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); return; } const char* str_p; size_t len; if (is_utf8(Rf_getCharCE(str))) { str_p = CHAR(str); len = Rf_xlength(str); } else { str_p = Rf_translateCharUTF8(str); len = strlen(str_p); } bool should_quote = options & quote_all || (options & quote_needed && needs_quote(str_p, delim, na_str)); if (should_quote) { buf.push_back('"'); } auto end = str_p + len; bool should_escape = options & (escape_double | escape_backslash); auto escape = options & escape_double ? '"' : options & escape_backslash ? '\\' : '\0'; buf.reserve(buf.size() + len); while (str_p < end) { if (should_escape && *str_p == '"') { buf.push_back(escape); } buf.push_back(*str_p++); } if (should_quote) { buf.push_back('"'); } return; } std::vector fill_buf( const cpp11::list& input, const char delim, const std::string& eol, const char* na_str, size_t options, const std::vector& types, const std::vector& ptrs, size_t begin, size_t end) { auto buf = std::vector(); auto na_len = strlen(na_str); for (size_t row = begin; row < end; ++row) { for (int col = 0; col < input.size(); ++col) { switch (types[col]) { case STRSXP: { auto str = STRING_ELT(input[col], row); str_to_buf(str, buf, delim, na_str, na_len, options); break; } case LGLSXP: { int value = static_cast(ptrs[col])[row]; switch (value) { case TRUE: append_literal(buf, "TRUE"); break; case FALSE: append_literal(buf, "FALSE"); break; default: std::copy(na_str, na_str + na_len, std::back_inserter(buf)); break; } break; } case REALSXP: { auto value = static_cast(ptrs[col])[row]; if (!R_FINITE(value)) { if (ISNA(value)) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); } else if (ISNAN(value)) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); } else if (value > 0) { append_literal(buf, "Inf"); } else { append_literal(buf, "-Inf"); } } else { char temp_buf[33]; int len = dtoa_grisu3(static_cast(ptrs[col])[row], temp_buf); std::copy(temp_buf, temp_buf + len, std::back_inserter(buf)); } break; } case INTSXP: { auto value = static_cast(ptrs[col])[row]; if (value == NA_INTEGER) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); } else { // TODO: use something like https://github.com/jeaiii/itoa for // faster integer writing char temp_buf[12]; auto len = sprintf(temp_buf, "%i", value); std::copy(temp_buf, temp_buf + len, std::back_inserter(buf)); } break; } } if (delim != '\0') { buf.push_back(delim); } } if (delim != '\0') { buf.pop_back(); } for (auto c : eol) { buf.push_back(c); } } return buf; } template void write_buf(const std::vector& buf, T& out) {} template <> void write_buf(const std::vector& buf, std::FILE*& out) { std::fwrite(buf.data(), sizeof buf[0], buf.size(), out); } template <> void write_buf(const std::vector& buf, std::vector& data) { std::copy(buf.begin(), buf.end(), std::back_inserter(data)); } template <> void write_buf(const std::vector& buf, SEXP& con) { R_WriteConnection(con, (void*)buf.data(), sizeof buf[0] * buf.size()); } #ifdef VROOM_USE_CONNECTIONS_API void write_buf_con( const std::vector& buf, Rconnection con, bool is_stdout) { if (is_stdout) { std::string out; std::copy(buf.begin(), buf.end(), std::back_inserter(out)); Rprintf("%.*s", buf.size(), out.c_str()); } else { R_WriteConnection(con, (void*)buf.data(), sizeof buf[0] * buf.size()); } } #else void write_buf_con(const std::vector& buf, SEXP con, bool is_stdout) { if (is_stdout) { std::string out; std::copy(buf.begin(), buf.end(), std::back_inserter(out)); Rprintf("%.*s", buf.size(), out.c_str()); } else { write_buf(buf, con); } } #endif std::vector get_types(const cpp11::list& input) { std::vector out; for (auto col : input) { out.push_back(TYPEOF(col)); } return out; } std::vector get_ptrs(const cpp11::list& input) { std::vector out; for (auto col : input) { switch (TYPEOF(col)) { case REALSXP: out.push_back(REAL(col)); break; case INTSXP: out.push_back(INTEGER(col)); break; case LGLSXP: out.push_back(LOGICAL(col)); break; default: out.push_back(nullptr); } } return out; } std::vector get_header( const cpp11::list& input, const char delim, const std::string& eol, size_t options) { cpp11::strings names(input.attr("names")); std::vector out; for (R_xlen_t i = 0; i < names.size(); ++i) { auto str = STRING_ELT(names, i); str_to_buf(str, out, delim, "", 0, options); if (delim != '\0') { out.push_back(delim); } } if (delim != '\0') { out.pop_back(); } for (auto c : eol) { out.push_back(c); } return out; } template void vroom_write_out( const cpp11::list& input, T& out, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines) { size_t begin = 0; size_t num_rows = Rf_xlength(input[0]); std::array>>, 2> futures; futures[0].resize(num_threads); futures[1].resize(num_threads); std::future write_fut; int idx = 0; auto types = get_types(input); auto ptrs = get_ptrs(input); if (!append && options & bom) { std::vector bom{'\xEF', '\xBB', '\xBF'}; write_buf(bom, out); } if (col_names) { auto header = get_header(input, delim, eol, options); write_buf(header, out); } std::unique_ptr pb = nullptr; if (progress) { pb = std::unique_ptr( new RProgress::RProgress(vroom::get_pb_format("write"), 1e12)); } while (begin < num_rows) { size_t t = 0; while (t < num_threads && begin < num_rows) { auto num_lines = std::min(buf_lines, num_rows - begin); auto end = begin + num_lines; futures[idx][t++] = std::async( fill_buf, input, delim, eol, na_str, options, types, ptrs, begin, end); begin += num_lines; } if (write_fut.valid()) { auto sz = write_fut.get(); if (progress) { pb->tick(sz); } } write_fut = std::async([&, idx, t] { size_t sz = 0; for (size_t i = 0; i < t; ++i) { auto buf = futures[idx][i].get(); write_buf(buf, out); sz += buf.size(); } return sz; }); idx = (idx + 1) % 2; } // Wait for the last writing to finish if (write_fut.valid()) { write_fut.get(); if (progress) { pb->update(1); } } } [[cpp11::register]] void vroom_write_( const cpp11::list& input, const std::string& filename, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines) { char mode[3] = "wb"; if (append) { strcpy(mode, "ab"); } std::FILE* out = unicode_fopen(filename.c_str(), mode); if (!out) { std::string msg("Cannot open file for writing:\n* "); msg += '\'' + filename + '\''; cpp11::stop(msg.c_str()); } vroom_write_out( input, out, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines); // Close the file std::fclose(out); } // TODO: Think about refactoring this so it and vroom_write_ can share some // code [[cpp11::register]] void vroom_write_connection_( const cpp11::list& input, const cpp11::sexp& con, const char delim, const std::string& eol, const char* na_str, bool col_names, size_t options, size_t num_threads, bool progress, size_t buf_lines, bool is_stdout, bool append) { char mode[3] = "wb"; if (append) { strcpy(mode, "ab"); } size_t begin = 0; size_t num_rows = Rf_xlength(input[0]); auto con_ = R_GetConnection(con); bool should_open = !is_open(con); if (should_open) { cpp11::package("base")["open"](con, mode); } bool should_close = should_open; std::array>>, 2> futures; futures[0].resize(num_threads); futures[1].resize(num_threads); std::future write_fut; int idx = 0; auto types = get_types(input); auto ptrs = get_ptrs(input); if (col_names) { auto header = get_header(input, delim, eol, options); write_buf_con(header, con_, is_stdout); } std::unique_ptr pb = nullptr; if (progress) { pb = std::unique_ptr( new RProgress::RProgress(vroom::get_pb_format("write"), 1e12)); } while (begin < num_rows) { size_t t = 0; while (t < num_threads && begin < num_rows) { auto num_lines = std::min(buf_lines, num_rows - begin); auto end = begin + num_lines; futures[idx][t++] = std::async( fill_buf, input, delim, eol, na_str, options, types, ptrs, begin, end); begin += num_lines; } for (size_t i = 0; i < t; ++i) { auto buf = futures[idx][i].get(); write_buf_con(buf, con_, is_stdout); auto sz = buf.size(); if (progress) { pb->tick(sz); } } idx = (idx + 1) % 2; } if (progress) { pb->update(1); } // Close the connection if (should_close) { cpp11::package("base")["close"](con); } } [[cpp11::register]] cpp11::strings vroom_format_( const cpp11::list& input, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines) { std::vector data; vroom_write_out( input, data, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines); cpp11::writable::strings out(1); out[0] = Rf_mkCharLenCE(data.data(), data.size(), CE_UTF8); return out; } vroom/src/DateTime.h0000644000176200001440000000645414142515724014066 0ustar liggesusers#ifndef READR_DATE_TIME_H_ #define READR_DATE_TIME_H_ #include #include #include #include class DateTime { int year_, mon_, day_, hour_, min_, sec_, offset_; double psec_; std::string tz_; public: DateTime( int year, int mon, int day, int hour = 0, int min = 0, int sec = 0, double psec = 0, const std::string& tz = "UTC") : year_(year), mon_(mon), day_(day), hour_(hour), min_(min), sec_(sec), offset_(0), psec_(psec), tz_(tz) {} // Used to add time zone offsets which can only be easily applied once // we've converted into seconds since epoch. void setOffset(int offset) { offset_ = offset; } // Is this a valid date time? bool validDateTime() const { return validDate() && validTime(); } bool validDate() const { // vroom does not allow negative years, date does if (year_ < 0) return false; return (date::year{year_} / mon_ / day_).ok(); } bool validTime() const { if (sec_ < 0 || sec_ > 60) return false; if (min_ < 0 || min_ > 59) return false; if (hour_ < 0 || hour_ > 23) return false; return true; } bool validDuration() const { if (sec_ < -59 || sec_ > 59) return false; if (min_ < -59 || min_ > 59) return false; return true; } double datetime() const { return (tz_ == "UTC") ? utctime() : localtime(); } int date() const { return utcdate(); } double time() const { return psec_ + sec_ + (min_ * 60.) + (hour_ * 3600.); } private: // Number of number of seconds since 1970-01-01T00:00:00Z. // Compared to usual implementations this returns a double, and supports // a wider range of dates. Invalid dates have undefined behaviour. double utctime() const { return utcdate() * 86400.0 + time() + offset_; } // Find number of days since 1970-01-01. // Invalid dates have undefined behaviour. int utcdate() const { if (!validDate()) return NA_REAL; const date::year_month_day ymd{date::year(year_) / mon_ / day_}; const date::sys_days st{ymd}; return st.time_since_epoch().count(); } double localtime() const { if (!validDateTime()) return NA_REAL; const date::time_zone* p_time_zone; if (!tzdb::locate_zone(tz_, p_time_zone)) { throw std::runtime_error( "'" + tz_ + "' not found in the time zone database."); } const date::local_seconds lt = std::chrono::seconds{sec_} + std::chrono::minutes{min_} + std::chrono::hours{hour_} + date::local_days{date::year{year_} / mon_ / day_}; date::local_info info; if (!tzdb::get_local_info(lt, p_time_zone, info)) { throw std::runtime_error( "Can't lookup local time info for the supplied time zone."); } switch (info.result) { case date::local_info::unique: return (lt.time_since_epoch() - info.first.offset).count() + psec_ + offset_; case date::local_info::ambiguous: // Choose `earliest` of the two ambiguous times return (lt.time_since_epoch() - info.first.offset).count() + psec_ + offset_; case date::local_info::nonexistent: return NA_REAL; } throw std::runtime_error("should never happen"); } }; #endif vroom/src/grisu3.h0000644000176200001440000000334314142512075013574 0ustar liggesusers#ifndef FASTREAD_GRISU3_H_ #define FASTREAD_GRISU3_H_ /* Copyright Jukka JylΓ€nki Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* This file is part of an implementation of the "grisu3" double to string conversion algorithm described in the research paper "Printing Floating-Point Numbers Quickly And Accurately with Integers" by Florian Loitsch, available at http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf */ extern "C" { /// Converts the given double-precision floating point number to a string representation. /** For most inputs, this string representation is the shortest such, which deserialized again, returns the same bit representation of the double. @param v The number to convert. @param dst [out] The double-precision floating point number will be written here as a null-terminated string. The conversion algorithm will write at most 25 bytes to this buffer. (null terminator is included in this count). The dst pointer may not be null. @return the number of characters written to dst, excluding the null terminator (which is always written) is returned here. */ int dtoa_grisu3(double v, char *dst); } #ifdef __cplusplus #include std::string dtoa_grisu3_string(double v); #endif #endif vroom/src/vroom_vec.h0000644000176200001440000001157414142515724014370 0ustar liggesusers#pragma once #include #include #include "LocaleInfo.h" #include "altrep.h" #include "index_collection.h" #include "vroom_errors.h" using namespace vroom; struct vroom_vec_info { std::shared_ptr column; size_t num_threads; std::shared_ptr na; std::shared_ptr locale; std::shared_ptr errors; std::string format; }; namespace vroom { inline bool is_explicit_na(SEXP na, const char* begin, const char* end) { R_xlen_t n = end - begin; for (R_xlen_t i = 0; i < Rf_xlength(na); ++i) { SEXP str = STRING_ELT(na, i); R_xlen_t str_n = Rf_xlength(str); const char* v = CHAR(STRING_ELT(na, i)); if (n == str_n && strncmp(v, begin, n) == 0) { return true; } } return false; } template struct StringAnnotationTypeMap { static const std::string annotation; }; template <> inline double na() { return NA_REAL; } template <> inline int na() { return NA_INTEGER; } template static auto parse_value( const I& itr, const C& col, F f, std::shared_ptr& errors, const char* expected, SEXP na) -> V { auto str = *itr; if (is_explicit_na(na, str.begin(), str.end())) { return vroom::na(); } V out = f(str.begin(), str.end()); if (cpp11::is_na(out)) { errors->add_error( itr.index(), col->get_index(), expected, std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return out; } template static auto parse_value( R_xlen_t i, const C& col, F f, std::shared_ptr& errors, const char* expected, SEXP na) -> V { auto&& str = col->at(i); if (is_explicit_na(na, str.begin(), str.end())) { return vroom::na(); } V out = f(str.begin(), str.end()); if (cpp11::is_na(out)) { auto itr = col->begin() + i; errors->add_error( itr.index(), col->get_index(), expected, std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return out; } } // namespace vroom #ifdef HAS_ALTREP class vroom_vec { public: // finalizer for the external pointer static void Finalize(SEXP ptr) { if (ptr == nullptr || R_ExternalPtrAddr(ptr) == nullptr) { return; } auto info_p = static_cast(R_ExternalPtrAddr(ptr)); delete info_p; info_p = nullptr; R_ClearExternalPtr(ptr); } static inline vroom_vec_info& Info(SEXP x) { return *static_cast(R_ExternalPtrAddr(R_altrep_data1(x))); } // ALTREP methods ------------------- // The length of the object static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } auto& inf = Info(vec); return inf.column->size(); } static inline string Get(SEXP vec, R_xlen_t i) { auto& inf = Info(vec); return inf.column->at(i); } // ALTVec methods ------------------- static const void* Dataptr_or_null(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 == R_NilValue) return nullptr; return STDVEC_DATAPTR(data2); } static std::shared_ptr> get_subset_index(SEXP indx, R_xlen_t x_len) { auto idx = std::make_shared>(); R_xlen_t n = Rf_xlength(indx); idx->reserve(n); int i_val; double d_val; for (R_xlen_t i = 0; i < n; ++i) { switch (TYPEOF(indx)) { case INTSXP: i_val = INTEGER_ELT(indx, i); if (i_val == NA_INTEGER || i_val > x_len) { return nullptr; } idx->push_back(i_val - 1); break; case REALSXP: d_val = REAL_ELT(indx, i); if (ISNA(d_val) || d_val > x_len) { return nullptr; } idx->push_back(d_val - 1); break; default: Rf_error("Invalid index"); } } return idx; } template static SEXP Extract_subset(SEXP x, SEXP indx, SEXP) { SEXP data2 = R_altrep_data2(x); // If the vector is already materialized, just fall back to the default // implementation if (data2 != R_NilValue) { return nullptr; } // If there are no indices to subset fall back to default implementation. if (Rf_xlength(indx) == 0) { return nullptr; } vroom_vec_info* info; // This block is here to avoid a false positive from rchck { auto& inf = Info(x); auto idx = get_subset_index(indx, Rf_xlength(x)); if (idx == nullptr) { return nullptr; } info = new vroom_vec_info{ inf.column->subset(idx), inf.num_threads, inf.na, inf.locale, inf.errors, inf.format}; } return T::Make(info); } }; #endif vroom/src/grisu3.c0000644000176200001440000003244314142512075013572 0ustar liggesusers/* Copyright Jukka JylΓ€nki Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Modifcations to dtoa_grisu3() referenced mikkelfj: are under the following * Copyright (c) 2016 Mikkel F. JΓΈrgensen, dvide.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 */ /* This file is part of an implementation of the "grisu3" double to string conversion algorithm described in the research paper "Printing Floating-Point Numbers Quickly And Accurately with Integers" by Florian Loitsch, available at http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf */ #include // uint64_t etc. #include // assert #include // ceil #include // sprintf #include #ifdef _MSC_VER #pragma warning(disable : 4204) // nonstandard extension used : non-constant aggregate initializer #endif #define D64_SIGN 0x8000000000000000ULL #define D64_EXP_MASK 0x7FF0000000000000ULL #define D64_FRACT_MASK 0x000FFFFFFFFFFFFFULL #define D64_IMPLICIT_ONE 0x0010000000000000ULL #define D64_EXP_POS 52 #define D64_EXP_BIAS 1075 #define DIYFP_FRACT_SIZE 64 #define D_1_LOG2_10 0.30102999566398114 // 1 / lg(10) #define MIN_TARGET_EXP -60 #define MASK32 0xFFFFFFFFULL #define CAST_U64(d) (*(uint64_t*)&d) #define MIN(x,y) ((x) <= (y) ? (x) : (y)) #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #define MIN_CACHED_EXP -348 #define CACHED_EXP_STEP 8 typedef struct diy_fp { uint64_t f; int e; } diy_fp; typedef struct power { uint64_t fract; int16_t b_exp, d_exp; } power; static const power pow_cache[] = { { 0xfa8fd5a0081c0288ULL, -1220, -348 }, { 0xbaaee17fa23ebf76ULL, -1193, -340 }, { 0x8b16fb203055ac76ULL, -1166, -332 }, { 0xcf42894a5dce35eaULL, -1140, -324 }, { 0x9a6bb0aa55653b2dULL, -1113, -316 }, { 0xe61acf033d1a45dfULL, -1087, -308 }, { 0xab70fe17c79ac6caULL, -1060, -300 }, { 0xff77b1fcbebcdc4fULL, -1034, -292 }, { 0xbe5691ef416bd60cULL, -1007, -284 }, { 0x8dd01fad907ffc3cULL, -980, -276 }, { 0xd3515c2831559a83ULL, -954, -268 }, { 0x9d71ac8fada6c9b5ULL, -927, -260 }, { 0xea9c227723ee8bcbULL, -901, -252 }, { 0xaecc49914078536dULL, -874, -244 }, { 0x823c12795db6ce57ULL, -847, -236 }, { 0xc21094364dfb5637ULL, -821, -228 }, { 0x9096ea6f3848984fULL, -794, -220 }, { 0xd77485cb25823ac7ULL, -768, -212 }, { 0xa086cfcd97bf97f4ULL, -741, -204 }, { 0xef340a98172aace5ULL, -715, -196 }, { 0xb23867fb2a35b28eULL, -688, -188 }, { 0x84c8d4dfd2c63f3bULL, -661, -180 }, { 0xc5dd44271ad3cdbaULL, -635, -172 }, { 0x936b9fcebb25c996ULL, -608, -164 }, { 0xdbac6c247d62a584ULL, -582, -156 }, { 0xa3ab66580d5fdaf6ULL, -555, -148 }, { 0xf3e2f893dec3f126ULL, -529, -140 }, { 0xb5b5ada8aaff80b8ULL, -502, -132 }, { 0x87625f056c7c4a8bULL, -475, -124 }, { 0xc9bcff6034c13053ULL, -449, -116 }, { 0x964e858c91ba2655ULL, -422, -108 }, { 0xdff9772470297ebdULL, -396, -100 }, { 0xa6dfbd9fb8e5b88fULL, -369, -92 }, { 0xf8a95fcf88747d94ULL, -343, -84 }, { 0xb94470938fa89bcfULL, -316, -76 }, { 0x8a08f0f8bf0f156bULL, -289, -68 }, { 0xcdb02555653131b6ULL, -263, -60 }, { 0x993fe2c6d07b7facULL, -236, -52 }, { 0xe45c10c42a2b3b06ULL, -210, -44 }, { 0xaa242499697392d3ULL, -183, -36 }, { 0xfd87b5f28300ca0eULL, -157, -28 }, { 0xbce5086492111aebULL, -130, -20 }, { 0x8cbccc096f5088ccULL, -103, -12 }, { 0xd1b71758e219652cULL, -77, -4 }, { 0x9c40000000000000ULL, -50, 4 }, { 0xe8d4a51000000000ULL, -24, 12 }, { 0xad78ebc5ac620000ULL, 3, 20 }, { 0x813f3978f8940984ULL, 30, 28 }, { 0xc097ce7bc90715b3ULL, 56, 36 }, { 0x8f7e32ce7bea5c70ULL, 83, 44 }, { 0xd5d238a4abe98068ULL, 109, 52 }, { 0x9f4f2726179a2245ULL, 136, 60 }, { 0xed63a231d4c4fb27ULL, 162, 68 }, { 0xb0de65388cc8ada8ULL, 189, 76 }, { 0x83c7088e1aab65dbULL, 216, 84 }, { 0xc45d1df942711d9aULL, 242, 92 }, { 0x924d692ca61be758ULL, 269, 100 }, { 0xda01ee641a708deaULL, 295, 108 }, { 0xa26da3999aef774aULL, 322, 116 }, { 0xf209787bb47d6b85ULL, 348, 124 }, { 0xb454e4a179dd1877ULL, 375, 132 }, { 0x865b86925b9bc5c2ULL, 402, 140 }, { 0xc83553c5c8965d3dULL, 428, 148 }, { 0x952ab45cfa97a0b3ULL, 455, 156 }, { 0xde469fbd99a05fe3ULL, 481, 164 }, { 0xa59bc234db398c25ULL, 508, 172 }, { 0xf6c69a72a3989f5cULL, 534, 180 }, { 0xb7dcbf5354e9beceULL, 561, 188 }, { 0x88fcf317f22241e2ULL, 588, 196 }, { 0xcc20ce9bd35c78a5ULL, 614, 204 }, { 0x98165af37b2153dfULL, 641, 212 }, { 0xe2a0b5dc971f303aULL, 667, 220 }, { 0xa8d9d1535ce3b396ULL, 694, 228 }, { 0xfb9b7cd9a4a7443cULL, 720, 236 }, { 0xbb764c4ca7a44410ULL, 747, 244 }, { 0x8bab8eefb6409c1aULL, 774, 252 }, { 0xd01fef10a657842cULL, 800, 260 }, { 0x9b10a4e5e9913129ULL, 827, 268 }, { 0xe7109bfba19c0c9dULL, 853, 276 }, { 0xac2820d9623bf429ULL, 880, 284 }, { 0x80444b5e7aa7cf85ULL, 907, 292 }, { 0xbf21e44003acdd2dULL, 933, 300 }, { 0x8e679c2f5e44ff8fULL, 960, 308 }, { 0xd433179d9c8cb841ULL, 986, 316 }, { 0x9e19db92b4e31ba9ULL, 1013, 324 }, { 0xeb96bf6ebadf77d9ULL, 1039, 332 }, { 0xaf87023b9bf0ee6bULL, 1066, 340 } }; static int cached_pow(int exp, diy_fp *p) { int k = (int)ceil((exp+DIYFP_FRACT_SIZE-1) * D_1_LOG2_10); int i = (k-MIN_CACHED_EXP-1) / CACHED_EXP_STEP + 1; p->f = pow_cache[i].fract; p->e = pow_cache[i].b_exp; return pow_cache[i].d_exp; } static diy_fp minus(diy_fp x, diy_fp y) { diy_fp d; d.f = x.f - y.f; d.e = x.e; assert(x.e == y.e && x.f >= y.f); return d; } static diy_fp multiply(diy_fp x, diy_fp y) { uint64_t a, b, c, d, ac, bc, ad, bd, tmp; diy_fp r; a = x.f >> 32; b = x.f & MASK32; c = y.f >> 32; d = y.f & MASK32; ac = a*c; bc = b*c; ad = a*d; bd = b*d; tmp = (bd >> 32) + (ad & MASK32) + (bc & MASK32); tmp += 1U << 31; // round r.f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); r.e = x.e + y.e + 64; return r; } static diy_fp normalize_diy_fp(diy_fp n) { assert(n.f != 0); while(!(n.f & 0xFFC0000000000000ULL)) { n.f <<= 10; n.e -= 10; } while(!(n.f & D64_SIGN)) { n.f <<= 1; --n.e; } return n; } static diy_fp double2diy_fp(double d) { diy_fp fp; uint64_t u64 = CAST_U64(d); if (!(u64 & D64_EXP_MASK)) { fp.f = u64 & D64_FRACT_MASK; fp.e = 1 - D64_EXP_BIAS; } else { fp.f = (u64 & D64_FRACT_MASK) + D64_IMPLICIT_ONE; fp.e = (int)((u64 & D64_EXP_MASK) >> D64_EXP_POS) - D64_EXP_BIAS; } return fp; } // pow10_cache[i] = 10^(i-1) static const unsigned int pow10_cache[] = { 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; static int largest_pow10(uint32_t n, int n_bits, uint32_t *power) { int guess = ((n_bits + 1) * 1233 >> 12) + 1/*skip first entry*/; if (n < pow10_cache[guess]) --guess; // We don't have any guarantees that 2^n_bits <= n. *power = pow10_cache[guess]; return guess; } static int round_weed(char *buffer, int len, uint64_t wp_W, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t ulp) { uint64_t wp_Wup = wp_W - ulp; uint64_t wp_Wdown = wp_W + ulp; while(rest < wp_Wup && delta - rest >= ten_kappa && (rest + ten_kappa < wp_Wup || wp_Wup - rest >= rest + ten_kappa - wp_Wup)) { --buffer[len-1]; rest += ten_kappa; } if (rest < wp_Wdown && delta - rest >= ten_kappa && (rest + ten_kappa < wp_Wdown || wp_Wdown - rest > rest + ten_kappa - wp_Wdown)) return 0; return 2*ulp <= rest && rest <= delta - 4*ulp; } static int digit_gen(diy_fp low, diy_fp w, diy_fp high, char *buffer, int *length, int *kappa) { uint64_t unit = 1; diy_fp too_low = { low.f - unit, low.e }; diy_fp too_high = { high.f + unit, high.e }; diy_fp unsafe_interval = minus(too_high, too_low); diy_fp one = { 1ULL << -w.e, w.e }; uint32_t p1 = (uint32_t)(too_high.f >> -one.e); uint64_t p2 = too_high.f & (one.f - 1); uint32_t div; *kappa = largest_pow10(p1, DIYFP_FRACT_SIZE + one.e, &div); *length = 0; while(*kappa > 0) { uint64_t rest; int digit = p1 / div; buffer[*length] = (char)('0' + digit); ++*length; p1 %= div; --*kappa; rest = ((uint64_t)p1 << -one.e) + p2; if (rest < unsafe_interval.f) return round_weed(buffer, *length, minus(too_high, w).f, unsafe_interval.f, rest, (uint64_t)div << -one.e, unit); div /= 10; } for(;;) { int digit; p2 *= 10; unit *= 10; unsafe_interval.f *= 10; // Integer division by one. digit = (int)(p2 >> -one.e); buffer[*length] = (char)('0' + digit); ++*length; p2 &= one.f - 1; // Modulo by one. --*kappa; if (p2 < unsafe_interval.f) return round_weed(buffer, *length, minus(too_high, w).f * unit, unsafe_interval.f, p2, one.f, unit); } } static int grisu3(double v, char *buffer, int *length, int *d_exp) { int mk, kappa, success; diy_fp dfp = double2diy_fp(v); diy_fp w = normalize_diy_fp(dfp); // normalize boundaries diy_fp t = { (dfp.f << 1) + 1, dfp.e - 1 }; diy_fp b_plus = normalize_diy_fp(t); diy_fp b_minus; diy_fp c_mk; // Cached power of ten: 10^-k uint64_t u64 = CAST_U64(v); assert(v > 0 && v <= 1.7976931348623157e308); // Grisu only handles strictly positive finite numbers. if (!(u64 & D64_FRACT_MASK) && (u64 & D64_EXP_MASK) != 0) { b_minus.f = (dfp.f << 2) - 1; b_minus.e = dfp.e - 2;} // lower boundary is closer? else { b_minus.f = (dfp.f << 1) - 1; b_minus.e = dfp.e - 1; } b_minus.f = b_minus.f << (b_minus.e - b_plus.e); b_minus.e = b_plus.e; mk = cached_pow(MIN_TARGET_EXP - DIYFP_FRACT_SIZE - w.e, &c_mk); w = multiply(w, c_mk); b_minus = multiply(b_minus, c_mk); b_plus = multiply(b_plus, c_mk); success = digit_gen(b_minus, w, b_plus, buffer, length, &kappa); *d_exp = kappa - mk; return success; } static int i_to_str(int val, char *str) { int len, i; char *s; char *begin = str; if (val < 0) { *str++ = '-'; val = -val; } s = str; for(;;) { int ni = val / 10; int digit = val - ni*10; *s++ = (char)('0' + digit); if (ni == 0) break; val = ni; } *s = '\0'; len = (int)(s - str); for(i = 0; i < len/2; ++i) { char ch = str[i]; str[i] = str[len-1-i]; str[len-1-i] = ch; } return (int)(s - begin); } int dtoa_grisu3(double v, char *dst) { int d_exp, len, success, decimals, i; uint64_t u64 = CAST_U64(v); char *s2 = dst; assert(dst); // Prehandle NaNs if ((u64 << 1) > 0xFFE0000000000000ULL) return sprintf(dst, "NaN(%08X%08X)", (uint32_t)(u64 >> 32), (uint32_t)u64); // Prehandle negative values. if ((u64 & D64_SIGN) != 0) { *s2++ = '-'; v = -v; u64 ^= D64_SIGN; } // Prehandle zero. if (!u64) { *s2++ = '0'; *s2 = '\0'; return (int)(s2 - dst); } // Prehandle infinity. if (u64 == D64_EXP_MASK) { *s2++ = 'i'; *s2++ = 'n'; *s2++ = 'f'; *s2 = '\0'; return (int)(s2 - dst); } success = grisu3(v, s2, &len, &d_exp); // If grisu3 was not able to convert the number to a string, then use old sprintf (suboptimal). if (!success) return sprintf(s2, "%.17g", v) + (int)(s2 - dst); // handle whole numbers as integers if they are < 10^15 if (d_exp >= 0 && d_exp <= MAX(2, 15 - len)) { while(d_exp-- > 0) s2[len++] = '0'; s2[len] = '\0'; return (int)(s2+len-dst); } // We now have an integer string of form "151324135" and a base-10 exponent for that number. // Next, decide the best presentation for that string by whether to use a decimal point, or the scientific exponent notation 'e'. // We don't pick the absolute shortest representation, but pick a balance between readability and shortness, e.g. // 1.545056189557677e-308 could be represented in a shorter form // 1545056189557677e-323 but that would be somewhat unreadable. decimals = MIN(-d_exp, MAX(1, len-1)); // mikkelfj: // fix zero prefix .1 => 0.1, important for JSON export. // prefer unscientific notation at same length: // -1.2345e-4 over -1.00012345, // -1.0012345 over -1.2345e-3 if (d_exp < 0 && (len + d_exp) > -3 && len <= -d_exp) { // mikkelfj: fix zero prefix .1 => 0.1, and short exponents 1.3e-2 => 0.013. memmove(s2 + 2 - d_exp - len, s2, len); s2[0] = '0'; s2[1] = '.'; for (i = 2; i < 2-d_exp-len; ++i) s2[i] = '0'; len += i; } else if (d_exp < 0 && len > 1) // Add decimal point? { for(i = 0; i < decimals; ++i) s2[len-i] = s2[len-i-1]; s2[len++ - decimals] = '.'; d_exp += decimals; // Need scientific notation as well? if (d_exp != 0) { s2[len++] = 'e'; len += i_to_str(d_exp, s2+len); } }// Add scientific notation? else if (d_exp < 0 || d_exp > 2) { s2[len++] = 'e'; len += i_to_str(d_exp, s2+len); } // Add zeroes instead of scientific notation? /*s2[len] = '\0'; // grisu3 doesn't null terminate, so ensure termination.*/ return (int)(s2+len-dst); } vroom/src/Makevars0000644000176200001440000000012414142515724013701 0ustar liggesusersPKG_CPPFLAGS=-Imio/include -DWIN32_LEAN_AND_MEAN -Ispdlog/include -DFMT_HEADER_ONLY vroom/src/vroom_big_int.h0000644000176200001440000000561714142515724015227 0ustar liggesusers#pragma once #include #include "altrep.h" constexpr long long NA_INTEGER64 = 0x8000000000000000LL; #include "vroom.h" namespace cpp11 { inline bool is_na(long long x) { return x == NA_INTEGER64; } } // namespace cpp11 namespace vroom { template <> inline long long na() { return NA_INTEGER64; } } // namespace vroom #include "vroom_vec.h" long long vroom_strtoll(const char* begin, const char* end); cpp11::doubles read_big_int(vroom_vec_info* info); union vroom_big_int_t { long long ll; double dbl; }; #ifdef HAS_ALTREP class vroom_big_int : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"integer64"}; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_big_int (len=%d, materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTREAL methods ----------------- static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_big_int(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } // the element at the index `i` static double real_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = vroom_vec::Info(vec); vroom_big_int_t res; res.ll = parse_value( i, info.column, vroom_strtoll, info.errors, "a big integer", *info.na); info.errors->warn_for_errors(); return res.dbl; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_big_int::class_t = R_make_altreal_class("vroom_big_int", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, real_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_big_int(DllInfo* dll); vroom/src/delimited_index_connection.cc0000644000176200001440000001540314142515724020070 0ustar liggesusers#include #include "delimited_index_connection.h" #include "connection.h" #include #include // std::async, std::future #include #include "r_utils.h" #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif #include "unicode_fopen.h" using namespace vroom; delimited_index_connection::delimited_index_connection( SEXP in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr errors, const size_t chunk_size, const bool progress) { has_header_ = has_header; quote_ = quote; trim_ws_ = trim_ws; escape_double_ = escape_double; escape_backslash_ = escape_backslash; comment_ = comment; skip_ = skip; progress_ = progress; filename_ = cpp11::as_cpp(cpp11::package("vroom")["vroom_tempfile"]()); std::FILE* out = unicode_fopen(filename_.c_str(), "wb"); auto con = R_GetConnection(in); bool should_open = !is_open(in); if (should_open) { cpp11::package("base")["open"](in, "rb"); } /* raw connections are always created as open, but we should close them */ bool should_close = should_open || Rf_inherits(in, "rawConnection"); std::array, 2> buf = { std::vector(chunk_size), std::vector(chunk_size)}; // std::vector(chunk_size)}; // A buf index that alternates between 0,1 auto i = 0; idx_ = std::vector(2); idx_[0].reserve(128); size_t sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); buf[i].resize(sz + 1); if (sz == 0) { std::fclose(out); if (should_close) { cpp11::package("base")["close"](in); } return; } bool has_quoted_newlines = quote != '\0'; // Parse header size_t start = find_first_line( buf[i], skip_, comment_, skip_empty_rows, has_quoted_newlines, quote); if (delim == nullptr) { delim_ = std::string(1, guess_delim(buf[i], start, 20, sz, quote)); } else { delim_ = delim; } delim_len_ = delim_.length(); size_t first_nl; newline_type nl; std::tie(first_nl, nl) = find_next_newline( buf[i], start, comment, skip_empty_rows, has_quoted_newlines, quote); bool single_line = first_nl == buf[i].size() - 1; if (sz > 1 && !has_expected_line_ending(nl, buf[i][first_nl])) { // This first newline must not have fit in the buffer, throw error // suggesting a larger buffer size. // Try reading again, if size is 0 we are at the end of the file, so should // just go on. size_t next_sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); if (!(next_sz == 0)) { if (should_close) { cpp11::package("base")["close"](in); } std::stringstream ss; ss << "The size of the connection buffer (" << chunk_size << ") was not large enough\nto fit a complete line:\n * Increase it " "by " "setting `Sys.setenv(\"VROOM_CONNECTION_SIZE\")`"; cpp11::stop("%s", ss.str().c_str()); } } std::unique_ptr pb = nullptr; if (progress_) { pb = std::unique_ptr( new RProgress::RProgress(get_pb_format("connection"), 1e12)); pb->tick(start); } bool n_max_set = n_max != static_cast(-1); n_max = n_max_set ? n_max + has_header_ : n_max; std::unique_ptr empty_pb = nullptr; // Index the first row size_t cols = 0; csv_state state = RECORD_START; size_t lines_read = index_region( buf[i], idx_[0], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, start, first_nl + 1, 0, n_max, cols, 0, errors, empty_pb, 1, -1); columns_ = idx_[0].size() - 1; SPDLOG_DEBUG( "first_line_columns: {0} first_nl_loc: {1} size: {2}", columns_, first_nl, sz); size_t total_read = 0; std::future parse_fut; std::future write_fut; // We don't actually want any progress bar, so just pass a dummy one. while (sz > 0) { if (parse_fut.valid()) { parse_fut.wait(); } n_max = n_max > lines_read ? n_max - lines_read : 0; if (n_max > 0) { parse_fut = std::async([&, i, sz, first_nl, total_read] { lines_read = index_region( buf[i], idx_[1], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, first_nl + 1, sz, total_read, n_max, cols, columns_, errors, empty_pb, 1, -1); }); } if (write_fut.valid()) { write_fut.wait(); if (n_max == 0) { break; } } write_fut = std::async( [&, i, sz] { std::fwrite(buf[i].data(), sizeof(char), sz, out); }); if (progress_) { pb->tick(sz); } total_read += sz; i = (i + 1) % 2; sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); if (sz > 0) { buf[i].resize(sz + 1); buf[i][sz] = '\0'; } first_nl = -1; // SPDLOG_DEBUG("first_nl_loc: {0} size: {1}", first_nl, sz); } if (parse_fut.valid()) { parse_fut.wait(); } if (write_fut.valid()) { write_fut.wait(); } std::fclose(out); if (progress_) { pb->update(1); } /* raw connections are always created as open, but we should close them */ if (should_close) { cpp11::package("base")["close"](in); } std::error_code error; mmap_ = make_mmap_source(filename_.c_str(), error); if (error) { cpp11::stop("%s", error.message().c_str()); } size_t file_size = mmap_.size(); if (!has_expected_line_ending(nl, mmap_[file_size - 1])) { if (columns_ == 0 || single_line) { idx_[0].push_back(file_size); ++columns_; } else { idx_[1].push_back(file_size); } } size_t total_size = std::accumulate( idx_.begin(), idx_.end(), std::size_t{0}, [](size_t sum, const idx_t& v) { sum += v.size() > 0 ? v.size() : 0; return sum; }); rows_ = columns_ > 0 ? total_size / (columns_ + 1) : 0; if (rows_ > 0 && has_header_) { --rows_; } #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt( "basic_logger", "logs/index_connection.idx", true); for (auto& i : idx_) { for (auto& v : i) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&i); } spdlog::drop("basic_logger"); #endif SPDLOG_DEBUG("columns: {0} rows: {1}", columns_, rows_); } vroom/src/vroom_lgl.h0000644000176200001440000000357514142515724014373 0ustar liggesusers#pragma once #include #include "parallel.h" #include "vroom_vec.h" const static char* const true_values[] = { "T", "t", "True", "TRUE", "true", (char*)NULL}; const static char* const false_values[] = { "F", "f", "False", "FALSE", "false", (char*)NULL}; inline bool isTrue(const char* start, const char* end) { size_t len = end - start; for (int i = 0; true_values[i]; i++) { size_t true_len = strlen(true_values[i]); if (true_len == len && strncmp(start, true_values[i], len) == 0) { return true; } } return false; } inline bool isFalse(const char* start, const char* end) { size_t len = end - start; for (int i = 0; false_values[i]; i++) { if (strlen(false_values[i]) == len && strncmp(start, false_values[i], len) == 0) { return true; } } return false; } inline int parse_logical(const char* start, const char* end, bool strict = true) { auto len = end - start; if (isTrue(start, end) || (!strict && len == 1 && *start == '1')) { return TRUE; } if (isFalse(start, end) || (!strict && len == 1 && *start == '0')) { return FALSE; } return NA_LOGICAL; } inline cpp11::logicals read_lgl(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::logicals out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> int { return parse_logical(begin, end, false); }, info->errors, "1/0/T/F/TRUE/FALSE", *info->na); } }, info->num_threads); info->errors->warn_for_errors(); return out; } vroom/src/fixed_width_index_connection.cc0000644000176200001440000001043514142515724020426 0ustar liggesusers#include #include #include "connection.h" #include "fixed_width_index_connection.h" #include "r_utils.h" #include "unicode_fopen.h" #include #include #include // std::async, std::future #include #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif using namespace vroom; fixed_width_index_connection::fixed_width_index_connection( SEXP in, std::vector col_starts, std::vector col_ends, bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress, const size_t chunk_size) { col_starts_ = std::move(col_starts); col_ends_ = std::move(col_ends); trim_ws_ = trim_ws; filename_ = cpp11::as_cpp(cpp11::package("vroom")["vroom_tempfile"]()); std::FILE* out = unicode_fopen(filename_.c_str(), "wb"); auto con = R_GetConnection(in); bool should_open = !is_open(in); if (should_open) { cpp11::package("base")["open"](in, "rb"); } std::array, 2> buf = { std::vector(chunk_size), std::vector(chunk_size)}; // A buf index that alternates between 0,1 auto i = 0; newlines_.reserve(128); size_t sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); buf[i][sz] = '\0'; // Parse header size_t start = find_first_line( buf[i], skip, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0'); // Check for windows newlines size_t first_nl; newline_type nl_type; std::tie(first_nl, nl_type) = find_next_newline( buf[i], start, comment, skip_empty_rows, false, /* quote */ '\0'); bool n_max_set = n_max != static_cast(-1); std::unique_ptr pb = nullptr; if (progress) { pb = std::unique_ptr( new RProgress::RProgress(get_pb_format("connection"), 1e12)); pb->tick(start); } size_t total_read = 0; std::future parse_fut; std::future write_fut; size_t lines_read = 0; size_t lines_remaining = n_max; std::unique_ptr empty_pb = nullptr; if (n_max > 0) { newlines_.push_back(start - 1); } while (sz > 0) { if (parse_fut.valid()) { parse_fut.wait(); } if (lines_read >= lines_remaining) { break; } lines_remaining -= lines_read; parse_fut = std::async([&, i, start, total_read, sz] { lines_read = index_region( buf[i], newlines_, start, sz, total_read, comment, skip_empty_rows, lines_remaining, empty_pb); }); if (write_fut.valid()) { write_fut.wait(); } write_fut = std::async( [&, i, sz] { std::fwrite(buf[i].data(), sizeof(char), sz, out); }); if (progress) { pb->tick(sz); } total_read += sz; i = (i + 1) % 2; sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); if (sz > 0) { buf[i][sz] = '\0'; } start = 0; SPDLOG_DEBUG("first_nl_loc: {0} size: {1}", start, sz); } if (parse_fut.valid()) { parse_fut.wait(); } if (write_fut.valid()) { write_fut.wait(); } std::fclose(out); if (progress) { pb->update(1); } /* raw connections are always created as open, but we should close them */ bool should_close = should_open || Rf_inherits(in, "rawConnection"); if (should_close) { cpp11::package("base")["close"](in); } std::error_code error; if (n_max != 0) { mmap_ = make_mmap_source(filename_.c_str(), error); if (error) { cpp11::stop("%s", error.message().c_str()); } } char last_char = mmap_[mmap_.size() - 1]; bool ends_with_newline = last_char == '\n' || last_char == '\r'; if (!n_max_set && !ends_with_newline) { newlines_.push_back(mmap_.size()); } #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt( "basic_logger", "logs/fixed_width_index_connection.idx", true); log->set_level(spdlog::level::debug); for (auto&& v : newlines_) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&newlines_); spdlog::drop("basic_logger"); #endif } vroom/src/vroom_big_int.cc0000644000176200001440000000305314142512075015351 0ustar liggesusers#include "vroom_big_int.h" #include "parallel.h" #include // A version of strtoll that doesn't need null terminated strings, to avoid // needing to copy the data long long vroom_strtoll(const char* begin, const char* end) { unsigned long long val = 0; bool is_neg = false; if (begin == end) { return NA_INTEGER64; } if (begin != end && *begin == '-') { is_neg = true; ++begin; } while (begin != end && isdigit(*begin)) { val = val * 10 + ((*begin++) - '0'); } if (val > LLONG_MAX) { return NA_INTEGER64; } // If there is more than digits, return NA if (begin != end) { return NA_INTEGER64; } return is_neg ? -val : val; } // Normal reading of integer vectors cpp11::doubles read_big_int(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { vroom_big_int_t res; res.ll = parse_value( b, col, vroom_strtoll, info->errors, "a big integer", *info->na); out[i++] = res.dbl; } }, info->num_threads); info->errors->warn_for_errors(); out.attr("class") = {"integer64"}; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_big_int::class_t; void init_vroom_big_int(DllInfo* dll) { vroom_big_int::Init(dll); } #else void init_vroom_big_int(DllInfo* dll) {} #endif vroom/src/vroom_errors.h0000644000176200001440000000603314142515724015121 0ustar liggesusers#pragma once #include "index.h" #include #include #include #include #include #include using namespace cpp11::literals; class vroom_errors { struct parse_error { size_t position; size_t column; parse_error(size_t pos, size_t col) : position(pos), column(col) {} }; public: vroom_errors() {} void add_error( size_t row, size_t column, std::string expected = "", std::string actual = "", std::string filename = "") { std::lock_guard guard(mutex_); rows_.push_back(row + 1); columns_.push_back(column + 1); expected_.emplace_back(expected); actual_.emplace_back(actual); filenames_.emplace_back(filename); } void add_parse_error(size_t position, size_t column) { std::lock_guard guard(mutex_); parse_errors_.emplace_back(position, column); } void resolve_parse_errors(const vroom::index& idx) { if (parse_errors_.size() == 0) { return; } // Sort the parse errors by their position std::sort( parse_errors_.begin(), parse_errors_.end(), [](const parse_error& lhs, const parse_error& rhs) { return lhs.position < rhs.position; }); auto row = idx.get_column(0)->begin(); auto row_end = idx.get_column(0)->end(); for (const auto& e : parse_errors_) { while (row != row_end && e.position > row.position()) { ++row; } std::stringstream ss_expected, ss_actual; ss_expected << idx.num_columns() << " columns"; ss_actual << e.column + 1 << " columns"; add_error( row.index() - 1, e.column, ss_expected.str(), ss_actual.str(), row.filename()); } } cpp11::data_frame error_table() const { return cpp11::writable::data_frame( {"row"_nm = rows_, "col"_nm = columns_, "expected"_nm = expected_, "actual"_nm = actual_, "file"_nm = filenames_}); } bool has_errors() const { return rows_.size() > 0; } void warn_for_errors() const { if (!have_warned_ && rows_.size() > 0) { have_warned_ = true; static auto warn = Rf_findFun( Rf_install("warn"), Rf_findVarInFrame(R_NamespaceRegistry, Rf_install("rlang"))); cpp11::sexp warn_call = Rf_lang3( warn, Rf_mkString( "One or more parsing issues, see `problems()` for details"), Rf_mkString("vroom_parse_issue")); Rf_eval(warn_call, R_EmptyEnv); } } void clear() { std::lock_guard guard(mutex_); rows_.clear(); columns_.clear(); expected_.clear(); actual_.clear(); filenames_.clear(); parse_errors_.clear(); } private: mutable bool have_warned_ = false; std::mutex mutex_; std::vector filenames_; std::vector parse_errors_; std::vector rows_; std::vector columns_; std::vector expected_; std::vector actual_; }; vroom/src/vroom_int.cc0000644000176200001440000000255714142512075014540 0ustar liggesusers#include "vroom_int.h" #include "parallel.h" // A version of strtoi that doesn't need null terminated strings, to avoid // needing to copy the data int strtoi(const char* begin, const char* end) { double val = 0; bool is_neg = false; if (begin == end) { return NA_INTEGER; } if (begin != end && *begin == '-') { is_neg = true; ++begin; } while (begin != end && isdigit(*begin)) { val = val * 10 + ((*begin++) - '0'); } // If there is more than digits, return NA if (begin != end) { return NA_INTEGER; } if (val > INT_MAX) { return NA_INTEGER; } return is_neg ? -val : val; } // Normal reading of integer vectors cpp11::integers read_int(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::integers out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, strtoi, info->errors, "an integer", *info->na); } }, info->num_threads); info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_int::class_t; void init_vroom_int(DllInfo* dll) { vroom_int::Init(dll); } #else void init_vroom_int(DllInfo* dll) {} #endif vroom/src/iterator.h0000644000176200001440000000440614143524032014207 0ustar liggesusers#pragma once #include "vroom.h" #include namespace vroom { class base_iterator { public: virtual void next() = 0; virtual void advance(ptrdiff_t n) = 0; virtual bool equal_to(const base_iterator& it) const = 0; virtual ptrdiff_t distance_to(const base_iterator& it) const = 0; virtual string value() const = 0; virtual base_iterator* clone() const = 0; virtual string at(ptrdiff_t n) const = 0; virtual ~base_iterator() {} virtual std::string filename() const = 0; virtual size_t index() const = 0; virtual size_t position() const = 0; }; class iterator { base_iterator* it_; public: using iterator_category = std::forward_iterator_tag; using value_type = string; using pointer = string*; using reference = string&; iterator() : it_(nullptr){}; iterator(base_iterator* it) : it_(it) {} iterator& operator=(const iterator& other) { base_iterator* original = it_; it_ = other.it_->clone(); delete original; return *this; } iterator(const iterator& other) : it_(other.it_->clone()) {} iterator operator++(int) { /* postfix */ iterator copy(*this); it_->next(); return copy; } iterator& operator++() /* prefix */ { it_->next(); return *this; } bool operator!=(const iterator& other) const { return !it_->equal_to(*other.it_); } bool operator==(const iterator& other) const { return it_->equal_to(*other.it_); } string operator*() const { return it_->value(); } iterator& operator+=(ptrdiff_t n) { it_->advance(n); return *this; } iterator& operator-=(ptrdiff_t n) { it_->advance(-n); return *this; } iterator operator+(ptrdiff_t n) const { iterator copy(*this); copy.it_->advance(n); return copy; } iterator operator-(ptrdiff_t n) const { iterator copy(*this); copy.it_->advance(-n); return copy; } ptrdiff_t operator-(const iterator& other) const { return -it_->distance_to(*other.it_); } string operator[](ptrdiff_t n) const { return it_->at(n); } std::string filename() const { return it_->filename(); } size_t index() const { return it_->index(); } size_t position() const { return it_->position(); } ~iterator() { if (it_ != nullptr) { delete it_; } } }; } // namespace vroom vroom/src/vroom_chr.cc0000644000176200001440000000223014142515724014512 0ustar liggesusers#include "vroom_chr.h" SEXP check_na(SEXP na, SEXP val) { for (R_xlen_t i = 0; i < Rf_xlength(na); ++i) { SEXP v = STRING_ELT(na, i); // We can just compare the addresses directly because they should now // both be in the global string cache. if (v == val) { return NA_STRING; } } return val; } cpp11::strings read_chr(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::strings out(n); SEXP nas = *info->na; cpp11::unwind_protect([&] { auto i = 0; auto col = info->column; for (auto b = col->begin(), e = col->end(); b != e; ++b) { auto str = *b; auto val = info->locale->encoder_.makeSEXP(str.begin(), str.end(), true); if (Rf_xlength(val) < str.end() - str.begin()) { info->errors->add_error( b.index(), col->get_index(), "", "embedded null", b.filename()); } SET_STRING_ELT(out, i++, check_na(nas, val)); } }); info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_chr::class_t; void init_vroom_chr(DllInfo* dll) { vroom_chr::Init(dll); } #else void init_vroom_chr(DllInfo* dll) {} #endif vroom/src/vroom_errors.cpp0000644000176200001440000000035514142512075015451 0ustar liggesusers#include "vroom_errors.h" #include #include [[cpp11::register]] cpp11::data_frame vroom_errors_(cpp11::external_pointer> errors) { return (*errors)->error_table(); } vroom/src/delimited_index.cc0000644000176200001440000002371514142515724015656 0ustar liggesusers#include "delimited_index.h" #include "parallel.h" #include "multi_progress.h" #include #include #include #include #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif #ifndef VROOM_STANDALONE #include "r_utils.h" #endif #include "unicode_fopen.h" using namespace vroom; delimited_index::delimited_index( const char* filename, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, size_t n_max, const char* comment, const bool skip_empty_rows, std::shared_ptr errors, size_t num_threads, bool progress, const bool use_threads) : filename_(filename), has_header_(has_header), quote_(quote), trim_ws_(trim_ws), escape_double_(escape_double), escape_backslash_(escape_backslash), skip_(skip), comment_(comment), rows_(0), columns_(0), progress_(progress), delim_len_(0) { std::error_code error; mmap_ = make_mmap_source(filename, error); if (error) { // We cannot actually portably compare error messages due to a bug in // libstdc++ (https://stackoverflow.com/a/54316671/2055486), so just print // the message on stderr return #ifndef VROOM_STANDALONE REprintf("mapping error: %s\n", error.message().c_str()); #else std::cerr << "mapping error: " << error.message() << '\n'; #endif return; } size_t file_size = mmap_.cend() - mmap_.cbegin(); if (!(mmap_[file_size - 1] == '\n' || mmap_[file_size - 1] == '\r')) { #ifndef VROOM_STANDALONE REprintf("Files must end with a newline\n"); #else std::cerr << "Files must end with a newline\n"; #endif return; } bool has_quoted_newlines = quote != '\0'; size_t start = find_first_line( mmap_, skip_, comment_, skip_empty_rows, has_quoted_newlines, quote); // If an empty file, or a file with only a newline. if (start >= file_size - 1) { return; } if (delim == nullptr) { #ifndef VROOM_STANDALONE delim_ = std::string(1, guess_delim(mmap_, start, /* guess_max */ 20, 0, quote)); #else throw std::runtime_error("Must specify a delimiter"); #endif } else { delim_ = delim; } delim_len_ = delim_.length(); size_t first_nl, second_nl; newline_type nl; std::tie(first_nl, nl) = find_next_newline( mmap_, start, comment_, skip_empty_rows, has_quoted_newlines, quote); std::tie(second_nl, std::ignore) = find_next_newline( mmap_, first_nl + 1, comment, skip_empty_rows, has_quoted_newlines, quote); size_t one_row_size = second_nl - first_nl; size_t guessed_rows = one_row_size > 0 ? (file_size - first_nl) / (one_row_size * 1.1) : 0; std::unique_ptr pb = nullptr; if (progress_) { #ifndef VROOM_STANDALONE auto format = get_pb_format("file", filename); auto width = get_pb_width(format); pb = std::unique_ptr( new multi_progress(format, file_size, width)); pb->tick(start); #endif } bool nmax_set = n_max != static_cast(-1); if (nmax_set) { n_max = n_max + has_header_; num_threads = 1; } // We want at least 10 lines per batch, otherwise threads aren't really // useful size_t batch_size = file_size / num_threads; size_t line_size = second_nl - first_nl; if (batch_size < line_size * 10) { num_threads = 1; } start_indexing: try { idx_ = std::vector(num_threads + 1); // Index the first row size_t cols = 0; csv_state state = RECORD_START; size_t lines_read = index_region( mmap_, idx_[0], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, start, first_nl + 1, 0, n_max, cols, 0, errors, pb, num_threads, -1); if (idx_[0].size() > 0) { columns_ = idx_[0].size() - 1; } std::vector> threads; if (nmax_set) { threads.emplace_back(std::async(std::launch::async, [&] { n_max = n_max > lines_read ? n_max - lines_read : 0; index_region( mmap_, idx_[1], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, first_nl + 1, file_size, 0, n_max, cols, columns_, errors, pb, num_threads, file_size / 100); })); } else { threads = parallel_for( file_size - first_nl, [&](size_t start, size_t end, size_t id) { newline_type nl; idx_[id + 1].reserve((guessed_rows / num_threads) * columns_); std::tie(start, nl) = find_next_newline( mmap_, first_nl + start, comment, skip_empty_rows, /* has_quote */ false, quote); ++start; std::tie(end, std::ignore) = find_next_newline( mmap_, first_nl + end, comment, skip_empty_rows, /* has_quote */ false, quote); ++end; size_t cols = 0; csv_state state = RECORD_START; index_region( mmap_, idx_[id + 1], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, start, end, 0, n_max, cols, columns_, errors, pb, num_threads, file_size / 100); }, num_threads, use_threads, false); } if (progress_) { #ifndef VROOM_STANDALONE pb->display_progress(); #endif } for (auto& t : threads) { t.get(); } } catch (newline_error& e) { num_threads = 1; errors->clear(); goto start_indexing; } size_t total_size = std::accumulate( idx_.begin(), idx_.end(), std::size_t{0}, [](size_t sum, const idx_t& v) { sum += v.size() > 0 ? v.size() : 0; return sum; }); rows_ = columns_ > 0 ? total_size / (columns_ + 1) : 0; if (rows_ > 0 && has_header_) { --rows_; } // REprintf("columns_: %i rows_: %i\n", columns_, rows_); #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt("basic_logger", "logs/index.idx"); log->set_level(spdlog::level::debug); for (auto& i : idx_) { for (auto& v : i) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&i); } spdlog::drop("basic_logger"); #endif SPDLOG_DEBUG( "columns: {0} rows: {1} total_size: {2}", columns_, rows_, total_size); } void delimited_index::trim_quotes(const char*& begin, const char*& end) const { if (begin != end && (*begin == quote_)) { ++begin; } if (end != begin && *(end - 1) == quote_) { --end; } } const string delimited_index::get_escaped_string( const char* begin, const char* end, bool has_quote) const { if (end <= begin) { return {begin, begin}; } // If not escaping just return without a copy if (!((escape_double_ && has_quote) || escape_backslash_)) { return {begin, end}; } std::string out; bool needs_escaping = false; auto cur = begin; auto prev = begin; while (cur < end) { if ((escape_double_ && has_quote && *cur == quote_) || (escape_backslash_ && *cur == '\\')) { if (!needs_escaping) { out.reserve(end - begin); needs_escaping = true; } std::copy(prev, cur, std::back_inserter(out)); ++cur; prev = cur; } ++cur; } if (needs_escaping) { std::copy(prev, cur, std::back_inserter(out)); return out; } return {begin, end}; } inline std::pair delimited_index::get_cell(size_t i, bool is_first) const { auto oi = i; auto i_row = i / (columns_); auto i_col = i % (columns_); auto ni = i_row * (columns_ + 1) + i_col; i = ni; for (const auto& idx : idx_) { auto sz = idx.size(); if (i + 1 < sz) { auto start = idx[i]; auto end = idx[i + 1]; if (start == end) { return {start, end}; } // By relying on 0 and 1 being true and false we can remove a branch // here, which improves performance a bit, as this function is called a // lot. if (!is_first) { start = start + delim_len_; } // REprintf( //"oi: %i ni: %i i: %i start: %i end: %i\n", oi, ni, i, start, end); return {start, end}; } i -= sz; } std::stringstream ss; ss.imbue(std::locale("")); ss << "Failure to retrieve index " << std::fixed << oi << " / " << rows_; throw std::out_of_range(ss.str()); /* should never get here */ return {0, 0}; } const string delimited_index::get_trimmed_val(size_t i, bool is_first, bool is_last) const { size_t begin_p; size_t end_p; std::tie(begin_p, end_p) = get_cell(i, is_first); const char* begin = mmap_.data() + begin_p; const char* end = mmap_.data() + end_p; // Check for windows newlines if the last column */ if (is_last) { if (begin < end) { if (*(end - 1) == '\r') { --end; } } } if (trim_ws_) { trim_whitespace(begin, end); } bool has_quote = false; if (quote_ != '\0') { has_quote = *begin == quote_; if (has_quote) { trim_quotes(begin, end); } if (trim_ws_) { trim_whitespace(begin, end); } } return get_escaped_string(begin, end, has_quote); } string delimited_index::get(size_t row, size_t col) const { auto i = (row + has_header_) * columns_ + col; return get_trimmed_val(i, col == 0, col == (columns_ - 1)); } vroom/src/vroom.cc0000644000176200001440000000537014142515724013666 0ustar liggesusers#include #include #include #include #include #include "LocaleInfo.h" #include "columns.h" #include "connection.h" #include "index.h" #include "index_collection.h" #include "vroom_rle.h" #include #include #include #include #include "unicode_fopen.h" #include "vroom_errors.h" [[cpp11::register]] SEXP vroom_( const cpp11::list& inputs, SEXP delim, const char quote, bool trim_ws, bool escape_double, bool escape_backslash, const char* comment, const bool skip_empty_rows, size_t skip, ptrdiff_t n_max, bool progress, const cpp11::sexp& col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep) { bool has_header = TYPEOF(col_names) == LGLSXP && cpp11::logicals(col_names)[0]; std::vector filenames; bool add_filename = !Rf_isNull(id); // We need to retrieve filenames now before the connection objects are read, // as they are invalid afterwards. if (add_filename) { filenames = get_filenames(inputs); } auto errors = new std::shared_ptr(new vroom_errors()); auto idx = std::make_shared( inputs, Rf_isNull(delim) ? nullptr : cpp11::as_cpp(delim), quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, *errors, num_threads, progress); (*errors)->resolve_parse_errors(*idx); return create_columns( idx, col_names, std::move(col_types), std::move(col_select), std::move(name_repair), id, filenames, na, locale, altrep, guess_max, errors, num_threads); } [[cpp11::register]] bool has_trailing_newline(const cpp11::strings& filename) { std::FILE* f = unicode_fopen(CHAR(filename[0]), "rb"); if (!f) { return true; } std::setvbuf(f, nullptr, _IONBF, 0); fseek(f, -1, SEEK_END); char c = fgetc(f); fclose(f); return c == '\n'; } [[cpp11::register]] SEXP vroom_rle(const cpp11::integers& input) { #ifdef HAS_ALTREP return vroom_rle::Make(input); #else R_xlen_t total_size = std::accumulate(input.begin(), input.end(), 0); cpp11::writable::strings out(total_size); cpp11::strings nms = input.names(); R_xlen_t idx = 0; for (R_xlen_t i = 0; i < Rf_xlength(input); ++i) { for (R_xlen_t j = 0; j < input[i]; ++j) { SET_STRING_ELT(out, idx++, nms[i]); } } return out; #endif } vroom/src/multi_progress.h0000644000176200001440000000454014142515724015442 0ustar liggesusers#pragma once #include #include #ifdef VROOM_STANDALONE // A stub class that doesn't do anything namespace RProgress { class RProgress { public: RProgress( std::string format = "[:bar] :percent", double total = 100, int width = 80 - 2, const char* complete_char = "=", const char* middle_char = "=", const char* incomplete_char = "-", bool clear = true, double show_after = 0.2) {} void update(double) {} void tick(double) {} void set_reverse(bool) {} }; } // namespace RProgress #else #include #include "RProgress.h" #endif class multi_progress { public: multi_progress( std::string format = "[:bar] :percent", size_t total = 100, int width = 78, const char* complete_char = "=", const char* incomplete_char = "-", bool clear = true, double show_after = 0.2) : pb_(new RProgress::RProgress( format, total, width, complete_char, complete_char, incomplete_char, clear, show_after)), progress_(0), total_(total), last_progress_(0), last_time_(std::chrono::system_clock::now()), update_interval_(10) { pb_->set_reverse(false); } void tick(size_t progress) { std::lock_guard guard(mutex_); progress_ += progress; cv_.notify_one(); } void finish() { std::lock_guard guard(mutex_); progress_ = total_; cv_.notify_one(); } void display_progress() { while (true) { std::unique_lock lk(mutex_); if (progress_ < total_ - 1) { cv_.wait(lk); auto now = std::chrono::system_clock::now(); std::chrono::duration diff = now - last_time_; if (diff > update_interval_) { pb_->tick(progress_ - last_progress_); last_progress_ = progress_; last_time_ = std::chrono::system_clock::now(); } } else { break; } } pb_->update(1); } private: std::unique_ptr pb_; size_t progress_; size_t total_; size_t last_progress_; std::chrono::time_point last_time_; std::chrono::milliseconds update_interval_; std::mutex mutex_; std::condition_variable cv_; }; vroom/src/columns.h0000644000176200001440000001525514142512075014045 0ustar liggesusers#pragma once #include #include #include #include #include "vroom.h" #include "vroom_big_int.h" #include "vroom_chr.h" #include "vroom_date.h" #include "vroom_dbl.h" #include "vroom_dttm.h" #include "vroom_fct.h" #include "vroom_int.h" #include "vroom_lgl.h" #include "vroom_num.h" #include "vroom_rle.h" #include "vroom_time.h" #include "vroom_vec.h" #include "connection.h" #include "index_collection.h" #include "collectors.h" namespace vroom { inline std::vector get_filenames(SEXP in) { auto n = Rf_xlength(in); std::vector out; out.reserve(n); for (R_xlen_t i = 0; i < n; ++i) { SEXP x = VECTOR_ELT(in, i); if (TYPEOF(x) == STRSXP) { out.emplace_back(cpp11::as_cpp(x)); } else { out.emplace_back(con_description(x)); } } return out; } inline SEXP generate_filename_column( const std::vector& filenames, const std::vector& lengths, size_t rows) { #ifdef HAS_ALTREP // suppress compiler warning about unused parameter, as this is only used // without altrep. (void)rows; cpp11::writable::integers rle(filenames.size()); for (R_xlen_t i = 0; i < R_xlen_t(lengths.size()); ++i) { rle[i] = lengths[i]; } rle.names() = filenames; return vroom_rle::Make(rle); #else std::vector out; out.reserve(rows); if (static_cast(filenames.size()) != lengths.size()) { cpp11::stop("inputs and lengths inconsistent"); } for (size_t i = 0; i < filenames.size(); ++i) { for (size_t j = 0; j < lengths[i]; ++j) { out.push_back(filenames[i]); } } return cpp11::as_sexp(out); #endif } inline cpp11::list create_columns( std::shared_ptr idx, cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, SEXP id, std::vector& filenames, cpp11::strings na, cpp11::list locale, size_t altrep, size_t guess_max, cpp11::external_pointer> errors, size_t num_threads) { R_xlen_t num_cols = idx->num_columns(); auto num_rows = idx->num_rows(); auto locale_info = std::make_shared(locale); R_xlen_t i = 0; bool add_filename = !Rf_isNull(id); cpp11::writable::list res(num_cols + add_filename); cpp11::writable::strings res_nms(num_cols + add_filename); if (add_filename) { res[i] = generate_filename_column(filenames, idx->row_sizes(), idx->num_rows()); res_nms[i] = cpp11::strings(id)[0]; ++i; } auto my_collectors = resolve_collectors( col_names, col_types, col_select, name_repair, idx, na, locale_info, guess_max, altrep); size_t to_parse = 0; for (R_xlen_t col = 0; col < num_cols; ++col) { auto collector = my_collectors[col]; if (collector.use_altrep()) { to_parse += num_rows; } } for (R_xlen_t col = 0; col < num_cols; ++col) { auto collector = my_collectors[col]; auto col_type = collector.type(); if (col_type == column_type::Skip) { continue; } // This is deleted in the finalizers when the vectors are GC'd by R auto info = new vroom_vec_info{ idx->get_column(col), num_threads, std::make_shared(na), locale_info, *errors, std::string()}; res_nms[i] = collector.name(); switch (collector.type()) { case column_type::Dbl: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_dbl::Make(info); #endif } else { res[i] = read_dbl(info); delete info; } break; case column_type::Int: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_int::Make(info); #endif } else { res[i] = read_int(info); delete info; } break; case column_type::BigInt: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_big_int::Make(info); #endif } else { res[i] = read_big_int(info); delete info; } break; case column_type::Num: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_num::Make(info); #endif } else { res[i] = read_num(info); delete info; } break; case column_type::Lgl: // if (collector.use_altrep()) { //#if defined HAS_ALTREP && R_VERSION >= R_Version(3, 6, 0) // res[i] = vroom_lgl::Make(info); //#endif //} else { res[i] = read_lgl(info); delete info; //} break; case column_type::Fct: { auto levels = collector["levels"]; if (Rf_isNull(levels)) { res[i] = read_fct_implicit( info, cpp11::as_cpp(collector["include_na"])); delete info; } else { bool ordered = cpp11::as_cpp(collector["ordered"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_fct::Make(info, levels, ordered); #endif } else { res[i] = read_fct_explicit(info, levels, ordered); delete info; } } break; } case column_type::Date: info->format = cpp11::as_cpp(collector["format"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_date::Make(info); #endif } else { res[i] = read_date(info); delete info; } break; case column_type::Dttm: info->format = cpp11::as_cpp(collector["format"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_dttm::Make(info); #endif } else { res[i] = read_dttm(info); delete info; } break; case column_type::Time: info->format = cpp11::as_cpp(collector["format"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_time::Make(info); #endif } else { res[i] = read_time(info); delete info; } break; default: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_chr::Make(info); #endif } else { res[i] = read_chr(info); delete info; } } ++i; } if (i < num_cols) { // Resize the list appropriately SETLENGTH(res, i); SET_TRUELENGTH(res, i); SETLENGTH(res_nms, i); SET_TRUELENGTH(res_nms, i); } res.attr("names") = res_nms; cpp11::writable::list spec(my_collectors.spec()); spec["delim"] = cpp11::writable::strings({idx->get_delim().c_str()}); spec.attr("class") = "col_spec"; res.attr("spec") = spec; res.attr("problems") = errors; return res; } } // namespace vroom vroom/src/fixed_width_index_connection.h0000644000176200001440000000106014142515724020262 0ustar liggesusers#pragma once #include "fixed_width_index.h" namespace vroom { class fixed_width_index_connection : public fixed_width_index { std::string filename_; public: fixed_width_index_connection( SEXP in, std::vector col_starts, std::vector col_ends, bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress, const size_t chunk_size); ~fixed_width_index_connection() { remove(filename_.c_str()); } }; } // namespace vroom vroom/src/Iconv.h0000644000176200001440000000102714142512075013433 0ustar liggesusers#ifndef READ_ICONV_H_ #define READ_ICONV_H_ #include #include "R_ext/Riconv.h" #include #include class Iconv { void* cd_; std::string buffer_; public: Iconv(const std::string& from, const std::string& to = "UTF-8"); virtual ~Iconv(); SEXP makeSEXP(const char* start, const char* end, bool hasNull = true); std::string makeString(const char* start, const char* end); private: // Returns number of characters in buffer size_t convert(const char* start, const char* end); }; #endif vroom/src/altrep.cc0000644000176200001440000001036414142515724014012 0ustar liggesusers#include "altrep.h" #include "vroom_big_int.h" #include "vroom_chr.h" #include "vroom_date.h" #include "vroom_dbl.h" #include "vroom_dttm.h" #include "vroom_fct.h" #include "vroom_int.h" #include "vroom_lgl.h" #include "vroom_num.h" #include "vroom_time.h" #include #include #include [[cpp11::register]] void force_materialization(SEXP x) { #ifdef HAS_ALTREP DATAPTR(x); #endif } bool vroom_altrep(SEXP x) { #ifdef HAS_ALTREP return R_altrep_inherits(x, vroom_chr::class_t) || R_altrep_inherits(x, vroom_date::class_t) || R_altrep_inherits(x, vroom_dbl::class_t) || R_altrep_inherits(x, vroom_dttm::class_t) || R_altrep_inherits(x, vroom_fct::class_t) || R_altrep_inherits(x, vroom_int::class_t) || // R_altrep_inherits(x, vroom_lgl::class_t) || R_altrep_inherits(x, vroom_num::class_t) || R_altrep_inherits(x, vroom_time::class_t) || R_altrep_inherits(x, vroom_big_int::class_t); #else return false; #endif } [[cpp11::register]] SEXP vroom_materialize(SEXP x, bool replace) { #ifdef HAS_ALTREP for (R_xlen_t col = 0; col < Rf_xlength(x); ++col) { SEXP elt = VECTOR_ELT(x, col); // First materialize all of the non-character vectors if (vroom_altrep(elt)) { DATAPTR(elt); } } // If replace replace the altrep vectors with their materialized // vectors if (replace) { for (R_xlen_t col = 0; col < Rf_xlength(x); ++col) { SEXP elt = PROTECT(VECTOR_ELT(x, col)); if (vroom_altrep(elt)) { SET_VECTOR_ELT(x, col, R_altrep_data2(elt)); R_set_altrep_data2(elt, R_NilValue); } UNPROTECT(1); } } #endif return x; } [[cpp11::register]] SEXP vroom_convert(SEXP x) { #ifdef HAS_ALTREP SEXP out = PROTECT(Rf_allocVector(VECSXP, Rf_xlength(x))); SHALLOW_DUPLICATE_ATTRIB(out, x); for (R_xlen_t col = 0; col < Rf_xlength(x); ++col) { SEXP elt = VECTOR_ELT(x, col); if (!ALTREP(elt)) { SET_VECTOR_ELT(out, col, elt); } else { R_xlen_t nrow = Rf_xlength(elt); switch (TYPEOF(elt)) { case LGLSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(LGLSXP, nrow)); int* out_p = LOGICAL(VECTOR_ELT(out, col)); int* in_p = LOGICAL(elt); for (R_xlen_t row = 0; row < nrow; ++row) { out_p[row] = in_p[row]; } break; } case INTSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(INTSXP, nrow)); int* out_p = INTEGER(VECTOR_ELT(out, col)); int* in_p = INTEGER(elt); for (R_xlen_t row = 0; row < nrow; ++row) { out_p[row] = in_p[row]; } break; } case REALSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(REALSXP, nrow)); double* out_p = REAL(VECTOR_ELT(out, col)); double* in_p = REAL(elt); for (R_xlen_t row = 0; row < nrow; ++row) { out_p[row] = in_p[row]; } break; } case STRSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(STRSXP, nrow)); SEXP out_elt = VECTOR_ELT(out, col); DATAPTR(elt); for (R_xlen_t row = 0; row < nrow; ++row) { SET_STRING_ELT(out_elt, row, STRING_ELT(elt, row)); } break; } } SHALLOW_DUPLICATE_ATTRIB(VECTOR_ELT(out, col), elt); } } UNPROTECT(1); return out; #else return x; #endif } [[cpp11::register]] std::string vroom_str_(const cpp11::sexp& x) { std::stringstream ss; #ifdef HAS_ALTREP if (ALTREP(x)) { auto csym = CAR(ATTRIB(ALTREP_CLASS(x))); auto psym = CADR(ATTRIB(ALTREP_CLASS(x))); bool is_altrep = ALTREP(x); bool materialzied = R_altrep_data2(x) != R_NilValue; ss << std::boolalpha << "altrep:" << is_altrep << '\t' << "type:" << CHAR(PRINTNAME(psym)) << "::" << CHAR(PRINTNAME(csym)); // We would have to dispatch to get the length of an object if (!Rf_isObject(x)) { ss << '\t' << "length:" << LENGTH(x); } ss << '\t' << "materialized:" << materialzied << '\n'; } #else if (false) { } #endif else { ss << std::boolalpha << "altrep:" << false << '\t' << "type: " << Rf_type2char(TYPEOF(x)); if (!Rf_isObject(x)) { ss << '\t' << "length:" << LENGTH(x); } ss << '\n'; } return ss.str(); } vroom/src/iconv_file.cc0000644000176200001440000000631114142515724014635 0ustar liggesusers#include #include #include "R_ext/Riconv.h" #include "connection.h" #define VROOM_BUFSIZ 1024 // Adapted from // https://www.gnu.org/software/libc/manual/html_node/iconv-Examples.html [[cpp11::register]] size_t convert_connection( SEXP in_con, SEXP out_con, const std::string& from, const std::string& to) { static auto isOpen = cpp11::package("base")["isOpen"]; static auto open = cpp11::package("base")["open"]; static auto close = cpp11::package("base")["close"]; char inbuf[VROOM_BUFSIZ]; char wrbuf[VROOM_BUFSIZ * 4]; char* wrptr = wrbuf; size_t insize = 0; void* cd; bool should_close_in = !isOpen(in_con); bool should_close_out = !isOpen(out_con); if (should_close_in) { open(in_con, "rb"); } if (should_close_out) { open(out_con, "wb"); } cd = Riconv_open(to.c_str(), from.c_str()); if (cd == (void*)-1) { /* Something went wrong. */ if (errno == EINVAL) { if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("Can't convert from %s to %s", from.c_str(), to.c_str()); } else { if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("Iconv initialisation failed"); } } size_t avail = VROOM_BUFSIZ * 4; size_t bytes_wrote = 0; while (avail > 0) { size_t nread; size_t nconv; const char* inptr = inbuf; /* Read more input. */ nread = R_ReadConnection(in_con, inbuf + insize, sizeof(inbuf) - insize); if (nread == 0) { /* When we come here the file is completely read. This still could mean there are some unused characters in the inbuf. */ /* Now write out the byte sequence to get into the initial state if this is necessary. */ Riconv(cd, nullptr, nullptr, &wrptr, &avail); break; } insize += nread; /* Do the conversion. */ nconv = Riconv(cd, &inptr, &insize, &wrptr, &avail); if (nconv == (size_t)-1) { /* Not everything went right. It might only be an unfinished byte sequence at the end of the buffer. Or it is a real problem. */ if (errno == EINVAL) { /* This is harmless. Simply move the unused bytes to the beginning of the buffer so that they can be used in the next round. */ memmove(inbuf, inptr, insize); } else { /* It is a real problem. Maybe we ran out of space in the output buffer or we have invalid input. */ if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("iconv failed"); break; } } R_WriteConnection(out_con, wrbuf, wrptr - wrbuf); bytes_wrote += wrptr - wrbuf; wrptr = wrbuf; avail = sizeof(wrbuf); } if (Riconv_close(cd) != 0) { if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("Iconv closed failed"); } if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } return bytes_wrote; } vroom/src/LocaleInfo.h0000644000176200001440000000071714142515724014401 0ustar liggesusers#ifndef FASTREAD_LOCALINFO #define FASTREAD_LOCALINFO #include "Iconv.h" #include #include #include class LocaleInfo { public: // LC_TIME std::vector mon_, monAb_, day_, dayAb_, amPm_; std::string dateFormat_, timeFormat_; // LC_NUMERIC std::string decimalMark_, groupingMark_; // LC_MISC std::string tz_; std::string encoding_; Iconv encoder_; LocaleInfo(const cpp11::list&); }; #endif vroom/src/vroom.h0000644000176200001440000000417414142515724013531 0ustar liggesusers#pragma once #ifndef VROOM_LOG #define SPDLOG_TRACE(...) (void)0 #define SPDLOG_DEBUG(...) (void)0 #define SPDLOG_INFO(...) (void)0 #else #include "spdlog/spdlog.h" #endif #include #include namespace vroom { enum column_type { Chr = 1, Fct = 2, Int = 4, Dbl = 8, Num = 16, Lgl = 32, Dttm = 64, Date = 128, Time = 256, BigInt = 512, Skip = 1024 }; // A custom string wrapper that avoids constructing a string object unless // needed because of escapes. class string { public: string(const std::string& str) : str_(str) { begin_ = str_.c_str(); end_ = begin_ + str_.length(); } string(std::string&& str) : str_(std::move(str)) { begin_ = str_.c_str(); end_ = begin_ + str_.length(); } string(const char* begin, const char* end) : begin_(begin), end_(end) {} const char* begin() const { return begin_; } const char* end() const { return end_; } size_t length() const { return end_ - begin_; } size_t size() const { return end_ - begin_; } bool operator==(const string& other) const { return length() == other.length() && strncmp(begin_, other.begin_, length()) == 0; } bool operator==(const std::string& other) const { return length() == other.length() && strncmp(begin_, other.data(), length()) == 0; } std::string str() const { if (size() > 0 && str_.size() == 0) { return std::string(begin_, end_); } else { return str_; } } private: const char* begin_; const char* end_; const std::string str_; }; template inline T na(); } // namespace vroom // Specialization for our custom strings, needed so we can use them in // unordered_maps // [1]: https://stackoverflow.com/a/17017281/2055486 // [2]: https://stackoverflow.com/a/34597485/2055486 namespace std { template <> struct hash { std::size_t operator()(const vroom::string& k) const { const char* begin = k.begin(); const char* end = k.end(); size_t result = 0; const size_t prime = 31; while (begin != end) { result = *begin++ + (result * prime); } return result; } }; } // namespace std vroom/src/DateTimeParser.h0000644000176200001440000003317014145734713015242 0ustar liggesusers#ifndef FASTREAD_DATE_TIME_PARSER_H_ #define FASTREAD_DATE_TIME_PARSER_H_ #include "DateTime.h" #include "LocaleInfo.h" #include #include #include // Parsing --------------------------------------------------------------------- template inline bool parseInt(Iterator& first, Iterator& last, Attr& res) { char buf[65]; size_t expected_size = last - first; if (expected_size > sizeof(buf) - 1) { res = NA_INTEGER; return false; } std::copy(first, last, buf); buf[expected_size] = '\0'; long lres; char* endp; errno = 0; lres = strtol(buf, &endp, 10); size_t parsed_size = endp - buf; /* next can happen on a 64-bit platform */ if (res > INT_MAX || res < INT_MIN) lres = NA_INTEGER; if (errno == ERANGE) lres = NA_INTEGER; res = static_cast(lres); first += parsed_size; return res != NA_INTEGER; } template inline bool parseDouble( const char /* decimalMark */, Iterator& first, Iterator& last, Attr& res) { char buf[65]; // It can't be a double if it is over 64 characters long. bool too_long = last - first > 64; if (too_long) { res = NA_REAL; return false; } std::copy(first, last, buf); buf[last - first] = '\0'; char* endp; errno = 0; res = strtod(buf, &endp); if (errno > 0) { res = NA_REAL; } else { first += endp - buf; } return !ISNA(res); } class DateTimeParser { int sign_, year_, mon_, day_, hour_, min_, sec_; double psec_; int amPm_; bool compactDate_; // used for guessing int tzOffsetHours_, tzOffsetMinutes_; std::string tz_; LocaleInfo* pLocale_; std::string tzDefault_; const char* dateItr_; const char* dateEnd_; public: DateTimeParser(LocaleInfo* pLocale) : pLocale_(pLocale), tzDefault_(pLocale->tz_), dateItr_(NULL), dateEnd_(NULL) { reset(); } // Parse ISO8601 date time. In benchmarks this only seems ~30% faster than // parsing with a format string so it doesn't seem necessary to add individual // parsers for other common formats. bool parseISO8601(bool /* partial */ = true) { // Date: YYYY-MM-DD, YYYYMMDD if (!consumeInteger(4, &year_)) return false; if (consumeThisChar('-')) compactDate_ = false; if (!consumeInteger(2, &mon_)) return false; if (!compactDate_ && !consumeThisChar('-')) return false; if (!consumeInteger(2, &day_)) return false; if (isComplete()) return true; // Spec requires T, but common to use space instead char next; if (!consumeChar(&next)) return false; if (next != 'T' && next != ' ') return false; // hh:mm:ss.sss, hh:mm:ss, hh:mm, hh // hhmmss.sss, hhmmss, hhmm if (!consumeInteger(2, &hour_)) return false; consumeThisChar(':'); consumeInteger(2, &min_); consumeThisChar(':'); consumeSeconds(&sec_, &psec_); if (isComplete()) return true; // Has a timezone tz_ = "UTC"; if (!consumeTzOffset(&tzOffsetHours_, &tzOffsetMinutes_)) return false; return isComplete(); } bool parseLocaleTime() { return parse(pLocale_->timeFormat_); } bool parseLocaleDate() { return parse(pLocale_->dateFormat_); } // A flexible time parser for the most common formats bool parseTime() { if (!consumeInteger(2, &hour_, false)) return false; if (!consumeThisChar(':')) return false; if (!consumeInteger(2, &min_)) return false; consumeThisChar(':'); consumeSeconds(&sec_, NULL); consumeWhiteSpace(); consumeString(pLocale_->amPm_, &amPm_); consumeWhiteSpace(); return isComplete(); } bool parseDate() { // Date: YYYY-MM-DD, YYYY/MM/DD if (!consumeInteger(4, &year_)) return false; if (!consumeThisChar('-') && !consumeThisChar('/')) return false; if (!consumeInteger(2, &mon_)) return false; if (!consumeThisChar('-') && !consumeThisChar('/')) return false; if (!consumeInteger(2, &day_)) return false; return isComplete(); } bool isComplete() { return dateItr_ == dateEnd_; } void setDate(const char* start, const char* end) { reset(); dateItr_ = start; dateEnd_ = end; } bool parse(const std::string& format) { consumeWhiteSpace(); // always consume leading whitespace std::string::const_iterator formatItr, formatEnd = format.end(); for (formatItr = format.begin(); formatItr != formatEnd; ++formatItr) { // Whitespace in format matches 0 or more whitespace in date if (std::isspace(*formatItr)) { consumeWhiteSpace(); continue; } // Any other characters must much exactly. if (*formatItr != '%') { if (!consumeThisChar(*formatItr)) return false; continue; } if (formatItr + 1 == formatEnd) throw std::runtime_error("Invalid format: trailing %"); formatItr++; switch (*formatItr) { case 'Y': // year with century if (!consumeInteger(4, &year_)) return false; break; case 'y': // year without century if (!consumeInteger(2, &year_)) return false; year_ += (year_ < 69) ? 2000 : 1900; break; case 'm': // month if (!consumeInteger(2, &mon_, false)) return false; break; case 'b': // abbreviated month name if (!consumeString(pLocale_->monAb_, &mon_)) return false; break; case 'B': // month name if (!consumeString(pLocale_->mon_, &mon_)) return false; break; case 'd': // day if (!consumeInteger(2, &day_, false)) return false; break; case 'a': // abbreviated day of week if (!consumeString(pLocale_->dayAb_, &day_)) return false; break; case 'e': // day with optional leading space if (!consumeIntegerWithSpace(2, &day_)) return false; break; case 'h': // hour, unrestricted if (!consumeHours(&hour_, &sign_)) return false; break; case 'H': // hour, 0-23 if (!consumeInteger(2, &hour_, false)) return false; if (hour_ < 0 || hour_ > 23) { return false; } break; case 'I': // hour if (!consumeInteger(2, &hour_, false)) return false; if (hour_ < 1 || hour_ > 12) { return false; } hour_ %= 12; break; case 'M': // minute if (!consumeInteger(2, &min_)) return false; break; case 'S': // seconds (integer) if (!consumeSeconds(&sec_, NULL)) return false; break; case 'O': // seconds (double) if (formatItr + 1 == formatEnd || *(formatItr + 1) != 'S') throw std::runtime_error( "Invalid format: %%O must be followed by %%S"); formatItr++; if (!consumeSeconds(&sec_, &psec_)) return false; break; case 'p': // AM/PM if (!consumeString(pLocale_->amPm_, &amPm_)) return false; break; case 'z': // time zone specification tz_ = "UTC"; if (!consumeTzOffset(&tzOffsetHours_, &tzOffsetMinutes_)) return false; break; case 'Z': // time zone name if (!consumeTzName(&tz_)) return false; break; // Extensions case '.': if (!consumeNonDigit()) return false; break; case '+': if (!consumeNonDigits()) return false; break; case '*': consumeNonDigits(); break; case 'A': // auto date / time if (formatItr + 1 == formatEnd) throw std::runtime_error( "Invalid format: %%A must be followed by another letter"); formatItr++; switch (*formatItr) { case 'D': if (!parseDate()) return false; break; case 'T': if (!parseTime()) return false; break; default: throw std::runtime_error("Invalid %%A auto parser"); } break; // Compound formats case 'D': parse("%m/%d/%y"); break; case 'F': parse("%Y-%m-%d"); break; case 'R': parse("%H:%M"); break; case 'X': case 'T': parse("%H:%M:%S"); break; case 'x': parse("%y/%m/%d"); break; default: throw std::runtime_error("Unsupported format"); } } consumeWhiteSpace(); // always consume trailing whitespace return isComplete(); } DateTime makeDateTime() { DateTime dt(year_, mon_, day_, hour(), min_, sec_, psec_, tz_); if (tz_ == "UTC") dt.setOffset(-tzOffsetHours_ * 3600 - tzOffsetMinutes_ * 60); return dt; } DateTime makeDate() { DateTime dt(year_, mon_, day_, 0, 0, 0, 0, "UTC"); return dt; } DateTime makeTime() { DateTime dt( 0, 1, 1, sign_ * hour(), sign_ * min_, sign_ * sec_, sign_ * psec_, "UTC"); return dt; } bool compactDate() { return compactDate_; } int year() { return year_; } private: int hour() { if (hour_ == 12) { // 12 AM if (amPm_ == 1) { return hour_ - 12; } // 12 PM return hour_; } // Rest of PM if (amPm_ == 2) { return hour_ + 12; } // 24 hour time return hour_; } inline bool consumeHours(int* pHour, int* pSign) { if (dateItr_ == dateEnd_) return false; int sign = 1; if (*dateItr_ == '-') { sign = -1; ++dateItr_; } else if (*dateItr_ == '+') { ++dateItr_; } if (!consumeInteger(10, pHour, false)) return false; *pSign = sign; return true; } inline bool consumeSeconds(int* pSec, double* pPartialSec) { double sec; if (!consumeDouble(&sec)) return false; *pSec = (int)sec; if (pPartialSec != NULL) *pPartialSec = sec - *pSec; return true; } // Assumes `pOut` is 1-indexed inline bool consumeString(const std::vector& haystack, int* pOut) { // haystack is always in UTF-8 std::string needleUTF8 = pLocale_->encoder_.makeString(dateItr_, dateEnd_); std::transform( needleUTF8.begin(), needleUTF8.end(), needleUTF8.begin(), ::tolower); for (size_t i = 0; i < haystack.size(); ++i) { std::string hay = haystack[i]; std::transform(hay.begin(), hay.end(), hay.begin(), ::tolower); if (needleUTF8.find(hay) != std::string::npos) { *pOut = i + 1; dateItr_ += hay.size(); return true; } } return false; } inline bool consumeInteger(int n, int* pOut, bool exact = true) { if (dateItr_ == dateEnd_ || *dateItr_ == '-' || *dateItr_ == '+') return false; const char* start = dateItr_; const char* end = std::min(dateItr_ + n, dateEnd_); bool ok = parseInt(dateItr_, end, *pOut); return ok && (!exact || (dateItr_ - start) == n); } // Integer with optional space inline bool consumeIntegerWithSpace(int n, int* pOut) { if (consumeThisChar(' ')) n--; return consumeInteger(n, pOut); } inline bool consumeDouble(double* pOut) { if (dateItr_ == dateEnd_ || *dateItr_ == '-' || *dateItr_ == '+') return false; return parseDouble(pLocale_->decimalMark_[0], dateItr_, dateEnd_, *pOut); } inline bool consumeWhiteSpace() { while (dateItr_ != dateEnd_ && std::isspace(*dateItr_)) dateItr_++; return true; } inline bool consumeNonDigit() { if (dateItr_ == dateEnd_ || std::isdigit(*dateItr_)) return false; dateItr_++; return true; } inline bool consumeNonDigits() { if (!consumeNonDigit()) return false; while (dateItr_ != dateEnd_ && !std::isdigit(*dateItr_)) dateItr_++; return true; } inline bool consumeChar(char* pOut) { if (dateItr_ == dateEnd_) return false; *pOut = *dateItr_++; return true; } inline bool consumeThisChar(char needed) { if (dateItr_ == dateEnd_ || *dateItr_ != needed) return false; dateItr_++; return true; } inline bool consumeAMPM(bool* pIsPM) { if (dateItr_ == dateEnd_) return false; if (consumeThisChar('A') || consumeThisChar('a')) { *pIsPM = false; } else if (consumeThisChar('P') || consumeThisChar('p')) { *pIsPM = true; } else { return false; } if (!(consumeThisChar('M') || consumeThisChar('m'))) return false; return true; } // ISO8601 style // Z // Β±hh:mm // Β±hhmm // Β±hh inline bool consumeTzOffset(int* pHours, int* pMinutes) { if (consumeThisChar('Z')) return true; // Optional +/- (required for ISO8601 but we'll let it slide) int mult = 1; if (*dateItr_ == '+' || *dateItr_ == '-') { mult = (*dateItr_ == '-') ? -1 : 1; dateItr_++; } // Required hours if (!consumeInteger(2, pHours)) return false; // Optional colon and minutes consumeThisChar(':'); consumeInteger(2, pMinutes); *pHours *= mult; *pMinutes *= mult; return true; } inline bool consumeTzName(std::string* pOut) { const char* tzStart = dateItr_; while (dateItr_ != dateEnd_ && !std::isspace(*dateItr_)) dateItr_++; pOut->assign(tzStart, dateItr_); return tzStart != dateItr_; } void reset() { sign_ = 1; year_ = -1; mon_ = 1; day_ = 1; hour_ = 0; min_ = 0; sec_ = 0; psec_ = 0; amPm_ = -1; compactDate_ = true; tzOffsetHours_ = 0; tzOffsetMinutes_ = 0; tz_ = tzDefault_; } }; #endif vroom/src/fixed_width_index.h0000644000176200001440000001677414143524032016056 0ustar liggesusers#pragma once #include "index.h" // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" #include # pragma clang diagnostic pop #else #include #endif // clang-format on #ifndef VROOM_STANDALONE #include "r_utils.h" #include "RProgress.h" #else #include "utils.h" #define NA_INTEGER INT_MIN #endif #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif #include "unicode_fopen.h" namespace vroom { class fixed_width_index : public index, public std::enable_shared_from_this { protected: fixed_width_index() : trim_ws_(0) {} std::vector newlines_; std::vector col_starts_; std::vector col_ends_; mio::mmap_source mmap_; bool trim_ws_; std::string filename_; public: fixed_width_index( const char* filename, std::vector col_starts, std::vector col_ends, bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress) : col_starts_(col_starts), col_ends_(col_ends), trim_ws_(trim_ws), filename_(filename) { std::error_code error; mmap_ = make_mmap_source(filename, error); if (error) { // We cannot actually portably compare error messages due to a bug in // libstdc++ (https://stackoverflow.com/a/54316671/2055486), so just print // the message on stderr return #ifndef VROOM_STANDALONE REprintf("mapping error: %s\n", error.message().c_str()); #else std::cerr << "mapping error: " << error.message() << '\n'; #endif return; } size_t file_size = mmap_.size(); size_t start = find_first_line( mmap_, skip, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0'); // Check for windows newlines size_t first_nl; newline_type nl; std::tie(first_nl, nl) = find_next_newline( mmap_, start, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0'); std::unique_ptr pb = nullptr; if (progress) { #ifndef VROOM_STANDALONE auto format = get_pb_format("file", filename); auto width = get_pb_width(format); pb = std::unique_ptr( new RProgress::RProgress(format, file_size, width)); pb->tick(start); #endif } bool n_max_set = n_max != static_cast(-1); if (n_max > 0) { newlines_.push_back(start - 1); } index_region( mmap_, newlines_, start, file_size - 1, 0, comment, skip_empty_rows, n_max, pb, file_size / 1000); if (!n_max_set) { if (nl == CRLF) { newlines_.push_back(file_size - 2); } else { newlines_.push_back(file_size - 1); } } if (progress) { #ifndef VROOM_STANDALONE pb->update(1); #endif } #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt( "basic_logger", "logs/fixed_width_index.idx", true); log->set_level(spdlog::level::debug); for (auto& v : newlines_) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&newlines_); spdlog::drop("basic_logger"); #endif } size_t num_rows() const override { return newlines_.size() - 1; } size_t num_columns() const override { return col_starts_.size(); } std::string get_delim() const override { /* TODO: FIXME */ return ""; } string get(size_t row, size_t col) const override { auto begin = mmap_.data() + (newlines_[row] + 1 + col_starts_[col]); auto line_end = mmap_.data() + (newlines_[row + 1]); if (line_end > begin && *(line_end - 1) == '\r') { --line_end; } const char* end; if (col_ends_[col] == NA_INTEGER) { end = mmap_.data() + newlines_[row + 1]; } else { end = mmap_.data() + (newlines_[row] + 1 + col_ends_[col]); } if (begin > line_end) { begin = line_end; } if (end > line_end) { end = line_end; } if (trim_ws_) { trim_whitespace(begin, end); } return {begin, end}; } class column_iterator : public base_iterator { std::shared_ptr idx_; size_t column_; size_t i_; public: column_iterator(std::shared_ptr idx, size_t column) : idx_(idx), column_(column), i_(0) {} void next() override { ++i_; } void advance(ptrdiff_t n) override { i_ += n; } bool equal_to(const base_iterator& it) const override { return i_ == static_cast(&it)->i_; } ptrdiff_t distance_to(const base_iterator& it) const override { return static_cast( static_cast(&it)->i_) - static_cast(i_); } string value() const override { return idx_->get(i_, column_); } column_iterator* clone() const override { return new column_iterator(*this); } string at(ptrdiff_t n) const override { return idx_->get(n, column_); } std::string filename() const override { return idx_->filename_; } size_t index() const override { return i_ / idx_->num_columns(); } size_t position() const override { return i_; } virtual ~column_iterator() = default; }; std::shared_ptr get_column(size_t column) const override { auto begin = new column_iterator(shared_from_this(), column); auto end = new column_iterator(shared_from_this(), column); end->advance(num_rows()); return std::make_shared(begin, end, column); } template size_t index_region( const T& source, std::vector& destination, size_t start, size_t end, size_t offset, const char* comment, const bool skip_empty_rows, size_t n_max, std::unique_ptr& pb, size_t update_size = -1) { size_t pos; newline_type first_nl, nl; std::tie(pos, first_nl) = find_next_newline( source, start, comment, skip_empty_rows, /* embededd_nl */ false, /* quote */ '\0'); size_t lines_read = 0; auto last_tick = start; while (pos < end) { ++lines_read; destination.push_back(offset + pos); if (lines_read >= n_max) { return lines_read; } if (pb) { auto tick_size = pos - last_tick; if (tick_size > update_size) { pb->tick(pos - last_tick); last_tick = pos; } } /* For this search we need to look only for either a CR or a LF, since a * CRLF could potentially be split between two buffers, and has been in * practice. */ std::tie(pos, nl) = find_next_newline( source, pos + 1, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0', /* type */ first_nl); } if (pb) { pb->tick(end - last_tick); } return lines_read; } std::shared_ptr get_row(size_t) const override { // TODO: UNUSED return nullptr; } std::shared_ptr get_header() const override { // TODO: UNUSED return nullptr; } std::string filename() const { return filename_; } }; } // namespace vroom vroom/src/parallel.h0000644000176200001440000000422114142515724014154 0ustar liggesusers#pragma once #include #include #include #include #include // adapted from https://stackoverflow.com/a/49188371/2055486 /// @param[in] nb_elements : size of your for loop /// @param[in] functor(start, end) : /// your function processing a sub chunk of the for loop. /// "start" is the first index to process (included) until the index "end" /// (excluded) /// @code /// for(int i = start; i < end; ++i) /// computation(i); /// @endcode /// @param use_threads : enable / disable threads. /// /// static std::vector> parallel_for( size_t nb_elements, std::function functor, size_t nb_threads, bool use_threads = true, bool cleanup = true) { #if !defined __APPLE__ && defined _LIBCPP_VERSION nb_threads = 1; #endif if (nb_threads == 1) { use_threads = false; } size_t batch_size = nb_elements / nb_threads; size_t batch_remainder = nb_elements % nb_threads; auto my_threads = std::vector>(nb_threads); if (use_threads) { // Multithread execution for (size_t i = 0; i < (nb_threads - 1); ++i) { size_t start = i * batch_size; my_threads[i] = std::async(std::launch::async, functor, start, start + batch_size, i); } // Last batch includes the remainder size_t start = (nb_threads - 1) * batch_size; my_threads[nb_threads - 1] = std::async( std::launch::async, functor, start, start + batch_size + batch_remainder, nb_threads - 1); } else { // Single thread execution (for easy debugging) for (size_t i = 0; i < (nb_threads - 1); ++i) { size_t start = i * batch_size; functor(start, start + batch_size, i); } // Last batch includes the remainder size_t start = (nb_threads - 1) * batch_size; functor(start, start + batch_size + batch_remainder, nb_threads - 1); return std::vector>(); } // Wait for the other thread to finish their task if (use_threads && cleanup) { for (auto& t : my_threads) { t.get(); } } return my_threads; } vroom/src/index_collection.cc0000644000176200001440000002055714143524032016043 0ustar liggesusers#include #include "delimited_index_connection.h" #include "fixed_width_index.h" #include "fixed_width_index_connection.h" #include "index.h" #include "index_collection.h" #include #include #include "r_utils.h" using namespace vroom; // Class index_collection::column::iterator index_collection::full_iterator::full_iterator( std::shared_ptr idx, size_t column) : i_(0), idx_(std::move(idx)), column_(column), end_(idx_->indexes_.size() - 1) { auto col = idx_->indexes_[i_]->get_column(column_); it_ = col->begin(); it_end_ = col->end(); it_start_ = col->begin(); } void index_collection::full_iterator::next() { ++it_; while (it_ == it_end_ && i_ < end_) { ++i_; it_ = idx_->indexes_[i_]->get_column(column_)->begin(); it_end_ = idx_->indexes_[i_]->get_column(column_)->end(); } } void index_collection::full_iterator::advance(ptrdiff_t n) { if (n == 0) { return; } if (n > 0) { while (n > 0) { auto diff = it_end_ - it_; if (n < diff) { it_ += n; return; } it_ += (diff - 1); n -= diff; next(); } return; } if (n < 0) { throw std::runtime_error("negative advance not supported"); } } ptrdiff_t index_collection::full_iterator::distance_to(const base_iterator& that) const { auto that_ = static_cast(&that); if (i_ == that_->i_) { ptrdiff_t res = that_->it_ - it_; return res; } ptrdiff_t count = 0; size_t i = i_; if (i_ < that_->i_) { count = it_end_ - it_; ++i; while (i < that_->i_) { count += idx_->indexes_[i]->num_rows(); ++i; } auto begin = idx_->indexes_[i]->get_column(column_)->begin(); count += that_->it_ - begin; return count; } count = it_start_ - it_; --i; while (i > that_->i_) { count -= idx_->indexes_[i]->num_rows(); --i; } auto end = idx_->indexes_[i]->get_column(column_)->end(); count += that_->it_ - end; return count; } string index_collection::full_iterator::value() const { return *it_; } index_collection::full_iterator* index_collection::full_iterator::clone() const { auto copy = new index_collection::full_iterator(*this); return copy; } string index_collection::full_iterator::at(ptrdiff_t n) const { return idx_->get(n, column_); } std::shared_ptr make_delimited_index( const cpp11::sexp& in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr& errors, const size_t num_threads, const bool progress) { auto standardise_one_path = cpp11::package("vroom")["standardise_one_path"]; auto x = standardise_one_path(in); bool is_connection = TYPEOF(x) != STRSXP; if (is_connection) { return std::make_shared( x, delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, get_env("VROOM_CONNECTION_SIZE", 1 << 17), progress); } auto filename = cpp11::as_cpp(x); return std::make_shared( filename.c_str(), delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, num_threads, progress); } void check_column_consistency( const std::shared_ptr& first, const std::shared_ptr& check, bool has_header, size_t i) { if (check->num_columns() != first->num_columns()) { std::stringstream ss; ss << "Files must all have " << first->num_columns() << " columns:\n" "* File " << i + 1 << " has " << check->num_columns() << " columns"; cpp11::stop("%s", ss.str().c_str()); } // If the files have a header ensure they are consistent with each other. if (has_header) { auto first_header = first->get_header()->begin(); auto check_header = check->get_header(); auto col = 0; for (auto header : *check_header) { if (!(header == *first_header)) { std::stringstream ss; ss << "Files must have consistent column names:\n" "* File 1 column " << col + 1 << " is: " << (*first_header).str() << "\n" "* File " << i + 1 << " column " << col + 1 << " is: " << header.str(); cpp11::stop("%s", ss.str().c_str()); } ++first_header; ++col; } } } // Index_collection index_collection::index_collection( const cpp11::list& in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr& errors, const size_t num_threads, const bool progress) : rows_(0), columns_(0) { std::shared_ptr first = make_delimited_index( in[0], delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, num_threads, progress); indexes_.push_back(first); columns_ = first->num_columns(); rows_ = first->num_rows(); for (int i = 1; i < in.size(); ++i) { std::shared_ptr idx = make_delimited_index( in[i], delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, num_threads, progress); check_column_consistency(first, idx, has_header, i); rows_ += idx->num_rows(); indexes_.emplace_back(std::move(idx)); } } std::shared_ptr make_fixed_width_index( const cpp11::sexp& in, const std::vector& col_starts, const std::vector& col_ends, const bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress) { auto standardise_one_path = cpp11::package("vroom")["standardise_one_path"]; auto x = standardise_one_path(in); bool is_connection = TYPEOF(x) != STRSXP; if (is_connection) { return std::make_shared( x, col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress, get_env("VROOM_CONNECTION_SIZE", 1 << 17)); } else { auto filename = cpp11::as_cpp(x); return std::make_shared( filename.c_str(), col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); } } index_collection::index_collection( const cpp11::list& in, const std::vector& col_starts, const std::vector& col_ends, const bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress) : rows_(0), columns_(0) { auto first = make_fixed_width_index( in[0], col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); columns_ = first->num_columns(); rows_ = first->num_rows(); indexes_.push_back(first); for (int i = 1; i < in.size(); ++i) { auto idx = make_fixed_width_index( in[i], col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); check_column_consistency(first, idx, false, i); rows_ += idx->num_rows(); indexes_.emplace_back(std::move(idx)); } } string index_collection::get(size_t row, size_t column) const { for (const auto& idx : indexes_) { if (row < idx->num_rows()) { return idx->get(row, column); } row -= idx->num_rows(); } /* should never get here */ return std::string(""); } vroom/src/collectors.h0000644000176200001440000001270614142515724014540 0ustar liggesusers#pragma once #include #include #include #include class collector { const cpp11::list data_; const SEXP name_; const column_type type_; const size_t altrep_; private: column_type derive_type(const std::string& t) { if (t == "collector_skip") { return column_type::Skip; } if (t == "collector_double") { return column_type::Dbl; } if (t == "collector_integer") { return column_type::Int; } if (t == "collector_big_integer") { return column_type::BigInt; } if (t == "collector_number") { return column_type::Num; } if (t == "collector_logical") { return column_type::Lgl; } if (t == "collector_factor") { return column_type::Fct; } if (t == "collector_date") { return column_type::Date; } if (t == "collector_datetime") { return column_type::Dttm; } if (t == "collector_time") { return column_type::Time; } return column_type::Chr; } public: collector(cpp11::list data, SEXP name, size_t altrep) : data_(data), name_(name), type_(derive_type(cpp11::strings(data_.attr("class"))[0])), altrep_(altrep) {} column_type type() const { return type_; } SEXP name() const { return name_; } SEXP operator[](const char* nme) { return data_[nme]; } bool use_altrep() { switch (type()) { case column_type::Skip: return false; case column_type::Dbl: return altrep_ & column_type::Dbl; case column_type::Int: return altrep_ & column_type::Int; case column_type::BigInt: return altrep_ & column_type::BigInt; case column_type::Num: return altrep_ & column_type::Num; case column_type::Fct: return altrep_ & column_type::Fct; case column_type::Date: return altrep_ & column_type::Date; case column_type::Dttm: return altrep_ & column_type::Dttm; case column_type::Time: return altrep_ & column_type::Time; case column_type::Chr: return altrep_ & column_type::Chr; default: return false; } } }; class collectors { cpp11::list spec_; cpp11::list collectors_; size_t altrep_; public: collectors(cpp11::list col_types, size_t altrep) : spec_(col_types), collectors_(col_types["cols"]), altrep_(altrep) {} collector operator[](int i) { return { collectors_[i], cpp11::strings(collectors_.attr("names"))[i], altrep_}; } cpp11::list spec() { return spec_; } }; inline cpp11::strings read_column_names( std::shared_ptr idx, std::shared_ptr locale_info) { cpp11::writable::strings nms(idx->num_columns()); auto col = 0; auto header = idx->get_header(); for (const auto& str : *header) { nms[col++] = locale_info->encoder_.makeSEXP(str.begin(), str.end(), false); } return nms; } std::string guess_type__( cpp11::writable::strings& input, const cpp11::strings& na, LocaleInfo* locale, bool guess_integer); inline collectors resolve_collectors( cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, std::shared_ptr idx, cpp11::strings na, std::shared_ptr locale_info, size_t guess_max, size_t altrep) { R_xlen_t num_cols = idx->num_columns(); auto num_rows = idx->num_rows(); auto vroom = cpp11::package("vroom"); cpp11::writable::strings col_nms; auto make_names = vroom["make_names"]; if (num_rows == 0 && num_cols == 0) { if (TYPEOF(col_names) == STRSXP) { col_nms = static_cast(col_names); } else { if (TYPEOF(col_types) == VECSXP) { col_nms = static_cast( make_names(R_NilValue, Rf_xlength(VECTOR_ELT(col_types, 0)))); } } } if (TYPEOF(col_names) == STRSXP) { col_nms = static_cast(col_names); } else if (TYPEOF(col_names) == LGLSXP && cpp11::logicals(col_names)[0]) { col_nms = read_column_names(idx, locale_info); } auto col_types_standardise = vroom["col_types_standardise"]; cpp11::list col_types_std(col_types_standardise( col_types, num_cols, col_nms, col_select, name_repair)); R_xlen_t guess_num = std::min(num_rows, guess_max); auto guess_step = guess_num > 0 ? num_rows / guess_num : 0; cpp11::writable::list my_collectors(col_types_std["cols"]); for (R_xlen_t col = 0; col < num_cols; ++col) { cpp11::writable::list my_collector(my_collectors[col]); std::string my_col_type = cpp11::strings(my_collector.attr("class"))[0]; if (my_col_type == "collector_guess") { cpp11::writable::strings col_vals(guess_num); for (R_xlen_t j = 0; j < guess_num - 1; ++j) { size_t row = j * guess_step; auto str = idx->get(row, col); SET_STRING_ELT( col_vals, j, locale_info->encoder_.makeSEXP(str.begin(), str.end(), true)); } // Always include the last row in the guess if (num_rows > 0 && guess_num - 1 >= 0) { size_t row = num_rows - 1; auto str = idx->get(row, col); col_vals[guess_num - 1] = locale_info->encoder_.makeSEXP(str.begin(), str.end(), true); } auto type = guess_type__(col_vals, na, locale_info.get(), false); auto fun_name = std::string("col_") + type; auto col_type = vroom[fun_name.c_str()]; my_collectors[col] = col_type(); } } return {col_types_std, altrep}; } vroom/src/cpp11.cpp0000644000176200001440000003370514151153616013646 0ustar liggesusers// Generated by cpp11: do not edit by hand // clang-format off #include "vroom_types.h" #include "cpp11/declarations.hpp" #include // altrep.cc void force_materialization(SEXP x); extern "C" SEXP _vroom_force_materialization(SEXP x) { BEGIN_CPP11 force_materialization(cpp11::as_cpp>(x)); return R_NilValue; END_CPP11 } // altrep.cc SEXP vroom_materialize(SEXP x, bool replace); extern "C" SEXP _vroom_vroom_materialize(SEXP x, SEXP replace) { BEGIN_CPP11 return cpp11::as_sexp(vroom_materialize(cpp11::as_cpp>(x), cpp11::as_cpp>(replace))); END_CPP11 } // altrep.cc SEXP vroom_convert(SEXP x); extern "C" SEXP _vroom_vroom_convert(SEXP x) { BEGIN_CPP11 return cpp11::as_sexp(vroom_convert(cpp11::as_cpp>(x))); END_CPP11 } // altrep.cc std::string vroom_str_(const cpp11::sexp& x); extern "C" SEXP _vroom_vroom_str_(SEXP x) { BEGIN_CPP11 return cpp11::as_sexp(vroom_str_(cpp11::as_cpp>(x))); END_CPP11 } // gen.cc cpp11::strings gen_character_(int n, int min, int max, std::string values, uint32_t seed, uint32_t seed2); extern "C" SEXP _vroom_gen_character_(SEXP n, SEXP min, SEXP max, SEXP values, SEXP seed, SEXP seed2) { BEGIN_CPP11 return cpp11::as_sexp(gen_character_(cpp11::as_cpp>(n), cpp11::as_cpp>(min), cpp11::as_cpp>(max), cpp11::as_cpp>(values), cpp11::as_cpp>(seed), cpp11::as_cpp>(seed2))); END_CPP11 } // guess_type.cc std::string guess_type_(cpp11::writable::strings input, const cpp11::strings& na, const cpp11::list& locale, bool guess_integer); extern "C" SEXP _vroom_guess_type_(SEXP input, SEXP na, SEXP locale, SEXP guess_integer) { BEGIN_CPP11 return cpp11::as_sexp(guess_type_(cpp11::as_cpp>(input), cpp11::as_cpp>(na), cpp11::as_cpp>(locale), cpp11::as_cpp>(guess_integer))); END_CPP11 } // iconv_file.cc size_t convert_connection(SEXP in_con, SEXP out_con, const std::string& from, const std::string& to); extern "C" SEXP _vroom_convert_connection(SEXP in_con, SEXP out_con, SEXP from, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(convert_connection(cpp11::as_cpp>(in_con), cpp11::as_cpp>(out_con), cpp11::as_cpp>(from), cpp11::as_cpp>(to))); END_CPP11 } // vroom_dttm.cc cpp11::writable::doubles utctime_(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& min, const cpp11::integers& sec, const cpp11::doubles& psec); extern "C" SEXP _vroom_utctime_(SEXP year, SEXP month, SEXP day, SEXP hour, SEXP min, SEXP sec, SEXP psec) { BEGIN_CPP11 return cpp11::as_sexp(utctime_(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day), cpp11::as_cpp>(hour), cpp11::as_cpp>(min), cpp11::as_cpp>(sec), cpp11::as_cpp>(psec))); END_CPP11 } // vroom_errors.cpp cpp11::data_frame vroom_errors_(cpp11::external_pointer> errors); extern "C" SEXP _vroom_vroom_errors_(SEXP errors) { BEGIN_CPP11 return cpp11::as_sexp(vroom_errors_(cpp11::as_cpp>>>(errors))); END_CPP11 } // vroom_fwf.cc cpp11::list vroom_fwf_(const cpp11::list& inputs, const std::vector& col_starts, const std::vector& col_ends, bool trim_ws, cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, size_t skip, const char* comment, bool skip_empty_rows, ptrdiff_t n_max, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep, bool progress); extern "C" SEXP _vroom_vroom_fwf_(SEXP inputs, SEXP col_starts, SEXP col_ends, SEXP trim_ws, SEXP col_names, SEXP col_types, SEXP col_select, SEXP name_repair, SEXP skip, SEXP comment, SEXP skip_empty_rows, SEXP n_max, SEXP id, SEXP na, SEXP locale, SEXP guess_max, SEXP num_threads, SEXP altrep, SEXP progress) { BEGIN_CPP11 return cpp11::as_sexp(vroom_fwf_(cpp11::as_cpp>(inputs), cpp11::as_cpp&>>(col_starts), cpp11::as_cpp&>>(col_ends), cpp11::as_cpp>(trim_ws), cpp11::as_cpp>(col_names), cpp11::as_cpp>(col_types), cpp11::as_cpp>(col_select), cpp11::as_cpp>(name_repair), cpp11::as_cpp>(skip), cpp11::as_cpp>(comment), cpp11::as_cpp>(skip_empty_rows), cpp11::as_cpp>(n_max), cpp11::as_cpp>(id), cpp11::as_cpp>(na), cpp11::as_cpp>(locale), cpp11::as_cpp>(guess_max), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(altrep), cpp11::as_cpp>(progress))); END_CPP11 } // vroom_fwf.cc cpp11::list whitespace_columns_(const std::string& filename, size_t skip, ptrdiff_t n, const std::string& comment); extern "C" SEXP _vroom_whitespace_columns_(SEXP filename, SEXP skip, SEXP n, SEXP comment) { BEGIN_CPP11 return cpp11::as_sexp(whitespace_columns_(cpp11::as_cpp>(filename), cpp11::as_cpp>(skip), cpp11::as_cpp>(n), cpp11::as_cpp>(comment))); END_CPP11 } // vroom_write.cc void vroom_write_(const cpp11::list& input, const std::string& filename, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines); extern "C" SEXP _vroom_vroom_write_(SEXP input, SEXP filename, SEXP delim, SEXP eol, SEXP na_str, SEXP col_names, SEXP append, SEXP options, SEXP num_threads, SEXP progress, SEXP buf_lines) { BEGIN_CPP11 vroom_write_(cpp11::as_cpp>(input), cpp11::as_cpp>(filename), cpp11::as_cpp>(delim), cpp11::as_cpp>(eol), cpp11::as_cpp>(na_str), cpp11::as_cpp>(col_names), cpp11::as_cpp>(append), cpp11::as_cpp>(options), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(progress), cpp11::as_cpp>(buf_lines)); return R_NilValue; END_CPP11 } // vroom_write.cc void vroom_write_connection_(const cpp11::list& input, const cpp11::sexp& con, const char delim, const std::string& eol, const char* na_str, bool col_names, size_t options, size_t num_threads, bool progress, size_t buf_lines, bool is_stdout, bool append); extern "C" SEXP _vroom_vroom_write_connection_(SEXP input, SEXP con, SEXP delim, SEXP eol, SEXP na_str, SEXP col_names, SEXP options, SEXP num_threads, SEXP progress, SEXP buf_lines, SEXP is_stdout, SEXP append) { BEGIN_CPP11 vroom_write_connection_(cpp11::as_cpp>(input), cpp11::as_cpp>(con), cpp11::as_cpp>(delim), cpp11::as_cpp>(eol), cpp11::as_cpp>(na_str), cpp11::as_cpp>(col_names), cpp11::as_cpp>(options), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(progress), cpp11::as_cpp>(buf_lines), cpp11::as_cpp>(is_stdout), cpp11::as_cpp>(append)); return R_NilValue; END_CPP11 } // vroom_write.cc cpp11::strings vroom_format_(const cpp11::list& input, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines); extern "C" SEXP _vroom_vroom_format_(SEXP input, SEXP delim, SEXP eol, SEXP na_str, SEXP col_names, SEXP append, SEXP options, SEXP num_threads, SEXP progress, SEXP buf_lines) { BEGIN_CPP11 return cpp11::as_sexp(vroom_format_(cpp11::as_cpp>(input), cpp11::as_cpp>(delim), cpp11::as_cpp>(eol), cpp11::as_cpp>(na_str), cpp11::as_cpp>(col_names), cpp11::as_cpp>(append), cpp11::as_cpp>(options), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(progress), cpp11::as_cpp>(buf_lines))); END_CPP11 } // vroom.cc SEXP vroom_(const cpp11::list& inputs, SEXP delim, const char quote, bool trim_ws, bool escape_double, bool escape_backslash, const char* comment, const bool skip_empty_rows, size_t skip, ptrdiff_t n_max, bool progress, const cpp11::sexp& col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep); extern "C" SEXP _vroom_vroom_(SEXP inputs, SEXP delim, SEXP quote, SEXP trim_ws, SEXP escape_double, SEXP escape_backslash, SEXP comment, SEXP skip_empty_rows, SEXP skip, SEXP n_max, SEXP progress, SEXP col_names, SEXP col_types, SEXP col_select, SEXP name_repair, SEXP id, SEXP na, SEXP locale, SEXP guess_max, SEXP num_threads, SEXP altrep) { BEGIN_CPP11 return cpp11::as_sexp(vroom_(cpp11::as_cpp>(inputs), cpp11::as_cpp>(delim), cpp11::as_cpp>(quote), cpp11::as_cpp>(trim_ws), cpp11::as_cpp>(escape_double), cpp11::as_cpp>(escape_backslash), cpp11::as_cpp>(comment), cpp11::as_cpp>(skip_empty_rows), cpp11::as_cpp>(skip), cpp11::as_cpp>(n_max), cpp11::as_cpp>(progress), cpp11::as_cpp>(col_names), cpp11::as_cpp>(col_types), cpp11::as_cpp>(col_select), cpp11::as_cpp>(name_repair), cpp11::as_cpp>(id), cpp11::as_cpp>(na), cpp11::as_cpp>(locale), cpp11::as_cpp>(guess_max), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(altrep))); END_CPP11 } // vroom.cc bool has_trailing_newline(const cpp11::strings& filename); extern "C" SEXP _vroom_has_trailing_newline(SEXP filename) { BEGIN_CPP11 return cpp11::as_sexp(has_trailing_newline(cpp11::as_cpp>(filename))); END_CPP11 } // vroom.cc SEXP vroom_rle(const cpp11::integers& input); extern "C" SEXP _vroom_vroom_rle(SEXP input) { BEGIN_CPP11 return cpp11::as_sexp(vroom_rle(cpp11::as_cpp>(input))); END_CPP11 } extern "C" { static const R_CallMethodDef CallEntries[] = { {"_vroom_convert_connection", (DL_FUNC) &_vroom_convert_connection, 4}, {"_vroom_force_materialization", (DL_FUNC) &_vroom_force_materialization, 1}, {"_vroom_gen_character_", (DL_FUNC) &_vroom_gen_character_, 6}, {"_vroom_guess_type_", (DL_FUNC) &_vroom_guess_type_, 4}, {"_vroom_has_trailing_newline", (DL_FUNC) &_vroom_has_trailing_newline, 1}, {"_vroom_utctime_", (DL_FUNC) &_vroom_utctime_, 7}, {"_vroom_vroom_", (DL_FUNC) &_vroom_vroom_, 21}, {"_vroom_vroom_convert", (DL_FUNC) &_vroom_vroom_convert, 1}, {"_vroom_vroom_errors_", (DL_FUNC) &_vroom_vroom_errors_, 1}, {"_vroom_vroom_format_", (DL_FUNC) &_vroom_vroom_format_, 10}, {"_vroom_vroom_fwf_", (DL_FUNC) &_vroom_vroom_fwf_, 19}, {"_vroom_vroom_materialize", (DL_FUNC) &_vroom_vroom_materialize, 2}, {"_vroom_vroom_rle", (DL_FUNC) &_vroom_vroom_rle, 1}, {"_vroom_vroom_str_", (DL_FUNC) &_vroom_vroom_str_, 1}, {"_vroom_vroom_write_", (DL_FUNC) &_vroom_vroom_write_, 11}, {"_vroom_vroom_write_connection_", (DL_FUNC) &_vroom_vroom_write_connection_, 12}, {"_vroom_whitespace_columns_", (DL_FUNC) &_vroom_whitespace_columns_, 4}, {NULL, NULL, 0} }; } void init_vroom_big_int(DllInfo* dll); void init_vroom_chr(DllInfo* dll); void init_vroom_date(DllInfo* dll); void init_vroom_dbl(DllInfo* dll); void init_vroom_dttm(DllInfo* dll); void init_vroom_fct(DllInfo* dll); void init_vroom_int(DllInfo* dll); void init_vroom_num(DllInfo* dll); void init_vroom_rle(DllInfo* dll); void init_vroom_time(DllInfo* dll); extern "C" attribute_visible void R_init_vroom(DllInfo* dll){ R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); init_vroom_big_int(dll); init_vroom_chr(dll); init_vroom_date(dll); init_vroom_dbl(dll); init_vroom_dttm(dll); init_vroom_fct(dll); init_vroom_int(dll); init_vroom_num(dll); init_vroom_rle(dll); init_vroom_time(dll); R_forceSymbols(dll, TRUE); } vroom/src/tzfile.h0000644000176200001440000001303413746062417013664 0ustar liggesusers#ifndef TZFILE_H #define TZFILE_H /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ /* ** This header is for use ONLY with the time conversion code. ** There is no guarantee that it will remain unchanged, ** or that it will remain at all. ** Do NOT copy it to any system include directory. ** Thank you! */ /* ** Information about time zone files. */ #ifndef TZDIR #define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */ #endif /* !defined TZDIR */ #ifndef TZDEFAULT #define TZDEFAULT "UTC" // needs to be a valid timezone, PR#16503 #endif /* !defined TZDEFAULT */ /* We don't ship posixrules, which is usually a link to a USA timezeone. So choose one instead. */ #ifndef TZDEFRULES #define TZDEFRULES "America/New_York" #endif /* !defined TZDEFRULES */ /* ** Each file begins with. . . */ #define TZ_MAGIC "TZif" struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ char tzh_reserved[15]; /* reserved--must be zero */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ char tzh_timecnt[4]; /* coded number of transition times */ char tzh_typecnt[4]; /* coded number of local time types */ char tzh_charcnt[4]; /* coded number of abbr. chars */ }; /* ** . . .followed by. . . ** ** tzh_timecnt (char [4])s coded transition times a la time(2) ** tzh_timecnt (unsigned char)s types of local time starting at above ** tzh_typecnt repetitions of ** one (char [4]) coded UT offset in seconds ** one (unsigned char) used to set tm_isdst ** one (unsigned char) that's an abbreviation list index ** tzh_charcnt (char)s '\0'-terminated zone abbreviations ** tzh_leapcnt repetitions of ** one (char [4]) coded leap second transition times ** one (char [4]) total correction after above ** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition ** time is standard time, if FALSE, ** transition time is wall clock time ** if absent, transition times are ** assumed to be wall clock time ** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition ** time is UT, if FALSE, ** transition time is local time ** if absent, transition times are ** assumed to be local time */ /* ** If tzh_version is '2' or greater, the above is followed by a second instance ** of tzhead and a second instance of the data in which each coded transition ** time uses 8 rather than 4 chars, ** then a POSIX-TZ-environment-variable-style string for use in handling ** instants after the last transition time stored in the file ** (with nothing between the newlines if there is no POSIX representation for ** such instants). ** ** If tz_version is '3' or greater, the above is extended as follows. ** First, the POSIX TZ string's hour offset may range from -167 ** through 167 as compared to the POSIX-required 0 through 24. ** Second, its DST start time may be January 1 at 00:00 and its stop ** time December 31 at 24:00 plus the difference between DST and ** standard time, indicating DST all year. */ /* ** In the current implementation, "tzset()" refuses to deal with files that ** exceed any of the limits below. */ #ifndef TZ_MAX_TIMES #define TZ_MAX_TIMES 1200 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES #ifndef NOSOLAR #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined NOSOLAR */ #ifdef NOSOLAR /* ** Must be at least 14 for Europe/Riga as of Jan 12 1995, ** as noted by Earl Chew. */ #define TZ_MAX_TYPES 20 /* Maximum number of local time types */ #endif /* !defined NOSOLAR */ #endif /* !defined TZ_MAX_TYPES */ // increased from 50, http://mm.icann.org/pipermail/tz/2015-August/022623.html #ifndef TZ_MAX_CHARS #define TZ_MAX_CHARS 100 /* Maximum number of abbreviation characters */ /* (limited by what unsigned chars can hold) */ #endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */ #define SECSPERMIN 60 #define MINSPERHOUR 60 #define HOURSPERDAY 24 #define DAYSPERWEEK 7 #define DAYSPERNYEAR 365 #define DAYSPERLYEAR 366 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) #define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY) #define MONSPERYEAR 12 #define TM_SUNDAY 0 #define TM_MONDAY 1 #define TM_TUESDAY 2 #define TM_WEDNESDAY 3 #define TM_THURSDAY 4 #define TM_FRIDAY 5 #define TM_SATURDAY 6 #define TM_JANUARY 0 #define TM_FEBRUARY 1 #define TM_MARCH 2 #define TM_APRIL 3 #define TM_MAY 4 #define TM_JUNE 5 #define TM_JULY 6 #define TM_AUGUST 7 #define TM_SEPTEMBER 8 #define TM_OCTOBER 9 #define TM_NOVEMBER 10 #define TM_DECEMBER 11 #define TM_YEAR_BASE 1900 #define EPOCH_YEAR 1970 #define EPOCH_WDAY TM_THURSDAY #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) /* ** Since everything in isleap is modulo 400 (or a factor of 400), we know that ** isleap(y) == isleap(y % 400) ** and so ** isleap(a + b) == isleap((a + b) % 400) ** or ** isleap(a + b) == isleap(a % 400 + b % 400) ** This is true even if % means modulo rather than Fortran remainder ** (which is allowed by C89 but not C99). ** We use this to avoid addition overflow problems. */ #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #endif /* !defined TZFILE_H */ vroom/src/altrep.h0000644000176200001440000000144614142512075013651 0ustar liggesusers#include #include #if R_VERSION >= R_Version(3, 5, 0) #define HAS_ALTREP #endif #ifdef HAS_ALTREP #if R_VERSION < R_Version(3, 6, 0) // workaround because R's not so conveniently uses `class` // as a variable name, and C++ is not happy about that // // SEXP R_new_altrep(R_altrep_class_t class, SEXP data1, SEXP data2); // // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wkeyword-macro" #define class klass # pragma clang diagnostic pop #else #define class klass #endif // clang-format on // Because functions declared in have C linkage extern "C" { #include } // undo the workaround #undef class #else extern "C" { #include } #endif #endif vroom/src/Iconv.cpp0000644000176200001440000000463014142515724013775 0ustar liggesusers#include "Iconv.h" #include #include Iconv::Iconv(const std::string& from, const std::string& to) { if (from == "UTF-8") { cd_ = nullptr; } else { cd_ = Riconv_open(to.c_str(), from.c_str()); if (cd_ == (void*)-1) { if (errno == EINVAL) { cpp11::stop("Can't convert from %s to %s", from.c_str(), to.c_str()); } else { cpp11::stop("Iconv initialisation failed"); } } // Allocate space in buffer buffer_.resize(1024); } } Iconv::~Iconv() { if (cd_ != nullptr) { Riconv_close(cd_); cd_ = nullptr; } } size_t Iconv::convert(const char* start, const char* end) { size_t n = end - start; // Ensure buffer is big enough: one input byte can never generate // more than 4 output bytes size_t max_size = n * 4; if (buffer_.size() < max_size) buffer_.resize(max_size); char* outbuf = &buffer_[0]; size_t inbytesleft = n, outbytesleft = max_size; size_t res = Riconv(cd_, &start, &inbytesleft, &outbuf, &outbytesleft); if (res == (size_t)-1) { switch (errno) { case EILSEQ: cpp11::stop("Invalid multibyte sequence"); case EINVAL: cpp11::stop("Incomplete multibyte sequence"); case E2BIG: cpp11::stop("Iconv buffer too small"); default: cpp11::stop("Iconv failed to convert for unknown reason"); } } return max_size - outbytesleft; } int my_strnlen(const char* s, int maxlen) { for (int n = 0; n < maxlen; ++n) { if (s[n] == '\0') return n; } return maxlen; } #if defined(__sun) #define readr_strnlen my_strnlen #else #define readr_strnlen strnlen #endif // To be safe, we need to check for nulls - this also needs to emit // a warning, but this behaviour is better than crashing SEXP safeMakeChar(const char* start, size_t n, bool hasNull) { size_t m = hasNull ? readr_strnlen(start, n) : n; if (m > INT_MAX) { Rf_error("R character strings are limited to 2^31-1 bytes"); } return Rf_mkCharLenCE(start, m, CE_UTF8); } SEXP Iconv::makeSEXP(const char* start, const char* end, bool hasNull) { if (cd_ == nullptr) return safeMakeChar(start, end - start, hasNull); int n = convert(start, end); return safeMakeChar(&buffer_[0], n, hasNull); } std::string Iconv::makeString(const char* start, const char* end) { if (cd_ == nullptr) return std::string(start, end); int n = convert(start, end); return std::string(&buffer_[0], n); } vroom/src/vroom_rle.cc0000644000176200001440000000030014142512075014510 0ustar liggesusers#include "vroom_rle.h" #ifdef HAS_ALTREP R_altrep_class_t vroom_rle::class_t; void init_vroom_rle(DllInfo* dll) { vroom_rle::Init(dll); } #else void init_vroom_rle(DllInfo* dll) {} #endif vroom/src/vroom_dttm.cc0000644000176200001440000000522614142515724014716 0ustar liggesusers#include "vroom_dttm.h" double parse_dttm( const char* begin, const char* end, DateTimeParser& parser, const std::string& format) { if (format == "%s") { double out; bool ok = parseDouble('.', begin, end, out); if (!ok) { return NA_REAL; } return out; } parser.setDate(begin, end); bool res = (format == "") ? parser.parseISO8601() : parser.parse(format); if (res) { DateTime dt = parser.makeDateTime(); if (dt.validDateTime()) { return dt.datetime(); } } return NA_REAL; } cpp11::doubles read_dttm(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); auto err_msg = info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->format; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; DateTimeParser parser(info->locale.get()); auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_dttm(begin, end, parser, info->format); }, info->errors, err_msg.c_str(), *info->na); } }, info->num_threads, true); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); out.attr("class") = {"POSIXct", "POSIXt"}; out.attr("tzone") = info->locale->tz_; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_dttm::class_t; void init_vroom_dttm(DllInfo* dll) { vroom_dttm::Init(dll); } #else void init_vroom_dttm(DllInfo* dll) {} #endif [[cpp11::register]] cpp11::writable::doubles utctime_( const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& min, const cpp11::integers& sec, const cpp11::doubles& psec) { int n = year.size(); if (month.size() != n || day.size() != n || hour.size() != n || min.size() != n || sec.size() != n || psec.size() != n) { cpp11::stop("All inputs must be same length"); } cpp11::writable::doubles out(n); for (int i = 0; i < n; ++i) { DateTime dt( year[i], month[i], day[i], hour[i], min[i], sec[i], psec[i], "UTC"); out[i] = dt.datetime(); } out.attr("class") = {"POSIXct", "POSIXt"}; out.attr("tzone") = "UTC"; return out; } vroom/vignettes/0000755000176200001440000000000014151250642013424 5ustar liggesusersvroom/vignettes/vroom.Rmd0000644000176200001440000002746614142515724015256 0ustar liggesusers--- title: "Get started with vroom" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Get started with vroom} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_knit$set(root.dir = tempdir()) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(tibble.print_min = 3) ``` The vroom package contains one main function `vroom()` which is used to read all types of delimited files. A delimited file is any file in which the data is separated (delimited) by one or more characters. The most common type of delimited files are CSV (Comma Separated Values) or TSV (Tab Separated Values) files, typically these files have a `.csv` and `.tsv` suffix respectively. ```{r} library(vroom) ``` This vignette covers the following topics: - The basics of reading files, including - single files - multiple files - compressed files - remote files - Skipping particular columns. - Specifying column types, for additional safety and when the automatic guessing fails. - Writing regular and compressed files ## Reading files To read a CSV, or other type of delimited file with vroom pass the file to `vroom()`. The delimiter will be automatically guessed if it is a common delimiter; e.g. ("," "\t" " " "|" ":" ";"). If the guessing fails or you are using a less common delimiter specify it with the `delim` parameter. (e.g. `delim = ","`). We have included an example CSV file in the vroom package for use in examples and tests. Access it with `vroom_example("mtcars.csv")` ```{r} # See where the example file is stored on your machine file <- vroom_example("mtcars.csv") file # Read the file, by default vroom will guess the delimiter automatically. vroom(file) # You can also specify it explicitly, which is (slightly) faster, and safer if # you know how the file is delimited. vroom(file, delim = ",") ``` ## Reading multiple files If you are reading a set of files which all have the same columns, you can pass the filenames directly to `vroom()` and it will combine them into one result. First we will create some files to read by splitting the mtcars dataset by number of cylinders, (it is OK if you don't currently understand this code). ```{r} mt <- tibble::rownames_to_column(mtcars, "model") purrr::iwalk( split(mt, mt$cyl), ~ vroom_write(.x, glue::glue("mtcars_{.y}.csv"), "\t") ) ``` Then we can efficiently read them into one table by passing the filenames directly to vroom. ```{r} files <- fs::dir_ls(glob = "mtcars*csv") files vroom(files) ``` Often the filename or directory where the files are stored contains information. The `id` parameter can be used to add an extra column to the result with the full path to each file. (in this case we name the column `path`). ```{r} vroom(files, id = "path") ``` ```{r, include = FALSE} # just to clear .Last.value 1 + 1 gc() unlink(files) ``` ## Reading compressed files vroom supports reading zip, gz, bz2 and xz compressed files automatically, just pass the filename of the compressed file to vroom. ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file) ``` `vroom()` decompresses, indexes and writes the decompressed data to a file in the temp directory in a single stream. The temporary file is used to lazily look up the values and will be automatically cleaned up when all values in the object have been fully read, the object is removed, or the R session ends. ### Reading single files from multiple multi-file zip archives If you are reading a zip file that contains multiple files with the same format, you can use a wrapper function like this: ```{r} read_all_zip <- function(file, ...) { filenames <- unzip(file, list = TRUE)$Name vroom(purrr::map(filenames, ~ unz(file, .x)), ...) } ``` ## Reading remote files vroom can read files directly from the internet as well by passing the URL of the file to vroom. ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv" vroom(file) ``` It can even read gzipped files from the internet (although not the other compressed formats). ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv.gz" vroom(file) ``` ## Column selection vroom provides the same interface for column selection and renaming as [dplyr::select()](https://dplyr.tidyverse.org/reference/select.html). This provides very efficient, flexible and readable selections. For example you can select by: - A character vector of column names ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file, col_select = c(model, cyl, gear)) ``` - A numeric vector of column indexes, e.g. `c(1, 2, 5)` ```{r} vroom(file, col_select = c(1, 3, 11)) ``` - Using the selection helpers such as `starts_with()` and `ends_with()` ```{r} vroom(file, col_select = starts_with("d")) ``` - You can also rename columns ```{r} vroom(file, col_select = list(car = model, everything())) ``` ## Reading fixed width files A fixed width file can be a very compact representation of numeric data. Unfortunately, it's also often painful to read because you need to describe the length of every field. vroom aims to make it as easy as possible by providing a number of different ways to describe the field structure. Use `vroom_fwf()` in conjunction with one of the following helper functions to read the file. ```{r} fwf_sample <- vroom_example("fwf-sample.txt") cat(readLines(fwf_sample)) ``` - `fwf_empty()` - Guess based on the position of empty columns. ```{r} vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) ``` - `fwf_widths()` - Use user provided set of field widths. ```{r} vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) ``` - `fwf_positions()` - Use user provided sets of start and end positions. ```{r} vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) ``` - `fwf_cols()` - Use user provided named widths. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) ``` - `fwf_cols()` - Use user provided named pairs of positions. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) ``` ## Column types vroom guesses the data types of columns as they are read, however sometimes the guessing fails and it is necessary to explicitly set the type of one or more columns. The available specifications are: (with single letter abbreviations in quotes) * `col_logical()` 'l', containing only `T`, `F`, `TRUE`, `FALSE`, `1` or `0`. * `col_integer()` 'i', integer values. * `col_big_integer()` 'I', Big integer values. (64bit integers) * `col_double()` 'd', floating point values. * `col_number()` 'n', numbers containing the `grouping_mark` * `col_date(format = "")` 'D': with the locale's `date_format`. * `col_time(format = "")` 't': with the locale's `time_format`. * `col_datetime(format = "")` 'T': ISO8601 date times. * `col_factor(levels, ordered)` 'f', a fixed set of values. * `col_character()` 'c', everything else. * `col_skip()` '_, -', don't import this column. * `col_guess()` '?', parse using the "best" type based on the input. You can tell vroom what columns to use with the `col_types()` argument in a number of ways. If you only need to override a single column the most concise way is to use a named vector. ```{r} # read the 'hp' columns as an integer vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i")) # also skip reading the 'cyl' column vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_")) # also read the gears as a factor vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f")) ``` You can read all the columns with the same type, by using the `.default` argument. For example reading everything as a character. ```{r} vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c")) ``` However you can also use the `col_*()` functions in a list. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor()) ) ``` This is most useful when a column type needs additional information, such as for categorical data when you know all of the levels of a factor. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5")))) ) ``` ## Name repair Often the names of columns in the original dataset are not ideal to work with. `vroom()` uses the same `.name_repair` argument as tibble, so you can use one of the default name repair strategies or provide a custom function. A great approach is to use the [`janitor::make_clean_names()`](http://sfirke.github.io/janitor/reference/make_clean_names.html) function as the input. This will automatically clean the names to use whatever case you specify, here I am setting it to use `ALLCAPS` names. ```{r, eval = FALSE} vroom( vroom_example("mtcars.csv"), .name_repair = ~ janitor::make_clean_names(., case = "all_caps") ) ``` ## Writing delimited files Use `vroom_write()` to write delimited files, the default delimiter is tab, to write TSV files. Writing to TSV by default has the following benefits: - Avoids the issue of whether to use `;` (common in Europe) or `,` (common in the US) - Unlikely to require quoting in fields, as very few fields contain tabs - More easily and efficiently ingested by Unix command line tools such as `cut`, `perl` and `awk`. ```{r} vroom_write(mtcars, "mtcars.tsv") ``` ```{r, include = FALSE} unlink("mtcars.tsv") ``` ### Writing CSV delimited files However you can also use `delim = ','` to write CSV files, which are common as inputs to GUI spreadsheet tools like Excel or Google Sheets. ```{r} vroom_write(mtcars, "mtcars.csv", delim = ",") ``` ```{r, include = FALSE} unlink("mtcars.csv") ``` ### Writing compressed files For gzip, bzip2 and xz compression the outputs will be automatically compressed if the filename ends in `.gz`, `.bz2` or `.xz`. ```{r} vroom_write(mtcars, "mtcars.tsv.gz") vroom_write(mtcars, "mtcars.tsv.bz2") vroom_write(mtcars, "mtcars.tsv.xz") ``` ```{r, include = FALSE} unlink(c("mtcars.tsv.gz", "mtcars.tsv.bz2", "mtcars.tsv.xz")) ``` It is also possible to use other compressors by using `pipe()` with `vroom_write()` to create a pipe connection to command line utilities, such as - [pigz](https://zlib.net/pigz/), a parallel gzip implementation - lbzip2, a parallel bzip2 implementation - [pixz](https://github.com/vasi/pixz), a parallel xz implementation - [Zstandard](https://facebook.github.io/zstd/), a modern real-time compression algorithm. The parallel compression versions can be considerably faster for large output files and generally `vroom_write()` is fast enough that the compression speed becomes the bottleneck when writing. ```{r, eval = nzchar(Sys.which("pigz"))} vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz")) ``` ```{r, include = FALSE} unlink("mtcars.tsv.gz") ``` ### Reading and writing from standard input and output vroom supports reading and writing to the C-level `stdin` and `stdout` of the R process by using `stdin()` and `stdout()`. E.g. from a shell prompt you can pipe to and from vroom directly. ```shell cat inst/extdata/mtcars.csv | Rscript -e 'vroom::vroom(stdin())' Rscript -e 'vroom::vroom_write(iris, stdout())' | head ``` Note this interpretation of `stdin()` and `stdout()` differs from that used elsewhere by R, however we believe it better matches most user's expectations for this use case. ## Further reading - `vignette("benchmarks")` discusses the performance of vroom, how it compares to alternatives and how it achieves its results. - [πŸ“½ vroom: Because Life is too short to read slow](https://www.youtube.com/watch?v=RA9AjqZXxMU&t=10s) - Presentation of vroom at UseR!2019 ([slides](https://speakerdeck.com/jimhester/vroom)) - [πŸ“Ή vroom: Read and write rectangular data quickly](https://www.youtube.com/watch?v=ZP_y5eaAc60) - a video tour of the vroom features. vroom/vignettes/benchmarks.Rmd0000644000176200001440000003451114142553112016207 0ustar liggesusers--- title: "Vroom Benchmarks" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Vroom Benchmarks} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(ggplot2) library(forcats) library(dplyr) library(tidyr) library(fs) pretty_sec <- function(x) { x[!is.na(x)] <- prettyunits::pretty_sec(x[!is.na(x)]) x } pretty_lgl <- function(x) { case_when( x == TRUE ~ "TRUE", x == FALSE ~ "FALSE", TRUE ~ "" ) } read_benchmark <- function(file, desc) { vroom::vroom(file, col_types = c("ccccddddd")) %>% filter(op != "setup") %>% mutate( altrep = case_when( grepl("^vroom_no_altrep", reading_package) ~ FALSE, grepl("^vroom", reading_package) ~ TRUE, TRUE ~ NA ), reading_package = case_when( grepl("^vroom", reading_package) ~ "vroom", TRUE ~ reading_package ), label = fct_reorder( glue::glue("{reading_package}{altrep}\n{manip_package}", altrep = ifelse(is.na(altrep), "", glue::glue("(altrep = {altrep})")) ), case_when(type == "real" ~ time, TRUE ~ 0), sum), op = factor(op, desc) ) } generate_subtitle <- function(data) { rows <- scales::comma(data$rows[[1]]) cols <- scales::comma(data$cols[[1]]) size <- fs_bytes(data$size[[1]]) glue::glue("{rows} x {cols} - {size}B") } plot_benchmark <- function(data, title) { subtitle <- generate_subtitle(data) data <- data %>% filter(reading_package != "read.delim", type == "real") p1 <- data %>% ggplot() + geom_bar(aes(x = label, y = time, fill = op, group = label), stat = "identity") + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + coord_flip() + labs(title = title, subtitle = subtitle, x = NULL, y = NULL, fill = NULL) + theme(legend.position = "bottom") p2 <- data %>% group_by(label) %>% summarise(max_memory = max(max_memory)) %>% ggplot() + geom_bar(aes(x = label, y = max_memory / (1024 * 1024)), stat = "identity") + scale_y_continuous(labels = scales::number_format(suffix = "Mb")) + coord_flip() + labs(title = "Maximum memory usage", x = NULL, y = NULL) + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) library(patchwork) p1 + p2 + plot_layout(widths = c(2, 1)) } make_table <- function(data) { data %>% filter(type == "real") %>% select(-label, -size, -type, -rows, -cols) %>% spread(op, time) %>% mutate( total = read + print + head + tail + sample + filter + aggregate, max_memory = as.character(bench::as_bench_bytes(max_memory)) ) %>% arrange(desc(total)) %>% mutate_if(is.numeric, pretty_sec) %>% mutate_if(is.logical, pretty_lgl) %>% select(reading_package, manip_package, altrep, max_memory, everything()) %>% rename( "reading\npackage" = reading_package, "manipulating\npackage" = manip_package, memory = max_memory ) %>% knitr::kable(digits = 2, align = "r", format = "html") } desc <- c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate") ``` vroom is a new approach to reading delimited and fixed width data into R. It stems from the observation that when parsing files reading data from disk and finding the delimiters is generally not the main bottle neck. Instead (re)-allocating memory and parsing the values into R data types (particularly for characters) takes the bulk of the time. Therefore you can obtain very rapid input by first performing a fast indexing step and then using the Altrep framework available in R versions 3.5+ to access the values in a lazy / delayed fashion. ## How it works The initial reading of the file simply records the locations of each individual record, the actual values are not read into R. Altrep vectors are created for each column in the data which hold a pointer to the index and the memory mapped file. When these vectors are indexed the value is read from the memory mapping. This means initial reading is extremely fast, in the real world dataset below it is ~ 1/4 the time of the multi-threaded `data.table::fread()`. Sampling operations are likewise extremely fast, as only the data actually included in the sample is read. This means things like the tibble print method, calling `head()`, `tail()` `x[sample(), ]` etc. have very low overhead. Filtering also can be fast, only the columns included in the filter selection have to be fully read and only the data in the filtered rows needs to be read from the remaining columns. Grouped aggregations likewise only need to read the grouping variables and the variables aggregated. Once a particular vector is fully materialized the speed for all subsequent operations should be identical to a normal R vector. This approach potentially also allows you to work with data that is larger than memory. As long as you are careful to avoid materializing the entire dataset at once it can be efficiently queried and subset. # Reading delimited files The following benchmarks all measure reading delimited files of various sizes and data types. Because vroom delays reading the benchmarks also do some manipulation of the data afterwards to try and provide a more realistic performance comparison. Because the `read.delim` results are so much slower than the others they are excluded from the plots, but are retained in the tables. ## Taxi Trip Dataset This real world dataset is from Freedom of Information Law (FOIL) Taxi Trip Data from the NYC Taxi and Limousine Commission 2013, originally posted at . It is also hosted on [archive.org](https://archive.org/details/nycTaxiTripData2013). The first table trip_fare_1.csv is 1.55G in size. #> Observations: 14,776,615 #> Variables: 11 #> $ medallion "89D227B655E5C82AECF13C3F540D4CF4", "0BD7C8F5B... #> $ hack_license "BA96DE419E711691B9445D6A6307C170", "9FD8F69F0... #> $ vendor_id "CMT", "CMT", "CMT", "CMT", "CMT", "CMT", "CMT... #> $ pickup_datetime "2013-01-01 15:11:48", "2013-01-06 00:18:35", ... #> $ payment_type "CSH", "CSH", "CSH", "CSH", "CSH", "CSH", "CSH... #> $ fare_amount 6.5, 6.0, 5.5, 5.0, 9.5, 9.5, 6.0, 34.0, 5.5, ... #> $ surcharge 0.0, 0.5, 1.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 0... #> $ mta_tax 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0... #> $ tip_amount 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0... #> $ tolls_amount 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.8, 0.0, 0... #> $ total_amount 7.0, 7.0, 7.0, 6.0, 10.5, 10.0, 6.5, 39.3, 7.0... ### Taxi Benchmarks code: [bench/taxi](https://github.com/r-lib/vroom/tree/main/inst/bench/taxi) All benchmarks were run on a Amazon EC2 [m5.4xlarge](https://aws.amazon.com/ec2/instance-types/m5/) instance with 16 vCPUs and an [EBS](https://aws.amazon.com/ebs/) volume type. The benchmarks labeled `vroom_base` uses `vroom` with base functions for manipulation. `vroom_dplyr` uses `vroom` to read the file and dplyr functions to manipulate. `data.table` uses `fread()` to read the file and `data.table` functions to manipulate and `readr` uses `readr` to read the file and `dplyr` to manipulate. By default vroom only uses Altrep for character vectors, these are labeled `vroom(altrep: normal)`. The benchmarks labeled `vroom(altrep: full)` instead use Altrep vectors for all supported types and `vroom(altrep: none)` disable Altrep entirely. The following operations are performed. - The data is read - `print()` - _N.B. read.delim uses `print(head(x, 10))` because printing the whole dataset takes > 10 minutes_ - `head()` - `tail()` - Sampling 100 random rows - Filtering for "UNK" payment, this is 6434 rows (0.0435% of total). - Aggregation of mean fare amount per payment type. ```{r, fig.height = 8, fig.width=10, warning = FALSE, echo = FALSE, message = FALSE} taxi <- read_benchmark(path_package("vroom", "bench", "taxi.tsv"), desc) plot_benchmark(taxi, "Time to analyze taxi trip data") make_table(taxi) ``` (*N.B. Rcpp used in the dplyr implementation fully materializes all the Altrep numeric vectors when using `filter()` or `sample_n()`, which is why the first of these cases have additional overhead when using full Altrep.*). ## All numeric data All numeric data is really a worst case scenario for vroom. The index takes about as much memory as the parsed data. Also because parsing doubles can be done quickly in parallel and text representations of doubles are only ~25 characters at most there isn't a great deal of savings for delayed parsing. For these reasons (and because the data.table implementation is very fast) vroom is a bit slower than fread for pure numeric data. However because vroom is multi-threaded it is a bit quicker than readr and read.delim for this type of data. ### Long code: [bench/all_numeric-long](https://github.com/r-lib/vroom/tree/main/inst/bench/all_numeric-long) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num <- read_benchmark(path_package("vroom", "bench", "all_numeric-long.tsv"), desc) plot_benchmark(all_num, "Time to analyze long all numeric data") make_table(all_num) ``` ### Wide code: [bench/all_numeric-wide](https://github.com/r-lib/vroom/tree/main/inst/bench/all_numeric-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num_wide <- read_benchmark(path_package("bench", "all_numeric-wide.tsv", package = "vroom"), desc) plot_benchmark(all_num_wide, "Time to analyze wide all numeric data") make_table(all_num_wide) ``` ## All character data code: [bench/all_character-long](https://github.com/r-lib/vroom/tree/main/inst/bench/all_character-long) All character data is a best case scenario for vroom when using Altrep, as it takes full advantage of the lazy reading. ### Long ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr <- read_benchmark(path_package("vroom", "bench", "all_character-long.tsv"), desc) plot_benchmark(all_chr, "Time to analyze long all character data") make_table(all_chr) ``` ### Wide code: [bench/all_character-wide](https://github.com/r-lib/vroom/tree/main/inst/bench/all_character-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr_wide <- read_benchmark(path_package("vroom", "bench", "all_character-wide.tsv"), desc) plot_benchmark(all_chr_wide, "Time to analyze wide all character data") make_table(all_chr_wide) ``` # Reading multiple delimited files code: [bench/taxi_multiple](https://github.com/r-lib/vroom/tree/main/inst/bench/taxi_multiple) ```{r, echo = FALSE, message = FALSE, eval = TRUE} mult <- read_benchmark(path_package("vroom", "bench", "taxi_multiple.tsv"), desc) ``` The benchmark reads all 12 files in the taxi trip fare data, totaling `r scales::comma(mult$rows[[1]])` rows and `r mult$cols[[1]]` columns for a total file size of `r format(fs_bytes(mult$size[[1]]))`. ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(mult, "Time to analyze multiple file data") make_table(mult) ``` # Reading fixed width files ## United States Census 5-Percent Public Use Microdata Sample files ```{r, echo = FALSE, message = FALSE, eval = TRUE} fwf <- read_benchmark(path_package("vroom", "bench", "fwf.tsv"), desc) ``` This fixed width dataset contains individual records of the characteristics of a 5 percent sample of people and housing units from the year 2000 and is freely available at . The data is split into files by state, and the state of California was used in this benchmark. The data totals `r scales::comma(fwf$rows[[1]])` rows and `r fwf$cols[[1]]` columns with a total file size of `r format(fs_bytes(fwf$size[[1]]))`. ## Census data benchmarks code: [bench/fwf](https://github.com/r-lib/vroom/tree/main/inst/bench/fwf) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(fwf, "Time to analyze fixed width data") make_table(fwf) ``` # Writing delimited files code: [bench/taxi_writing](https://github.com/r-lib/vroom/tree/main/inst/bench/taxi_writing) The benchmarks write out the taxi trip dataset in a few different ways. - An uncompressed file - A gzip compressed file using `gzfile()` _(readr and vroom do this automatically for files ending in `.gz`)_ - A gzip compressed file compressed with multiple threads (natively for data.table and using a `pipe()` connection to [pigz](https://zlib.net/pigz/) for the rest). - A [Zstandard](https://facebook.github.io/zstd/) compressed file (data.table does not support this format). ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} taxi_writing <- read_benchmark(path_package("vroom", "bench", "taxi_writing.tsv"), c("setup", "writing")) %>% rename( package = reading_package, compression = manip_package ) %>% mutate( package = factor(package, c("base", "readr", "data.table", "vroom")), compression = factor(compression, rev(c("gzip", "multithreaded_gzip", "zstandard", "uncompressed"))) ) %>% filter(type == "real") subtitle <- generate_subtitle(taxi_writing) taxi_writing %>% ggplot(aes(x = compression, y = time, fill = package)) + geom_bar(stat = "identity", position = position_dodge2(reverse = TRUE, padding = .05)) + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + theme(legend.position = "bottom") + coord_flip() + labs(title = "Writing taxi trip data", subtitle = subtitle, x = NULL, y = NULL, fill = NULL) taxi_writing %>% select(-size, -op, -rows, -cols, -type, -altrep, -label, -max_memory) %>% mutate_if(is.numeric, pretty_sec) %>% pivot_wider(names_from = package, values_from = time) %>% arrange(desc(compression)) %>% knitr::kable(digits = 2, align = "r", format = "html") ``` ## Session and package information ```{r, echo = FALSE, warning = FALSE, message = FALSE} si <- vroom::vroom(path_package("vroom", "bench", "session_info.tsv")) class(si) <- c("packages_info", "data.frame") select(as.data.frame(si), package, version = ondiskversion, date, source) %>% knitr::kable() ``` vroom/R/0000755000176200001440000000000014151153615011617 5ustar liggesusersvroom/R/vroom_lines.R0000644000176200001440000000367214142515724014311 0ustar liggesusers#' Read lines from a file #' #' `vroom_lines()` is similar to `readLines()`, however it reads the lines #' lazily like [vroom()], so operations like `length()`, `head()`, `tail()` and `sample()` #' can be done much more efficiently without reading all the data into R. #' @inheritParams vroom #' @examples #' lines <- vroom_lines(vroom_example("mtcars.csv")) #' #' length(lines) #' head(lines, n = 2) #' tail(lines, n = 2) #' sample(lines, size = 2) #' @export vroom_lines <- function(file, n_max = Inf, skip = 0, na = character(), skip_empty_rows = FALSE, locale = default_locale(), altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress()) { if (!rlang::is_missing(altrep_opts)) { lifecycle::deprecate_warn("1.1.0", "vroom_lines(altrep_opts = )", "vroom_lines(altrep = )") altrep <- altrep_opts } file <- standardise_path(file) if (!is_ascii_compatible(locale$encoding)) { file <- reencode_path(file, locale$encoding) locale$encoding <- "UTF-8" } if (n_max < 0 || is.infinite(n_max)) { n_max <- -1 } if (length(file) == 0 || n_max == 0) { return(character()) } col_select <- rlang::quo(NULL) # delim = "\1" sets the delimiter to be start of header, which should never # appear in modern text. This essentially means the only record breaks will # be newlines. Ideally this would be "\0", but R doesn't let you have nulls # in character vectors. out <- vroom_(file, delim = "\1", col_names = "V1", col_types = cols(col_character()), id = NULL, skip = skip, col_select = col_select, name_repair = "minimal", na = na, quote = "", trim_ws = FALSE, escape_double = FALSE, escape_backslash = FALSE, comment = "", skip_empty_rows = skip_empty_rows, locale = locale, guess_max = 0, n_max = n_max, altrep = vroom_altrep(altrep), num_threads = num_threads, progress = progress ) if (length(out) == 0) { return(character()) } out[[1]] } vroom/R/sysdata.rda0000644000176200001440000007513014142512075013764 0ustar liggesusersBZh91AY&SYετN²@υWΰ?πΏπυψwΎ@ΰƒ~#C‹ί]σΓΦΎ€ο»mχ4W^μΠttΩπλ'¬ο»C  ξςw*Φ(ι’Ύνz-›ΎOΌYΩ|+zΞΩnWuΉ:Ϋ»»Ϋ§½Ϊvϋή \=»_X6±{»v‘ΞΒΪΗΊνΧVw»½«yΥοqη½hc-lν΅Ϊφο/vŠΛέΫ#ο}πχgFY―½οwžζφϋοs}n}Χ›‹ΥΨk˜λwuξrυλOvέμ»Χ½ΪΤ»šBΐρ(’ξλšξΪΆvυΪΧ\ΧMWΊφκςnΝ)]w.ο―KvΎž}τ{eν›m{et2lP °¦«Z’…hD–͐χ}»Ρς Ζλ*¬yΌuθ€σ½½«Ϋάτ{Μθ*€(R!EP vΐnޝξκ«N§=ζ6χ½NOf,“vήΐΠ‚ ‘¦@“"hΑš4¦Τ6”π 0™yOSυ2bšhυ<  1Sb)νMͺiνSΕ£ΚzM4!΄ΔΤmPυ4ΠP @€4Π @‚F)ψ„Ÿ i4ž§‘4Ι“L&††šOB2ih&ƒA i¦š $$@€š‘£@MOFšM0‰ν"z2™4¦ΤΖSτ¦G‘4σI4£idς™4$š™4=MOΣSTύ Sj1’!¦€Θ4£!ι@4€@𠦂E ΔΑ2˜ 4Π&SΠSΡ‘3S@ςŒΙ6§©£ΤΝA¦¦ƒΤΙ¦Τ @h2`α‚oΡΒ€lΘaAU ς#€ΐ"¨ΐ 2Š( ¨¨φθθ!Uω^•Υ}εο»sζ~—cσΗέώ½_³ξώΟX/έl_BwΏό©πPύ}=ϊήό―}·αϊGΪό«cΦ%°`}vψβIσUΓSΏΫσ›ρ^υήS™ύD…T>%­°ύ«Ž‰]…Υ@Ζ_ηίωvW4ώ½6ό΄… OκOωhŠ™˜)ϊAχ Τ˜R+C#dπ”.[πΕE.9«χW²l―”ˆ ϋΠπNνwΠ—ςΌ ΄έh’ƒWkqX>g―Ζξν*gφ¬QXϊžό6μκή~φͺfτͺˆBΌ45fΏHΗKρhόΫσfΟ³€ΚPŸΪFλλΌω“σΉ”μkSoςeˆe‰κ•OΗΟ~ή{‡εώ?ΡwY±–…$3Wγ Cteeσgςί—τHޞύJYαnπΫW pΈXŒuŒεόμ[Zfς!‘­¨Π ’†ΒΠΓ¨ΨΆ©§'g¦ΪΆ¬WΖ€‡¨Ά‹Ί›―ρρόΫΣ†ΓfψCΔς©z―'γΎ‡«%Ί>mV9­ιΆ₯g&<Ψλš?&¬B££’‘*σ+w…Ό:–΄ξοp˜S'€?±φ‚/ςύί“ν]nωΦωώΗAΙ’8Ϋm·Τh#mΌΥ󯏢"‡KT°Ε«2°“AρΈ4ΨΪ6±tq||Όί‘ΟΣδΉέ33Θm±}-•₯Nυωͺo\ΦνG ϋ?˜Τ₯ΰ…8 ¦Πc΅UWΧkc•y”Υα…κ:)‹Α‰]“‘A‘¨(τηOTŸ‡‘ΐ8Yuδ%ϋ—L›"ΔS‘jΒ™ι/ž”Χ₯Η φHtCΧ₯ κ$’@zτa–<]έČ·υq“ώχ±Λχ~χφ<žŸί₯ θ{WλφΈ=gsφύΊŽyή–Wz;όχš©ΔHA‰"H’$‰"HΰτύΧ›η|'WΣρϊS{}η΅—έfN»?Ϊ08›ΐάΪ8½ji/‰UgA λψ¬›μͺΤ­2š½ΉΆΏKvΚ[υΑŒh[f¬η:ύ8 ϊΤhÍΪUΞ;Σ†ͺτQ‘΄Όk}οΓψwβmͺ΅b¦ )νZw½ΦΧφ}!Qžnf ύ•7Σ5Q+ΣΫ'χ±ψL‹υ­R‡ΑιΡθ'"ξa ΐΘ2$ΎεzχΟή˜΄Υά6=,šΊkΉ΄ΘΡD=©z« ΑXΙFCxΫέB’6Ӎk gR).R-c[·*©°|Wί•q΄ξ±ϋ  ΖiJθδκ%Θrπž.EvδβUλΐύKHbψΏWφχ’YΫΛ¨w—πonνΠ·΄vR솃c²©Σ‘Ι lO½>{γzΑ%(cέ–cΊ!ŽΦ@Ε™ͺT‡¬»QΞY€o#΄Z―YιΚΏ“›,ΊkΚγΐd Pΐ­n}ό ΗVήδψ\ζΎ©ύ!˜υ~ 8ΑΔ9ν¬’:O€do –¬ƒΠ©n©Hˆ Τιœί‡ΰΦηψ»ΉΫ'ΓηηΎtp[σyΤ!€πϊ —>iΟfΜό笌 ΐ―G]Ν΄ή«XQ­΅―`!(1)€ύΓΒ`Έ…|\Σ πΑ©ευ|’w&τ NŸAυ{.©½ λυžηG0 oE811ψαuba·±0zPε2qΐρl},ΐΗFSλ?Cρότέ‚Ϋ·$ΙΉ]”Œ4p–*΅υΑ›τΩνx ‹1.v"@žΣQωςαΏ½·‡gΗ{qi›ωο³vž–kΰ΄ΠrVΩ­L₯C>3mΆΞ\o‡΄«½fό¬― J1΅NνΌΨ–‘ εzχ‹Εβυ<^/XgBυV,ϊ§ ‡Deς»°3-;—@g2 ΰ#UΪslά'“Zw™Υ4G€]ΐπ-pƒ ο>cη™3ε2DΟΚTPDς”›MH0`6Ή(ΪΤrύόΙΎ}¦B~F-sM¬IΚΗίέD]7b9ι ς°Φ·[šύϊμΗ1Y… .¦tE±άύχΡϊ>ŽΈ2€$~ϊ9KX†ˆσ‹ε,-ΘΚT—ζŸg„¨Ÿ/G%Yω,Ό7±μWφτΜgVΔw}ε¦!Υuίΰ‰iχž7…ΰuΗLΧ?KεŒπ‡|g²…Γ܁ξ―”Νϋ·Kώξν΄σΚXΓψϋ=JŠ‹ΝtTΛ+4ό±ΒΫϊ,C:ž€Œ<4@c`` C¬2ŒB³γ9/kηYU*Ε•P¨F¬ŸUλ (κ5‘3ψNCζ˜> ή>αsYgK©ΒπX_[—ΐΡ[roӐ_r80½ :=HΥ‘ΘζΈθΰ¦5jΑ‘σŒιΞ›‹VμΖ9˜Ϋεs‰u›―§ZUΆ§Kfγε8|«Ϋ‰Ζΐ¦Ÿzη§«Np]`chjXqD‚Ψρ`’{FzɊ°β&{}Α“’𨬀ͺ,95°ja•9²φ+ΜΫΥ}ΰΔ[ͺσgΚζlύŒbΓDG φςϊώ!MݚτRˆΐ[ΚAΕͺμτΩqp£²UλΛl„n) PΘÁeKyŒ[%|N77nyW$툢 [ άΩfˆF›<αŽ‹„πMΘσηOΏ›=Xoπ|η^qΚwOT‘xΛOΣ D^ΰΚ)wQξ ¨ΈυΑi#φΤˆ“aΆaΗ\DAC₯ 2ΈJΫ½‹€<Ί%εΒ߈P―ŸΦνqqͺπ°(έ ;ψiρΡΈg„δ˜Ύ(ܟ?p~Ρ`d‘x]d/w§Ί–ψuQΥ<€eAΎυΐηMϊJoΣ©φDyl1§ Qtκστ_…°Πw”+&χW˜8d3•)¨c¨œΤQ•¦˜ύΥMwΊ’a§>Η―ͺζ³'2‚υυ ns‡yZo:άκh©Η8Ζ#―πΊ* .πΔ;εΖ6ΠtΑ 0ƒ D5²%J# ΉtE―’Α‹ͺ5_k š{ΖΗ‚ψ…3ήΑ !|r “"δ+„Js©–tAf UFb*”4σ³Σ;`zœ΅1ƒδ@q£ΫΑέ«,ΒΓ…o5RA₯˜·©:—ƒEύώ–†‰ž0ήq[@§ež—3ŸPNΑ‚šΐcΆ#~ ”PRŒcQ)ι#6λCΏ—$9w{‡pΓ”CV mΆΛD©ΰΞ°β­π1Νρ€o Α­AVυ,F‹e8^ΛCzŠαEΤ€@aNOΓΫ^η_E[~^uqn΄Ο)SC„ »\2ο ρ=‘Δ7vFƒ0a”uB §œ"ξΎδ^w§ΪuΝeη€α±œ† c=[Θ5Ε:{CDΡ?³HmyTJ@a±hUŒβΥδςγu:!:6ρψ9ΨϊΛ²dζ†ν‘§m²Θ΄β,C ™CF°oWΔCωΆ_|]½ƒrμC”AΖ‘J-Κυˆ!Ϋη4Q—HΑ €βηΩΛ½p6ϊ>ΪVΜήχab5²qί'“Ι'ζf?aΗ›ν{^ΤΝέ°%½κLί9ί™2dɎύ ‰ή{ DΖv<@8BWώ_³9Sπψ½#+\ψΫψά wν΄ζνA§*kVKμή¨ύ·dkZήδ3½,νc|£.»ν+i«Ή–g»±uΤΊ/€ΥnW¬οΘW::Υ>mΖΖΕμΊΗΌλ/›RΫ\»›-ͺ}w½03“μrϋλ³ ,†έo~)±B΅’‘‹νZΰχ¬Βo‹pOΏβς0!οΘ€³ X°σΠ=Z¨ηg06 °ίU”έΥdΉμ[¨ŽH$Α#εd­ “zϊϊϊέ³Ή,ι>ωžC•οΦyJΓ •Q’š$‚ΖLω>?lνjύ?– •@€ΔZ…­>O΅‚UΈ‰’ΦBΒκ h©γ™žm₯ "γΦ\cΤ­Z~ήν™ρœdE <‰_΅©|+Δθ‰qaεo’έΰΪtΝ!¨ΫKxΔΈzT σˆΐ©tΑO΄J+ά€F ”)£Ž3"D"«οˆ*€΅vΎΕipL`kΔΥ―L[±.hάk~αδ xύΨυΒ£°(yυ‘k¨Σnμu#v^Ε‹ό­Φ/Zγϊ7¨’‚ԍ ±žŠΫΥόΉΑWHΆΆΆΈΉ1Τ«R!^ ­\₯RbI$’ADn††††‰‚hδXΎ±<[Ÿ§[tφ6φ,ΞιDC–­BR(δir΅ξŒΖ>ώΛBNλyL΄Ρδ*€²VΠ«%JDe˜nοΕ՚MΗΛza i‘+¦Ϊ/Ί}˜!τΚ£OΊά‘τ‡οΧžL{ qnΊΥBxω8υcΎ‹€­£sM₯‹%Ί-»Ξ½“6qŒ™˜˜Elΐk±Λώνψ όLV΄o½ŸiΒ·>ΩLprΉυα0μ0υΉηΜΩ}‹e ΪΙ2Ω‘ˆ€βŸΞδ δD4"3ΔεR Κ•ίKλ)¦aχ¬@ )§7ΉΙκwωγR =’·.l–ξ\·9dΕνw3㣞D­±]]ΙΛ,³Y₯?Τ’©j|dVR;<-M€N¨WWΗw¨²x”ρΈυΫNˆλ)38CΈ^9Œƒ ͺo–b¦ΰώm4ιΨηMΆ*ZaŸΚ}ΐ¬Ή^ /’&{$ϋ @TU’βξϊτ=ΊτΎ’’X.2VQs³Sϊ^§ΝωPd( :ΕΦGC€Υ†6«Jͺ4`ΦϊνΎX‰ξ΅G|π²ΛΎL†Œ}•P8«•οdeE%œŒ%†λΑ‹<Ωg^κT@ζq’~c‡?Yϋ€‘ηήΑ(£Avl Ϋ|œœœœœœΜ­gψηΝ©>poVτk€†Ίq²zi¦ši¦•Ώ ‚^SPΒΛοXݖАΞ;;LίΎ±°FΌΡ]ŒχαάΎ2Ύ.ΣΈΓΰ ‘Žqu[ZƒϊΉͺ™ΡAϋξ&Xσ]·t­sΖ/ηV'³3›—$Όέή΅eeέsδΰ΅ΰΚ’œ‡ Χ‰Μu‡)\ ΕD&ΗTeΘJ’²O΅ιο ΝΉώςΖ\XrGs`­R!˜ C¦ΑΞ›²β’•ήlΚβŽkΟ3ςεΊρ–t T₯π OTHΑC6IΡα§›Οίίίίί³6Uƒ1ΝκiLm;Α±A†Β·΄—Ž£X΅F«AF^«<:ζ,ΧΨ6–bΗns“*N6˜ΞŠOΩσNιΧε‚?’β΁άS(¬ZECKT8:‡U[j ©™pϊ~žzwR œςΕmi&«lΖl1™•ϊ’ΞΕ‘ο0αΪΓΊ§2sΖ& S­°¨αsMzγQŽ—*ιk %d³©DQΈ°<³2<€u¨΄M\‡¨ΝSΩΞνΆΛW±Φ8gf 49LZ†–Q έ€ˆΌŒςH(>ϊYωΡ—rέtΚήf"ΘκηGi‘˜`φ΅€5XŒ¦aΤΔ³%(tπ‡ ͺ™ €Λ.ξVάF˜mc![ΓΥΥΥΥΥεςΚνKyΪ‘–Φ#A0q  @σ΅1˜Μf3ŠΊZ³RfъMΨ™ΰ°Ύχ4Ÿά φc*nσ"…V½+ζV°)ω\§X…§FΣͺ2Ι±‰Ω΅Λ1fW@+ΨPΉΛ πΠΒ§!JgT~ϋEDWA›,A|Χ΄³  kΪUμΆͺΊ` uγP—TΆΥšFŒΝ6¨p_-@@W<€L½°Ÿμ©CžιβΠ’ΣhΟ·ww»ΛxZΨΆO²NώŠ£Ρ(ΐmΝΙΘ ²θA¨Β WXγET”W°Ί‚ˆ˜Λ½Ε Ǟπ|XovτΞ5ΙbυpB&‘”*![n–‹SL(«€φnSΟ}ύ§ι1hP§Ÿs Θέ₯ο:„χΡ™ 8Θ\QΩύέΒΰΞ­#u±€@*³Σ7λeΐs ΣP•CΘpγSƒ)¨=hπgEΙYΤ%φQ‚6 Mr²^Ό–Œ…πkb( Bt΄,(Ό5ͺ©ͺš©ˆ½’Vΐ 2²–!‘ >+|π2(šΆρδMβΈΈ}Ω–€":`(Ήξ!¬€’θ―eqpπΠYLE-‚ϊ¬ƒ}kGXuΝ‰p4]ΩηA6άΌΊ=°ΈΓ@΄θ&"P₯X{Θ}ukκΉ•Πop8ŠΝ›LwxΗwΆ²šk•‹ε΄β/{ε]q:Q₯šΟΩΧu)ΆšE@ςΊNΔgšδX,€€Π 2*ͺ₯t ΅Ε³£ΰ[3Œλ A@γ š,4M Β τπM•ZΫ…• Ρ«­'c–ΪιœRζ„ε|TPZ@Qƒ …G ][}θδᡌδP—"€Ždmn 錎ΉΌΔd—ΆΌp,τ \ΖWέ΄Z™Y¦›kF‘EMJφ _«UJΗUT•ί1ƒԝL!ˆR–ΫW2Β]«œEQ ΐ8 υ\­žωλKH ͺ3BŠ@Œ²e2sgΛ"ŒYy έH(‡ΑUD”DUutΨ<τ /Ÿ$F!(Qh‘UH•E"TJYD`€₯¦&šfJX$(€’’&HH†XJB& $"H‚ ‘ZhR *€(ˆ"Q …’(d…IŠ …(˜€ ™bF%…@„*–bIX„bJQ¦„• ’$bT‘¨©B™H‚"%‚ he()b€`@ "€ˆ‘ @’€€ hIͺˆ‘(h †„ˆ‘A₯( b"ZB%@d%)€₯!IR€h ͺBš )R•*j) ‰$ F‘™*aˆhhR¨ B¨–*I" B˜¨’ЍHJ”¨€( ‚‚’€˜’’‘Z& ’)‰"ͺ”(`”Šš(Š ˜‰Raˆ* f˜’B"J €˜)‰ ™( (aͺ)™) ‚‚hB¦((JͺHj*( fh ‰f’XŠ"D¨ˆ’Š‘‚š&j¨( ‘ͺT $Š’ ‰H"‚Zš™H†)’d€ͺ‘ ‚†JŠ™(†¦ …f*¦)`™ ‰’¨ˆ”ˆ ˜`™d‰hX *ͺ iV‚†©$ˆˆi(‘’&*)’Š! bhJ©(€¦•Rͺ*‰’ !E*…‘H!I„š‰B!Q’$€("QIϋ wΨA\ΐ€ηΐ /N$ΆΨOgƒΰν^ι³―wΜΞxrBH:θκΦ¬λ aMŽ˜€M ©3 Ewd{rˆ) Fy¨’ αg΅«Α֍Σ5dyEΨ.`Q‘ 'βΊΜ€Œe•xŒΌ+?χ.œ<ζRΣ0ηC±(ΪΦγˆΑp²-ŽeƒaϊSζώςXθέ(lσ`€Θ:°m²ήζͺ-iE‘…^ΰu"KύΨ3˜ϊ™nΜΐΏž5kV¨Νν0έ(ω†@|ƒ5³ΚsηWL )›΄ i1šš ΕσCρœ™y$:ΤŠ»Q”‘ύ„ΐd)IxΘ³ Pw…Άπ“ †=.²†FΠ·)R+ˆΘ6dmHγΤB&Σq‘·QΆ£#jΚ΅–δnmŠKŽ‹¬§PεAΣj4R";ΛIΜαϋσ?kO˜6Œ±Θ2δeνΌ§EΧ…·Δ6'gΤuη²ΗD‚gηΜφt˜Px«3Ιγυ}œVVςσ/Olΐυ`X ο©‘ŒΣi‘‘«cVB3ŸΥφ$“6gη§K²}ώΏΰπwϋο^ωβυ:6}ΉόŽΩSΚZœJ+Ύ₯΄΄yΨfdκf?²cί>ΏΨkϊ`ΖC 3 ’ρq ΰ0ή·M«PΣ4•^ΚΎΑΩύΞVάχc΅υχ;|Ύηn^ΞήωΦfX`υŽΰ˜ω¨βU0ΖνΤTB——ά–YΌ™σΰdά:Dαg^ <Άœ:kξ9ΎΈ}πΊΫ[Pe7˜ϋΜΎφ:~O?_—ΤϋŸD Œy3ϋ»ϋΌ1ϊκ\0ƒι•US πAdΕSήrlf~AάMwgξrwAλ&dέQ £Ξh΅j>V]Υ^« Ψ† T[δCgΫ—ΐώs}”όŸ€Ί5ΐ{kWη<ο‚zo.φ1qI&/)E^€οeو%@CΚΩSm#δέ;•νTW7q30ΰηΝ6o,5ReΔψ΅[‹Uε PT5(ΰM @Ζ• χϋσ£νlAΙI\ΛGΆόΨ“Ϊ΅©Ϋψ°ήaόΞ`/\tnι9αή₯“ ζ‚BαΏΉk§sΰϊXGΖ§ο€@[Ÿ½³ΖΎ.Ÿ:~θφ9wώ·³Ε·³Ήi54)H¦'*άν@€2.Πτώr)“ohyΑe§τQΕ6k$Λ{|‰-οE”zQŽτ’,Κ'ΏAdAμ««[=;E–@³;Œ…H1VV΅­kQZΐ LŠjœη9Ξbs€™ΰΉΞsœΔζπ'2'ΊlŒb#{’*TΦ΅¬V¨•jΠRJ"Q¦†”zQφ}ΉχV'δώγC#ΰόL`δ»ύgXλ—a'Œwάσ‡ξOΟ»Οvtμ4†Eρ­ψίθUŠΝ½"-–Q0¬±εΓ,,0Ak44騜‚Y•g'θmΰ<²ΘA¨™ΒΚΕ gZ6UήCŒRŠ ³”T“FFu”„­]R”Q-Έ0`ζΒB³ΩlΏ’wOΠ>Ρφ†~iΦω_]ώ1ΥΧΨ Κυβ3ElKlόΠPΈ,Οξ<Œ†ΑE*kjU΅sΔ ­ΟŸό'ςi΅ΰ: ­fχ“Š˜Η2–`ΙΛ"1Γ5Σ₯™‚|‘tq6¦Σ*"4Κ“1JΗ[΅ΎσqY@CνžF]ηλΎA‡Ð?G–΄Ώ°±bŠ4ζΈSΕβπ}Wwf;UNi²‡5>¬€Ψ»c{¦™­Έ6Φ7£œ'‰΅(␱λ:»EεB•5<• ƒΣ—η–Θ’+€$θͺzωW―ΖόŸ{t쾍Υa“+ΝT$ωX¨`HFέ­K£ηΰ+¨΅‰νRζ –±Χ­ŽH(`P ΅q@φ²  ₯²ˆ6βΫ%Ά™7‘Nrͺ*wΑvi"ξ4μXV‘1”¬,‚4Κ­qTŒ’J• …ρ20PN \gηίΗ"ς=ΔΏ—’Ο pnΣΟnLœAΐƒMv˜u—qΩΆͺG5Jc–ε±:ΈΦ νg”ο,₯ΣΐŽΠΚ ·ρ ΄Φu7Mh΅9]tΚε#ΉF)αWΊ';»¦YqΛe†ΎeΝ†²`Σ~ŽΆνβώΩ€ dΩbŠŸžiƒΝ™ώι,5§©ΜΘ΅‡#œ…Ϋ μκΦϊ7:΅–&‡MΧΤ p»ΕδͺuΧ9j'ή] %ƒΰ]{,ς†AցΩLC~Ž’dθ ?,bFšεLΘΦIιΠΒ74ˆΫθγh[Φ–ξφ¬d±x¨'VtB­QC±vY»_LϊNφ¦ΡσΨfDΊψMŒΠhͺs4΄2―.4Žτ«R‘Ρ+RŽU­θβ4f,GΡΏ§½Fή)8ΝzžέΝΝkežϋΘε*;μ4uxΤΏt£*;.οδTVkΔ36k·ηFΣΌuΟι \qšΠuΐΙe²«β°*γ°X›8O8as”šΫqγΝ†ylMLjΔHQ)ŠrΡkΪΨ†ͺΡk;:WvγdWOn]¬α•cκfw'|4eΗ„σ0Ό» Ξ½%S$›^jM捁a†Bχ“*KΡ#mΗ9εαpΈ4 λL˜‹6ZΥWE‘žΕDΪo30υ ³pcχTuΡRA\Ό«»?(Da6ώš©w[qšqΕΡ–ΟXH«I 4 `I&KY€:@9”8QUŽχτnβ-Œ%έ΄‘q„)bІm–!xU­Τ±dlΡ‚Υ€ΑF©oOΫ¨„ΡLPΐ=DΙ9Κ±ƒ’ lS 7·9€m6GyΔRPo―₯Ίψvϊ@ϊ·ώŸH_ Eƒ’ˆ5E.½ž³APΫjQ!f8νŠC.¦ω©L%* 976§&vQȝEΥQb'Η]™h”ΥAtNa„Α“¬’Κν!V•‚”Ε¦l”τeΩ‹ΑŠ•Ζϋω9Q*PX-θΫΕΘ\l,°κεT5LφγΞθμΣΜb=Φ\‰Ϊ#Α’όŽ( γL|:œςνᐩxI•%™Χ°ν₯WŽ[ρΡφfI·γ0Γ":ρΨ$Ά">Α`{˜―ΤΩ§7njΚφ7Eνάό!ΥΤ«ΞtΜ’sφ”γ <έΛΟ^c&Θ"…φΒΥonδNiMζ©Χ‘’Φν(eB—EX€¨‡€η.σϋ+,¨‡6Z(=M^χ₯T[ΔC3^²«φΏψκAεuœ«§½θΆ3υ‘Œ΅ςzI¬™?NωΘvχU(γ?>Έχ/뚹d­υYΎνΧYωΝϊεžΉNΩ»ο½J‚­3†o9Xόͺ³*}ߝ•œpΨŠžΞn<¬8UjQηQ†ιΣ«§<ΰ 1Β! >t5 λΥίQΘ2Κϊ(BΠX]†Qf8 w6q_Β /ρK„ Hk <Υ p\ŸΕ^ “ΕρΦ·ΕΈQκ7Pι8₯°j‚Ά#+j\8ARΊ\P˜pΌ~Κm1G=Μ «‚Q󍢡5οOΦZtΈ5υ«ͺt­*·M­zΔ³6ΒγtοV@ “IλlCVI$Ε ₯τζζH!VJΧhJΆΕ«I­dpD 9α ’1%j-Ty(kis ¦XšpΚ7Ž€dΪ²6Š5Y©Ύ\κΨežμ3YD5χ&©±‰²ΐΩ g²DCΫ3ΆVQžG Ζ6FFΙΆ[μpΈmΘ!œŽU *”Σc#/^* [H½ρœ­Ό’Š-ΌΌ\―Βrτ/匍} #ττα½ί§΄ξ;ΐί}–}lόO›v[φUjm©όneΟ‹Ό˜Z„ΚΕωzΤˆ_eΧηˆMΝφ^σ@#¬}χοχ*)ψΒΐμcE6J-)dδ6x‘l#3ZΏRoŒφD²β―ψA΄ώ@ΪIO½Β§Ι’4°wι„r˜AΤΜβ?˜Œ:beNΌφλεΔQδΒd(PX!€iΣhκ[˜uRώνά6έ" ‡P%°4TέΛKύέ8!ΜΓΔT¬ˆa0ƒ’ΓKGhΗΡΟIΏΣεΏό\ωΧWΗΡϋΪE°Σ&†hώŽ}vΜ IΆΔ1‰ΎνbΝW5Άe+εX—}°q-ρςγqόν9ϋΉ±=gΏ¦’žν‚ξ~„˜ ƒPG™8τrt™AReυH‹&-jΡι#a²h©²Ά^λ+ΦΘΗ²ʝŠ€ϊ¬οeZrχuΏ“(u ΧmΤ γuΔ2n)' σ°J γΝΚυ&:ωˆR€DAς Aήη΅’¨Ί{tU@XnœΝβκĚ£ΕΘψP!V2ί1₯!ής yψΨ6 FTηαΔ{)€ρK†{Ή#νωPΎϋΌ£š«@ξ?½6Σdε πˆρΝ΄•Xΰ„μ•ϋšιEΟΥοΟ}νΡΔlγ«9#76!δ λFΫY©”aγ±λŒM„ι6]ΜqκwμΞύΏΖαίaζ8ƒW<<ώΎRΖ¨VWn}±i( R .λ£ί2+εpnΫ]‰­ˆ1Ÿ“ζΥxFg»uŒ5wyς­œ}ΐIΏ~εΠΈΗš<>#RΕ/ΩΓΣ'4dXλ~ `xξ%Ύ`‡€ςΗ ° KRΔ΅θ4ˆ-„$%M8lΙ£=5vΡyεr\#ΫΆΚΔfή–΅ DŠtȘk:;”Xς‚½:―»Ψ¬aOψ‰RΌ&ΨU‘Ή±€k‹4ƒΣα$€Τς„£Že(ΦΡοs  >"lOδγ3κ²φqœΪtq£¨ςζ >m§qmq@BΉ‚.sΤ0‡ρψ$ 4ΜY0VhK4dΟIDTX˜G¦Δˆ8iρDΰ8©#lFΎ­wdƒH²J:isaΎXν«Gγ…MΓ’l$p –[ΒθΡVWnμΠ¨€ύ α‰Ύ(ΔY% Rς9±»X ΦWu>θη cP\Шˏ³OoAcνξΖε€=mΣc7b3…‰°‘ήΕΆ`~σ€ΖήζΎ&nώiνδΏ–—Ο5yώR‘ξΦ;|α{An2σ zk›μH{ MλaeΙםf?YΉzΗ²r‡‡Mͺ†Ddψš¦˜ι qΕAΈσdBuη(L§Ζz3β»]=TΛ9qδ²H8 €5&npw}dE4M3—•όWΆe^Τφ‹Ϊ½½†…2Σ&ΩXYbΖ9αΛΫ)Ί9ۏ ;­Υοs/n6K²žλ-¬ΡθRπ%vͺκ:˜ΊbŒ ³I»ŒΦχa@€YgΘn9ίdΡ›Άš1η'L>έMŸ/Ε¦3¦.=ι¨Μ£Γh@νεΦών·|QΌl"@ΐ^BYŒœ Sx~MΎ ΔQ§ΡΡ‘άνζ8κs¨lξž7ν0aΜ`ŒΜε½Κ8Ύγ§ΉΞh”T4υψιoVθΕuˆSmλΫ0ΆΑ*``Χ†Šn‰vο€]qέ,wθeƒγ€€Yώ 7’,4Ψ 5±^‘΅ ΓlId)ς Q{Γ&R,ή6«a€`ΝO«+νΣM·P+DB vPrωyxΨYΐΣΔφβ’nεΘxα| k!›Σ ˜eΆθLΩ/άΒBDR`˜D TJ,λg:oψ9–b‚›‘™]AΧυKՏΪ œŽinβώoi2 ŸΛoΔΪ·5zi6―]yŽyK―B˜±ίΛψ1€Έa<œ%ΰ‘m΅ΒςL “”H»ΠZΧ²J†j’VCK…ƒ0Ν%υ]dΠdԁ/鍹2Υ .Ή¦₯Ύ K„‰Ξ»\½έ:‘5σ―"νξιH^υ{uƒέ‘ζ μSΡχΡύνυ‘ ΞΏεAΣό³ Θ—V6ΒVƒς㬀”=ω1ƝWn·ŠΙδG^’b‚ΐ!Ί ς{κηΆΣ<Ιf¦Hˆό­―ΌnχΗV"eŽύκƒv‘NOOe€Zx{΅Όz™”<Μf¬/AqύOvpκϋψ`G23 «ΖŸ… υνςΛ·α\|rpίΥf‘yd―2‚Δΰ`Υ ΚνΛ.©.k4šŠ+jϊtΪyȁ’ytclN’žDΉ• ,lψ–ΧVͺ ή…ΘΞπ• ­eE``ΡM@­°υΩ…ξjΙ«±².^Nr‰7Gιρp°Ιl-¨Δ Ξ'0 e°ΖΘ”K3όΈ{·μ]Oc·Ωςχ»#Ν…†AiUαI±)¦©"΄©iŒό;9|ŒΟΒΦά6Śn5Mi#”§ŠˆdnωΰCΚοXΏ'YΦή·£·ν”u\΄θεΘΝψ5φΊ,½?S#Υ.ŒJβ±4w†Ll βk& Θ…šg«C{½ Ή2*εJQ†³’2εξ„V·@Ο‹™³ΊBπ ]υwΣ/+^h,Φzϋ±ΑvD/”X3αΒ Ή–‡Η'ε1€N†uν#όqΗΣ(VNωhE΄/Μ©½‡™Λή»4^/X"Η{JOHκ@ͺu”a`cΟGΝΒ©jΆσ2ƒD4Œ¬]„*ِήνd <››ϊ{ŒhκCίq'˜‘¬ό[‰χMέύΰ½ #z!δgΛΞ5Ώ†<ηΫςϋ›jXŽW³Υ5Ÿ3ΕjηΜ‘Ίϋή>ΰkpα> #χ–tάΙ3K+”ƒ·Mz½ΆžΎ09xxώ+ƒX½Θ« QΥz’"·[γ€R½Ψ)ΗZέWS4ΉΩΤQ›r­‹8-ΐ€ŽΘΠ–@βγηΰ_³J2fv 0‘ƒlmΞ΅eD„Pow[:°ν+— ΛZ›=.#αevέb Π< %4γΫ)̜Wu’WrYsΰIεœ~Ύ\”mΆ±ΝΚ$OΡD³„K%芯6δΰΡίkmΔjT6£4Ώ#ςΆϋΞδ 1΄Žͺ!ͺ[y$ͺesƒn,%#`1±Œ©δ…j!ήτx6ΖϋztI`V`›0Π»VΎύώΝJ’Φ_ζ{T o—eJ>#ž€ίe@¨ ·ΜɎ:jΚjρΡ tκƒ\2όRή jOΘΒ¦εCeζBSzΝΫhXζ)Ώ Sž_»ζδ•?@©šΈƒ³ ƒ’;α²ηaΎΘ$‹†0ͺr†<Qtœ)αlCqQs„aηWΊΞWeR}π-–F%ϊ±†‹¨'.ŽP‡aЎΗ2τ.f‡ΆχρKP ΚS·DΰjΏόa’ΎΜλτ)Άnψlβ‘XhֈDΖj@  ƒ‚2jY—₯οςcwΠ4A°ΐδŸ‡6YEmgo’ρεzΑήGRœΌ΄u έžηG/‘ΡΝF+2¦™œW=j½ΝΨΙSBM—!ŒF%t% ΰR6°–uΕ:awκΟ-)ΪBΊΙ‡GT‘EμŸfωΚ¬nήBFΌιz;\Η%Q^τΧΉ„Ώ ΅ν!ιΪ ΊΑλWm_€HaΑ΄²X!ε­ΟΏ ‡}—ΙkΔh/+ΎτΉ7ιdΉω²wCCutF»> ζξΞQh*hD››ΏΆϋϊ₯Ԋ1γ)ΦTe#)f8u_…†00†1ΉΤΤΔ‚{¬ΤZθP+l"]€ζv²E7·S/tOΥβˆδέ•ΈvΈ.A±‘΄ήNF§wΆZ»X(Hu ΦΛdkΕΝppˆC³'ΑqΝ:R1ιǜΌεΥΔ«ŒcαuΗ―αθΞ ”)8wύ|ΆέΦ0“xνδ …Κ;c†εQ2IUα―aŽ{T6:§t₯N¬lώ&ϋITΗξέΌυΦ_N&R:Aξa‡ Δ4€E\ ΊT6ΐD’sλ›p…΅q“„ιYsθΠ>£0¦ UbK<ΪΠ/[F‚ρ@½£1Ηvu°A4Ό”τ潡Qˆ¬–”'7kΨΊdM [μΦ4GLΑ! ζ†£Ϊ }U€œΣ€Ϋo^ν]|¦Tͺ› υϊjTδ‡`™ψφŒrΪ₯6΄ψσλ–ž@TΟB΅Ÿ˜|+ZcΰzΣπ}“ΦΠτΈ3‡dydά`~JJNΙΡΏ-—‘DŽ~ήω-ǝŠί―’ϋœςφρjΎi-ξΞμcΪ$%3~Ϋ’61ΒHI !$ oΠH£θPc§,ΝyΖ­΄ 0θώ/gt•”Rd%χτΫdƒσ-bΰΑFοw)@β“Λ!ν ˘ƈ}ΞϋFF³―jΉΘ|¬ζ==4HκΟΰ«όιgω±TG2?,”υwͺ†ϊ4«0 š)ύΡ3ξηFbόΣ…@Ψͺ·ζ(2t2_αŸάύ¦^°εΧ”θk 8FΫ&ω&σΓίω=ŸrŠ€©°Ύ*ͺ{9b²°UΆάσζ2EΖ\Š3ƒ:})„:hGΤμgeaŒ¦˜ΠFw³­ι%Ί,7&N"0 ζŽkΒ‰G‘ϋΛΧΚ/μκsLυν’¦:EΐaΞΒ,2ΙώUΰ‘Ν”λ΅ŠPB{β6.pSPKn -€ -£ΙΣE₯ΫΝΑSžI] RέΆ>|Ϋγ2ˆhCIΊ9§ϊžMΑηΘλ@½₯§„κ—UΕ}{ͺάeƒA»V»ΌπyŒκΑϊ-ͺ>pΘδΨρΚψš¦Ee9ΊŠ=ρ›¨c-u.o’΅y@οιYko{O6Ε©Γ€ΧK3" Β½7οιΕ€l“k <›­3ΙfΛ¬ρQž‘² ΫΫ^Χ†4¨FO›n±ή¬=|…΄F`τΦhhΪ Η…τΔ„0h¦iΡ“Z΅ Ε!ŽPθj‹ŠTYΨ'.9γKŠX EC0γ\RMZ#Ξκ΅B‡Kί`‚ΠͺŒ–Ζ:m’>22.\i€δ2Όr]Α'%ElUΛ-ΑΡd†Ž-ʂڏ£ Θf7ζό:κƒYLΒkEQTSM0W ¨tž#ςΊ6ό΄ΉΓӎΙZθΖp‡G§›^Ϋ ύ$· ¦SΔS˜?k5© ·ϋΒh€„Z °ΜP-€±n)0Cό+ 8‘³bπ lΔ ‚r‡ζα‡=ψσπ–κχwώΌŠΓxΔ_·τ¬Ξ’γ0΄βŠ”AJwΞ ,G0u\ήˆΑ ‰.4 U`Α@§π_‰[1Α8>ΔΤΐeητ Ή~„ω8“k.[—‡1Θ/b-yω…šm8 F€:υ ϊO’χΤ‚)NΙHξωθi΄θ3,'وθ4μΣΪΫ‹ΎB€†1ΟT‘†?¦`‡Φ00δ€uΑ έ>}“nΡ΄Xδ›Œ’K3Δώ‚*(EΥ7γk3©£Ξγ)lGιέ‰š“ΠκΔC%ξν·’ΉΗWΗπ‡Λ₯šVΛo ±ι7$:γΖ;b0 ~0}!ϊU:•‘XŒE‹Kc‘~γ†u_ΚAAJΓΣzέa=¨TύίT…ΧlΊΘΔ'γ#·}uӜ‚² lg•ύς‰N^‡Ψ4@WAW#‡λtδ-ζΥεX}GΕbQD€g«ηe£‡6―±νΫΔ#θxk ΩΤόΎtΗηοΆm€€ο?Pμύ­§'Ε—‰χ܍1HΚΚlΝΝ£Ύ«: :ˆΩo‰ψ¨ΙβσΗω²κύ΄‘>σ‘Ÿ-xYŒπ`ϊQθz;YΈ,>νηViΞ/±r , @`κΚU©3κσΚΓqΑΰQ· ―`Ύέ§³wj` ”eρ†—NΑ(gVΎ'yKαΖηΉώ| Ύ9Ÿ‰/Σ3ιϋ²lβχς~?mη!+΅Y9bΤg"γzκΜŠϊρ]˜NžP1νE*Ό HMh6φHΑlΟΌΕ 'x΅·ΩηƒΩ-VeΎ˜G,Σ*3‚±pŸ’[όΆ“ϊNDπ²Gžyr2σg&DDQQX/’Τ³L;Ÿ1`]]‘—i΄› ¨TŒh¦|To.#"Ψ ψ έŸ\4ΊΞ~:^ΦΛYf1y°βn²Υ9J]*l³ –ΪbnΙP΅iœX °γΜΜ+-ψη£*4 δ―@#. Υ«€ΪϊzΪͺς3vžλ&Ύ’Θγ³αH|«˜0οΝt―‹ c˜υo&[ŸX±ΆŽ†¬*J/Μ0ŽΌ ˆTœΊ#Hcάί ͺR₯Χ5ρ\β–Ε€c uBΧ–PΉ‚έD =ξs>΅„μ˜AεΚ”IΠՍnUhΑ;}Ή`XΉςG cšΥͺCƒί—W©F5κΦΥBβd|Ϟ²o(­h0΅x97Υ³ΟlΜfU‰fu Ÿ~ώwσ—SXy(Ί5Β3ƒ‚^έ/«!!ψ΅|o_Φ§παθ=ŒόΌΝέ―­Α[[τ5XΤh€¦$ΥΟμ’„.¦HΙiˆft=*B82α`%% {:ΐlΠ–4%‹y°ŠŽ#Kχ―f!1βΥ₯΅eΐέ~*2©»?OŸΕςφ›΅¬X“ΥμΪ‹f"0χ0mϋΟέϋΟο8Ή{ΓZΨ£HmA‚€ΕαΧ¦gψΨ–;Η=Συ»¨piη©^;­6p«o'οO€‡@ΓΡκπρx΅·Ύ΅ΜεŠη”½Gž¨†£9ͺ=”YΣ…_]J€ /νή{ωi·Ρα³L ’τ½;Χ‚Πd·Ή~ς~Α—j’¦ †A“*”4'ΟJΞJ T³υ‘R?(š,†g`«˜+V6›L&K€ίΟZPyΒQΓΓ ζځΗOΉν£b4~†Ύ¬`ύΓάφώw Χ *ΌJΈ–ςdάθ Aρ‘ΝΗ¬‰ž`daΕ„b Λ£ —3ΈΞaδ/&pκR$ dahΈ°’ wι5FΛγ>>κlw2M»ίrA_dQΉΉEΓ¨7οκpάSq δΡ…‚Α’C;Ίc35Π…?œM ‚©­¦M1;ŠGπ4ζ’Σμ‡Άm:ΥUδγvΎ»ž-ŽŽ8Za’_π™05J#pΒΜκ;Ήμ½ήΗ,ψΩΏ-Ύ―o ½ΪΫuψ8Ϊ›{ΎI“E‚OOΡ½Hφe ’Tͺͺ؝d3»κ㒈ΒEάυXζΐΜPNΊ"cl~lΉ­R΅·Ό;܌ŸΡtκ5’“ƒ ‹ 9YŠAMβ:튬ΓH£‚κ ちšgL`F!‹ύφι`:‡Ξ6Έh`PqΫ«2νHςΣξy\½όqχw}λφ6ω›zsνiΔέΈϊ[άujpcixKΟαL ρςgτY]7wΑϊΏp TόXΣŽ8šκ·=G½BQk*i-Κ14ž¦’ΪB¦–θ”|œyΫbΚ<+Ξϋο>‹\š΅Μ›.κΌCσŠh£Vσ‡vΈάΚρV4{ΰψ;][΄:ύV~¦NϊϋΉ'πΟ₯6 π3νδΫ³Πε;εψ^γ…Ύμf}΄εΆγMSσ{OΩσmςΊΪ!…O€"oτΈMΌ;Ϋtτ‚m)'w°!δH$=z€C‡ £θΆΉ9Η¨£ψiι˜™Zʔt‰Wd0‘7*ˆjKρ}ζέ?#ΦYνλ§ρ§cuΒήΉδ-Υ½Ξdγ²υ˜ύ‚B™vdΖΛ.Ώ³θγσ6eχY•Λ5Ϋν/)Pκ@'܅凉Df[Ψ§¬-wžXΦ]68eΦ\²C+αΛ™/Ό4Ά0$πiGšEΜ¦‡ 9iDBͺŠ=œΙaζθ*οΟΥ•>†Y‘Ώ˜ΐ ϊΌO γ·7Q#ͺDοrQGtΘΩ³T"Ο»^ ‘ΑΖfLεϋ] ΠΗψ8’1(˜C@Η0MvR@ΐή]™t5κΫxΪΫM±X–V#i\ h(wIRZ\‰έγ&ŒΥ>5ουτb2gΎΌlΩλ§f5ΫrΚύې„OΌτ|Βg`<2Θz/Ά{xΨΨ u6œ“@mφ¦=«8³Tζ±ͺ4ΪΎŒ- Ά+βUS’[]Ψ£xv—¨΅)9ζƒsβ7ͺ{βMΌ]"J„[Συ–ϋ³H~ΓKΥ›trσΚΥΊλΜ,ΒY9΄" ;¬F;ΨάΤ„ ΉΰϋΥCι~Ε΄xΦ (S}_χ‘‰|Ή~ Η&i:™rj’,τ :UΣ6₯†²Ήα@΅]‚‰­[(ΥΩ˜][”ς29¨˜nT^‘N‹’φ± wΣ-2S ύ–ΐ’C7λŸήρώΐς`y{ُ» ώΜα[pέΖx;η·ωYMΨ #<³”‰&υLWˆΘL ι†ε)yτ=ͺhμy0ΜΎ.βzά²Αˆκ[ŸΒ­[HpcSβ0ΑxH’²Η‰&ΆsT_Ή 鳆qLΐlνυρφ˜ΫmkEύ†Gόmικρό‡–Ύ¨–NxΤ…9  ΑΎΕ… &§Μ%ι»–Όΐ>3‘ι^²Œ]f ―μ’•}τŸ†˜kσψ6³±/!ςr‚rrAQΩF— @ 1~“|4?*€£ταδN`ΩΐeύC“¨ ZX—³ς§οsΡξxCͺχ₯I†qΖΌ™(ΝI`mώ>κ7V0‰ρ)§ΧωU·š3Ρφ97t˜ ’ο%m3ΠΧ”/!ΰžP¦ Θ*·„³8pΒzχεν"nΠδΤγAς†mς0<SZZ„n! 7Υ_gͺΎhYΪm6žSˆΌ!R²³€9{¨tβζˆ_pΚσ{1%LL΄ωo)ΈNk₯ ab…€ϋœΪ«X¬π hL` eΊvΛς˜²=LψxOX±½*μc’Χ'<Οg‘^Ύπ’Ώ‹^Όuš.Rxˆ»F[™οŠ SŸ2έθ†ΩS‘ˆo»ά8ΐ뒞κ£π-ŒΐίσύSΣ@=sτ=§Κws”@±LK‡lζαR§χEoF·Sέ†z,Π{”ED h‘ %λ‚B»±R₯ν™\Ό™‰-3·uڝz:λ3;?‹ύb湝¦ΣιΙ§1ΙΙFhγ»/Ο‘pΞjΟψu£‘ˆmH(H‹!Β;=μIŠ„˜Ϊe/ ΪD_U؏ǜTτ“‰HA,Ž%₯žΝ6W3ΩμΗOθ»NΞβΓ/%χζ~ϋώo@(–ΟΊ+ΰίψ•‰–ސN‘o_ˆ%ZΓPW>'i7+HX¨θΔ¬¦ρεϊωiΩι6°―£qK³Γμοc{»3ΎϋžŒ_+9l&:W^20zuͺ6©§y–«W‘Γ±«ήλί 5daφάbWz―υmͺ―YlΔmŒo³ •ΡˆώΞέωΫ™‚Κ_TΆU3Α‰Β™γ}I@Ζwž†ΒγpxŒ…R—jΓbŽΧ°©΄― ‡ƒd άbHp¨Dα@›Z6ώ‡βιΌΡIΣΩϊηLσ€j64ŠT1f ΤOYγ‰τ·qΐΰβΧ~ˆi©ΚgΤήβ·H)F€ αΑQ‘vΙλ9\n5 ΑΓ±§–Ωο‹Τ.yQΌqqεfθ šΉίΊ£¦ι‘πo½―¬P`γM«Ί­₯0εtΖΆ|ι{ €1‘Ž G ž.¦ͺαΛισ=1ͺρρŸ>­¨^ͺ’‰-(ΚƒΞ©πvΓΞ¨w&6c8Τ4δOͺz 8qY Ɉma#ƒo L}™lΓ2ΰέΗ¬¬p1 ΩjξEM,f:¨Ζ­¨cq†TWVxlάL6μΣb9ΓXjz{AT32v:ΑΌΣ έU•Ε:Jα% σ Θq…ρxΙn€P)Ϊ’ͺŠL]=žm―oλφυš%§υΔYJκΩqΤΉY…PμΙ,|ΟΫadΈ^Χσpδ10ΤZ΄! }-.₯‡Ύ˜π~66‡5Ό[pž|α}ϊ€αA«€ˆ΄οΎdτu’dW[CQ.‰δWu›ˆΡV`…e&m7oΧdvm₯T€?%λ½s˜X©C &Z“FP‡§ͺ%Έ{iϊSΐš(Χ ΈΌΊ{ςŒρnάΜ-zƒM6†ΣyΘι;ž^œ6<ΤKj‡ΡκAΠ„Ύ#ŽFΈ‘ϋΞLΐ‚§YΚN7Wjά±Tά_Z§&Β#–$>΅wθζŸ ς2έ 2[‹‘>K’™B ₯aΓI•bœˆi $…"'ΝaΛ1.”-cFFΫ’Υ*Ξ&ΗΔΑ}^K Α°ττό톻πΛΝτmƒcŠ4κμͺyΩ7φŸΟ“γ\€'Ξ†>ŒΙ£·f™·Νk…LΛ?eΖμš>x™Βfσœ1’ςeΆΛΔ$}ΓυCπΈσGΌ9Žπύ!ΛΨF|ΎeΚΞΡR§έRYŽ…1a¬ΛƒIkˆλύΆS^ΕΆdM[˜ϊŒy}Λj”(EˆNψT_ι‚;DXϊCg8Ž έ0”€»)tαUAF^-G—ԝΗx Ά½JΟ-TTP`' ΤVγ‹Yύο§ςδοΈγMνν-ψ΄~š(Y€to•’y΄]Ε 5eI”-'e‰eΤ³3ΐ‚  \rF¨2ΣΦΠκniςjΖ½}ψ/ονMΨUDfꐒ‚“Ψκ ν€šzͺ.91NχΨ€τ›S1WΐβC,ΞΚ‰’fΫΩΥύ‡^ηΆ_pώÈψL „ 1εξ=z¨ίFlk$γr3Μ4’CjΖνϋΜ!Ϋn#7…ςj4G`¨¨,@»tΌεςΠ"’)D lN$§}Tγ~Y‚ΰXNφ ™ƒAχΎυi=¨žQL”jF ΆΓΓκͺ|!EΞJxq(tYG%Ψΐ,K)τK§υΟΦv˜„% ’ϋ*I­4zC[V–s!sŸ’~IŠAΑ/ϋLO'ΑšΫ‚rt'`…²²Εφœ+y9VOγ\Τρωπ³V]Φγuρϋ˜-ΦΖρOLΨ¬>™ΐ,ώpξ"ΑŠKΊyάάB ± –˜ ΅CSΚaN0†΅„η[iƒw–ΟŽ}6―ΐ"z “eά±Κ—{ΆzείšώυΏN7P·NRΝ1΅›6š’Βy ₯c[JΛCυmΰ ³ΘΘάM,MGΑΟ–b|}\¬h`Ξηr€"«Ψˆˆ‹~Ψ<^,[ΰ°xϊ.χpε.^Ϊεfμ½+qπ0ιΠ‚ΣjΜ%¨s”g0&Τ+WJW₯+5Ψ“SBΗ›rΓƒJsδQ.QJ’ξI~‹-ΌΏ₯― \ΙΓ6ƒƒTι‘²=«qYk―{Ιe" «νkΥYϊξž9Gλ¬ sYI ²q1’ˆΏj`m⨃ε΄ψΎΐη œš^uΪ8Α}μήΜ掽Lͺ¬2Ύ2›Ύ$|9X©U˜2S\hά!~‘˜ϊϋ@σY²‹MδοΎj$ΟA¨Ήψ+ΛZέ56"戴3 Ρ`ΒHθΔ™žζtΈε’ ‘³ŠάMœ ΖΰY$ψ&Ύ]Sw\NΨŸ\%f½’©ž­AE0~ΒΘόCTKΖ ΐ±’Teqž΅„ΡwW'Ψ‘4³o ): ‚#δ0—«δ„Lμ>iCAφrόυΈΔ€λ¨}l‹κ>.οΧ³Ο‘νΥόάρWΣ­¨‚₯*P%uζπζzŸƒn΄”MΈ‰©}cϊ}Α‡Σ2γ~Β^Ώ[3t¦όD©υzΈ.n ί­ͺ\~Xh/’VΗE΄Ιθ£[€ΊΝμ‰ΑAλ£=X(`PQy @Οcόγ§›ΪUΈg₯‡ͺφ± cE6»ϊTK±ΊlΜαΕ@#uf3¨ΗΜΖΕ3΅Tβ‚ώ¨ύΐϋB°XfDCΑ4&1ΊκŒSΫC·_ϊz‘2°Ι9œ(ήTθœb$ΗΈ=N#ΗrΗΑ1γE* MΩΤ{8‘|~.ztηΣFν—Ό |§B4ς(&ƒσVΒtvΥ€<–`6`Κ|U² JΏ"[tρxχ Ψ`ΣZθτΰŸ¦΅j-ša,cnxυSΜρquTœNί μΕ£ˆ:·ˆ<ŠψΗ½{DΤ#ω/κt΄ΊuΠ’ZγnVaD€@ςΏ ­ƒ»ΊJ*‚< Sί₯ΛMω/¨¨d Lρ"pmž2θΕq83MχrPn­γ™μΈ·νHoΐCΌ7eήpα>|ΌˆŠ=Š©:3sX !ZlΠ›Β2ξͺ(1AŠM£.ΣgΛΫΕ?*HŠΝ’e»6τd¬„γΧV³ΫΝ ε7ρφDWΕ„λR9–P™·§=LΊϋOΆΦ xh5ήυ‹°Τ@„θΗ―bZώΨΰX7ƒxQMΙ‚2&1ιŠƒ)έ›Ϋ' ;ύYωΛm9zβš‡3ΜUΑμμǏo9#>Λ)YΉΪ{Λ2”>)˜lζ»Ώ―ΦX°<pf©„%EYJΫΜhχιΰφ“{Rž31Ό^&κ <έZœ3`§’¬=—o-‡VξC!Ξ9—]<όQυ±iK_ρpτ~μΙ¬νρ(eΟ­­ŽD|`_]D>)sX: Ό6§“xμ+ΈͺW₯γ­½±ŽaQ±› ΠΏΓ’Λ―σΕ'(λ‡λύΟ_ ¬%ΡίŠuΦV@¬:ψω¬€ΰεK;•#ΏΣΥξΜrŒΓ<ΓKPmCwœf IO~Ί`2ŒϋωNώ£Σkε>Ξ8„½9m‡lͺυ”σK=^uŽ™λbVξχ©σΓq蒁±`ZdEeh-ζ4ό”ΆˆM@±%DΫ6λΞΙ $2zќk’^ϋULΗ―Γ‚“:΅— ŸEœ‰²N «l =ΎΗ΄βύWέχ΄0ΫLό­ύœύ\],ΎΡ=T,άΊσCc¦΄`@ιπλ΅Ηύ¦lΡ:Η·Γ±όHpΩ„L›½1/$ή±,¬lςω ΣZΚΔbΑLεekšΦ±Si"lœ•,σ8}=@¨ΑŒcΗ38a §”XkΑ λ•ωζ†θŒΖΏ½hφ9όoπέΗ„άε27m«MΪι^ΐT_7ΈΑ@κ{1†ͺ!‚n z-ΙχΙWxf½T<u5Ζπ %¨:κβξΑ³'ϋΧGŸΖ‡»XPΔPj-¬eά ŽHuΡCλ[Ϋc³H0ΦtΦΰ₯§ˆ‰ώθζν‚   –‰…Χtz«@£"if°άf”‘·€ΦRΦ― Μ7³Εμ1Šp `χOγ:Ά’QϊΤ@«W2š'ΜC2₯yvΔ*‘Š“νA½w­ή₯Ϊ »QtΒ5FΗ`!₯MΏg¨φ|φ›D₯ΜFV€z{J4Τ<§…Sj*V‚)“u9kαX|gd»U3$Tw^)΅­@`a‹·”Ηd2φφY“ •‡!ωΗΪ§]Bx¨ΤI΄Θ§ !Lj;ΔχΉŽ`Ό)ہDzKyΫ§TΫ)Qοα.zξεˆκ+”bS€ήUœ1 ‘fχυ₯>ΫJh“‰ΖB\‡ζΔΨd4Ά±,{ϊ­ƒ‹χ(X-ϊgJ’’Š7ζ‹Άψ™1Η¨ξtΤ0‡pΔϋ•gg½Εs°έoRD°γv#% gu• hDMρ˜RžuJωΊ`NΣ;[ΔΔύeX-ίο8Ζn  Ψ˜έΪ2β₯k£ϊ»YΏvυJ~χ•­•†=֏dί‡AVŸ¨΄"/?Mϊ’4μέ«²=‘Œ ‡φ;aeRU…W' \α@ώ4” CAΏN©:ηRXτΰ°xΟν’ΰΰό˜ €P ‡n—Σ|ŸΧI?J„―Pu~ΟˆΕ‡Θ…!3ζ³ΰ’ ;,6΄$άb Ξ‚DΖ€Gόΐ›jοΕWΆlξC¨E  t©©.”5 ¨5¨}Έn=iα_i}/ˆΊΟw™\ζ€ΐΐΐ½ξ+ΥΕώ»Y εbyt—CλΆGt<ΫP ƒ`5Ά΅ύ3q΄/6bm{NθzŝMG$μ›Τζϊψ,°ξ† Ό('8J“Ž’h@n7°8!+ϊ :Y“œ_~ΐΌΈ€/ΒΕk+K£€ΏxΞЕb3 ,*Uͺΐμ6‹h₯nΆαγΞBCΒOwφa0u„ΰ™ΑCΊ’‰Ο}΄κζΧ9'%μ‡l °ιΌšƒkψpΙΖh`sɚ>n㠈‘)”ŒBσ†1 HCΚi '^@Φ ς `ΓΖ-†ƒI¬υ™O›‡?žf†"PPg$Žc)0QZ¦’ev[³ΤώœA!ΌB Ζ½ Ή`ZΛ•θηJ²“Ζ/λ‹Φ:XΌi+½z΅_2‘₯―°κ‘Qλ&ΰUuΧW“PhfΣ’?²Α± ά4’RF €6X}εiο\™ϊΗΊl:ΖκΏφ' {C1Ε‡‘“lu0DJ+ΠκW 9π1Γα  –^ρt„OD}\—’`ΒMHΔX€“˜¨J„€ G‚ΣωΖ–ͺh²λ„·΄CΓζyZ³ΘΑbyV ΛPȚ"gv"šlΝΉΒ .”€ ΡP$ΊEZ‘.“*οe•fΔ@–~ž#Qλ5φf^οωQr’€ )1‘.lJe€ΣΜ;`‘…,$q!qƒDAM$Vr±1€&—qΓ ζ½–£U»~σ˜uΒΪέYβΩ·HΐΒΤ """-[ Q|αq…Ž­₯Ž&(mPξΡΝ,’2 ΉGΉΑH"#· ¨@Ž€$uIP‘€–d,ΐŽ0»?IΌQSA΄Ϊ@ψ4 Άΐγ>@sβ €Wό AόΦ₯Ε0mΙ¨€¬ 1zCά=Σέ=Σγ-A⦄tfπ/meρ³9ΏΈ?ιPξψ#|½ΙMp]ƒΜyL„Ώ«>(ΘςωϋB*^d™Μ—(ќΘ.0ϋI+ΔdΜd1ϊ Βκ·R=Cƒ)="Š9I#Ϊn ύβEe‡YgΧΐ^=τ ppiά¨(ΚkC!rΞ «Γ Z§Δ²?³$Ÿ–pLκQ³"+\d(QA—%ΙΝbΈΠΨ/ΐŠ₯άTCb;¬ jγγ-G“ Π`Γ`ƒ–₯¨ΞSΕ«Wύ‰w-ομkϋG§οjk/: ΘΝ*ι<ϊ±εI ϊq> B¬8†’L`»€:€ςx}A€½θΰΔǜ\λhβ7τ#pΝη—Θό­°ξ0”g(„δ#MΖoh―@’Π±;ΘxΞ=zKω7ο–ώŒ»{RώΚ yU vΣ€Y,‰bΈ €³#21ή‘Έ#NΠ€sγΘ3Q@h+5 Zυ‚¨)B>±*NPW…€ ΔBx:JθΓΣΒ΅ ±Αΐ§Y™ –«’ja;”†Α`L5,“Ž2rC £„Θ"δC8ŽΣy€±Κ (h zΠVΙκ<£2sRf"–!.c1h³q%¬ΐVVTR1ΜXΟp6hZA€ZŽ 8ƒιaΦΟ4`*χ’RΪI6―ϋ€Ο‘ά‘ΑˆΑˆPΕχ>0FiI!9ωθ1A¨4™ΕŸΪid‹$€„ΩΞ \Ίΐ\ΐ\ΐ\ΐΰkYƒ9¬Π#ZZ‰λn·LR3‘1q ij5"’εJ ‘ηθςά$vϊ:ό«―·ΉΆ2Όy‚Δ+cq›ΚΕX3@h ™fSξωξθκlκ]'Œ5&6#Ζ3P€dW!­T ‘V†€u£­³X΅`΄ Z¨KΰΦΎλ7¦ˆ1#`ΐϋ¨7$ŽC*u|δ˜7†Ν¦£7΅¬{SΜώΛΠκ†°lcU–€Θζœ dΒ©EΣ%LA&4Μƒς fAΞ.pη9Ξc˜ζ/Jρ`ŽPΪƒBΏ1FsΥ4EJ’ ΐh…‘#B©@¨ŒEΑwΈƒΌΘ!`wœ`–9Λ½>œδ v‚θ •¬ΰͺFaf/ύάήδ\BΨ!|Γ@!Ba7™ ΰΠΔ­K©’εΞ`Ύ@χDgΓK2kη"‚2fCˆΤ ˆ {φ$‹1QzΔΐψ˜ΛΌ\e[γ ¨ƒˆ 8€EήoΘΙ”:ΛΧ«%»―</Ύf ΏΏ/}Ά^““v³ ©λ€g!XeΚ‹LqΗz02£I¨gq¨•W“#πμςιν*lkG<"ΩR•4–’iXj5©20dŽλΚͺόg)΄DγRPœΰ$9­ Ε΄’Φ *2‹(» RΔFδ±4 ΙΘ0ωΩβ €uμonΒϋϋηΛw―–U–n#§  pP#cln[˜LΣ•‚ΐΑ5„Δ`Δ„@œΚ …αXq‘x€U%¬Λ—.PΗΟr!@Δ!€ΐ茠(Σ–›¬@nBƒœN˜€GBBΐ:: :ΠD±η<ζpYχ­~{Y΅.±YφNΝΪ‘ΈΉ’+Ύb—0Ά!l †Γa¬Φk5¬‘‰Ÿ?§ϊ“νοΨ’Q˜³i7[,ͺ]έάwMΕΨ7p ΔF°5¬ Κc–ξΠ΄ΨδΠυΔP#,a0ΒĎ7pz©‹DFθŠΑW΄6›Mf£Τ+Βαj3*c3θο9І “““&‚yŒΦD4­I 'ΔHB)I%pŠΠΈzW₯ΜεYN%ΟθύY‡ΧΉX1#ΐ1ŒύΠHΟJ`~‰ξsδp 1δŸδώ™ϋΪ:£ϊ¦%'€»YΈΫΩ:OψΩ ¨‰αξ /λξƒϊ‡φ£EΡx³¨™ό:Cg—7LΣŽή…κΨ‘3ώ |f180Iΰšμά9†XUω‘‘)‘„„1ްμ„|‡γ9(0Φ 5†δ<ΕgΒ +_|;Zε” ήG1J†­ΉάGJlΩΏΩ-oYŽ<ϋΙR>—Sf¬ Ε+Œ%Ρl/°yΝ5†±Ÿ ϊΛΕw›Θ9υξ3*}δτc―Η§ΟΘΝ/4λω$°M@¬ˆΚͺe•–Ζ.Ξά‹J la“e" ‡ίœhώ·¬,θ=g ΰpΌλΟ™±Χ^Ι¬ΘυZΣ’~ _An½%ŽϋΤΒjDΗηΡ"›ό‘“5‡ΥΜ=DW±ΚL‹—›²λm Π0ύ5ώA’fΑ“[2Ύ(Bd²iΙ>¦qx mgMΥ_θUG:/ Ζ|E‡ΘUXcCCΝpFŸˆlωπΚςΞχα•ΰ=Δύ{Ν8Χ7Ω °F?€E (ΐ, $ƒ˜·σƒίΒ‹Y\˜QvRHm§ ”.\½% A…(Ι*”Cχ3MΎ/Tυ½ησίΡ~㝊n›ω.;Άώ{ŽŸΠqέΧΰΑχΗμ}o<ϊ?—/P_lλφ žC—ϊίεΙλτ˜[£Ύ+‰’hυΓl(ƒΦζμˆgŸcύ"«„§X <~αœΑ(ιΏp>SόPšΞžDΟz\ŒcΌϋΫ"kΞδvέRˆ ‚ι†δmύ£ίžΟρηΚε>ΣϊΥO½³{’ψ£x/qςΊ°?ZκΓο›fέ“ΆtΟά?‘2 … 6­%λyk‘Ήΰϊ–uΏ Š[–¬ΨΒ₯~DΒΌoύU3εW`ΝMj‡α|i«s§Š'eΟαV ϋ4―ΡGΆ“ώ«*«5ΈΚhϋd}γX¦­²cΥ\₯ΔΡϋ%4Žν;ήΪ6O?J~w­θގ•‡#Χ‰λIΚ‰Ζq7B7Cήκa;ΛC“m‡CRIάθ_¨ε:ΉψσΚό"ž?† [ŠΗξ~ˆ;φ'ό'uσi·«ς<ϊd–§N&Θ'i‘f„Mjͺg6υw Qœί#υΉ RΧζ»/^€m#με~ί?GS©κϊοK 3εΈ­~ε¬:“β(Β‰mΝψrrΦ‚~`ά΄ΪνL,βιάnEˆR‰B%’Μ£Žμϊ%|s+³ΗEδωΉL؏7F•šΣΡ!IΆ½2;τ{~cιi6–FΔ<·? zΈφβ>ΥD‘‹ž“%u7χΊυ؎²d ‹E•DL2BθωGB‰PΨΠΰο*ό6ξΓΗΝΝΪψ—¨ι,NœΩ O§g9‘žPl>$ξ±]=|ΛfBbsοδ›ΉΉ,q₯!ΤpζΚ>[48ZΘ²£³Λze­MΫΓ «;‹ΝάB„Œό.}π8| κ]cςσD jψ;εkλmΝ_βΤρβœBρ³σƒ/΅9οΙ―qoD§’Η“sTUB‹»8 h”MΫπN;ύ1αͺΨθyzΰ_ΜcͺGž(’RμŽzΖυπ‘£ξγƒμ«ΛŽrο$9NβŽΫ’ξ¦cƒ6¨3$dΙPPa)δΟ‡Α1R. λ€zWπΥmΉηΙλλ^-'ˆΡΡJΤaΖό²‘2£Xno8ΓΒΓ"λβў6λƞ3δΥ½Υ)ΥΗE©Ξͺϊ"απΰ @ΛΙr܌7―EC³°bg GνF"ΝόŽύ±ρΊΕςŸny2Ώ–κ]Ώ­tVδ«ςΉuε½jΥB‘ιk}οΔ7φΕρπψ}°d0ϋΛQ < @Η€>ΪgδŒyL{>$­―AN: ΙυΪD‘ςΌZΕ‘‚ϋ-€2†Ψ<‚Πjš5S€Ϋf"ΙQ΅0B ε‡y‘ΡψΔ“δ6MF‘‰‘`k ˜oΓƒ!ϊυΙcΣ3Α·ΒΆΞP‘Γ1@*X8xςΥ‘[eω°δ‰Χ₯f…ξ/ίΣUΐb!x77$Ds·…l8‡Β»…hCMΌgGΆ ΄½ bEqζhζΥwόUΦ„z#RžΦ΄š•Άλ<%Πςί³ΟΉ _Xԏ1ͺƈωCĜl-π"‹ ŽΊζ{‡±L!B΄Yμ X²΅²%ӠƏ6Ϊ–NΚͺ₯v–ΪiΏ΄ό2ω\˜Κ°φ*]?p>‘dˆΩT(Ί§Οϊnύސ58Υ߷ßgN›r˜Ζ΅ΡςλmάwLO֏¦–hg»…ΗX&9ΐ«@ݚfρc£xρlΛΟ_‰Ϊ<=Φςβά#sΟϊ¦Ά΅„ϋ}-iM8ϊŠSv&΅Η˜<ω”@ZΧ9ϋξ_xB‚&ύ™φhΠΣGζ)­―sρͺΚΖι:K΄DD€°\g‹œό 7±tDŒ“` Li11B+[†ΛΨ’ˆyc΄x3CMΖ (Θ2Ύσj_8©K#P »˜L|½ς$r‡!R Σ0( f…‘l[–ς%Eκ[¨% t.ΛJ£@^2jΰ¨7‹ζ5#°Lί!§ΐfŽb‘Τ$0cb(F(κ½Ώά0')P”ŠͺΒΰM ΑΐXHF@ήhΑ£ΎPΈAγΔηβŠ(‘Δk6@eΞN₯œΐ˜’P ‡έ&–A{Ζ`ˆ€π/•―pŒό\ΑΒ xr‡QΪaέΞ kAOWYγπx©δΆ¨“ͺΰ‹‰Π…’·ρ†j?™nZ*α™ΈΆCKοœ'?ΓD¨‘qhτή\­.|D …β’”Ν\ŐPPΠ"6d8Ζ2  ²ζη €  ‘½Π°σšΓ¬2%ε6β°‰¬!Ζ€Β-σ„Β°” ‡Dƒμ¬²Ο)Ϊ}x@ρžBΊj¬˜P@ΐ>…O† η)d,Ήs|!g܊&₯2 g—€ρςυΓͺ7N]ζδ JU>Γ™ζ F^ιQɈη>sθΐ–9 œπσxqTy3ν½¨~V΄0”ε/AΈχ{ΚβZηžφKKιΈUYω[ΔΘ. b*‡<ŠA(:-Žs#‡zΔΔΤXy[BͺΉO8eΖtΞ^Vy Β3β)ΩQ°¨σXXNΏq‰ι/Ύγƒ^rX[ ‡Q5QvΨAAΆά,²¨x·3Iœ‡³|8m’G1uΩv½6Η9Κr%yl.!9τ7Ζj,+&Ξ; ύΞ’P‹ P3ΰ)X"ZršLΖ³VomPζΙtŒ™5τ³G!@±c°d-· 0$L€‰ΘSMΧHν3”QΠs% Οϊ (‰΄‘A+ˆ9qœΗ €\e βπΎ%aN—‚‚ŽΛ”Ύ*•P}κηXΒ!eƒˆ3–Έd:DZ߁vΜBBvw²ΐΡsΊExzά:ЁCpD‘«9Ώ‘Τς”υ“ψθΗkΝQ_χΖ^r6;ˆο4 ƒ―ŽEΊΐtΔ‚x·QΌ«“‘‰Ύ±~ΣXοt/R*ͺœH@S"G“OVΙΎ³―}Vq–Χ Š!)”›­Ίƒ Ήt²Όœ aD.2 5˜xα!ϊC€j¦€Τ¨δΩ³σΈ’Δ†R'„PλΰC=@k^`¬F’Σ”›8PΫ.:Θ‘'H"<₯`ΰpUkζΡΙΛ(τœE<Β}$B…”‘4£QΠX[Νq\O₯œΩh.ΊˆB1.ΘΔΦwΜ1¨&_£Œάo#ΣτξI8cnμδ1ηkkhπ ͺ-J #εœ!ΒΔqχ4’CΌk;₯ΛVg|₯vfwŒ}Bm‹ί_ƒΞͺ½­pμwAπ‹C¦`9 ­»pcŒqΔxaΈ-’*ΌΆ'w{-ξ™Ϋ‰œυ‘Žα£ΜΒ³šΕ!Τi!—Τ=>a2t’Λ4ΆΑ4σ—°i…3l+0XVˆDΆA˜DXyO)ε2Λθ15g”ΚR _˜™P©θ;kθeξθγΒvlŒIΫ†ΈΈ4@Γ8ΐΣtέ₯ Κ~°7yƒΐ}ό¦SθϞν₯~Ž―νX΅ύΑ†“PΟΜ?ΞΓYυzZDπτӟ.ς%iW•Ήžid³·SH©†Œ`ƒdŽε6ΎT£O heO˜κkΞ-Vπ» ΦXΜζxπ—~ώΤ¨ώπ΄EEΕ(‰§έ±ΉΕBW2`ͺ%l¬ˆ ¦ΠVά³_kpΓτRΒDμΙ¦|ΈΐςΔ C²•Ÿ' QNUΖ“ΣΩCΙXνΛX;‘ΩGiοψ=vdƒO˜€hδzΎoϏC•Ξθέ»z€©i[c²ύϊqaΊ˜ƒgΤ9ΗƒγΎPμŽlX3Œ`F[ €['ίR•δ―Δ{Ug|VIΞr0£œΡbό6_rΪPμΩ΄ο˜σ5‘ŽϊNL—-–ΨΫ­KƒžΗΔϊy{F+°ϋε‡†ζxsu—Υ|c}Ηi…ςΈ‰LFx0π9TrNΑτoπ3GΣυύΟ OΩέL«•yβRΒ’‚@ζ2 $#S‡πΓΉ8B±_][Φ%s…PλF€Ν)A/49™©%©ΆΫm·­kV Z“9ΞχaμΑΉQΆΫmΆΫϋ¬ΦΑΔθ^#Τ<‡Ο(’‹‘Θf"‘β}ŽAξJc#NFΆ.ζΉΌΠ#α‚ ΑF%–‰_Σ)ΞU“ιƒΟG8Ή_ς‰jΗ™5‰q“< HJ+9Žfβ|ϊΞς υχ½D+mΪl7ω’gI3ί}e%A¬Lσ Uh—Fη[πΘ bxΗdΣJΝ°Ό˜BΒPβGX‚`ε_†xσ0u(ΫΗ ΊX  ‡hέGΘ Δθυμ§ cKhuΒ%ΧLlσ‚qψ  t³`πΒm0ήtpά2χ=‚?Fͺ‰K°ΕAαή\ς{Bκος£iVsˆ£’zJ!f[šB>.^#wHγξeFSeόŽ©π<5­„Ad—wΎ-°(ΨM3(δy­·F*Ί&ώΓνE.δΖ„8‚c·nerΔΚtΞ €%–βdρΈL>˜–DΥ>fΗ¦Υ ·‘&ΣΊšWήδ7Δ‚k‘IΙΦ5Ήθ8ο<κ‡yC@ΐω”οϋJΑΌŠΕ3NΏ¦R»ο…FdζΡΰ8Νgέ»Β± Ίd#TL+ο˜† $γ1ά Χάί†ƒC::1[Œ —„(ƒ οƒK£ΒΛμ$˜d£GΞ¨Άb|†ό*V.υ―YεΪό§£Y^oU]„!A¦ƒΣ‰GA=-: >ΩάN.[Υg·§C―δζN5Άίγ3¦ )#ΩFΌ6 ³zσΒ1މLΎκ"?‡KG2Φθ9(tR₯ΔδΗ4Lρ"1ΦM™žΎb7eσ%—(Λb₯;Dλ¦{0ΒqΨbΔ^W3C’ψΠ.Xραs‰Apν›ΛŽs"Η1 gή>R%& H•Œ.1$|&“ΖTb3¨π—ž"g€Όζ>“1aÌυ›Ž³°τžƒ”όΑ—΄ε2“0 Ε±\:9B ααd<DBΙσ‡οŒ'‡›όΐΒ oή…ΝSΚΌ>ΘύΠΗΘ8Ξ'« ϟνi|hφΌ_:tτNIΏ₯π,θύΒμΈq>Ο–‘Ÿͺ"άυT†ΟάύΏεRK?ή?φ‡Θ1’.$~‰ϊΑΨyΚȐ$žΈΞΡέθχ¬d`m34,Ay Μ Ž}Sο 8ΐT`@δ(’ΕFγκΠ¬ ˜f„4,r›ŒJ(£P`l06ƒ7ㇴ›A@ϊΖ²Ε2f$-μδ6—7°Νg1bŒM cε!π 6FC8Ξဴ:§ΰϋ Ιο:₯"ͺͺPq“ιhOͺvΘ†|ξ:άϋνμ³γ;&°¦<‡Sθ<ήΖΏ,:»gΆL'-9iΛNZrΣ–œ΄©°oG˜ηΊήυκ{Ο3ŸΖDβbͺ·Ή>ΰoŠβjw|3π#Ρ,ygί σΨ›·l‚± ‘V£Τͺ€\’©_φœζοffόαυηdΠόPς?jzΚΒΩν‰AύCψ‡YˆΞsφ€ήNg_9Ρ―9ͺΗύΔΠ†cέ-bƒx8κΠ ΘΣ37G΅ξθθޞC?@‡ςCΘy!δ<†g zΨ§Ž£sρOΆψμrxIœΗΐH‰ξ}_¦νYXΚO¬‰μMΣύi°=“¨Ά­ |!}H{ƒ!yμŽΪঊЧ¦ Q°žmοίs gΗ49Tω§¦|r±RĐ=²Xη!{Q‘‘ν`?VŒδιŸ,Φ΅«šΗ,υ3Ϋ¨ƒ#"‡ΌΓΜpˆ¦$ψI/Νψ»’)Β„‡/ :pvroom/R/utils.R0000644000176200001440000000605214142515724013110 0ustar liggesusersenv_to_logical <- function (var, default = TRUE) { res <- Sys.getenv(var, default) if (res %in% c("1", "yes", "true")) { TRUE } else if (res %in% c("0", "no", "false")) { FALSE } else { default } } is_windows <- function() { identical(tolower(Sys.info()[["sysname"]]), "windows") } is_loaded <- function(pkg) { isTRUE(pkg[[1]] %in% loadedNamespaces()) } `%||%` <- function(x, y) if (is.null(x)) y else x collapse_transformer <- function(regex = "[*]$", ...) { function(text, envir) { if (grepl(regex, text)) { text <- sub(regex, "", text) res <- eval(parse(text = text, keep.source = FALSE), envir) glue::glue_collapse(res, ...) } else { glue::identity_transformer(text, envir) } } } is_named <- function (x) { nms <- names(x) if (is.null(nms)) { return(FALSE) } all(nms != "" & !is.na(nms)) } deparse2 <- function(expr, ..., sep = "\n") { paste(deparse(expr, ...), collapse = sep) } is_syntactic <- function(x) make.names(x) == x # Conditionally exported in zzz.R # @export compare.spec_tbl_df <- function (x, y, ...) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL attr(y, "spec") <- NULL attr(y, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") class(y) <- setdiff(class(y), "spec_tbl_df") NextMethod("compare") } # Conditionally exported in zzz.R # @export compare_proxy.spec_tbl_df <- function(x, path) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") if ("path" %in% names(formals(waldo::compare_proxy))) { list(object = x, path = path) } else { x } } # Conditionally exported in zzz.R # @export as_tibble.spec_tbl_df <- function(x, ...) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") NextMethod("as_tibble") } # Conditionally exported in zzz.R # @export all.equal.spec_tbl_df <- function(target, current, ...) { attr(target, "spec") <- NULL attr(target, "problems") <- NULL attr(current, "spec") <- NULL attr(current, "problems") <- NULL class(target) <- setdiff(class(target), "spec_tbl_df") class(current) <- setdiff(class(current), "spec_tbl_df") NextMethod("all.equal") } # Conditionally exported in zzz.R # @export as.data.frame.spec_tbl_df <- function(x, ...) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") NextMethod("as.data.frame") } is_rstudio_console <- function() { !(Sys.getenv("RSTUDIO", "") == "" || Sys.getenv("RSTUDIO_TERM", "") != "") } is_rstudio_version <- function(min, max = .Machine$integer.max) { tryCatch( expr = { version <- rstudioapi::getVersion() version >= min && version < max }, error = function(e) FALSE ) } #' @importFrom methods setOldClass setOldClass(c("spec_tbl_df", "tbl_df", "tbl", "data.frame")) utctime <- function(year, month, day, hour, min, sec, psec) { utctime_(as.integer(year), as.integer(month), as.integer(day), as.integer(hour), as.integer(min), as.integer(sec), as.numeric(psec)) } vroom/R/zzz.R0000644000176200001440000000322314142515724012602 0ustar liggesusers.onUnload <- function(libpath) { library.dynam.unload("vroom", libpath) } .onLoad <- function(...) { tzdb::tzdb_initialize() # only register conflicting S3 methods if readr is not already loaded. if (!"readr" %in% loadedNamespaces()) { s3_register("base::format", "col_spec") s3_register("base::print", "col_spec") s3_register("base::print", "collector") s3_register("base::print", "date_names") s3_register("base::print", "locale") s3_register("utils::str", "col_spec") s3_register("base::all.equal", "spec_tbl_df") s3_register("base::as.data.frame", "spec_tbl_df") s3_register("tibble::as_tibble", "spec_tbl_df") s3_register("testthat::compare", "spec_tbl_df") s3_register("waldo::compare_proxy", "spec_tbl_df") } } .onAttach <- function(libname, pkgname) { env <- as.environment(paste0("package:", pkgname)) env[[".conflicts.OK"]] <- TRUE } s3_register <- function(generic, class, method = NULL) { stopifnot(is.character(generic), length(generic) == 1) stopifnot(is.character(class), length(class) == 1) pieces <- strsplit(generic, "::")[[1]] stopifnot(length(pieces) == 2) package <- pieces[[1]] generic <- pieces[[2]] if (is.null(method)) { method <- get(paste0(generic, ".", class), envir = parent.frame()) } stopifnot(is.function(method)) if (package %in% loadedNamespaces()) { registerS3method(generic, class, method, envir = asNamespace(package)) } # Always register hook in case package is later unloaded & reloaded setHook( packageEvent(package, "onLoad"), function(...) { registerS3method(generic, class, method, envir = asNamespace(package)) } ) } vroom/R/col_types.R0000644000176200001440000004627314142515724013762 0ustar liggesusers#' Create column specification #' #' `cols()` includes all columns in the input data, guessing the column types #' as the default. `cols_only()` includes only the columns you explicitly #' specify, skipping the rest. #' #' The available specifications are: (long names in quotes and string abbreviations in brackets) #' #' | function | long name | short name | description | #' | ---------- | ----------- | ---------- | ------------- | #' | `col_logical()` | "logical" | "l" | Logical values containing only `T`, `F`, `TRUE` or `FALSE`. | #' | `col_integer()` | "integer" | "i" | Integer numbers. | #' | `col_big_integer()` | "big_integer" | "I" | Big Integers (64bit), requires the `bit64` package. | #' | `col_double()` | "double", "numeric" | "d" | 64-bit double floating point numbers. #' | `col_character()` | "character" | "c" | Character string data. | #' | `col_factor(levels, ordered)` | "factor" | "f" | A fixed set of values. | #' | `col_date(format = "")` | "date" | "D" | Calendar dates formatted with the locale's `date_format`. | #' | `col_time(format = "")` | "time" | "t" | Times formatted with the locale's `time_format`. | #' | `col_datetime(format = "")` | "datetime", "POSIXct" | "T" | ISO8601 date times. | #' | `col_number()` | "number" | "n" | Human readable numbers containing the `grouping_mark` | #' | `col_skip()` | "skip", "NULL" | "_", "-" | Skip and don't import this column. | #' | `col_guess()` | "guess", "NA" | "?" | Parse using the "best" guessed type based on the input. | #' #' @param ... Either column objects created by `col_*()`, or their abbreviated #' character names (as described in the `col_types` argument of #' [vroom()]). If you're only overriding a few columns, it's #' best to refer to columns by name. If not named, the column types must match #' the column names exactly. In `col_*()` functions these are stored in the #' object. #' @param .default Any named columns not explicitly overridden in `...` #' will be read with this column type. #' @param .delim The delimiter to use when parsing. If the `delim` argument #' used in the call to `vroom()` it takes precedence over the one specified in #' `col_types`. #' @export #' @aliases col_types #' @examples #' cols(a = col_integer()) #' cols_only(a = col_integer()) #' #' # You can also use the standard abbreviations #' cols(a = "i") #' cols(a = "i", b = "d", c = "_") #' #' # Or long names (like utils::read.csv) #' cols(a = "integer", b = "double", c = "skip") #' #' # You can also use multiple sets of column definitions by combining #' # them like so: #' #' t1 <- cols( #' column_one = col_integer(), #' column_two = col_number()) #' #' t2 <- cols( #' column_three = col_character()) #' #' t3 <- t1 #' t3$cols <- c(t1$cols, t2$cols) #' t3 cols <- function(..., .default = col_guess(), .delim = NULL) { col_types <- rlang::list2(...) is_character <- vapply(col_types, is.character, logical(1)) col_types[is_character] <- lapply(col_types[is_character], col_concise) if (is.character(.default)) { .default <- col_concise(.default) } col_spec(col_types, .default, .delim) } #' @export #' @rdname cols cols_only <- function(...) { cols(..., .default = col_skip()) } # col_spec ---------------------------------------------------------------- col_spec <- function(col_types, default = col_guess(), delim) { stopifnot(is.list(col_types)) stopifnot(is.collector(default)) is_collector <- vapply(col_types, is.collector, logical(1)) if (any(!is_collector)) { stop("Some `col_types` are not S3 collector objects: ", paste(which(!is_collector), collapse = ", "), call. = FALSE) } structure( list( cols = col_types, default = default, delim = delim ), class = "col_spec" ) } is.col_spec <- function(x) inherits(x, "col_spec") #' Coerce to a column specification #' #' This is most useful for generating a specification using the short form or coercing from a list. #' #' @param x Input object #' @keywords internal #' @examples #' as.col_spec("cccnnn") #' @export as.col_spec <- function(x) UseMethod("as.col_spec") #' @export as.col_spec.character <- function(x) { if (is_named(x)) { return(as.col_spec(as.list(x))) } letters <- strsplit(x, "")[[1]] col_spec(lapply(letters, col_concise), col_guess(), delim = NULL) } #' @export as.col_spec.NULL <- function(x) { col_spec(list(), delim = NULL) } #' @export as.col_spec.list <- function(x) { do.call(cols, x) } #' @export as.col_spec.col_spec <- function(x) { if (!"delim" %in% names(x)) { x["delim"] <- list(NULL) } x } #' @export as.col_spec.default <- function(x) { stop("`col_types` must be NULL, a list or a string", call. = FALSE) } # Conditionally exported in zzz.R # @export print.col_spec <- function(x, n = Inf, condense = NULL, colour = crayon::has_color(), ...) { cat(format.col_spec(x, n = n, condense = condense, colour = colour, ...)) invisible(x) } #' @description #' `cols_condense()` takes a spec object and condenses its definition by setting #' the default column type to the most frequent type and only listing columns #' with a different type. #' @rdname spec #' @export cols_condense <- function(x) { types <- vapply(x$cols, function(xx) class(xx)[[1]], character(1)) counts <- table(types) most_common <- names(counts)[counts == max(counts)][[1]] x$default <- x$cols[types == most_common][[1]] x$cols <- x$cols[types != most_common] x } # Conditionally exported in zzz.R # @export format.col_spec <- function(x, n = Inf, condense = NULL, colour = crayon::has_color(), ...) { if (n == 0) { return("") } # condense if cols >= n condense <- condense %||% (length(x$cols) >= n) if (isTRUE(condense)) { x <- cols_condense(x) } # truncate to minumum of n or length cols <- x$cols[seq_len(min(length(x$cols), n))] default <- NULL if (inherits(x$default, "collector_guess")) { fun_type <- "cols" } else if (inherits(x$default, "collector_skip")) { fun_type <- "cols_only" } else { fun_type <- "cols" type <- sub("^collector_", "", class(x$default)[[1]]) default <- paste0(".default = col_", type, "()") } delim <- x$delim if (!is.null(delim) && nzchar(delim)) { delim <- paste0('.delim = ', double_quote(delim), '') } cols_args <- c( default, vapply(seq_along(cols), function(i) { col_funs <- sub("^collector_", "col_", class(cols[[i]])[[1]]) args <- vapply(cols[[i]], deparse2, character(1), sep = "\n ") args <- paste(names(args), args, sep = " = ", collapse = ", ") col_funs <- paste0(col_funs, "(", args, ")") col_funs <- colourise_cols(col_funs, colour) col_names <- names(cols)[[i]] %||% "" # Need to handle unnamed columns and columns with non-syntactic names named <- col_names != "" non_syntactic <- !is_syntactic(col_names) & named col_names[non_syntactic] <- paste0("`", gsub("`", "\\\\`", col_names[non_syntactic]), "`") out <- paste0(col_names, " = ", col_funs) out[!named] <- col_funs[!named] out }, character(1) ), delim ) if (length(x$cols) == 0 && length(cols_args) == 0) { return(paste0(fun_type, "()\n")) } out <- paste0(fun_type, "(\n ", paste(collapse = ",\n ", cols_args)) if (length(x$cols) > n) { out <- paste0(out, "\n # ... with ", length(x$cols) - n, " more columns") } out <- paste0(out, "\n)\n") out } colourise_cols <- function(cols, colourise = crayon::has_color()) { if (!isTRUE(colourise)) { return(cols) } fname <- sub("[(].*", "", cols) for (i in seq_along(cols)) { cols[[i]] <- switch(fname, col_skip = , col_guess = cols[[i]], col_character = , col_factor = crayon::red(cols[[i]]), col_logical = crayon::yellow(cols[[i]]), col_double = , col_integer = , col_number = crayon::green(cols[[i]]), col_date = , col_datetime = , col_time = crayon::blue(cols[[i]]) ) } cols } # This allows str() on a tibble object to print a little nicer. # Conditionally exported in zzz.R # @export str.col_spec <- function(object, ..., indent.str = "") { # Split the formatted column spec into strings specs <- strsplit(format(object), "\n")[[1]] cat(sep = "", "\n", # Append the current indentation string to the specs paste(indent.str, specs, collapse = "\n"), "\n") } #' Examine the column specifications for a data frame #' #' `spec()` extracts the full column specification from a tibble #' created by readr. #' #' @family parsers #' @param x The data frame object to extract from #' @return A col_spec object. #' @export #' @examples #' df <- vroom(vroom_example("mtcars.csv")) #' s <- spec(df) #' s #' #' cols_condense(s) spec <- function(x) { stopifnot(inherits(x, "tbl_df")) attr(x, "spec") } col_concise <- function(x) { switch(x, "_" = , "skip" =, "NULL" =, "-" = col_skip(), "NA" = , "?" = col_guess(), character =, c = col_character(), factor =, f = col_factor(), double =, numeric =, d = col_double(), integer =, i = col_integer(), big_integer =, I = col_big_integer(), logical = , l = col_logical(), number = , n = col_number(), date = , Date = , D = col_date(), datetime = , POSIXct = , T = col_datetime(), time =, t = col_time(), stop("Unknown shortcut: ", x, call. = FALSE) ) } vroom_enquo <- function(x) { if (rlang::quo_is_call(x, "c") || rlang::quo_is_call(x, "list")) { return(rlang::as_quosures(rlang::get_expr(x)[-1], rlang::get_env(x))) } x } vroom_select <- function(x, col_select, id) { spec <- attr(x, "spec") # Drop any NULL columns is_null <- vapply(x, is.null, logical(1)) x[is_null] <- NULL # reorder and rename columns if (inherits(col_select, "quosures") || !rlang::quo_is_null(col_select)) { if (inherits(col_select, "quosures")) { vars <- tidyselect::vars_select(c(id, names(spec(x)$cols)), !!!col_select) } else { vars <- tidyselect::vars_select(c(id, names(spec(x)$cols)), !!col_select) } # This can't be just names(x) as we need to have skipped # names as well to pass to vars_select() x <- x[vars] names(x) <- names(vars) } attr(x, "spec") <- spec x } col_types_standardise <- function(spec, num_cols, col_names, col_select, name_repair) { if (num_cols == 0) { if (length(spec$cols) > 0) { num_cols <- length(spec$cols) } else if (length(col_names) > 0) { num_cols <- length(col_names) } } if (length(col_names) == 0) { col_names <- make_names(NULL, num_cols) } col_names <- vctrs::vec_as_names(col_names, repair = name_repair) type_names <- names(spec$cols) if (length(spec$cols) == 0) { # no types specified so use defaults spec$cols <- rep(list(spec$default), num_cols) names(spec$cols) <- col_names[seq_along(spec$cols)] } else if (is.null(type_names)) { # unnamed types & names guessed from header: match exactly if (num_cols < length(spec$cols)) { spec$cols <- spec$cols[seq_len(num_cols)] } else { spec$cols <- c(spec$cols, rep(list(spec$default), num_cols - length(spec$cols))) } names(spec$cols) <- col_names[seq_along(spec$cols)] } else { # named types if (num_cols > length(col_names)) { col_names <- make_names(col_names, num_cols) } bad_types <- !(type_names %in% col_names) if (any(bad_types)) { rlang::warn(paste0("The following named parsers don't match the column names: ", paste0(type_names[bad_types], collapse = ", ")), class = "vroom_mismatched_column_name") spec$cols <- spec$cols[!bad_types] type_names <- type_names[!bad_types] } default_types <- !(col_names %in% type_names) if (any(default_types)) { defaults <- rep(list(spec$default), sum(default_types)) names(defaults) <- col_names[default_types] spec$cols[names(defaults)] <- defaults } spec$cols <- spec$cols[col_names] } if (inherits(col_select, "quosures") || !rlang::quo_is_null(col_select)) { if (inherits(col_select, "quosures")) { to_keep <- names(spec$cols) %in% tidyselect::vars_select(names(spec$cols), !!!col_select, .strict = FALSE) } else { to_keep <- names(spec$cols) %in% tidyselect::vars_select(names(spec$cols), !!col_select, .strict = FALSE) } spec$cols[!to_keep] <- rep(list(col_skip()), sum(!to_keep)) } # Set the names, ignoring skipped columns kept <- !vapply(spec$cols, inherits, logical(1), "collector_skip") # Fill the column names if they are shorter than what is kept. if (length(col_names) == length(spec$cols)) { names(spec$cols)[kept] <- col_names[kept] } else if (length(col_names) == sum(kept)) { names(spec$cols)[kept] <- col_names } else { col_names <- make_names(col_names, sum(kept)) names(spec$cols)[kept] <- col_names } spec } #' Guess the type of a vector #' #' @inheritParams readr::guess_parser #' @examples #' # Logical vectors #' guess_type(c("FALSE", "TRUE", "F", "T")) #' # Integers and doubles #' guess_type(c("1","2","3")) #' guess_type(c("1.6","2.6","3.4")) #' # Numbers containing grouping mark #' guess_type("1,234,566") #' # ISO 8601 date times #' guess_type(c("2010-10-10")) #' guess_type(c("2010-10-10 01:02:03")) #' guess_type(c("01:02:03 AM")) #' @export guess_type <- function(x, na = c("", "NA"), locale = default_locale(), guess_integer = FALSE) { type <- guess_type_(x, na = na, locale = locale, guess_integer = guess_integer) get(paste0("col_", type), asNamespace("vroom"))() } guess_parser <- function(x, na = c("", "NA"), locale = default_locale(), guess_integer = FALSE) { guess_type_(x, na = na, locale = locale, guess_integer = guess_integer) } show_dims <- function(x) { cli_block(class = "vroom_dim_message", { cli::cli_text(" {.strong Rows: }{.val {NROW(x)}} {.strong Columns: }{.val {NCOL(x)}} ") }) } collector_value <- function(x, ...) { UseMethod("collector_value") } #' @export collector_value.collector_character <- function(x, ...) { character() } #' @export collector_value.collector_double <- function(x, ...) { numeric() } #' @export collector_value.collector_integer <- function(x, ...) { integer() } #' @export collector_value.collector_numeric <- function(x, ...) { numeric() } #' @export collector_value.collector_logical <- function(x, ...) { logical() } #' @export collector_value.collector_factor <- function(x, ...) { factor() } #' @export collector_value.collector_datetime <- function(x, ...) { as.POSIXct(double()) } #' @export collector_value.collector_date <- function(x, ...) { as.Date(double()) } #' @export collector_value.collector_time <- function(x, ...) { hms::hms() } #' @export collector_value.collector_guess <- function(x, ...) { character() } #' @importFrom crayon silver #' @importFrom glue double_quote #' @export summary.col_spec <- function(object, width = getOption("width"), locale = default_locale(), ...) { if (length(object$cols) == 0) { return(invisible(object)) } type_map <- c("collector_character" = "chr", "collector_double" = "dbl", "collector_integer" = "int", "collector_num" = "num", "collector_logical" = "lgl", "collector_factor" = "fct", "collector_datetime" = "dttm", "collector_date" = "date", "collector_time" = "time", "collector_guess" = "???") col_types <- vapply(object$cols, function(x) class(x)[[1]], character(1)) col_types <- droplevels(factor(type_map[col_types], levels = unname(type_map))) type_counts <- table(col_types) n <- length(type_counts) types <- format(vapply(names(type_counts), color_type, character(1))) counts <- format(glue::glue("({type_counts})"), justify = "right") col_width <- min(width - (crayon::col_nchar(types) + nchar(counts) + 4)) columns <- vapply(split(names(object$cols), col_types), function(x) glue::glue_collapse(x, ", ", width = col_width), character(1)) fmt_num <- function(x) { prettyNum(x, big.mark = locale$grouping_mark, decimal.mark = locale$decimal_mark) } delim <- object$delim %||% "" txt <- glue::glue( .transformer = collapse_transformer(sep = "\n"), entries = glue::glue("{format(types)} {counts}: {columns}"), ' {if (nzchar(delim)) paste(bold("Delimiter:"), double_quote(delim)) else ""} {entries*} ') cli_block(class = "vroom_spec_message", { cli::cli_h1("Column specification") cli::cli_verbatim(txt) }) invisible(object) } show_col_types <- function(x, locale) { show_dims(x) summary(spec(x), locale = locale) cli_block(class = "vroom_spec_message", { cli::cli_verbatim("\n\n") cli::cli_alert_info("Use {.fn spec} to retrieve the full column specification for this data.") cli::cli_alert_info("Specify the column types or set {.arg show_col_types = FALSE} to quiet this message.") }) } cli_block <- function(expr, class = NULL, type = rlang::inform) { msg <- "" withCallingHandlers( expr, message = function(x) { msg <<- paste0(msg, x$message) invokeRestart("muffleMessage") } ) msg <- sub("^\n", "", msg) msg <- sub("\n+$", "", msg) type(msg, class = class) } color_type <- function(type) { switch(type, chr = , fct = crayon::red(type), lgl = crayon::yellow(type), dbl = , int = , num = crayon::green(type), date = , dttm = , time = crayon::blue(type), "???" = type ) } #' @rdname cols #' @export col_logical <- function(...) { collector("logical", ...) } #' @rdname cols #' @export col_integer <- function(...) { collector("integer", ...) } #' @rdname cols #' @export col_big_integer <- function(...) { collector("big_integer", ...) } #' @rdname cols #' @export col_double <- function(...) { collector("double", ...) } #' @rdname cols #' @export col_character <- function(...) { collector("character", ...) } #' @rdname cols #' @export col_skip <- function(...) { collector("skip", ...) } #' @rdname cols #' @export col_number <- function(...) { collector("number", ...) } #' @rdname cols #' @export col_guess <- function(...) { collector("guess", ...) } #' @inheritParams readr::col_factor #' @rdname cols #' @export col_factor <- function(levels = NULL, ordered = FALSE, include_na = FALSE, ...) { collector("factor", levels = levels, ordered = ordered, include_na = include_na, ...) } #' @inheritParams readr::col_datetime #' @rdname cols #' @export col_datetime <- function(format = "", ...) { collector("datetime", format = format, ...) } #' @rdname cols #' @export col_date <- function(format = "", ...) { collector("date", format = format, ...) } #' @rdname cols #' @export col_time <- function(format = "", ...) { collector("time", format = format, ...) } vroom/R/vroom_fwf.R0000644000176200001440000001416014142515724013753 0ustar liggesusers#' Read a fixed width file into a tibble #' #' @details #' *Note*: `fwf_empty()` cannot take a R connection such as a URL as input, as #' this would result in reading from the connection twice. In these cases it is #' better to download the file first before reading. #' @inheritParams readr::read_fwf #' @inheritParams vroom #' @export #' @examples #' fwf_sample <- vroom_example("fwf-sample.txt") #' cat(readLines(fwf_sample)) #' #' # You can specify column positions in several ways: #' # 1. Guess based on position of empty columns #' vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) #' # 2. A vector of field widths #' vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) #' # 3. Paired vectors of start and end positions #' vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) #' # 4. Named arguments with start and end positions #' vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) #' # 5. Named arguments with column widths #' vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) vroom_fwf <- function(file, col_positions = fwf_empty(file, skip, n = guess_max), col_types = NULL, col_select = NULL, id = NULL, locale = default_locale(), na = c("", "NA"), comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, skip = 0, n_max = Inf, guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique") { verify_fwf_positions(col_positions) if (!rlang::is_missing(altrep_opts)) { lifecycle::deprecate_warn("1.1.0", "vroom_fwf(altrep_opts = )", "vroom_fwf(altrep = )") altrep <- altrep_opts } file <- standardise_path(file) if (!is_ascii_compatible(locale$encoding)) { file <- reencode_path(file, locale$encoding) locale$encoding <- "UTF-8" } if (length(file) == 0 || (n_max == 0 & identical(col_positions$col_names, FALSE))) { out <- tibble::tibble() class(out) <- c("spec_tbl_df", class(out)) return(out) } if (n_max < 0 || is.infinite(n_max)) { n_max <- -1 } if (guess_max < 0 || is.infinite(guess_max)) { guess_max <- -1 } col_select <- vroom_enquo(rlang::enquo(col_select)) has_col_types <- !is.null(col_types) col_types <- as.col_spec(col_types) out <- vroom_fwf_(file, as.integer(col_positions$begin), as.integer(col_positions$end), trim_ws = trim_ws, col_names = col_positions$col_names, col_types = col_types, col_select = col_select, name_repair = .name_repair, id = id, na = na, guess_max = guess_max, skip = skip, comment = comment, skip_empty_rows = skip_empty_rows, n_max = n_max, num_threads = num_threads, altrep = vroom_altrep(altrep), locale = locale, progress = progress) out <- tibble::as_tibble(out, .name_repair = .name_repair) out <- vroom_select(out, col_select, id) class(out) <- c("spec_tbl_df", class(out)) if (should_show_col_types(has_col_types, show_col_types)) { show_col_types(out, locale) } out } #' @rdname vroom_fwf #' @inheritParams readr::read_fwf #' @export #' @param n Number of lines the tokenizer will read to determine file structure. By default #' it is set to 100. fwf_empty <- function(file, skip = 0, col_names = NULL, comment = "", n = 100L) { file <- standardise_one_path(standardise_path(file)[[1]]) if (inherits(file, "connection")) { stop("`file` must be a regular file, not a connection", call. = FALSE) } if (n < 0 || is.infinite(n)) { n <- -1 } out <- whitespace_columns_(file[[1]], skip, comment = comment, n = n) out$end[length(out$end)] <- NA col_names <- fwf_col_names(col_names, length(out$begin)) out$col_names <- col_names out } #' @rdname vroom_fwf #' @export #' @param widths Width of each field. Use NA as width of last field when #' reading a ragged fwf file. #' @param col_names Either NULL, or a character vector column names. fwf_widths <- function(widths, col_names = NULL) { pos <- cumsum(c(1L, abs(widths))) fwf_positions(pos[-length(pos)], pos[-1] - 1L, col_names) } #' @rdname vroom_fwf #' @export #' @param start,end Starting and ending (inclusive) positions of each field. #' Use NA as last end field when reading a ragged fwf file. fwf_positions <- function(start, end = NULL, col_names = NULL) { stopifnot(length(start) == length(end)) col_names <- fwf_col_names(col_names, length(start)) tibble::tibble( begin = start - 1L, end = end, # -1 to change to 0 offset, +1 to be exclusive, col_names = as.character(col_names) ) } #' @rdname vroom_fwf #' @export #' @param ... If the first element is a data frame, #' then it must have all numeric columns and either one or two rows. #' The column names are the variable names. The column values are the #' variable widths if a length one vector, and if length two, variable start and end #' positions. The elements of `...` are used to construct a data frame #' with or or two rows as above. fwf_cols <- function(...) { x <- lapply(list(...), as.integer) names(x) <- fwf_col_names(names(x), length(x)) x <- tibble::as_tibble(x) if (nrow(x) == 2) { fwf_positions(as.integer(x[1, ]), as.integer(x[2, ]), names(x)) } else if (nrow(x) == 1) { fwf_widths(as.integer(x[1, ]), names(x)) } else { stop("All variables must have either one (width) two (start, end) values.", call. = FALSE) } } fwf_col_names <- function(nm, n) { nm <- nm %||% rep("", n) nm_empty <- (nm == "") nm[nm_empty] <- paste0("X", seq_len(n))[nm_empty] nm } verify_fwf_positions <- function(col_positions) { is_greater <- stats::na.omit(col_positions$begin > col_positions$end) if (any(is_greater)) { bad <- which(is_greater) stop("`col_positions` must have begin less than end.\n* Invalid values at position(s): ", paste0(collapse = ", ", bad), call. = FALSE) } } vroom/R/example.R0000644000176200001440000000144014142512075013373 0ustar liggesusers#' Get path to vroom examples #' #' vroom comes bundled with a number of sample files in #' its 'inst/extdata' directory. Use `vroom_examples()` to list all the #' available examples and `vroom_example()` to retrieve the path to one #' example. #' @param path Name of file. #' @param pattern A regular expression of filenames to match. If `NULL` all #' available files are returned. #' listed. #' @export #' @examples #' # List all available examples #' vroom_examples() #' #' # Get path to one example #' vroom_example("mtcars.csv") vroom_example <- function (path) { system.file("extdata", path, package = "vroom", mustWork = TRUE) } #' @rdname vroom_example #' @export vroom_examples <- function (pattern = NULL) { list.files(system.file("extdata", package = "vroom"), pattern = pattern) } vroom/R/cpp11.R0000644000176200001440000000562414151153615012675 0ustar liggesusers# Generated by cpp11: do not edit by hand force_materialization <- function(x) { invisible(.Call(`_vroom_force_materialization`, x)) } vroom_materialize <- function(x, replace) { .Call(`_vroom_vroom_materialize`, x, replace) } vroom_convert <- function(x) { .Call(`_vroom_vroom_convert`, x) } vroom_str_ <- function(x) { .Call(`_vroom_vroom_str_`, x) } gen_character_ <- function(n, min, max, values, seed, seed2) { .Call(`_vroom_gen_character_`, n, min, max, values, seed, seed2) } guess_type_ <- function(input, na, locale, guess_integer) { .Call(`_vroom_guess_type_`, input, na, locale, guess_integer) } convert_connection <- function(in_con, out_con, from, to) { .Call(`_vroom_convert_connection`, in_con, out_con, from, to) } utctime_ <- function(year, month, day, hour, min, sec, psec) { .Call(`_vroom_utctime_`, year, month, day, hour, min, sec, psec) } vroom_errors_ <- function(errors) { .Call(`_vroom_vroom_errors_`, errors) } vroom_fwf_ <- function(inputs, col_starts, col_ends, trim_ws, col_names, col_types, col_select, name_repair, skip, comment, skip_empty_rows, n_max, id, na, locale, guess_max, num_threads, altrep, progress) { .Call(`_vroom_vroom_fwf_`, inputs, col_starts, col_ends, trim_ws, col_names, col_types, col_select, name_repair, skip, comment, skip_empty_rows, n_max, id, na, locale, guess_max, num_threads, altrep, progress) } whitespace_columns_ <- function(filename, skip, n, comment) { .Call(`_vroom_whitespace_columns_`, filename, skip, n, comment) } vroom_write_ <- function(input, filename, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines) { invisible(.Call(`_vroom_vroom_write_`, input, filename, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines)) } vroom_write_connection_ <- function(input, con, delim, eol, na_str, col_names, options, num_threads, progress, buf_lines, is_stdout, append) { invisible(.Call(`_vroom_vroom_write_connection_`, input, con, delim, eol, na_str, col_names, options, num_threads, progress, buf_lines, is_stdout, append)) } vroom_format_ <- function(input, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines) { .Call(`_vroom_vroom_format_`, input, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines) } vroom_ <- function(inputs, delim, quote, trim_ws, escape_double, escape_backslash, comment, skip_empty_rows, skip, n_max, progress, col_names, col_types, col_select, name_repair, id, na, locale, guess_max, num_threads, altrep) { .Call(`_vroom_vroom_`, inputs, delim, quote, trim_ws, escape_double, escape_backslash, comment, skip_empty_rows, skip, n_max, progress, col_names, col_types, col_select, name_repair, id, na, locale, guess_max, num_threads, altrep) } has_trailing_newline <- function(filename) { .Call(`_vroom_has_trailing_newline`, filename) } vroom_rle <- function(input) { .Call(`_vroom_vroom_rle`, input) } vroom/R/date.R0000644000176200001440000000464114142515724012667 0ustar liggesusers#' Create or retrieve date names #' #' When parsing dates, you often need to know how weekdays of the week and #' months are represented as text. This pair of functions allows you to either #' create your own, or retrieve from a standard list. The standard list is #' derived from ICU (`https://site.icu-project.org`) via the _stringi_ package. #' #' @param mon,mon_ab Full and abbreviated month names. #' @param day,day_ab Full and abbreviated week day names. Starts with Sunday. #' @param am_pm Names used for AM and PM. #' @export #' @examples #' date_names_lang("en") #' date_names_lang("ko") #' date_names_lang("fr") date_names <- function(mon, mon_ab = mon, day, day_ab = day, am_pm = c("AM", "PM")) { stopifnot(is.character(mon), length(mon) == 12) stopifnot(is.character(mon_ab), length(mon_ab) == 12) stopifnot(is.character(day), length(day) == 7) stopifnot(is.character(day_ab), length(day_ab) == 7) structure( list( mon = enc2utf8(mon), mon_ab = enc2utf8(mon_ab), day = enc2utf8(day), day_ab = enc2utf8(day_ab), am_pm = enc2utf8(am_pm) ), class = "date_names" ) } #' @export #' @rdname date_names #' @param language A BCP 47 locale, made up of a language and a region, #' e.g. `"en_US"` for American English. See `date_names_langs()` #' for a complete list of available locales. date_names_lang <- function(language) { stopifnot(is.character(language), length(language) == 1) symbols <- date_symbols[[language]] if (is.null(symbols)) { stop("Unknown language '", language, "'", call. = FALSE) } symbols } #' @export #' @rdname date_names date_names_langs <- function() { names(date_symbols) } # Conditionally exported in zzz.R # @export print.date_names <- function(x, ...) { cat("\n") if (identical(x$day, x$day_ab)) { day <- paste0(x$day, collapse = ", ") } else { day <- paste0(x$day, " (", x$day_ab, ")", collapse = ", ") } if (identical(x$mon, x$mon_ab)) { mon <- paste0(x$mon, collapse = ", ") } else { mon <- paste0(x$mon, " (", x$mon_ab, ")", collapse = ", ") } am_pm <- paste0(x$am_pm, collapse = "/") cat_wrap("Days: ", day) cat_wrap("Months: ", mon) cat_wrap("AM/PM: ", am_pm) } is.date_names <- function(x) inherits(x, "date_names") cat_wrap <- function(header, body) { body <- strwrap(body, exdent = nchar(header)) cat(header, paste(body, collapse = "\n"), "\n", sep = "") } vroom/R/path.R0000644000176200001440000001642614142515724012712 0ustar liggesusersis_ascii_compatible <- function(encoding) { identical(iconv(list(charToRaw("\n")), from = "ASCII", to = encoding, toRaw = TRUE)[[1]], charToRaw("\n")) } reencode_path <- function(path, encoding) { if (length(path) > 1) { stop(sprintf("Reading files of encoding '%s' can only be done for single files at a time", encoding), call. = FALSE) } if (inherits(path[[1]], "connection")) { in_con <- path[[1]] } else { in_con <- file(path[[1]]) } out_file <- tempfile() out_con <- file(out_file) convert_connection(in_con, out_con, encoding, "UTF-8") withr::defer(unlink(out_file), envir = parent.frame()) return(list(out_file)) } # These functions adapted from https://github.com/tidyverse/readr/blob/192cb1ca5c445e359f153d2259391e6d324fd0a2/R/source.R standardise_path <- function(path) { if (is.raw(path)) { return(list(rawConnection(path, "rb"))) } if (inherits(path, "connection")) { # If the connection is `stdin()`, change it to `file("stdin")`, as we don't # support text mode connections. if (path == stdin()) { return(list(file("stdin"))) } return(list(path)) } if (is.character(path)) { if (inherits(path, "AsIs")) { if (length(path) > 1) { path <- paste(path, collapse = "\n") } return(list(chr_to_file(path, envir = parent.frame()))) } if (any(grepl("\n", path))) { lifecycle::deprecate_soft("1.5.0", "vroom(file = 'must use `I()` for literal data')", details = glue::glue(' # Bad: vroom("foo\\nbar\\n") # Good: vroom(I("foo\\nbar\\n")) ') ) return(list(chr_to_file(path, envir = parent.frame()))) } } as.list(path) } standardise_one_path <- function (path, write = FALSE) { if (is.raw(path)) { return(rawConnection(path, "rb")) } if (!is.character(path)) { return(path) } if (is_url(path)) { if (requireNamespace("curl", quietly = TRUE)) { con <- curl::curl(path) } else { rlang::inform("`curl` package not installed, falling back to using `url()`") con <- url(path) } ext <- tolower(tools::file_ext(path)) return( switch(ext, zip = , bz2 = , xz = { close(con) stop("Reading from remote `", ext, "` compressed files is not supported,\n", " download the files locally first.", call. = FALSE) }, gz = gzcon(con), con ) ) } p <- split_path_ext(basename(path)) if (write) { path <- normalizePath(path, mustWork = FALSE) } else { path <- check_path(path) } if (rlang::is_installed("archive")) { formats <- archive_formats(p$extension) extension <- p$extension while(is.null(formats) && nzchar(extension)) { extension <- split_path_ext(extension)$extension formats <- archive_formats(extension) } if (!is.null(formats)) { p$extension <- extension if (write) { if (is.null(formats[[1]])) { return(archive::file_write(path, filter = formats[[2]])) } return(archive::archive_write(path, p$path, format = formats[[1]], filter = formats[[2]])) } if (is.null(formats[[1]])) { return(archive::file_read(path, filter = formats[[2]])) } return(archive::archive_read(path, format = formats[[1]], filter = formats[[2]])) } } if (!write) { compression <- detect_compression(path) } else { compression <- NA } if (is.na(compression)) { compression <- tools::file_ext(path) } if (write && compression == "zip") { stop("Can only read from, not write to, .zip", call. = FALSE) } switch(compression, gz = gzfile(path, ""), bz2 = bzfile(path, ""), xz = xzfile(path, ""), zip = zipfile(path, ""), if (!has_trailing_newline(path)) { file(path) } else { path } ) } split_path_ext <- function(path) { regex <- "^([^.]*)[.](.*)" res <- regexpr(regex, path, perl = TRUE) if (length(res) == 0 || res == -1) { return(list(path = path, extension = "")) } starts <- attr(res, "capture.start")[1, ] lengths <- attr(res, "capture.length")[, ] list( path = substr(path, starts[[1]], starts[[1]] + lengths[[1]] - 1), extension = substr(path, starts[[2]], starts[[2]] + lengths[[2]] - 1) ) } # Adapted from archive:::format_and_filter_by_extension # https://github.com/r-lib/archive/blob/125f9930798dc20fa12cda30319ca3e9a134a409/R/archive.R#L73 archive_formats <- function(ext) { switch(ext, "7z" = list("7zip", "none"), "cpio" = list("cpio", "none"), "iso" = list("iso9660", "none"), "mtree" = list("mtree", "none"), "tar" = list("tar", "none"), "tgz" = list("tar", "gzip"), "taz" = list("tar", "gzip"), "tar.gz" = list("tar", "gzip"), "tbz" = list("tar", "bzip2"), "tbz2" = list("tar", "bzip2"), "tz2" = list("tar", "bzip2"), "tar.bz2" = list("tar", "bzip2"), "tlz" = list("tar", "lzma"), "tar.lzma" = list("tar", "lzma"), "txz" = list("tar", "xz"), "tar.xz" = list("tar", "xz"), "tzo" = list("tar", "lzop"), "taZ" = list("tar", "compress"), "tZ" = list("tar", "compress"), "tar.zst"= list("tar", "zstd"), "warc" = list("warc", "none"), "jar" = list("zip", "none"), "zip" = list("zip", "none"), "Z" = list(NULL, "compress"), "zst" = list(NULL, "zst"), NULL) } is_url <- function(path) { grepl("^((http|ftp)s?|sftp)://", path) } check_path <- function(path) { if (file.exists(path)) return(normalizePath(path, "/", mustWork = FALSE)) stop("'", path, "' does not exist", if (!is_absolute_path(path)) { paste0(" in current working directory ('", getwd(), "')") }, ".", call. = FALSE ) } is_absolute_path <- function(path) { grepl("^(/|[A-Za-z]:|\\\\|~)", path) } zipfile <- function(path, open = "r") { files <- utils::unzip(path, list = TRUE) file <- files$Name[[1]] if (nrow(files) > 1) { rlang::inform(paste0("Multiple files in zip: reading '", file, "'")) } unz(path, file, open = open) } utils::globalVariables("con") chr_to_file <- function(x, envir = parent.frame()) { out <- tempfile() con <- file(out, "wb") writeLines(sub("\n$", "", x), con, useBytes = TRUE) close(con) withr::defer(unlink(out), envir = envir) normalizePath(out) } detect_compression <- function(path) { bytes <- readBin(path, "raw", n = 6) if (length(bytes) >= 2 && bytes[[1]] == 0x1f && bytes[[2]] == 0x8b) { return("gz") } if (length(bytes) >= 6 && bytes[[1]] == 0xFD && bytes[[2]] == 0x37 && bytes[[3]] == 0x7A && bytes[[4]] == 0x58 && bytes[[5]] == 0x5A && bytes[[6]] == 0x00) { return("xz") } if (length(bytes) >= 3 && bytes[[1]] == 0x42 && bytes[[2]] == 0x5a && bytes[[3]] == 0x68) { return("bz2") } # normal zip if (length(bytes) >= 4 && bytes[[1]] == 0x50 && bytes[[2]] == 0x4B && bytes[[3]] == 0x03 && bytes[[4]] == 0x04) { return("zip") } # empty zip if (length(bytes) >= 4 && bytes[[1]] == 0x50 && bytes[[2]] == 0x4B && bytes[[3]] == 0x05 && bytes[[4]] == 0x06) { return("zip") } # spanned zip if (length(bytes) >= 4 && bytes[[1]] == 0x50 && bytes[[2]] == 0x4B && bytes[[3]] == 0x07 && bytes[[4]] == 0x08) { return("zip") } NA_character_ } vroom/R/collector.R0000644000176200001440000000044414142512075013731 0ustar liggesuserscollector <- function(type, ...) { structure(list(...), class = c(paste0("collector_", type), "collector")) } is.collector <- function(x) inherits(x, "collector") # Conditionally exported in zzz.R # @export print.collector <- function(x, ...) { cat("<", class(x)[1], ">\n", sep = "") } vroom/R/vroom-package.R0000644000176200001440000000042714142512075014477 0ustar liggesusers#' @keywords internal #' @aliases vroom-package "_PACKAGE" # The following block is used by usethis to automatically manage # roxygen namespace tags. Modify with care! ## usethis namespace: start #' @importFrom lifecycle deprecated deprecate_warn ## usethis namespace: end NULL vroom/R/locale.R0000644000176200001440000000770414142515724013214 0ustar liggesusers#' Create locales #' #' A locale object tries to capture all the defaults that can vary between #' countries. You set the locale in once, and the details are automatically #' passed on down to the columns parsers. The defaults have been chosen to #' match R (i.e. US English) as closely as possible. See #' `vignette("locales")` for more details. #' #' @param date_names Character representations of day and month names. Either #' the language code as string (passed on to [date_names_lang()]) #' or an object created by [date_names()]. #' @param date_format,time_format Default date and time formats. #' @param decimal_mark,grouping_mark Symbols used to indicate the decimal #' place, and to chunk larger numbers. Decimal mark can only be `,` or #' `.`. #' @param tz Default tz. This is used both for input (if the time zone isn't #' present in individual strings), and for output (to control the default #' display). The default is to use "UTC", a time zone that does not use #' daylight savings time (DST) and hence is typically most useful for data. #' The absence of time zones makes it approximately 50x faster to generate #' UTC times than any other time zone. #' #' Use `""` to use the system default time zone, but beware that this #' will not be reproducible across systems. #' #' For a complete list of possible time zones, see [OlsonNames()]. #' Americans, note that "EST" is a Canadian time zone that does not have #' DST. It is *not* Eastern Standard Time. It's better to use #' "US/Eastern", "US/Central" etc. #' @param encoding Default encoding. #' @export #' @examples #' locale() #' locale("fr") #' #' # South American locale #' locale("es", decimal_mark = ",") locale <- function(date_names = "en", date_format = "%AD", time_format = "%AT", decimal_mark = ".", grouping_mark = ",", tz = "UTC", encoding = "UTF-8") { if (is.character(date_names)) { date_names <- date_names_lang(date_names) } stopifnot(is.date_names(date_names)) if (missing(grouping_mark) && !missing(decimal_mark)) { grouping_mark <- if (decimal_mark == ".") "," else "." } else if (missing(decimal_mark) && !missing(grouping_mark)) { decimal_mark <- if (grouping_mark == ".") "," else "." } stopifnot(is.character(decimal_mark), length(decimal_mark) == 1) stopifnot(is.character(grouping_mark), length(grouping_mark) == 1) if (decimal_mark == grouping_mark) { stop("`decimal_mark` and `grouping_mark` must be different", call. = FALSE) } tz <- check_tz(tz) check_encoding(encoding) structure( list( date_names = date_names, date_format = date_format, time_format = time_format, decimal_mark = decimal_mark, grouping_mark = grouping_mark, tz = tz, encoding = encoding ), class = "locale" ) } is.locale <- function(x) inherits(x, "locale") # Conditionally exported in zzz.R # @export print.locale <- function(x, ...) { cat("\n") cat("Numbers: ", prettyNum(123456.78, big.mark = x$grouping_mark, decimal.mark = x$decimal_mark, digits = 8), "\n", sep = "") cat("Formats: ", x$date_format, " / ", x$time_format, "\n", sep = "") cat("Timezone: ", x$tz, "\n", sep = "") cat("Encoding: ", x$encoding, "\n", sep = "") print(x$date_names) } #' @export #' @rdname locale default_locale <- function() { loc <- getOption("vroom.default_locale") if (is.null(loc)) { loc <- locale() options("vroom.default_locale" = loc) } loc } check_tz <- function(x) { stopifnot(is.character(x), length(x) == 1) if (identical(x, "")) { x <- Sys.timezone() if (identical(x, "") || identical(x, NA_character_)) { x <- "UTC" } } if (x %in% tzdb::tzdb_names()) { x } else { stop("Unknown TZ ", x, call. = FALSE) } } check_encoding <- function(x) { stopifnot(is.character(x), length(x) == 1) if (tolower(x) %in% tolower(iconvlist())) return(TRUE) stop("Unknown encoding ", x, call. = FALSE) } vroom/R/vroom_write.R0000644000176200001440000001410014142515724014315 0ustar liggesusers#' Write a data frame to a delimited file #' #' @inheritParams readr::write_tsv #' @inheritParams vroom #' @param escape The type of escape to use when quotes are in the data. #' - `double` - quotes are escaped by doubling them. #' - `backslash` - quotes are escaped by a preceding backslash. #' - `none` - quotes are not escaped. #' @param quote How to handle fields which contain characters that need to be quoted. #' - `needed` - Only quote fields which need them. #' - `all` - Quote all fields. #' - `none` - Never quote fields. #' @param bom If `TRUE` add a UTF-8 BOM at the beginning of the file. This is #' recommended when saving data for consumption by excel, as it will force #' excel to read the data with the correct encoding (UTF-8) #' @param delim Delimiter used to separate values. Defaults to `\t` to write #' tab separated value (TSV) files. #' @param na String used for missing values. Defaults to 'NA'. #' @param path `r lifecycle::badge("deprecated")` is no longer supported, use #' `file` instead. #' @export #' @examples #' # If you only specify a file name, vroom_write() will write #' # the file to your current working directory. #' out_file <- tempfile(fileext = "csv") #' vroom_write(mtcars, out_file, ",") #' #' # You can also use a literal filename #' # vroom_write(mtcars, "mtcars.tsv") #' #' # If you add an extension to the file name, write_()* will #' # automatically compress the output. #' # vroom_write(mtcars, "mtcars.tsv.gz") #' # vroom_write(mtcars, "mtcars.tsv.bz2") #' # vroom_write(mtcars, "mtcars.tsv.xz") vroom_write <- function(x, file, delim = '\t', eol = "\n", na = "NA", col_names = !append, append = FALSE, quote = c("needed", "all", "none"), escape = c("double", "backslash", "none"), bom = FALSE, num_threads = vroom_threads(), progress = vroom_progress(), path = deprecated()) { if (lifecycle::is_present(path)) { file <- path lifecycle::deprecate_soft( when = "1.5.0", what = "vroom_write(file)" ) } quote <- match.arg(quote) escape <- match.arg(escape) opts <- get_vroom_write_opts(quote, escape, bom) # Standardise path returns a list, but we will only ever have 1 output file. file <- standardise_one_path(file, write = TRUE) # If there are no columns in the data frame, just create an empty file and return if (NCOL(x) == 0) { if (!inherits(file, "connection")) { file.create(file) } return(invisible(x)) } # We need to convert any altrep vectors to normal vectors otherwise we can't fill the # write buffers from other threads. xx <- vroom_convert(x) xx[] <- lapply(xx, output_column) # This seems to work ok in practice buf_lines <- max(as.integer(Sys.getenv("VROOM_WRITE_BUFFER_LINES", nrow(x) / 100 / num_threads)), 1) if (inherits(file, "connection")) { vroom_write_connection_(xx, file, delim, eol, na_str = na, col_names = col_names, options = opts, num_threads = num_threads, progress = progress, buf_lines = buf_lines, is_stdout = file == stdout(), append = append) } else { vroom_write_(xx, file, delim, eol, na_str = na, col_names = col_names, append = append, options = opts, num_threads = num_threads, progress = progress, buf_lines = buf_lines) } invisible(x) } get_vroom_write_opts <- function(quote, escape, bom) { v_opts <- vroom_write_opts() bitwOr( v_opts[paste0("quote_", quote)], bitwOr( v_opts[paste0("escape_", escape)], if (bom) v_opts["bom"] else 0) ) } vroom_write_opts <- function() c( "quote_none" = 0L, "escape_none" = 0L, "quote_needed" = 1L, "quote_all" = 2L, "escape_double" = 4L, "escape_backslash" = 8L, "bom" = 16L ) #' Convert a data frame to a delimited string #' #' This is equivalent to [vroom_write()], but instead of writing to #' disk, it returns a string. It is primarily useful for examples and for #' testing. #' #' @inheritParams vroom_write #' @export vroom_format <- function(x, delim = "\t", eol = "\n", na = "NA", col_names = TRUE, escape = c("double", "backslash", "none"), quote = c("needed", "all", "none"), bom = FALSE, num_threads = vroom_threads()) { quote <- match.arg(quote) escape <- match.arg(escape) opts <- get_vroom_write_opts(quote, escape, bom) # This seems to work ok in practice buf_lines <- max(as.integer(Sys.getenv("VROOM_WRITE_BUFFER_LINES", nrow(x) / 100 / num_threads)), 1) x[] <- lapply(x, output_column) vroom_format_(x, delim = delim, eol = eol, na_str = na, col_names = col_names, append = FALSE, options = opts, num_threads = vroom_threads(), progress = vroom_progress(), buf_lines = buf_lines) } #' Write lines to a file #' #' @inheritParams vroom_write #' @export vroom_write_lines <- function(x, file, eol = "\n", na = "NA", append = FALSE, num_threads = vroom_threads()) { stopifnot(is.character(x)) x <- list(X1 = x) class(x) <- "data.frame" attr(x, "row.names") <- c(NA_integer_, -length(x[[1]])) vroom_write(x, file = file, delim = "", col_names = FALSE, eol = eol, na = na, append = append, quote = "none", escape = "none", num_threads = num_threads ) } #' Preprocess column for output #' #' This is a generic function that applied to each column before it is saved #' to disk. It provides a hook for S3 classes that need special handling. #' #' @keywords internal #' @param x A vector #' @examples #' # Most types are returned unchanged #' output_column(1) #' output_column("x") #' #' # datetimes are formatted in ISO 8601 #' output_column(Sys.Date()) #' output_column(Sys.time()) #' @export output_column <- function(x) { UseMethod("output_column") } #' @export output_column.default <- function(x) { if (!is.object(x)) return(x) as.character(x) } #' @export output_column.double <- function(x) { x } #' @export output_column.POSIXt <- function(x) { format(x, "%Y-%m-%dT%H:%M:%OSZ", tz = "UTC", justify = "none") } #' @export output_column.character <- function(x) { x } #' @export output_column.factor <- function(x) { # TODO: look into doing writing directly in C++ as.character(x) } vroom/R/generator.R0000644000176200001440000001330414142515724013734 0ustar liggesusers#' Generate individual vectors of the types supported by vroom #' #' @param n The size of the vector to generate #' @param min The minimum range for the vector #' @param max The maximum range for the vector #' @param values The explicit values to use. #' @param f The random function to use. #' @inheritParams base::sample.int #' @param ... Additional arguments passed to internal generation functions #' @name generators #' @examples #' # characters #' gen_character(4) #' #' # factors #' gen_factor(4) #' #' # logical #' gen_logical(4) #' #' # numbers #' gen_double(4) #' gen_integer(4) #' #' # temporal data #' gen_time(4) #' gen_date(4) #' gen_datetime(4) #' @export gen_character <- function(n, min = 5, max = 25, values = c(letters, LETTERS, 0:9), ...) { if (min > max) { max <- min } # The seed for the C++ RNG used is an unsigned 32 bit integer, which is why I # multiply int max by 2. Possibly an off by one error here though... seeds <- sample.int(2 * .Machine$integer.max, 2) gen_character_(n, min, max, paste(values, collapse = ""), seeds[[1]], seeds[[2]]) } #' @rdname generators #' @export gen_double <- function(n, f = stats::rnorm, ...) { f(n, ...) } #' @rdname generators #' @export gen_number <- gen_double #' @rdname generators #' @export gen_integer <- function(n, min = 1L, max = .Machine$integer.max, prob = NULL, ...) { max <- max - min + 1L sample.int(max, size = n, replace = TRUE, prob = prob) + min - 1L } #' @rdname generators #' @param num_levels The number of factor levels to generate #' @param ordered Should the factors be ordered factors? #' @param levels The explicit levels to use, if `NULL` random levels are generated using [gen_name()]. #' @export gen_factor <- function(n, levels = NULL, ordered = FALSE, num_levels = gen_integer(1L, 1L, 25L), ...) { if (is.null(levels)) { levels <- gen_name(num_levels) } res <- gen_integer(n, max = length(levels), ...) attr(res, "levels") <- levels if (ordered) { class(res) <- c("ordered", "factor") } else { class(res) <- "factor" } res } #' @rdname generators #' @param fractional Whether to generate times with fractional seconds #' @export gen_time <- function(n, min = 0, max = hms::hms(days = 1), fractional = FALSE, ...) { res <- hms::hms(seconds = stats::runif(n, min = min, max = max)) if (!fractional) { res <- hms::as_hms(floor(res)) } res } #' @rdname generators #' @export gen_date <- function(n, min = as.Date("2001-01-01"), max = as.Date("2021-01-01"), ...) { structure(as.numeric(gen_integer(n, min = min, max = max)), class = "Date") } #' @rdname generators #' @param tz The timezone to use for dates #' @export gen_datetime <- function(n, min = as.POSIXct("2001-01-01"), max = as.POSIXct("2021-01-01"), tz = "UTC", ...) { structure(stats::runif(n, min = min, max = max), class = c("POSIXct", "POSIXt"), tzone = tz) } #' @rdname generators #' @export gen_logical <- function(n, ...) { c(TRUE, FALSE)[sample.int(n = 2, n, replace = TRUE)] } all_col_types <- tibble::tribble( ~ type, ~ class, "character", "character", "factor", "character", "double", "numeric", "integer", "numeric", "number", "numeric", "date", "temporal", "datetime", "temporal", "time", "temporal", ) #' Generate a random tibble #' #' This is useful for benchmarking, but also for bug reports when you cannot #' share the real dataset. #' #' There is also a family of functions to generate individual vectors of each #' type. #' #' @param rows Number of rows to generate #' @param cols Number of columns to generate, if `NULL` this is derived from `col_types`. #' @param missing The percentage (from 0 to 1) of missing data to use #' @seealso [generators] to generate individual vectors. #' @inheritParams vroom #' @examples #' # random 10 x 5 table with random column types #' rand_tbl <- gen_tbl(10, 5) #' rand_tbl #' #' # all double 25 x 4 table #' dbl_tbl <- gen_tbl(25, 4, col_types = "dddd") #' dbl_tbl #' #' # Use the dots in long form column types to change the random function and options #' types <- rep(times = 4, list(col_double(f = stats::runif, min = -10, max = 25))) #' types #' dbl_tbl2 <- gen_tbl(25, 4, col_types = types) #' dbl_tbl2 #' @export gen_tbl <- function(rows, cols = NULL, col_types = NULL, locale = default_locale(), missing = 0) { if (is.null(cols) && is.null(col_types)) { stop("One of `cols` or `col_types` must be set", call. = FALSE) } spec <- as.col_spec(col_types) if (is.null(cols)) { cols <- length(spec$cols) } nms <- make_names(names(spec$cols), cols) specs <- col_types_standardise(spec, length(nms), nms, vroom_enquo(rlang::quo(NULL)), "unique") res <- vector("list", cols) for (i in seq_len(cols)) { type <- sub("collector_", "", class(specs$cols[[i]])[[1]]) if (type == "guess") { type <- sample(all_col_types[["type"]], 1) specs$cols[[i]] <- do.call(paste0("col_", type), list()) } fun_nme <- paste0("gen_", type) res[[i]] <- do.call(fun_nme, c(rows, specs$cols[[i]])) } if (missing > 0) { res[] <- lapply(res, function(x) { x[sample(c(TRUE, FALSE), size = rows, prob = c(missing, 1 - missing), replace = TRUE)] <- NA x }) } names(res) <- nms attr(res, "spec") <- specs tibble::as_tibble(res) } # Name and adjective list from https://github.com/rstudio/cranwhales/blob/93349fe1bc790f115a3d56660b6b99ffe258d9a2/random-names.R #' @rdname generators #' @export gen_name <- local({ # This will run during build / installation, but that is OK adjectives <- readLines(system.file("words", "adjectives.txt", package = "vroom")) animals <- readLines(system.file("words", "animals.txt", package = "vroom")) function(n) { paste0(sample(adjectives, n, replace = TRUE), "_", sample(animals, n, replace = TRUE)) } }) vroom/R/vroom.R0000644000176200001440000003645614142515724013125 0ustar liggesusers#' @useDynLib vroom, .registration = TRUE #' @importFrom bit64 integer64 NULL #' Read a delimited file into a tibble #' #' @inheritParams readr::read_delim #' @param file path to a local file. #' @param delim One or more characters used to delimit fields within a #' file. If `NULL` the delimiter is guessed from the set of `c(",", "\t", " ", #' "|", ":", ";")`. #' @param num_threads Number of threads to use when reading and materializing #' vectors. If your data contains newlines within fields the parser will #' automatically be forced to use a single thread only. #' @param escape_double Does the file escape quotes by doubling them? #' i.e. If this option is `TRUE`, the value '""' represents #' a single quote, '"'. #' @param id Either a string or 'NULL'. If a string, the output will contain a #' variable with that name with the filename(s) as the value. If 'NULL', the #' default, no variable will be created. #' @param col_select One or more selection expressions, like in #' `dplyr::select()`. Use `c()` or `list()` to use more than one expression. #' See `?dplyr::select` for details on available selection options. #' @param .name_repair Handling of column names. By default, vroom ensures #' column names are not empty and unique. See `.name_repair` as documented in #' [tibble::tibble()] for additional options including supplying user defined #' name repair functions. #' @param altrep Control which column types use Altrep representations, #' either a character vector of types, `TRUE` or `FALSE`. See #' [vroom_altrep()] for for full details. #' @param altrep_opts \Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")} #' @param show_col_types Control showing the column specifications. If `TRUE` #' column specifications are always show, if `FALSE` they are never shown. If #' `NULL` (the default) they are shown only if an explicit specification is not #' given to `col_types`. #' @export #' @examples #' # get path to example file #' input_file <- vroom_example("mtcars.csv") #' input_file #' #' # Read from a path #' #' # Input sources ------------------------------------------------------------- #' # Read from a path #' vroom(input_file) #' # You can also use paths directly #' # vroom("mtcars.csv") #' #' \dontrun{ #' # Including remote paths #' vroom("https://github.com/r-lib/vroom/raw/main/inst/extdata/mtcars.csv") #' } #' #' # Or directly from a string with `I()` #' vroom(I("x,y\n1,2\n3,4\n")) #' #' # Column selection ---------------------------------------------------------- #' # Pass column names or indexes directly to select them #' vroom(input_file, col_select = c(model, cyl, gear)) #' vroom(input_file, col_select = c(1, 3, 11)) #' #' # Or use the selection helpers #' vroom(input_file, col_select = starts_with("d")) #' #' # You can also rename specific columns #' vroom(input_file, col_select = list(car = model, everything())) #' #' # Column types -------------------------------------------------------------- #' # By default, vroom guesses the columns types, looking at 1000 rows #' # throughout the dataset. #' # You can specify them explicitly with a compact specification: #' vroom(I("x,y\n1,2\n3,4\n"), col_types = "dc") #' #' # Or with a list of column types: #' vroom(I("x,y\n1,2\n3,4\n"), col_types = list(col_double(), col_character())) #' #' # File types ---------------------------------------------------------------- #' # csv #' vroom(I("a,b\n1.0,2.0\n"), delim = ",") #' # tsv #' vroom(I("a\tb\n1.0\t2.0\n")) #' # Other delimiters #' vroom(I("a|b\n1.0|2.0\n"), delim = "|") #' #' # Read datasets across multiple files --------------------------------------- #' mtcars_by_cyl <- vroom_example(vroom_examples("mtcars-")) #' mtcars_by_cyl #' #' # Pass the filenames directly to vroom, they are efficiently combined #' vroom(mtcars_by_cyl) vroom <- function( file, delim = NULL, col_names = TRUE, col_types = NULL, col_select = NULL, id = NULL, skip = 0, n_max = Inf, na = c("", "NA"), quote = '"', comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, escape_double = TRUE, escape_backslash = FALSE, locale = default_locale(), guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique" ) { # vroom does not support newlines as the delimiter, just as the EOL, so just # assign a value that should never appear in CSV text as the delimiter, # 001, start of heading. if (identical(delim, "\n")) { delim <- "\x01" } if (!rlang::is_missing(altrep_opts)) { deprecate_warn("1.1.0", "vroom(altrep_opts = )", "vroom(altrep = )") altrep <- altrep_opts } file <- standardise_path(file) if (!is_ascii_compatible(locale$encoding)) { file <- reencode_path(file, locale$encoding) locale$encoding <- "UTF-8" } if (length(file) == 0 || (n_max == 0 & identical(col_names, FALSE))) { return(tibble::tibble()) } if (n_max < 0 || is.infinite(n_max)) { n_max <- -1 } if (guess_max < 0 || is.infinite(guess_max)) { guess_max <- -1 } # Workaround weird RStudio / Progress bug: https://github.com/r-lib/progress/issues/56#issuecomment-384232184 if ( isTRUE(progress) && is_windows() && identical(Sys.getenv("RSTUDIO"), "1")) { Sys.setenv("RSTUDIO" = "1") } col_select <- vroom_enquo(rlang::enquo(col_select)) has_col_types <- !is.null(col_types) col_types <- as.col_spec(col_types) na <- enc2utf8(na) out <- vroom_(file, delim = delim %||% col_types$delim, col_names = col_names, col_types = col_types, id = id, skip = skip, col_select = col_select, name_repair = .name_repair, na = na, quote = quote, trim_ws = trim_ws, escape_double = escape_double, escape_backslash = escape_backslash, comment = comment, skip_empty_rows = skip_empty_rows, locale = locale, guess_max = guess_max, n_max = n_max, altrep = vroom_altrep(altrep), num_threads = num_threads, progress = progress) # Drop any NULL columns is_null <- vapply(out, is.null, logical(1)) out[is_null] <- NULL # If no rows expand columns to be the same length and names as the spec if (NROW(out) == 0) { cols <- attr(out, "spec")[["cols"]] for (i in seq_along(cols)) { out[[i]] <- collector_value(cols[[i]]) } names(out) <- names(cols) } out <- tibble::as_tibble(out, .name_repair = identity) class(out) <- c("spec_tbl_df", class(out)) out <- vroom_select(out, col_select, id) if (should_show_col_types(has_col_types, show_col_types)) { show_col_types(out, locale) } out } should_show_col_types <- function(has_col_types, show_col_types) { if (is.null(show_col_types)) { return(isTRUE(!has_col_types)) } isTRUE(show_col_types) } make_names <- function(x, len) { if (len == 0) { return(character()) } if (length(x) == len) { return(x) } if (length(x) > len) { return(x[seq_len(len)]) } nms <- make.names(seq_len(len)) nms[seq_along(x)] <- x nms } #' Determine if progress bars should be shown #' #' Progress bars are shown _unless_ one of the following is `TRUE` #' - The bar is explicitly disabled by setting `Sys.getenv("VROOM_SHOW_PROGRESS"="false")` #' - The code is run in a non-interactive session (`interactive()` is `FALSE`). #' - The code is run in an RStudio notebook chunk. #' - The code is run by knitr / rmarkdown. #' - The code is run by testthat (the `TESTTHAT` envvar is `true`). #' @export #' @examples #' vroom_progress() vroom_progress <- function() { env_to_logical("VROOM_SHOW_PROGRESS", TRUE) && interactive() && !isTRUE(getOption("knitr.in.progress")) && !isTRUE(getOption("rstudio.notebook.executing")) && !(is_loaded("testthat") && testthat::is_testing()) } #' @importFrom crayon blue cyan green bold reset col_nchar pb_file_format <- function(filename) { # Workaround RStudio bug https://github.com/rstudio/rstudio/issues/4777 withr::with_options(list(crayon.enabled = (!is_rstudio_console() || is_rstudio_version("1.2.1578")) && getOption("crayon.enabled", TRUE)), glue::glue_col("{bold}indexing{reset} {blue}{basename(filename)}{reset} [:bar] {green}:rate{reset}, eta: {cyan}:eta{reset}") ) } pb_width <- function(format) { ansii_chars <- nchar(format) - col_nchar(format) getOption("width", 80L) + ansii_chars } pb_connection_format <- function(unused) { withr::with_options(list(crayon.enabled = (!is_rstudio_console() || is_rstudio_version("1.2.1578")) && getOption("crayon.enabled", TRUE)), glue::glue_col("{bold}indexed{reset} {green}:bytes{reset} in {cyan}:elapsed{reset}, {green}:rate{reset}") ) } pb_write_format <- function(unused) { withr::with_options(list(crayon.enabled = (!is_rstudio_console() || is_rstudio_version("1.2.1578")) && getOption("crayon.enabled", TRUE)), glue::glue_col("{bold}wrote{reset} {green}:bytes{reset} in {cyan}:elapsed{reset}, {green}:rate{reset}") ) } # Guess delimiter by splitting every line by each delimiter and choosing the # delimiter which splits the lines into the highest number of consistent fields guess_delim <- function(lines, delims = c(",", "\t", " ", "|", ":", ";")) { if (length(lines) == 0) { return("") } # blank text within quotes lines <- gsub('"[^"]*"', "", lines) splits <- lapply(delims, strsplit, x = lines, useBytes = TRUE, fixed = TRUE) counts <- lapply(splits, function(x) table(lengths(x))) num_fields <- vapply(counts, function(x) as.integer(names(x)[[1]]), integer(1)) num_lines <- vapply(counts, function(x) (x)[[1]], integer(1)) top_lines <- 0 top_idx <- 0 for (i in seq_along(delims)) { if (num_fields[[i]] >= 2 && num_lines[[i]] > top_lines || (top_lines == num_lines[[i]] && (top_idx <= 0 || num_fields[[top_idx]] < num_fields[[i]]))) { top_lines <- num_lines[[i]] top_idx <- i } } if (top_idx == 0) { stop(glue::glue(' Could not guess the delimiter.\n {silver("Use `vroom(delim =)` to specify one explicitly.")} '), call. = FALSE) } delims[[top_idx]] } cached <- new.env(parent = emptyenv()) vroom_threads <- function() { res <- as.integer( Sys.getenv("VROOM_THREADS", cached$num_threads <- cached$num_threads %||% parallel::detectCores() ) ) if (is.na(res) || res <= 0) { res <- 1 } res } vroom_tempfile <- function() { dir <- Sys.getenv("VROOM_TEMP_PATH") if (!nzchar(dir)) { dir <- tempdir() } tempfile(pattern = "vroom-", tmpdir = dir) } #' Show which column types are using Altrep #' #' `vroom_altrep()` can be used directly as input to the `altrep` #' argument of [vroom()]. #' #' Alternatively there is also a family of environment variables to control use of #' the Altrep framework. These can then be set in your `.Renviron` file, e.g. #' with `usethis::edit_r_environ()`. For versions of R where the Altrep #' framework is unavailable (R < 3.5.0) they are automatically turned off and #' the variables have no effect. The variables can take one of `true`, `false`, #' `TRUE`, `FALSE`, `1`, or `0`. #' #' - `VROOM_USE_ALTREP_NUMERICS` - If set use Altrep for _all_ numeric types #' (default `false`). #' #' There are also individual variables for each type. Currently only #' `VROOM_USE_ALTREP_CHR` defaults to `true`. #' #' - `VROOM_USE_ALTREP_CHR` #' - `VROOM_USE_ALTREP_FCT` #' - `VROOM_USE_ALTREP_INT` #' - `VROOM_USE_ALTREP_BIG_INT` #' - `VROOM_USE_ALTREP_DBL` #' - `VROOM_USE_ALTREP_NUM` #' - `VROOM_USE_ALTREP_LGL` #' - `VROOM_USE_ALTREP_DTTM` #' - `VROOM_USE_ALTREP_DATE` #' - `VROOM_USE_ALTREP_TIME` #' #' @param which A character vector of column types to use Altrep for. Can also #' take `TRUE` or `FALSE` to use Altrep for all possible or none of the #' types #' @examples #' vroom_altrep() #' vroom_altrep(c("chr", "fct", "int")) #' vroom_altrep(TRUE) #' vroom_altrep(FALSE) #' @export vroom_altrep <- function(which = NULL) { if (!is.null(which)) { if (is.logical(which)) { types <- names(altrep_vals()) if (isTRUE(which)) { which <- as.list(stats::setNames(rep(TRUE, length(types)), types)) } else { which <- as.list(stats::setNames(rep(FALSE, length(types)), types)) } } else { which <- match.arg(which, names(altrep_vals()), several.ok = TRUE) which <- as.list(stats::setNames(rep(TRUE, length(which)), which)) } } args <- list( getRversion() >= "3.5.0" && which$chr %||% vroom_use_altrep_chr(), getRversion() >= "3.5.0" && which$fct %||% vroom_use_altrep_fct(), getRversion() >= "3.5.0" && which$int %||% vroom_use_altrep_int(), getRversion() >= "3.5.0" && which$dbl %||% vroom_use_altrep_dbl(), getRversion() >= "3.5.0" && which$num %||% vroom_use_altrep_num(), getRversion() >= "3.6.0" && which$lgl %||% vroom_use_altrep_lgl(), # logicals only supported in R 3.6.0+ getRversion() >= "3.5.0" && which$dttm %||% vroom_use_altrep_dttm(), getRversion() >= "3.5.0" && which$date %||% vroom_use_altrep_date(), getRversion() >= "3.5.0" && which$time %||% vroom_use_altrep_time(), getRversion() >= "3.5.0" && which$big_int %||% vroom_use_altrep_big_int() ) out <- 0L for (i in seq_along(args)) { out <- bitwOr(out, bitwShiftL(as.integer(args[[i]]), i - 1L)) } structure(out, class = "vroom_altrep") } #' Show which column types are using Altrep #' #' @description #' \Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")} #' This function is deprecated in favor of `vroom_altrep()`. #' #' @inheritParams vroom_altrep #' @export vroom_altrep_opts <- function(which = NULL) { deprecate_warn("1.1.0", "vroom_altrep_opts()", "vroom_altrep()") vroom_altrep(which) } altrep_vals <- function() c( "none" = 0L, "chr" = 1L, "fct" = 2L, "int" = 4L, "dbl" = 8L, "num" = 16L, "lgl" = 32L, "dttm" = 64L, "date" = 128L, "time" = 256L, "big_int" = 512L, "skip" = 1024L ) #' @export print.vroom_altrep <- function(x, ...) { vals <- altrep_vals() reps <- names(vals)[bitwAnd(vals, x) > 0] cat("Using Altrep representations for:\n", glue::glue(" * {reps} ", reps = glue::glue_collapse(reps, "\n * ")), "\n", sep = "") } vroom_use_altrep_chr <- function() { env_to_logical("VROOM_USE_ALTREP_CHR", TRUE) } vroom_use_altrep_fct <- function() { # fct is a numeric internally env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_FCT", FALSE) } vroom_use_altrep_int <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_INT", FALSE) } vroom_use_altrep_big_int <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_BIG_INT", FALSE) } vroom_use_altrep_dbl <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_DBL", FALSE) } vroom_use_altrep_num <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_NUM", FALSE) } vroom_use_altrep_lgl <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_LGL", FALSE) } vroom_use_altrep_dttm <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_DTTM", FALSE) } vroom_use_altrep_date <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_DATE", FALSE) } vroom_use_altrep_time <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_TIME", FALSE) } vroom/R/problems.R0000644000176200001440000000233014142515724013566 0ustar liggesusers#' Retrieve parsing problems #' #' vroom will only fail to parse a file if the file is invalid in a way that is #' unrecoverable. However there are a number of non-fatal problems that you #' might want to know about. You can retrieve a data frame of these problems #' with this function. #' #' @param x A data frame from `vroom::vroom()`. #' @param lazy If `TRUE`, just the problems found so far are returned. If #' `FALSE` (the default) the lazy data is first read completely and all #' problems are returned. #' @return A data frame with one row for each problem and four columns: #' - row,col - Row and column of problem #' - expected - What vroom expected to find #' - actual - What it actually found #' - file - The file with the problem #' @export problems <- function(x, lazy = FALSE) { if (!isTRUE(lazy)) { vroom_materialize(x, replace = FALSE) } probs <- attr(x, "problems") if (typeof(probs) != "externalptr") { rlang::abort("`x` must have a problems attribute that is an external pointer.\n Is this object from readr and not vroom?") } probs <- vroom_errors_(probs) probs <- probs[!duplicated(probs), ] probs <- probs[order(probs$file, probs$row, probs$col), ] tibble::as_tibble(probs) } vroom/R/tidyselect.R0000644000176200001440000000107314142512075014113 0ustar liggesusers#' @aliases select_helpers #' @importFrom tidyselect contains #' @export tidyselect::contains #' @importFrom tidyselect ends_with #' @export tidyselect::ends_with #' @importFrom tidyselect everything #' @export tidyselect::everything #' @importFrom tidyselect matches #' @export tidyselect::matches #' @importFrom tidyselect num_range #' @export tidyselect::num_range #' @importFrom tidyselect one_of #' @export tidyselect::one_of #' @importFrom tidyselect starts_with #' @export tidyselect::starts_with #' @importFrom tidyselect last_col #' @export tidyselect::last_col vroom/R/altrep.R0000644000176200001440000000145014142512075013230 0ustar liggesusers#' Structure of objects #' #' Similar to `str()` but with more information for Altrep objects. #' #' @param x a vector #' @examples #' # when used on non-altrep objects altrep will always be false #' vroom_str(mtcars) #' #' mt <- vroom(vroom_example("mtcars.csv"), ",", altrep = c("chr", "dbl")) #' vroom_str(mt) #' @export vroom_str <- function(x) { UseMethod("vroom_str") } #' @export vroom_str.data.frame <- function(x) { classes <- glue::glue_collapse(glue::single_quote(class(x)), ", ", last = ", and ") rows <- nrow(x) cols <- ncol(x) cat(glue::glue("{classes}: {rows} obs., {cols} vars.:\n\n"), sep = "") nms <- names(x) for (i in seq_along(x)) { cat("$", nms[[i]], ":\t", sep = "") vroom_str(x[[i]]) } } #' @export vroom_str.default <- function(x) { cat(vroom_str_(x)) } vroom/NEWS.md0000644000176200001440000004036014151250600012507 0ustar liggesusers# vroom 1.5.7 * Jenny Bryan is now the official maintainer. * Fix uninitialized bool detected by CRAN's UBSAN check (https://github.com/r-lib/vroom/pull/386) * Fix buffer overflow when trying to parse an integer field that is over 64 characters long (https://github.com/tidyverse/readr/issues/1326) * Fix subset indexing when indexes span a file boundary multiple times (#383) # vroom 1.5.6 * `vroom(col_select=)` now works if `col_names = FALSE` as intended (#381) * `vroom(n_max=)` now correctly handles cases when reading from a connection and the file does _not_ end with a newline (https://github.com/tidyverse/readr/issues/1321) * `vroom()` no longer issues a spurious warning when the parsing needs to be restarted due to the presence of embedded newlines (https://github.com/tidyverse/readr/issues/1313) * Fix performance issue when materializing subsetted vectors (#378) * `vroom_format()` now uses the same internal multi-threaded code as `vroom_write()`, improving its performance in most cases (#377) * `vroom_fwf()` no longer omits the last line if it does _not_ end with a newline (https://github.com/tidyverse/readr/issues/1293) * Empty files or files with only a header line and no data no longer cause a crash if read with multiple files (https://github.com/tidyverse/readr/issues/1297) * Files with a header but no contents, or a empty file if `col_names = FALSE` no longer cause a hang when `progress = TRUE` (https://github.com/tidyverse/readr/issues/1297) * Commented lines with comments at the end of lines no longer hang R (https://github.com/tidyverse/readr/issues/1309) * Comment lines containing unpaired quotes are no longer treated as unterminated quotations (https://github.com/tidyverse/readr/issues/1307) * Values with only a `Inf` or `NaN` prefix but additional data afterwards, like `Inform` or no longer inappropriately guessed as doubles (https://github.com/tidyverse/readr/issues/1319) * Time types now support `%h` format to denote hour durations greater than 24, like readr (https://github.com/tidyverse/readr/issues/1312) * Fix performance issue when materializing subsetted vectors (#378) # vroom 1.5.5 * `vroom()` now supports files with only carriage return newlines (`\r`). (#360, https://github.com/tidyverse/readr/issues/1236) * `vroom()` now parses single digit datetimes more consistently as readr has done (https://github.com/tidyverse/readr/issues/1276) * `vroom()` now parses `Inf` values as doubles (https://github.com/tidyverse/readr/issues/1283) * `vroom()` now parses `NaN` values as doubles (https://github.com/tidyverse/readr/issues/1277) * `VROOM_CONNECTION_SIZE` is now parsed as a double, which supports scientific notation (#364) * `vroom()` now works around specifying a `\n` as the delimiter (#365, https://github.com/tidyverse/dplyr/issues/5977) * `vroom()` no longer crashes if given a `col_name` and `col_type` both less than the number of columns (https://github.com/tidyverse/readr/issues/1271) * `vroom()` no longer hangs if given an empty value for `locale(grouping_mark=)` (https://github.com/tidyverse/readr/issues/1241) * Fix performance regression when guessing with large numbers of rows (https://github.com/tidyverse/readr/issues/1267) # vroom 1.5.4 * `vroom(col_types=)` now accepts column type names like those accepted by utils::read.table. e.g. vroom::vroom(col_types = list(a = "integer", b = "double", c = "skip")) * `vroom()` now respects the `quote` parameter properly in the first two lines of the file (https://github.com/tidyverse/readr/issues/1262) * `vroom_write()` now always correctly writes its output including column names in UTF-8 (https://github.com/tidyverse/readr/issues/1242) * `vroom_write()` now creates an empty file when given a input without any columns (https://github.com/tidyverse/readr/issues/1234) # vroom 1.5.3 * `vroom(col_types=)` now truncates the column types if the user passes too many types. (#355) * `vroom()` now always includes the last row when guessing (#352) * `vroom(trim_ws = TRUE)` now trims field content within quotes as well as without (#354). Previously vroom explicitly retained field content inside quotes regardless of the value of `trim_ws`. # vroom 1.5.2 * `vroom()` now supports inputs with unnamed column types that are less than the number of columns (#296) * `vroom()` now outputs the correct column names even in the presence of skipped columns (#293, [tidyverse/readr#1215](https://github.com/tidyverse/readr/issues/1215)) * `vroom_fwf(n_max=)` now works as intended when the input is a connection. * `vroom()` and `vroom_write()` now automatically detect the compression format regardless of the file extension for bzip2, xzip, gzip and zip files (#348) * `vroom()` and `vroom_write()` now automatically support many more archive formats thanks to the archive package. These include new support for writing zip files, reading and writing 7zip, tar and ISO files. * `vroom(num_threads = 1)` will now not spawn any threads. This can be used on as a workaround on systems without full thread support. * Threads are now automatically disabled on non-macOS systems compiling against clang's libc++. Most systems non-macOS systems use the more common gcc libstdc++, so this should not effect most users. # vroom 1.5.1 * Parsers now treat NA values as NA even if they are valid values for the types (#342) * Element-wise indexing into lazy (ALTREP) vectors now has much less overhead (#344) # vroom 1.5.0 ## Major improvements * New `vroom(show_col_types=)` argument to more simply control when column types are shown. * `vroom()`, `vroom_fwf()` and `vroom_lines()` now support multi-byte encodings such as UTF-16 and UTF-32 by converting these files to UTF-8 under the hood (#138) * `vroom()` now supports skipping comments and blank lines within data, not just at the start of the file (#294, #302) * `vroom()` now uses the tzdb package when parsing date-times (@DavisVaughan, #273) * `vroom()` now emits a warning of class `vroom_parse_issue` if there are non-fatal parsing issues. * `vroom()` now emits a warning of class `vroom_mismatched_column_name` if the user supplies a column type that does not match the name of a read column (#317). * The vroom package now uses the MIT license, as part of systematic relicensing throughout the r-lib and tidyverse packages (#323) ## Minor improvements and fixes * `vroom() correctly reads double values with comma as decimal separator (@kent37 #313) * `vroom()` now correctly skips lines with only one quote if the format doesn't use quoting (https://github.com/tidyverse/readr/issues/991#issuecomment-616378446) * `vroom()` and `vroom_lines()` now handle files with mixed windows and POSIX line endings (https://github.com/tidyverse/readr/issues/1210) * `vroom()` now outputs a tibble with the expected number of columns and types based on `col_types` and `col_names` even if the file is empty (#297). * `vroom()` no longer mis-indexes files read from connections with windows line endings when the two line endings falls on separate sides of the read buffer (#331) * `vroom()` no longer crashes if `n_max = 0` and `col_names` is a character (#316) * `vroom()` now preserves the spec attribute when vroom and readr are both loaded (#303) * `vroom()` now allows specifying column names in `col_types` that have been repaired (#311) * `vroom()` no longer inadvertently calls `.name_repair` functions twice (#310). * `vroom()` is now more robust to quoting issues when tracking the CSV state (#301) * `vroom()` now registers the S3 class with `methods::setOldClass()` (r-dbi/DBI#345) * `col_datetime()` now supports '%s' format, which represents decimal seconds since the Unix epoch. * `col_numeric()` now supports `grouping_mark` and `decimal_mark` that are unicode characters, such as U+00A0 which is commonly used as the grouping mark for numbers in France (https://github.com/tidyverse/readr/issues/796). * `vroom_fwf()` gains a `skip_empty_rows` argument to skip empty lines (https://github.com/tidyverse/readr/issues/1211) * `vroom_fwf()` now respects `n_max`, as intended (#334) * `vroom_lines()` gains a `na` argument. * `vroom_write_lines()` no longer escapes or quotes lines. * `vroom_write_lines()` now works as intended (#291). * `vroom_write(path=)` has been deprecated, in favor of `file`, to match readr. * `vroom_write_lines()` now exposes the `num_threads` argument. * `problems()` now prints the correct row number of parse errors (#326) * `problems()` now throws a more informative error if called on a readr object (#308). * `problems()` now de-duplicates identical problems (#318) * Fix an inadvertent performance regression when reading values (#309) * `n_max` argument is correctly respected in edge cases (#306) * factors with implicit levels now work when fields are quoted, as intended (#330) * Guessing double types no longer unconditionally ignores leading whitespace. Now whitespace is only ignored when `trim_ws` is set. # vroom 1.4.0 ## Major changes and new functions * vroom now tracks indexing and parsing errors like readr. The first time an issue is encountered a warning will be signaled. A tibble of all found problems can be retrieved with `vroom::problems()`. (#247) * Data with newlines within quoted fields will now automatically revert to using a single thread and be properly read (#282) * NUL values in character data are now permitted, with a warning. * New `vroom_write_lines()` function to write a character vector to a file (#291) * `vroom_write()` gains a `eol=` parameter to specify the end of line character(s) to use. Use `vroom_write(eol = "\r\n")` to write a file with Windows style newlines (#263). ## Minor improvements and fixes * Datetime formats used when guessing now match those used when parsing (#240) * Quotes are now only valid next to newlines or delimiters (#224) * `vroom()` now signals an R error for invalid date and datetime formats, instead of crashing the session (#220). * `vroom(comment = )` now accepts multi-character comments (#286) * `vroom_lines()` now works with empty files (#285) * Vectors are now subset properly when given invalid subscripts (#283) * `vroom_write()` now works when the delimiter is empty, e.g. `delim = ""` (#287). * `vroom_write()` now works with all ALTREP vectors, including string vectors (#270) * An internal call to `new.env()` now correctly uses the `parent` argument (#281) # vroom 1.3.2 * Test failures on R 4.1 related to factors with NA values fixed (#262) * `vroom()` now works without error with readr versions of col specs (#256, #264, #266) # vroom 1.3.1 * Test failures on R 4.1 related to POSIXct classes fixed (#260) * Column subsetting with double indexes now works again (#257) * `vroom(n_max=)` now only partially downloads files from connections, as intended (#259) # vroom 1.3.0 * The Rcpp dependency has been removed in favor of cpp11. * `vroom()` now handles cases when `id` is set and a column in skipped (#237) * `vroom()` now supports column selections when there are some empty column names (#238) * `vroom()` argument `n_max` now works properly for files with windows newlines and no final newline (#244) * Subsetting vectors now works with `View()` in RStudio if there are now rows to subset (#253). * Subsetting datetime columns now works with `NA` indices (#236). # vroom 1.2.1 * `vroom()` now writes the column names if given an input with no rows (#213) * `vroom()` columns now support indexing with NA values (#201) * `vroom()` no longer truncates the last value in a file if the file contains windows newlines but no final newline (#219). * `vroom()` now works when the `na` argument is encoded in non ASCII or UTF-8 locales _and_ the file encoding is not the same as the native encoding (#233). * `vroom_fwf()` now verifies that the positions are valid, namely that the begin value is always less than the previous end (#217). * `vroom_lines()` gains a `locale` argument so you can control the encoding of the file (#218) * `vroom_write()` now supports the `append` argument with R connections (#232) # vroom 1.2.0 ## Breaking changes * `vroom_altrep_opts()` and the argument `vroom(altrep_opts =)` have been renamed to `vroom_altrep()` and `altrep` respectively. The prior names have been deprecated. ## New Features * `vroom()` now supports reading Big Integer values with the `bit64` package. Use `col_big_integer()` or the "I" shortcut to read a column as big integers. (#198) * `cols()` gains a `.delim` argument and `vroom()` now uses it as the delimiter if it is provided (#192) * `vroom()` now supports reading from `stdin()` directly, interpreted as the C-level standard input (#106). ## Minor improvements and fixes * `col_date` now parses single digit month and day (@edzer, #123, #170) * `fwf_empty()` now uses the `skip` parameter, as intended. * `vroom()` can now read single line files without a terminal newline (#173). * `vroom()` can now select the id column if provided (#110). * `vroom()` now correctly copies string data for factor levels (#184) * `vroom()` no longer crashes when files have trailing fields, windows newlines and the file is not newline or null terminated. * `vroom()` now includes a spec object with the `col_types` class, as intended. * `vroom()` now better handles floating point values with very large exponents (#164). * `vroom()` now uses better heuristics to guess the delimiter and now throws an error if a delimiter cannot be guessed (#126, #141, #167). * `vroom()` now has an improved error message when a file does not exist (#169). * `vroom()` no longer leaks file handles (#177, #180) * `vroom()` now outputs its messages on `stdout()` rather than `stderr()`, which avoids the text being red in RStudio and in the Windows GUI. * `vroom()` no longer overflows when reading files with more than 2B entries (@wlattner, #183). * `vroom_fwf()` is now more robust if not all lines are the expected length (#78) * `vroom_fwf()` and `fwf_empty()` now support passing `Inf` to `guess_max()`. * `vroom_str()` now works with S4 objects. * `vroom_fwf()` now handles files with dos newlines properly. * `vroom_write()` now does not try to write anything when given empty inputs (#172). * Dates, times, and datetimes now properly consider the locale when parsing. * Added benchmarks with _wide_ data for both numeric and character data (#87, @R3myG) * The delimiter used for parsing is now shown in the message output (#95 @R3myG) # vroom 1.0.2 ## New Features * The column created by `id` is now stored as an run length encoded Altrep vector, which uses less memory and is much faster for large inputs. (#111) ## Minor improvements and fixes * `vroom_lines()` now properly respects the `n_max` parameter (#142) * `vroom()` and `vroom_lines()` now support reading files which do not end in newlines by using a file connection (#40). * `vroom_write()` now works with the standard output connection `stdout()` (#106). * `vroom_write()` no longer crashes non-deterministically when used on Altrep vectors. * The integer parser now returns NA values for invalid inputs (#135) * Fix additional UBSAN issue in the mio project reported by CRAN (#97) * Fix indexing into connections with quoted fields (#119) * Move example files for `vroom()` out of `\dontshow{}`. * Fix integer overflow with very large files (#116, #119) * Fix missing columns and windows newlines (#114) * Fix encoding of column names (#113, #115) * Throw an error message when writing a zip file, which is not supported (@metaOO, #145) * Default message output from `vroom()` now uses `Rows` and `Cols` (@meta00, #140) # vroom 1.0.1 ## New Features * `vroom_lines()` function added, to (lazily) read lines from a file into a character vector (#90). ## Minor improvements and fixes * Fix for a hang on Windows caused by a race condition in the progress bar (#98) * Remove accidental runtime dependency on testthat (#104) * Fix to actually return non-Altrep character columns on R 3.2, 3.3 and 3.4. * Disable colors in the progress bar when running in RStudio, to work around an issue where the progress bar would be garbled (https://github.com/rstudio/rstudio/issues/4777) * Fix for UBSAN issues reported by CRAN (#97) * Fix for rchk issues reported by CRAN (#94) * The progress bar now only updates every 10 milliseconds. * Getting started vignette index entry now more informative (#92) # vroom 1.0.0 * Initial release * Added a `NEWS.md` file to track changes to the package. vroom/MD50000644000176200001440000003757314151431426011744 0ustar liggesusersbfb9399b3528ad69a79b22b91fc28dd4 *DESCRIPTION f336abdde07864ff91d6e5291bcafa04 *LICENSE 9de2da89aed2f6ef2f7316961e66675c *NAMESPACE add5ae13782c7a2f37c84432f531c673 *NEWS.md d7578264258a633b154d1ea963e14f13 *R/altrep.R 2b28586e9ed777325a5d7310a780e457 *R/col_types.R 7e38e98c5c023b59dcfa340dd18b74a9 *R/collector.R 3d5e00a963393cf29b6938c71def49e8 *R/cpp11.R a7399ea4bb7e979fd965e85e69ff0ed2 *R/date.R ee39c18a50ad3b42be83083abe596cbc *R/example.R 2104ca32a255de56cf7d4f6c513704d1 *R/generator.R 5d1649cb20f8177dad6bb1916e7d281e *R/locale.R 3f84f3a8fedcd16a63efdbdef1867592 *R/path.R 5835af9c6e8d52d58a21da5b890e2149 *R/problems.R 44c86bd029ec5ce1cda465ae14421c9c *R/sysdata.rda 8d970b8724b592bddb1e4e50d16ec6d3 *R/tidyselect.R 4f60caeb4688ef2e73c93ea76fc501ac *R/utils.R 116a82275c479a49a4b2d9313b0f8e2a *R/vroom-package.R 8b92abb6497e5d1b7b93c1efaf73b3f4 *R/vroom.R a50ef1393e14f85afa7b4b3408573ac3 *R/vroom_fwf.R ad55db8281ebcc9432b9f832c543aa3b *R/vroom_lines.R a5e10b4280da96f172632cf5efe753cf *R/vroom_write.R 2b72228974b8947a01b68eada8f15ede *R/zzz.R 63cdb665374db0814f622e52038a71c2 *README.md bac92e72a36605f2f0a6205590397c1a *build/vignette.rds 72576c4d910bb3c1440e841ba1e82a4e *build/vroom.pdf f66fad5f29df5d746eba05092f5db402 *inst/COPYRIGHTS 9abda750c39e1a16375bf0faa0bd2c6c *inst/WORDLIST 079547f9bdda65602ec9546b56591cd9 *inst/bench/GNUmakefile 81b65cd5dafc0ef9280e7012a96eb28f *inst/bench/README.md 962d06b985e9e8193a542aa49664461d *inst/bench/all_character-long.tsv adb19a2ad5f8df1a425c3f141bbb1ca6 *inst/bench/all_character-long/data.table-data.table.R dc62df65db108c3edf6b5a5e01f3c0a2 *inst/bench/all_character-long/input.R b7e86e3149030918d62d9085c9dadd04 *inst/bench/all_character-long/read.delim-base.R f019cf85f14ec96e800472b1352fffde *inst/bench/all_character-long/readr-dplyr.R 6217f8f584aceb27f04ce2cab2302e98 *inst/bench/all_character-long/vroom-base.R 85de11b01605e56086180437bdb0ca45 *inst/bench/all_character-long/vroom-dplyr.R b58b6986b69cf07ac7964270c94dbb8b *inst/bench/all_character-long/vroom_no_altrep-dplyr.R d2c0a521f2f5a23609e93c8ba945c3ac *inst/bench/all_character-wide.tsv adb19a2ad5f8df1a425c3f141bbb1ca6 *inst/bench/all_character-wide/data.table-data.table.R dc62df65db108c3edf6b5a5e01f3c0a2 *inst/bench/all_character-wide/input.R b7e86e3149030918d62d9085c9dadd04 *inst/bench/all_character-wide/read.delim-base.R f019cf85f14ec96e800472b1352fffde *inst/bench/all_character-wide/readr-dplyr.R 6217f8f584aceb27f04ce2cab2302e98 *inst/bench/all_character-wide/vroom-base.R 85de11b01605e56086180437bdb0ca45 *inst/bench/all_character-wide/vroom-dplyr.R b58b6986b69cf07ac7964270c94dbb8b *inst/bench/all_character-wide/vroom_no_altrep-dplyr.R 6191cb718a65d44c0daa2a68a26678be *inst/bench/all_numeric-long.tsv 75d64c09a3c43c1217bf1c3d9c98d290 *inst/bench/all_numeric-long/data.table-data.table.R e91cda9b9eb6f49a2548549ecf9ed467 *inst/bench/all_numeric-long/input.R da8f45a2672500ed9ecb2c9b59ea2a64 *inst/bench/all_numeric-long/read.delim-base.R 9a9f4b4f35b233b38814c243c3f1815f *inst/bench/all_numeric-long/readr-dplyr.R 123685f2b589d8f51662fc63681d23d9 *inst/bench/all_numeric-long/vroom-base.R 4648ea621c2e2938d7823aa4119e16c1 *inst/bench/all_numeric-long/vroom-dplyr.R 81f9b037b915cc475618706f6b710979 *inst/bench/all_numeric-long/vroom_no_altrep-base.R 9a6290051b3b157163bd6afc61efa4ea *inst/bench/all_numeric-long/vroom_no_altrep-dplyr.R 53814663e32460a5ecc75aad6c23ad11 *inst/bench/all_numeric-wide.tsv 75d64c09a3c43c1217bf1c3d9c98d290 *inst/bench/all_numeric-wide/data.table-data.table.R e91cda9b9eb6f49a2548549ecf9ed467 *inst/bench/all_numeric-wide/input.R da8f45a2672500ed9ecb2c9b59ea2a64 *inst/bench/all_numeric-wide/read.delim-base.R 9a9f4b4f35b233b38814c243c3f1815f *inst/bench/all_numeric-wide/readr-dplyr.R 123685f2b589d8f51662fc63681d23d9 *inst/bench/all_numeric-wide/vroom-base.R 4648ea621c2e2938d7823aa4119e16c1 *inst/bench/all_numeric-wide/vroom-dplyr.R 81f9b037b915cc475618706f6b710979 *inst/bench/all_numeric-wide/vroom_no_altrep-base.R 9a6290051b3b157163bd6afc61efa4ea *inst/bench/all_numeric-wide/vroom_no_altrep-dplyr.R 6684603ba8dd737f4c3cbef4afbef69f *inst/bench/download-data.sh 272eb2473e8927f9a70985f4baae314a *inst/bench/fwf.tsv 5ff8c048c6a2857e100f759acce6f57d *inst/bench/fwf/read.delim-base.R 13fee535654a32c302044cc380400811 *inst/bench/fwf/readr-dplyr.R e4a0bc2e0e47fa3a14f02ce7fa4005dd *inst/bench/fwf/vroom-base.R 7c846584c6e0e5e4c2fb1f27af3d6d9e *inst/bench/fwf/vroom-dplyr.R 72faf515d2a3e87a140b15203a51422d *inst/bench/fwf/vroom_no_altrep-dplyr.R a9bef83722c563c8889b1ecaec91073f *inst/bench/run-bench-fwf.R 46fe1a77f4af4bb647c17b1e5b3f6d0e *inst/bench/run-bench.R cb525dd7d173f28932d6620e1e7aaf4e *inst/bench/script.sh 8aa4f38f904c3623e9b916907f77474e *inst/bench/session_info.R 739020b62d11d339fcd031adcc51fdb8 *inst/bench/session_info.tsv 4ab3d6b59a213b29ba612815a83678bf *inst/bench/summarise-benchmarks.R 26e194ad5ae7130a5ee491b7629d33a4 *inst/bench/taxi.tsv 67993afdf5abbc57c367492e103d98b2 *inst/bench/taxi/data.table-data.table.R a932ed0ffbeacde593f5a6065cac8762 *inst/bench/taxi/read.delim-base.R 2416b4a645cdc93327c0d7cc3aee1b32 *inst/bench/taxi/readr-dplyr.R 94ac2bea499d537469f3ab25af03a6bf *inst/bench/taxi/vroom-base.R ed53d4dc02d668ccfe4f826a5817131d *inst/bench/taxi/vroom-dplyr.R 2f5c39593ca034162f7a804b792ecb2d *inst/bench/taxi/vroom_no_altrep-dplyr.R 1e41ec0c73e72d2259961877f5e60030 *inst/bench/taxi_multiple.tsv aeff388f7d649d76028e1238270d6483 *inst/bench/taxi_multiple/data.table-data.table.R ac9b0e14211305511911366bc7f39b53 *inst/bench/taxi_multiple/readr-dplyr.R 5f45cc33ee2da55da015df6f4e42eaca *inst/bench/taxi_multiple/vroom-base.R df54197ddbcc59fc2bfce4fc27d9284c *inst/bench/taxi_multiple/vroom-dplyr.R 486f016a15d9ca9f859b08fa0fa1ca3f *inst/bench/taxi_multiple/vroom_no_altrep-dplyr.R 1a269cc94a87f78d77b5460ef7f0f92e *inst/bench/taxi_writing.tsv bce900b3729f5169c2a0e3ff69066e2f *inst/bench/taxi_writing/base-gzip.R ec78a353534aac0dc72f34de7817d365 *inst/bench/taxi_writing/base-multithreaded_gzip.R 59aa3d11d4e89fe086c6653160733ce0 *inst/bench/taxi_writing/base-uncompressed.R 65d54f18d2ae928dc35113b7d83c9724 *inst/bench/taxi_writing/base-zstandard.R 2452746d818a560078b619f58eef2585 *inst/bench/taxi_writing/data.table-gzip.R 08acfab7dc00869e0802968c6232e688 *inst/bench/taxi_writing/data.table-multithreaded_gzip.R 5e403204b04f1164999719aa31041ba5 *inst/bench/taxi_writing/data.table-uncompressed.R 220bb0e090cde9eebd6c36d204804d7c *inst/bench/taxi_writing/readr-gzip.R ba6b5437d3646624706c6480272e4e40 *inst/bench/taxi_writing/readr-multithreaded_gzip.R 2e67ae5451b9016d0ceb5fe54478ca71 *inst/bench/taxi_writing/readr-uncompressed.R f36cf746c35c0938ed1ce52fc682f9fc *inst/bench/taxi_writing/readr-zstandard.R f55501606ca8159731a842638a5dc782 *inst/bench/taxi_writing/vroom-gzip.R d66a90ed9df1fab170da6bbfeb3dac1b *inst/bench/taxi_writing/vroom-multithreaded_gzip.R 11eaebca3f055e57e42ee3c56916e9f2 *inst/bench/taxi_writing/vroom-uncompressed.R 50e91970e6b3ab9c44280d973cdc8a93 *inst/bench/taxi_writing/vroom-zstandard.R 8a6e7d748d21aa5048a0a7c37614ae8e *inst/doc/benchmarks.R 9790810035aefaec5b5acc86554c8ce7 *inst/doc/benchmarks.Rmd 570dd12ab8efc7d55ea3183d94532e21 *inst/doc/benchmarks.html a27e3f81e16ece992e23561318c49ece *inst/doc/vroom.R 622959f2520b142e209b7e9d4d32e85c *inst/doc/vroom.Rmd 6ef8f3e9341b403b5892b540327903b5 *inst/doc/vroom.html 891bca40aeba031848809c3e587b20d7 *inst/extdata/fwf-sample.txt c25b678e1bf7c33f1438853930348b5f *inst/extdata/mtcars-4.csv 2a4acab046ab5735b2589365a29888fa *inst/extdata/mtcars-6.csv 86fea77ddd827fa7a394d1a20736920f *inst/extdata/mtcars-8.csv c502359c26a0931eef53b2207b2344f9 *inst/extdata/mtcars.csv 397ea2199f10f07a4a0c49800f24d4a5 *inst/extdata/mtcars.csv.bz2 fe1d9b4a0c1eb859115f6b9a8ff7f5f7 *inst/extdata/mtcars.csv.gz 0dfbcad9416210addd8b23f740e3ae72 *inst/extdata/mtcars.csv.xz c2636e23318e78960fafbcab4b7b5a97 *inst/extdata/mtcars.csv.zip dacabb877353b4cc5ca46d51f3a980f8 *inst/words/adjectives.txt 031ecab52f8891f5939ba4ffdc46aa8b *inst/words/animals.txt 1545e6ae5677bec5c3fabcec2e640a9a *man/as.col_spec.Rd 4ef05fdf724d9e31719901f2f1373b00 *man/cols.Rd c327af5267f8e7ff8291681bb608ed61 *man/date_names.Rd cb1e46f469cfbbbde29c8b5113e1d789 *man/figures/lifecycle-archived.svg c0d2e5a54f1fa4ff02bf9533079dd1f7 *man/figures/lifecycle-defunct.svg a1b8c987c676c16af790f563f96cbb1f *man/figures/lifecycle-deprecated.svg c3978703d8f40f2679795335715e98f4 *man/figures/lifecycle-experimental.svg 952b59dc07b171b97d5d982924244f61 *man/figures/lifecycle-maturing.svg 27b879bf3677ea76e3991d56ab324081 *man/figures/lifecycle-questioning.svg 46de21252239c5a23d400eae83ec6b2d *man/figures/lifecycle-retired.svg 6902bbfaf963fbc4ed98b86bda80caa2 *man/figures/lifecycle-soft-deprecated.svg 53b3f893324260b737b3c46ed2a0e643 *man/figures/lifecycle-stable.svg 273b88eb801722e06ecf3e14e8aad67e *man/figures/logo.png 26b0e6d7257d798ae2ddb9eee0bad521 *man/gen_tbl.Rd 79199e713c01afb921c7b7e7302005e1 *man/generators.Rd 5a081fc6a179c2c3c69a526b598f6d59 *man/guess_type.Rd ed6764f79d88f000296189a14e33c4b1 *man/locale.Rd c1bfc3f64e9c81a4886e7fc618d94b92 *man/output_column.Rd 888435763d07a4f4711123cdf3f42f4a *man/problems.Rd 8ea663763ab5298343bfd97f43925321 *man/reexports.Rd d2960252b4b247bef56c46bc9e4b4c38 *man/spec.Rd a8880968951bae49337890d9a9b11182 *man/vroom-package.Rd 053264911738dfa227bb58d775dd2af8 *man/vroom.Rd 5134b3078fe1ef9ddc466372eaa5c95b *man/vroom_altrep.Rd 2b269584f37dbfc0720f322254b6c0d2 *man/vroom_altrep_opts.Rd 43fc776b99cec2a1feafbdc827d914dd *man/vroom_example.Rd 6d60f0f1e4e04251c35b3a0402a44990 *man/vroom_format.Rd 0a6f1b1f9a3fafee625a735c65c22a3f *man/vroom_fwf.Rd 3e2bb093678c52ba5d62d072c5daf658 *man/vroom_lines.Rd 680d0b76d64026eaaae630ceeeec8393 *man/vroom_progress.Rd c40ee20a33c6cb2fbe7f177339742a21 *man/vroom_str.Rd 72626f45550a521cdae5575a1b332752 *man/vroom_write.Rd cd2d37fa9bcdcc64af21fa6de7afac58 *man/vroom_write_lines.Rd f47b13e3b0712c14612a722e75325d30 *src/DateTime.h e5c3afdbc303bf5627b30fcbdf0403d8 *src/DateTimeParser.h 0d0ad673b72273bb6e4e1d1e479cdf16 *src/Iconv.cpp 78fb08b4a0ff380fc612de22c4fa0f8c *src/Iconv.h 63b5460e3126815b41d4ef15c6ae3709 *src/LocaleInfo.cpp e53e95e6227bb57203b78aa543db284f *src/LocaleInfo.h 218fe0451665a9e5d682c106e7557af2 *src/Makevars 2f48b5329dc1e2326238428ad6859b89 *src/altrep.cc 981800e144b27a60da8293a147457b09 *src/altrep.h d3272bdb78ca04f6fb983b7ae9ef2a77 *src/collectors.h 19639be826dbd3f87d988965a1b6dfcd *src/columns.h 69cf09d6f4e60bb39691070d4516b06f *src/connection.h 9c68c03bf1ce7e5832597e3e4a0cd966 *src/cpp11.cpp 6bbda1aa7d8dba97f9f9baa424ef122a *src/delimited_index.cc 625b366beac6451187de29565509d19c *src/delimited_index.h 737d7b4f7dbcdc852e84539b19975dd7 *src/delimited_index_connection.cc f952f303a08a5f67efbcd34cb7beaee1 *src/delimited_index_connection.h b033f37b66e1d7323f10e033bfc70730 *src/fixed_width_index.h 391c85514dec38ac523ef13686d31f89 *src/fixed_width_index_connection.cc d457cb616fca2821cb3d65122e329101 *src/fixed_width_index_connection.h da10ba3830f76c5f2e2caae63d9e6621 *src/gen.cc a5bbd1e5b57ad521baa58618e8ed31bf *src/grisu3.c d763bbf07076d54cf56b950534f343ab *src/grisu3.h 16c7f364f19a34cf31f4387b9c3a7f86 *src/guess_type.cc 1ca643821616d125a0bad00d7ca97c8c *src/iconv_file.cc 0a5e66a402095f5aeb9046fe6d6a3c1e *src/index.h 1cbda26cf57d7a5ae435440a0d1dd21c *src/index_collection.cc 0d41fcd469cc6bc4769dd9dcf0339dc5 *src/index_collection.h 1960b51b7beff4084c076accc5e62052 *src/iterator.h e1c9a711c35a90bc5b390b5353f4bb71 *src/mio/LICENSE 8cf1647ef048f533fd8f5c607f6c17d0 *src/mio/README.md 8cf194a0ba13d524b3edfa0f08e6a4df *src/mio/include/mio/detail/mmap.ipp b7f5fafa0aa67c0dd638a38a4a69d24e *src/mio/include/mio/detail/string_util.hpp 55dd33f9d3e166f6cc32952d86cdaa8c *src/mio/include/mio/mmap.hpp 3a884ac9f9863e5d2f58a3293a0a6880 *src/mio/include/mio/page.hpp 5d82bbbaf755ddcb59af919fc9efe29d *src/mio/include/mio/shared_mmap.hpp 778a0d3d0056150fe5337759b80c6ccd *src/multi_progress.h 1e3105050c184dd3a18f0a4220561354 *src/parallel.h 24896937d2b90d69a34a0313dbc6537c *src/r_utils.h d261cb4daf619cef259029b993429e7e *src/tzfile.h 6c4259245163d58f694c66d3caea835c *src/unicode_fopen.h 8cd98f01a8b7076f7caccce852a93523 *src/utils.h 89463ed14c213bb9d0927ae7e26e17cd *src/vroom.cc bc6e59a97792f73522c4b706c649e3b5 *src/vroom.h bcd039412d33b49e5ac2f570b241e93f *src/vroom_big_int.cc 8e285da00af731f756bbded5a2a210a0 *src/vroom_big_int.h eb8d41f82dc2789b3e08019f4840dc7b *src/vroom_chr.cc f5bcfd2d6b179b23688a030da3023de4 *src/vroom_chr.h 66eb93fb8d0d9e07d443893468363711 *src/vroom_date.cc b8d64c92836769dce863acc310f6b61c *src/vroom_date.h 52f8c1c415eb8f1ee27624c72b467cca *src/vroom_dbl.cc c23812aecc61b346d4cdf9bf1c095fe4 *src/vroom_dbl.h af3a86fe7d992356fbc42ea6c3a71004 *src/vroom_dttm.cc cc0d7a00b928323216daa4c7fe801307 *src/vroom_dttm.h ea3604e25a2c3442de752290a23217ff *src/vroom_errors.cpp c0212994d1d6e6506e7f70c71a5f65e9 *src/vroom_errors.h 2404ced932ce3ece2dfd9f5b49108d6b *src/vroom_fct.cc 90680f24d70e27a8d3b7d4e96b3b35c5 *src/vroom_fct.h 923b1d8cf6ad43048735f0906fbface0 *src/vroom_fwf.cc d0620846d5d9c5d1bb46de4c471e8836 *src/vroom_int.cc 28a9647fd5fe2d080e81add8b60e92e4 *src/vroom_int.h 54b804a64842b414b3bb5e4c8daf4657 *src/vroom_lgl.h 43ab6ea9ec923d28955d34a6e77d5666 *src/vroom_num.cc 07496d6fb89add1067bc08bf8acb0f43 *src/vroom_num.h b75c4a68af405e5a7c7e3ffd618d6fad *src/vroom_rle.cc 08d453b74508258cb0ed110118df7a4e *src/vroom_rle.h eb9e65723f8164b55f51aa1cb57b1bf3 *src/vroom_time.cc abf04133f8e92e47c25cccbaedfc56e9 *src/vroom_time.h 622e4959c76f105da75c4ae1440ea6e9 *src/vroom_types.h 05c88a65030ce08af2106cf71c7be934 *src/vroom_vec.h f6c71a2aca442f1d6138a30b2e9716c9 *src/vroom_write.cc 0622a97a2aaa3c342f09636052c2d7f5 *tests/spelling.R c82a2577cb9993b282b951cca140d5d9 *tests/testthat.R d41d8cd98f00b204e9800998ecf8427e *tests/testthat/empty-file 7c924d6682b55bb601d4e5b428123709 *tests/testthat/enc-iso-8859-1.txt 6e78e051cd53e60db1e95d692e1d6201 *tests/testthat/fwf-trailing-crlf.txt 2b4d8a640b79cf108e795e2a81a9cb4b *tests/testthat/fwf-trailing.txt c22dc4abbe3f38c60e008ec0e92509fe *tests/testthat/helper.R d41d8cd98f00b204e9800998ecf8427e *tests/testthat/logs/fixed_width_index.idx d41d8cd98f00b204e9800998ecf8427e *tests/testthat/logs/fixed_width_index_connection.idx d41d8cd98f00b204e9800998ecf8427e *tests/testthat/logs/index.idx d41d8cd98f00b204e9800998ecf8427e *tests/testthat/logs/index_connection.idx 2a8debaec7bb301e3e5455463b018367 *tests/testthat/mtcars.zip 716a6dfa9225688ecb2f91dab9954fcb *tests/testthat/multi-byte-ascii.txt 17a6ec92cea2c1a5caa57f6612f3393c *tests/testthat/multi-byte-unicode.txt 1079c96176280816d6627e9d96750120 *tests/testthat/multi-file/bar 365bb8566485f194fac0ae108cbf22cb *tests/testthat/multi-file/baz 0cad2bebd3e80c23b2dad26a746df5c1 *tests/testthat/multi-file/foo 1a7acac5f18865215e5bdc6bbe618907 *tests/testthat/multi-file/qux ea427f49d3ef99f68cc3902c7d317a87 *tests/testthat/raw.csv 952c30f579ed3438656f98f3d39f4af1 *tests/testthat/test-big-int.R 499edc14e0e51ca0e4fe827e1b237df1 *tests/testthat/test-chr.R 7b226d771e6b6a6164e3c769efe15a9e *tests/testthat/test-col_types.R 837e8ff294790892a522f89163667e51 *tests/testthat/test-connection.R f96176c36b87d1971daeeb2b8a9ce980 *tests/testthat/test-datetime.R 913abdb993e2926cc13ad335f772d0b8 *tests/testthat/test-dbl.R c1e3be8942f3a2a58083bd1319b1a56e *tests/testthat/test-factor.R 2d7dba6067b90d4d874ed642efb18031 *tests/testthat/test-int.R 89eee040ccb0ba243901045b26e05b98 *tests/testthat/test-logical.R 2a945d9305d538d52195b02df2534791 *tests/testthat/test-multi-byte.R f2927162c28ef85300d58875196a3a4a *tests/testthat/test-multi-file.R fac6d3f989eda914f12e380f494baa21 *tests/testthat/test-num.R 71cfe305f37133109f56f4d00cb0887a *tests/testthat/test-path.R 912ea24b3b730cc4409f122e6023e0e9 *tests/testthat/test-problems.R f681d6601e053c43de8332913e78f237 *tests/testthat/test-select.R 743d446d4eab9ea05e44b4425fdd5472 *tests/testthat/test-vroom.R e15e4b20e49fea5fbf8fe02a1d8ddbbd *tests/testthat/test-vroom_fwf.R d0d2e9b9819c731774723a38503a1fde *tests/testthat/test-vroom_lines.R d78f04d482f9756eb3202e8dd1efc324 *tests/testthat/test-vroom_write.R 9790810035aefaec5b5acc86554c8ce7 *vignettes/benchmarks.Rmd 622959f2520b142e209b7e9d4d32e85c *vignettes/vroom.Rmd vroom/inst/0000755000176200001440000000000014151250641012370 5ustar liggesusersvroom/inst/doc/0000755000176200001440000000000014151250641013135 5ustar liggesusersvroom/inst/doc/vroom.html0000644000176200001440000023442514151250641015177 0ustar liggesusers Get started with vroom

Get started with vroom

The vroom package contains one main function vroom() which is used to read all types of delimited files. A delimited file is any file in which the data is separated (delimited) by one or more characters.

The most common type of delimited files are CSV (Comma Separated Values) or TSV (Tab Separated Values) files, typically these files have a .csv and .tsv suffix respectively.

library(vroom)

This vignette covers the following topics:

  • The basics of reading files, including
    • single files
    • multiple files
    • compressed files
    • remote files
  • Skipping particular columns.
  • Specifying column types, for additional safety and when the automatic guessing fails.
  • Writing regular and compressed files

Reading files

To read a CSV, or other type of delimited file with vroom pass the file to vroom(). The delimiter will be automatically guessed if it is a common delimiter; e.g.Β (β€œ,” β€œβ€ " β€œ|” β€œ:” β€œ;”). If the guessing fails or you are using a less common delimiter specify it with the delim parameter. (e.g.Β delim = ",").

We have included an example CSV file in the vroom package for use in examples and tests. Access it with vroom_example("mtcars.csv")

# See where the example file is stored on your machine
file <- vroom_example("mtcars.csv")
file
#> [1] "/private/var/folders/9x/_8jnmxwj3rq1t90mlr6_0k1w0000gn/T/RtmprMsRu4/Rinst156b368267e5c/vroom/extdata/mtcars.csv"

# Read the file, by default vroom will guess the delimiter automatically.
vroom(file)
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # … with 29 more rows

# You can also specify it explicitly, which is (slightly) faster, and safer if
# you know how the file is delimited.
vroom(file, delim = ",")
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # … with 29 more rows

Reading multiple files

If you are reading a set of files which all have the same columns, you can pass the filenames directly to vroom() and it will combine them into one result.

First we will create some files to read by splitting the mtcars dataset by number of cylinders, (it is OK if you don’t currently understand this code).

mt <- tibble::rownames_to_column(mtcars, "model")
purrr::iwalk(
  split(mt, mt$cyl),
  ~ vroom_write(.x, glue::glue("mtcars_{.y}.csv"), "\t")
)

Then we can efficiently read them into one table by passing the filenames directly to vroom.

files <- fs::dir_ls(glob = "mtcars*csv")
files
#> mtcars_4.csv mtcars_6.csv mtcars_8.csv
vroom(files)
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: "\t"
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model        mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Datsun 710  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
#> 2 Merc 240D   24.4     4  147.    62  3.69  3.19  20       1     0     4     2
#> 3 Merc 230    22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
#> # … with 29 more rows

Often the filename or directory where the files are stored contains information. The id parameter can be used to add an extra column to the result with the full path to each file. (in this case we name the column path).

vroom(files, id = "path")
#> Rows: 32 Columns: 13
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: "\t"
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 13
#>   path   model   mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>  <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 mtcar… Dats…  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
#> 2 mtcar… Merc…  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
#> 3 mtcar… Merc…  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
#> # … with 29 more rows

Reading compressed files

vroom supports reading zip, gz, bz2 and xz compressed files automatically, just pass the filename of the compressed file to vroom.

file <- vroom_example("mtcars.csv.gz")

vroom(file)
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # … with 29 more rows

vroom() decompresses, indexes and writes the decompressed data to a file in the temp directory in a single stream. The temporary file is used to lazily look up the values and will be automatically cleaned up when all values in the object have been fully read, the object is removed, or the R session ends.

Reading single files from multiple multi-file zip archives

If you are reading a zip file that contains multiple files with the same format, you can use a wrapper function like this:

read_all_zip <- function(file, ...) {
  filenames <- unzip(file, list = TRUE)$Name
  vroom(purrr::map(filenames, ~ unz(file, .x)), ...)
}

Reading remote files

vroom can read files directly from the internet as well by passing the URL of the file to vroom.

file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv"
vroom(file)

It can even read gzipped files from the internet (although not the other compressed formats).

file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv.gz"
vroom(file)

Column selection

vroom provides the same interface for column selection and renaming as dplyr::select(). This provides very efficient, flexible and readable selections. For example you can select by:

  • A character vector of column names
file <- vroom_example("mtcars.csv.gz")

vroom(file, col_select = c(model, cyl, gear))
#> Rows: 32 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (1): model
#> dbl (2): cyl, gear
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 3
#>   model           cyl  gear
#>   <chr>         <dbl> <dbl>
#> 1 Mazda RX4         6     4
#> 2 Mazda RX4 Wag     6     4
#> 3 Datsun 710        4     4
#> # … with 29 more rows
  • A numeric vector of column indexes, e.g.Β c(1, 2, 5)
vroom(file, col_select = c(1, 3, 11))
#> Rows: 32 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (1): model
#> dbl (2): cyl, gear
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 3
#>   model           cyl  gear
#>   <chr>         <dbl> <dbl>
#> 1 Mazda RX4         6     4
#> 2 Mazda RX4 Wag     6     4
#> 3 Datsun 710        4     4
#> # … with 29 more rows
  • Using the selection helpers such as starts_with() and ends_with()
vroom(file, col_select = starts_with("d"))
#> Rows: 32 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> dbl (2): disp, drat
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 2
#>    disp  drat
#>   <dbl> <dbl>
#> 1   160  3.9 
#> 2   160  3.9 
#> 3   108  3.85
#> # … with 29 more rows
  • You can also rename columns
vroom(file, col_select = list(car = model, everything()))
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   car            mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # … with 29 more rows

Reading fixed width files

A fixed width file can be a very compact representation of numeric data. Unfortunately, it’s also often painful to read because you need to describe the length of every field. vroom aims to make it as easy as possible by providing a number of different ways to describe the field structure. Use vroom_fwf() in conjunction with one of the following helper functions to read the file.

fwf_sample <- vroom_example("fwf-sample.txt")
cat(readLines(fwf_sample))
#> John Smith          WA        418-Y11-4111 Mary Hartford       CA        319-Z19-4341 Evan Nolan          IL        219-532-c301
  • fwf_empty() - Guess based on the position of empty columns.
vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn")))
#> Rows: 3 Columns: 4
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (4): first, last, state, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 4
#>   first last     state ssn         
#>   <chr> <chr>    <chr> <chr>       
#> 1 John  Smith    WA    418-Y11-4111
#> 2 Mary  Hartford CA    319-Z19-4341
#> 3 Evan  Nolan    IL    219-532-c301
  • fwf_widths() - Use user provided set of field widths.
vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn")))
#> Rows: 3 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (3): name, state, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 3
#>   name          state ssn         
#>   <chr>         <chr> <chr>       
#> 1 John Smith    WA    418-Y11-4111
#> 2 Mary Hartford CA    319-Z19-4341
#> 3 Evan Nolan    IL    219-532-c301
  • fwf_positions() - Use user provided sets of start and end positions.
vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn")))
#> Rows: 3 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (2): name, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 2
#>   name          ssn         
#>   <chr>         <chr>       
#> 1 John Smith    418-Y11-4111
#> 2 Mary Hartford 319-Z19-4341
#> 3 Evan Nolan    219-532-c301
  • fwf_cols() - Use user provided named widths.
vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12))
#> Rows: 3 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (3): name, state, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 3
#>   name          state ssn         
#>   <chr>         <chr> <chr>       
#> 1 John Smith    WA    418-Y11-4111
#> 2 Mary Hartford CA    319-Z19-4341
#> 3 Evan Nolan    IL    219-532-c301
  • fwf_cols() - Use user provided named pairs of positions.
vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42)))
#> Rows: 3 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (2): name, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 2
#>   name          ssn         
#>   <chr>         <chr>       
#> 1 John Smith    418-Y11-4111
#> 2 Mary Hartford 319-Z19-4341
#> 3 Evan Nolan    219-532-c301

Column types

vroom guesses the data types of columns as they are read, however sometimes the guessing fails and it is necessary to explicitly set the type of one or more columns.

The available specifications are: (with single letter abbreviations in quotes)

  • col_logical() β€˜l’, containing only T, F, TRUE, FALSE, 1 or 0.
  • col_integer() β€˜i’, integer values.
  • col_big_integer() β€˜I’, Big integer values. (64bit integers)
  • col_double() β€˜d’, floating point values.
  • col_number() β€˜n’, numbers containing the grouping_mark
  • col_date(format = "") β€˜D’: with the locale’s date_format.
  • col_time(format = "") β€˜t’: with the locale’s time_format.
  • col_datetime(format = "") β€˜T’: ISO8601 date times.
  • col_factor(levels, ordered) β€˜f’, a fixed set of values.
  • col_character() β€˜c’, everything else.
  • col_skip() ’_, -’, don’t import this column.
  • col_guess() β€˜?’, parse using the β€œbest” type based on the input.

You can tell vroom what columns to use with the col_types() argument in a number of ways.

If you only need to override a single column the most concise way is to use a named vector.

# read the 'hp' columns as an integer
vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i"))
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # … with 29 more rows

# also skip reading the 'cyl' column
vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_"))
#> # A tibble: 32 Γ— 11
#>   model           mpg  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>         <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4      21     160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 Wag  21     160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710     22.8   108    93  3.85  2.32  18.6     1     1     4     1
#> # … with 29 more rows

# also read the gears as a factor
vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f"))
#> # A tibble: 32 Γ— 11
#>   model           mpg  disp    hp  drat    wt  qsec    vs    am gear   carb
#>   <chr>         <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 Mazda RX4      21     160   110  3.9   2.62  16.5     0     1 4         4
#> 2 Mazda RX4 Wag  21     160   110  3.9   2.88  17.0     0     1 4         4
#> 3 Datsun 710     22.8   108    93  3.85  2.32  18.6     1     1 4         1
#> # … with 29 more rows

You can read all the columns with the same type, by using the .default argument. For example reading everything as a character.

vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c"))
#> # A tibble: 32 Γ— 12
#>   model        mpg   cyl   disp  hp    drat  wt    qsec  vs    am    gear  carb 
#>   <chr>        <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Mazda RX4    21    6     160   110   3.9   2.62  16.46 0     1     4     4    
#> 2 Mazda RX4 W… 21    6     160   110   3.9   2.875 17.02 0     1     4     4    
#> 3 Datsun 710   22.8  4     108   93    3.85  2.32  18.61 1     1     4     1    
#> # … with 29 more rows

However you can also use the col_*() functions in a list.

vroom(
  vroom_example("mtcars.csv"),
  col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor())
)
#> # A tibble: 32 Γ— 11
#>   model           mpg  disp    hp  drat    wt  qsec    vs    am gear   carb
#>   <chr>         <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 Mazda RX4      21     160   110  3.9   2.62  16.5     0     1 4         4
#> 2 Mazda RX4 Wag  21     160   110  3.9   2.88  17.0     0     1 4         4
#> 3 Datsun 710     22.8   108    93  3.85  2.32  18.6     1     1 4         1
#> # … with 29 more rows

This is most useful when a column type needs additional information, such as for categorical data when you know all of the levels of a factor.

vroom(
  vroom_example("mtcars.csv"),
  col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5"))))
)
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am gear   carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1 4         4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1 4         4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1 4         1
#> # … with 29 more rows

Name repair

Often the names of columns in the original dataset are not ideal to work with. vroom() uses the same .name_repair argument as tibble, so you can use one of the default name repair strategies or provide a custom function. A great approach is to use the janitor::make_clean_names() function as the input. This will automatically clean the names to use whatever case you specify, here I am setting it to use ALLCAPS names.

vroom(
  vroom_example("mtcars.csv"),
  .name_repair = ~ janitor::make_clean_names(., case = "all_caps")
)

Writing delimited files

Use vroom_write() to write delimited files, the default delimiter is tab, to write TSV files. Writing to TSV by default has the following benefits: - Avoids the issue of whether to use ; (common in Europe) or , (common in the US) - Unlikely to require quoting in fields, as very few fields contain tabs - More easily and efficiently ingested by Unix command line tools such as cut, perl and awk.

vroom_write(mtcars, "mtcars.tsv")

Writing CSV delimited files

However you can also use delim = ',' to write CSV files, which are common as inputs to GUI spreadsheet tools like Excel or Google Sheets.

vroom_write(mtcars, "mtcars.csv", delim = ",")

Writing compressed files

For gzip, bzip2 and xz compression the outputs will be automatically compressed if the filename ends in .gz, .bz2 or .xz.

vroom_write(mtcars, "mtcars.tsv.gz")

vroom_write(mtcars, "mtcars.tsv.bz2")

vroom_write(mtcars, "mtcars.tsv.xz")

It is also possible to use other compressors by using pipe() with vroom_write() to create a pipe connection to command line utilities, such as

  • pigz, a parallel gzip implementation
  • lbzip2, a parallel bzip2 implementation
  • pixz, a parallel xz implementation
  • Zstandard, a modern real-time compression algorithm.

The parallel compression versions can be considerably faster for large output files and generally vroom_write() is fast enough that the compression speed becomes the bottleneck when writing.

vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz"))

Reading and writing from standard input and output

vroom supports reading and writing to the C-level stdin and stdout of the R process by using stdin() and stdout(). E.g. from a shell prompt you can pipe to and from vroom directly.

cat inst/extdata/mtcars.csv | Rscript -e 'vroom::vroom(stdin())'

Rscript -e 'vroom::vroom_write(iris, stdout())' | head

Note this interpretation of stdin() and stdout() differs from that used elsewhere by R, however we believe it better matches most user’s expectations for this use case.

Further reading

vroom/inst/doc/vroom.Rmd0000644000176200001440000002746614142515724014770 0ustar liggesusers--- title: "Get started with vroom" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Get started with vroom} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_knit$set(root.dir = tempdir()) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(tibble.print_min = 3) ``` The vroom package contains one main function `vroom()` which is used to read all types of delimited files. A delimited file is any file in which the data is separated (delimited) by one or more characters. The most common type of delimited files are CSV (Comma Separated Values) or TSV (Tab Separated Values) files, typically these files have a `.csv` and `.tsv` suffix respectively. ```{r} library(vroom) ``` This vignette covers the following topics: - The basics of reading files, including - single files - multiple files - compressed files - remote files - Skipping particular columns. - Specifying column types, for additional safety and when the automatic guessing fails. - Writing regular and compressed files ## Reading files To read a CSV, or other type of delimited file with vroom pass the file to `vroom()`. The delimiter will be automatically guessed if it is a common delimiter; e.g. ("," "\t" " " "|" ":" ";"). If the guessing fails or you are using a less common delimiter specify it with the `delim` parameter. (e.g. `delim = ","`). We have included an example CSV file in the vroom package for use in examples and tests. Access it with `vroom_example("mtcars.csv")` ```{r} # See where the example file is stored on your machine file <- vroom_example("mtcars.csv") file # Read the file, by default vroom will guess the delimiter automatically. vroom(file) # You can also specify it explicitly, which is (slightly) faster, and safer if # you know how the file is delimited. vroom(file, delim = ",") ``` ## Reading multiple files If you are reading a set of files which all have the same columns, you can pass the filenames directly to `vroom()` and it will combine them into one result. First we will create some files to read by splitting the mtcars dataset by number of cylinders, (it is OK if you don't currently understand this code). ```{r} mt <- tibble::rownames_to_column(mtcars, "model") purrr::iwalk( split(mt, mt$cyl), ~ vroom_write(.x, glue::glue("mtcars_{.y}.csv"), "\t") ) ``` Then we can efficiently read them into one table by passing the filenames directly to vroom. ```{r} files <- fs::dir_ls(glob = "mtcars*csv") files vroom(files) ``` Often the filename or directory where the files are stored contains information. The `id` parameter can be used to add an extra column to the result with the full path to each file. (in this case we name the column `path`). ```{r} vroom(files, id = "path") ``` ```{r, include = FALSE} # just to clear .Last.value 1 + 1 gc() unlink(files) ``` ## Reading compressed files vroom supports reading zip, gz, bz2 and xz compressed files automatically, just pass the filename of the compressed file to vroom. ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file) ``` `vroom()` decompresses, indexes and writes the decompressed data to a file in the temp directory in a single stream. The temporary file is used to lazily look up the values and will be automatically cleaned up when all values in the object have been fully read, the object is removed, or the R session ends. ### Reading single files from multiple multi-file zip archives If you are reading a zip file that contains multiple files with the same format, you can use a wrapper function like this: ```{r} read_all_zip <- function(file, ...) { filenames <- unzip(file, list = TRUE)$Name vroom(purrr::map(filenames, ~ unz(file, .x)), ...) } ``` ## Reading remote files vroom can read files directly from the internet as well by passing the URL of the file to vroom. ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv" vroom(file) ``` It can even read gzipped files from the internet (although not the other compressed formats). ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv.gz" vroom(file) ``` ## Column selection vroom provides the same interface for column selection and renaming as [dplyr::select()](https://dplyr.tidyverse.org/reference/select.html). This provides very efficient, flexible and readable selections. For example you can select by: - A character vector of column names ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file, col_select = c(model, cyl, gear)) ``` - A numeric vector of column indexes, e.g. `c(1, 2, 5)` ```{r} vroom(file, col_select = c(1, 3, 11)) ``` - Using the selection helpers such as `starts_with()` and `ends_with()` ```{r} vroom(file, col_select = starts_with("d")) ``` - You can also rename columns ```{r} vroom(file, col_select = list(car = model, everything())) ``` ## Reading fixed width files A fixed width file can be a very compact representation of numeric data. Unfortunately, it's also often painful to read because you need to describe the length of every field. vroom aims to make it as easy as possible by providing a number of different ways to describe the field structure. Use `vroom_fwf()` in conjunction with one of the following helper functions to read the file. ```{r} fwf_sample <- vroom_example("fwf-sample.txt") cat(readLines(fwf_sample)) ``` - `fwf_empty()` - Guess based on the position of empty columns. ```{r} vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) ``` - `fwf_widths()` - Use user provided set of field widths. ```{r} vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) ``` - `fwf_positions()` - Use user provided sets of start and end positions. ```{r} vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) ``` - `fwf_cols()` - Use user provided named widths. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) ``` - `fwf_cols()` - Use user provided named pairs of positions. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) ``` ## Column types vroom guesses the data types of columns as they are read, however sometimes the guessing fails and it is necessary to explicitly set the type of one or more columns. The available specifications are: (with single letter abbreviations in quotes) * `col_logical()` 'l', containing only `T`, `F`, `TRUE`, `FALSE`, `1` or `0`. * `col_integer()` 'i', integer values. * `col_big_integer()` 'I', Big integer values. (64bit integers) * `col_double()` 'd', floating point values. * `col_number()` 'n', numbers containing the `grouping_mark` * `col_date(format = "")` 'D': with the locale's `date_format`. * `col_time(format = "")` 't': with the locale's `time_format`. * `col_datetime(format = "")` 'T': ISO8601 date times. * `col_factor(levels, ordered)` 'f', a fixed set of values. * `col_character()` 'c', everything else. * `col_skip()` '_, -', don't import this column. * `col_guess()` '?', parse using the "best" type based on the input. You can tell vroom what columns to use with the `col_types()` argument in a number of ways. If you only need to override a single column the most concise way is to use a named vector. ```{r} # read the 'hp' columns as an integer vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i")) # also skip reading the 'cyl' column vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_")) # also read the gears as a factor vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f")) ``` You can read all the columns with the same type, by using the `.default` argument. For example reading everything as a character. ```{r} vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c")) ``` However you can also use the `col_*()` functions in a list. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor()) ) ``` This is most useful when a column type needs additional information, such as for categorical data when you know all of the levels of a factor. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5")))) ) ``` ## Name repair Often the names of columns in the original dataset are not ideal to work with. `vroom()` uses the same `.name_repair` argument as tibble, so you can use one of the default name repair strategies or provide a custom function. A great approach is to use the [`janitor::make_clean_names()`](http://sfirke.github.io/janitor/reference/make_clean_names.html) function as the input. This will automatically clean the names to use whatever case you specify, here I am setting it to use `ALLCAPS` names. ```{r, eval = FALSE} vroom( vroom_example("mtcars.csv"), .name_repair = ~ janitor::make_clean_names(., case = "all_caps") ) ``` ## Writing delimited files Use `vroom_write()` to write delimited files, the default delimiter is tab, to write TSV files. Writing to TSV by default has the following benefits: - Avoids the issue of whether to use `;` (common in Europe) or `,` (common in the US) - Unlikely to require quoting in fields, as very few fields contain tabs - More easily and efficiently ingested by Unix command line tools such as `cut`, `perl` and `awk`. ```{r} vroom_write(mtcars, "mtcars.tsv") ``` ```{r, include = FALSE} unlink("mtcars.tsv") ``` ### Writing CSV delimited files However you can also use `delim = ','` to write CSV files, which are common as inputs to GUI spreadsheet tools like Excel or Google Sheets. ```{r} vroom_write(mtcars, "mtcars.csv", delim = ",") ``` ```{r, include = FALSE} unlink("mtcars.csv") ``` ### Writing compressed files For gzip, bzip2 and xz compression the outputs will be automatically compressed if the filename ends in `.gz`, `.bz2` or `.xz`. ```{r} vroom_write(mtcars, "mtcars.tsv.gz") vroom_write(mtcars, "mtcars.tsv.bz2") vroom_write(mtcars, "mtcars.tsv.xz") ``` ```{r, include = FALSE} unlink(c("mtcars.tsv.gz", "mtcars.tsv.bz2", "mtcars.tsv.xz")) ``` It is also possible to use other compressors by using `pipe()` with `vroom_write()` to create a pipe connection to command line utilities, such as - [pigz](https://zlib.net/pigz/), a parallel gzip implementation - lbzip2, a parallel bzip2 implementation - [pixz](https://github.com/vasi/pixz), a parallel xz implementation - [Zstandard](https://facebook.github.io/zstd/), a modern real-time compression algorithm. The parallel compression versions can be considerably faster for large output files and generally `vroom_write()` is fast enough that the compression speed becomes the bottleneck when writing. ```{r, eval = nzchar(Sys.which("pigz"))} vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz")) ``` ```{r, include = FALSE} unlink("mtcars.tsv.gz") ``` ### Reading and writing from standard input and output vroom supports reading and writing to the C-level `stdin` and `stdout` of the R process by using `stdin()` and `stdout()`. E.g. from a shell prompt you can pipe to and from vroom directly. ```shell cat inst/extdata/mtcars.csv | Rscript -e 'vroom::vroom(stdin())' Rscript -e 'vroom::vroom_write(iris, stdout())' | head ``` Note this interpretation of `stdin()` and `stdout()` differs from that used elsewhere by R, however we believe it better matches most user's expectations for this use case. ## Further reading - `vignette("benchmarks")` discusses the performance of vroom, how it compares to alternatives and how it achieves its results. - [πŸ“½ vroom: Because Life is too short to read slow](https://www.youtube.com/watch?v=RA9AjqZXxMU&t=10s) - Presentation of vroom at UseR!2019 ([slides](https://speakerdeck.com/jimhester/vroom)) - [πŸ“Ή vroom: Read and write rectangular data quickly](https://www.youtube.com/watch?v=ZP_y5eaAc60) - a video tour of the vroom features. vroom/inst/doc/benchmarks.Rmd0000644000176200001440000003451114142553112015721 0ustar liggesusers--- title: "Vroom Benchmarks" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Vroom Benchmarks} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(ggplot2) library(forcats) library(dplyr) library(tidyr) library(fs) pretty_sec <- function(x) { x[!is.na(x)] <- prettyunits::pretty_sec(x[!is.na(x)]) x } pretty_lgl <- function(x) { case_when( x == TRUE ~ "TRUE", x == FALSE ~ "FALSE", TRUE ~ "" ) } read_benchmark <- function(file, desc) { vroom::vroom(file, col_types = c("ccccddddd")) %>% filter(op != "setup") %>% mutate( altrep = case_when( grepl("^vroom_no_altrep", reading_package) ~ FALSE, grepl("^vroom", reading_package) ~ TRUE, TRUE ~ NA ), reading_package = case_when( grepl("^vroom", reading_package) ~ "vroom", TRUE ~ reading_package ), label = fct_reorder( glue::glue("{reading_package}{altrep}\n{manip_package}", altrep = ifelse(is.na(altrep), "", glue::glue("(altrep = {altrep})")) ), case_when(type == "real" ~ time, TRUE ~ 0), sum), op = factor(op, desc) ) } generate_subtitle <- function(data) { rows <- scales::comma(data$rows[[1]]) cols <- scales::comma(data$cols[[1]]) size <- fs_bytes(data$size[[1]]) glue::glue("{rows} x {cols} - {size}B") } plot_benchmark <- function(data, title) { subtitle <- generate_subtitle(data) data <- data %>% filter(reading_package != "read.delim", type == "real") p1 <- data %>% ggplot() + geom_bar(aes(x = label, y = time, fill = op, group = label), stat = "identity") + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + coord_flip() + labs(title = title, subtitle = subtitle, x = NULL, y = NULL, fill = NULL) + theme(legend.position = "bottom") p2 <- data %>% group_by(label) %>% summarise(max_memory = max(max_memory)) %>% ggplot() + geom_bar(aes(x = label, y = max_memory / (1024 * 1024)), stat = "identity") + scale_y_continuous(labels = scales::number_format(suffix = "Mb")) + coord_flip() + labs(title = "Maximum memory usage", x = NULL, y = NULL) + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) library(patchwork) p1 + p2 + plot_layout(widths = c(2, 1)) } make_table <- function(data) { data %>% filter(type == "real") %>% select(-label, -size, -type, -rows, -cols) %>% spread(op, time) %>% mutate( total = read + print + head + tail + sample + filter + aggregate, max_memory = as.character(bench::as_bench_bytes(max_memory)) ) %>% arrange(desc(total)) %>% mutate_if(is.numeric, pretty_sec) %>% mutate_if(is.logical, pretty_lgl) %>% select(reading_package, manip_package, altrep, max_memory, everything()) %>% rename( "reading\npackage" = reading_package, "manipulating\npackage" = manip_package, memory = max_memory ) %>% knitr::kable(digits = 2, align = "r", format = "html") } desc <- c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate") ``` vroom is a new approach to reading delimited and fixed width data into R. It stems from the observation that when parsing files reading data from disk and finding the delimiters is generally not the main bottle neck. Instead (re)-allocating memory and parsing the values into R data types (particularly for characters) takes the bulk of the time. Therefore you can obtain very rapid input by first performing a fast indexing step and then using the Altrep framework available in R versions 3.5+ to access the values in a lazy / delayed fashion. ## How it works The initial reading of the file simply records the locations of each individual record, the actual values are not read into R. Altrep vectors are created for each column in the data which hold a pointer to the index and the memory mapped file. When these vectors are indexed the value is read from the memory mapping. This means initial reading is extremely fast, in the real world dataset below it is ~ 1/4 the time of the multi-threaded `data.table::fread()`. Sampling operations are likewise extremely fast, as only the data actually included in the sample is read. This means things like the tibble print method, calling `head()`, `tail()` `x[sample(), ]` etc. have very low overhead. Filtering also can be fast, only the columns included in the filter selection have to be fully read and only the data in the filtered rows needs to be read from the remaining columns. Grouped aggregations likewise only need to read the grouping variables and the variables aggregated. Once a particular vector is fully materialized the speed for all subsequent operations should be identical to a normal R vector. This approach potentially also allows you to work with data that is larger than memory. As long as you are careful to avoid materializing the entire dataset at once it can be efficiently queried and subset. # Reading delimited files The following benchmarks all measure reading delimited files of various sizes and data types. Because vroom delays reading the benchmarks also do some manipulation of the data afterwards to try and provide a more realistic performance comparison. Because the `read.delim` results are so much slower than the others they are excluded from the plots, but are retained in the tables. ## Taxi Trip Dataset This real world dataset is from Freedom of Information Law (FOIL) Taxi Trip Data from the NYC Taxi and Limousine Commission 2013, originally posted at . It is also hosted on [archive.org](https://archive.org/details/nycTaxiTripData2013). The first table trip_fare_1.csv is 1.55G in size. #> Observations: 14,776,615 #> Variables: 11 #> $ medallion "89D227B655E5C82AECF13C3F540D4CF4", "0BD7C8F5B... #> $ hack_license "BA96DE419E711691B9445D6A6307C170", "9FD8F69F0... #> $ vendor_id "CMT", "CMT", "CMT", "CMT", "CMT", "CMT", "CMT... #> $ pickup_datetime "2013-01-01 15:11:48", "2013-01-06 00:18:35", ... #> $ payment_type "CSH", "CSH", "CSH", "CSH", "CSH", "CSH", "CSH... #> $ fare_amount 6.5, 6.0, 5.5, 5.0, 9.5, 9.5, 6.0, 34.0, 5.5, ... #> $ surcharge 0.0, 0.5, 1.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 0... #> $ mta_tax 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0... #> $ tip_amount 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0... #> $ tolls_amount 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.8, 0.0, 0... #> $ total_amount 7.0, 7.0, 7.0, 6.0, 10.5, 10.0, 6.5, 39.3, 7.0... ### Taxi Benchmarks code: [bench/taxi](https://github.com/r-lib/vroom/tree/main/inst/bench/taxi) All benchmarks were run on a Amazon EC2 [m5.4xlarge](https://aws.amazon.com/ec2/instance-types/m5/) instance with 16 vCPUs and an [EBS](https://aws.amazon.com/ebs/) volume type. The benchmarks labeled `vroom_base` uses `vroom` with base functions for manipulation. `vroom_dplyr` uses `vroom` to read the file and dplyr functions to manipulate. `data.table` uses `fread()` to read the file and `data.table` functions to manipulate and `readr` uses `readr` to read the file and `dplyr` to manipulate. By default vroom only uses Altrep for character vectors, these are labeled `vroom(altrep: normal)`. The benchmarks labeled `vroom(altrep: full)` instead use Altrep vectors for all supported types and `vroom(altrep: none)` disable Altrep entirely. The following operations are performed. - The data is read - `print()` - _N.B. read.delim uses `print(head(x, 10))` because printing the whole dataset takes > 10 minutes_ - `head()` - `tail()` - Sampling 100 random rows - Filtering for "UNK" payment, this is 6434 rows (0.0435% of total). - Aggregation of mean fare amount per payment type. ```{r, fig.height = 8, fig.width=10, warning = FALSE, echo = FALSE, message = FALSE} taxi <- read_benchmark(path_package("vroom", "bench", "taxi.tsv"), desc) plot_benchmark(taxi, "Time to analyze taxi trip data") make_table(taxi) ``` (*N.B. Rcpp used in the dplyr implementation fully materializes all the Altrep numeric vectors when using `filter()` or `sample_n()`, which is why the first of these cases have additional overhead when using full Altrep.*). ## All numeric data All numeric data is really a worst case scenario for vroom. The index takes about as much memory as the parsed data. Also because parsing doubles can be done quickly in parallel and text representations of doubles are only ~25 characters at most there isn't a great deal of savings for delayed parsing. For these reasons (and because the data.table implementation is very fast) vroom is a bit slower than fread for pure numeric data. However because vroom is multi-threaded it is a bit quicker than readr and read.delim for this type of data. ### Long code: [bench/all_numeric-long](https://github.com/r-lib/vroom/tree/main/inst/bench/all_numeric-long) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num <- read_benchmark(path_package("vroom", "bench", "all_numeric-long.tsv"), desc) plot_benchmark(all_num, "Time to analyze long all numeric data") make_table(all_num) ``` ### Wide code: [bench/all_numeric-wide](https://github.com/r-lib/vroom/tree/main/inst/bench/all_numeric-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num_wide <- read_benchmark(path_package("bench", "all_numeric-wide.tsv", package = "vroom"), desc) plot_benchmark(all_num_wide, "Time to analyze wide all numeric data") make_table(all_num_wide) ``` ## All character data code: [bench/all_character-long](https://github.com/r-lib/vroom/tree/main/inst/bench/all_character-long) All character data is a best case scenario for vroom when using Altrep, as it takes full advantage of the lazy reading. ### Long ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr <- read_benchmark(path_package("vroom", "bench", "all_character-long.tsv"), desc) plot_benchmark(all_chr, "Time to analyze long all character data") make_table(all_chr) ``` ### Wide code: [bench/all_character-wide](https://github.com/r-lib/vroom/tree/main/inst/bench/all_character-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr_wide <- read_benchmark(path_package("vroom", "bench", "all_character-wide.tsv"), desc) plot_benchmark(all_chr_wide, "Time to analyze wide all character data") make_table(all_chr_wide) ``` # Reading multiple delimited files code: [bench/taxi_multiple](https://github.com/r-lib/vroom/tree/main/inst/bench/taxi_multiple) ```{r, echo = FALSE, message = FALSE, eval = TRUE} mult <- read_benchmark(path_package("vroom", "bench", "taxi_multiple.tsv"), desc) ``` The benchmark reads all 12 files in the taxi trip fare data, totaling `r scales::comma(mult$rows[[1]])` rows and `r mult$cols[[1]]` columns for a total file size of `r format(fs_bytes(mult$size[[1]]))`. ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(mult, "Time to analyze multiple file data") make_table(mult) ``` # Reading fixed width files ## United States Census 5-Percent Public Use Microdata Sample files ```{r, echo = FALSE, message = FALSE, eval = TRUE} fwf <- read_benchmark(path_package("vroom", "bench", "fwf.tsv"), desc) ``` This fixed width dataset contains individual records of the characteristics of a 5 percent sample of people and housing units from the year 2000 and is freely available at . The data is split into files by state, and the state of California was used in this benchmark. The data totals `r scales::comma(fwf$rows[[1]])` rows and `r fwf$cols[[1]]` columns with a total file size of `r format(fs_bytes(fwf$size[[1]]))`. ## Census data benchmarks code: [bench/fwf](https://github.com/r-lib/vroom/tree/main/inst/bench/fwf) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(fwf, "Time to analyze fixed width data") make_table(fwf) ``` # Writing delimited files code: [bench/taxi_writing](https://github.com/r-lib/vroom/tree/main/inst/bench/taxi_writing) The benchmarks write out the taxi trip dataset in a few different ways. - An uncompressed file - A gzip compressed file using `gzfile()` _(readr and vroom do this automatically for files ending in `.gz`)_ - A gzip compressed file compressed with multiple threads (natively for data.table and using a `pipe()` connection to [pigz](https://zlib.net/pigz/) for the rest). - A [Zstandard](https://facebook.github.io/zstd/) compressed file (data.table does not support this format). ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} taxi_writing <- read_benchmark(path_package("vroom", "bench", "taxi_writing.tsv"), c("setup", "writing")) %>% rename( package = reading_package, compression = manip_package ) %>% mutate( package = factor(package, c("base", "readr", "data.table", "vroom")), compression = factor(compression, rev(c("gzip", "multithreaded_gzip", "zstandard", "uncompressed"))) ) %>% filter(type == "real") subtitle <- generate_subtitle(taxi_writing) taxi_writing %>% ggplot(aes(x = compression, y = time, fill = package)) + geom_bar(stat = "identity", position = position_dodge2(reverse = TRUE, padding = .05)) + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + theme(legend.position = "bottom") + coord_flip() + labs(title = "Writing taxi trip data", subtitle = subtitle, x = NULL, y = NULL, fill = NULL) taxi_writing %>% select(-size, -op, -rows, -cols, -type, -altrep, -label, -max_memory) %>% mutate_if(is.numeric, pretty_sec) %>% pivot_wider(names_from = package, values_from = time) %>% arrange(desc(compression)) %>% knitr::kable(digits = 2, align = "r", format = "html") ``` ## Session and package information ```{r, echo = FALSE, warning = FALSE, message = FALSE} si <- vroom::vroom(path_package("vroom", "bench", "session_info.tsv")) class(si) <- c("packages_info", "data.frame") select(as.data.frame(si), package, version = ondiskversion, date, source) %>% knitr::kable() ``` vroom/inst/doc/benchmarks.html0000644000176200001440000203661114151250640016150 0ustar liggesusers Vroom Benchmarks

Vroom Benchmarks

vroom is a new approach to reading delimited and fixed width data into R.

It stems from the observation that when parsing files reading data from disk and finding the delimiters is generally not the main bottle neck. Instead (re)-allocating memory and parsing the values into R data types (particularly for characters) takes the bulk of the time.

Therefore you can obtain very rapid input by first performing a fast indexing step and then using the Altrep framework available in R versions 3.5+ to access the values in a lazy / delayed fashion.

How it works

The initial reading of the file simply records the locations of each individual record, the actual values are not read into R. Altrep vectors are created for each column in the data which hold a pointer to the index and the memory mapped file. When these vectors are indexed the value is read from the memory mapping.

This means initial reading is extremely fast, in the real world dataset below it is ~ 1/4 the time of the multi-threaded data.table::fread(). Sampling operations are likewise extremely fast, as only the data actually included in the sample is read. This means things like the tibble print method, calling head(), tail() x[sample(), ] etc. have very low overhead. Filtering also can be fast, only the columns included in the filter selection have to be fully read and only the data in the filtered rows needs to be read from the remaining columns. Grouped aggregations likewise only need to read the grouping variables and the variables aggregated.

Once a particular vector is fully materialized the speed for all subsequent operations should be identical to a normal R vector.

This approach potentially also allows you to work with data that is larger than memory. As long as you are careful to avoid materializing the entire dataset at once it can be efficiently queried and subset.

Reading delimited files

The following benchmarks all measure reading delimited files of various sizes and data types. Because vroom delays reading the benchmarks also do some manipulation of the data afterwards to try and provide a more realistic performance comparison.

Because the read.delim results are so much slower than the others they are excluded from the plots, but are retained in the tables.

Taxi Trip Dataset

This real world dataset is from Freedom of Information Law (FOIL) Taxi Trip Data from the NYC Taxi and Limousine Commission 2013, originally posted at https://chriswhong.com/open-data/foil_nyc_taxi/. It is also hosted on archive.org.

The first table trip_fare_1.csv is 1.55G in size.

#> Observations: 14,776,615
#> Variables: 11
#> $ medallion       <chr> "89D227B655E5C82AECF13C3F540D4CF4", "0BD7C8F5B...
#> $ hack_license    <chr> "BA96DE419E711691B9445D6A6307C170", "9FD8F69F0...
#> $ vendor_id       <chr> "CMT", "CMT", "CMT", "CMT", "CMT", "CMT", "CMT...
#> $ pickup_datetime <chr> "2013-01-01 15:11:48", "2013-01-06 00:18:35", ...
#> $ payment_type    <chr> "CSH", "CSH", "CSH", "CSH", "CSH", "CSH", "CSH...
#> $ fare_amount     <dbl> 6.5, 6.0, 5.5, 5.0, 9.5, 9.5, 6.0, 34.0, 5.5, ...
#> $ surcharge       <dbl> 0.0, 0.5, 1.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 0...
#> $ mta_tax         <dbl> 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0...
#> $ tip_amount      <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
#> $ tolls_amount    <dbl> 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.8, 0.0, 0...
#> $ total_amount    <dbl> 7.0, 7.0, 7.0, 6.0, 10.5, 10.0, 6.5, 39.3, 7.0...

Taxi Benchmarks

code: bench/taxi

All benchmarks were run on a Amazon EC2 m5.4xlarge instance with 16 vCPUs and an EBS volume type.

The benchmarks labeled vroom_base uses vroom with base functions for manipulation. vroom_dplyr uses vroom to read the file and dplyr functions to manipulate. data.table uses fread() to read the file and data.table functions to manipulate and readr uses readr to read the file and dplyr to manipulate. By default vroom only uses Altrep for character vectors, these are labeled vroom(altrep: normal). The benchmarks labeled vroom(altrep: full) instead use Altrep vectors for all supported types and vroom(altrep: none) disable Altrep entirely.

The following operations are performed.

  • The data is read
  • print() - N.B. read.delim uses print(head(x, 10)) because printing the whole dataset takes > 10 minutes
  • head()
  • tail()
  • Sampling 100 random rows
  • Filtering for β€œUNK” payment, this is 6434 rows (0.0435% of total).
  • Aggregation of mean fare amount per payment type.
reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 6.18GB 1m 12.3s 6ms 1ms 1ms 1ms 1.3s 895ms 1m 14.5s
readr dplyr 6.91GB 37.3s 147ms 2ms 1ms 17ms 249ms 538ms 38.3s
vroom dplyr FALSE 6.55GB 18.4s 117ms 2ms 1ms 14ms 961ms 1.2s 20.7s
vroom base TRUE 6.35GB 1.4s 158ms 3ms 1ms 1ms 1.1s 7.4s 10s
data.table data.table 6.38GB 5.8s 12ms 1ms 1ms 1ms 104ms 764ms 6.7s
vroom dplyr TRUE 6.41GB 1.3s 76ms 2ms 1ms 11ms 1.3s 4s 6.7s

(N.B. Rcpp used in the dplyr implementation fully materializes all the Altrep numeric vectors when using filter() or sample_n(), which is why the first of these cases have additional overhead when using full Altrep.).

All numeric data

All numeric data is really a worst case scenario for vroom. The index takes about as much memory as the parsed data. Also because parsing doubles can be done quickly in parallel and text representations of doubles are only ~25 characters at most there isn’t a great deal of savings for delayed parsing.

For these reasons (and because the data.table implementation is very fast) vroom is a bit slower than fread for pure numeric data.

However because vroom is multi-threaded it is a bit quicker than readr and read.delim for this type of data.

Long

code: bench/all_numeric-long

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 4.79GB 1m 51.4s 1.4s 1ms 1ms 2ms 4.5s 37ms 1m 57.3s
readr dplyr 2.82GB 13.1s 64ms 2ms 1ms 16ms 18ms 55ms 13.3s
vroom dplyr FALSE 2.75GB 1.3s 48ms 1ms 1ms 14ms 18ms 46ms 1.5s
vroom base FALSE 2.69GB 1.3s 48ms 1ms 1ms 3ms 6ms 55ms 1.4s
vroom dplyr TRUE 3.29GB 604ms 64ms 1ms 1ms 14ms 42ms 235ms 959ms
vroom base TRUE 3.28GB 581ms 55ms 1ms 1ms 3ms 29ms 251ms 920ms
data.table data.table 2.72GB 256ms 13ms 1ms 1ms 4ms 6ms 25ms 302ms

Wide

code: bench/all_numeric-wide

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 14.41GB 8m 41s 131ms 7ms 7ms 9ms 75ms 5ms 8m 41.2s
readr dplyr 5.46GB 56.1s 96ms 3ms 3ms 26ms 18ms 39ms 56.3s
vroom dplyr FALSE 5.35GB 6.9s 63ms 3ms 3ms 95ms 14ms 31ms 7.1s
vroom base FALSE 5.34GB 6.9s 61ms 3ms 3ms 5ms 6ms 7ms 7s
vroom dplyr TRUE 7.26GB 3s 68ms 4ms 14ms 23ms 20ms 77ms 3.2s
vroom base TRUE 7.26GB 3s 68ms 4ms 4ms 5ms 11ms 42ms 3.1s
data.table data.table 5.48GB 1.3s 100ms 1ms 1ms 3ms 4ms 4ms 1.4s

All character data

code: bench/all_character-long

All character data is a best case scenario for vroom when using Altrep, as it takes full advantage of the lazy reading.

Long

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 4.53GB 1m 43.1s 8ms 1ms 1ms 2ms 28ms 293ms 1m 43.4s
readr dplyr 4.35GB 1m 2.6s 102ms 2ms 1ms 17ms 20ms 215ms 1m 2.9s
vroom dplyr FALSE 4.3GB 50.5s 50ms 2ms 1ms 16ms 21ms 150ms 50.7s
data.table data.table 4.73GB 42.8s 16ms 1ms 1ms 4ms 16ms 149ms 43s
vroom base TRUE 3.22GB 595ms 46ms 1ms 1ms 3ms 163ms 2.1s 2.9s
vroom dplyr TRUE 3.21GB 640ms 58ms 2ms 1ms 16ms 185ms 1.2s 2.1s

Wide

code: bench/all_character-wide

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 13.09GB 8m 30.4s 149ms 7ms 8ms 26ms 224ms 59ms 8m 30.9s
readr dplyr 12.21GB 7m 39.4s 217ms 4ms 3ms 29ms 38ms 57ms 7m 39.8s
vroom dplyr FALSE 12.14GB 4m 7.3s 67ms 3ms 3ms 28ms 35ms 37ms 4m 7.5s
data.table data.table 12.66GB 3m 21.8s 135ms 2ms 2ms 33ms 168ms 15ms 3m 22.1s
vroom base TRUE 6.57GB 3.1s 62ms 5ms 4ms 5ms 55ms 252ms 3.5s
vroom dplyr TRUE 6.57GB 3.1s 64ms 5ms 4ms 27ms 82ms 160ms 3.4s

Reading multiple delimited files

code: bench/taxi_multiple

The benchmark reads all 12 files in the taxi trip fare data, totaling 173,179,759 rows and 11 columns for a total file size of 18.4G.

reading package manipulating package altrep memory read print head tail sample filter aggregate total
readr dplyr 63.5GB 7m 55s 837ms 1ms 1ms 15ms 4.2s 13.5s 8m 13.6s
vroom dplyr FALSE 63.1GB 3m 52.3s 2.2s 2ms 1ms 14ms 10.5s 7.2s 4m 12.2s
vroom base TRUE 88.3GB 20.3s 3s 1ms 1ms 1ms 21.5s 2m 22.6s 3m 7.5s
vroom dplyr TRUE 88GB 20.4s 2.8s 1ms 1ms 13ms 23.9s 1m 5.6s 1m 52.7s
data.table data.table 59.6GB 1m 35.3s 7ms 1ms 1ms 1ms 1.1s 4.7s 1m 41.1s

Reading fixed width files

United States Census 5-Percent Public Use Microdata Sample files

This fixed width dataset contains individual records of the characteristics of a 5 percent sample of people and housing units from the year 2000 and is freely available at https://web.archive.org/web/20150908055439/https://www2.census.gov/census_2000/datasets/PUMS/FivePercent/California/all_California.zip. The data is split into files by state, and the state of California was used in this benchmark.

The data totals 2,342,339 rows and 37 columns with a total file size of 677M.

Census data benchmarks

code: bench/fwf

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 6.17GB 18m 9.6s 16ms 1ms 2ms 3ms 492ms 90ms 18m 10.2s
readr dplyr 6.19GB 32.6s 48ms 2ms 1ms 17ms 95ms 94ms 32.8s
vroom dplyr FALSE 5.96GB 14.7s 44ms 1ms 1ms 15ms 468ms 91ms 15.3s
vroom base TRUE 4.65GB 164ms 56ms 1ms 1ms 7ms 285ms 1.8s 2.3s
vroom dplyr TRUE 4.62GB 163ms 48ms 2ms 1ms 16ms 306ms 1.3s 1.8s

Writing delimited files

code: bench/taxi_writing

The benchmarks write out the taxi trip dataset in a few different ways.

  • An uncompressed file
  • A gzip compressed file using gzfile() (readr and vroom do this automatically for files ending in .gz)
  • A gzip compressed file compressed with multiple threads (natively for data.table and using a pipe() connection to pigz for the rest).
  • A Zstandard compressed file (data.table does not support this format).
compression base data.table readr vroom
gzip 3m 17.1s 1m 7.8s 2m 0.2s 1m 14.4s
multithreaded_gzip 1m 37.8s 8.9s 53.4s 8.1s
zstandard 1m 39.9s NA 54.2s 12.4s
uncompressed 1m 37.4s 1.5s 52.2s 1.7s

Session and package information

package version date source
base 4.1.0 2021-05-18 local
data.table 1.14.0 2021-02-21 RSPM (R 4.1.0)
dplyr 1.0.7 2021-06-18 RSPM (R 4.1.0)
readr 1.4.0 2020-10-05 RSPM (R 4.1.0)
vroom 1.5.1 2021-06-22 local
vroom/inst/doc/benchmarks.R0000644000176200001440000001504014151250640015374 0ustar liggesusers## ---- include = FALSE--------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(ggplot2) library(forcats) library(dplyr) library(tidyr) library(fs) pretty_sec <- function(x) { x[!is.na(x)] <- prettyunits::pretty_sec(x[!is.na(x)]) x } pretty_lgl <- function(x) { case_when( x == TRUE ~ "TRUE", x == FALSE ~ "FALSE", TRUE ~ "" ) } read_benchmark <- function(file, desc) { vroom::vroom(file, col_types = c("ccccddddd")) %>% filter(op != "setup") %>% mutate( altrep = case_when( grepl("^vroom_no_altrep", reading_package) ~ FALSE, grepl("^vroom", reading_package) ~ TRUE, TRUE ~ NA ), reading_package = case_when( grepl("^vroom", reading_package) ~ "vroom", TRUE ~ reading_package ), label = fct_reorder( glue::glue("{reading_package}{altrep}\n{manip_package}", altrep = ifelse(is.na(altrep), "", glue::glue("(altrep = {altrep})")) ), case_when(type == "real" ~ time, TRUE ~ 0), sum), op = factor(op, desc) ) } generate_subtitle <- function(data) { rows <- scales::comma(data$rows[[1]]) cols <- scales::comma(data$cols[[1]]) size <- fs_bytes(data$size[[1]]) glue::glue("{rows} x {cols} - {size}B") } plot_benchmark <- function(data, title) { subtitle <- generate_subtitle(data) data <- data %>% filter(reading_package != "read.delim", type == "real") p1 <- data %>% ggplot() + geom_bar(aes(x = label, y = time, fill = op, group = label), stat = "identity") + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + coord_flip() + labs(title = title, subtitle = subtitle, x = NULL, y = NULL, fill = NULL) + theme(legend.position = "bottom") p2 <- data %>% group_by(label) %>% summarise(max_memory = max(max_memory)) %>% ggplot() + geom_bar(aes(x = label, y = max_memory / (1024 * 1024)), stat = "identity") + scale_y_continuous(labels = scales::number_format(suffix = "Mb")) + coord_flip() + labs(title = "Maximum memory usage", x = NULL, y = NULL) + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) library(patchwork) p1 + p2 + plot_layout(widths = c(2, 1)) } make_table <- function(data) { data %>% filter(type == "real") %>% select(-label, -size, -type, -rows, -cols) %>% spread(op, time) %>% mutate( total = read + print + head + tail + sample + filter + aggregate, max_memory = as.character(bench::as_bench_bytes(max_memory)) ) %>% arrange(desc(total)) %>% mutate_if(is.numeric, pretty_sec) %>% mutate_if(is.logical, pretty_lgl) %>% select(reading_package, manip_package, altrep, max_memory, everything()) %>% rename( "reading\npackage" = reading_package, "manipulating\npackage" = manip_package, memory = max_memory ) %>% knitr::kable(digits = 2, align = "r", format = "html") } desc <- c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate") ## ---- fig.height = 8, fig.width=10, warning = FALSE, echo = FALSE, message = FALSE---- taxi <- read_benchmark(path_package("vroom", "bench", "taxi.tsv"), desc) plot_benchmark(taxi, "Time to analyze taxi trip data") make_table(taxi) ## ---- fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_num <- read_benchmark(path_package("vroom", "bench", "all_numeric-long.tsv"), desc) plot_benchmark(all_num, "Time to analyze long all numeric data") make_table(all_num) ## ---- fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_num_wide <- read_benchmark(path_package("bench", "all_numeric-wide.tsv", package = "vroom"), desc) plot_benchmark(all_num_wide, "Time to analyze wide all numeric data") make_table(all_num_wide) ## ---- fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_chr <- read_benchmark(path_package("vroom", "bench", "all_character-long.tsv"), desc) plot_benchmark(all_chr, "Time to analyze long all character data") make_table(all_chr) ## ---- fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_chr_wide <- read_benchmark(path_package("vroom", "bench", "all_character-wide.tsv"), desc) plot_benchmark(all_chr_wide, "Time to analyze wide all character data") make_table(all_chr_wide) ## ---- echo = FALSE, message = FALSE, eval = TRUE------------------------------ mult <- read_benchmark(path_package("vroom", "bench", "taxi_multiple.tsv"), desc) ## ---- fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- plot_benchmark(mult, "Time to analyze multiple file data") make_table(mult) ## ---- echo = FALSE, message = FALSE, eval = TRUE------------------------------ fwf <- read_benchmark(path_package("vroom", "bench", "fwf.tsv"), desc) ## ---- fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- plot_benchmark(fwf, "Time to analyze fixed width data") make_table(fwf) ## ---- fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- taxi_writing <- read_benchmark(path_package("vroom", "bench", "taxi_writing.tsv"), c("setup", "writing")) %>% rename( package = reading_package, compression = manip_package ) %>% mutate( package = factor(package, c("base", "readr", "data.table", "vroom")), compression = factor(compression, rev(c("gzip", "multithreaded_gzip", "zstandard", "uncompressed"))) ) %>% filter(type == "real") subtitle <- generate_subtitle(taxi_writing) taxi_writing %>% ggplot(aes(x = compression, y = time, fill = package)) + geom_bar(stat = "identity", position = position_dodge2(reverse = TRUE, padding = .05)) + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + theme(legend.position = "bottom") + coord_flip() + labs(title = "Writing taxi trip data", subtitle = subtitle, x = NULL, y = NULL, fill = NULL) taxi_writing %>% select(-size, -op, -rows, -cols, -type, -altrep, -label, -max_memory) %>% mutate_if(is.numeric, pretty_sec) %>% pivot_wider(names_from = package, values_from = time) %>% arrange(desc(compression)) %>% knitr::kable(digits = 2, align = "r", format = "html") ## ---- echo = FALSE, warning = FALSE, message = FALSE-------------------------- si <- vroom::vroom(path_package("vroom", "bench", "session_info.tsv")) class(si) <- c("packages_info", "data.frame") select(as.data.frame(si), package, version = ondiskversion, date, source) %>% knitr::kable() vroom/inst/doc/vroom.R0000644000176200001440000001314714151250641014430 0ustar liggesusers## ----setup, include = FALSE--------------------------------------------------- knitr::opts_knit$set(root.dir = tempdir()) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(tibble.print_min = 3) ## ----------------------------------------------------------------------------- library(vroom) ## ----------------------------------------------------------------------------- # See where the example file is stored on your machine file <- vroom_example("mtcars.csv") file # Read the file, by default vroom will guess the delimiter automatically. vroom(file) # You can also specify it explicitly, which is (slightly) faster, and safer if # you know how the file is delimited. vroom(file, delim = ",") ## ----------------------------------------------------------------------------- mt <- tibble::rownames_to_column(mtcars, "model") purrr::iwalk( split(mt, mt$cyl), ~ vroom_write(.x, glue::glue("mtcars_{.y}.csv"), "\t") ) ## ----------------------------------------------------------------------------- files <- fs::dir_ls(glob = "mtcars*csv") files vroom(files) ## ----------------------------------------------------------------------------- vroom(files, id = "path") ## ---- include = FALSE--------------------------------------------------------- # just to clear .Last.value 1 + 1 gc() unlink(files) ## ----------------------------------------------------------------------------- file <- vroom_example("mtcars.csv.gz") vroom(file) ## ----------------------------------------------------------------------------- read_all_zip <- function(file, ...) { filenames <- unzip(file, list = TRUE)$Name vroom(purrr::map(filenames, ~ unz(file, .x)), ...) } ## ---- eval = as.logical(Sys.getenv("NOT_CRAN", "false"))---------------------- # file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv" # vroom(file) ## ---- eval = as.logical(Sys.getenv("NOT_CRAN", "false"))---------------------- # file <- "https://raw.githubusercontent.com/r-lib/vroom/main/inst/extdata/mtcars.csv.gz" # vroom(file) ## ----------------------------------------------------------------------------- file <- vroom_example("mtcars.csv.gz") vroom(file, col_select = c(model, cyl, gear)) ## ----------------------------------------------------------------------------- vroom(file, col_select = c(1, 3, 11)) ## ----------------------------------------------------------------------------- vroom(file, col_select = starts_with("d")) ## ----------------------------------------------------------------------------- vroom(file, col_select = list(car = model, everything())) ## ----------------------------------------------------------------------------- fwf_sample <- vroom_example("fwf-sample.txt") cat(readLines(fwf_sample)) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) ## ----------------------------------------------------------------------------- # read the 'hp' columns as an integer vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i")) # also skip reading the 'cyl' column vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_")) # also read the gears as a factor vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f")) ## ----------------------------------------------------------------------------- vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c")) ## ----------------------------------------------------------------------------- vroom( vroom_example("mtcars.csv"), col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor()) ) ## ----------------------------------------------------------------------------- vroom( vroom_example("mtcars.csv"), col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5")))) ) ## ---- eval = FALSE------------------------------------------------------------ # vroom( # vroom_example("mtcars.csv"), # .name_repair = ~ janitor::make_clean_names(., case = "all_caps") # ) ## ----------------------------------------------------------------------------- vroom_write(mtcars, "mtcars.tsv") ## ---- include = FALSE--------------------------------------------------------- unlink("mtcars.tsv") ## ----------------------------------------------------------------------------- vroom_write(mtcars, "mtcars.csv", delim = ",") ## ---- include = FALSE--------------------------------------------------------- unlink("mtcars.csv") ## ----------------------------------------------------------------------------- vroom_write(mtcars, "mtcars.tsv.gz") vroom_write(mtcars, "mtcars.tsv.bz2") vroom_write(mtcars, "mtcars.tsv.xz") ## ---- include = FALSE--------------------------------------------------------- unlink(c("mtcars.tsv.gz", "mtcars.tsv.bz2", "mtcars.tsv.xz")) ## ---- eval = nzchar(Sys.which("pigz"))---------------------------------------- vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz")) ## ---- include = FALSE--------------------------------------------------------- unlink("mtcars.tsv.gz") vroom/inst/COPYRIGHTS0000644000176200001440000000527114142515724014021 0ustar liggesusersThis package is provided you under the terms of the GNU General Public License version 3. Included below is license and copyright information for externally maintained libraries used by vroom. All other code in vroom is copyright RStudio, Inc. ==== mio is licensed for use as follows MIT License Copyright (c) 2018 https://github.com/mandreyel/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== grisu3 is licensed for use as follows /* Copyright Jukka JylΓ€nki Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Modifcations to dtoa_grisu3() referenced mikkelfj: are under the following * Copyright (c) 2016 Mikkel F. JΓΈrgensen, dvide.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 */ ==== vroom/inst/extdata/0000755000176200001440000000000014142512075014024 5ustar liggesusersvroom/inst/extdata/mtcars.csv0000644000176200001440000000324414116714026016036 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Mazda RX4,21,6,160,110,3.9,2.62,16.46,0,1,4,4 Mazda RX4 Wag,21,6,160,110,3.9,2.875,17.02,0,1,4,4 Datsun 710,22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 Hornet 4 Drive,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 Hornet Sportabout,18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 Valiant,18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 Duster 360,14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 Merc 240D,24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 Merc 230,22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 Merc 280,19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 Merc 280C,17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 Merc 450SE,16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 Merc 450SL,17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 Merc 450SLC,15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 Cadillac Fleetwood,10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 Lincoln Continental,10.4,8,460,215,3,5.424,17.82,0,0,3,4 Chrysler Imperial,14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 Fiat 128,32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 Honda Civic,30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 Toyota Corolla,33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 Toyota Corona,21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 Dodge Challenger,15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 AMC Javelin,15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 Camaro Z28,13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 Pontiac Firebird,19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 Fiat X1-9,27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 Porsche 914-2,26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 Lotus Europa,30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 Ford Pantera L,15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 Ferrari Dino,19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 Maserati Bora,15,8,301,335,3.54,3.57,14.6,0,1,5,8 Volvo 142E,21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 vroom/inst/extdata/mtcars-4.csv0000644000176200001440000000113214142512075016170 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Datsun 710,22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 Merc 240D,24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 Merc 230,22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 Fiat 128,32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 Honda Civic,30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 Toyota Corolla,33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 Toyota Corona,21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 Fiat X1-9,27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 Porsche 914-2,26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 Lotus Europa,30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 Volvo 142E,21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 vroom/inst/extdata/mtcars.csv.zip0000644000176200001440000000176313746062417016653 0ustar liggesusersPKΒ…RN΄lšI€ mtcars.csvUT œ'k\W@t\ux υu”έnΫ8…ού|–D鲕t)΄E·θcŽYτR²‹μΣοκΗΩmz£Δ?ΝΜ™3sŠmθωι|δ‡—ž·έxζΟgή&?ρŸ{ ~Ή?ρcπ‰|zΪ}ς΄ž}ώnΈ’Όδ²,Έ”Χ’ζJ” Β”‡άps»ΝώςΗ·ˆΚY.(Τ†μύ4^ζpEα=ΞdQρZ¨,•(%—»1 ab†νSw " Β([-a@k‘$βΤΒ@$}ΓΎœcšόSΌLτYΗq™DZZHz²$H Ϊ}σ}η‡|κQ _.(3WuœB©-Ξώ2N!±όU#τ@›³ΒΓ:.­¨Μ’…t`Κ{άB-ˆoJdq΅(kΚ Κ9Ί°\Χ7ΉLΏ5}ΏVsxSο~U*7Δ <•^ͺΈB¦3`6 !!ͺίυ c‹/wΩ¨Y9”ˆKΉ!ŽzΰkkΡϊ†<ΠΉώΡΒizUΎ4€ z“‘_ΡψΆλ{`χ}ΣΟ[΄.ηgœ‚¦ΤE8 ίΚ¬«­'έpˆύΐš8Lέ†ΙχJν$«€3*»₯RΨ<§—±‡ώ8Cκ3ΩfΖΣ”¨"R›άΘϋΞOL*ψEe8²gY’v(ε’Ÿέ«10jMwν\3aAXEǏ₯6Y΅0jχ5ΎΔ PL²pM3 L’±-0²±¨tz‹τŠ<Ν›₯cΈ^ς:kŽμ xœς6±=Φ<ϋΎΓ1$j™₯i@€-Φω±y…Tn·χŸφ§Ώ†ΎΦ.λΒddPmgΟ¬HγO>EφΪI=œ]GΞεEBΌ0rΣϊ‘ΪJΎθRxκR;ΟšT¬» o‘jn|²†ΚMϊ.ίa")ˆWo=’έƒ1KχΣxx¬–ζ W.Βi^γ=  .οChΈ{ˆΣedw—Ο~mlm‘΅”TŠƒ„ΕΏ€ζ8έΗΤ²Gl©<{Θϋ%« Σ̍…xŽΌh—H0\HΙ§Žν»!’ŽF=WL`ύ¨ΝnH‰ύ>"ΒΤ±1yΌΚΝΑRΣDX³l7#Κ…¨vίbLu7ojY0ž’BTyΏoύPKΒ…RN΄lšI€ €mtcars.csvUTœ'k\ux υPKPvroom/inst/extdata/fwf-sample.txt0000644000176200001440000000020114142512075016617 0ustar liggesusersJohn Smith WA 418-Y11-4111 Mary Hartford CA 319-Z19-4341 Evan Nolan IL 219-532-c301 vroom/inst/extdata/mtcars-8.csv0000644000176200001440000000144014142512075016176 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Hornet Sportabout,18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 Duster 360,14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 Merc 450SE,16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 Merc 450SL,17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 Merc 450SLC,15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 Cadillac Fleetwood,10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 Lincoln Continental,10.4,8,460,215,3,5.424,17.82,0,0,3,4 Chrysler Imperial,14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 Dodge Challenger,15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 AMC Javelin,15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 Camaro Z28,13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 Pontiac Firebird,19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 Ford Pantera L,15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 Maserati Bora,15,8,301,335,3.54,3.57,14.6,0,1,5,8 vroom/inst/extdata/mtcars-6.csv0000644000176200001440000000062014142512075016173 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Mazda RX4,21,6,160,110,3.9,2.62,16.46,0,1,4,4 Mazda RX4 Wag,21,6,160,110,3.9,2.875,17.02,0,1,4,4 Hornet 4 Drive,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 Valiant,18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 Merc 280,19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 Merc 280C,17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 Ferrari Dino,19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 vroom/inst/extdata/mtcars.csv.bz20000644000176200001440000000151013746062417016534 0ustar liggesusersBZh91AY&SY=sBί€@ΰ?v]Π>η°Pš‡’SΤ5§ιO&“Θ#F™4 ΝC24Θ&‘SΪτ©=@ hhhΘh‘ M@@υ@i±g# „%ͺχ:$H2δμ=::oοΉξr?ar1G]zκ­¬@‰$V€5v°@Le’n¬' @&«>kg!;j"42vίΤυΗή΄^τxjοk΅:Λμœ‘ίHˆ†ž/\–XŒ2'§%›εNўώϊ΄k΅μ!g]w6ΝLΝdI)S@ΐ[­FΑe6!Ώ€m€ ΐUsrφX3©1σ…n³΅½9.γUcθΎ%Bπ9ωxσιΠΏΞxξgάΔΏj©ίL βd(»!βqSs2_ŠF±’ΤmβlœΠ.ŒH³‹z"$Σk+/-t,fh\^oλ,Z\Ύ9Œγ‰¬IijuΒI0σ–75A—‹†—B[ήςu`tΡ&—³ΉT΅ΆƒΟŠšά"ΩΞν²R^D0ΌxtχάχφχŒ¬ΠtΆΕ‘ΆΌ6έBi„β{pΛ¨Kω՚₯m>oΊ jfΆR532 ΪΓT4SO5θ՜0OΛ.ƒ£Λšε–βhτ8ž1<ˆO"&”±OU-¨P_Ž6εδ·‡9³fA“>7ΞA“PHΫ4 cΕVΧ1Β Ρ[1[ !ΰfͺ•Λ­ΧpϊΎ‘@¬8aΠΧ=σ£«WŠ… xγWF+ΔΖ­„Œ.Ρm[NRQεάλV,ξΒΣϋ§υσέΨDCoψξe1ƒH q ΰ„=Δ`ψΓPΨ`½1ΕόOŽJΉ}ΫΛ— >wΐ ε’„nfη‚G‡„™„m*!½h±pM‡Θ”+.sΤψ±n” t†βξH§ `θ@vroom/inst/extdata/mtcars.csv.gz0000644000176200001440000000154613746062417016470 0ustar liggesusers‹ ‘i\mtcars.csvu”έnΫ8…ού|–D鲕t)΄E·θcŽYτR²‹μΣοκΗΩmz£Δ?ΝΜ™3sŠmθωι|δ‡—ž·έxζΟgή&?ρŸ{ ~Ή?ρcπ‰|zΪ}ς΄ž}ώnΈ’Όδ²,Έ”Χ’ζJ” Β”‡άps»ΝώςΗ·ˆΚY.(Τ†μύ4^ζpEα=ΞdQρZ¨,•(%—»1 ab†νSw " Β([-a@k‘$βΤΒ@$}ΓΎœcšόSΌLτYΗq™DZZHz²$H Ϊ}σ}η‡|κQ _.(3WuœB©-Ξώ2N!±όU#τ@›³ΒΓ:.­¨Μ’…t`Κ{άB-ˆoJdq΅(kΚ Κ9Ί°\Χ7ΉLΏ5}ΏVsxSο~U*7Δ <•^ͺΈB¦3`6 !!ͺίυ c‹/wΩ¨Y9”ˆKΉ!ŽzΰkkΡϊ†<ΠΉώΡΒizUΎ4€ z“‘_ΡψΆλ{`χ}ΣΟ[΄.ηgœ‚¦ΤE8 ίΚ¬«­'έpˆύΐš8Lέ†ΙχJν$«€3*»₯RΨ<§—±‡ώ8Cκ3ΩfΖΣ”¨"R›άΘϋΞOL*ψEe8²gY’v(ε’Ÿέ«10jMwν\3aAXEǏ₯6Y΅0jχ5ΎΔ PL²pM3 L’±-0²±¨tz‹τŠ<Ν›₯cΈ^ς:kŽμ xœς6±=Φ<ϋΎΓ1$j™₯i@€-Φω±y…Tn·χŸφ§Ώ†ΎΦ.λΒddPmgΟ¬HγO>EφΪI=œ]GΞεEBΌ0rΣϊ‘ΪJΎθRxκR;ΟšT¬» o‘jn|²†ΚMϊ.ίa")ˆWo=’έƒ1KχΣxx¬–ζ W.Βi^γ=  .οChΈ{ˆΣedw—Ο~mlm‘΅”TŠƒ„ΕΏ€ζ8έΗΤ²Gl©<{Θϋ%« Σ̍…xŽΌh—H0\HΙ§Žν»!’ŽF=WL`ύ¨ΝnH‰ύ>"ΒΤ±1yΌΚΝΑRΣDX³l7#Κ…¨vίbLu7ojY0ž’BTyΏoύ΄lš€vroom/inst/extdata/mtcars.csv.xz0000644000176200001440000000156413746062417016511 0ustar liggesusersύ7zXZζΦ΄F!t/ε£ΰ£1]6›Θ°WΡTξŠ‘hpD'©φΆq εͺρ(3=ƒy»κd‘OI­Ϋn„¬I£Œ&¬l-δ;ύ5εaύoά"6GY Ζ€g£(<1kλΟqΩ€)€ΎIΒλΥ¦δV J8$(`·ƒ ξ‹Nα]rΥ5•NŒΒa9^W ‚Ό>ψ―fCΡ0ωγΙΩY₯½dΦβY°λžϋΘ^VΑ6ωvιU5Όμ°ƒSeOΩ\4²Ε$=1εeθ^CμΉ0΅ZΥ‹"BJBεŒW Cx8ήάιτΧn@y¨ο3bΒ‘tΤ1 ςu+EΝ ,Γ>β’ΕήρŸ1C^X!„Π#N{2'ηA.ΏΙαBΏςD Ο–Ÿ^ %NώράGͺ…sc[5’)>ƒρ -<†€t^’δ–pλ.uώηΑz)—²qΧsή$NsημY§Wψκ2ύ}‘@¬OpZπ‘*ΆsώWfxž'C6ΓCJO€š^‰8Sx±oΖπX£‘o+b%yI[λF"φˆ9ειœ‰»— ŽξΖ”π9P:gŽ,―T{AάXχ΅Qϊ‡('ΆΚe ~φ—χGΣ4ΞN=€ΟϊΏ‰AžZdWςFδΫΘΖHψΣλψ0ύ›ππΙβ\/ˆΰKΥΚ.-4΅€#ɜ]ΣΏϊΌI)v ν©8WNHX ½ 8£κužΦHUΥ.;#$–χ¨»?ωΒρ%UζΕMwώxΏYΖVaBΛBƒŒ@ηΥod‡gέ “ψΆΝ€ 1²+±ΔgϋYZvroom/inst/bench/0000755000176200001440000000000014151250642013450 5ustar liggesusersvroom/inst/bench/run-bench.R0000755000176200001440000000103214142512075015454 0ustar liggesusers#!/usr/bin/env Rscript args <- commandArgs(trailingOnly = TRUE) source_file <- args[[1]] out_file <- args[[2]] file <- args[-c(1:2)] cat(source_file, "\n") out <- bench::workout_expressions(as.list(parse(source_file, keep.source = FALSE))) x <- vroom::vroom(file, col_types = list()) out$size <- sum(file.size(file)) out$rows <- nrow(x) out$cols <- ncol(x) out$process <- as.numeric(out$process) out$real <- as.numeric(out$real) out$max_memory <- as.numeric(bench::bench_process_memory()[["max"]]) vroom::vroom_write(out, out_file) vroom/inst/bench/all_character-long.tsv0000644000176200001440000001723514151250016017732 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.05641532699999985 400248755 5080014848 1000000 25 data.table data.table setup real 0.05641937255859375 400248755 5080014848 1000000 25 data.table data.table read process 45.553360995 400248755 5080014848 1000000 25 data.table data.table read real 42.82739567756653 400248755 5080014848 1000000 25 data.table data.table print process 0.01535388299999596 400248755 5080014848 1000000 25 data.table data.table print real 0.015356302261352539 400248755 5080014848 1000000 25 data.table data.table head process 2.4213100000025634e-4 400248755 5080014848 1000000 25 data.table data.table head real 2.4390220642089844e-4 400248755 5080014848 1000000 25 data.table data.table tail process 2.0244400000279938e-4 400248755 5080014848 1000000 25 data.table data.table tail real 0.00020360946655273438 400248755 5080014848 1000000 25 data.table data.table sample process 0.003910263000001635 400248755 5080014848 1000000 25 data.table data.table sample real 0.003912210464477539 400248755 5080014848 1000000 25 data.table data.table filter process 0.06275645499999882 400248755 5080014848 1000000 25 data.table data.table filter real 0.01575303077697754 400248755 5080014848 1000000 25 data.table data.table aggregate process 0.19549771300000174 400248755 5080014848 1000000 25 data.table data.table aggregate real 0.14883995056152344 400248755 5080014848 1000000 25 read.delim base setup process 4.0029999999191546e-6 400248755 4863918080 1000000 25 read.delim base setup real 6.198883056640625e-6 400248755 4863918080 1000000 25 read.delim base read process 103.065713264 400248755 4863918080 1000000 25 read.delim base read real 103.06781911849976 400248755 4863918080 1000000 25 read.delim base print process 0.007192154999998479 400248755 4863918080 1000000 25 read.delim base print real 0.007193565368652344 400248755 4863918080 1000000 25 read.delim base head process 2.5602699999183187e-4 400248755 4863918080 1000000 25 read.delim base head real 2.5725364685058594e-4 400248755 4863918080 1000000 25 read.delim base tail process 4.7244900000009693e-4 400248755 4863918080 1000000 25 read.delim base tail real 4.737377166748047e-4 400248755 4863918080 1000000 25 read.delim base sample process 0.0015474770000025728 400248755 4863918080 1000000 25 read.delim base sample real 0.0015492439270019531 400248755 4863918080 1000000 25 read.delim base filter process 0.027589560000009783 400248755 4863918080 1000000 25 read.delim base filter real 0.027591228485107422 400248755 4863918080 1000000 25 read.delim base aggregate process 0.29284924599998874 400248755 4863918080 1000000 25 read.delim base aggregate real 0.29285216331481934 400248755 4863918080 1000000 25 readr dplyr setup process 0.27801725900000007 400248755 4671979520 1000000 25 readr dplyr setup real 0.27802062034606934 400248755 4671979520 1000000 25 readr dplyr read process 62.577469189 400248755 4671979520 1000000 25 readr dplyr read real 62.581751346588135 400248755 4671979520 1000000 25 readr dplyr print process 0.07374980299999834 400248755 4671979520 1000000 25 readr dplyr print real 0.10151100158691406 400248755 4671979520 1000000 25 readr dplyr head process 0.0011762729999986732 400248755 4671979520 1000000 25 readr dplyr head real 0.0011777877807617188 400248755 4671979520 1000000 25 readr dplyr tail process 5.460859999999457e-4 400248755 4671979520 1000000 25 readr dplyr tail real 5.471706390380859e-4 400248755 4671979520 1000000 25 readr dplyr sample process 0.016387897000001317 400248755 4671979520 1000000 25 readr dplyr sample real 0.01638960838317871 400248755 4671979520 1000000 25 readr dplyr filter process 0.019892996999999468 400248755 4671979520 1000000 25 readr dplyr filter real 0.019894838333129883 400248755 4671979520 1000000 25 readr dplyr aggregate process 0.21462222699999245 400248755 4671979520 1000000 25 readr dplyr aggregate real 0.21462440490722656 400248755 4671979520 1000000 25 vroom base setup process 0.23405636800000007 400248755 3461603328 1000000 25 vroom base setup real 0.23406624794006348 400248755 3461603328 1000000 25 vroom base read process 3.9978824160000004 400248755 3461603328 1000000 25 vroom base read real 0.5949602127075195 400248755 3461603328 1000000 25 vroom base print process 0.045972403000000384 400248755 3461603328 1000000 25 vroom base print real 0.0459742546081543 400248755 3461603328 1000000 25 vroom base head process 9.583050000001592e-4 400248755 3461603328 1000000 25 vroom base head real 9.601116180419922e-4 400248755 3461603328 1000000 25 vroom base tail process 5.036210000000096e-4 400248755 3461603328 1000000 25 vroom base tail real 5.049705505371094e-4 400248755 3461603328 1000000 25 vroom base sample process 0.002841930999999853 400248755 3461603328 1000000 25 vroom base sample real 0.0028433799743652344 400248755 3461603328 1000000 25 vroom base filter process 0.16209093400000008 400248755 3461603328 1000000 25 vroom base filter real 0.1620922088623047 400248755 3461603328 1000000 25 vroom base aggregate process 2.1265156109999994 400248755 3461603328 1000000 25 vroom base aggregate real 2.1265347003936768 400248755 3461603328 1000000 25 vroom dplyr setup process 0.3563563940000001 400248755 3446657024 1000000 25 vroom dplyr setup real 0.35636043548583984 400248755 3446657024 1000000 25 vroom dplyr read process 3.6933613389999995 400248755 3446657024 1000000 25 vroom dplyr read real 0.6395699977874756 400248755 3446657024 1000000 25 vroom dplyr print process 0.057654061000000034 400248755 3446657024 1000000 25 vroom dplyr print real 0.057656049728393555 400248755 3446657024 1000000 25 vroom dplyr head process 0.0010614000000002122 400248755 3446657024 1000000 25 vroom dplyr head real 0.0010628700256347656 400248755 3446657024 1000000 25 vroom dplyr tail process 5.792140000000501e-4 400248755 3446657024 1000000 25 vroom dplyr tail real 5.805492401123047e-4 400248755 3446657024 1000000 25 vroom dplyr sample process 0.015052698999999947 400248755 3446657024 1000000 25 vroom dplyr sample real 0.015054941177368164 400248755 3446657024 1000000 25 vroom dplyr filter process 0.1843323209999994 400248755 3446657024 1000000 25 vroom dplyr filter real 0.18434882164001465 400248755 3446657024 1000000 25 vroom dplyr aggregate process 1.2399041610000001 400248755 3446657024 1000000 25 vroom dplyr aggregate real 1.2399187088012695 400248755 3446657024 1000000 25 vroom_no_altrep dplyr setup process 0.33683956000000004 400248755 4617994240 1000000 25 vroom_no_altrep dplyr setup real 0.33684349060058594 400248755 4617994240 1000000 25 vroom_no_altrep dplyr read process 53.144246626 400248755 4617994240 1000000 25 vroom_no_altrep dplyr read real 50.454383850097656 400248755 4617994240 1000000 25 vroom_no_altrep dplyr print process 0.04912083899999686 400248755 4617994240 1000000 25 vroom_no_altrep dplyr print real 0.04912281036376953 400248755 4617994240 1000000 25 vroom_no_altrep dplyr head process 0.0010306440000036332 400248755 4617994240 1000000 25 vroom_no_altrep dplyr head real 0.001032114028930664 400248755 4617994240 1000000 25 vroom_no_altrep dplyr tail process 5.248319999964224e-4 400248755 4617994240 1000000 25 vroom_no_altrep dplyr tail real 5.261898040771484e-4 400248755 4617994240 1000000 25 vroom_no_altrep dplyr sample process 0.015900661999999954 400248755 4617994240 1000000 25 vroom_no_altrep dplyr sample real 0.01590275764465332 400248755 4617994240 1000000 25 vroom_no_altrep dplyr filter process 0.020508452000001398 400248755 4617994240 1000000 25 vroom_no_altrep dplyr filter real 0.02051067352294922 400248755 4617994240 1000000 25 vroom_no_altrep dplyr aggregate process 0.14906932099999892 400248755 4617994240 1000000 25 vroom_no_altrep dplyr aggregate real 0.14907217025756836 400248755 4617994240 1000000 25 vroom/inst/bench/GNUmakefile0000644000176200001440000000235714142512075015532 0ustar liggesusersMAKEFLAGS += --no-builtin-rules BENCH_LONG_ROWS := 10000 BENCH_LONG_COLS := 25 BENCH_WIDE_ROWS := 1000 BENCH_WIDE_COLS := 100 BENCH_INPUTS := all_numeric-long/input.tsv all_numeric-wide/input.tsv all_character-long/input.tsv all_character-wide/input.tsv TAXI_INPUTS := $(wildcard ~/data/small_trip_fare_*.csv) FWF_INPUT := ~/data/small_PUMS5_06.TXT BENCH_SRC := $(wildcard */*.R) BENCH_MARKS := $(BENCH_SRC:.R=.tsv) BENCH_OUT := $(patsubst %/,%.tsv, $(wildcard */)) all: $(BENCH_OUT) session_info.tsv $(BENCH_OUT) : $(BENCH_MARKS) Rscript summarise-benchmarks.R session_info.tsv: session_info.R ./$< %-long/input.tsv: %-long/input.R Rscript $< $(BENCH_LONG_ROWS) $(BENCH_LONG_COLS) $@ %-wide/input.tsv: %-wide/input.R Rscript $< $(BENCH_WIDE_ROWS) $(BENCH_WIDE_COLS) $@ taxi/%.tsv : taxi/%.R $(TAXI_INPUTS) run-bench.R $< $@ $(word 1, $(TAXI_INPUTS)) taxi_multiple/%.tsv : taxi_multiple/%.R $(TAXI_INPUTS) run-bench.R $< $@ $(TAXI_INPUTS) taxi_writing/%.tsv : taxi_writing/%.R $(TAXI_INPUTS) run-bench.R $< $@ $(word 1, $(TAXI_INPUTS)) fwf/%.tsv : fwf/%.R $(FWF_INPUT) run-bench-fwf.R $< $@ $(FWF_INPUT) %.tsv : %.R $(BENCH_INPUTS) run-bench.R $< $@ $(@D)/input.tsv clean: rm -f $(BENCH_INPUTS) $(BENCH_MARKS) $(BENCH_OUT) session_info.tsv vroom/inst/bench/taxi_writing/0000755000176200001440000000000014151431426016161 5ustar liggesusersvroom/inst/bench/taxi_writing/data.table-uncompressed.R0000644000176200001440000000030514142512075023006 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } data.table::fwrite(data, tempfile(fileext = ".tsv"), sep = "\t") vroom/inst/bench/taxi_writing/readr-multithreaded_gzip.R0000644000176200001440000000032214142512075023260 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, pipe(sprintf("pigz > %s", tempfile(fileext = ".gz")))) vroom/inst/bench/taxi_writing/base-zstandard.R0000644000176200001440000000044214142512075021206 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } { con <- pipe(sprintf("zstd > %s", tempfile(fileext = ".zst")), "wb") write.table(data, con, sep = "\t", quote = FALSE, row.names = FALSE) close(con) } vroom/inst/bench/taxi_writing/data.table-multithreaded_gzip.R0000644000176200001440000000030414142512075024162 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } data.table::fwrite(data, tempfile(fileext = ".gz"), sep = "\t") vroom/inst/bench/taxi_writing/readr-zstandard.R0000644000176200001440000000032314142512075021367 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, pipe(sprintf("zstd > %s", tempfile(fileext = ".zst")))) vroom/inst/bench/taxi_writing/readr-uncompressed.R0000644000176200001440000000026714142512075022113 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, tempfile(fileext = ".tsv")) vroom/inst/bench/taxi_writing/vroom-gzip.R0000644000176200001440000000027714142512075020423 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, tempfile(fileext = ".gz"), delim = "\t") vroom/inst/bench/taxi_writing/vroom-multithreaded_gzip.R0000644000176200001440000000033314142512075023327 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, pipe(sprintf("pigz > %s", tempfile(fileext = ".gz"))), delim = "\t") vroom/inst/bench/taxi_writing/vroom-uncompressed.R0000644000176200001440000000030014142512075022144 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, tempfile(fileext = ".tsv"), delim = "\t") vroom/inst/bench/taxi_writing/vroom-zstandard.R0000644000176200001440000000033414142512075021436 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, pipe(sprintf("zstd > %s", tempfile(fileext = ".zst"))), delim = "\t") vroom/inst/bench/taxi_writing/base-gzip.R0000644000176200001440000000041514142512075020165 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } { con <- gzfile(tempfile(fileext = ".gz"), "wb") write.table(data, con, sep = "\t", quote = FALSE, row.names = FALSE) close(con) } vroom/inst/bench/taxi_writing/base-uncompressed.R0000644000176200001440000000034014142512075021720 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } write.table(data, tempfile(fileext = ".tsv"), sep = "\t", quote = FALSE, row.names = FALSE) vroom/inst/bench/taxi_writing/readr-gzip.R0000644000176200001440000000026614142512075020354 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, tempfile(fileext = ".gz")) vroom/inst/bench/taxi_writing/base-multithreaded_gzip.R0000644000176200001440000000044114142512075023077 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } { con <- pipe(sprintf("pigz > %s", tempfile(fileext = ".gz")), "wb") write.table(data, con, sep = "\t", quote = FALSE, row.names = FALSE) close(con) } vroom/inst/bench/taxi_writing/data.table-gzip.R0000644000176200001440000000032114142512075021246 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } data.table::fwrite(data, tempfile(fileext = ".gz"), sep = "\t", nThread = 1) vroom/inst/bench/taxi_writing.tsv0000644000176200001440000001172114151244612016720 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols base gzip setup process 56.307022157 1641999923 6515875840 14388451 11 base gzip setup real 16.904335260391235 1641999923 6515875840 14388451 11 base gzip writing process 197.11310746799998 1641999923 6515875840 14388451 11 base gzip writing real 197.12195253372192 1641999923 6515875840 14388451 11 base multithreaded_gzip setup process 49.800825008 1641999923 6515879936 14388451 11 base multithreaded_gzip setup real 15.864639043807983 1641999923 6515879936 14388451 11 base multithreaded_gzip writing process 97.815085082 1641999923 6515879936 14388451 11 base multithreaded_gzip writing real 97.83104109764099 1641999923 6515879936 14388451 11 base uncompressed setup process 48.850021397 1641999923 6515879936 14388451 11 base uncompressed setup real 16.385770320892334 1641999923 6515879936 14388451 11 base uncompressed writing process 97.42563251000001 1641999923 6515879936 14388451 11 base uncompressed writing real 97.43097257614136 1641999923 6515879936 14388451 11 base zstandard setup process 57.130642056999996 1641999923 6515888128 14388451 11 base zstandard setup real 18.90769624710083 1641999923 6515888128 14388451 11 base zstandard writing process 99.85411830200002 1641999923 6515888128 14388451 11 base zstandard writing real 99.8676381111145 1641999923 6515888128 14388451 11 data.table gzip setup process 57.120600576 1641999923 6518173696 14388451 11 data.table gzip setup real 19.275273323059082 1641999923 6518173696 14388451 11 data.table gzip writing process 67.75096147299999 1641999923 6518173696 14388451 11 data.table gzip writing real 67.75405859947205 1641999923 6518173696 14388451 11 data.table multithreaded_gzip setup process 52.228046424 1641999923 7046705152 14388451 11 data.table multithreaded_gzip setup real 18.820579051971436 1641999923 7046705152 14388451 11 data.table multithreaded_gzip writing process 67.654484228 1641999923 7046705152 14388451 11 data.table multithreaded_gzip writing real 8.864737272262573 1641999923 7046705152 14388451 11 data.table uncompressed setup process 51.447035740000004 1641999923 6576988160 14388451 11 data.table uncompressed setup real 16.117270708084106 1641999923 6576988160 14388451 11 data.table uncompressed writing process 9.219789825999996 1641999923 6576988160 14388451 11 data.table uncompressed writing real 1.4708619117736816 1641999923 6576988160 14388451 11 readr gzip setup process 52.427885992 1641999923 6816428032 14388451 11 readr gzip setup real 16.014821529388428 1641999923 6816428032 14388451 11 readr gzip writing process 120.15885149100001 1641999923 6816428032 14388451 11 readr gzip writing real 120.1623752117157 1641999923 6816428032 14388451 11 readr multithreaded_gzip setup process 52.571174903999996 1641999923 6816079872 14388451 11 readr multithreaded_gzip setup real 16.05108642578125 1641999923 6816079872 14388451 11 readr multithreaded_gzip writing process 53.408188828 1641999923 6816079872 14388451 11 readr multithreaded_gzip writing real 53.427462577819824 1641999923 6816079872 14388451 11 readr uncompressed setup process 50.408979955 1641999923 6815977472 14388451 11 readr uncompressed setup real 16.516019582748413 1641999923 6815977472 14388451 11 readr uncompressed writing process 52.224211567000005 1641999923 6815977472 14388451 11 readr uncompressed writing real 52.229180574417114 1641999923 6815977472 14388451 11 readr zstandard setup process 53.053004752 1641999923 6815940608 14388451 11 readr zstandard setup real 17.89621663093567 1641999923 6815940608 14388451 11 readr zstandard writing process 54.141594968999996 1641999923 6815940608 14388451 11 readr zstandard writing real 54.15725231170654 1641999923 6815940608 14388451 11 vroom gzip setup process 58.208655639999996 1641999923 6657384448 14388451 11 vroom gzip setup real 18.150486946105957 1641999923 6657384448 14388451 11 vroom gzip writing process 93.763101656999993 1641999923 6657384448 14388451 11 vroom gzip writing real 74.44156193733215 1641999923 6657384448 14388451 11 vroom multithreaded_gzip setup process 52.529673003999996 1641999923 6656368640 14388451 11 vroom multithreaded_gzip setup real 17.085912942886353 1641999923 6656368640 14388451 11 vroom multithreaded_gzip writing process 23.073533718 1641999923 6656368640 14388451 11 vroom multithreaded_gzip writing real 8.076940298080444 1641999923 6656368640 14388451 11 vroom uncompressed setup process 52.838888093 1641999923 7582793728 14388451 11 vroom uncompressed setup real 18.450870513916016 1641999923 7582793728 14388451 11 vroom uncompressed writing process 22.587353183000005 1641999923 7582793728 14388451 11 vroom uncompressed writing real 1.6948139667510986 1641999923 7582793728 14388451 11 vroom zstandard setup process 51.565757179 1641999923 6656360448 14388451 11 vroom zstandard setup real 16.19530463218689 1641999923 6656360448 14388451 11 vroom zstandard writing process 22.155763127999997 1641999923 6656360448 14388451 11 vroom zstandard writing real 12.370139837265015 1641999923 6656360448 14388451 11 vroom/inst/bench/all_numeric-wide.tsv0000644000176200001440000002234114151250016017423 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.051875126999999965 1963219523 5880053760 100000 1000 data.table data.table setup real 0.051879167556762695 1963219523 5880053760 100000 1000 data.table data.table read process 5.577086213 1963219523 5880053760 100000 1000 data.table data.table read real 1.2563517093658447 1963219523 5880053760 100000 1000 data.table data.table print process 0.09918665400000037 1963219523 5880053760 100000 1000 data.table data.table print real 0.09919476509094238 1963219523 5880053760 100000 1000 data.table data.table head process 4.1016100000046407e-4 1963219523 5880053760 100000 1000 data.table data.table head real 4.124641418457031e-4 1963219523 5880053760 100000 1000 data.table data.table tail process 4.3974700000060096e-4 1963219523 5880053760 100000 1000 data.table data.table tail real 4.417896270751953e-4 1963219523 5880053760 100000 1000 data.table data.table sample process 0.002832650000000214 1963219523 5880053760 100000 1000 data.table data.table sample real 0.002834796905517578 1963219523 5880053760 100000 1000 data.table data.table filter process 0.003388150000000145 1963219523 5880053760 100000 1000 data.table data.table filter real 0.003390073776245117 1963219523 5880053760 100000 1000 data.table data.table aggregate process 0.014421183999999698 1963219523 5880053760 100000 1000 data.table data.table aggregate real 0.003484010696411133 1963219523 5880053760 100000 1000 read.delim base setup process 3.0330000000411417e-6 1963219523 15470862336 100000 1000 read.delim base setup real 4.76837158203125e-6 1963219523 15470862336 100000 1000 read.delim base read process 520.970175058 1963219523 15470862336 100000 1000 read.delim base read real 520.9826147556305 1963219523 15470862336 100000 1000 read.delim base print process 0.1300029069999482 1963219523 15470862336 100000 1000 read.delim base print real 0.13001203536987305 1963219523 15470862336 100000 1000 read.delim base head process 0.006249463000017386 1963219523 15470862336 100000 1000 read.delim base head real 0.006251096725463867 1963219523 15470862336 100000 1000 read.delim base tail process 0.006446566000022358 1963219523 15470862336 100000 1000 read.delim base tail real 0.006448507308959961 1963219523 15470862336 100000 1000 read.delim base sample process 0.008708142000045882 1963219523 15470862336 100000 1000 read.delim base sample real 0.008709907531738281 1963219523 15470862336 100000 1000 read.delim base filter process 0.07440451399997983 1963219523 15470862336 100000 1000 read.delim base filter real 0.07440614700317383 1963219523 15470862336 100000 1000 read.delim base aggregate process 0.00439676700000291 1963219523 15470862336 100000 1000 read.delim base aggregate real 0.0043985843658447266 1963219523 15470862336 100000 1000 readr dplyr setup process 0.26801563699999975 1963219523 5863723008 100000 1000 readr dplyr setup real 0.2680184841156006 1963219523 5863723008 100000 1000 readr dplyr read process 56.104649359 1963219523 5863723008 100000 1000 readr dplyr read real 56.11158347129822 1963219523 5863723008 100000 1000 readr dplyr print process 0.0787199290000018 1963219523 5863723008 100000 1000 readr dplyr print real 0.0954742431640625 1963219523 5863723008 100000 1000 readr dplyr head process 0.0027141720000045666 1963219523 5863723008 100000 1000 readr dplyr head real 0.0027158260345458984 1963219523 5863723008 100000 1000 readr dplyr tail process 0.0022157200000023636 1963219523 5863723008 100000 1000 readr dplyr tail real 0.002217531204223633 1963219523 5863723008 100000 1000 readr dplyr sample process 0.02533303300000256 1963219523 5863723008 100000 1000 readr dplyr sample real 0.025334835052490234 1963219523 5863723008 100000 1000 readr dplyr filter process 0.017108968999998808 1963219523 5863723008 100000 1000 readr dplyr filter real 0.017110586166381836 1963219523 5863723008 100000 1000 readr dplyr aggregate process 0.038782436000005305 1963219523 5863723008 100000 1000 readr dplyr aggregate real 0.03878426551818848 1963219523 5863723008 100000 1000 vroom base setup process 0.22703830000000025 1963219523 7791280128 100000 1000 vroom base setup real 0.2270522117614746 1963219523 7791280128 100000 1000 vroom base read process 16.174510832 1963219523 7791280128 100000 1000 vroom base read real 2.998760223388672 1963219523 7791280128 100000 1000 vroom base print process 0.07004265899999851 1963219523 7791280128 100000 1000 vroom base print real 0.06718945503234863 1963219523 7791280128 100000 1000 vroom base head process 0.003972434999997887 1963219523 7791280128 100000 1000 vroom base head real 0.003973960876464844 1963219523 7791280128 100000 1000 vroom base tail process 0.00348148499999823 1963219523 7791280128 100000 1000 vroom base tail real 0.0034830570220947266 1963219523 7791280128 100000 1000 vroom base sample process 0.00464282200000099 1963219523 7791280128 100000 1000 vroom base sample real 0.0046443939208984375 1963219523 7791280128 100000 1000 vroom base filter process 0.049587988999999055 1963219523 7791280128 100000 1000 vroom base filter real 0.010494709014892578 1963219523 7791280128 100000 1000 vroom base aggregate process 0.041909286999999296 1963219523 7791280128 100000 1000 vroom base aggregate real 0.04191112518310547 1963219523 7791280128 100000 1000 vroom dplyr setup process 0.2996260059999998 1963219523 7792242688 100000 1000 vroom dplyr setup real 0.29964709281921387 1963219523 7792242688 100000 1000 vroom dplyr read process 16.673107875 1963219523 7792242688 100000 1000 vroom dplyr read real 2.9666857719421387 1963219523 7792242688 100000 1000 vroom dplyr print process 0.07086207600000094 1963219523 7792242688 100000 1000 vroom dplyr print real 0.06784224510192871 1963219523 7792242688 100000 1000 vroom dplyr head process 0.003975565000001069 1963219523 7792242688 100000 1000 vroom dplyr head real 0.0039768218994140625 1963219523 7792242688 100000 1000 vroom dplyr tail process 0.013946671999999438 1963219523 7792242688 100000 1000 vroom dplyr tail real 0.013948440551757812 1963219523 7792242688 100000 1000 vroom dplyr sample process 0.02246668400000118 1963219523 7792242688 100000 1000 vroom dplyr sample real 0.022468090057373047 1963219523 7792242688 100000 1000 vroom dplyr filter process 0.05683080300000043 1963219523 7792242688 100000 1000 vroom dplyr filter real 0.019407272338867188 1963219523 7792242688 100000 1000 vroom dplyr aggregate process 0.07652678499999865 1963219523 7792242688 100000 1000 vroom dplyr aggregate real 0.07652854919433594 1963219523 7792242688 100000 1000 vroom_no_altrep base setup process 0.22699760899999988 1963219523 5739347968 100000 1000 vroom_no_altrep base setup real 0.22700119018554688 1963219523 5739347968 100000 1000 vroom_no_altrep base read process 40.352723725000004 1963219523 5739347968 100000 1000 vroom_no_altrep base read real 6.934685707092285 1963219523 5739347968 100000 1000 vroom_no_altrep base print process 0.060421132000001876 1963219523 5739347968 100000 1000 vroom_no_altrep base print real 0.06042838096618652 1963219523 5739347968 100000 1000 vroom_no_altrep base head process 0.0024158189999994306 1963219523 5739347968 100000 1000 vroom_no_altrep base head real 0.0024175643920898438 1963219523 5739347968 100000 1000 vroom_no_altrep base tail process 0.0020524330000029067 1963219523 5739347968 100000 1000 vroom_no_altrep base tail real 0.002053976058959961 1963219523 5739347968 100000 1000 vroom_no_altrep base sample process 0.004886046000002864 1963219523 5739347968 100000 1000 vroom_no_altrep base sample real 0.00488734245300293 1963219523 5739347968 100000 1000 vroom_no_altrep base filter process 0.005605907000003185 1963219523 5739347968 100000 1000 vroom_no_altrep base filter real 0.00560760498046875 1963219523 5739347968 100000 1000 vroom_no_altrep base aggregate process 0.0062097089999966215 1963219523 5739347968 100000 1000 vroom_no_altrep base aggregate real 0.006211280822753906 1963219523 5739347968 100000 1000 vroom_no_altrep dplyr setup process 0.2939437899999999 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr setup real 0.2939589023590088 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr read process 41.10774825 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr read real 6.923159122467041 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr print process 0.06295269499999989 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr print real 0.06295442581176758 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr head process 0.0024358220000024744 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr head real 0.0024373531341552734 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr tail process 0.002060569999997597 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr tail real 0.002062082290649414 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr sample process 0.09410682600000086 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr sample real 0.09410929679870605 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr filter process 0.013741070000001798 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr filter real 0.013743400573730469 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr aggregate process 0.030236498000000722 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr aggregate real 0.030238628387451172 1963219523 5740023808 100000 1000 vroom/inst/bench/all_numeric-long.tsv0000644000176200001440000002177614151250016017445 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.05067038700000004 490798139 2920148992 1000000 25 data.table data.table setup real 0.050673484802246094 490798139 2920148992 1000000 25 data.table data.table read process 1.339093691 490798139 2920148992 1000000 25 data.table data.table read real 0.25513172149658203 490798139 2920148992 1000000 25 data.table data.table print process 0.012984569999999973 490798139 2920148992 1000000 25 data.table data.table print real 0.01298666000366211 490798139 2920148992 1000000 25 data.table data.table head process 2.257949999999731e-4 490798139 2920148992 1000000 25 data.table data.table head real 0.00022745132446289062 490798139 2920148992 1000000 25 data.table data.table tail process 1.8293999999974275e-4 490798139 2920148992 1000000 25 data.table data.table tail real 1.842975616455078e-4 490798139 2920148992 1000000 25 data.table data.table sample process 0.0032215970000000205 490798139 2920148992 1000000 25 data.table data.table sample real 0.0032236576080322266 490798139 2920148992 1000000 25 data.table data.table filter process 0.00553502799999972 490798139 2920148992 1000000 25 data.table data.table filter real 0.005329132080078125 490798139 2920148992 1000000 25 data.table data.table aggregate process 0.15706757400000004 490798139 2920148992 1000000 25 data.table data.table aggregate real 0.024374723434448242 490798139 2920148992 1000000 25 read.delim base setup process 3.1689999999695573e-6 490798139 5142343680 1000000 25 read.delim base setup real 5.0067901611328125e-6 490798139 5142343680 1000000 25 read.delim base read process 111.410966158 490798139 5142343680 1000000 25 read.delim base read real 111.41260313987732 490798139 5142343680 1000000 25 read.delim base print process 1.4099199399999947 490798139 5142343680 1000000 25 read.delim base print real 1.4099228382110596 490798139 5142343680 1000000 25 read.delim base head process 2.753560000030575e-4 490798139 5142343680 1000000 25 read.delim base head real 2.765655517578125e-4 490798139 5142343680 1000000 25 read.delim base tail process 4.943510000003926e-4 490798139 5142343680 1000000 25 read.delim base tail real 4.954338073730469e-4 490798139 5142343680 1000000 25 read.delim base sample process 0.0011230939999933298 490798139 5142343680 1000000 25 read.delim base sample real 0.0011248588562011719 490798139 5142343680 1000000 25 read.delim base filter process 4.450478324000002 490798139 5142343680 1000000 25 read.delim base filter real 4.450565814971924 490798139 5142343680 1000000 25 read.delim base aggregate process 0.03682857900000158 490798139 5142343680 1000000 25 read.delim base aggregate real 0.03683161735534668 490798139 5142343680 1000000 25 readr dplyr setup process 0.2670424519999999 490798139 3029172224 1000000 25 readr dplyr setup real 0.267045259475708 490798139 3029172224 1000000 25 readr dplyr read process 13.121372993 490798139 3029172224 1000000 25 readr dplyr read real 13.124317646026611 490798139 3029172224 1000000 25 readr dplyr print process 0.0572878620000008 490798139 3029172224 1000000 25 readr dplyr print real 0.06383562088012695 490798139 3029172224 1000000 25 readr dplyr head process 0.001062892000000204 490798139 3029172224 1000000 25 readr dplyr head real 0.001064300537109375 490798139 3029172224 1000000 25 readr dplyr tail process 4.806849999994256e-4 490798139 3029172224 1000000 25 readr dplyr tail real 4.818439483642578e-4 490798139 3029172224 1000000 25 readr dplyr sample process 0.015185605999999297 490798139 3029172224 1000000 25 readr dplyr sample real 0.015187501907348633 490798139 3029172224 1000000 25 readr dplyr filter process 0.017675308999999473 490798139 3029172224 1000000 25 readr dplyr filter real 0.01767706871032715 490798139 3029172224 1000000 25 readr dplyr aggregate process 0.05443157900000095 490798139 3029172224 1000000 25 readr dplyr aggregate real 0.05443406105041504 490798139 3029172224 1000000 25 vroom base setup process 0.22593527800000013 490798139 3523780608 1000000 25 vroom base setup real 0.2259387969970703 490798139 3523780608 1000000 25 vroom base read process 3.8099602220000004 490798139 3523780608 1000000 25 vroom base read real 0.5808789730072021 490798139 3523780608 1000000 25 vroom base print process 0.05762982400000016 490798139 3523780608 1000000 25 vroom base print real 0.05461859703063965 490798139 3523780608 1000000 25 vroom base head process 9.245710000005403e-4 490798139 3523780608 1000000 25 vroom base head real 0.00092601776123046875 490798139 3523780608 1000000 25 vroom base tail process 4.916189999999432e-4 490798139 3523780608 1000000 25 vroom base tail real 4.928112030029297e-4 490798139 3523780608 1000000 25 vroom base sample process 0.0027541729999995823 490798139 3523780608 1000000 25 vroom base sample real 0.0027556419372558594 490798139 3523780608 1000000 25 vroom base filter process 0.2551561959999997 490798139 3523780608 1000000 25 vroom base filter real 0.028618335723876953 490798139 3523780608 1000000 25 vroom base aggregate process 0.2509484510000002 490798139 3523780608 1000000 25 vroom base aggregate real 0.25095057487487793 490798139 3523780608 1000000 25 vroom dplyr setup process 0.29869805699999996 490798139 3531214848 1000000 25 vroom dplyr setup real 0.29870104789733887 490798139 3531214848 1000000 25 vroom dplyr read process 3.8170653839999997 490798139 3531214848 1000000 25 vroom dplyr read real 0.603179931640625 490798139 3531214848 1000000 25 vroom dplyr print process 0.06604458400000013 490798139 3531214848 1000000 25 vroom dplyr print real 0.06310796737670898 490798139 3531214848 1000000 25 vroom dplyr head process 9.071970000000817e-4 490798139 3531214848 1000000 25 vroom dplyr head real 0.00090885162353515625 490798139 3531214848 1000000 25 vroom dplyr tail process 4.918530000006527e-4 490798139 3531214848 1000000 25 vroom dplyr tail real 0.00049304962158203125 490798139 3531214848 1000000 25 vroom dplyr sample process 0.013811199000000052 490798139 3531214848 1000000 25 vroom dplyr sample real 0.013812541961669922 490798139 3531214848 1000000 25 vroom dplyr filter process 0.2886524169999998 490798139 3531214848 1000000 25 vroom dplyr filter real 0.041882991790771484 490798139 3531214848 1000000 25 vroom dplyr aggregate process 0.23468923700000044 490798139 3531214848 1000000 25 vroom dplyr aggregate real 0.23469161987304688 490798139 3531214848 1000000 25 vroom_no_altrep base setup process 0.22674703399999996 490798139 2886311936 1000000 25 vroom_no_altrep base setup real 0.22675061225891113 490798139 2886311936 1000000 25 vroom_no_altrep base read process 9.917269908 490798139 2886311936 1000000 25 vroom_no_altrep base read real 1.325624942779541 490798139 2886311936 1000000 25 vroom_no_altrep base print process 0.04774370600000033 490798139 2886311936 1000000 25 vroom_no_altrep base print real 0.04775238037109375 490798139 2886311936 1000000 25 vroom_no_altrep base head process 8.502769999996218e-4 490798139 2886311936 1000000 25 vroom_no_altrep base head real 0.00085163116455078125 490798139 2886311936 1000000 25 vroom_no_altrep base tail process 4.878420000000716e-4 490798139 2886311936 1000000 25 vroom_no_altrep base tail real 4.889965057373047e-4 490798139 2886311936 1000000 25 vroom_no_altrep base sample process 0.0028887550000007423 490798139 2886311936 1000000 25 vroom_no_altrep base sample real 0.0028901100158691406 490798139 2886311936 1000000 25 vroom_no_altrep base filter process 0.005902124000000342 490798139 2886311936 1000000 25 vroom_no_altrep base filter real 0.005903720855712891 490798139 2886311936 1000000 25 vroom_no_altrep base aggregate process 0.054749883000001276 490798139 2886311936 1000000 25 vroom_no_altrep base aggregate real 0.05475163459777832 490798139 2886311936 1000000 25 vroom_no_altrep dplyr setup process 0.2972948229999999 490798139 2957496320 1000000 25 vroom_no_altrep dplyr setup real 0.29729771614074707 490798139 2957496320 1000000 25 vroom_no_altrep dplyr read process 10.102860680000001 490798139 2957496320 1000000 25 vroom_no_altrep dplyr read real 1.3375225067138672 490798139 2957496320 1000000 25 vroom_no_altrep dplyr print process 0.04706370399999926 490798139 2957496320 1000000 25 vroom_no_altrep dplyr print real 0.04706525802612305 490798139 2957496320 1000000 25 vroom_no_altrep dplyr head process 8.443339999999466e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr head real 8.456707000732422e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr tail process 4.372789999997906e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr tail real 4.3845176696777344e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr sample process 0.013971556000001328 490798139 2957496320 1000000 25 vroom_no_altrep dplyr sample real 0.013973236083984375 490798139 2957496320 1000000 25 vroom_no_altrep dplyr filter process 0.01792857100000056 490798139 2957496320 1000000 25 vroom_no_altrep dplyr filter real 0.017930269241333008 490798139 2957496320 1000000 25 vroom_no_altrep dplyr aggregate process 0.045251213000000234 490798139 2957496320 1000000 25 vroom_no_altrep dplyr aggregate real 0.04525351524353027 490798139 2957496320 1000000 25 vroom/inst/bench/all_character-wide.tsv0000644000176200001440000001762614151250016017727 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.05824787900000006 1600080624 13596225536 100000 1000 data.table data.table setup real 0.05825209617614746 1600080624 13596225536 100000 1000 data.table data.table read process 205.61311071900002 1600080624 13596225536 100000 1000 data.table data.table read real 201.76576399803162 1600080624 13596225536 100000 1000 data.table data.table print process 0.13403993000000014 1600080624 13596225536 100000 1000 data.table data.table print real 0.13405299186706543 1600080624 13596225536 100000 1000 data.table data.table head process 0.001164771000020437 1600080624 13596225536 100000 1000 data.table data.table head real 0.0011682510375976562 1600080624 13596225536 100000 1000 data.table data.table tail process 0.0012540889999854699 1600080624 13596225536 100000 1000 data.table data.table tail real 0.0012569427490234375 1600080624 13596225536 100000 1000 data.table data.table sample process 0.03256328700001632 1600080624 13596225536 100000 1000 data.table data.table sample real 0.03256726264953613 1600080624 13596225536 100000 1000 data.table data.table filter process 0.20218088300001114 1600080624 13596225536 100000 1000 data.table data.table filter real 0.16787242889404297 1600080624 13596225536 100000 1000 data.table data.table aggregate process 0.048374496000008094 1600080624 13596225536 100000 1000 data.table data.table aggregate real 0.014370918273925781 1600080624 13596225536 100000 1000 read.delim base setup process 3.6660000000132698e-6 1600080624 14050525184 100000 1000 read.delim base setup real 5.7220458984375e-6 1600080624 14050525184 100000 1000 read.delim base read process 510.41117117600004 1600080624 14050525184 100000 1000 read.delim base read real 510.42269372940063 1600080624 14050525184 100000 1000 read.delim base print process 0.14892288600003667 1600080624 14050525184 100000 1000 read.delim base print real 0.1489250659942627 1600080624 14050525184 100000 1000 read.delim base head process 0.006831051000062871 1600080624 14050525184 100000 1000 read.delim base head real 0.006834268569946289 1600080624 14050525184 100000 1000 read.delim base tail process 0.007016168999939509 1600080624 14050525184 100000 1000 read.delim base tail real 0.007021188735961914 1600080624 14050525184 100000 1000 read.delim base sample process 0.025838453000005757 1600080624 14050525184 100000 1000 read.delim base sample real 0.025842905044555664 1600080624 14050525184 100000 1000 read.delim base filter process 0.22350397200000316 1600080624 14050525184 100000 1000 read.delim base filter real 0.2235558032989502 1600080624 14050525184 100000 1000 read.delim base aggregate process 0.05853724200005672 1600080624 14050525184 100000 1000 read.delim base aggregate real 0.05854058265686035 1600080624 14050525184 100000 1000 readr dplyr setup process 0.33448946199999985 1600080624 13111070720 100000 1000 readr dplyr setup real 0.33449411392211914 1600080624 13111070720 100000 1000 readr dplyr read process 459.43154994500003 1600080624 13111070720 100000 1000 readr dplyr read real 459.44729495048523 1600080624 13111070720 100000 1000 readr dplyr print process 0.13655689199998733 1600080624 13111070720 100000 1000 readr dplyr print real 0.21697449684143066 1600080624 13111070720 100000 1000 readr dplyr head process 0.0032427959999949962 1600080624 13111070720 100000 1000 readr dplyr head real 0.0032444000244140625 1600080624 13111070720 100000 1000 readr dplyr tail process 0.0025115230000096744 1600080624 13111070720 100000 1000 readr dplyr tail real 0.002513408660888672 1600080624 13111070720 100000 1000 readr dplyr sample process 0.02890705900000512 1600080624 13111070720 100000 1000 readr dplyr sample real 0.02890944480895996 1600080624 13111070720 100000 1000 readr dplyr filter process 0.037404203999983565 1600080624 13111070720 100000 1000 readr dplyr filter real 0.03740739822387695 1600080624 13111070720 100000 1000 readr dplyr aggregate process 0.056806200999972134 1600080624 13111070720 100000 1000 readr dplyr aggregate real 0.056809425354003906 1600080624 13111070720 100000 1000 vroom base setup process 0.27099322299999984 1600080624 7050588160 100000 1000 vroom base setup real 0.27102088928222656 1600080624 7050588160 100000 1000 vroom base read process 16.096324971999998 1600080624 7050588160 100000 1000 vroom base read real 3.1437342166900635 1600080624 7050588160 100000 1000 vroom base print process 0.06175371999999868 1600080624 7050588160 100000 1000 vroom base print real 0.06175589561462402 1600080624 7050588160 100000 1000 vroom base head process 0.004202636999998788 1600080624 7050588160 100000 1000 vroom base head real 0.004204511642456055 1600080624 7050588160 100000 1000 vroom base tail process 0.003743497999998624 1600080624 7050588160 100000 1000 vroom base tail real 0.003745555877685547 1600080624 7050588160 100000 1000 vroom base sample process 0.004824033999998534 1600080624 7050588160 100000 1000 vroom base sample real 0.0048258304595947266 1600080624 7050588160 100000 1000 vroom base filter process 0.05477418799999967 1600080624 7050588160 100000 1000 vroom base filter real 0.054776668548583984 1600080624 7050588160 100000 1000 vroom base aggregate process 0.2514594879999983 1600080624 7050588160 100000 1000 vroom base aggregate real 0.2514626979827881 1600080624 7050588160 100000 1000 vroom dplyr setup process 0.36314286799999995 1600080624 7050731520 100000 1000 vroom dplyr setup real 0.3631572723388672 1600080624 7050731520 100000 1000 vroom dplyr read process 15.844784293 1600080624 7050731520 100000 1000 vroom dplyr read real 3.108229875564575 1600080624 7050731520 100000 1000 vroom dplyr print process 0.06392608800000232 1600080624 7050731520 100000 1000 vroom dplyr print real 0.06392836570739746 1600080624 7050731520 100000 1000 vroom dplyr head process 0.004407775999997199 1600080624 7050731520 100000 1000 vroom dplyr head real 0.004410743713378906 1600080624 7050731520 100000 1000 vroom dplyr tail process 0.0039727230000004 1600080624 7050731520 100000 1000 vroom dplyr tail real 0.0039751529693603516 1600080624 7050731520 100000 1000 vroom dplyr sample process 0.026044930000001187 1600080624 7050731520 100000 1000 vroom dplyr sample real 0.026047945022583008 1600080624 7050731520 100000 1000 vroom dplyr filter process 0.08114447399999847 1600080624 7050731520 100000 1000 vroom dplyr filter real 0.0811469554901123 1600080624 7050731520 100000 1000 vroom dplyr aggregate process 0.1597604110000006 1600080624 7050731520 100000 1000 vroom dplyr aggregate real 0.15976333618164062 1600080624 7050731520 100000 1000 vroom_no_altrep dplyr setup process 0.349206017 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr setup real 0.3492105007171631 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr read process 258.09181479800003 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr read real 247.31999611854553 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr print process 0.06623288399998728 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr print real 0.06623482704162598 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr head process 0.0027959219999615925 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr head real 0.002797365188598633 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr tail process 0.0024252139999703104 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr tail real 0.0024268627166748047 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr sample process 0.027183294999986174 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr sample real 0.02718496322631836 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr filter process 0.034791021999978966 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr filter real 0.03479290008544922 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr aggregate process 0.036543052000013176 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr aggregate real 0.0365450382232666 1600080624 13033082880 100000 1000 vroom/inst/bench/download-data.sh0000755000176200001440000000105014142512075016522 0ustar liggesusers#!/bin/bash # download and extract taxi data mkdir ~/data/ cd ~/data wget -O ~/data/trip_fare.7z https://archive.org/download/nycTaxiTripData2013/trip_fare.7z && \ sudo apt install p7zip-full && \ 7z x trip_fare.7z &> data.out # fix trailing space in header for every file ls *trip_fare*.csv | xargs -P 16 sed -i '1 s/, /,/g' # download the US census data wget -O ~/data/all_California.zip https://www2.census.gov/census_2000/datasets/PUMS/FivePercent/California/all_California.zip && \ sudo apt install unzip && \ unzip all_California.zip vroom/inst/bench/all_numeric-wide/0000755000176200001440000000000014151431426016671 5ustar liggesusersvroom/inst/bench/all_numeric-wide/vroom-dplyr.R0000644000176200001440000000041614142512075021307 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-wide/input.R0000644000176200001440000000040614142512075020153 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") data <- vroom::gen_tbl(rows, cols, col_types = strrep("d", cols)) vroom::vroom_write(data, output, "\t") vroom/inst/bench/all_numeric-wide/readr-dplyr.R0000644000176200001440000000037214142512075021243 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-wide/vroom-base.R0000644000176200001440000000034514142512075021070 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-wide/vroom_no_altrep-dplyr.R0000644000176200001440000000043614142512075023354 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-wide/vroom_no_altrep-base.R0000644000176200001440000000036514142512075023135 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-wide/read.delim-base.R0000644000176200001440000000033514142512075021731 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-wide/data.table-data.table.R0000644000176200001440000000034414142512075023011 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 > 3, ] e <- x[ , .(mean(X1)), by = as.integer(X2)] vroom/inst/bench/README.md0000644000176200001440000000135514142512075014734 0ustar liggesusers# Benchmarks for vroom The benchmarks are run with a makefile, run `make` to run them. ## Personal notes on running benchmarks on AWS ### Starting up - Create new volume from previous vroom snapshot - `io1` volume type - 2500 IOPS - Attach volume to instance - `/dev/sda1` ### Attach to instance `ssh vroom-bench` When you first start there may be some unattended upgrades installing, use top to monitor and wait until they are done. ### running ``` make -j 1 \ TAXI_INPUTS='$(wildcard ~/data/trip_fare*csv)' \ FWF_INPUT=~/data/PUMS5_06.TXT \ BENCH_LONG_ROWS=1000000 \ BENCH_LOG_COLS=25 \ BENCH_WIDE_ROWS=100000 \ BENCH_WIDE_COLS=1000 ``` ### Tearing down - Shut down instance - Detach volume - Create snapshot - Delete volume vroom/inst/bench/fwf.tsv0000644000176200001440000001442414151244612014775 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols read.delim base setup process 3.063999999941558e-6 709936880 6621343744 2342339 37 read.delim base setup real 5.245208740234375e-6 709936880 6621343744 2342339 37 read.delim base read process 1089.464910198 709936880 6621343744 2342339 37 read.delim base read real 1089.5732316970825 709936880 6621343744 2342339 37 read.delim base print process 0.015051766999931715 709936880 6621343744 2342339 37 read.delim base print real 0.015721797943115234 709936880 6621343744 2342339 37 read.delim base head process 8.085290000963141e-4 709936880 6621343744 2342339 37 read.delim base head real 8.099079132080078e-4 709936880 6621343744 2342339 37 read.delim base tail process 0.0010067789999084198 709936880 6621343744 2342339 37 read.delim base tail real 0.0010082721710205078 709936880 6621343744 2342339 37 read.delim base sample process 0.0029781670000375016 709936880 6621343744 2342339 37 read.delim base sample real 0.0029802322387695312 709936880 6621343744 2342339 37 read.delim base filter process 0.49151955999991515 709936880 6621343744 2342339 37 read.delim base filter real 0.49154090881347656 709936880 6621343744 2342339 37 read.delim base aggregate process 0.08967751000000135 709936880 6621343744 2342339 37 read.delim base aggregate real 0.08968019485473633 709936880 6621343744 2342339 37 readr dplyr setup process 0.12630891799999988 709936880 6644965376 2342339 37 readr dplyr setup real 0.1263124942779541 709936880 6644965376 2342339 37 readr dplyr read process 32.593650083 709936880 6644965376 2342339 37 readr dplyr read real 32.59428787231445 709936880 6644965376 2342339 37 readr dplyr print process 0.04750896499999868 709936880 6644965376 2342339 37 readr dplyr print real 0.04751086235046387 709936880 6644965376 2342339 37 readr dplyr head process 0.0011317969999993238 709936880 6644965376 2342339 37 readr dplyr head real 0.0011332035064697266 709936880 6644965376 2342339 37 readr dplyr tail process 6.720350000009034e-4 709936880 6644965376 2342339 37 readr dplyr tail real 6.732940673828125e-4 709936880 6644965376 2342339 37 readr dplyr sample process 0.016384803000001114 709936880 6644965376 2342339 37 readr dplyr sample real 0.01638650894165039 709936880 6644965376 2342339 37 readr dplyr filter process 0.09458435599999859 709936880 6644965376 2342339 37 readr dplyr filter real 0.09458684921264648 709936880 6644965376 2342339 37 readr dplyr aggregate process 0.09379347599999477 709936880 6644965376 2342339 37 readr dplyr aggregate real 0.0937960147857666 709936880 6644965376 2342339 37 vroom base setup process 7.52309999998424e-5 709936880 4989079552 2342339 37 vroom base setup real 7.796287536621094e-5 709936880 4989079552 2342339 37 vroom base read process 0.16403843000000018 709936880 4989079552 2342339 37 vroom base read real 0.16362786293029785 709936880 4989079552 2342339 37 vroom base print process 0.056157112999999814 709936880 4989079552 2342339 37 vroom base print real 0.05505537986755371 709936880 4989079552 2342339 37 vroom base head process 5.586259999996734e-4 709936880 4989079552 2342339 37 vroom base head real 5.600452423095703e-4 709936880 4989079552 2342339 37 vroom base tail process 8.067250000003412e-4 709936880 4989079552 2342339 37 vroom base tail real 8.080005645751953e-4 709936880 4989079552 2342339 37 vroom base sample process 0.00669999900000029 709936880 4989079552 2342339 37 vroom base sample real 0.006701469421386719 709936880 4989079552 2342339 37 vroom base filter process 0.284221665 709936880 4989079552 2342339 37 vroom base filter real 0.2842233180999756 709936880 4989079552 2342339 37 vroom base aggregate process 2.1088666509999996 709936880 4989079552 2342339 37 vroom base aggregate real 1.7768657207489014 709936880 4989079552 2342339 37 vroom dplyr setup process 0.08314487400000026 709936880 4959260672 2342339 37 vroom dplyr setup real 0.08314847946166992 709936880 4959260672 2342339 37 vroom dplyr read process 0.1629412410000004 709936880 4959260672 2342339 37 vroom dplyr read real 0.16250014305114746 709936880 4959260672 2342339 37 vroom dplyr print process 0.04872845899999989 709936880 4959260672 2342339 37 vroom dplyr print real 0.047605037689208984 709936880 4959260672 2342339 37 vroom dplyr head process 0.001250199999999868 709936880 4959260672 2342339 37 vroom dplyr head real 0.0012516975402832031 709936880 4959260672 2342339 37 vroom dplyr tail process 7.490030000001369e-4 709936880 4959260672 2342339 37 vroom dplyr tail real 0.00075054168701171875 709936880 4959260672 2342339 37 vroom dplyr sample process 0.01527529800000016 709936880 4959260672 2342339 37 vroom dplyr sample real 0.01527714729309082 709936880 4959260672 2342339 37 vroom dplyr filter process 0.305116822 709936880 4959260672 2342339 37 vroom dplyr filter real 0.30511975288391113 709936880 4959260672 2342339 37 vroom dplyr aggregate process 1.2913172750000004 709936880 4959260672 2342339 37 vroom dplyr aggregate real 1.2913398742675781 709936880 4959260672 2342339 37 vroom_no_altrep dplyr setup process 0.08015654499999991 709936880 6397005824 2342339 37 vroom_no_altrep dplyr setup real 0.08015990257263184 709936880 6397005824 2342339 37 vroom_no_altrep dplyr read process 34.306891702 709936880 6397005824 2342339 37 vroom_no_altrep dplyr read real 14.682522296905518 709936880 6397005824 2342339 37 vroom_no_altrep dplyr print process 0.04313145500000104 709936880 6397005824 2342339 37 vroom_no_altrep dplyr print real 0.04313325881958008 709936880 6397005824 2342339 37 vroom_no_altrep dplyr head process 8.676749999949607e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr head real 8.692741394042969e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr tail process 5.269520000013017e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr tail real 5.283355712890625e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr sample process 0.01451422600000285 709936880 6397005824 2342339 37 vroom_no_altrep dplyr sample real 0.014516115188598633 709936880 6397005824 2342339 37 vroom_no_altrep dplyr filter process 0.4679828829999977 709936880 6397005824 2342339 37 vroom_no_altrep dplyr filter real 0.46799564361572266 709936880 6397005824 2342339 37 vroom_no_altrep dplyr aggregate process 0.09054751499999725 709936880 6397005824 2342339 37 vroom_no_altrep dplyr aggregate real 0.09055042266845703 709936880 6397005824 2342339 37 vroom/inst/bench/taxi.tsv0000644000176200001440000001742414151244612015163 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.0535136759999999 1641999923 6853369856 14388451 11 data.table data.table setup real 0.05351758003234863 1641999923 6853369856 14388451 11 data.table data.table read process 28.634132631 1641999923 6853369856 14388451 11 data.table data.table read real 5.829838037490845 1641999923 6853369856 14388451 11 data.table data.table print process 0.011972201999999044 1641999923 6853369856 14388451 11 data.table data.table print real 0.011974573135375977 1641999923 6853369856 14388451 11 data.table data.table head process 2.2678200000214588e-4 1641999923 6853369856 14388451 11 data.table data.table head real 2.281665802001953e-4 1641999923 6853369856 14388451 11 data.table data.table tail process 1.8586699999900702e-4 1641999923 6853369856 14388451 11 data.table data.table tail real 0.00018739700317382812 1641999923 6853369856 14388451 11 data.table data.table sample process 6.299819999995293e-4 1641999923 6853369856 14388451 11 data.table data.table sample real 6.320476531982422e-4 1641999923 6853369856 14388451 11 data.table data.table filter process 0.34992646499999935 1641999923 6853369856 14388451 11 data.table data.table filter real 0.10343503952026367 1641999923 6853369856 14388451 11 data.table data.table aggregate process 1.6930106420000008 1641999923 6853369856 14388451 11 data.table data.table aggregate real 0.76350998878479 1641999923 6853369856 14388451 11 read.delim base setup process 2.617000000038061e-6 1641999923 6637604864 14388451 11 read.delim base setup real 4.291534423828125e-6 1641999923 6637604864 14388451 11 read.delim base read process 72.30426375399999 1641999923 6637604864 14388451 11 read.delim base read real 72.30614757537842 1641999923 6637604864 14388451 11 read.delim base print process 0.0059510929999930795 1641999923 6637604864 14388451 11 read.delim base print real 0.0059528350830078125 1641999923 6637604864 14388451 11 read.delim base head process 1.9695099999239574e-4 1641999923 6637604864 14388451 11 read.delim base head real 1.983642578125e-4 1641999923 6637604864 14388451 11 read.delim base tail process 4.1288200000622055e-4 1641999923 6637604864 14388451 11 read.delim base tail real 4.1413307189941406e-4 1641999923 6637604864 14388451 11 read.delim base sample process 3.71048000005203e-4 1641999923 6637604864 14388451 11 read.delim base sample real 3.726482391357422e-4 1641999923 6637604864 14388451 11 read.delim base filter process 1.2673573149999982 1641999923 6637604864 14388451 11 read.delim base filter real 1.2681455612182617 1641999923 6637604864 14388451 11 read.delim base aggregate process 0.8941533619999973 1641999923 6637604864 14388451 11 read.delim base aggregate real 0.8941662311553955 1641999923 6637604864 14388451 11 readr dplyr setup process 0.33623017600000016 1641999923 7415922688 14388451 11 readr dplyr setup real 0.3362598419189453 1641999923 7415922688 14388451 11 readr dplyr read process 37.30245180599999 1641999923 7415922688 14388451 11 readr dplyr read real 37.303383588790894 1641999923 7415922688 14388451 11 readr dplyr print process 0.12003345499999796 1641999923 7415922688 14388451 11 readr dplyr print real 0.14695501327514648 1641999923 7415922688 14388451 11 readr dplyr head process 0.0013562000000035823 1641999923 7415922688 14388451 11 readr dplyr head real 0.0013580322265625 1641999923 7415922688 14388451 11 readr dplyr tail process 6.067529999995713e-4 1641999923 7415922688 14388451 11 readr dplyr tail real 6.082057952880859e-4 1641999923 7415922688 14388451 11 readr dplyr sample process 0.01610033400000077 1641999923 7415922688 14388451 11 readr dplyr sample real 0.01610279083251953 1641999923 7415922688 14388451 11 readr dplyr filter process 0.24887825199999725 1641999923 7415922688 14388451 11 readr dplyr filter real 0.24891424179077148 1641999923 7415922688 14388451 11 readr dplyr aggregate process 0.5376497970000003 1641999923 7415922688 14388451 11 readr dplyr aggregate real 0.5376534461975098 1641999923 7415922688 14388451 11 vroom base setup process 0.233707 1666833417 6815055872 14776615 11 vroom base setup real 0.23376321099931374 1666833417 6815055872 14776615 11 vroom base read process 17.640452000000003 1666833417 6815055872 14776615 11 vroom base read real 1.3566655699978583 1666833417 6815055872 14776615 11 vroom base print process 0.1533760000000015 1666833417 6815055872 14776615 11 vroom base print real 0.15729006798937917 1666833417 6815055872 14776615 11 vroom base head process 0.0020479999999984955 1666833417 6815055872 14776615 11 vroom base head real 0.0020507239969447255 1666833417 6815055872 14776615 11 vroom base tail process 4.980000000003315e-4 1666833417 6815055872 14776615 11 vroom base tail real 5.036990041844547e-4 1666833417 6815055872 14776615 11 vroom base sample process 2.119999999976585e-4 1666833417 6815055872 14776615 11 vroom base sample real 2.1384801948443055e-4 1666833417 6815055872 14776615 11 vroom base filter process 1.1206159999999983 1666833417 6815055872 14776615 11 vroom base filter real 1.1206832609605044 1666833417 6815055872 14776615 11 vroom base aggregate process 8.975451 1666833417 6815055872 14776615 11 vroom base aggregate real 7.352519184001721 1666833417 6815055872 14776615 11 vroom dplyr setup process 0.443401 1666833417 6881202176 14776615 11 vroom dplyr setup real 0.4445208079996519 1666833417 6881202176 14776615 11 vroom dplyr read process 16.979603 1666833417 6881202176 14776615 11 vroom dplyr read real 1.2900599709828384 1666833417 6881202176 14776615 11 vroom dplyr print process 0.07167099999999849 1666833417 6881202176 14776615 11 vroom dplyr print real 0.07509800099069253 1666833417 6881202176 14776615 11 vroom dplyr head process 0.0017300000000020077 1666833417 6881202176 14776615 11 vroom dplyr head real 0.00173125701257959 1666833417 6881202176 14776615 11 vroom dplyr tail process 3.6899999999917554e-4 1666833417 6881202176 14776615 11 vroom dplyr tail real 3.7027703365311027e-4 1666833417 6881202176 14776615 11 vroom dplyr sample process 0.010666000000000508 1666833417 6881202176 14776615 11 vroom dplyr sample real 0.010672853037249297 1666833417 6881202176 14776615 11 vroom dplyr filter process 1.3317319999999988 1666833417 6881202176 14776615 11 vroom dplyr filter real 1.3320457949885167 1666833417 6881202176 14776615 11 vroom dplyr aggregate process 3.956316000000001 1666833417 6881202176 14776615 11 vroom dplyr aggregate real 3.9592064779717475 1666833417 6881202176 14776615 11 vroom_no_altrep dplyr setup process 0.301629645 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr setup real 0.3016328811645508 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr read process 47.375392178000006 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr read real 18.417577743530273 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr print process 0.1009485880000014 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr print real 0.11608505249023438 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr head process 0.001024977000000149 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr head real 0.0010266304016113281 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr tail process 4.7993700000148465e-4 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr tail real 4.813671112060547e-4 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr sample process 0.013319092999999782 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr sample real 0.0133209228515625 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr filter process 0.960716675999997 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr filter real 0.9607300758361816 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr aggregate process 1.2211422310000017 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr aggregate real 1.2211759090423584 1641999923 7029014528 14388451 11 vroom/inst/bench/run-bench-fwf.R0000755000176200001440000000727314142512075016251 0ustar liggesusers#!/usr/bin/env Rscript args <- commandArgs(trailingOnly = TRUE) source_file <- args[[1]] out_file <- args[[2]] file <- args[-c(1:2)] library(vroom) fields <- vroom(col_names = c("begin", "end", "width", "col_names"), delim = "\t", "1 1 1 RECTYPE 2 8 7 SERIALNO 9 9 1 SAMPLE 10 11 2 STATE 12 12 1 REGION 13 13 1 DIVISION 14 18 5 PUMA5 19 23 5 PUMA1 24 27 4 MSACMSA5 28 31 4 MSAPMSA5 32 35 4 MSACMSA1 36 39 4 MSAPMSA1 40 41 2 AREATYP5 42 43 2 AREATYP1 44 57 14 TOTPUMA5 58 71 14 LNDPUMA5 72 85 14 TOTPUMA1 86 99 14 LNDPUMA1 100 101 2 SUBSAMPL 102 105 4 HWEIGHT 106 107 2 PERSONS 108 108 1 UNITTYPE 109 109 1 HSUB 110 110 1 HAUG 111 111 1 VACSTAT 112 112 1 VACSTATA 113 113 1 TENURE 114 114 1 TENUREA 115 116 2 BLDGSZ 117 117 1 BLDGSZA 118 118 1 YRBUILT 119 119 1 YRBUILTA 120 120 1 YRMOVED 121 121 1 YRMOVEDA 122 122 1 ROOMS 123 123 1 ROOMSA 124 124 1 BEDRMS 125 125 1 BEDRMSA 126 126 1 CPLUMB 127 127 1 CPLUMBA 128 128 1 CKITCH 129 129 1 CKITCHA 130 130 1 PHONE 131 131 1 PHONEA 132 132 1 FUEL 133 133 1 FUELA 134 134 1 VEHICL 135 135 1 VEHICLA 136 136 1 BUSINES 137 137 1 BUSINESA 138 138 1 ACRES 139 139 1 ACRESA 140 140 1 AGSALES 141 141 1 AGSALESA 142 145 4 ELEC 146 146 1 ELECA 147 150 4 GAS 151 151 1 GASA 152 155 4 WATER 156 156 1 WATERA 157 160 4 OIL 161 161 1 OILA 162 165 4 RENT 166 166 1 RENTA 167 167 1 MEALS 168 168 1 MEALSA 169 169 1 MORTG1 170 170 1 MORTG1A 171 175 5 MRT1AMT 176 176 1 MRT1AMTA 177 177 1 MORTG2 178 178 1 MORTG2A 179 183 5 MRT2AMT 184 184 1 MRT2AMTA 185 185 1 TAXINCL 186 186 1 TAXINCLA 187 188 2 TAXAMT 189 189 1 TAXAMTA 190 190 1 INSINCL 191 191 1 INSINCLA 192 195 4 INSAMT 196 196 1 INSAMTA 197 200 4 CONDFEE 201 201 1 CONDFEEA 202 203 2 VALUE 204 204 1 VALUEA 205 205 1 MHLOAN 206 206 1 MHLOANA 207 211 5 MHCOST 212 212 1 MHCOSTA 213 213 1 HHT 214 215 2 P65 216 217 2 P18 218 219 2 NPF 220 221 2 NOC 222 223 2 NRC 224 224 1 PSF 225 225 1 PAOC 226 226 1 PARC 227 227 1 SVAL 228 232 5 SMOC 233 235 3 SMOCAPI 236 236 1 SRNT 237 240 4 GRENT 241 243 3 GRAPI 244 244 1 FNF 245 245 1 HHL 246 246 1 LNGI 247 247 1 WIF 248 248 1 EMPSTAT 249 250 2 WORKEXP 251 258 8 HINC 259 266 8 FINC ") fields$begin <- fields$begin - 1 types <- cols( .default = col_double(), RECTYPE = col_character(), SERIALNO = col_character(), STATE = col_character(), PUMA5 = col_character(), PUMA1 = col_character(), MSACMSA5 = col_character(), MSAPMSA5 = col_character(), MSACMSA1 = col_character(), MSAPMSA1 = col_character(), AREATYP5 = col_character(), AREATYP1 = col_character(), LNDPUMA5 = col_character(), TOTPUMA1 = col_character(), LNDPUMA1 = col_character(), SUBSAMPL = col_character(), HWEIGHT = col_character(), PERSONS = col_character(), BLDGSZ = col_character(), ELEC = col_character(), GAS = col_character(), WATER = col_character(), OIL = col_character(), RENT = col_character(), MRT1AMT = col_character(), MRT2AMT = col_character(), TAXAMT = col_character(), INSAMT = col_character(), CONDFEE = col_character(), VALUE = col_character(), MHCOST = col_character(), P65 = col_character(), P18 = col_character(), NPF = col_character(), NOC = col_character(), NRC = col_character(), SMOC = col_character(), SMOCAPI = col_character(), GRENT = col_character(), GRAPI = col_character(), WORKEXP = col_character(), HINC = col_character(), FINC = col_character() ) cat(source_file, "\n") out <- bench::workout_expressions(as.list(parse(source_file, keep.source = FALSE))) x <- vroom::vroom(file, col_types = list()) out$size <- sum(file.size(file)) out$rows <- nrow(x) out$cols <- ncol(x) out$process <- as.numeric(out$process) out$real <- as.numeric(out$real) out$max_memory <- as.numeric(bench::bench_process_memory()[["max"]]) vroom::vroom_write(out, out_file) vroom/inst/bench/session_info.tsv0000644000176200001440000000157414142515724016720 0ustar liggesuserspackage ondiskversion loadedversion path loadedpath attached is_base date source md5ok library base 4.1.0 NA /opt/R/4.1.0/lib/R/library/base NA TRUE TRUE 2021-05-18 local NA /opt/R/4.1.0/lib/R/library data.table 1.14.0 NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/data.table NA FALSE FALSE 2021-02-21 RSPM (R 4.1.0) NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 dplyr 1.0.7 NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/dplyr NA FALSE FALSE 2021-06-18 RSPM (R 4.1.0) NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 readr 1.4.0 NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/readr NA FALSE FALSE 2020-10-05 RSPM (R 4.1.0) NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 vroom 1.5.1 1.5.1 /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/vroom /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/vroom FALSE FALSE 2021-06-22 local NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 vroom/inst/bench/all_numeric-long/0000755000176200001440000000000014151431426016700 5ustar liggesusersvroom/inst/bench/all_numeric-long/vroom-dplyr.R0000644000176200001440000000041614142512075021316 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-long/input.R0000644000176200001440000000040614142512075020162 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") data <- vroom::gen_tbl(rows, cols, col_types = strrep("d", cols)) vroom::vroom_write(data, output, "\t") vroom/inst/bench/all_numeric-long/readr-dplyr.R0000644000176200001440000000037214142512075021252 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-long/vroom-base.R0000644000176200001440000000034514142512075021077 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-long/vroom_no_altrep-dplyr.R0000644000176200001440000000043614142512075023363 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-long/vroom_no_altrep-base.R0000644000176200001440000000036514142512075023144 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-long/read.delim-base.R0000644000176200001440000000033514142512075021740 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-long/data.table-data.table.R0000644000176200001440000000034414142512075023020 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 > 3, ] e <- x[ , .(mean(X1)), by = as.integer(X2)] vroom/inst/bench/taxi_multiple/0000755000176200001440000000000014151431426016331 5ustar liggesusersvroom/inst/bench/taxi_multiple/vroom-dplyr.R0000644000176200001440000000054414142512075020751 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, id = "path", col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = TRUE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi_multiple/readr-dplyr.R0000644000176200001440000000056114142512075020703 0ustar liggesusers({ library(readr); library(dplyr); library(purrr) }) x <- map_dfr(set_names(file), .id = "path", ~ read_csv(.x, col_types = c(pickup_datetime = "c"), quote = "", trim_ws = FALSE, na = character()) ) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi_multiple/vroom-base.R0000644000176200001440000000050614142512075020527 0ustar liggesuserslibrary(vroom) x <- vroom(file, id = "path", col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = TRUE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$payment_type == "UNK", ] e <- tapply(x$tip_amount, x$payment_type, mean) vroom/inst/bench/taxi_multiple/vroom_no_altrep-dplyr.R0000644000176200001440000000054514142512075023015 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, id = "path", col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi_multiple/data.table-data.table.R0000644000176200001440000000046514142512075022455 0ustar liggesuserslibrary(data.table) x <- rbindlist(idcol = "path", lapply(stats::setNames(file, file), fread, sep = ",", quote = "", strip.white = FALSE, na.strings = NULL) ) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[payment_type == "UNK", ] e <- x[ , .(mean(tip_amount)), by = payment_type] vroom/inst/bench/all_character-wide/0000755000176200001440000000000014151431426017163 5ustar liggesusersvroom/inst/bench/all_character-wide/vroom-dplyr.R0000644000176200001440000000043414142512075021601 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-wide/input.R0000644000176200001440000000124614142512075020450 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") library(vroom) # We want ~ 1000 rows to filter num_levels <- 5 levels <- c("helpless_sheep", gen_name(num_levels - 1)) filt_p <- 1000 / rows # The prob for the rest should just be evenly spaced rest_p <- rep((1 - filt_p) / (num_levels - 1), num_levels - 1) col_types <- stats::setNames( c(list( col_factor(levels = levels, prob = c(filt_p, rest_p))), rep(list(col_character()), cols - 1) ), make.names(seq_len(cols))) data <- gen_tbl(rows, cols, col_types = col_types) vroom_write(data, output, "\t") vroom/inst/bench/all_character-wide/readr-dplyr.R0000644000176200001440000000041014142512075021526 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-wide/vroom-base.R0000644000176200001440000000037714142512075021367 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-wide/vroom_no_altrep-dplyr.R0000644000176200001440000000045214142512075023644 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-wide/read.delim-base.R0000644000176200001440000000036714142512075022230 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-wide/data.table-data.table.R0000644000176200001440000000035714142512075023307 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 == "helpless_sheep", ] e <- x[ , .(mean(nchar(X2))), by = X1] vroom/inst/bench/session_info.R0000755000176200001440000000035414142512075016277 0ustar liggesusers#!/usr/bin/env Rscript vroom::vroom_write( sessioninfo::package_info(c("vroom", "readr", "dplyr", "data.table", "base"), dependencies = FALSE, include_base = TRUE), here::here("inst", "bench", "session_info.tsv"), delim = "\t" ) vroom/inst/bench/all_character-long/0000755000176200001440000000000014151431426017172 5ustar liggesusersvroom/inst/bench/all_character-long/vroom-dplyr.R0000644000176200001440000000043414142512075021610 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-long/input.R0000644000176200001440000000124614142512075020457 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") library(vroom) # We want ~ 1000 rows to filter num_levels <- 5 levels <- c("helpless_sheep", gen_name(num_levels - 1)) filt_p <- 1000 / rows # The prob for the rest should just be evenly spaced rest_p <- rep((1 - filt_p) / (num_levels - 1), num_levels - 1) col_types <- stats::setNames( c(list( col_factor(levels = levels, prob = c(filt_p, rest_p))), rep(list(col_character()), cols - 1) ), make.names(seq_len(cols))) data <- gen_tbl(rows, cols, col_types = col_types) vroom_write(data, output, "\t") vroom/inst/bench/all_character-long/readr-dplyr.R0000644000176200001440000000041014142512075021535 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-long/vroom-base.R0000644000176200001440000000037714142512075021376 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-long/vroom_no_altrep-dplyr.R0000644000176200001440000000045214142512075023653 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-long/read.delim-base.R0000644000176200001440000000036714142512075022237 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-long/data.table-data.table.R0000644000176200001440000000035714142512075023316 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 == "helpless_sheep", ] e <- x[ , .(mean(nchar(X2))), by = X1] vroom/inst/bench/fwf/0000755000176200001440000000000014151431426014233 5ustar liggesusersvroom/inst/bench/fwf/vroom-dplyr.R0000644000176200001440000000036214142512075016651 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom_fwf(file, fields, col_types = types) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, PERSONS == "06") e <- group_by(x, STATE) %>% summarise(avg_TOTPUM5 = mean(TOTPUMA5)) vroom/inst/bench/fwf/readr-dplyr.R0000644000176200001440000000036114142512075016603 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_fwf(file, fields, col_types = types) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, PERSONS == "06") e <- group_by(x, STATE) %>% summarise(avg_TOTPUM5 = mean(TOTPUMA5)) vroom/inst/bench/fwf/vroom-base.R0000644000176200001440000000031514142512075016427 0ustar liggesuserslibrary(vroom) x <- vroom_fwf(file, fields, col_types = types) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$PERSONS== "06", ] e <- tapply(x$TOTPUMA5, x$STATE, mean) vroom/inst/bench/fwf/vroom_no_altrep-dplyr.R0000644000176200001440000000040514142512075020712 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom_fwf(file, fields, col_types = types, altrep_opts = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, PERSONS == "06") e <- group_by(x, STATE) %>% summarise(avg_TOTPUM5 = mean(TOTPUMA5)) vroom/inst/bench/fwf/read.delim-base.R0000644000176200001440000000033414142512075017272 0ustar liggesusers({}) x <- read.fwf(file, widths = fields$width, col.names = fields$col_names) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$PERSONS== "06", ] e <- tapply(x$TOTPUMA5, x$STATE, mean) vroom/inst/bench/taxi_multiple.tsv0000644000176200001440000001533214151244612017072 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.049356014000000004 19747530143 63974539264 173179759 11 data.table data.table setup real 0.05027127265930176 19747530143 63974539264 173179759 11 data.table data.table read process 365.63006013800003 19747530143 63974539264 173179759 11 data.table data.table read real 95.29237675666809 19747530143 63974539264 173179759 11 data.table data.table print process 0.006093298999985564 19747530143 63974539264 173179759 11 data.table data.table print real 0.006696939468383789 19747530143 63974539264 173179759 11 data.table data.table head process 2.292589999797201e-4 19747530143 63974539264 173179759 11 data.table data.table head real 2.3102760314941406e-4 19747530143 63974539264 173179759 11 data.table data.table tail process 1.8573600004856416e-4 19747530143 63974539264 173179759 11 data.table data.table tail real 1.8715858459472656e-4 19747530143 63974539264 173179759 11 data.table data.table sample process 6.668740000463913e-4 19747530143 63974539264 173179759 11 data.table data.table sample real 6.690025329589844e-4 19747530143 63974539264 173179759 11 data.table data.table filter process 3.5333796889999576 19747530143 63974539264 173179759 11 data.table data.table filter real 1.0772333145141602 19747530143 63974539264 173179759 11 data.table data.table aggregate process 14.88072779800001 19747530143 63974539264 173179759 11 data.table data.table aggregate real 4.706362962722778 19747530143 63974539264 173179759 11 readr dplyr setup process 0.2852784450000001 19747530143 68142870528 173179759 11 readr dplyr setup real 0.2862823009490967 19747530143 68142870528 173179759 11 readr dplyr read process 475.032626874 19747530143 68142870528 173179759 11 readr dplyr read real 475.0412588119507 19747530143 68142870528 173179759 11 readr dplyr print process 0.4224021850000099 19747530143 68142870528 173179759 11 readr dplyr print real 0.836052656173706 19747530143 68142870528 173179759 11 readr dplyr head process 5.290510000008908e-4 19747530143 68142870528 173179759 11 readr dplyr head real 5.30242919921875e-4 19747530143 68142870528 173179759 11 readr dplyr tail process 5.061389999809762e-4 19747530143 68142870528 173179759 11 readr dplyr tail real 5.07354736328125e-4 19747530143 68142870528 173179759 11 readr dplyr sample process 0.014291614999990543 19747530143 68142870528 173179759 11 readr dplyr sample real 0.014293432235717773 19747530143 68142870528 173179759 11 readr dplyr filter process 4.235514479999949 19747530143 68142870528 173179759 11 readr dplyr filter real 4.235594272613525 19747530143 68142870528 173179759 11 readr dplyr aggregate process 13.503164607999963 19747530143 68142870528 173179759 11 readr dplyr aggregate real 13.503409147262573 19747530143 68142870528 173179759 11 vroom base setup process 0.24334018300000015 19747530143 94792810496 173179759 11 vroom base setup real 0.24334383010864258 19747530143 94792810496 173179759 11 vroom base read process 280.08802939099996 19747530143 94792810496 173179759 11 vroom base read real 20.344595670700073 19747530143 94792810496 173179759 11 vroom base print process 2.7922258899999974 19747530143 94792810496 173179759 11 vroom base print real 3.047116756439209 19747530143 94792810496 173179759 11 vroom base head process 2.6394799999707175e-4 19747530143 94792810496 173179759 11 vroom base head real 2.655982971191406e-4 19747530143 94792810496 173179759 11 vroom base tail process 4.8234499996624436e-4 19747530143 94792810496 173179759 11 vroom base tail real 4.837512969970703e-4 19747530143 94792810496 173179759 11 vroom base sample process 2.3426100000278893e-4 19747530143 94792810496 173179759 11 vroom base sample real 2.353191375732422e-4 19747530143 94792810496 173179759 11 vroom base filter process 21.519468974000006 19747530143 94792810496 173179759 11 vroom base filter real 21.5198495388031 19747530143 94792810496 173179759 11 vroom base aggregate process 162.64416488300003 19747530143 94792810496 173179759 11 vroom base aggregate real 142.6223108768463 19747530143 94792810496 173179759 11 vroom dplyr setup process 0.3229789220000001 19747530143 94496784384 173179759 11 vroom dplyr setup real 0.3229827880859375 19747530143 94496784384 173179759 11 vroom dplyr read process 281.055523347 19747530143 94496784384 173179759 11 vroom dplyr read real 20.365679502487183 19747530143 94496784384 173179759 11 vroom dplyr print process 2.542836164999983 19747530143 94496784384 173179759 11 vroom dplyr print real 2.785041570663452 19747530143 94496784384 173179759 11 vroom dplyr head process 9.97338999980002e-4 19747530143 94496784384 173179759 11 vroom dplyr head real 9.989738464355469e-4 19747530143 94496784384 173179759 11 vroom dplyr tail process 4.8055199999907927e-4 19747530143 94496784384 173179759 11 vroom dplyr tail real 4.818439483642578e-4 19747530143 94496784384 173179759 11 vroom dplyr sample process 0.01243754600000102 19747530143 94496784384 173179759 11 vroom dplyr sample real 0.012439489364624023 19747530143 94496784384 173179759 11 vroom dplyr filter process 23.858187990000033 19747530143 94496784384 173179759 11 vroom dplyr filter real 23.858795642852783 19747530143 94496784384 173179759 11 vroom dplyr aggregate process 65.63478174599999 19747530143 94496784384 173179759 11 vroom dplyr aggregate real 65.63620686531067 19747530143 94496784384 173179759 11 vroom_no_altrep dplyr setup process 0.317607953 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr setup real 0.31838297843933105 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr read process 607.194956447 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr read real 232.3293113708496 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr print process 2.005394704000082 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr print real 2.162087917327881 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr head process 0.0010485940000535265 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr head real 0.0010504722595214844 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr tail process 4.88735000089946e-4 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr tail real 4.901885986328125e-4 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr sample process 0.0136381549999669 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr sample real 0.013639688491821289 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr filter process 10.491835902000048 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr filter real 10.491984844207764 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr aggregate process 7.2028114820000155 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr aggregate real 7.202939510345459 19747530143 67751387136 173179759 11 vroom/inst/bench/taxi/0000755000176200001440000000000014151431426014416 5ustar liggesusersvroom/inst/bench/taxi/vroom-dplyr.R0000644000176200001440000000051214142512075017031 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi/readr-dplyr.R0000644000176200001440000000046614142512075016774 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_csv(file, col_types = c(pickup_datetime = "c"), quote = "", trim_ws = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi/vroom-base.R0000644000176200001440000000044014142512075016611 0ustar liggesuserslibrary(vroom) x <- vroom(file, col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$payment_type == "UNK", ] e <- tapply(x$tip_amount, x$payment_type, mean) vroom/inst/bench/taxi/vroom_no_altrep-dplyr.R0000644000176200001440000000053014142512075021074 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi/read.delim-base.R0000644000176200001440000000037514142512075017462 0ustar liggesusers({}) x <- read.delim(file, sep = ",", quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$payment_type == "UNK", ] e <- tapply(x$tip_amount, x$payment_type, mean) vroom/inst/bench/taxi/data.table-data.table.R0000644000176200001440000000037014142512075020535 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = ",", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[payment_type == "UNK", ] e <- x[ , .(mean(tip_amount)), by = payment_type] vroom/inst/bench/summarise-benchmarks.R0000755000176200001440000000354314142512075017724 0ustar liggesuserslibrary(vroom) library(dplyr) library(fs) library(purrr) library(tidyr) summarise_dir <- function(dir, desc) { out_file <- path(path_dir(dir), path_ext_set(path_file(dir), "tsv")) col_types <- cols( exprs = col_character(), process = col_character(), real = col_character(), size = col_double(), rows = col_double(), cols = col_double() ) dir_ls(dir, glob = "*tsv") %>% discard(~endsWith(.x, "input.tsv")) %>% vroom(id = "path", col_types = col_types) %>% mutate(path = path_ext_remove(path_file(path))) %>% group_by(path) %>% mutate(op = desc) %>% separate(path, c("reading_package", "manip_package"), "-") %>% pivot_longer(., cols = c(process, real), names_to = "type", values_to = "time") %>% select(reading_package, manip_package, op, type, time, size, max_memory, rows, cols) %>% vroom_write(out_file, delim = "\t") } summarise_dir(here::here("inst/bench/all_numeric-long"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/all_numeric-wide"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/all_character-long"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/all_character-wide"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/taxi"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/taxi_multiple"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/taxi_writing"), c("setup", "writing")) summarise_dir(here::here("inst/bench/fwf"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) vroom/inst/bench/script.sh0000755000176200001440000000400714142512075015315 0ustar liggesusers#!/bin/bash # need to run download-data.sh before this script # don't forget to set your GITHUB_PAT before running this script [ -n "${GITHUB_PAT}" ] || { echo >&2 "Set GITHUB_PAT first!"; exit 1; } # Install R runtime prereqs sudo apt-get update -qq && \ DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ apt-transport-https \ build-essential \ curl \ gfortran \ libatlas-base-dev \ libbz2-dev \ libcairo2 \ libcurl4-openssl-dev \ libicu-dev \ liblzma-dev \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libpcre3-dev \ libssl-dev \ libssh2-1-dev \ libtcl8.6 \ libtiff5 \ libtk8.6 \ libx11-6 \ libxml2-dev libxt6 \ locales \ sudo \ tzdata \ wget \ zlib1g-dev && \ sudo rm -rf /var/lib/apt/lists/* OS_IDENTIFIER=ubuntu-1804 R_VERSION=3.6.0 # download precompiled R binary and install wget -O R-${R_VERSION}.tar.gz https://cdn.rstudio.com/r/${OS_IDENTIFIER}/R-${R_VERSION}-${OS_IDENTIFIER}.tar.gz sudo mkdir -p /opt/R sudo chown ubuntu /opt/R tar zx -C /opt/R -f ./R-${R_VERSION}.tar.gz rm R-${R_VERSION}.tar.gz export PATH=/opt/R/${R_VERSION}/bin:$PATH # clone vroom git clone https://jimhester:${GITHUB_PAT}/github.com/r-lib/vroom.git # download and extract data vroom/inst/bench/download-data.sh & cd vroom # set common Rprofile options echo "options(repos = 'https://cran.rstudio.org', Ncpus = 16)" > ~/.Rprofile # install R dependencies Rscript -e 'install.packages("remotes")' \ -e 'remotes::install_local(dependencies = TRUE)' # install additional packages for benchmarking Rscript -e 'remotes::install_cran(c("data.table", "callr", "here", "sessioninfo"))' sudo apt-get install -y pigz zstd # wait for download job to finish wait make bench PKG_VERSION=$(perl -ne 'print $1 if /Version: (.*)/' DESCRIPTION) git checkout -b bench/R-${R_VERSION}_vroom-${PKG_VERSION} git config --global user.email "james.f.hester@gmail.com" git config --global user.name "Jim Hester" git commit -m 'Update benchmarks' inst/bench/*tsv vroom/inst/words/0000755000176200001440000000000014142512075013530 5ustar liggesusersvroom/inst/words/animals.txt0000644000176200001440000000232114142512075015713 0ustar liggesusersaardvark addax alligator alpaca anteater antelope aoudad ape argali armadillo baboon badger basilisk bat bear beaver bee beetle bighorn bird bison boar budgerigar buffalo bull bunny burro camel canary capybara cat chameleon chamois cheetah chicken chimpanzee chinchilla chipmunk civet coati colt cougar cow coyote crab crocodile crow deer devil dingo doe dog donkey dormouse dromedary dugong eagle eland elephant elk ermine ewe fawn ferret finch fish fox frog gazelle gemsbok giraffe gnu goat gopher gorilla guanaco hamster hare hartebeest hedgehog hippopotamus hog horse hyena ibex iguana impala jackal jaguar jerboa kangaroo kitten koala lamb lemur leopard lion lizard llama lovebird lynx mandrill mare marmoset marten meerkat mink mole mongoose monkey monster moose mouse mule musk-ox muskrat mustang newt ocelot octopus okapi opossum orangutan oryx otter owl ox panda panther parakeet parrot peccary pig platypus pony porcupine porpoise pronghorn puma puppy quagga rabbit raccoon ram rat reindeer rhinoceros rooster salamander seal sheep shrew skunk sloth snake springbok squirrel stallion starfish steer tapir tiger toad turtle vicuna walrus warthog waterbuck weasel whale wildcat wolf wolverine wombat woodchuck yak zebra zebu vroom/inst/words/adjectives.txt0000644000176200001440000000271214142512075016414 0ustar liggesusersable abundant adorable agreeable alive ancient angry bad beautiful better bewildered big bitter black blue boiling brave breeze brief broad broken bumpy calm careful chilly chubby clean clever clumsy cold colossal cooing cool creepy crooked cuddly curly curved damaged damp dead deafening deep defeated delicious delightful different dirty drab dry dusty eager early easy elegant embarrassed empty faint faithful famous fancy fast fat few fierce filthy first flaky flat fluffy freezing fresh full gentle gifted gigantic glamorous good gray greasy great green grumpy handsome happy heavy helpful helpless high hissing hollow hot huge icy immense important inexpensive itchy jealous jolly juicy kind large last late lazy light little lively long loose loud low magnificent mammoth many massive melodic melted miniature modern mushy mysterious narrow nervous new next nice noisy numerous nutritious obedient obnoxious odd old orange other own panicky petite plain powerful prickly proud public puny purple purring quaint quick quiet rainy rapid raspy red relieved repulsive rich right rotten round salty same scary scrawny screeching shallow short shy silly skinny slow small sparkling sparse square steep sticky straight strong substantial sweet swift tall tart tasteless teeny tender thankful thoughtless thundering tiny ugliest uneven uninterested unsightly uptight vast victorious voiceless warm weak wet whispering white wide witty wooden worried wrong yellow young yummy zealous vroom/inst/WORDLIST0000644000176200001440000000131714151154756013576 0ustar liggesusersafl Altrep Altrepisode AppVeyor backslashed BCP benchmarking bool BOM Bugfixes bz bzip centric chriswhong cpp CMD CRAN's Codecov csv datetime de dbi delim durations Dowle dplyr envvar extdata FranΓ§ois fread funder fwf Gabe gcc github grisu gz gzip gzipped http https Kalibera KiB knitr lbzip libc libstdc Lifecycle macOS mandreyel mio mis mtcars multithreaded natively nyc nycflights NUL Only’ parsers pigz pixz POSIXct Preprocess purrr Rcpp Rdatatable readr recognised relicensing rmarkdown Romain RStudio's RStudio’s stringi testthat tibble tidyverse Tierney tokenizer tsv tz tzdb unicode unterminated UNK xz rchk UBSAN EBS FivePercent Microdata ORCID PUMS vCPUs www deterministically UseR xlarge xzip Zstandard