Skip to content

Comments

fix(lib/utils): simpler algorithm for removeDotSegments#140

Merged
Uzlopak merged 11 commits intofastify:mainfrom
wooffie:fix-139
Aug 16, 2025
Merged

fix(lib/utils): simpler algorithm for removeDotSegments#140
Uzlopak merged 11 commits intofastify:mainfrom
wooffie:fix-139

Conversation

@wooffie
Copy link
Contributor

@wooffie wooffie commented Aug 14, 2025

Replace regex to linear algorithm. This fixes issue with fatal error/out of range.
Closes #139

Checklist

@wooffie wooffie mentioned this pull request Aug 14, 2025
2 tasks
@zekth
Copy link
Member

zekth commented Aug 14, 2025

Nice! Can we run benchmarks on this branch vs main?

wooffie and others added 2 commits August 14, 2025 12:15
Co-authored-by: Aras Abbasi <aras.abbasi@googlemail.com>
Signed-off-by: Burkov Egor <xwooffie@gmail.com>
Co-authored-by: Aras Abbasi <aras.abbasi@googlemail.com>
Signed-off-by: Burkov Egor <xwooffie@gmail.com>
@wooffie
Copy link
Contributor Author

wooffie commented Aug 14, 2025

Nice! Can we run benchmarks on this branch vs main?

Actually no changes across this branch/main cause benchmark don't use enought long url to see it, and function not used across all library calls.

You can add benchmark suite:

suite.add('fast-uri: serialize uri', function () {
  fasturi.serialize({
    scheme: 'uri',
    userinfo: 'foo:bar',
    host: 'example.com',
    port: 1,
    path: './a/./b/c/../.././d/../e/f/.././/',
    query: 'query',
    fragment: 'fragment'
  })
})

Results:
main branch - fast-uri: serialize uri x 345,155 ops/sec ±3.12% (92 runs sampled)
this branch - fast-uri: serialize uri x 843,644 ops/sec ±2.90% (87 runs sampled)
urijs - urijs: serialize uri x 239,765 ops/sec ±0.57% (98 runs sampled)

wooffie and others added 2 commits August 14, 2025 12:57
Co-authored-by: Aras Abbasi <aras.abbasi@googlemail.com>
Signed-off-by: Burkov Egor <xwooffie@gmail.com>
@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 14, 2025

@zekth

Details
function removeDotSegments (path) {
  let input = path
  const output = []
  let nextSlash = -1
  let len = 0

  while (len = input.length) {
    if (len === 1) {
      if (input === '.') {
        break
      } else if (input === '/') {
        output.push('/')
        break
      } else {
        output.push(input)
        break
      }
    } else if (len === 2) {
      if (input[0] === '.') {
        if (input[1] === '.') {
          break
        } else if (input[1] === '/') {
          input = input.slice(2)
          continue
        }
      } else if (input[0] === '/') {
        if (input[1] === '.' || input[1] === '/') {
          output.push('/')
          break
        }
      }
    } else if (len === 3) {
      if (input === '/..') {
        if (output.length !== 0) {
          output.pop()
        }
        output.push('/')
        break
      }
    }
    if (input[0] === '.') {
      if (input[1] === '.') {
        if (input[2] === '/') {
          input = input.slice(3)
        }
      } else if (input[1] === '/') {
        input = input.slice(2)
      }
    } else if (input[0] === '/') {
      if (input[1] === '.') {
        if (input[2] === '/') {
          input = input.slice(2)
          continue
        } else if (input[2] === '.') {
          if (input[3] === '/') {
            input = input.slice(3)
            if (output.length !== 0) {
              output.pop()
            }
            continue
          }
        }
      }
    }

    // Rule 2E: Move normal path segment to output
    if ((nextSlash = input.indexOf('/', 1)) === -1) {
      output.push(input)
      break
    } else {
      output.push(input.slice(0, nextSlash))
      input = input.slice(nextSlash)
    }
  }

  return output.join('')
}

this pr:

aras@aras-HP-ZBook-15-G3:~/workspace/fast-uri$ node benchmark.js 
fast-uri: serialize uri x 559,853 ops/sec ±1.13% (91 runs sampled)

mine:

aras@aras-HP-ZBook-15-G3:~/workspace/fast-uri$ node benchmark.js 
fast-uri: serialize uri x 653,915 ops/sec ±1.55% (93 runs sampled)

@Fdawgs Fdawgs changed the title fix(lib/utils): simpler algorithm for removeDotSegments (#139) fix(lib/utils): simpler algorithm for removeDotSegments Aug 14, 2025
Copy link
Member

@Fdawgs Fdawgs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a supporting unit test for the bug raised in #139

@Fdawgs Fdawgs requested a review from a team August 15, 2025 07:47
@gurgunday
Copy link
Member

@zekth

this pr:

aras@aras-HP-ZBook-15-G3:~/workspace/fast-uri$ node benchmark.js 
fast-uri: serialize uri x 559,853 ops/sec ±1.13% (91 runs sampled)

mine:

aras@aras-HP-ZBook-15-G3:~/workspace/fast-uri$ node benchmark.js 
fast-uri: serialize uri x 653,915 ops/sec ±1.55% (93 runs sampled)

@wooffie if you can confirm these results, I'd suggest applying @Uzlopak's suggestion. I understand it might look a little worse but performance is a key part of the Fastify ecosystem, and besides I'm used to seeing code like this in URI specs

Thanks again!

Copy link
Member

@gurgunday gurgunday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@gurgunday gurgunday requested review from Fdawgs, Uzlopak and zekth August 15, 2025 11:12
@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 15, 2025

@wooffie

Can you please add your benchmark to the suite?

Then we can make a final comparison between main and pr ;)

@wooffie
Copy link
Contributor Author

wooffie commented Aug 15, 2025

My results:

fast-uri: serialize uri x 1,349,182 ops/sec ±6.04% (78 runs sampled)
urijs: serialize uri x 385,399 ops/sec ±2.95% (81 runs sampled)
fast-uri: serialize long uri with dots x 921,619 ops/sec ±3.07% (92 runs sampled)
urijs: serialize long uri with dots x 215,996 ops/sec ±0.49% (96 runs sampled

urijs has more complexity to work with dots.
Default test shows that urijs slower by 71%
Long test show 76%
Also if add more and more dots ('./a/./b/c/../.././d/../e/f/.././/a/3/24/bds/../../../2/../b/c//') we can get 80% and this difference will grow

@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 15, 2025

@wooffie

Well, it is more like, not that the changes resulted in a regression in other cases.

@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 15, 2025

I dont see any regression.

main:

fast-uri: parse domain x 1,118,155 ops/sec ±2.26% (90 runs sampled)
urijs: parse domain x 332,608 ops/sec ±0.70% (90 runs sampled)
WHATWG URL: parse domain x 2,862,487 ops/sec ±0.61% (94 runs sampled)
fast-uri: parse IPv4 x 1,602,475 ops/sec ±0.94% (92 runs sampled)
urijs: parse IPv4 x 287,487 ops/sec ±0.79% (94 runs sampled)
fast-uri: parse IPv6 x 860,477 ops/sec ±0.51% (94 runs sampled)
urijs: parse IPv6 x 222,171 ops/sec ±1.23% (94 runs sampled)
fast-uri: parse URN x 1,737,473 ops/sec ±0.51% (95 runs sampled)
urijs: parse URN x 859,554 ops/sec ±0.49% (93 runs sampled)
WHATWG URL: parse URN x 3,475,550 ops/sec ±0.60% (91 runs sampled)
fast-uri: parse URN uuid x 1,074,608 ops/sec ±0.99% (95 runs sampled)
urijs: parse URN uuid x 616,849 ops/sec ±0.59% (94 runs sampled)
fast-uri: serialize uri x 949,836 ops/sec ±1.49% (94 runs sampled)
urijs: serialize uri x 264,787 ops/sec ±0.47% (95 runs sampled)
fast-uri: serialize long uri with dots x 218,870 ops/sec ±0.66% (95 runs sampled)
urijs: serialize long uri with dots x 132,720 ops/sec ±0.72% (94 runs sampled)
fast-uri: serialize IPv6 x 409,919 ops/sec ±2.59% (91 runs sampled)
urijs: serialize IPv6 x 193,344 ops/sec ±0.47% (96 runs sampled)
fast-uri: serialize ws x 1,002,939 ops/sec ±0.65% (95 runs sampled)
urijs: serialize ws x 263,285 ops/sec ±0.56% (95 runs sampled)
fast-uri: resolve x 303,951 ops/sec ±0.75% (91 runs sampled)
urijs: resolve x 159,221 ops/sec ±2.24% (92 runs sampled)

PR:

fast-uri: parse domain x 1,120,644 ops/sec ±1.12% (90 runs sampled)
urijs: parse domain x 324,418 ops/sec ±1.68% (94 runs sampled)
WHATWG URL: parse domain x 2,781,028 ops/sec ±1.01% (93 runs sampled)
fast-uri: parse IPv4 x 1,574,421 ops/sec ±0.92% (95 runs sampled)
urijs: parse IPv4 x 270,356 ops/sec ±1.00% (93 runs sampled)
fast-uri: parse IPv6 x 829,637 ops/sec ±0.78% (93 runs sampled)
urijs: parse IPv6 x 214,412 ops/sec ±0.77% (95 runs sampled)
fast-uri: parse URN x 1,694,760 ops/sec ±0.90% (92 runs sampled)
urijs: parse URN x 844,838 ops/sec ±0.72% (97 runs sampled)
WHATWG URL: parse URN x 3,428,778 ops/sec ±0.59% (93 runs sampled)
fast-uri: parse URN uuid x 1,080,123 ops/sec ±0.62% (95 runs sampled)
urijs: parse URN uuid x 614,282 ops/sec ±0.63% (94 runs sampled)
fast-uri: serialize uri x 1,137,299 ops/sec ±0.78% (95 runs sampled)
urijs: serialize uri x 260,947 ops/sec ±0.55% (95 runs sampled)
fast-uri: serialize long uri with dots x 634,818 ops/sec ±0.69% (95 runs sampled)
urijs: serialize long uri with dots x 126,910 ops/sec ±0.63% (95 runs sampled)
fast-uri: serialize IPv6 x 427,203 ops/sec ±0.75% (96 runs sampled)
urijs: serialize IPv6 x 192,218 ops/sec ±1.24% (94 runs sampled)
fast-uri: serialize ws x 1,218,515 ops/sec ±1.29% (95 runs sampled)
urijs: serialize ws x 246,424 ops/sec ±1.12% (95 runs sampled)
fast-uri: resolve x 473,679 ops/sec ±0.92% (94 runs sampled)
urijs: resolve x 161,916 ops/sec ±0.58% (94 runs sampled)

@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 15, 2025

But there is one question left: Is it still everything according to the RFC? :D.

@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 15, 2025

I wrote some more unit tests to ensure that the rfc is met...well it wasnt. I missed two continue operators.

I pushed into this PR.

@Fdawgs Fdawgs removed their request for review August 15, 2025 15:03
Copy link
Member

@gurgunday gurgunday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Member

@zekth zekth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent

@Uzlopak Uzlopak merged commit f0160b0 into fastify:main Aug 16, 2025
26 checks passed
mergify bot added a commit to ArcadeData/arcadedb that referenced this pull request Oct 12, 2025
…n /studio [skip ci]

Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.0.6 to 3.1.0.
Release notes

*Sourced from [fast-uri's releases](https://github.com/fastify/fast-uri/releases).*

> v3.1.0
> ------
>
> What's Changed
> --------------
>
> * ci: remove master branch support by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#126](https://redirect.github.com/fastify/fast-uri/pull/126)
> * chore(test) remove .gitkeep by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#128](https://redirect.github.com/fastify/fast-uri/pull/128)
> * ci(ci): set job permissions by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#129](https://redirect.github.com/fastify/fast-uri/pull/129)
> * ci: set permissions at workflow level by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#131](https://redirect.github.com/fastify/fast-uri/pull/131)
> * ci: set workflow permissions to read-only by default by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#132](https://redirect.github.com/fastify/fast-uri/pull/132)
> * ci(ci): restore job level permissions by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#133](https://redirect.github.com/fastify/fast-uri/pull/133)
> * build(deps-dev): bump tsd from 0.31.2 to 0.32.0 by [`@​dependabot`](https://github.com/dependabot)[bot] in [fastify/fast-uri#134](https://redirect.github.com/fastify/fast-uri/pull/134)
> * ci(ci): pin actions to commit-hash by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#135](https://redirect.github.com/fastify/fast-uri/pull/135)
> * ci: add node 24 to test matrix by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#136](https://redirect.github.com/fastify/fast-uri/pull/136)
> * ci: use tags for immutable github actions by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#137](https://redirect.github.com/fastify/fast-uri/pull/137)
> * chore(license): update date ranges; standardise style by [`@​Fdawgs`](https://github.com/Fdawgs) in [fastify/fast-uri#138](https://redirect.github.com/fastify/fast-uri/pull/138)
> * fix(lib/utils): simpler algorithm for removeDotSegments by [`@​wooffie`](https://github.com/wooffie) in [fastify/fast-uri#140](https://redirect.github.com/fastify/fast-uri/pull/140)
> * chore: remove uri-js by [`@​Uzlopak`](https://github.com/Uzlopak) in [fastify/fast-uri#142](https://redirect.github.com/fastify/fast-uri/pull/142)
> * chore: replace benchmark with tinybench by [`@​Uzlopak`](https://github.com/Uzlopak) in [fastify/fast-uri#143](https://redirect.github.com/fastify/fast-uri/pull/143)
> * fix: ipv4 is never normalized by [`@​Uzlopak`](https://github.com/Uzlopak) in [fastify/fast-uri#144](https://redirect.github.com/fastify/fast-uri/pull/144)
> * feat: add browser testing by [`@​Uzlopak`](https://github.com/Uzlopak) in [fastify/fast-uri#146](https://redirect.github.com/fastify/fast-uri/pull/146)
> * Update README.md by [`@​zekth`](https://github.com/zekth) in [fastify/fast-uri#145](https://redirect.github.com/fastify/fast-uri/pull/145)
> * chore: use jsdoc for static code analysis by [`@​Uzlopak`](https://github.com/Uzlopak) in [fastify/fast-uri#147](https://redirect.github.com/fastify/fast-uri/pull/147)
> * fix(lib/schemes/urn): check nid for undefined by [`@​wooffie`](https://github.com/wooffie) in [fastify/fast-uri#141](https://redirect.github.com/fastify/fast-uri/pull/141)
>
> New Contributors
> ----------------
>
> * [`@​wooffie`](https://github.com/wooffie) made their first contribution in [fastify/fast-uri#140](https://redirect.github.com/fastify/fast-uri/pull/140)
>
> **Full Changelog**: <fastify/fast-uri@v3.0.6...v3.1.0>


Commits

* [`2d08e68`](fastify/fast-uri@2d08e68) v3.1.0
* [`4f8cdd1`](fastify/fast-uri@4f8cdd1) fix(lib/schemes/urn): check nid for undefined ([#141](https://redirect.github.com/fastify/fast-uri/issues/141))
* [`eedf840`](fastify/fast-uri@eedf840) chore: use jsdoc for static code analysis ([#147](https://redirect.github.com/fastify/fast-uri/issues/147))
* [`fbf4545`](fastify/fast-uri@fbf4545) Update README.md ([#145](https://redirect.github.com/fastify/fast-uri/issues/145))
* [`56b0c74`](fastify/fast-uri@56b0c74) feat: add browser testing ([#146](https://redirect.github.com/fastify/fast-uri/issues/146))
* [`ee22a06`](fastify/fast-uri@ee22a06) fix: ipv4 is never normalized ([#144](https://redirect.github.com/fastify/fast-uri/issues/144))
* [`509dd94`](fastify/fast-uri@509dd94) chore: replace benchmark with tinybench ([#143](https://redirect.github.com/fastify/fast-uri/issues/143))
* [`7a16582`](fastify/fast-uri@7a16582) chore: remove uri-js ([#142](https://redirect.github.com/fastify/fast-uri/issues/142))
* [`f0160b0`](fastify/fast-uri@f0160b0) fix(lib/utils): simpler algorithm for removeDotSegments ([#140](https://redirect.github.com/fastify/fast-uri/issues/140))
* [`14d12da`](fastify/fast-uri@14d12da) chore(license): update date ranges; standardise style ([#138](https://redirect.github.com/fastify/fast-uri/issues/138))
* Additional commits viewable in [compare view](fastify/fast-uri@v3.0.6...v3.1.0)
  
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility\_score?dependency-name=fast-uri&package-manager=npm\_and\_yarn&previous-version=3.0.6&new-version=3.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
You can trigger a rebase of this PR by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
  
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show  ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
> \*\*Note\*\*
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: fatal error for old nodes

5 participants