UNPKG

confusablematcher-js-interop

Version:

TypeScript NAPI Interop for ConfusableMatcher.

428 lines (339 loc) 18.4 kB
# TypeScript NAPI Interop for Confusable Matcher This library exports a wrapper class bundled with TypeScript declarations that allows you to use the C++ ConfusableMatcher inside your JavaScript (NodeJS/CommonJS) applications. <!-- TOC depthFrom:2 --> - [1. Installation](#1-installation) - [2. Usage](#2-usage) - [2.1. new ConfusableMatcher()](#21-new-confusablematcher) - [2.1.1. addMapping(key, value): void](#211-addmappingkey-value-void) - [2.1.2. addMappings(mappings): void](#212-addmappingsmappings-void) - [2.1.3. removeMapping(key, value): void](#213-removemappingkey-value-void) - [2.1.4. removeMappings(mappings): void](#214-removemappingsmappings-void) - [2.1.5. getMappings(): Mapping[]](#215-getmappings-mapping) - [2.1.6. getKeyMappings(value): string[]](#216-getkeymappingsvalue-string) - [2.1.7. addSkip(skip): void](#217-addskipskip-void) - [2.1.8. addSkips(skips): void](#218-addskipsskips-void) - [2.1.9. removeSkip(skip): void](#219-removeskipskip-void) - [2.1.10. removeSkips(skips): void](#2110-removeskipsskips-void) - [2.1.11. getSkips(): string[]](#2111-getskips-string) - [2.1.12. computeStringPosPointers(needle): number](#2112-computestringpospointersneedle-number) - [2.1.13. freeStringPosPointers(pointer): void](#2113-freestringpospointerspointer-void) - [2.1.14. indexOfSync(input, needle, options): IResult](#2114-indexofsyncinput-needle-options-iresult) - [2.1.15. indexOf(input, needle, options): Promise<IResult>](#2115-indexofinput-needle-options-promiseiresult) - [2.1.16. lastIndexOfSync(input, needle, options): IResult](#2116-lastindexofsyncinput-needle-options-iresult) - [2.1.17. lastIndexOf(input, needle, options): Promise<IResult>](#2117-lastindexofinput-needle-options-promiseiresult) - [2.1.18. containsSync(input, needle, options): boolean](#2118-containssyncinput-needle-options-boolean) - [2.1.19. contains(input, needle, options?): Promise<boolean>](#2119-containsinput-needle-options-promiseboolean) - [2.1.20. indexOfDebugFailuresSync(input, needle, options?): string[]](#2120-indexofdebugfailuressyncinput-needle-options-string) - [2.1.21. indexOfDebugFailures(input, needle, options?): Promise<string[]>](#2121-indexofdebugfailuresinput-needle-options-promisestring) - [2.1.22. indexOfDebugFailuresExSync(input, needle, options?): IDebugFailureResult](#2122-indexofdebugfailuresexsyncinput-needle-options-idebugfailureresult) - [2.1.23. indexOfDebugFailuresEx(input, needle, options?): Promise<IDebugFailureResult>](#2123-indexofdebugfailuresexinput-needle-options-promiseidebugfailureresult) - [3. Development](#3-development) - [3.1. Testing](#31-testing) - [3.2. Benchmarks](#32-benchmarks) <!-- /TOC --> ## 1. Installation **To install this module, CMake v3.0 or higher must be present on the system.** ```bash yarn add confusablematcher-js-interop ``` ## 2. Usage The example below shows explicit typings which are not necessary, and can be inferred, but highlights the types you may wish to use in your application. ```ts import type { IIndexOfOptions, IResult, Mapping, StrPosPointer } from 'confusablematcher-js-interop'; import { ConfusableMatcher, EReturnStatus } from 'confusablematcher-js-interop'; const map: Mapping[] = [['Z', 'Ž']]; const skips: string[] = [' ', '_', '-']; const cm = new ConfusableMatcher(map, skips, true); const input = 'Žebras are a short, stocky animal that is generally about 8 feet long and stands between 4 and 5 feet at the shoulder.'; const needle = 'Zebras'; let strPosPtrs: StrPosPointer | undefined = cm.computeStringPosPointers(needle); const options: Partial<IIndexOfOptions> = { matchOnWordBoundary: true, matchRepeating: true, needlePosPointers: strPosPtrs, startFromEnd: false, startIndex: 0, timeoutNs: 1e6, // 1 ms }; const result: IResult = cm.indexOfSync(input, needle, options); cm.freeStringPosPointers(strPosPtrs); strPosPtrs = undefined; delete options.needlePosPointers; const status: EReturnStatus = result.status; console.table({ ...result, status: EReturnStatus[status] }); ``` **It is important to realise the distinction between the synchronous and asynchronous methods.** **Async methods are considerably slower than the sync versions, however they are [non-blocking](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/#don-t-block-the-event-loop) by utilizing the [NodeJS thread pool](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/#what-code-runs-on-the-worker-pool).** **[The thread pool size can be changed by setting the `UV_THREADPOOL_SIZE` environment variable](https://nodejs.org/api/cli.html#cli_uv_threadpool_size_size). One thread per physical CPU core is recommended, and the default is four.** **Keep in mind more threads is more context switches, setting a higher thread count that core count will not give you insane performance - you will have to tune your application for it's runtime environment.** ### 2.1. new ConfusableMatcher() ```ts /** * @param maps An array of [Key, Value] tuples. A key is the character to look for, a value is the resulting map. * @param skips An iterable of characters to skip. * @param addDefaultValues Whether to add ASCII characters and spaces to the map. */ new ConfusableMatcher(maps: Mapping[] = [], skips: Iterable<string> = [], addDefaultValues = true) ``` #### 2.1.1. addMapping(key, value): void ```ts /** * @description Adds a single map. * @param key The value to look for. * @param value The replacement value. */ addMapping(key: string, value: string): void ``` #### 2.1.2. addMappings(mappings): void ```ts /** * @description Adds multiples maps. * @param mappings An array of [Key, Value] tuples. A key is the character to look for, a value is the resulting map. */ addMappings(mappings: Iterable<Mapping>): void ``` #### 2.1.3. removeMapping(key, value): void ```ts /** * @description Removes a single map. * @param key The value to look for. * @param value The replacement value. */ removeMapping(key: string, value: string): void ``` #### 2.1.4. removeMappings(mappings): void ```ts /** * @description Removes multiples maps. * @param mappings An array of [Key, Value] tuples. A key is the character to look for, a value is the resulting map. */ removeMappings(mappings: Mapping[]): void ``` #### 2.1.5. getMappings(): Mapping[] ```ts /** * @returns An array of maps. */ getMappings(): Mapping[] ``` #### 2.1.6. getKeyMappings(value): string[] This method returns all of the mappings for a target value. For example: using the map `['Z', 'Ž']` and calling `getKeyMappings('Ž')` would return `['Z'].` ```ts /** * @returns Map key's for a value. */ getKeyMappings(value: string): string[] ``` #### 2.1.7. addSkip(skip): void ```ts /** * @description Adds a single skip. * @param skip The value to skip. */ addSkip(skip: string): void ``` #### 2.1.8. addSkips(skips): void ```ts /** * @description Adds multiple skips. * @param skips An array of values to skip. */ addSkips(skips: string[]): void ``` #### 2.1.9. removeSkip(skip): void ```ts /** * @description Removes a single skip. * @param skip The skip value to remove. */ removeSkip(skip: string): void ``` #### 2.1.10. removeSkips(skips): void ```ts /** * @description Removes multiple skips. * @param skips An array of skip values to remove. */ removeSkips(skips: string[]): void ``` #### 2.1.11. getSkips(): string[] ```ts /** * @returns An array of skips. */ getSkips(): string[] ``` #### 2.1.12. computeStringPosPointers(needle): number ```ts /** * @description Pre-computes a needle as a tree structure internally for faster matching. * If this is used, manual memory cleanup through `freeStringPosPointers` is required. * @param needle The needle to build a tree for. * @returns A pointer value as int64 (number). */ computeStringPosPointers(needle: string): number ``` #### 2.1.13. freeStringPosPointers(pointer): void ```ts /** * @description Frees the memory used by a precomputed needle. * @param pointer An int64 pointer value returned from `computeStringPosPointers`. */ freeStringPosPointers(pointer: number): void ``` #### 2.1.14. indexOfSync(input, needle, options): IResult ```ts /** * @description Searches for the first occurrence of `needle` in `input`. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns An object containing match information. */ indexOfSync(input: string, needle: string, options?: Partial<IIndexOfOptions>): IResult ``` #### 2.1.15. indexOf(input, needle, options): Promise<IResult> ```ts /** * @description Searches for the first occurrence of `needle` in `input`. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns A Promise that resolves to an object containing match information. */ indexOf(input: string, needle: string, options?: Partial<IIndexOfOptions>): Promise<IResult> ``` #### 2.1.16. lastIndexOfSync(input, needle, options): IResult ```ts /** * @description Searches for the last occurrence of `needle` in `input`. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns An object containing match information. */ lastIndexOfSync(input: string, needle: string, options?: Omit<Partial<IIndexOfOptions>, 'startFromEnd'>): IResult ``` #### 2.1.17. lastIndexOf(input, needle, options): Promise<IResult> ```ts /** * @description Searches for the last occurrence of `needle` in `input`. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns A Promise that resolves to an object containing match information. */ lastIndexOf(input: string, needle: string, options?: Omit<Partial<IIndexOfOptions>, 'startFromEnd'>): Promise<IResult> ``` #### 2.1.18. containsSync(input, needle, options): boolean ```ts /** * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns True if the `needle` is found inside `input`. */ containsSync(input: string, needle: string, options?: IIndexOfOptions): boolean ``` #### 2.1.19. contains(input, needle, options?): Promise<boolean> ```ts /** * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns A Promise that resolves to true if the `needle` is found inside `input`. */ contains(input: string, needle: string, options?: IIndexOfOptions): Promise<boolean> ``` #### 2.1.20. indexOfDebugFailuresSync(input, needle, options?): string[] ```ts /** * @description Searches for the first occurrence of `needle` in `input` with debugging enabled. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns An array of strings containing failure debugging reasons. */ indexOfDebugFailuresSync(input: string, needle: string, options?: Partial<IIndexOfOptions>): string[] ``` #### 2.1.21. indexOfDebugFailures(input, needle, options?): Promise<string[]> ```ts /** * @description Searches for the first occurrence of `needle` in `input` with debugging enabled. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns An array of strings containing failure debugging reasons. */ indexOfDebugFailures(input: string, needle: string, options?: Partial<IIndexOfOptions>): Promise<string[]> ``` #### 2.1.22. indexOfDebugFailuresExSync(input, needle, options?): IDebugFailureResult ```ts /** * @description Searches for the first occurrence of `needle` in `input` with debugging enabled. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns An object containing match information. */ indexOfDebugFailuresExSync(input: string, needle: string, options?: Partial<IIndexOfOptions>): IDebugFailureResult ``` #### 2.1.23. indexOfDebugFailuresEx(input, needle, options?): Promise<IDebugFailureResult> ```ts /** * @description Searches for the first occurrence of `needle` in `input` with debugging enabled. * @param input The string to search. * @param needle The string to look for in `input`. * @param options An optional object containing options in the search. * @returns A Promise that resolves to an object containing match information. */ indexOfDebugFailuresEx(input: string, needle: string, options?: Partial<IIndexOfOptions>): Promise<IDebugFailureResult> ``` ## 3. Development If you wish to develop this module, you can clone your own fork then execute the following commands to get setup: ```bash # Download submodules git submodule update --init --recursive --force --remote # Install dependencies yarn ``` If you want to build C++, run `yarn build:cpp`. If you want to build TypeScript, run `yarn build:ts`. You can additionally run `yarn clean` to clean the build output for both languages. ### 3.1. Testing Once you have the development environment setup, you can run `yarn test` to run the test suite. If you would like to generate coverage, you can run `yarn test:coverage`. If you want to watch the test files and re-run them on code changes, you can run `yarn test:watch` If writing tests, please replicate the tests in the [ConfusableMatcher source code](https://github.com/TETYYS/ConfusableMatcher). ### 3.2. Benchmarks You can run a benchmark by following the development steps, then running `yarn benchmark`. You can enable performance debugging with benchmark flags the following benchmark flags: | Flag | Description | | --------------- | ----------------------------------------------------------------- | | -l --loop | Prints the time elapsed in milliseconds between event loop ticks. | | -m --microtasks | Prints the duration it took for a promise to resolve. | The command `yarn benchmark:debug` will run the benchmark with all flags enabled. Please PR your reported time and hardware. | Processor | Memory | Version | #indexOf() Performance | #indexOfSync() Performance | | ------------------------- | ---------------------------------------------- | ------- | --------------------------------------- | ---------------------------------------- | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.8.0 | 10,603 ops/sec ±1.04% (87 runs sampled) | 146,163 ops/sec ±0.15% (97 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.7.1 | 15,639 ops/sec ±0.65% (83 runs sampled) | 246,712 ops/sec ±0.35% (96 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.7.0 | 15,134 ops/sec ±0.84% (83 runs sampled) | 244,674 ops/sec ±0.41% (96 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.6.0 | 16,084 ops/sec ±1.83% (78 runs sampled) | 237,149 ops/sec ±0.42% (93 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.5.0 | 14,664 ops/sec ±0.78% (87 runs sampled) | 239,525 ops/sec ±0.47% (96 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.4.0 | 14,686 ops/sec ±0.77% (85 runs sampled) | 240,802 ops/sec ±0.62% (95 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.3.0 | 14,794 ops/sec ±0.70% (85 runs sampled) | 240,648 ops/sec ±0.36% (94 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 4.0.0 | 15,312 ops/sec ±1.11% (81 runs sampled) | 248,335 ops/sec ±0.48% (93 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 3.2.0 | 15,944 ops/sec ±1.17% (79 runs sampled) | 251,881 ops/sec ±1.77% (93 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 3.1.0 | 14,659 ops/sec ±1.33% (81 runs sampled) | 249,710 ops/sec ±0.38% (97 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 3.0.0 | 16,133 ops/sec ±0.90% (81 runs sampled) | 264,466 ops/sec ±0.32% (95 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 2.2.0 | | 265,129 ops/sec ±1.02% (95 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 2.1.0 | | 254,060 ops/sec ±0.75% (92 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 2.0.1 | | 316,975 ops/sec ±0.30% (90 runs sampled) | | AMD Ryzen 5 3600 6-Core | DDR4-3200MHz CL16-18-18-38 1.35V 16GB (2x8GB) | 2.0.1 | | 307,821 ops/sec ±1.59% (89 runs sampled) | | Intel i5-8300H 4-Core | DDR4-2666MHz CL23-19-19-43 1.20V 08GB (1x8GB) | 2.0.1 | | 328,581 ops/sec ±1.56% (90 runs sampled) | | AMD Ryzen 9 3900X 12-Core | DDR4-3200MHz CL16-18-18-38 1.35V 64GB (4x16GB) | 2.0.0 | | 137,296 ops/sec ±0.42% (90 runs sampled) | | AMD Ryzen 5 3600 6-Core | DDR4-3200MHz CL16-18-18-38 1.35V 16GB (2x8GB) | 2.0.0 | | 126,622 ops/sec ±1.07% (88 runs sampled) | | Intel i5-8300H 4-Core | DDR4-2666MHz CL23-19-19-43 1.20V 08GB (1x8GB) | 2.0.0 | | 125,028 ops/sec ±3.93% (87 runs sampled) |