Skip to content

Commit d018b18

Browse files
authored
Merge pull request #1684 from Mr-Rm/v2/fix-1683
fix #1683: инкрементальное ХешированиеДанных без захвата файлов
2 parents 4ade77d + d29b7ec commit d018b18

4 files changed

Lines changed: 421 additions & 158 deletions

File tree

src/OneScript.StandardLibrary/Hash/Crc32.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,38 @@ public override void Initialize()
6262

6363
protected override void HashCore(byte[] array, int ibStart, int cbSize)
6464
{
65-
for (var i = ibStart; i < cbSize - ibStart; i++)
65+
for (var i = ibStart; i < ibStart + cbSize; i++)
6666
_crc = (_crc >> 8) ^ table[array[i] ^ _crc & 0xff];
6767
}
6868

6969
protected override byte[] HashFinal()
7070
{
7171
var result = BitConverter.GetBytes(~_crc);
72-
if (BitConverter.IsLittleEndian)
73-
Array.Reverse(result);
7472

7573
HashValue = result;
7674
return result;
77-
}
78-
75+
}
76+
77+
#region IncrementalHash
78+
public void AppendData(byte[] array)
79+
{
80+
HashCore(array, 0, array.Length);
81+
}
82+
83+
public void AppendData(byte[] array, int offset, int count)
84+
{
85+
HashCore(array, offset, count);
86+
}
87+
88+
public byte[] GetCurrentHash()
89+
{
90+
return BitConverter.GetBytes(~_crc);
91+
}
92+
93+
public UInt32 GetCurrentHashAsUInt32()
94+
{
95+
return ~_crc;
96+
}
97+
#endregion
7998
}
8099
}

src/OneScript.StandardLibrary/Hash/HashImpl.cs

Lines changed: 170 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -5,156 +5,234 @@ This Source Code Form is subject to the terms of the
55
at http://mozilla.org/MPL/2.0/.
66
----------------------------------------------------------*/
77

8-
using System;
9-
using System.IO;
10-
using System.Security.Cryptography;
11-
using System.Text;
128
using OneScript.Contexts;
139
using OneScript.Exceptions;
1410
using OneScript.StandardLibrary.Binary;
1511
using OneScript.Values;
1612
using ScriptEngine.Machine;
1713
using ScriptEngine.Machine.Contexts;
14+
using System;
15+
using System.IO;
16+
using System.Security.Cryptography;
17+
using System.Text;
1818

1919
namespace OneScript.StandardLibrary.Hash
20-
{
20+
{
21+
/// <summary>
22+
/// Реализует инкрементальный расчет хеш-суммы по добавленным данным.
23+
/// Тип вычисляемого значения определяются типом хеш-функции.
24+
/// </summary>
2125
[ContextClass("ХешированиеДанных", "DataHashing")]
2226
public class HashImpl : AutoContext<HashImpl>, IDisposable
23-
{
24-
private HashAlgorithm _provider;
25-
private HashFunctionEnum _enumValue;
26-
private CombinedStream _toCalculate=new CombinedStream();
27-
private bool _calculated;
27+
{
28+
private const int BUFFER_SIZE = (1024 * 32);
29+
30+
private readonly Crc32 _crc32;
31+
private readonly IncrementalHash _provider;
32+
private readonly HashFunctionEnum _enumValue;
2833
private byte[] _hash;
2934

30-
public HashImpl(HashAlgorithm provider, HashFunctionEnum enumValue)
35+
public HashImpl(IncrementalHash provider, HashFunctionEnum enumValue)
3136
{
3237
_provider = provider;
3338
_enumValue = enumValue;
34-
_calculated = false;
35-
}
36-
37-
public byte[] InternalHash
38-
{
39-
get
40-
{
41-
if (!_calculated)
42-
{
43-
_hash = _provider.ComputeHash(_toCalculate);
44-
_calculated = true;
45-
}
46-
return _hash;
47-
}
48-
}
49-
39+
if (enumValue == HashFunctionEnum.CRC32)
40+
{
41+
_crc32 = new Crc32();
42+
}
43+
else ArgumentNullException.ThrowIfNull(provider);
44+
45+
AppendData(Array.Empty<byte>());
46+
}
47+
48+
/// <summary>
49+
/// Вид хеш-функции, определяющий способ вычисления хеш-суммы.
50+
/// Только для чтения
51+
/// </summary>
52+
/// <value>Перечисление ХешФункция</value>
5053
[ContextProperty("ХешФункция", "HashFunction")]
51-
public HashFunctionEnum Extension => _enumValue;
52-
54+
public HashFunctionEnum HashFunction => _enumValue;
55+
56+
/// <summary>
57+
/// Текущее значение хеш-суммы. Только для чтения
58+
/// </summary>
59+
/// <value>Для хеш-функции CRC32 - Число, для остальных - ДвоичныеДанные
60+
/// </value>
5361
[ContextProperty("ХешСумма", "HashSum")]
5462
public IValue Hash
5563
{
5664
get
5765
{
58-
if (_provider is Crc32)
59-
{
60-
var buffer = new byte[4];
61-
Array.Copy(InternalHash, buffer, 4);
62-
if (BitConverter.IsLittleEndian)
63-
Array.Reverse(buffer);
64-
var ret = BitConverter.ToUInt32(buffer, 0);
65-
return ValueFactory.Create((decimal)ret);
66-
}
67-
return new BinaryDataContext(InternalHash);
66+
if (_crc32 != null)
67+
return ValueFactory.Create(_crc32.GetCurrentHashAsUInt32());
68+
69+
return new BinaryDataContext(_hash);
6870
}
69-
}
70-
71+
}
72+
73+
/// <summary>
74+
/// Нестандартное расширение!
75+
/// Строковое представление текущего значения хеш-суммы. Только для чтения
76+
/// </summary>
77+
/// <value>Для хеш-функции CRC32 - Число, для остальных - ДвоичныеДанные</value>
7178
[ContextProperty("ХешСуммаСтрокой", "HashSumOfString")]
7279
public string HashString
7380
{
7481
get
75-
{
82+
{
83+
if (_crc32 != null)
84+
return _crc32.GetCurrentHashAsUInt32().ToString("X8");
85+
7686
var sb = new StringBuilder();
77-
for (int i = 0; i < InternalHash.Length; i++)
78-
sb.Append(InternalHash[i].ToString("X2"));
87+
for (int i = 0; i < _hash.Length; i++)
88+
sb.Append(_hash[i].ToString("X2"));
7989
return sb.ToString();
8090
}
8191
}
8292

93+
private void AppendData(byte[] data)
94+
{
95+
AppendData(data, data.Length);
96+
}
8397

98+
private void AppendData(byte[] data, int count)
99+
{
100+
if (_crc32 != null)
101+
{
102+
_crc32.AppendData(data,0,count);
103+
}
104+
else
105+
{
106+
_provider.AppendData(data,0,count);
107+
_hash = _provider.GetCurrentHash();
108+
}
109+
}
110+
111+
private void AppendStream(Stream stream)
112+
{
113+
var buffer = new byte[BUFFER_SIZE];
114+
while (true)
115+
{
116+
var read = stream.Read(buffer,0, BUFFER_SIZE);
117+
if (read == 0)
118+
break;
119+
120+
AppendData(buffer, read);
121+
}
122+
}
123+
124+
private void AppendStream(Stream stream, int count)
125+
{
126+
if (count <= 0)
127+
{
128+
AppendStream(stream);
129+
return;
130+
}
131+
132+
int bufSize = Math.Min(BUFFER_SIZE, count);
133+
var buffer = new byte[bufSize];
134+
int toRead = count;
135+
while (toRead > 0)
136+
{
137+
var read = stream.Read(buffer,0, Math.Min(toRead, bufSize));
138+
if (read == 0)
139+
break;
140+
141+
AppendData(buffer, read);
142+
toRead -= read;
143+
}
144+
}
145+
146+
/// <summary>
147+
/// Добавляет данные и обновляет хеш-сумму
148+
/// </summary>
149+
/// <param name="toAdd">Источник данных. Строка, ДвоичныеДанные или Поток</param>
150+
/// <param name="count">Для источника данных типов Строка или ДвоичныеДанные - игнорируется.
151+
/// Для источника данных типа Поток - Количество байтов, которые читаются из потока.
152+
/// Если количество не задано, нулевое <b>или отрицательное</b>, то читаются все данные до конца потока.
153+
/// </param>
84154
[ContextMethod("Добавить", "Append")]
85-
public void Append(BslValue toAdd, uint count = 0)
155+
public void Append(BslValue toAdd, BslValue count = null)
86156
{
87157
switch (toAdd)
88158
{
89159
case BslStringValue s:
90-
AddStream(new MemoryStream(Encoding.UTF8.GetBytes((string)s)));
160+
AppendData(Encoding.UTF8.GetBytes((string)s));
91161
break;
92-
case BslObjectValue obj when obj is IStreamWrapper wrapper:
93-
var stream = wrapper.GetUnderlyingStream();
94-
var readByte = (int)Math.Min(count == 0 ? stream.Length : count, stream.Length - stream.Position);
95-
var buffer = new byte[readByte];
96-
stream.Read(buffer, 0, readByte);
97-
AddStream(new MemoryStream(buffer));
162+
case BinaryDataContext binaryData:
163+
AppendStream(binaryData.GetStream());
98164
break;
99-
case BslObjectValue obj when obj is BinaryDataContext binaryData:
100-
AddStream(binaryData.GetStream());
165+
166+
case IStreamWrapper wrapper:
167+
var stream = wrapper.GetUnderlyingStream();
168+
if (count == null)
169+
{
170+
AppendStream(stream);
171+
}
172+
else
173+
{
174+
int cnt;
175+
try
176+
{
177+
cnt = (int)count;
178+
}
179+
catch
180+
{
181+
if (count is BslStringValue)
182+
throw RuntimeException.InvalidNthArgumentValue(2);
183+
else
184+
throw RuntimeException.InvalidNthArgumentType(2);
185+
}
186+
187+
AppendStream(stream, cnt);
188+
}
101189
break;
190+
102191
default:
103-
throw RuntimeException.InvalidArgumentType(nameof(toAdd));
192+
throw RuntimeException.InvalidNthArgumentType(1);
104193
}
105194
}
106195

196+
/// <summary>
197+
/// Добавляет двоичные данные из файла и обновляет хеш-сумму
198+
/// </summary>
199+
/// <param name="path">Имя файла, из которого читаются данные. Тип: Строка</param>
107200
[ContextMethod("ДобавитьФайл", "AppendFile")]
108201
public void AppendFile(string path)
109202
{
110203
if (!File.Exists(path))
111-
throw RuntimeException.InvalidArgumentType();
112-
AddStream(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
204+
throw RuntimeException.InvalidArgumentType();
205+
206+
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
207+
AppendStream(stream);
113208
}
114209

115-
[ContextMethod("Очистить", "Clear")]
116-
public void Clear()
117-
{
118-
_toCalculate.Close();
119-
_toCalculate.Dispose();
120-
_toCalculate = new CombinedStream();
121-
_calculated = false;
122-
}
123-
124-
125210
[ScriptConstructor(Name = "По указанной хеш-функции")]
126211
public static HashImpl Constructor(HashFunctionEnum providerEnum)
127-
{
128-
var objectProvider = GetProvider(providerEnum);
212+
{
213+
if (providerEnum == HashFunctionEnum.CRC32)
214+
return new HashImpl(null, providerEnum);
215+
216+
var objectProvider = IncrementalHash.CreateHash(GetAlgorithmName(providerEnum));
129217
return new HashImpl(objectProvider, providerEnum);
130218
}
131219

132-
private static HashAlgorithm GetProvider(HashFunctionEnum algo)
220+
private static HashAlgorithmName GetAlgorithmName(HashFunctionEnum algo)
133221
{
134-
switch (algo)
135-
{
136-
case HashFunctionEnum.CRC32:
137-
return new Crc32();
138-
default:
139-
var ret = HashAlgorithm.Create(algo.ToString());
140-
if (ret == null)
141-
throw RuntimeException.InvalidArgumentType();
142-
return ret;
143-
}
144-
}
145-
222+
return algo switch
223+
{
224+
HashFunctionEnum.MD5 => HashAlgorithmName.MD5,
225+
HashFunctionEnum.SHA1 => HashAlgorithmName.SHA1,
226+
HashFunctionEnum.SHA256 => HashAlgorithmName.SHA256,
227+
HashFunctionEnum.SHA384 => HashAlgorithmName.SHA384,
228+
HashFunctionEnum.SHA512 => HashAlgorithmName.SHA512,
229+
_ => throw RuntimeException.InvalidArgumentValue()
230+
};
231+
}
232+
146233
public void Dispose()
147234
{
148-
_toCalculate.Close();
149-
_toCalculate.Dispose();
150-
}
151-
152-
private void AddStream(Stream stream)
153-
{
154-
_toCalculate.AddStream(stream);
155-
_toCalculate.Seek(0, SeekOrigin.Begin);
156-
_calculated = false;
157-
235+
_provider?.Dispose();
158236
}
159237
}
160238
}

0 commit comments

Comments
 (0)