+131
| # AGENTS.md — node-re2 | ||
| > `node-re2` provides Node.js bindings for [RE2](https://github.com/google/re2): a fast, safe alternative to backtracking regular expression engines. The npm package name is `re2`. It is a C++ native addon built with `node-gyp` and `nan`. | ||
| For project structure, module dependencies, and the architecture overview see [ARCHITECTURE.md](./ARCHITECTURE.md). | ||
| For detailed usage docs see the [README](./README.md) and the [wiki](https://github.com/uhop/node-re2/wiki). | ||
| ## Setup | ||
| This project uses git submodules for vendored dependencies (RE2 and Abseil): | ||
| ```bash | ||
| git clone --recursive https://github.com/uhop/node-re2.git | ||
| cd node-re2 | ||
| npm install | ||
| ``` | ||
| If the native addon fails to download a prebuilt artifact, it builds locally via `node-gyp`. | ||
| ## Commands | ||
| - **Install:** `npm install` (downloads prebuilt artifact or builds from source) | ||
| - **Build (release):** `npm run rebuild` (or `node-gyp -j max rebuild`) | ||
| - **Build (debug):** `npm run rebuild:dev` (or `node-gyp -j max rebuild --debug`) | ||
| - **Test:** `npm test` (runs `tape6 --flags FO`, worker threads) | ||
| - **Test (sequential):** `npm run test:seq` | ||
| - **Test (multi-process):** `npm run test:proc` | ||
| - **Test (single file):** `node tests/test-<name>.mjs` | ||
| - **TypeScript check:** `npm run ts-check` | ||
| - **Lint:** `npm run lint` (Prettier check) | ||
| - **Lint fix:** `npm run lint:fix` (Prettier write) | ||
| - **Verify build:** `npm run verify-build` | ||
| ## Project structure | ||
| ``` | ||
| node-re2/ | ||
| ├── package.json # Package config; "tape6" section configures test discovery | ||
| ├── binding.gyp # node-gyp build configuration for the C++ addon | ||
| ├── re2.js # Main entry point: loads native addon, sets up Symbol aliases | ||
| ├── re2.d.ts # TypeScript declarations for the public API | ||
| ├── tsconfig.json # TypeScript config (noEmit, strict, types: ["node"]) | ||
| ├── lib/ # C++ source code (native addon) | ||
| │ ├── addon.cc # Node.js addon initialization, method registration | ||
| │ ├── wrapped_re2.h # WrappedRE2 class definition (core C++ wrapper) | ||
| │ ├── wrapped_re2_set.h # WrappedRE2Set class definition (RE2.Set wrapper) | ||
| │ ├── isolate_data.h # Per-isolate data struct for thread-safe addon state | ||
| │ ├── new.cc # Constructor: parse pattern/flags, create RE2 instance | ||
| │ ├── exec.cc # RE2.prototype.exec() implementation | ||
| │ ├── test.cc # RE2.prototype.test() implementation | ||
| │ ├── match.cc # RE2.prototype.match() implementation | ||
| │ ├── replace.cc # RE2.prototype.replace() implementation | ||
| │ ├── search.cc # RE2.prototype.search() implementation | ||
| │ ├── split.cc # RE2.prototype.split() implementation | ||
| │ ├── to_string.cc # RE2.prototype.toString() implementation | ||
| │ ├── accessors.cc # Property accessors (source, flags, lastIndex, etc.) | ||
| │ ├── pattern.cc # Pattern translation (RegExp → RE2 syntax, Unicode classes) | ||
| │ ├── set.cc # RE2.Set implementation (multi-pattern matching) | ||
| │ ├── util.cc # Shared utilities (UTF-8/UTF-16 conversion, buffer helpers) | ||
| │ ├── util.h # Utility declarations | ||
| │ └── pattern.h # Pattern translation declarations | ||
| ├── scripts/ | ||
| │ └── verify-build.js # Quick smoke test for the built addon | ||
| ├── tests/ # Test files (test-*.mjs using tape-six) | ||
| ├── ts-tests/ # TypeScript type-checking tests | ||
| │ └── test-types.ts # Verifies type declarations compile correctly | ||
| ├── bench/ # Benchmarks | ||
| ├── vendor/ # Vendored C++ dependencies (git submodules) | ||
| │ ├── re2/ # Google RE2 library source | ||
| │ └── abseil-cpp/ # Abseil C++ library (RE2 dependency) | ||
| └── .github/ # CI workflows, Dependabot config, actions | ||
| ``` | ||
| ## Code style | ||
| - **CommonJS** throughout (`"type": "commonjs"` in package.json). | ||
| - **No transpilation** — JavaScript code runs directly. | ||
| - **C++ code** uses tabs for indentation, 4-wide. JavaScript uses 2-space indentation. | ||
| - **Prettier** for JS/TS formatting (see `.prettierrc`): 80 char width, single quotes, no bracket spacing, no trailing commas, arrow parens "avoid". | ||
| - **nan** (Native Abstractions for Node.js) for the C++ addon API. | ||
| - Semicolons are enforced by Prettier (default `semi: true`). | ||
| - Imports use `require()` syntax in source, `import` in tests (`.mjs`). | ||
| ## Critical rules | ||
| - **Do not modify vendored code.** Never edit files under `vendor/`. They are git submodules. | ||
| - **Do not modify or delete test expectations** without understanding why they changed. | ||
| - **Do not add comments or remove comments** unless explicitly asked. | ||
| - **Keep `re2.js` and `re2.d.ts` in sync.** All public API exposed from `re2.js` must be typed in `re2.d.ts`. | ||
| - **The addon must build on all supported platforms:** Linux (x64, arm64, Alpine), macOS (x64, arm64), Windows (x64, arm64). | ||
| - **RE2 is always Unicode-mode.** The `u` flag is always added implicitly. | ||
| - **Buffer support is a first-class feature.** All methods that accept strings must also accept Buffers, returning Buffers when given Buffer input. | ||
| ## Architecture | ||
| - `re2.js` is the main entry point. It loads the native C++ addon from `build/Release/re2.node` and sets up `Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, and `Symbol.matchAll` on the prototype. | ||
| - The C++ addon (`lib/*.cc`) wraps Google's RE2 library via nan. Each RegExp method has its own `.cc` file. | ||
| - `lib/new.cc` handles construction: parsing patterns, translating RegExp syntax to RE2 syntax (via `lib/pattern.cc`), and creating the underlying `re2::RE2` instance. | ||
| - `lib/pattern.cc` translates JavaScript RegExp features to RE2 equivalents, including Unicode class names (`\p{Letter}` → `\p{L}`, `\p{Script=Latin}` → `\p{Latin}`). | ||
| - `lib/set.cc` implements `RE2.Set` for multi-pattern matching using `re2::RE2::Set`. | ||
| - `lib/util.cc` provides UTF-8 ↔ UTF-16 conversion helpers and buffer utilities. | ||
| - Prebuilt native artifacts are hosted on GitHub Releases and downloaded at install time via `install-artifact-from-github`. | ||
| ## Writing tests | ||
| ```js | ||
| import test from 'tape-six'; | ||
| import {RE2} from '../re2.js'; | ||
| test('example', t => { | ||
| const re = new RE2('a(b*)', 'i'); | ||
| const result = re.exec('aBbC'); | ||
| t.ok(result); | ||
| t.equal(result[0], 'aBb'); | ||
| t.equal(result[1], 'Bb'); | ||
| }); | ||
| ``` | ||
| - Test files use `tape-six`: `.mjs` for runtime tests, `.ts` for TypeScript typing tests. | ||
| - Test file naming convention: `test-*.mjs` in `tests/`, `test-*.ts` in `ts-tests/`. | ||
| - Tests are configured in `package.json` under the `"tape6"` section. | ||
| - Test files should be directly executable: `node tests/test-foo.mjs`. | ||
| ## Key conventions | ||
| - The library is a drop-in replacement for `RegExp` — the `RE2` object emulates the standard `RegExp` API. | ||
| - `RE2.Set` provides multi-pattern matching: `new RE2.Set(patterns, flags, options)`. | ||
| - Static helpers: `RE2.getUtf8Length(str)`, `RE2.getUtf16Length(buf)`. | ||
| - `RE2.unicodeWarningLevel` controls behavior when non-Unicode regexps are created. | ||
| - The `install` script tries to download a prebuilt `.node` artifact before falling back to `node-gyp rebuild`. | ||
| - All C++ source is in `lib/`, all vendored third-party C++ is in `vendor/`. |
+152
| # Architecture | ||
| `node-re2` provides Node.js bindings for Google's [RE2](https://github.com/google/re2) regular expression engine. It is a C++ native addon built with `node-gyp` and `nan`. The `RE2` object is a drop-in replacement for `RegExp` with guaranteed linear-time matching (no ReDoS). | ||
| ## Project layout | ||
| ``` | ||
| package.json # Package config; "tape6" section configures test discovery | ||
| binding.gyp # node-gyp build configuration for the C++ addon | ||
| re2.js # Main entry point: loads native addon, sets up Symbol aliases | ||
| re2.d.ts # TypeScript declarations for the public API | ||
| tsconfig.json # TypeScript config (noEmit, strict, types: ["node"]) | ||
| lib/ # C++ source code (native addon) | ||
| ├── addon.cc # Node.js addon initialization, method registration | ||
| ├── wrapped_re2.h # WrappedRE2 class definition (core C++ wrapper) | ||
| ├── wrapped_re2_set.h # WrappedRE2Set class definition (RE2.Set wrapper) | ||
| ├── isolate_data.h # Per-isolate data struct for thread-safe addon state | ||
| ├── new.cc # Constructor: parse pattern/flags, create RE2 instance | ||
| ├── exec.cc # RE2.prototype.exec() implementation | ||
| ├── test.cc # RE2.prototype.test() implementation | ||
| ├── match.cc # RE2.prototype.match() implementation | ||
| ├── replace.cc # RE2.prototype.replace() implementation | ||
| ├── search.cc # RE2.prototype.search() implementation | ||
| ├── split.cc # RE2.prototype.split() implementation | ||
| ├── to_string.cc # RE2.prototype.toString() implementation | ||
| ├── accessors.cc # Property accessors (source, flags, lastIndex, etc.) | ||
| ├── pattern.cc # Pattern translation (RegExp → RE2 syntax, Unicode classes) | ||
| ├── pattern.h # Pattern translation declarations | ||
| ├── set.cc # RE2.Set implementation (multi-pattern matching) | ||
| ├── util.cc # Shared utilities (UTF-8/UTF-16 conversion, buffer helpers) | ||
| └── util.h # Utility declarations | ||
| scripts/ | ||
| └── verify-build.js # Quick smoke test for the built addon | ||
| tests/ # Test files (test-*.mjs using tape-six) | ||
| ts-tests/ # TypeScript type-checking tests | ||
| └── test-types.ts # Verifies type declarations compile correctly | ||
| bench/ # Benchmarks | ||
| vendor/ # Vendored C++ dependencies (git submodules) — DO NOT MODIFY | ||
| ├── re2/ # Google RE2 library source | ||
| └── abseil-cpp/ # Abseil C++ library (RE2 dependency) | ||
| .github/ # CI workflows, Dependabot config, actions | ||
| ``` | ||
| ## Core concepts | ||
| ### How the addon works | ||
| 1. `re2.js` is the entry point. It loads the compiled C++ addon from `build/Release/re2.node`. | ||
| 2. The addon exposes an `RE2` constructor that wraps `re2::RE2` from Google's RE2 library. | ||
| 3. `re2.js` adds `Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, and `Symbol.matchAll` to the prototype so `RE2` instances work with ES6 string methods. | ||
| 4. The `RE2` constructor can be called with or without `new` (factory mode). | ||
| ### C++ addon structure | ||
| Each RegExp method has its own `.cc` file for maintainability: | ||
| | File | Purpose | | ||
| | --------------- | ---------------------------------------------------------------- | | ||
| | `addon.cc` | Node.js module initialization, registers all methods/accessors | | ||
| | `isolate_data.h` | Per-isolate data struct (`AddonData`) for thread-safe addon state | | ||
| | `wrapped_re2.h` | `WrappedRE2` class: holds `re2::RE2*`, flags, lastIndex, source | | ||
| | `new.cc` | Constructor: parses pattern + flags, translates syntax, creates RE2 instance | | ||
| | `exec.cc` | `exec()` — find match with capture groups | | ||
| | `test.cc` | `test()` — boolean match check | | ||
| | `match.cc` | `match()` — String.prototype.match equivalent | | ||
| | `replace.cc` | `replace()` — substitution with string or function replacer | | ||
| | `search.cc` | `search()` — find index of first match | | ||
| | `split.cc` | `split()` — split string by pattern | | ||
| | `to_string.cc` | `toString()` — `/pattern/flags` representation | | ||
| | `accessors.cc` | Property getters: `source`, `flags`, `lastIndex`, `global`, `ignoreCase`, `multiline`, `dotAll`, `unicode`, `sticky`, `hasIndices`, `internalSource` | | ||
| | `pattern.cc` | Translates JS RegExp syntax to RE2 syntax, maps Unicode property names | | ||
| | `set.cc` | `RE2.Set` — multi-pattern matching via `re2::RE2::Set` | | ||
| | `util.cc` | UTF-8 ↔ UTF-16 conversion, buffer/string helpers | | ||
| ### Pattern translation (pattern.cc) | ||
| JavaScript RegExp features are translated to RE2 equivalents: | ||
| - Named groups: `(?<name>...)` syntax is preserved (RE2 supports it natively). | ||
| - Unicode classes: long names like `\p{Letter}` are mapped to short names `\p{L}`. Script names like `\p{Script=Latin}` are mapped to `\p{Latin}`. | ||
| - Backreferences and lookahead assertions are **not supported** — RE2 throws `SyntaxError`. | ||
| ### Buffer support | ||
| All methods accept both strings and Node.js Buffers: | ||
| - Buffer inputs are assumed UTF-8 encoded. | ||
| - Buffer inputs produce Buffer outputs (in composite result objects too). | ||
| - Offsets and lengths are in bytes (not characters) when using Buffers. | ||
| - The `useBuffers` property on replacer functions controls offset reporting in `replace()`. | ||
| ### RE2.Set (set.cc) | ||
| Multi-pattern matching using `re2::RE2::Set`: | ||
| - `new RE2.Set(patterns, flags?, options?)` — compile multiple patterns into a single automaton. | ||
| - `set.test(str)` — returns `true` if any pattern matches. | ||
| - `set.match(str)` — returns array of indices of matching patterns. | ||
| - Properties: `size`, `source`, `sources`, `flags`, `anchor`. | ||
| ### Build system | ||
| - `binding.gyp` defines the node-gyp build: compiles all `.cc` files in `lib/` plus vendored RE2 and Abseil sources. | ||
| - Platform-specific compiler flags are set for GCC, Clang, and MSVC. | ||
| - The `install` npm script first tries to download a prebuilt `re2.node` from GitHub Releases via `install-artifact-from-github`, falling back to a local `node-gyp rebuild`. | ||
| - Prebuilt artifacts cover: Linux (x64, arm64, Alpine/musl), macOS (x64, arm64), Windows (x64, arm64). | ||
| ## Module dependency graph | ||
| ``` | ||
| re2.js ──→ build/Release/re2.node (compiled C++ addon) | ||
| │ | ||
| ├── lib/addon.cc (init) | ||
| │ ├── lib/new.cc ──→ lib/pattern.cc | ||
| │ ├── lib/exec.cc | ||
| │ ├── lib/test.cc | ||
| │ ├── lib/match.cc | ||
| │ ├── lib/replace.cc | ||
| │ ├── lib/search.cc | ||
| │ ├── lib/split.cc | ||
| │ ├── lib/to_string.cc | ||
| │ ├── lib/accessors.cc | ||
| │ └── lib/set.cc | ||
| │ | ||
| ├── lib/wrapped_re2.h (shared class definition) | ||
| ├── lib/wrapped_re2_set.h (RE2.Set class) | ||
| ├── lib/util.cc / lib/util.h (shared utilities) | ||
| │ | ||
| └── vendor/ (re2 + abseil-cpp) | ||
| ``` | ||
| ## Testing | ||
| - **Framework**: tape-six (`tape6`) | ||
| - **Run all**: `npm test` (worker threads via `tape6 --flags FO`) | ||
| - **Run sequential**: `npm run test:seq` | ||
| - **Run multi-process**: `npm run test:proc` | ||
| - **Run single file**: `node tests/test-<name>.mjs` | ||
| - **TypeScript check**: `npm run ts-check` | ||
| - **Lint**: `npm run lint` (Prettier check) | ||
| - **Lint fix**: `npm run lint:fix` (Prettier write) | ||
| - **Verify build**: `npm run verify-build` (quick smoke test) | ||
| ## Import paths | ||
| ```js | ||
| // CommonJS (source, scripts) | ||
| const RE2 = require('re2'); | ||
| // ESM (tests) | ||
| import {RE2} from '../re2.js'; | ||
| ``` |
+467
| # node-re2 | ||
| > Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. Drop-in RegExp replacement that prevents ReDoS (Regular Expression Denial of Service). Works with strings and Buffers. C++ native addon built with node-gyp and nan. | ||
| - Drop-in replacement for RegExp with linear-time matching guarantee | ||
| - Prevents ReDoS by disallowing backreferences and lookahead assertions | ||
| - Full Unicode mode (always on) | ||
| - Buffer support for high-performance binary/UTF-8 processing | ||
| - Named capture groups | ||
| - Symbol-based methods (Symbol.match, Symbol.search, Symbol.replace, Symbol.split, Symbol.matchAll) | ||
| - RE2.Set for multi-pattern matching | ||
| - Prebuilt binaries for Linux, macOS, Windows (x64 + arm64) | ||
| - TypeScript declarations included | ||
| ## Install | ||
| ```bash | ||
| npm install re2 | ||
| ``` | ||
| Prebuilt native binaries are downloaded automatically. Falls back to building from source via node-gyp if no prebuilt is available. | ||
| ## Quick start | ||
| ```js | ||
| const RE2 = require('re2'); | ||
| // Create and use like RegExp | ||
| const re = new RE2('a(b*)', 'i'); | ||
| const result = re.exec('aBbC'); | ||
| console.log(result[0]); // "aBb" | ||
| console.log(result[1]); // "Bb" | ||
| // Works with ES6 string methods | ||
| 'hello world'.match(new RE2('\\w+', 'g')); // ['hello', 'world'] | ||
| 'hello world'.replace(new RE2('world'), 'RE2'); // 'hello RE2' | ||
| ``` | ||
| ## Importing | ||
| ```js | ||
| // CommonJS | ||
| const RE2 = require('re2'); | ||
| // ESM | ||
| import { RE2 } from 're2'; | ||
| ``` | ||
| ## Construction | ||
| `new RE2(pattern[, flags])` or `RE2(pattern[, flags])` (factory mode). | ||
| Pattern can be: | ||
| - **String**: `new RE2('\\d+')` | ||
| - **String with flags**: `new RE2('\\d+', 'gi')` | ||
| - **RegExp**: `new RE2(/ab*/ig)` — copies pattern and flags. | ||
| - **RE2**: `new RE2(existingRE2)` — copies pattern and flags. | ||
| - **Buffer**: `new RE2(Buffer.from('pattern'))` — pattern from UTF-8 buffer. | ||
| Supported flags: | ||
| - `g` — global (find all matches) | ||
| - `i` — ignoreCase | ||
| - `m` — multiline (`^`/`$` match line boundaries) | ||
| - `s` — dotAll (`.` matches `\n`) | ||
| - `u` — unicode (always on, added implicitly) | ||
| - `y` — sticky (match at lastIndex only) | ||
| - `d` — hasIndices (include index info for capture groups) | ||
| Invalid patterns throw `SyntaxError`. Patterns with backreferences or lookahead throw `SyntaxError`. | ||
| ## Properties | ||
| ### Instance properties | ||
| - `re.source` (string) — the pattern string, escaped for use in `new RE2(re.source)` or `new RegExp(re.source)`. | ||
| - `re.flags` (string) — the flags string (e.g., `'giu'`). | ||
| - `re.lastIndex` (number) — the index at which to start the next match (used with `g` or `y` flags). | ||
| - `re.global` (boolean) — whether the `g` flag is set. | ||
| - `re.ignoreCase` (boolean) — whether the `i` flag is set. | ||
| - `re.multiline` (boolean) — whether the `m` flag is set. | ||
| - `re.dotAll` (boolean) — whether the `s` flag is set. | ||
| - `re.unicode` (boolean) — always `true` (RE2 always operates in Unicode mode). | ||
| - `re.sticky` (boolean) — whether the `y` flag is set. | ||
| - `re.hasIndices` (boolean) — whether the `d` flag is set. | ||
| - `re.internalSource` (string) — the RE2-translated pattern (for debugging; may differ from `source`). | ||
| ### Static properties | ||
| - `RE2.unicodeWarningLevel` (string) — controls behavior when a non-Unicode regexp is created: | ||
| - `'nothing'` (default) — silently add `u` flag. | ||
| - `'warnOnce'` — warn once, then silently add `u`. Assigning resets the one-time flag. | ||
| - `'warn'` — warn every time. | ||
| - `'throw'` — throw `SyntaxError` every time. | ||
| ## RegExp methods | ||
| ### re.exec(str) | ||
| Executes a search for a match. Returns a result array or `null`. | ||
| ```js | ||
| const re = new RE2('a(b+)', 'g'); | ||
| const result = re.exec('abbc abbc'); | ||
| // result[0] === 'abb' | ||
| // result[1] === 'bb' | ||
| // result.index === 0 | ||
| // result.input === 'abbc abbc' | ||
| // re.lastIndex === 3 | ||
| ``` | ||
| With `d` flag (hasIndices), result has `.indices` property with `[start, end]` pairs for each group. | ||
| With `g` or `y` flag, advances `lastIndex`. Call repeatedly to iterate matches. | ||
| ### re.test(str) | ||
| Returns `true` if the pattern matches, `false` otherwise. | ||
| ```js | ||
| new RE2('\\d+').test('abc123'); // true | ||
| new RE2('\\d+').test('abcdef'); // false | ||
| ``` | ||
| With `g` or `y` flag, advances `lastIndex`. | ||
| ### re.toString() | ||
| Returns `'/pattern/flags'` string representation. | ||
| ```js | ||
| new RE2('abc', 'gi').toString(); // '/abc/giu' | ||
| ``` | ||
| ## String methods (via Symbol) | ||
| RE2 instances implement well-known symbols, so they work directly with ES6 string methods: | ||
| ### str.match(re) / re[Symbol.match](str) | ||
| ```js | ||
| 'test 123 test 456'.match(new RE2('\\d+', 'g')); // ['123', '456'] | ||
| 'test 123'.match(new RE2('(\\d+)')); // ['123', '123', index: 5, input: 'test 123'] | ||
| ``` | ||
| ### str.matchAll(re) / re[Symbol.matchAll](str) | ||
| Returns an iterator of all matches (requires `g` flag). | ||
| ```js | ||
| const re = new RE2('\\d+', 'g'); | ||
| for (const m of '1a2b3c'.matchAll(re)) { | ||
| console.log(m[0]); // '1', '2', '3' | ||
| } | ||
| ``` | ||
| ### str.search(re) / re[Symbol.search](str) | ||
| Returns the index of the first match, or `-1`. | ||
| ```js | ||
| 'hello world'.search(new RE2('world')); // 6 | ||
| ``` | ||
| ### str.replace(re, replacement) / re[Symbol.replace](str, replacement) | ||
| Returns a new string with matches replaced. | ||
| ```js | ||
| 'aabba'.replace(new RE2('b', 'g'), 'c'); // 'aacca' | ||
| ``` | ||
| Replacement string supports: | ||
| - `$1`, `$2`, ... — numbered capture groups. | ||
| - `$<name>` — named capture groups. | ||
| - `$&` — the matched substring. | ||
| - `` $` `` — portion before the match. | ||
| - `$'` — portion after the match. | ||
| - `$$` — literal `$`. | ||
| Replacement function receives `(match, ...groups, offset, input)`: | ||
| ```js | ||
| 'abc'.replace(new RE2('(b)'), (match, g1, offset) => `[${g1}@${offset}]`); | ||
| // 'a[b@1]c' | ||
| ``` | ||
| ### str.split(re[, limit]) / re[Symbol.split](str[, limit]) | ||
| Splits string by pattern. | ||
| ```js | ||
| 'a1b2c3'.split(new RE2('\\d')); // ['a', 'b', 'c', ''] | ||
| 'a1b2c3'.split(new RE2('\\d'), 2); // ['a', 'b'] | ||
| ``` | ||
| ## String methods (direct) | ||
| These are convenience methods on the RE2 instance with swapped argument order: | ||
| - `re.match(str)` — equivalent to `str.match(re)`. | ||
| - `re.search(str)` — equivalent to `str.search(re)`. | ||
| - `re.replace(str, replacement)` — equivalent to `str.replace(re, replacement)`. | ||
| - `re.split(str[, limit])` — equivalent to `str.split(re, limit)`. | ||
| ```js | ||
| const re = new RE2('\\d+', 'g'); | ||
| re.match('test 123 test 456'); // ['123', '456'] | ||
| re.search('test 123'); // 5 | ||
| re.replace('test 1 and 2', 'N'); // 'test N and N' (global replaces all) | ||
| re.split('a1b2c'); // ['a', 'b', 'c'] | ||
| ``` | ||
| ## Buffer support | ||
| All methods accept Node.js Buffers (UTF-8) instead of strings. When given Buffer input, they return Buffer output. | ||
| ```js | ||
| const re = new RE2('матч', 'g'); | ||
| const buf = Buffer.from('тест матч тест'); | ||
| const result = re.exec(buf); | ||
| // result[0] is a Buffer containing 'матч' in UTF-8 | ||
| // result.index is in bytes (not characters) | ||
| ``` | ||
| Differences from string mode: | ||
| - All offsets and lengths are in **bytes**, not characters. | ||
| - Results contain Buffers instead of strings. | ||
| - Use `buf.toString()` to convert results back to strings. | ||
| ### useBuffers on replacer functions | ||
| When using `re.replace(buf, replacerFn)`, the replacer receives string arguments and character offsets by default. Set `replacerFn.useBuffers = true` to receive byte offsets instead: | ||
| ```js | ||
| function replacer(match, offset, input) { | ||
| return '<' + offset + ' bytes>'; | ||
| } | ||
| replacer.useBuffers = true; | ||
| new RE2('б').replace(Buffer.from('абв'), replacer); | ||
| ``` | ||
| ## RE2.Set | ||
| Multi-pattern matching — compile many patterns into a single automaton and test/match against all of them at once. Faster than testing individual patterns when the number of patterns is large. | ||
| ### Constructor | ||
| ```js | ||
| new RE2.Set(patterns[, flagsOrOptions][, options]) | ||
| ``` | ||
| - `patterns` — any iterable of strings, Buffers, RegExp, or RE2 instances. | ||
| - `flagsOrOptions` — optional string/Buffer with flags (apply to all patterns), or options object. | ||
| - `options.anchor` — `'unanchored'` (default), `'start'`, or `'both'`. | ||
| ```js | ||
| const set = new RE2.Set([ | ||
| '^/users/\\d+$', | ||
| '^/posts/\\d+$', | ||
| '^/api/.*$' | ||
| ], 'i', {anchor: 'start'}); | ||
| ``` | ||
| ### set.test(str) | ||
| Returns `true` if any pattern matches, `false` otherwise. | ||
| ```js | ||
| set.test('/users/42'); // true | ||
| set.test('/unknown'); // false | ||
| ``` | ||
| ### set.match(str) | ||
| Returns an array of indices of matching patterns, sorted ascending. Empty array if none match. | ||
| ```js | ||
| set.match('/users/42'); // [0] | ||
| set.match('/api/users'); // [2] | ||
| set.match('/unknown'); // [] | ||
| ``` | ||
| ### Properties | ||
| - `set.size` (number) — number of patterns. | ||
| - `set.source` (string) — all patterns joined with `|`. | ||
| - `set.sources` (string[]) — individual pattern sources. | ||
| - `set.flags` (string) — flags string. | ||
| - `set.anchor` (string) — anchor mode. | ||
| ### set.toString() | ||
| Returns `'/pattern1|pattern2|.../flags'`. | ||
| ```js | ||
| set.toString(); // '/^/users/\\d+$|^/posts/\\d+$|^/api/.*$/iu' | ||
| ``` | ||
| ## Static helpers | ||
| ### RE2.getUtf8Length(str) | ||
| Calculate the byte size needed to encode a UTF-16 string as UTF-8. | ||
| ```js | ||
| RE2.getUtf8Length('hello'); // 5 | ||
| RE2.getUtf8Length('привет'); // 12 | ||
| ``` | ||
| ### RE2.getUtf16Length(buf) | ||
| Calculate the character count needed to encode a UTF-8 buffer as a UTF-16 string. | ||
| ```js | ||
| RE2.getUtf16Length(Buffer.from('hello')); // 5 | ||
| RE2.getUtf16Length(Buffer.from('привет')); // 6 | ||
| ``` | ||
| ## Named groups | ||
| Named capture groups are supported: | ||
| ```js | ||
| const re = new RE2('(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})'); | ||
| const result = re.exec('2024-01-15'); | ||
| result.groups.year; // '2024' | ||
| result.groups.month; // '01' | ||
| result.groups.day; // '15' | ||
| ``` | ||
| Named backreferences in replacement strings: | ||
| ```js | ||
| '2024-01-15'.replace( | ||
| new RE2('(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})'), | ||
| '$<d>/$<m>/$<y>' | ||
| ); // '15/01/2024' | ||
| ``` | ||
| ## Unicode classes | ||
| RE2 supports Unicode property escapes. Long names are translated to RE2 short names: | ||
| ```js | ||
| new RE2('\\p{Letter}+'); // same as \p{L}+ | ||
| new RE2('\\p{Number}+'); // same as \p{N}+ | ||
| new RE2('\\p{Script=Latin}+'); // same as \p{Latin}+ | ||
| new RE2('\\p{sc=Cyrillic}+'); // same as \p{Cyrillic}+ | ||
| new RE2('\\P{Letter}+'); // negated: non-letters | ||
| ``` | ||
| Only `\p{name}` form is supported (not `\p{name=value}` in general). Exception: `Script` and `sc` names. | ||
| ## Limitations | ||
| RE2 does **not** support: | ||
| - **Backreferences** (`\1`, `\2`, etc.) — throw `SyntaxError`. | ||
| - **Lookahead assertions** (`(?=...)`, `(?!...)`) — throw `SyntaxError`. | ||
| - **Lookbehind assertions** (`(?<=...)`, `(?<!...)`) — throw `SyntaxError`. | ||
| Fallback pattern: | ||
| ```js | ||
| let re = /pattern-with-lookahead(?=foo)/; | ||
| try { | ||
| re = new RE2(re); | ||
| } catch (e) { | ||
| // use original RegExp as fallback | ||
| } | ||
| const result = re.exec(input); | ||
| ``` | ||
| ## Common patterns | ||
| ### Drop-in RegExp replacement | ||
| ```js | ||
| const RE2 = require('re2'); | ||
| // Before (vulnerable to ReDoS): | ||
| const re = new RegExp(userInput); | ||
| // After (safe): | ||
| const re = new RE2(userInput); | ||
| ``` | ||
| ### Process Buffer data efficiently | ||
| ```js | ||
| const RE2 = require('re2'); | ||
| const fs = require('fs'); | ||
| const data = fs.readFileSync('large-file.txt'); | ||
| const re = new RE2('pattern', 'g'); | ||
| let match; | ||
| while ((match = re.exec(data)) !== null) { | ||
| console.log('Found at byte offset:', match.index); | ||
| } | ||
| ``` | ||
| ### Route matching with RE2.Set | ||
| ```js | ||
| const RE2 = require('re2'); | ||
| const routes = new RE2.Set([ | ||
| '^/users/\\d+$', | ||
| '^/posts/\\d+$', | ||
| '^/api/v\\d+/.*$' | ||
| ], 'i'); | ||
| function findRoute(path) { | ||
| const matches = routes.match(path); | ||
| return matches.length > 0 ? matches[0] : -1; | ||
| } | ||
| findRoute('/users/42'); // 0 | ||
| findRoute('/posts/7'); // 1 | ||
| findRoute('/api/v2/foo'); // 2 | ||
| findRoute('/unknown'); // -1 | ||
| ``` | ||
| ### Validate user-supplied patterns safely | ||
| ```js | ||
| const RE2 = require('re2'); | ||
| function safeMatch(input, pattern, flags) { | ||
| try { | ||
| const re = new RE2(pattern, flags); | ||
| return re.test(input); | ||
| } catch (e) { | ||
| return false; // invalid pattern | ||
| } | ||
| } | ||
| ``` | ||
| ## TypeScript | ||
| ```ts | ||
| import RE2 from 're2'; | ||
| const re: RE2 = new RE2('\\d+', 'g'); | ||
| const result: RegExpExecArray | null = re.exec('test 123'); | ||
| // Buffer overloads | ||
| const bufResult: RE2BufferExecArray | null = re.exec(Buffer.from('test 123')); | ||
| // RE2.Set | ||
| const set: RE2Set = new RE2.Set(['a', 'b'], 'i'); | ||
| const matches: number[] = set.match('abc'); | ||
| ``` | ||
| ## Project structure notes | ||
| - Entry point: `re2.js` (loads native addon), types: `re2.d.ts`. | ||
| - C++ addon source: `lib/*.cc`, `lib/*.h`. | ||
| - Tests: `tests/test-*.mjs` (runtime), `ts-tests/test-*.ts` (type-checking). | ||
| - Vendored dependencies: `vendor/re2/`, `vendor/abseil-cpp/` (git submodules) — **never modify files under `vendor/`**. | ||
| ## Links | ||
| - Docs: https://github.com/uhop/node-re2/wiki | ||
| - npm: https://www.npmjs.com/package/re2 | ||
| - Repository: https://github.com/uhop/node-re2 | ||
| - RE2 syntax: https://github.com/google/re2/wiki/Syntax |
+132
| # node-re2 | ||
| > Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. Drop-in RegExp replacement that prevents ReDoS. Works with strings and Buffers. | ||
| ## Install | ||
| npm install re2 | ||
| ## Quick start | ||
| ```js | ||
| // CommonJS | ||
| const RE2 = require('re2'); | ||
| // ESM | ||
| import {RE2} from 're2'; | ||
| const re = new RE2('a(b*)', 'i'); | ||
| const result = re.exec('aBbC'); | ||
| console.log(result[0]); // "aBb" | ||
| console.log(result[1]); // "Bb" | ||
| ``` | ||
| ## Why use node-re2? | ||
| The built-in Node.js RegExp engine can run in exponential time with vulnerable patterns (ReDoS). RE2 guarantees linear-time matching by disallowing backreferences and lookahead assertions. | ||
| ## API | ||
| ### Construction | ||
| ```js | ||
| const RE2 = require('re2'); | ||
| const re1 = new RE2('\\d+'); // from string | ||
| const re2 = new RE2('\\d+', 'gi'); // with flags | ||
| const re3 = new RE2(/ab*/ig); // from RegExp | ||
| const re4 = new RE2(re3); // from another RE2 | ||
| const re5 = RE2('\\d+'); // factory (no new) | ||
| ``` | ||
| Supported flags: `g` (global), `i` (ignoreCase), `m` (multiline), `s` (dotAll), `u` (unicode, always on), `y` (sticky), `d` (hasIndices). | ||
| ### RegExp methods | ||
| - `re.exec(str)` — find match with capture groups. | ||
| - `re.test(str)` — boolean match check. | ||
| - `re.toString()` — `/pattern/flags` representation. | ||
| ### String methods (via Symbol) | ||
| RE2 instances work with ES6 string methods: | ||
| ```js | ||
| 'abc'.match(re); | ||
| 'abc'.search(re); | ||
| 'abc'.replace(re, 'x'); | ||
| 'abc'.split(re); | ||
| Array.from('abc'.matchAll(re)); | ||
| ``` | ||
| ### String methods (direct) | ||
| - `re.match(str)` — equivalent to `str.match(re)`. | ||
| - `re.search(str)` — equivalent to `str.search(re)`. | ||
| - `re.replace(str, replacement)` — equivalent to `str.replace(re, replacement)`. | ||
| - `re.split(str[, limit])` — equivalent to `str.split(re, limit)`. | ||
| ### Properties | ||
| - `re.source` — pattern string. | ||
| - `re.flags` — flags string. | ||
| - `re.lastIndex` — index for next match (with `g` or `y` flag). | ||
| - `re.global`, `re.ignoreCase`, `re.multiline`, `re.dotAll`, `re.unicode`, `re.sticky`, `re.hasIndices` — boolean flag accessors. | ||
| - `re.internalSource` — RE2-translated pattern (for debugging). | ||
| ### Buffer support | ||
| All methods accept Buffers (UTF-8) instead of strings. Buffer input produces Buffer output. Offsets are in bytes. | ||
| ```js | ||
| const re = new RE2('матч', 'g'); | ||
| const buf = Buffer.from('тест матч тест'); | ||
| const result = re.exec(buf); | ||
| // result[0] is a Buffer | ||
| ``` | ||
| ### RE2.Set | ||
| Multi-pattern matching — test a string against many patterns at once. | ||
| ```js | ||
| const set = new RE2.Set(['^/users/\\d+$', '^/posts/\\d+$'], 'i'); | ||
| set.test('/users/7'); // true | ||
| set.match('/posts/42'); // [1] | ||
| set.sources; // ['^/users/\\d+$', '^/posts/\\d+$'] | ||
| ``` | ||
| - `new RE2.Set(patterns[, flags][, options])` — compile patterns. | ||
| - `options.anchor`: `'unanchored'` (default), `'start'`, or `'both'`. | ||
| - `set.test(str)` — returns `true` if any pattern matches. | ||
| - `set.match(str)` — returns array of matching pattern indices. | ||
| - Properties: `size`, `source`, `sources`, `flags`, `anchor`. | ||
| ### Static helpers | ||
| - `RE2.getUtf8Length(str)` — byte size of string as UTF-8. | ||
| - `RE2.getUtf16Length(buf)` — character count of UTF-8 buffer as UTF-16 string. | ||
| - `RE2.unicodeWarningLevel` — `'nothing'` (default), `'warnOnce'`, `'warn'`, or `'throw'`. | ||
| ## Limitations | ||
| RE2 does not support: | ||
| - **Backreferences** (`\1`, `\2`, etc.) | ||
| - **Lookahead assertions** (`(?=...)`, `(?!...)`) | ||
| These throw `SyntaxError`. Use try-catch to fall back to RegExp when needed: | ||
| ```js | ||
| let re = /pattern-with-lookahead/; | ||
| try { re = new RE2(re); } catch (e) { /* use original RegExp */ } | ||
| ``` | ||
| ## Project notes | ||
| - C++ addon source is in `lib/`. Vendored deps (`vendor/re2/`, `vendor/abseil-cpp/`) are git submodules — **never modify files under `vendor/`**. | ||
| ## Links | ||
| - Docs: https://github.com/uhop/node-re2/wiki | ||
| - npm: https://www.npmjs.com/package/re2 | ||
| - Full LLM reference: https://github.com/uhop/node-re2/blob/master/llms-full.txt |
+2
-2
@@ -43,3 +43,3 @@ #include "./wrapped_re2.h" | ||
| auto s = t.ToLocalChecked(); | ||
| info.GetReturnValue().Set(static_cast<int>(s->Utf8Length(v8::Isolate::GetCurrent()))); | ||
| info.GetReturnValue().Set(static_cast<int>(utf8Length(s, v8::Isolate::GetCurrent()))); | ||
| } | ||
@@ -201,3 +201,3 @@ | ||
| auto s = t.ToLocalChecked(); | ||
| auto argLength = s->Utf8Length(isolate); | ||
| auto argLength = utf8Length(s, isolate); | ||
@@ -204,0 +204,0 @@ auto buffer = node::Buffer::New(isolate, s).ToLocalChecked(); |
+6
-6
@@ -79,6 +79,6 @@ #include "./wrapped_re2.h" | ||
| auto s = t.ToLocalChecked(); | ||
| size = s->Utf8Length(isolate); | ||
| size = utf8Length(s, isolate); | ||
| buffer.resize(size + 1); | ||
| data = &buffer[0]; | ||
| s->WriteUtf8(isolate, data, buffer.size()); | ||
| writeUtf8(s, isolate, data, buffer.size()); | ||
| buffer[size] = '\0'; | ||
@@ -138,6 +138,6 @@ } | ||
| auto s = t.ToLocalChecked(); | ||
| size = s->Utf8Length(isolate); | ||
| size = utf8Length(s, isolate); | ||
| buffer.resize(size + 1); | ||
| data = &buffer[0]; | ||
| s->WriteUtf8(isolate, data, buffer.size()); | ||
| writeUtf8(s, isolate, data, buffer.size()); | ||
| buffer[size] = '\0'; | ||
@@ -197,6 +197,6 @@ | ||
| auto s = t.ToLocalChecked(); | ||
| size = s->Utf8Length(isolate); | ||
| size = utf8Length(s, isolate); | ||
| buffer.resize(size + 1); | ||
| data = &buffer[0]; | ||
| s->WriteUtf8(isolate, data, buffer.size()); | ||
| writeUtf8(s, isolate, data, buffer.size()); | ||
| buffer[size] = '\0'; | ||
@@ -203,0 +203,0 @@ |
+10
-10
@@ -37,5 +37,5 @@ #include "./wrapped_re2_set.h" | ||
| auto s = t.ToLocalChecked(); | ||
| size = s->Utf8Length(isolate); | ||
| size = utf8Length(s, isolate); | ||
| buffer.resize(size + 1); | ||
| s->WriteUtf8(isolate, &buffer[0], buffer.size()); | ||
| writeUtf8(s, isolate, &buffer[0], buffer.size()); | ||
| buffer[buffer.size() - 1] = '\0'; | ||
@@ -291,6 +291,6 @@ data = &buffer[0]; | ||
| auto s = t.ToLocalChecked(); | ||
| auto utf8Length = s->Utf8Length(isolate); | ||
| auto len = utf8Length(s, isolate); | ||
| auto buffer = node::Buffer::New(isolate, s).ToLocalChecked(); | ||
| keepAlive = buffer; | ||
| str.reset(buffer, node::Buffer::Length(buffer), utf8Length, 0); | ||
| str.reset(buffer, node::Buffer::Length(buffer), len, 0); | ||
| return true; | ||
@@ -336,3 +336,3 @@ } | ||
| auto context = Nan::GetCurrentContext(); | ||
| auto isolate = context->GetIsolate(); | ||
| auto isolate = v8::Isolate::GetCurrent(); | ||
@@ -346,3 +346,3 @@ if (!info.IsConstructCall()) | ||
| } | ||
| auto isolate = context->GetIsolate(); | ||
| auto isolate = v8::Isolate::GetCurrent(); | ||
| auto addonData = getAddonData(isolate); | ||
@@ -520,5 +520,5 @@ if (!addonData) return; | ||
| auto s = t.ToLocalChecked(); | ||
| size = s->Utf8Length(isolate); | ||
| size = utf8Length(s, isolate); | ||
| buffer.resize(size + 1); | ||
| s->WriteUtf8(isolate, &buffer[0], buffer.size()); | ||
| writeUtf8(s, isolate, &buffer[0], buffer.size()); | ||
| buffer[size] = '\0'; | ||
@@ -536,5 +536,5 @@ data = &buffer[0]; | ||
| auto s = t.ToLocalChecked(); | ||
| size = s->Utf8Length(isolate); | ||
| size = utf8Length(s, isolate); | ||
| buffer.resize(size + 1); | ||
| s->WriteUtf8(isolate, &buffer[0], buffer.size()); | ||
| writeUtf8(s, isolate, &buffer[0], buffer.size()); | ||
| buffer[size] = '\0'; | ||
@@ -541,0 +541,0 @@ data = &buffer[0]; |
+29
-0
@@ -228,2 +228,31 @@ #pragma once | ||
| // V8 13.4 introduced Utf8LengthV2 / WriteUtf8V2; V8 14.6 removed the bare | ||
| // Utf8Length / WriteUtf8. On older V8 (Node 22) only the bare forms exist. | ||
| #if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 13 || \ | ||
| (V8_MAJOR_VERSION == 13 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 4)) | ||
| inline size_t utf8Length(v8::Local<v8::String> s, v8::Isolate *isolate) | ||
| { | ||
| return s->Utf8LengthV2(isolate); | ||
| } | ||
| inline void writeUtf8(v8::Local<v8::String> s, v8::Isolate *isolate, char *buffer, size_t capacity) | ||
| { | ||
| s->WriteUtf8V2(isolate, buffer, capacity); | ||
| } | ||
| #else | ||
| inline size_t utf8Length(v8::Local<v8::String> s, v8::Isolate *isolate) | ||
| { | ||
| return static_cast<size_t>(s->Utf8Length(isolate)); | ||
| } | ||
| inline void writeUtf8(v8::Local<v8::String> s, v8::Isolate *isolate, char *buffer, size_t capacity) | ||
| { | ||
| s->WriteUtf8(isolate, buffer, static_cast<int>(capacity)); | ||
| } | ||
| #endif | ||
| inline size_t getUtf16PositionByCounter(const char *data, size_t from, size_t n) | ||
@@ -230,0 +259,0 @@ { |
+17
-10
| { | ||
| "name": "re2", | ||
| "version": "1.24.0", | ||
| "version": "1.24.1", | ||
| "description": "Bindings for RE2: fast, safe alternative to backtracking regular expression engines.", | ||
@@ -11,4 +11,8 @@ "homepage": "https://github.com/uhop/node-re2", | ||
| "files": [ | ||
| "AGENTS.md", | ||
| "ARCHITECTURE.md", | ||
| "binding.gyp", | ||
| "lib", | ||
| "llms-full.txt", | ||
| "llms.txt", | ||
| "re2.d.ts", | ||
@@ -19,13 +23,13 @@ "scripts/*.js", | ||
| "dependencies": { | ||
| "install-artifact-from-github": "^1.4.0", | ||
| "nan": "^2.26.2", | ||
| "node-gyp": "^12.2.0" | ||
| "install-artifact-from-github": "^1.6.0", | ||
| "nan": "^2.27.0", | ||
| "node-gyp": "^12.3.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^25.5.0", | ||
| "@types/node": "^25.7.0", | ||
| "nano-benchmark": "^1.0.15", | ||
| "prettier": "^3.8.1", | ||
| "tape-six": "^1.7.13", | ||
| "tape-six-proc": "^1.2.8", | ||
| "typescript": "^6.0.2" | ||
| "prettier": "^3.8.3", | ||
| "tape-six": "^1.9.0", | ||
| "tape-six-proc": "^1.2.9", | ||
| "typescript": "^6.0.3" | ||
| }, | ||
@@ -54,4 +58,7 @@ "scripts": { | ||
| "type": "git", | ||
| "url": "git://github.com/uhop/node-re2.git" | ||
| "url": "git+https://github.com/uhop/node-re2.git" | ||
| }, | ||
| "engines": { | ||
| "node": ">=22" | ||
| }, | ||
| "keywords": [ | ||
@@ -58,0 +65,0 @@ "RegExp", |
+1
-0
@@ -0,1 +1,2 @@ | ||
| // @ts-self-types="./re2.d.ts" | ||
| 'use strict'; | ||
@@ -2,0 +3,0 @@ |
+1
-0
@@ -388,2 +388,3 @@ # node-re2 [![NPM version][npm-img]][npm-url] | ||
| - 1.24.1 *Support for Node 22, 24, 26 + precompiled binaries.* | ||
| - 1.24.0 *Fixed multi-threaded crash in worker threads (#235). Added named import: `import {RE2} from 're2'`. Added CJS test. Updated docs and dependencies.* | ||
@@ -390,0 +391,0 @@ - 1.23.3 *Updated Abseil and dev dependencies.* |
@@ -121,3 +121,3 @@ // | ||
| #define ABSL_LTS_RELEASE_VERSION 20260107 | ||
| #define ABSL_LTS_RELEASE_PATCH_LEVEL 0 | ||
| #define ABSL_LTS_RELEASE_PATCH_LEVEL 1 | ||
@@ -124,0 +124,0 @@ // Helper macro to convert a CPP variable to a string literal. |
@@ -107,3 +107,6 @@ // Copyright 2018 The Abseil Authors. | ||
| #elif defined(_MSC_VER) && !defined(__clang__) && defined(__AVX__) | ||
| // 32-bit builds with AVX do not have _mm_crc32_u64, so the _M_X64 condition is | ||
| // necessary. | ||
| #elif defined(_MSC_VER) && !defined(__clang__) && defined(__AVX__) && \ | ||
| defined(_M_X64) | ||
@@ -110,0 +113,0 @@ // MSVC AVX (/arch:AVX) implies SSE 4.2. |
@@ -736,2 +736,6 @@ // Copyright 2017 The Abseil Authors. | ||
| EXPECT_EQ("", bytes); // Results in empty output. | ||
| // Ensure there is no sign extension bug on a signed char. | ||
| hex.assign("\xC8" "b", 2); | ||
| EXPECT_FALSE(absl::HexStringToBytes(hex, &bytes)); | ||
| } | ||
@@ -738,0 +742,0 @@ |
@@ -830,3 +830,3 @@ // Copyright 2017 The Abseil Authors. | ||
| /* clang-format off */ | ||
| constexpr std::array<char, 256> kHexValueLenient = { | ||
| constexpr std::array<uint8_t, 256> kHexValueLenient = { | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
@@ -850,3 +850,3 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| constexpr std::array<signed char, 256> kHexValueStrict = { | ||
| constexpr std::array<int8_t, 256> kHexValueStrict = { | ||
| -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
@@ -879,3 +879,3 @@ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
| to[i] = static_cast<char>(kHexValueLenient[from[i * 2] & 0xFF] << 4) + | ||
| (kHexValueLenient[from[i * 2 + 1] & 0xFF]); | ||
| static_cast<char>(kHexValueLenient[from[i * 2 + 1] & 0xFF]); | ||
| } | ||
@@ -998,4 +998,6 @@ } | ||
| for (size_t i = 0; i < buf_size; ++i) { | ||
| int h1 = absl::kHexValueStrict[static_cast<size_t>(*hex_p++)]; | ||
| int h2 = absl::kHexValueStrict[static_cast<size_t>(*hex_p++)]; | ||
| int h1 = absl::kHexValueStrict[static_cast<size_t>( | ||
| static_cast<uint8_t>(*hex_p++))]; | ||
| int h2 = absl::kHexValueStrict[static_cast<size_t>( | ||
| static_cast<uint8_t>(*hex_p++))]; | ||
| if (h1 == -1 || h2 == -1) { | ||
@@ -1002,0 +1004,0 @@ return size_t{0}; |
Sorry, the diff of this file is not supported yet
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
12343732
0.27%1730
0.23%2626
0.04%423
0.24%Updated
Updated