Changelog
Source:NEWS.md
kit 0.0.24 (development version)
Bug Fixes
Fix
vswitch()/nswitch()withcheckEnc = TRUEwhen the first string is already UTF-8 but later elements are latin1 orNA:enc2UTF8()now checks every element and preservesNA_STRING, and value-majornswitch()compares each pair against its own converted value instead of the first one.Fix
funique()/fduplicated()/uniqLen()hashing-0and differingNaNpayloads as distinct doubles. Vector, matrix and data-frame paths now canonicalise doubles the same wayREQUALcompares them, matching base R.
Notes
Speed up
vswitch()andnswitch()when there are many value/output pairs. For more than 8 pairs the implementation builds a value-to-last-pair hash map once and scansxin cache-sized blocks instead of comparingxagainst every value. Matching semantics are unchanged: integer/logicalNAstill matchesNA, doubles/complex canonicalise-0and never matchNA/NaN, strings compareCHARSXPidentity, duplicate values keep the last pair, and threaded results stay deterministic. On 2m elements, 20 values are about 2-2.5x faster and 100 values 4.5-5.7x faster; the value-major path is kept for 8 or fewer pairs.Speed up
funique()/fduplicated()/uniqLen()on data frames and matrices. Row hashes now use a 64-bit rolling mix instead of mixing every cell and mixing again, and a bounded sample estimates cardinality so high-cardinality inputs allocate the final table once while low-cardinality inputs keep the small grow-on-demand table. On 10m rows, 2-column integer data frames improve about 2.5x and mixed integer/double frames about 2.3-2.5x; 2m frames improve 1.5-1.8x. The scalar vector path was left unchanged: it is already faster thandata.table::uniqueN()and dedicated hash variants stayed under the 20% threshold.Add
tools/benchmark-core.R, a manual benchmark covering 2m/10m elements, 5/20/100 switch values, integer/string/factor/NA inputs, data frames, and 1/2/4 threads. It is not run in CI.
kit 0.0.23 (2026-11-02)
Bug Fixes
Fix
nswitch()with duplicate values andnThread > 1: the outer loop over value/output pairs is now serial and atomic-output pairs are parallelised over the elements ofx, so the last matching pair wins deterministically. Also fix the list-input/logical-output path reading output elementiinstead ofj(#63 follow-up).Fix
fduplicated()on logical vectors longer thanINT_MAX: the generic path used anintloop index and storedi + 1in aninttable slot, so a multi-gigabyte logical vector could overflow and read out of bounds. It now raises the standard “Long vector not supported yet” error (#61 follow-up).Apply
xtfrmto classed vectors before thetopn()long-vector heap path, so custom classes followorder()semantics there too; classed long vectors whosextfrmis not integer/double now error clearly instead of falling back to the integer-indexorder()path (#83 follow-up).Fix
nswitch()segfaulting when called without value/output pairs (e.g.nswitch(1:3)ornswitch(1:3, default = 0L)): the argument count is now validated before any pair is read, and the call errors with a clear message (#87).Evaluate the internal
order()/sort()calls intopn,psort, andcharToFactin the base environment. A caller-definedorder/sortcould previously shadow them and maketopnreturn out-of-bounds indices orpsort/charToFacterror or sort incorrectly (#74).Fix shared-memory lifetime and error-path safety in
shareData(): POSIX mapping names are now owned copies, long-vector lengths, allocation sizes, and existing mapping sizes are checked, finalizer verbosity is per mapping, failed mappings release descriptors, mappings, and temporary state, reader cleanup is unwind-safe, and finalizers verify mapping identity before unlinking owned names (#55).Fix segfault in
vswitch/nswitchon length-0 character input: the character encoding path dereferenced element 0 without checking length. The encoding check is now skipped for emptyxandisMixEnc/enc2UTF8handle empty input safely, with no overhead on the fast path (#66).Fix
.onAttach/.onLoadcrash on badkit.nThread(NULL/NA/length != 1): the option is now sanitized to1L, the default is restored viais.null(getOption()), and the startup message only prints in interactive sessions (#56).Keep
nswitch’s per-output recycle mask on the heap instead of the C stack, matchingvswitch, so a very large number of value/output pairs cannot overflow the stack (#74).Fix
shareData/getData/clearDatainput validation: badmap_name(""/NA/length != 1/non-character) andverbose(NA/non-logical/length != 1) now fail fast in R with a clear message instead of reachingshm_openand its cryptic error; connections are closed viaon.exiteven whenserialize/unserializefails (#59).Audit compiled length handling for 64-bit long vectors:
IS_BOOL/IS_LOGICALand the characterpsort/charToFactpaths now useXLENGTH()(long character vectors are refused with the standard “Long vector not supported yet” error before any data is read),funique/uniqLenhandle > 2^31 elements for factors and logicals,topnon long inputs returns double indices likebase::order,subSetRowMatrixrow indexing no longer overflows for matrices with >= 2^31 cells, and byte-size casts that truncated above 4 GiB are fixed (#61).Fix two segfaults found while auditing length handling (#61):
funique/uniqLenon factors containingNAread the count table out of bounds (theNAcode is now kept in its own slot and missing elements are returned in the same order asbase::unique/length(unique())), anddfToMatrixrefused to bound-check a flatintindex, sofunique/uniqLenon data.frames with more thanINT_MAXcells (e.g. 50k columns) wrote out of bounds; such data.frames now raise the standard “Long vector not supported yet” error. Factor codes outside1..nlevels(e.g. afterattr(x, "levels")surgery) no longer index the count table out of bounds either and are returned asNAlike base; distinct invalid codes collapse into one missing value.Guard the mixed-type data.frame paths in
funique/fduplicated(dupDataFrameR),uniqLen(dupLenDataFrameR), andcountOccur(countOccurDataFrameR): with more thanINT_MAXrows the row count was truncated tointinsideallocMatrix(), so 231-232 rows errored with R’s confusing “negative extents to matrix” message (and above 2^32 rows the mixedfunique/uniqLenlogical-column case could write out of bounds, which the per-column 2^30 guard only prevents for other types). These paths now stop up front with the standard “Long vector not supported yet” error, before any allocation, when the rows exceedINT_MAXor the matrix would exceedINT_MAXcells — the same bound the all-same-type branch has applied todfToMatrix()since the length audit (#61), so the same data.frame now fails the same way regardless of whether its columns share one type (#82).
Notes
Mix hashed keys with a 32-bit avalanche before indexing the growable duplicate tables. The previous top-bits-only multiplicative hash mapped sequential integer IDs to only ~16% of the slots in a power-of-two table, so
funique()/fduplicated()/uniqLen()fell back to long linear-probe scans on common ID-like data. The tables still start at 256 slots and grow on demand; addingtools/benchmark-unique.Rkeeps this case visible.Document
?kitwith a package page that explains OpenMP threads, platform support, encoding behavior, and the main functions. Add “OpenMP and threads” and “Platforms” sections to the README, and addlibrary(kit)to the first README example (#90).Reduce peak memory in
funique,fduplicated,uniqLen,psort, andcharToFactby starting duplicate hash tables at 256 slots and growing them on demand (4x, capped at the previous full-size table) instead of allocating the full table up front. Rehashing re-reads the source values, so results are unchanged; inputs with few unique values use much less memory, while peak memory can be higher at growth boundaries for high-cardinality inputs (#74).Speed up
vswitchfor string and list outputs by scanningxonce instead of once per value and fusing default/nawrites into the match pass; single-value lookups also avoid a separate initialization pass. Atomic outputs keep the value-major vectorized pass, with the first value fused into initialization. Last-match-wins behavior for duplicatevaluesis preserved, mixed output item types now fail with a clear error, and list-output scratch arrays are heap allocated so large outputs no longer exhaust the C stack (#74).Bound the
topnlinear candidate scan: input ordered opposite todecreasingmade every element replace the current worst and rescan allncandidates, son = 1500on 2e7 elements took ~14.5 s. The scan now falls back to the existing radixorder()path once its rescan work exceeds a few passes over the input, reducing that case to ~0.11 s while leaving random and small-ninputs on the fast path. The fallback allocates a full-lengthorderindex, so peak memory is higher on adversarial inputs. Classed numeric vectors havextfrmapplied once and then use the same fast selector, so a custom class ordering matchesorder()without forcing a full radix sort for identity classes such asDate,POSIXct, and factors (#74).Serialize
shareDatapayloads directly to a raw vector instead of through a raw connection, removing one full-payload copy on write and connection overhead on read; the shared-memory format is unchanged (#74).Reduce
topnmemory use on long vectors (more thanINT_MAXelements): the long path allocated a full-length radixorder()index (~8 x length bytes, ~17 GB for a 2^31-element input, only to return n indices) and is replaced by a bounded heap of the n best (value, index) entries, so memory is now proportional to n; unmaterialized ALTREP inputs are read element-wise without being materialized. The result is unchanged — double indices likeorder(vec, method="radix", na.last=TRUE)[1:n], ties and missing values keeping input order regardless ofhasna— anddecreasing=TRUEnow works on such inputs, where theorder()path errored with base R’s “long vectors not supported” (#83).Document the
shareData/getData/clearDatacontract in?shareData: deep-copy cost (object serialized once, then copied into and out of shared memory, not a pointer), single-readgetData, lifetime and cleanup semantics, platform differences, and a two-session\dontrunexample; cross-links the zero-copy request (#43) (#60).Clean up
DESCRIPTIONmetadata: bumpDependstoR (>= 3.5.0)to match theDATAPTR_ROfallback insrc/kit.h(...length(),R_RegisterCCallable/STRING_PTR_ROneed newer than 3.1), useByteCompile: yesper WRE, dropRepository: CRANfrom the dev tree, and addConfig/Needs/website: pkgdown(Suggestsalready coversdata.table/tibble/tinytestused in tests and vignette) (#58).The test suite was migrated to tinytest: the legacy custom
check()only printed failures without stopping, so regressions went unnoticed. Tests now fail loudly (1271 expectations) with zero additional dependencies.Replace the remaining
Rbooleanscalars in the C sources withbool(callToOrder,dupMatrixR,topnR, and thehasFactorflags inpsum), keepingasLogical()as the R-value boundary.Rbooleanis only intended for theTRUE/FALSEenum and misbehaves for logical vector elements understdbool.hand C23, as reported by @aitap in #42. TheLGLSXP/INTSXPbranches insubSetColMatrixare merged sinceLOGICAL()andINTEGER()are bothint*(#62).Reduce a redundant initialization pass in multi-argument
psum(..., na.rm = TRUE)for integer, double, and complex inputs while preserving missing-value semantics (#74).New test coverage for
fpmin/fpmax/prange(NA/NaN+na.rm,logical < integer < doublepromotion,prangedouble-only rule, single list/data.framepath),kit.nThread = 1vs2determinism (iif/vswitch/nswitch/charToFact/psort), and mixed-encodingcheckEnc,funiquedata.table/tibbleattributes,Date/factor preservation and empty inputs (#63, tests-only, no C changes). Coverage now requires > 90% via Codecov.Add a root
.gitignorefor build artifacts (src/*.o,src/kit.so, thesrc/Makevarsthatconfiguregenerates,*.tar.gz,*.Rcheck/,cobertura.xml,docs/, and R session junk), so an in-treeR CMD INSTALLno longer leaves 13 untracked files that a straygit add -Awould commit. Thesrc/Makevarsrule is narrow so the trackedsrc/Makevars.inandsrc/Makevars.winstay visible, and the trackedcleanupscript is deliberately not ignored: it is what keeps the generatedMakevarsout of the distribution tarball (#78).
kit 0.0.22 (2026-08-25)
Bug Fixes
Fix
shareDataandgetDataon platforms whereshm_openrequires names to start with a slash, e.g. FreeBSD. Shared memory object names are now normalized on all POSIX platforms as recommended by POSIX. Thanks to @nunotexbsd for raising an issue (#40).Fix a mapping leak in
getDatawhere the wrong region was unmapped during cleanup.
kit 0.0.21 (2026-01-17)
CRAN release: 2026-01-23
New Features
- Functions
fpmin,fpmax, andprangeare added as parallel equivalents of base Rpmin,pmax, and range operations. These functions compute element-wise minimum, maximum, and range (max - min) across multiple vectors. They support integer, logical, and double types, with automatic type promotion. Implemented by @SebKrantz.
Notes
- The GitHub repo move to fastverse/kit, a new website exists at fastverse.org/kit, and Sebastian Krantz and the fastverse Team have taken responsibility to maintain the package. Morgan Jacob has stepped down, but remains available for consultation.
kit 0.0.14 (2023-08-12)
CRAN release: 2023-08-12
Notes
Update configure file to extend support for GCC
Correct warnings in NEWS.Rd (strong)
Correct typo in funique.Rd thanks to @davidbudzynski
kit 0.0.13 (2023-02-24)
CRAN release: 2023-02-24
Notes
Function
pprodnow returns double output even if inputs are integer - in line withbase::prod- to avoid integer overflows.Update configure file
kit 0.0.12 (2022-10-26)
CRAN release: 2022-10-26
New Features
Function
pcountNAis equivalent topcount(..., value = NA).Function
pcountNAandpcount(..., value = NA)allowNAcounting with mixed data type (includingdata.frame).pcountNAalso supports list-vectors as inputs and counts empty orNULLelements asNA.Functions
panyv,panyNA,pallvandpallNAare added as efficient wrappers aroundpcountandpcountNA. They are parallel equivalents of scalar functionsbase::anyNAandanyv,allvandallNAin the ‘collapse’ R package.Functions
pfirstandplastare added to efficiently obtain the row-wise first and last non-missing value or non-empty element of lists. They are parallel equivalents to the (column-wise)ffirstandflastfunctions in the ‘collapse’ R package. Implemented by @SebKrantz.Functions
psum/pprod/pmeanalso support logical vectors as input. Implemented by @SebKrantz.
Bug Fixes
- Function
charToFactwas not returning proper results. Thanks to @alex-raw for raising an issue.
Notes
Function
pprodnow returns double output even if inputs are integer - in line withbase::prod- to avoid integer overflows.C compiler warnings on CRAN R-devel caused by compilation with -Wstrict-prototypes are now fixed. Declaration of functions without prototypes is depreciated in all versions of C. Thanks to Sebastian Krantz for the PR.
kit 0.0.10 (2021-11-28)
CRAN release: 2021-11-28
New Features
- Function
psum,pmean,pprod,panyandpallnow support lists. Thanks to Sebastian Krantz for the request and code suggestion.
Bug Fixes
- Function
topnshould now work for ALTREP object. Thanks to @ben-schwen for raising an issue.
kit 0.0.8 (2021-08-21)
CRAN release: 2021-08-17
New Features
Function
funiquenow preserves the attributes if the input is adata.table,tibbleor similar objects. Thanks to Sebastian Krantz for the request.Function
topnnow defaults to base Rorderfor large value ofn. Please see updated documentation for more information?kit::topn.Function
charToFactgains a new argumentaddNA=TRUEto be used to include (or not)NAin levels of the output.Function
shareData,getDataandclearDataimplemented to share data objects between R sessions. These functions are experimental and might change in the future. Feedback is welcome. Please see?kit::shareDatafor more information.
kit 0.0.7 (2021-03-07)
CRAN release: 2021-03-09
New Features
Function
charToFactgains a new argumentdecreasing=FALSEto be used to order levels of the output in decreasing or increasing order.Function
topngains a new argumentindex=TRUEto be used return index (TRUE) or values (FALSE) of input vector.
kit 0.0.6 (2021-02-21)
CRAN release: 2021-02-21
New Features
Function
funiqueandfduplicatedgain an additional argumentfromLast=FALSEto indicate whether the search should start from the end or beginning PR#11.Functions
pall,pany,pmean,pprodandpsumacceptdata.frameas input PR#15. Please see documentation for more information.Function
charToFactis equivalent to to base Ras.factorbut is much quicker and only converts character vector to factor. Note that it is parallelised. For more details and benchmark please see?kit::charToFact.Function
psortis experimental and equivalent to to base Rsortbut is only for character vector. It can sort by “C locale” or by “R session locale”. For more details and benchmark please see?kit::psort.
Notes
A few OpenMP directives were missing for functions
vswitchandnswitchfor character vectors. These have been added in PR#12.Function
funiquewas not preserving attributes for character, logical and complex vectors/data.frames. Thanks to Sebastian Krantz (@SebKrantz) for bringing that to my attention. This has been fixed in PR#13.Functions
funiqueanduniqLenshould now be faster forfactorandlogicalvectors PR#14.
kit 0.0.5 (2020-11-21)
CRAN release: 2020-11-21
New Features
Function
uniqLen(x)is equivalent to base Rlength(unique(x))anduniqueNin package data.table. FunctionuniqLen, implemented in C, supports vectors,data.frameandmatrix. It should be faster than these functions. For more details and benchmark please see?kit::uniqLen.Function
vswitchnow supports mixed encoding and gains an additional argumentcheckEnc=TRUE. Thanks to Xianying Tan (@shrektan) for the request and review PR#7.Function
nswitchis a nested version of functionvswitchand also supports mixed encoding. Please see please see?kit::nswitchfor further details. Thanks to Xianying Tan (@shrektan) for the request and review PR#10.
kit 0.0.4 (2020-07-21)
CRAN release: 2020-07-21
New Features
Function
countOccur(x), implemented in C, is comparable tobaseR functiontable. It returns adata.frameand is between 3 to 50 times faster. For more details, please see?kit::countOccur.Functions
funiqueandfduplicatednow support matrices. Additionally, these two functions should also have better performance compare to previous release.Functions
topnhas an additional argumenthasna=TRUEto indicates whether data containsNAvalue or not. If the data does not containNAvalues, the function should be faster.
C-Level Facilities
- A few C functions have been added to subset
data.frameandmatrixas well as do other operations. These functions are not exported or visible to the user but might become available and callable at C level in the future.
Bug Fixes
Function
fposwas not properly handlingNaNandNAfor complex and double. This should now be fixed. The function has also been changed in case the ‘needle’ and ‘haysatck’ are vectors so that a vector is returned.Functions
funiqueandfduplicatedwere not properly handling data containingPOSIXdata. This has now been fixed.
kit 0.0.3 (2020-06-21)
CRAN release: 2020-06-26
New Features
Functions
fduplicated(x)andfunique(x), implemented in C, are comparable tobaseR functionsduplicatedandunique. For more details, please see?kit::funique.Functions
psumandpprodhave now better performance for type double and complex.
kit 0.0.2 (2020-05-22)
CRAN release: 2020-05-24
New Features
Function
count(x, value), implemented in C, to simply count the number of times an elementvalueoccurs in a vector or in a listx. For more details, please see?kit::count.Function
pmean(..., na.rm=FALSE),pall(..., na.rm=FALSE),pany(..., na.rm=FALSE)andpcount(..., value), implemented in C, are similar to already available functionpsumandpprod. These functions respectively apply base R functionsmean,allandanyelement-wise. For more details, benchmarks and help, please see?kit::pmean.
kit 0.0.1 (2020-05-03)
CRAN release: 2020-05-08
Initial Release
Function
fpos(needle, haystack, all=TRUE, overlap=TRUE), implemented in C, is inspired by base functionwhichwhen used in the following formwhich(x == y, arr.ind =TRUE). Functionfposreturns the index(es) or position(s) of a matrix/vector within a larger matrix/vector. Please see?kit::fposfor more details.Function
iif(test, yes, no, na=NULL, tprom=FALSE, nThread=getOption("kit.nThread")), originally contributed asfifelsein package data.table, was moved to package kit to be developed independently. Unlike the current version offifelse,iifallows type promotion like base functionifelse. For further details about the differences withfifelse, as well ashutils::if_elseanddplyr::if_else, please see?kit::iif.Function
nif(..., default=NULL), implemented in C, is inspired by SQL CASE WHEN. It is comparable to dplyr functioncase_whenhowever it evaluates it arguments in a lazy way (i.e only when needed). Functionnifwas originally contributed as functionfcasein the data.table package but then moved to package kit so its development may resume independently. Please see?kit::niffor more details.Function
pprod(..., na.rm=FALSE)andpsum(..., na.rm=FALSE), implemented in C, are inspired by base functionpminandpmax. These new functions work only for integer, double and complex types and do not recycle vectors. Please see?kit::psumfor more details.Function
setlevels(x, old, new, skip_absent=FALSE), implemented in C, may be used to set levels of a factor object. Please see?kit::setlevelsfor more details.Function
topn(vec, n=6L, decreasing=TRUE), implemented in C, returns the top largest or smallestnvalues for a given numeric vectorvec. It is inspired bydplyr::top_nand equivalent to base functions order and sort in specific cases as shown in the documentation. Please see?kit::topnfor more details.Function
vswitch(x, values, outputs, default=NULL, nThread=getOption("kit.nThread")), implemented in C, is a vectorised version ofbaseR functionswitch. This function can also be seen as a particular case of functionnif. Please see?kit::switchfor more details.