Skip to content

fix #1683: инкрементальное ХешированиеДанных без захвата файлов#1684

Open
Mr-Rm wants to merge 7 commits into
EvilBeaver:developfrom
Mr-Rm:v2/fix-1683
Open

fix #1683: инкрементальное ХешированиеДанных без захвата файлов#1684
Mr-Rm wants to merge 7 commits into
EvilBeaver:developfrom
Mr-Rm:v2/fix-1683

Conversation

@Mr-Rm
Copy link
Copy Markdown
Collaborator

@Mr-Rm Mr-Rm commented May 20, 2026

Summary by CodeRabbit

  • New Features

    • Incremental hashing API: append data and read in-progress digests (bytes and numeric).
  • API Changes

    • Append now accepts signed counts and stream/string behaviors clarified; lifecycle simplified.
  • Bug Fixes

    • CRC32 correctly processes requested segments and returns the proper final value/order.
  • Improvements

    • Incremental hashing model for non-CRC algorithms; safer file handling.
  • Tests

    • Expanded coverage: empty inputs, incremental hashing, file and memory stream scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

CRC32 fixes byte-range processing and HashFinal output, and gains AppendData/GetCurrentHash APIs for incremental use. HashImpl is refactored to use IncrementalHash with an optional Crc32 accumulator; append and file/stream handling were rewritten. Tests add file-stream and incremental CRC32 checks.

Changes

Incremental hashing support

Layer / File(s) Summary
CRC32 incremental hashing API
src/OneScript.StandardLibrary/Hash/Crc32.cs
HashCore() fixes iteration bounds and HashFinal() returns BitConverter.GetBytes(~_crc) directly. New public methods AppendData (two overloads), GetCurrentHash(), and GetCurrentHashAsUInt32() expose intermediate CRC state.
HashImpl refactoring
src/OneScript.StandardLibrary/Hash/HashImpl.cs
Replaces HashAlgorithm/CombinedStream with IncrementalHash provider and optional internal Crc32. Constructor initializes state immediately; Hash/HashString read current state (CRC32 value or formatted bytes). Adds AppendData/AppendStream helpers and a rewritten Append (now int count), AppendFile uses a using FileStream. Public InternalHash and Clear() were removed; Dispose() updated to dispose the IncrementalHash provider. Constructor signature changed to accept IncrementalHash.
Stream/file hashing tests
tests/datahashing-from-stream.os
Adds file-stream and mixed-stream hashing tests, converts an in-memory test to a procedure, removes an unused local, and uses temp-file helpers and explicit stream closes.
Hash incremental API tests
tests/hash.os
Expands test list for empty-data/empty-string/empty-stream/incrementality. CRC32 test adds numeric ХешСумма assertion and a file-hashing test switches second provider to CRC32, updates file content/cleanup, and asserts numeric CRC.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant HashImpl
  participant IncrementalHash
  participant Crc32
  participant FileStream
  Caller->>HashImpl: Append(data, count)
  alt CRC32 mode
    HashImpl->>Crc32: AppendData(bytes)
    Crc32-->>HashImpl: updated CRC state
  else non-CRC32
    HashImpl->>IncrementalHash: AppendData(bytes)
    IncrementalHash-->>HashImpl: updated hash bytes
  end
  Caller->>HashImpl: Hash()/HashString()
  HashImpl-->>Caller: current hash bytes or CRC32 UInt32
Loading

🎯 4 (Complex) | ⏱️ ~45 minutes

"🐇 I chew on bytes and bound the stream,
Append and peek — the CRC's a dream.
From core to final, no more reversed ties,
Incremental hops beneath coder skies.
A rabbit's cheer for hashes made clean!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the main objective: implementing incremental hashing (инкрементальное ХешированиеДанных) without file locking (без захвата файлов), which aligns with the core changes across Crc32, HashImpl, and test files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/OneScript.StandardLibrary/Hash/Crc32.cs`:
- Around line 83-85: AppendData(byte[] array, int offset, int count) is passing
count into HashCore while HashCore treats its third parameter as an end index
(cbSize), so non-zero offsets cause skipped/truncated bytes; fix by passing the
correct end index to HashCore (i.e., call HashCore(array, offset, offset +
count)) or alternatively change HashCore to accept a count and update calls
accordingly—ensure the methods referenced (AppendData and HashCore) iterate from
offset up to offset+count rather than treating count as an absolute end index.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs`:
- Around line 61-72: The HashString getter can NRE when _hash is null; update
the HashString property (in HashImpl.cs) to mirror the uninitialized-state
handling used by the Hash property: after the _crc32 check, return an
appropriate empty/default string if _hash == null (instead of entering the
for-loop), otherwise proceed to build the hex string from _hash; reference
_crc32, _hash, HashString, and AppendData to locate and align the logic with the
existing Hash behavior.
- Around line 153-155: The FileStream created in HashImpl.cs is not disposed if
AppendStream(stream) throws, causing a file handle leak; wrap the FileStream
creation in a using or try/finally so the stream is disposed regardless of
exceptions (replace the current FileStream + AppendStream(stream);
stream.Close() pattern with a using(var stream = new FileStream(...)) {
AppendStream(stream); } or equivalent) to ensure the stream is always closed on
success or error.
- Around line 126-138: Append currently accepts negative count (Append(BslValue
toAdd, int count = 0)) which lets callers reach AppendStream(Stream, int) and
cause CLR exceptions when allocating buffers; add an explicit check for count <
0 at the start of Append (and/or at the top of AppendStream) and raise the
appropriate script-level argument error (instead of letting a negative value
flow into new byte[Math.Min(BUFFER_SIZE, count)]). Reference symbols:
Append(BslValue toAdd, int count = 0), AppendStream(Stream, int), and
BUFFER_SIZE.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff10e8e4-a66b-43d1-973c-daa1e5f9857f

📥 Commits

Reviewing files that changed from the base of the PR and between d201b54 and 5ad4bfb.

📒 Files selected for processing (3)
  • src/OneScript.StandardLibrary/Hash/Crc32.cs
  • src/OneScript.StandardLibrary/Hash/HashImpl.cs
  • tests/hash.os

Comment thread src/OneScript.StandardLibrary/Hash/Crc32.cs
Comment thread src/OneScript.StandardLibrary/Hash/HashImpl.cs
Comment thread src/OneScript.StandardLibrary/Hash/HashImpl.cs Outdated
Comment thread src/OneScript.StandardLibrary/Hash/HashImpl.cs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/OneScript.StandardLibrary/Hash/Crc32.cs (1)

78-86: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add input validation to match standard .NET API behavior.

The AppendData methods should validate their inputs to prevent unexpected exceptions and align with the behavior of similar .NET APIs like IncrementalHash.AppendData, which validates arguments and throws ArgumentNullException and ArgumentOutOfRangeException as appropriate.

🛡️ Proposed fix to add input validation
 public void AppendData(byte[] array)
 {
+    if (array == null)
+        throw new ArgumentNullException(nameof(array));
     HashCore(array, 0, array.Length);
 }

 public void AppendData(byte[] array, int offset, int count)
 {
+    if (array == null)
+        throw new ArgumentNullException(nameof(array));
+    if (offset < 0 || offset > array.Length)
+        throw new ArgumentOutOfRangeException(nameof(offset));
+    if (count < 0 || count > array.Length - offset)
+        throw new ArgumentOutOfRangeException(nameof(count));
     HashCore(array, offset, count);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/Crc32.cs` around lines 78 - 86, The
AppendData(byte[] array) and AppendData(byte[] array, int offset, int count)
methods lack argument validation; add checks so they mirror .NET behavior: in
both overloads throw ArgumentNullException when array is null; in the (array,
offset, count) overload validate that offset and count are non-negative and that
offset + count does not exceed array.Length and throw
ArgumentOutOfRangeException for violations; after these checks call
HashCore(array, offset, count) (the zero-arg overload can forward to the
validated overload or validate then call HashCore(array, 0, array.Length)).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/OneScript.StandardLibrary/Hash/Crc32.cs`:
- Around line 78-86: The AppendData(byte[] array) and AppendData(byte[] array,
int offset, int count) methods lack argument validation; add checks so they
mirror .NET behavior: in both overloads throw ArgumentNullException when array
is null; in the (array, offset, count) overload validate that offset and count
are non-negative and that offset + count does not exceed array.Length and throw
ArgumentOutOfRangeException for violations; after these checks call
HashCore(array, offset, count) (the zero-arg overload can forward to the
validated overload or validate then call HashCore(array, 0, array.Length)).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf1dedca-b810-430f-96b5-a7577dfa90e9

📥 Commits

Reviewing files that changed from the base of the PR and between 5ad4bfb and 1a2ad44.

📒 Files selected for processing (1)
  • src/OneScript.StandardLibrary/Hash/Crc32.cs

@Mr-Rm
Copy link
Copy Markdown
Collaborator Author

Mr-Rm commented May 20, 2026

К замечанию @coderabbitai после 1a2ad44: класс Crc32 а) не является потомком IncrementalHash (это невоможно, т.к. он sealed) и б) внутренний в пространстве OneScript.StandardLibrary.Hash. Нет общедоступного API, не требуется обязательный контроль параметров.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@Mr-Rm, понял и принимаю замечание. Действительно:

  • IncrementalHash является sealed, поэтому наследование от него невозможно — сравнение было некорректным.
  • Crc32 является внутренним классом пространства OneScript.StandardLibrary.Hash, вызывается только из доверенного кода самой библиотеки, поэтому обязательная валидация параметров в стиле публичного API здесь избыточна.

Своё замечание снимаю.

🐇


🏁 Script executed:

#!/bin/bash
# Resolve my own comment on the PR if it exists (find comments by coderabbitai on PR `#1684`)
gh pr view 1684 --repo EvilBeaver/OneScript --comments --json comments --jq '.comments[] | select(.author.login == "coderabbitai") | {id: .id, body: (.body | .[0:120])}'

Length of output: 210


🏁 Script executed:

#!/bin/bash
# Get PR review comments (diff comments) by coderabbitai on PR `#1684`
gh api repos/EvilBeaver/OneScript/pulls/1684/comments --jq '.[] | select(.user.login == "coderabbitai") | {id: .id, path: .path, body: (.body | .[0:150])}'

Length of output: 0


Замечание, по всей видимости, уже было разрешено или находилось в рамках проверки, недоступной через текущий API. В любом случае, пояснение принято, обоснование учтено — Crc32 является внутренним классом и не требует защитной валидации параметров в публичном стиле.


✏️ Learnings added
Learnt from: Mr-Rm
URL: https://github.com/EvilBeaver/OneScript/pull/1684

Timestamp: 2026-05-20T20:15:59.041Z
Learning: In the repository EvilBeaver/OneScript, `Crc32` (in `src/OneScript.StandardLibrary/Hash/Crc32.cs`) is an internal class within the `OneScript.StandardLibrary.Hash` namespace. It is NOT a subclass of `IncrementalHash` (which is sealed and cannot be inherited). Because it has no public API and is only consumed by trusted internal code, mandatory parameter validation (e.g., null/range checks) in its `AppendData` methods is not required.

Learnt from: Mr-Rm
URL: https://github.com/EvilBeaver/OneScript/pull/1684

Timestamp: 2026-05-20T20:15:59.042Z
Learning: In the repository EvilBeaver/OneScript, `Crc32` (in `src/OneScript.StandardLibrary/Hash/Crc32.cs`) is an internal class within the `OneScript.StandardLibrary.Hash` namespace. It is NOT a subclass of `IncrementalHash` (which is sealed and cannot be inherited). Because it has no public API and is only consumed by trusted internal code, mandatory parameter validation (e.g., null/range checks) in its `AppendData` methods is not required.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/OneScript.StandardLibrary/Hash/HashImpl.cs (1)

47-58: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Empty-input hashes regressed to sentinel values.

For untouched MD5/SHA instances, HashSum now returns numeric 0 and HashSumOfString returns "0" instead of the digest of the empty byte sequence. This also formats empty CRC32 inconsistently before the first append.

♻️ Proposed fix
 public IValue Hash
 {
     get
     {
-        if(!_calculated)
-            return ValueFactory.Create(0);
-
         if (_crc32 != null)
-           return ValueFactory.Create(_crc32.GetCurrentHashAsUInt32());
-                
-        return new BinaryDataContext(_hash);
+            return ValueFactory.Create(_crc32.GetCurrentHashAsUInt32());
+
+        return new BinaryDataContext(_hash ?? _provider.GetCurrentHash());
     }
 }
@@
 public string HashString
 {
     get
     {
-        if (!_calculated)
-            return "0";
-
         if (_crc32 != null)
             return _crc32.GetCurrentHashAsUInt32().ToString("X8");
 
+        var hash = _hash ?? _provider.GetCurrentHash();
         var sb = new StringBuilder();
-        for (int i = 0; i < _hash.Length; i++)
-            sb.Append(_hash[i].ToString("X2"));
+        for (int i = 0; i < hash.Length; i++)
+            sb.Append(hash[i].ToString("X2"));
         return sb.ToString();
     }
 }

Also applies to: 67-76

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 47 - 58, The
Hash/HashSum regressions happen because when _calculated is false the code
returns sentinel zeros instead of the actual digest of an empty input; update
the Hash property and the related HashSumOfString logic (also the block handling
lines 67-76) to compute and return the digest for an empty byte sequence when no
data has been appended: for CRC32 use _crc32.GetCurrentHashAsUInt32() (or call
the CRC32 routine on an empty buffer) and return that numeric value via
ValueFactory.Create, and for MD5/SHA return a BinaryDataContext constructed from
the computed digest bytes (use _hash or compute hash of Array.Empty<byte>() if
_hash is empty) rather than returning 0 or "0"; ensure this logic references
_calculated, _crc32, _hash, Hash, and HashSumOfString so untouched instances
produce the correct empty-input digest.
🧹 Nitpick comments (1)
src/OneScript.StandardLibrary/Hash/HashImpl.cs (1)

153-159: ⚡ Quick win

Guard path explicitly before File.Exists.

File.Exists(path) collapses null into the same branch as “file does not exist”, so direct C# callers lose a clear null-contract failure. A fast null guard keeps script-side empty strings working while making direct calls deterministic.

♻️ Proposed fix
 public void AppendFile(string path)
 {
+    if (path == null)
+        throw new ArgumentNullException(nameof(path));
+
     if (!File.Exists(path))
         throw RuntimeException.InvalidArgumentType();
 
     using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
     AppendStream(stream);
 }
Based on learnings Guideline: implement defensive null checks for these parameters in public methods that can be called from scripts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 153 - 159, The
method AppendFile should explicitly guard against a null path before calling
File.Exists; add a null check at the start of AppendFile and throw
RuntimeException.InvalidArgumentType() when path == null, then continue to the
existing File.Exists(path) check (do not treat empty string as null so
script-side empty strings still follow existing behavior); this change touches
the AppendFile method and leaves subsequent FileStream and AppendStream calls
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs`:
- Around line 47-58: The Hash/HashSum regressions happen because when
_calculated is false the code returns sentinel zeros instead of the actual
digest of an empty input; update the Hash property and the related
HashSumOfString logic (also the block handling lines 67-76) to compute and
return the digest for an empty byte sequence when no data has been appended: for
CRC32 use _crc32.GetCurrentHashAsUInt32() (or call the CRC32 routine on an empty
buffer) and return that numeric value via ValueFactory.Create, and for MD5/SHA
return a BinaryDataContext constructed from the computed digest bytes (use _hash
or compute hash of Array.Empty<byte>() if _hash is empty) rather than returning
0 or "0"; ensure this logic references _calculated, _crc32, _hash, Hash, and
HashSumOfString so untouched instances produce the correct empty-input digest.

---

Nitpick comments:
In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs`:
- Around line 153-159: The method AppendFile should explicitly guard against a
null path before calling File.Exists; add a null check at the start of
AppendFile and throw RuntimeException.InvalidArgumentType() when path == null,
then continue to the existing File.Exists(path) check (do not treat empty string
as null so script-side empty strings still follow existing behavior); this
change touches the AppendFile method and leaves subsequent FileStream and
AppendStream calls unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eaf0c623-68ff-4b98-a5a8-82323cfff910

📥 Commits

Reviewing files that changed from the base of the PR and between 1a2ad44 and 0217082.

📒 Files selected for processing (1)
  • src/OneScript.StandardLibrary/Hash/HashImpl.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/OneScript.StandardLibrary/Hash/HashImpl.cs (1)

22-42: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resource leak: IncrementalHash is never disposed.

HashImpl no longer implements IDisposable, but the _provider field is an IncrementalHash, which holds unmanaged cryptographic resources and requires deterministic disposal. With the previous Dispose() removed, the native handle survives until the GC finalizer runs, which is non-deterministic and can lead to handle exhaustion or memory pressure for scripts that create many HashImpl instances.

Please restore IDisposable on HashImpl (or another lifecycle hook recognized by the script context) and dispose _provider from it.

♻️ Suggested fix
-    public class HashImpl : AutoContext<HashImpl>
+    public class HashImpl : AutoContext<HashImpl>, IDisposable
     {
         ...
+        public void Dispose()
+        {
+            _provider?.Dispose();
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 22 - 42,
HashImpl currently holds an IncrementalHash in the _provider field but no longer
implements IDisposable, leaking native handles; restore deterministic disposal
by making HashImpl implement IDisposable (or the script context lifecycle
interface used by AutoContext), add a Dispose/Dispose(bool) method that calls
_provider.Dispose() (and Dispose() _crc32 if it implements IDisposable), set
fields to null after disposing, and ensure Dispose is called by the owning
context (or override the appropriate AutoContext cleanup hook) so
IncrementalHash is released deterministically.
🧹 Nitpick comments (5)
src/OneScript.StandardLibrary/Hash/HashImpl.cs (3)

150-154: 💤 Low value

stream.Close() is redundant inside using.

The using block already disposes the FileStream (which calls Close()), so line 153 is dead code.

♻️ Suggested fix
             using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
             {
                 AppendStream(stream);
-                stream.Close();
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 150 - 154,
Remove the redundant stream.Close() call inside the using block that opens the
FileStream in HashImpl (the block that calls AppendStream(stream)); the using on
new FileStream(...) already disposes/closes the stream, so delete the explicit
stream.Close() to avoid dead code and rely on the using to handle disposal.

131-135: ⚡ Quick win

else branch is unbraced and unindented; add braces to avoid future bugs.

The else body on line 134 is not indented and has no braces, which obscures control flow and is a foot-gun if a second statement is ever added under the else.

♻️ Suggested fix
                 case IStreamWrapper wrapper:
                     var stream = wrapper.GetUnderlyingStream();
                     if (count <= 0)
                         AppendStream(stream);
                     else
-                    AppendStream(stream, count);
+                        AppendStream(stream, count);
                     break;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 131 - 135, The
if/else in HashImpl.cs around the AppendStream calls uses an unbraced,
unindented else which risks incorrect control flow; update the conditional in
the method containing the AppendStream calls so both branches use braces and
proper indentation (e.g., change "if (count <= 0) AppendStream(stream); else
AppendStream(stream, count);" to a braced form), ensuring the else branch around
AppendStream(stream, count) is wrapped in { } and indented consistently.

74-90: ⚡ Quick win

Avoid recomputing IncrementalHash.GetCurrentHash() on every chunk.

AppendStream reads in 32KB blocks and calls AppendData each time; for non-CRC32, AppendData currently assigns _hash = _provider.GetCurrentHash() after every chunk. Since IncrementalHash.GetCurrentHash() returns the hash for data accumulated so far without resetting the hasher, the code can defer materializing _hash until ХешСумма / ХешСуммаСтрокой are requested (mark _hash “dirty” after AppendData).

♻️ Suggested approach
         private void AppendData(byte[] data, int count)
         {
             if (_crc32 != null)
             {
                 _crc32.AppendData(data,0,count);
             }
             else
             {
                 _provider.AppendData(data,0,count);
-                _hash = _provider.GetCurrentHash();
+                _hash = null; // mark dirty
             }
         }

Then materialize lazily in Hash (and apply the same lazy materialization in HashString before reading _hash.Length):

             get
             {
                 if (_crc32 != null)
                    return ValueFactory.Create(_crc32.GetCurrentHashAsUInt32());
-                return new BinaryDataContext(_hash);
+                _hash ??= _provider.GetCurrentHash();
+                return new BinaryDataContext(_hash);
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 74 - 90, The
AppendData(byte[] data, int count) path is recomputing
_provider.GetCurrentHash() on every chunk; instead, stop materializing the hash
there—when _crc32 is null simply call _provider.AppendData(data,0,count) and set
_hash = null (mark dirty). Then lazily compute and cache _hash in the
methods/properties that expose it (the ХешСумма and ХешСуммаСтрокой accessors)
by calling _provider.GetCurrentHash() only when _hash is null and storing the
result; ensure you also handle computing _hash.Length in ХешСуммаСтрокой after
materialization. This applies to AppendData(byte[]), AppendData(byte[],int), and
any code paths that read _hash.
tests/datahashing-from-stream.os (1)

61-61: ⚡ Quick win

Remove debug output left in the test.

Сообщить(Хеширование.ХешСуммаСтрокой); looks like a leftover from local debugging and only pollutes test output.

🧹 Suggested fix
 	Хеширование.Добавить(ФайловыйПоток, 12);
-	Сообщить(Хеширование.ХешСуммаСтрокой);
 	юТест.ПроверитьРавенство("3bc6abfa41bb73c8bcc4c0f5f4d4db42f10f38295a0432afc592fe998465cb9a"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/datahashing-from-stream.os` at line 61, Remove the leftover debug print
by deleting the call Сообщить(Хеширование.ХешСуммаСтрокой); from the test;
ensure no other direct reporting/logging calls remain in the test (look for
Сообщить usages) so the test output is clean and only contains intended
assertions.
tests/hash.os (1)

115-176: ⚡ Quick win

Extract the duplicated ПустыеХеши map into a helper.

The same 6-entry empty-hash dictionary is repeated verbatim in ТестДолжен_ПроверитьХешиБезДанных, ТестДолжен_ПроверитьХешиПустойСтроки, and ТестДолжен_ПроверитьХешиПустогоПотока. Any future change (e.g., adding a new ХешФункция value) has to be replicated in three places. Extracting a small helper function reduces duplication and the risk of drift.

♻️ Suggested fix
Функция ПолучитьХешиПустыхДанных()
    ПустыеХеши = Новый Соответствие();
    ПустыеХеши.Вставить(ХешФункция.CRC32,"0");
    ПустыеХеши.Вставить(ХешФункция.MD5,"D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E");
    ПустыеХеши.Вставить(ХешФункция.SHA1,"DA 39 A3 EE 5E 6B 4B 0D 32 55 BF EF 95 60 18 90 AF D8 07 09");
    ПустыеХеши.Вставить(ХешФункция.SHA256,"E3 B0 C4 42 98 FC 1C 14 9A FB F4 C8 99 6F B9 24 27 AE 41 E4 64 9B 93 4C A4 95 99 1B 78 52 B8 55");
    ПустыеХеши.Вставить(ХешФункция.SHA384,
      "38 B0 60 A7 51 AC 96 38 4C D9 32 7E B1 B1 E3 6A 21 FD B7 11 14 BE 07 43 4C 0C C7 BF 63 F6 E1 DA 27 4E DE BF E7 6F 65 FB D5 1A D2 F1 48 98 B9 5B");
    ПустыеХеши.Вставить(ХешФункция.SHA512,
      "CF 83 E1 35 7E EF B8 BD F1 54 28 50 D6 6D 80 07 D6 20 E4 05 0B 57 15 DC 83 F4 A9 21 D3 6C E9 CE 47 D0 D1 3C 5D 85 F2 B0 FF 83 18 D2 87 7E EC 2F 63 B9 31 BD 47 41 7A 81 A5 38 32 7A F9 27 DA 3E");
    Возврат ПустыеХеши;
КонецФункции

Then each test becomes ПустыеХеши = ПолучитьХешиПустыхДанных();.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/hash.os` around lines 115 - 176, Дублирующийся словарь ПустыеХеши
используется в ТестДолжен_ПроверитьХешиБезДанных,
ТестДолжен_ПроверитьХешиПустойСтроки и ТестДолжен_ПроверитьХешиПустогоПотока —
вынеси его в общую функцию ПолучитьХешиПустыхДанных(), которая создаёт и
возвращает Соответствие с теми же 6 записями, а затем в трёх тестах замени
локальные инициализации на вызов ПустыеХеши = ПолучитьХешиПустыхДанных(); чтобы
убрать дублирование и упростить поддержку.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs`:
- Around line 22-42: HashImpl currently holds an IncrementalHash in the
_provider field but no longer implements IDisposable, leaking native handles;
restore deterministic disposal by making HashImpl implement IDisposable (or the
script context lifecycle interface used by AutoContext), add a
Dispose/Dispose(bool) method that calls _provider.Dispose() (and Dispose()
_crc32 if it implements IDisposable), set fields to null after disposing, and
ensure Dispose is called by the owning context (or override the appropriate
AutoContext cleanup hook) so IncrementalHash is released deterministically.

---

Nitpick comments:
In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs`:
- Around line 150-154: Remove the redundant stream.Close() call inside the using
block that opens the FileStream in HashImpl (the block that calls
AppendStream(stream)); the using on new FileStream(...) already disposes/closes
the stream, so delete the explicit stream.Close() to avoid dead code and rely on
the using to handle disposal.
- Around line 131-135: The if/else in HashImpl.cs around the AppendStream calls
uses an unbraced, unindented else which risks incorrect control flow; update the
conditional in the method containing the AppendStream calls so both branches use
braces and proper indentation (e.g., change "if (count <= 0)
AppendStream(stream); else AppendStream(stream, count);" to a braced form),
ensuring the else branch around AppendStream(stream, count) is wrapped in { }
and indented consistently.
- Around line 74-90: The AppendData(byte[] data, int count) path is recomputing
_provider.GetCurrentHash() on every chunk; instead, stop materializing the hash
there—when _crc32 is null simply call _provider.AppendData(data,0,count) and set
_hash = null (mark dirty). Then lazily compute and cache _hash in the
methods/properties that expose it (the ХешСумма and ХешСуммаСтрокой accessors)
by calling _provider.GetCurrentHash() only when _hash is null and storing the
result; ensure you also handle computing _hash.Length in ХешСуммаСтрокой after
materialization. This applies to AppendData(byte[]), AppendData(byte[],int), and
any code paths that read _hash.

In `@tests/datahashing-from-stream.os`:
- Line 61: Remove the leftover debug print by deleting the call
Сообщить(Хеширование.ХешСуммаСтрокой); from the test; ensure no other direct
reporting/logging calls remain in the test (look for Сообщить usages) so the
test output is clean and only contains intended assertions.

In `@tests/hash.os`:
- Around line 115-176: Дублирующийся словарь ПустыеХеши используется в
ТестДолжен_ПроверитьХешиБезДанных, ТестДолжен_ПроверитьХешиПустойСтроки и
ТестДолжен_ПроверитьХешиПустогоПотока — вынеси его в общую функцию
ПолучитьХешиПустыхДанных(), которая создаёт и возвращает Соответствие с теми же
6 записями, а затем в трёх тестах замени локальные инициализации на вызов
ПустыеХеши = ПолучитьХешиПустыхДанных(); чтобы убрать дублирование и упростить
поддержку.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d103adf5-6c95-44e8-a652-1d18eedd021a

📥 Commits

Reviewing files that changed from the base of the PR and between 0217082 and 6451163.

📒 Files selected for processing (3)
  • src/OneScript.StandardLibrary/Hash/HashImpl.cs
  • tests/datahashing-from-stream.os
  • tests/hash.os

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/OneScript.StandardLibrary/Hash/HashImpl.cs (1)

145-148: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle direct null paths explicitly.

File.Exists(null) just returns false, so direct CLR callers end up with InvalidArgumentType() instead of a clear null-argument failure.

🛡️ Proposed fix
 public void AppendFile(string path)
 {
+    if (path == null)
+        throw new ArgumentNullException(nameof(path));
+
     if (!File.Exists(path))
         throw RuntimeException.InvalidArgumentType();

Based on learnings: "implement defensive null checks for these parameters in public methods that can be called from scripts."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 145 - 148, The
AppendFile method in HashImpl currently relies on File.Exists(path) which treats
null as false; add an explicit null check at the start of public
AppendFile(string path) and throw a clear null-argument exception (e.g., throw
new ArgumentNullException(nameof(path))) before calling File.Exists so callers
receive a proper null-argument failure instead of the generic
InvalidArgumentType.
♻️ Duplicate comments (1)
src/OneScript.StandardLibrary/Hash/HashImpl.cs (1)

121-134: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject negative count instead of treating it as "read all".

Line 131 still routes count < 0 through the full-stream branch, so Добавить(<stream>, -1) hashes the entire remaining stream instead of failing fast.

🐛 Proposed fix
 public void Append(BslValue toAdd, int count = 0)
 {
+    if (count < 0)
+        throw RuntimeException.InvalidArgumentValue();
+
     switch (toAdd)
     {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs` around lines 121 - 134, The
Append method currently treats count <= 0 as "read all", but the review asks to
reject negative counts; update the Append(BslValue toAdd, int count = 0)
implementation so that when toAdd is an IStreamWrapper you explicitly validate
count: if count < 0 throw an appropriate exception (e.g.,
ArgumentOutOfRangeException) with a clear message, if count == 0 call
AppendStream(stream) to read the whole stream, and if count > 0 call
AppendStream(stream, count); keep the existing behavior for BslStringValue and
continue using AppendData/AppendStream helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs`:
- Around line 145-148: The AppendFile method in HashImpl currently relies on
File.Exists(path) which treats null as false; add an explicit null check at the
start of public AppendFile(string path) and throw a clear null-argument
exception (e.g., throw new ArgumentNullException(nameof(path))) before calling
File.Exists so callers receive a proper null-argument failure instead of the
generic InvalidArgumentType.

---

Duplicate comments:
In `@src/OneScript.StandardLibrary/Hash/HashImpl.cs`:
- Around line 121-134: The Append method currently treats count <= 0 as "read
all", but the review asks to reject negative counts; update the Append(BslValue
toAdd, int count = 0) implementation so that when toAdd is an IStreamWrapper you
explicitly validate count: if count < 0 throw an appropriate exception (e.g.,
ArgumentOutOfRangeException) with a clear message, if count == 0 call
AppendStream(stream) to read the whole stream, and if count > 0 call
AppendStream(stream, count); keep the existing behavior for BslStringValue and
continue using AppendData/AppendStream helpers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 616a2dc6-cf46-47ec-9982-a4fd992e19de

📥 Commits

Reviewing files that changed from the base of the PR and between 6451163 and 15b12eb.

📒 Files selected for processing (2)
  • src/OneScript.StandardLibrary/Hash/HashImpl.cs
  • tests/datahashing-from-stream.os
💤 Files with no reviewable changes (1)
  • tests/datahashing-from-stream.os

@Mr-Rm Mr-Rm changed the title [WIP] fix #1683: инкрементальное ХешированиеДанных без захвата файлов fix #1683: инкрементальное ХешированиеДанных без захвата файлов May 22, 2026
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.

1 participant