Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,21 @@ public Stream GetStream()
return InMemory ? new MemoryStream(_buffer, 0, _buffer.Length, false, true) : _backingFile.OpenReadStream();
}

/// <summary>
/// Данные целиком. Данные во временном файле при каждом обращении читаются в новый массив,
/// а сами остаются в файле: большие файлы для этого в нем и хранятся
/// </summary>
public byte[] Buffer
{
get
{
if (!InMemory)
{
using var readStream = _backingFile.OpenReadStream();
LoadToBuffer(readStream);
_backingFile.Dispose();
_backingFile = null;
}
if (InMemory)
return _buffer;

return _buffer;
using var readStream = _backingFile.OpenReadStream();
var buffer = new byte[readStream.Length];
readStream.ReadExactly(buffer);
return buffer;
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ public BinaryDataContext GetBinaryDataFromBase64BinaryData(BinaryDataContext dat
try
{
var enc = new UTF8Encoding(false,true);
var str = enc.GetString(data.Buffer, 0, data.Buffer.Length);
var str = enc.GetString(data.Buffer);
return new BinaryDataContext(Convert.FromBase64String(str));
}
catch
Expand Down Expand Up @@ -554,7 +554,8 @@ public BinaryDataBuffer GetBinaryDataBufferFromBinaryData(BinaryDataContext data
{
CheckAndThrowIfNull(data);

return new BinaryDataBuffer(data.Buffer);
// Копия: запись в буфер не должна менять двоичные данные (из файла Buffer и так читает новый массив)
return new BinaryDataBuffer(data.InMemory ? (byte[])data.Buffer.Clone() : data.Buffer);
}

/// <summary>
Expand All @@ -567,7 +568,8 @@ public BinaryDataContext GetBinaryDataFromBinaryDataBuffer(BinaryDataBuffer buff
{
CheckAndThrowIfNull(buffer);

return new BinaryDataContext(buffer.Bytes);
// Копия: двоичные данные не должны меняться вместе с буфером
return new BinaryDataContext((byte[])buffer.Bytes.Clone());
}

}
Expand Down
3 changes: 2 additions & 1 deletion src/OneScript.StandardLibrary/NativeApi/NativeApiVariant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public void Assign(IValue value)
NativeApiProxy.SetVariantReal(Ptr, Convert.ToDouble(value.AsNumber()));
break;
case BinaryDataContext binaryData:
NativeApiProxy.SetVariantBlob(Ptr, binaryData.Buffer, binaryData.Buffer.Length);
var blob = binaryData.Buffer;
NativeApiProxy.SetVariantBlob(Ptr, blob, blob.Length);
break;
case DateTime dt:
NativeApiProxy.SetVariantTm(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*----------------------------------------------------------
This Source Code Form is subject to the terms of the
Mozilla Public License, v.2.0. If a copy of the MPL
was not distributed with this file, You can obtain one
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Threading;
using FluentAssertions;
using OneScript.StandardLibrary.Binary;
using Xunit;

namespace OneScript.StandardLibrary.Tests
{
public class BinaryDataThreadSafetyTests
{
[Fact]
public void FileBackedDataIsReadFromManyThreads()
{
const int threadsCount = 8;
var expected = new byte[64 * 1024];
new Random(42).NextBytes(expected);
var errors = new ConcurrentQueue<string>();

for (var iteration = 0; iteration < 50; iteration++)
{
// Больше лимита памяти: данные во временном файле
using var data = new BinaryDataContext(new MemoryStream(expected), 1);
using var barrier = new Barrier(threadsCount);

var threads = Enumerable.Range(0, threadsCount).Select(number => new Thread(() =>
{
barrier.SignalAndWait();
try
{
if (!ReadAll(data, viaBuffer: number % 2 == 0).AsSpan().SequenceEqual(expected))
errors.Enqueue("данные отличаются");
}
catch (Exception e)
{
errors.Enqueue($"{e.GetType().Name}: {e.Message}");
}
})).ToArray();

foreach (var thread in threads)
{
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
}

errors.Should().BeEmpty();
}

[Fact]
public void BufferKeepsFileBackedDataInFile()
{
var expected = new byte[] { 1, 2, 3 };
using var data = new BinaryDataContext(new MemoryStream(expected), 1);

data.Buffer.Should().Equal(expected);
data.InMemory.Should().BeFalse();
ReadAll(data, viaBuffer: false).Should().Equal(expected);
}

private static byte[] ReadAll(BinaryDataContext data, bool viaBuffer)
{
if (viaBuffer)
return data.Buffer;

using var stream = data.GetStream();
using var copy = new MemoryStream();
stream.CopyTo(copy);
return copy.ToArray();
}
}
}
26 changes: 26 additions & 0 deletions tests/BinaryData-global.os
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
ВсеТесты.Добавить("ТестДолжен_Проверить_ОтсутствиеBOMДляКодировокБезBOM");
ВсеТесты.Добавить("ТестДолжен_Проверить_КодировкиДляПолучитьСтрокуИзДвоичныхДанных");
ВсеТесты.Добавить("ТестДолжен_Проверить_КодировкиДляПолучитьСтрокуИзБуфераДвоичныхДанных");
ВсеТесты.Добавить("ТестДолжен_Проверить_ЗаписьВБуферИзДвоичныхДанныхНеМеняетИх");
ВсеТесты.Добавить("ТестДолжен_Проверить_ДвоичныеДанныеИзБуфераНеМеняютсяВместеСБуфером");

Возврат ВсеТесты;
КонецФункции
Expand Down Expand Up @@ -471,3 +473,27 @@
юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "⌥",
"Неверная Строка из ДвоичныхДанных без BOM для "+Кодировка);
КонецПроцедуры

Процедура ТестДолжен_Проверить_ЗаписьВБуферИзДвоичныхДанныхНеМеняетИх() Экспорт

ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки("AAAA");
Буфер1 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДвоичныеДанные);
Буфер2 = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДвоичныеДанные);

Буфер1.Установить(0, КодСимвола("Z"));

юТест.ПроверитьРавенство("AAAA", ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные));
юТест.ПроверитьРавенство(КодСимвола("A"), Буфер2[0]);

КонецПроцедуры

Процедура ТестДолжен_Проверить_ДвоичныеДанныеИзБуфераНеМеняютсяВместеСБуфером() Экспорт

Буфер = ПолучитьБуферДвоичныхДанныхИзСтроки("AAAA");
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(Буфер);

Буфер.Установить(0, КодСимвола("Z"));

юТест.ПроверитьРавенство("AAAA", ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные));

КонецПроцедуры
Loading