Skip to content

Sync Dev - #1024

Merged
boomzero merged 116 commits into
masterfrom
dev
Sep 19, 2026
Merged

boomzero merged 116 commits into
masterfrom
dev

Conversation

@boomzero

@boomzero boomzero commented Sep 19, 2026

Copy link
Copy Markdown
Member

What does this PR aim to accomplish?:

Merge dev into master to publish all changes since stable 3.6.0, covering 3.6.1–3.6.6, as stable 3.7.0. The repository's UpdateToRelease workflow prepares the next stable version (expected 3.7.0).

How does this PR accomplish the above?:

Use a merge commit to preserve development history. Includes the fixes and improvements from #1006, #1013, #1015, #1019, #1021, and #1023. No additional application changes are introduced by this integration PR.

Verification before opening:

  • git merge-tree --write-tree origin/master origin/dev succeeds without conflicts.
  • node --check passes for the dev userscript.
  • Update.json parses successfully; package and userscript versions are both 3.6.6.
  • Existing checks on dev commit 5f348ffaab00f0d97dd7d9b26cc827b2bc4b8576 passed.
  • No other open dev-to-master PR exists.
  • The package has no implemented automated test suite. Browser behavior in the new and classic interfaces has not been verified during this integration.

By submitting this pull request, I confirm the following:

  1. I have read and understood the contributor's guide, as well as this entire template. I understand which branch to base my commits and Pull Requests against. This release integration targets master as requested by the maintainer.
  2. I have commented on my proposed changes within the code. Not applicable: integration of existing changes only.
  3. I have tested my changes. Syntax, JSON, and merge checks completed; functional browser testing is not claimed.
  4. I am willing to help maintain this change if there are issues with it later. Future maintenance commitment remains with the maintainers.
  5. It is compatible with the GNU General Public License v3.0. Existing project changes; no new dependencies or licensing changes introduced by this PR.
  6. I have squashed any insignificant commits. (git rebase) Not applicable: preserve dev history for this branch integration.
  7. I have checked that another pull request for this purpose does not exist.
  8. I have considered and confirmed that this submission will be valuable to others.
  9. I accept that this submission may not be used, and the pull request can be closed at the will of the maintainer.
  10. I give this submission freely and claim no ownership to its content.
  11. I have verified that my changes work correctly in both the new UI and the old/classic UI. Not verified by this assistant; please verify both interfaces before checking this item.

  • I have read the above and my PR is ready for review. Check this box to confirm

Summary by Sourcery

Merge the accumulated development fixes and feature improvements into the stable 3.7.0 release.

New Features:

  • Add local captcha display, submission support, and optional automatic recognition with retry handling.
  • Upgrade data retrieval with gzip/Base93 encoding and an optional high-speed numeric mode while supporting pages without a data-application area.

Bug Fixes:

  • Fix comparison editor sizing, submission-history rendering for problem ID 0, and ended-contest fallback submissions including cooldowns, errors, and full problem IDs.

Enhancements:

  • Preserve development history while consolidating the 3.6.1–3.6.6 fixes and enhancements into the 3.7.0 release.

Chores:

  • Bump the package and userscript versions to 3.7.0.

Summary by cubic

Publishes the accumulated 3.6.1–3.6.6 development updates as stable 3.7.0 by merging dev into master; this merge carries only the existing dev history.

What's included

  • Fixes [Bug] Compare Source Editor 高度值太小 #1004 and the submission-history layout when the problem ID is 0.
  • Replaces the 获取数据 Base64 payload with gzip + Base93 compression, and adds an NSC3 numeric mode with an optional line-break toggle.
  • Shows 获取数据 controls even when the server doesn't render the apply-data area.
  • Shows and submits the server captcha on the submit page, and adds an AutoCaptcha setting that fills 4-digit captchas locally and retries unclear ones.
  • Restores ended-contest fallback submissions: cooldown errors are retried, and server messages are shown instead of a generic failure.

Caveats

  • No automated tests exist, and behavior in the classic and new UIs hasn't been verified.

Written for commit fce1af6. Summary will update on new commits.

Review in cubic

boomzero and others added 30 commits May 3, 2025 19:23
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
sync: dev to extern-contrib
boomzero and others added 21 commits August 23, 2026 11:44
vcode.php 用 imagettftext(15px, Vera.ttf) 在固定位置绘制 4 位数字,
每个字形在不同验证码之间逐像素相同,因此模板匹配可以直接读出来,
不需要把图片发给任何模型。

背景是一种随机色,文字是它的反色,噪点是第三种随机色:取出现次数
最多的颜色作为背景即可定位文字颜色。跳过 1 像素黑边,否则接近白色
的背景会让黑边被当成文字。切分出的字形与 10 个模板逐一比对,除了
奖励命中的笔画,也惩罚模板之外的墨点——否则带噪点的 0 会和 9 打平。

只有切出恰好 4 个字形、且每个字形的最佳得分领先第二名 4 分以上时
才填入,否则留空由用户填写:错误答案会触发 vfail,把验证码升级成
8 位字母数字,比留空更糟。

实测 300 张生成样本 227 正确 0 错误,40 张真实验证码填入 31 张,
14 张人工标注样本 13 正确 1 放弃 0 错误。因为不会填错,AutoCaptcha
恢复默认开启,同时移除 captchaSolve 依赖与相关 @connect。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
验证码为空的拦截原来只在"提交"的处理函数里。检查未通过时页面会留下
"强制提交"按钮,而真正发出请求的是它:这段时间里用户可以点击验证码
图片换一张(会清空输入框)或手动清空,再点"强制提交"就会带着空验证码
发出请求,触发本要避免的 vfail 升级。

把拦截提取为 CaptchaIsMissing,在实际发出请求的 PassCheck 处理函数
开头也调用一次,并在拦截时重新隐藏"强制提交"按钮。

Reported-by: chatgpt-codex-connector
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
回退路径本身可以解析出真实题号,问题在 POST 之后:内层 fetch 只把响应
console.log 掉,从不判断成功与否,随后外层无条件覆盖成"提交失败"。日志里
那次回退实际收到的是 XMOJ 的提交冷却页(`请勿重复提交`,HUSTOJ 的
$OJ_SUBMIT_COOLDOWN_TIME 默认 5 秒,XMOJ 渲染成页面而非 302),响应被丢掉,
所以状态里没有任何提交记录。

- 把回退逻辑抽成 SubmitToEndedContestProblem,返回 {Success, Message}
- 只把 redirected 当成功信号,成功后 return,不再被外层覆盖
- 遇到 `请勿重复提交` 等冷却过去后重试(3 秒一次,最多 5 次)
- 其余失败从响应的 .jumbotron 取服务端原文,不再显示通用报错
- 遇到 `验证码错误` 立即刷新验证码并停止重试(答案已被消耗)
- 题号改用 /\d+/ 提取,原 substring(2, 6) 会把 5 位题号截成 4 位
- 补齐缺失的 GetCaptchaParameter(),并在解析失败时恢复提交按钮

Closes #1017

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
冷却等待期间验证码输入框和刷新按钮仍可交互。如果用户在这 3 秒里清空了
输入框或刷新了图片,下一次重试会用空的 GetCaptchaParameter() 发出请求,
而空答案会让服务端把本 session 的 4 位验证码换成 8 位。

在循环每次 POST 之前调用 CaptchaIsMissing()(它自己会提示并恢复按钮),
并用 Handled 标志让外层直接返回,不覆盖它设置的提示。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
contest.php 或 submit.php 的 fetch 被 reject(网络错误)时,异常会一路穿出
SubmitToEndedContestProblem、穿出 .then 回调、穿出 PassCheck 的 async 监听器
——整条链上没有任何 catch。结果 ShowSubmitStatus 和恢复按钮的两行都不会执行,
提交按钮永远停在"正在提交...",而错误框在监听器开头已经被设成 display: none,
用户什么提示都看不到。这正是本 PR 声称要修掉的那个症状。

把两处网络请求都包进 try/catch,失败时返回错误信息交给外层显示。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ack-1017

fix: 比赛结束后回退提交不再被静默丢弃
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat: 验证码识别不出时自动换一张
@sourcery-ai

sourcery-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This integration release merges the accumulated dev fixes through 3.6.6 into master, primarily improving captcha-aware submissions, ended-contest recovery, compressed test-data retrieval with an NSC numeric mode, and several UI edge cases; metadata is updated while functional browser verification remains outstanding.

Sequence diagram for captcha-aware code submission

sequenceDiagram
    participant User
    participant SubmitPage
    participant Captcha as vcode.php
    participant Submit as submit.php

    User->>SubmitPage: Open submitpage.php
    SubmitPage->>SubmitPage: NativeCaptchaShown
    opt Captcha required
        SubmitPage->>Captcha: fetch
        Captcha-->>SubmitPage: ImageBlob
        SubmitPage->>SubmitPage: SolveCaptcha
        SubmitPage-->>User: Display image and optional answer
    end
    User->>SubmitPage: Click Submit
    SubmitPage->>SubmitPage: CaptchaIsMissing
    SubmitPage->>Submit: POST source and GetCaptchaParameter
    alt Captcha error
        Submit-->>SubmitPage: 验证码错误
        SubmitPage->>Captcha: RefreshCaptcha
        SubmitPage-->>User: Request corrected captcha
    else Accepted or other response
        Submit-->>SubmitPage: Submission response
    end
Loading

Sequence diagram for ended-contest submission recovery

sequenceDiagram
    participant User
    participant SubmitPage
    participant Contest as contest.php
    participant Submit as submit.php

    User->>SubmitPage: Submit contest solution
    SubmitPage->>Submit: POST cid and pid
    Submit-->>SubmitPage: 没有这个比赛
    SubmitPage->>SubmitPage: SubmitToEndedContestProblem
    SubmitPage->>Contest: fetch contest page
    Contest-->>SubmitPage: Contest problem table
    SubmitPage->>SubmitPage: Resolve original PID
    loop Cooldown retries up to 5 attempts
        SubmitPage->>Submit: POST original problem and source
        alt Accepted or redirected
            Submit-->>SubmitPage: Submission created
        else 请勿重复提交
            SubmitPage->>SubmitPage: Wait 3 seconds
        else Network or server failure
            Submit-->>SubmitPage: Error message
        end
    end
    SubmitPage-->>User: Show submission status
Loading

Flow diagram for test-data decoding modes

flowchart TD
    Start[Get data] --> Extract[ExtractData]
    Extract --> Base93[Base93Decode]
    Base93 --> Format{Payload format}
    Format -->|gzip| Gzip[GzipDecode]
    Format -->|NSC1 or NSC3| Numeric[NumberStreamDecode]
    Gzip --> Display[DecodeBytesForDisplay]
    Numeric --> Display
    Display --> Results[Show decoded data]
Loading

File-Level Changes

Change Details Files
Promote the accumulated development changes and align release metadata for version 3.6.6.
  • Adds the release manifest/update data.
  • Bumps package and userscript versions from 3.6.0 to 3.6.6.
  • Preserves the dev-to-master history through the integration merge.
Update.json
package.json
XMOJ.user.js
Add captcha-aware submission handling with local recognition and resilient retries.
  • Detects server-rendered or forced captcha requirements and manages a single-image/session challenge flow.
  • Adds optional local four-digit template matching with confidence checks, automatic refreshes, manual fallback, and input locking during fetches.
  • Rechecks captcha state immediately before submission and handles captcha errors without consuming an unintended retry.
  • Adds ended-contest fallback submission to the original problem, cooldown retries, and network/error reporting.
XMOJ.user.js
Replace legacy test-data encoding and retrieval with gzip/Base93 and optional NSC numeric compression.
  • Adds client-side Base93, gzip, NSC1, and NSC3 decoding with validation and invalid-UTF-8 fallback.
  • Generates updated C++ data-fetch helpers, including high-speed numeric mode and configurable line-break preservation.
  • Supports missing application controls and guards optional data elements before attaching UI handlers.
XMOJ.user.js
Fix submission-list rendering and small interface behaviors.
  • Handles problem ID 0 rows without assuming a resubmit link and constructs links through DOM APIs.
  • Applies default-off settings consistently when initializing utility settings and controls.
  • Adds merge-view sizing CSS and updates data-panel behavior for pages without the legacy application element.
XMOJ.user.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@hendragon-bot hendragon-bot Bot added the user-script This issue or pull request is related to the main user script label Sep 19, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="XMOJ.user.js" line_range="4630" />
<code_context>
                             "referrer": location.href,
                             "method": "POST",
-                            "body": (SearchParams.get("id") != null ? "id=" + SearchParams.get("id") : "cid=" + SearchParams.get("cid") + "&pid=" + SearchParams.get("pid")) + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch
+                            "body": (SearchParams.get("id") != null ? "id=" + SearchParams.get("id") : "cid=" + SearchParams.get("cid") + "&pid=" + SearchParams.get("pid")) + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch + GetCaptchaParameter()
                         }).then(async (Response) => {
                             if (Response.redirected) {
</code_context>
<issue_to_address>
**issue (bug_risk):** The `PassCheck` handler awaits the direct `submit.php` fetch without a `try/catch`. When that request or `Response.text()` rejects because of a network failure, the async listener exits before restoring `Submit.disabled`, its label, or the error message, leaving the submission UI stuck on “正在提交...”.

**Triggers:** When the direct submission request fails at the network or response-reading stage.

**Suggested fix:** Wrap the direct submission fetch and response parsing in `try/catch`, then show the failure and restore the submit controls in the error path.
</issue_to_address>

### Comment 2
<location path="XMOJ.user.js" line_range="6036" />
<code_context>
+vector<T> tk(const string&s){int N=s.size(),Z=1<<16;vector<int>h(Z,-1),p(1<<15,-1);vector<T>v;v.reserve(N/5+1);auto H=[&](int i){return ((uint32_t)(uint8_t)s[i]*63001u+(uint32_t)(uint8_t)s[i+1]*251u+(uint8_t)s[i+2])&(Z-1);};auto I=[&](int i){if(i+2<N){int z=H(i);p[i&32767]=h[z];h[z]=i;}};auto M=[&](int i){int l=0,d=0;if(i+2>=N)return make_pair(0,0);int c=h[H(i)],m=min(258,N-i),q=0;const char*a=s.data()+i;while(c>=0&&i-c<=32768){int lim=l>=8?64:128;if(q++>=lim)break;const char*b=s.data()+c;if(b[0]!=a[0]||b[1]!=a[1]||b[2]!=a[2]){c=p[c&32767];continue;}if(l>=3&&(b[l]!=a[l]||memcmp(a,b,l))){c=p[c&32767];continue;}int z=l>=3?l+1:3;while(z+8<=m){uint64_t x,y;memcpy(&x,a+z,8);memcpy(&y,b+z,8);if(x!=y)break;z+=8;}while(z<m&&a[z]==b[z])z++;if(z>l)l=z,d=i-c;if(z>=128||z==m)break;c=p[c&32767];}return make_pair(l,d);};for(int i=0;i<N;){pair<int,int>u=M(i);int l=u.first,d=u.second;I(i);if(l>=3&&l<16&&i+1<N){pair<int,int>q=M(i+1);if(q.first>l){v.push_back(T((uint8_t)s[i++],0));continue;}}if(l>=3){v.push_back(T(l,d));for(int j=1;j<l;j++)I(i+j);i+=l;}else v.push_back(T((uint8_t)s[i++],0));}return v;}
</code_context>
<issue_to_address>
**issue (bug_risk):** The generated gzip compressor's match search reads `b[l]` and `memcmp(a, b, l)` without verifying that the candidate match at `c` has at least `l` remaining bytes. A shorter candidate near the end of the input is therefore compared past its valid range, and the subsequent match extension can use invalid bytes, producing corrupted gzip payloads or undefined behavior in the generated program.

**Triggers:** When gzip mode processes repetitive input with a previously considered match that is shorter than the current best match, especially near the end of the input.

**Suggested fix:** Track each candidate's remaining length and reject candidates shorter than `l` before accessing `b[l]` or comparing `l` bytes.

```suggestion
vector<T> tk(const string&s){int N=s.size(),Z=1<<16;vector<int>h(Z,-1),p(1<<15,-1);vector<T>v;v.reserve(N/5+1);auto H=[&](int i){return ((uint32_t)(uint8_t)s[i]*63001u+(uint32_t)(uint8_t)s[i+1]*251u+(uint8_t)s[i+2])&(Z-1);};auto I=[&](int i){if(i+2<N){int z=H(i);p[i&32767]=h[z];h[z]=i;}};auto M=[&](int i){int l=0,d=0;if(i+2>=N)return make_pair(0,0);int c=h[H(i)],m=min(258,N-i),q=0;const char*a=s.data()+i;while(c>=0&&i-c<=32768){int lim=l>=8?64:128;if(q++>=lim)break;const char*b=s.data()+c;int r=min(m,N-c);if(b[0]!=a[0]||b[1]!=a[1]||b[2]!=a[2]){c=p[c&32767];continue;}if(l>=3&&(r<=l||b[l]!=a[l]||memcmp(a,b,l))){c=p[c&32767];continue;}int z=l>=3?l+1:3;while(z+8<=r){uint64_t x,y;memcpy(&x,a+z,8);memcpy(&y,b+z,8);if(x!=y)break;z+=8;}while(z<r&&a[z]==b[z])z++;if(z>l)l=z,d=i-c;if(z>=128||z==m)break;c=p[c&32767];}return make_pair(l,d);};for(int i=0;i<N;){pair<int,int>u=M(i);int l=u.first,d=u.second;I(i);if(l>=3&&l<16&&i+1<N){pair<int,int>q=M(i+1);if(q.first>l){v.push_back(T((uint8_t)s[i++],0));continue;}}if(l>=3){v.push_back(T(l,d));for(int j=1;j<l;j++)I(i+j);i+=l;}else v.push_back(T((uint8_t)s[i++],0));}return v;}
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and the change automates captcha handling and substantially rewrites submission fallback and data decoding, so a defect could submit incorrect or duplicate code, consume captcha attempts, or display reconstructed input incorrectly. Reverting stops future behavior but cannot retract submissions already created or captcha attempts already consumed.

Blocking findings: XMOJ.user.js:4630, XMOJ.user.js:6036


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread XMOJ.user.js
"referrer": location.href,
"method": "POST",
"body": (SearchParams.get("id") != null ? "id=" + SearchParams.get("id") : "cid=" + SearchParams.get("cid") + "&pid=" + SearchParams.get("pid")) + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch
"body": (SearchParams.get("id") != null ? "id=" + SearchParams.get("id") : "cid=" + SearchParams.get("cid") + "&pid=" + SearchParams.get("pid")) + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch + GetCaptchaParameter()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): The PassCheck handler awaits the direct submit.php fetch without a try/catch. When that request or Response.text() rejects because of a network failure, the async listener exits before restoring Submit.disabled, its label, or the error message, leaving the submission UI stuck on “正在提交...”.

Triggers: When the direct submission request fails at the network or response-reading stage.

Suggested fix: Wrap the direct submission fetch and response parsing in try/catch, then show the failure and restore the submit controls in the error path.

Comment thread XMOJ.user.js
struct W{string o;uint64_t b;int n;W():b(0),n(0){}void p(uint32_t v,int k){b|=(uint64_t)v<<n;n+=k;while(n>=8)o+=char(b),b>>=8,n-=8;}void a(){if(n)o+=char(b),b=0,n=0;}string f(){a();return o;}};uint32_t R(uint32_t x,int n){uint32_t y=0;while(n--)y=y*2+(x&1),x>>=1;return y;}struct T{uint16_t l,d;T(int L=0,int D=0):l(L),d(D){}};
int Lb[]={3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258},Le[]={0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0},Db[]={1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577},De[]={0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
int li(int x){int i=0;while(i<28&&x>=Lb[i+1])i++;return i;}int di(int x){int i=0;while(i<29&&x>=Db[i+1])i++;return i;}
vector<T> tk(const string&s){int N=s.size(),Z=1<<16;vector<int>h(Z,-1),p(1<<15,-1);vector<T>v;v.reserve(N/5+1);auto H=[&](int i){return ((uint32_t)(uint8_t)s[i]*63001u+(uint32_t)(uint8_t)s[i+1]*251u+(uint8_t)s[i+2])&(Z-1);};auto I=[&](int i){if(i+2<N){int z=H(i);p[i&32767]=h[z];h[z]=i;}};auto M=[&](int i){int l=0,d=0;if(i+2>=N)return make_pair(0,0);int c=h[H(i)],m=min(258,N-i),q=0;const char*a=s.data()+i;while(c>=0&&i-c<=32768){int lim=l>=8?64:128;if(q++>=lim)break;const char*b=s.data()+c;if(b[0]!=a[0]||b[1]!=a[1]||b[2]!=a[2]){c=p[c&32767];continue;}if(l>=3&&(b[l]!=a[l]||memcmp(a,b,l))){c=p[c&32767];continue;}int z=l>=3?l+1:3;while(z+8<=m){uint64_t x,y;memcpy(&x,a+z,8);memcpy(&y,b+z,8);if(x!=y)break;z+=8;}while(z<m&&a[z]==b[z])z++;if(z>l)l=z,d=i-c;if(z>=128||z==m)break;c=p[c&32767];}return make_pair(l,d);};for(int i=0;i<N;){pair<int,int>u=M(i);int l=u.first,d=u.second;I(i);if(l>=3&&l<16&&i+1<N){pair<int,int>q=M(i+1);if(q.first>l){v.push_back(T((uint8_t)s[i++],0));continue;}}if(l>=3){v.push_back(T(l,d));for(int j=1;j<l;j++)I(i+j);i+=l;}else v.push_back(T((uint8_t)s[i++],0));}return v;}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): The generated gzip compressor's match search reads b[l] and memcmp(a, b, l) without verifying that the candidate match at c has at least l remaining bytes. A shorter candidate near the end of the input is therefore compared past its valid range, and the subsequent match extension can use invalid bytes, producing corrupted gzip payloads or undefined behavior in the generated program.

Triggers: When gzip mode processes repetitive input with a previously considered match that is shorter than the current best match, especially near the end of the input.

Suggested fix: Track each candidate's remaining length and reject candidates shorter than l before accessing b[l] or comparing l bytes.

Suggested change
vector<T> tk(const string&s){int N=s.size(),Z=1<<16;vector<int>h(Z,-1),p(1<<15,-1);vector<T>v;v.reserve(N/5+1);auto H=[&](int i){return ((uint32_t)(uint8_t)s[i]*63001u+(uint32_t)(uint8_t)s[i+1]*251u+(uint8_t)s[i+2])&(Z-1);};auto I=[&](int i){if(i+2<N){int z=H(i);p[i&32767]=h[z];h[z]=i;}};auto M=[&](int i){int l=0,d=0;if(i+2>=N)return make_pair(0,0);int c=h[H(i)],m=min(258,N-i),q=0;const char*a=s.data()+i;while(c>=0&&i-c<=32768){int lim=l>=8?64:128;if(q++>=lim)break;const char*b=s.data()+c;if(b[0]!=a[0]||b[1]!=a[1]||b[2]!=a[2]){c=p[c&32767];continue;}if(l>=3&&(b[l]!=a[l]||memcmp(a,b,l))){c=p[c&32767];continue;}int z=l>=3?l+1:3;while(z+8<=m){uint64_t x,y;memcpy(&x,a+z,8);memcpy(&y,b+z,8);if(x!=y)break;z+=8;}while(z<m&&a[z]==b[z])z++;if(z>l)l=z,d=i-c;if(z>=128||z==m)break;c=p[c&32767];}return make_pair(l,d);};for(int i=0;i<N;){pair<int,int>u=M(i);int l=u.first,d=u.second;I(i);if(l>=3&&l<16&&i+1<N){pair<int,int>q=M(i+1);if(q.first>l){v.push_back(T((uint8_t)s[i++],0));continue;}}if(l>=3){v.push_back(T(l,d));for(int j=1;j<l;j++)I(i+j);i+=l;}else v.push_back(T((uint8_t)s[i++],0));}return v;}
vector<T> tk(const string&s){int N=s.size(),Z=1<<16;vector<int>h(Z,-1),p(1<<15,-1);vector<T>v;v.reserve(N/5+1);auto H=[&](int i){return ((uint32_t)(uint8_t)s[i]*63001u+(uint32_t)(uint8_t)s[i+1]*251u+(uint8_t)s[i+2])&(Z-1);};auto I=[&](int i){if(i+2<N){int z=H(i);p[i&32767]=h[z];h[z]=i;}};auto M=[&](int i){int l=0,d=0;if(i+2>=N)return make_pair(0,0);int c=h[H(i)],m=min(258,N-i),q=0;const char*a=s.data()+i;while(c>=0&&i-c<=32768){int lim=l>=8?64:128;if(q++>=lim)break;const char*b=s.data()+c;int r=min(m,N-c);if(b[0]!=a[0]||b[1]!=a[1]||b[2]!=a[2]){c=p[c&32767];continue;}if(l>=3&&(r<=l||b[l]!=a[l]||memcmp(a,b,l))){c=p[c&32767];continue;}int z=l>=3?l+1:3;while(z+8<=r){uint64_t x,y;memcpy(&x,a+z,8);memcpy(&y,b+z,8);if(x!=y)break;z+=8;}while(z<r&&a[z]==b[z])z++;if(z>l)l=z,d=i-c;if(z>=128||z==m)break;c=p[c&32767];}return make_pair(l,d);};for(int i=0;i<N;){pair<int,int>u=M(i);int l=u.first,d=u.second;I(i);if(l>=3&&l<16&&i+1<N){pair<int,int>q=M(i+1);if(q.first>l){v.push_back(T((uint8_t)s[i++],0));continue;}}if(l>=3){v.push_back(T(l,d));for(int j=1;j<l;j++)I(i+j);i+=l;}else v.push_back(T((uint8_t)s[i++],0));}return v;}

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 19, 2026

Copy link
Copy Markdown

Deploying xmoj-script-dev-channel with  Cloudflare Pages  Cloudflare Pages

Latest commit: fce1af6
Status: ✅  Deploy successful!
Preview URL: https://ce2b43f8.xmoj-script-dev-channel.pages.dev

View logs

@boomzero
boomzero merged commit 5a0c2e1 into master Sep 19, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XXL user-script This issue or pull request is related to the main user script

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Compare Source Editor 高度值太小

3 participants