diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs
index e56d15ed3..24dbc0dd4 100644
--- a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs
+++ b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs
@@ -49,7 +49,7 @@ public BinaryDataContext(Stream stream)
private void ReadFromStream(Stream stream)
{
- if (stream.Length < FileBackingConstants.DEFAULT_MEMORY_LIMIT)
+ if (stream.Length < BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes())
{
LoadToBuffer(stream);
}
diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs
index eeb073ad9..c3f7e8ac9 100644
--- a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs
+++ b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs
@@ -6,12 +6,13 @@ This Source Code Form is subject to the terms of the
----------------------------------------------------------*/
using System;
+using ScriptEngine;
namespace OneScript.StandardLibrary.Binary
{
public static class FileBackingConstants
{
- public const int DEFAULT_MEMORY_LIMIT = 1024 * 1024 * 50; // 50 Mb
+ public const int DEFAULT_MEMORY_LIMIT = BinaryDataConfigurationDefaults.InMemoryMaxBytes;
public const int SYSTEM_IN_MEMORY_LIMIT = Int32.MaxValue;
}
}
\ No newline at end of file
diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs
index 7a0502643..515a1f07e 100644
--- a/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs
+++ b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs
@@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the
using System;
using System.IO;
+using ScriptEngine.Machine;
namespace OneScript.StandardLibrary.Binary
{
@@ -20,7 +21,7 @@ public sealed class FileBackingStream : Stream
private string _backingFileName;
- public FileBackingStream() : this(FileBackingConstants.DEFAULT_MEMORY_LIMIT)
+ public FileBackingStream() : this(BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes())
{
}
diff --git a/src/ScriptEngine.HostedScript/oscript.cfg b/src/ScriptEngine.HostedScript/oscript.cfg
index 1ddb2504f..6a664aa51 100644
--- a/src/ScriptEngine.HostedScript/oscript.cfg
+++ b/src/ScriptEngine.HostedScript/oscript.cfg
@@ -19,4 +19,8 @@ lib.system = ../lib
#encoding.script=utf-8
-#systemlanguage = ru
\ No newline at end of file
+#systemlanguage = ru
+
+# Порог в байтах: пока размер двоичных данных в памяти не превышает это значение, данные держатся в RAM;
+# при большем размере используется временный файл. По умолчанию 52428800 (50 МБ).
+#binaryData.inMemoryMaxBytes=52428800
\ No newline at end of file
diff --git a/src/ScriptEngine/BinaryDataConfigurationDefaults.cs b/src/ScriptEngine/BinaryDataConfigurationDefaults.cs
new file mode 100644
index 000000000..346858a5c
--- /dev/null
+++ b/src/ScriptEngine/BinaryDataConfigurationDefaults.cs
@@ -0,0 +1,20 @@
+/*----------------------------------------------------------
+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/.
+----------------------------------------------------------*/
+
+namespace ScriptEngine
+{
+ ///
+ /// Значения по умолчанию для буферизации двоичных данных в памяти до перехода на временный файл.
+ ///
+ public static class BinaryDataConfigurationDefaults
+ {
+ ///
+ /// Лимит по умолчанию (50 МБ), если в конфигурации не задан ключ binaryData.inMemoryMaxBytes.
+ ///
+ public const int InMemoryMaxBytes = 1024 * 1024 * 50;
+ }
+}
diff --git a/src/ScriptEngine/BslProcess.cs b/src/ScriptEngine/BslProcess.cs
index 65b8c9de2..f4aa49d34 100644
--- a/src/ScriptEngine/BslProcess.cs
+++ b/src/ScriptEngine/BslProcess.cs
@@ -40,23 +40,33 @@ public BslProcess(int id, ExecutionContext context, IEnumerable e.BeforeProcessStart(this));
- }
- _isRunning = true;
+ if (notifyExecutors)
+ BinaryDataRuntimeSettings.PushFromServices(Services);
try
{
+ if (notifyExecutors)
+ Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this));
+
+ _isRunning = true;
+
return _bslExecutorsByModule[module.GetType()](this, target, module, method, arguments);
}
finally
{
if (notifyExecutors)
{
- Array.ForEach(_executorProviders, e => e.AfterProcessExit(this));
- _isRunning = false;
+ try
+ {
+ if (_isRunning)
+ Array.ForEach(_executorProviders, e => e.AfterProcessExit(this));
+ }
+ finally
+ {
+ BinaryDataRuntimeSettings.Pop();
+ _isRunning = false;
+ }
}
}
}
diff --git a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs
index 37225644d..f62b03fc0 100644
--- a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs
+++ b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs
@@ -79,6 +79,9 @@ public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder)
var holder = sp.Resolve();
return holder.GetConfig();
});
+
+ services.RegisterSingleton(sp =>
+ new BinaryDataMemoryLimitFromOptions(sp.Resolve()));
services.Register();
diff --git a/src/ScriptEngine/IBinaryDataMemoryLimit.cs b/src/ScriptEngine/IBinaryDataMemoryLimit.cs
new file mode 100644
index 000000000..aeb5744ac
--- /dev/null
+++ b/src/ScriptEngine/IBinaryDataMemoryLimit.cs
@@ -0,0 +1,28 @@
+/*----------------------------------------------------------
+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/.
+----------------------------------------------------------*/
+
+namespace ScriptEngine
+{
+ ///
+ /// Лимит объёма данных в памяти для объектов «ДвоичныеДанные» и смежных потоков
+ /// до выгрузки во временный файл (байты).
+ ///
+ public interface IBinaryDataMemoryLimit
+ {
+ int MaxBytesInMemory { get; }
+ }
+
+ internal sealed class BinaryDataMemoryLimitFromOptions : IBinaryDataMemoryLimit
+ {
+ public BinaryDataMemoryLimitFromOptions(OneScriptCoreOptions options)
+ {
+ MaxBytesInMemory = options.BinaryDataInMemoryMaxBytes;
+ }
+
+ public int MaxBytesInMemory { get; }
+ }
+}
diff --git a/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs b/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs
new file mode 100644
index 000000000..6f0302e3c
--- /dev/null
+++ b/src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs
@@ -0,0 +1,43 @@
+/*----------------------------------------------------------
+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.Threading;
+using OneScript.DependencyInjection;
+
+namespace ScriptEngine.Machine
+{
+ ///
+ /// Лимит памяти для двоичных данных на время выполнения процесса BSL (AsyncLocal).
+ ///
+ public static class BinaryDataRuntimeSettings
+ {
+ private static readonly AsyncLocal InMemoryMaxBytes = new AsyncLocal();
+
+ internal static void PushFromServices(IServiceContainer services)
+ {
+ var opts = services.TryResolve();
+ var maxBytes = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+ if (maxBytes <= 0)
+ maxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+ InMemoryMaxBytes.Value = maxBytes;
+ }
+
+ internal static void Pop()
+ {
+ InMemoryMaxBytes.Value = null;
+ }
+
+ ///
+ /// Текущий лимит «в памяти до временного файла» для двоичных данных (байты).
+ /// Вне процесса BSL возвращает значение по умолчанию из .
+ ///
+ public static int GetEffectiveInMemoryMaxBytes()
+ {
+ return InMemoryMaxBytes.Value ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+ }
+ }
+}
diff --git a/src/ScriptEngine/OneScriptCoreOptions.cs b/src/ScriptEngine/OneScriptCoreOptions.cs
index 7d90b0793..609901df7 100644
--- a/src/ScriptEngine/OneScriptCoreOptions.cs
+++ b/src/ScriptEngine/OneScriptCoreOptions.cs
@@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Text;
using OneScript.Commons;
using OneScript.Native.Compiler;
@@ -21,6 +22,7 @@ public class OneScriptCoreOptions
private const string PREPROCESSOR_DEFINITIONS_KEY = "preprocessor.define";
private const string DEFAULT_RUNTIME_KEY = "runtime.default";
private const string EXPLICIT_IMPORT = "lang.explicitImports";
+ private const string BINARY_DATA_IN_MEMORY_MAX = "binaryData.inMemoryMaxBytes";
public OneScriptCoreOptions(KeyValueConfig config)
{
@@ -29,6 +31,7 @@ public OneScriptCoreOptions(KeyValueConfig config)
PreprocessorDefinitions = SetupDefinitions(config[PREPROCESSOR_DEFINITIONS_KEY]);
UseNativeAsDefaultRuntime = SetupDefaultRuntime(config[DEFAULT_RUNTIME_KEY]);
ExplicitImports = SetupExplicitImports(config[EXPLICIT_IMPORT]);
+ BinaryDataInMemoryMaxBytes = SetupBinaryDataInMemoryMax(config[BINARY_DATA_IN_MEMORY_MAX]);
}
public string SystemLanguage { get; }
@@ -41,6 +44,12 @@ public OneScriptCoreOptions(KeyValueConfig config)
public ExplicitImportsBehavior ExplicitImports { get; }
+ ///
+ /// Максимальный объём двоичных данных в памяти до перехода на временный файл (байты).
+ /// Задаётся ключом binaryData.inMemoryMaxBytes в конфигурации.
+ ///
+ public int BinaryDataInMemoryMaxBytes { get; }
+
private static IEnumerable SetupDefinitions(string s)
{
return s?.Split(',') ?? Array.Empty();
@@ -79,5 +88,25 @@ private static ExplicitImportsBehavior SetupExplicitImports(string keyValue)
return ExplicitImportsBehavior.Warn;
}
}
+
+ private static int SetupBinaryDataInMemoryMax(string rawValue)
+ {
+ if (string.IsNullOrWhiteSpace(rawValue))
+ return BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+
+ if (!int.TryParse(rawValue.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var bytes))
+ {
+ SystemLogger.Write($"Invalid value for {BINARY_DATA_IN_MEMORY_MAX}: {rawValue}");
+ return BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+ }
+
+ if (bytes <= 0 || bytes == int.MaxValue)
+ {
+ SystemLogger.Write($"Value for {BINARY_DATA_IN_MEMORY_MAX} must be between 1 and {int.MaxValue - 1}: {bytes}");
+ return BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+ }
+
+ return bytes;
+ }
}
}
\ No newline at end of file
diff --git a/src/oscript/CgiBehavior.cs b/src/oscript/CgiBehavior.cs
index 911782454..13a103d65 100644
--- a/src/oscript/CgiBehavior.cs
+++ b/src/oscript/CgiBehavior.cs
@@ -15,6 +15,7 @@ This Source Code Form is subject to the terms of the
using OneScript.StandardLibrary;
using oscript.Web;
+using ScriptEngine;
using ScriptEngine.HostedScript;
using ScriptEngine.Hosting;
using ScriptEngine.Machine;
@@ -67,8 +68,13 @@ private int RunCGIMode(string scriptFile)
});
var engine = ConsoleHostBuilder.Build(builder);
-
- var request = new WebRequestContext();
+
+ var binaryMemoryLimit = engine.Services.TryResolve()?.MaxBytesInMemory
+ ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+ if (binaryMemoryLimit <= 0)
+ binaryMemoryLimit = BinaryDataConfigurationDefaults.InMemoryMaxBytes;
+
+ var request = new WebRequestContext(binaryMemoryLimit);
engine.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true);
engine.InjectObject(this);
diff --git a/src/oscript/Web/WebRequestContext.cs b/src/oscript/Web/WebRequestContext.cs
index 30f46d52c..d80a1bf0c 100644
--- a/src/oscript/Web/WebRequestContext.cs
+++ b/src/oscript/Web/WebRequestContext.cs
@@ -15,6 +15,7 @@ This Source Code Form is subject to the terms of the
using OneScript.StandardLibrary.Collections;
using OneScript.StandardLibrary.Text;
using oscript.Web.Multipart;
+using ScriptEngine;
using ScriptEngine.Machine;
using ScriptEngine.Machine.Contexts;
@@ -27,8 +28,18 @@ public sealed class WebRequestContext : AutoContext, IDisposa
private FileBackingStream _postBody;
- public WebRequestContext()
+ private readonly int _postBodyInMemoryMaxBytes;
+
+ public WebRequestContext() : this(BinaryDataConfigurationDefaults.InMemoryMaxBytes)
+ {
+ }
+
+ public WebRequestContext(int postBodyInMemoryMaxBytes)
{
+ if (postBodyInMemoryMaxBytes <= 0)
+ throw new ArgumentOutOfRangeException(nameof(postBodyInMemoryMaxBytes),
+ "Лимит памяти для тела POST-запроса должен быть положительным числом байт.");
+ _postBodyInMemoryMaxBytes = postBodyInMemoryMaxBytes;
var get = Environment.GetEnvironmentVariable("QUERY_STRING");
if (get != null) FillGetMap(get);
@@ -68,7 +79,8 @@ private void ProcessPostData()
var type = Environment.GetEnvironmentVariable("CONTENT_TYPE");
using var stdin = Console.OpenStandardInput();
- var dest = new FileBackingStream(FileBackingConstants.DEFAULT_MEMORY_LIMIT, len);
+ var initialCapacity = Math.Min(len, _postBodyInMemoryMaxBytes);
+ var dest = new FileBackingStream(_postBodyInMemoryMaxBytes, initialCapacity);
stdin.CopyTo(dest);
dest.Position = 0;