diff --git a/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataBuffer.cs b/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataBuffer.cs index f11bf92f3..ed1374880 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataBuffer.cs +++ b/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataBuffer.cs @@ -97,11 +97,7 @@ public override void SetIndexedValue(IValue index, IValue val) /// /// Булево (Boolean) [ContextProperty("ТолькоЧтение", "ReadOnly")] - public bool ReadOnly - { - get { return _readOnly; } - - } + public bool ReadOnly => _readOnly; /// /// @@ -521,27 +517,121 @@ public ulong ReadInt64(int position, IValue byteOrder = null) /// - /// Разделить буфер на части по заданному разделителю. - /// - /// НЕ РЕАЛИЗОВАН + /// Разделить буфер на части по заданному разделителю или массиву разделителей. /// /// - /// - /// - /// По двоичному буферу - /// - /// /// /// Разделитель. /// - /// + /// Массив из буферов двоичных данных /// [ContextMethod("Разделить", "Split")] - public IValue Split(IValue separator) + public ArrayImpl Split(IValue separator) { - throw new NotImplementedException(); + var buffers = ParseParam(separator); + + // Функция поиска требует, чтобы буферы были в порядке убывания размера + buffers.Sort((a, b) => b._buffer.LongLength.CompareTo(a._buffer.LongLength)); + return SplitBuffer(buffers.ToArray()); + } + + private static List ParseParam(IValue separator) + { + var rawSeparator = separator?.GetRawValue(); + switch (rawSeparator) + { + case BinaryDataBuffer buffer: + return new List { CheckedBuffer(buffer) }; + + case ArrayImpl array: + { + var buffers = new List(); + + foreach (var element in array) + { + buffers.AddRange(ParseParam(element)); + } + + return buffers; + } + + default: + throw RuntimeException.InvalidArgumentType(); + } + } + + private static BinaryDataBuffer CheckedBuffer(BinaryDataBuffer buffer) + { + if (buffer.Size == 0) + { + throw RuntimeException.InvalidArgumentValue(); + } + + return buffer; + } + + private ArrayImpl SplitBuffer(BinaryDataBuffer[] splitter) + { + var result = new List(); + long start = 0; + var foundPosition = FindFirst(splitter, start); + while (foundPosition.pos != -1) + { + var length = foundPosition.pos - start; + result.Add(new BinaryDataBuffer(Copy(start, length), ByteOrder)); + start = foundPosition.pos + foundPosition.buffer.Size; + foundPosition = FindFirst(splitter, start); + } + + // хвостовой элемент + result.Add(new BinaryDataBuffer(Copy(start, _buffer.LongLength - start))); + return new ArrayImpl(result); + } + + /// + /// Ищет ближайшее вхождение любого из буферов. Если на одной позиции находятся два и более буфера, берется бОльший. + /// + /// Массив искомых буферов + /// Начальная позиция поиска + /// Буфер и позиция или null, если нет вхождений + private (BinaryDataBuffer buffer, long pos) FindFirst(BinaryDataBuffer[] buffers, long start) + { + var maxI = Size - buffers[buffers.Length - 1].Size; + for (var i = start; i < maxI; i++) + { + foreach (var expectedBuffer in buffers) + { + if (SubsequenceEquals(_buffer, i, expectedBuffer._buffer)) + { + return (expectedBuffer, i); + } + } + } + + return (null, -1); + } + + private byte[] Copy(long start, long length) + { + if (length == 0) return Array.Empty(); + var partition = new byte[length]; + Array.Copy(_buffer, start, partition, 0, length); + return partition; } + private static bool SubsequenceEquals(byte[] sequence, long start, byte[] subsequence) + { + for (long j = 0; j < subsequence.LongLength; j++) + { + if (subsequence[j] != sequence[start + j]) + { + return false; + } + } + + return true; + } + /// /// /// Создает копию массива. diff --git a/tests/binary-objects.os b/tests/binary-objects.os index 130fc076a..aa5bc3a01 100644 --- a/tests/binary-objects.os +++ b/tests/binary-objects.os @@ -11,6 +11,7 @@ ВсеТесты = Новый Массив; ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСоздаетсяБуферДвоичныхДанных"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхНесколькимиБуферами"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноЗаписатьБайты"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноПрочитатьБайты"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноЗаписатьПрочитатьЦелое16"); @@ -30,6 +31,8 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМетодОткрытьПотокДляЧтенияВозвращаетПотокТолькоДляЧтения"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеРаботаетСПотоком"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеРаботаетСДвоичнымиДанными"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхОднимБуфером"); Возврат ВсеТесты; @@ -351,3 +354,58 @@ НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхОднимБуфером() Экспорт + + Подстроки = Новый Массив; + Подстроки.Добавить("Часть1"); + Подстроки.Добавить("Часть2"); + Подстроки.Добавить("Часть3"); + РазделительТекстом = "123"; + + ТестовыйБуфер = ПолучитьБуферДвоичныхДанныхИзСтроки(СтрСоединить(Подстроки, РазделительТекстом)); + Разделитель = ПолучитьБуферДвоичныхДанныхИзСтроки(РазделительТекстом); + + РазделенныеДанные = ТестовыйБуфер.Разделить(Разделитель); + + юТест.ПроверитьРавенство(РазделенныеДанные.Количество(), 3, "два разделителя - три элемента"); + + Для Инд = 0 По Подстроки.ВГраница() Цикл + + СтрокаИзБуфера = ПолучитьСтрокуИзБуфераДвоичныхДанных(РазделенныеДанные[Инд]); + юТест.ПроверитьРавенство(СтрокаИзБуфера, Подстроки[Инд], "сравнение строк после разделения буфера"); + + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхНесколькимиБуферами() Экспорт + + Подстроки = Новый Массив; + Подстроки.Добавить("Часть1"); + Подстроки.Добавить("Часть2"); + Подстроки.Добавить("Часть3"); + Подстроки.Добавить("Часть4"); + Подстроки.Добавить("Часть5"); + + ТестоваяСтрока = "Часть1\R\R\NЧасть2\R\NЧасть3\NЧасть4\RЧасть5"; + + ТестовыйБуфер = ПолучитьБуферДвоичныхДанныхИзСтроки(ТестоваяСтрока); + Разделители = Новый Массив; + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\N")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R\N")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R\R\N")); + + РазделенныеДанные = ТестовыйБуфер.Разделить(Разделители); + + юТест.ПроверитьРавенство(РазделенныеДанные.Количество(), 5); + + Для Инд = 0 По РазделенныеДанные.ВГраница() Цикл + + СтрокаИзБуфера = ПолучитьСтрокуИзБуфераДвоичныхДанных(РазделенныеДанные[Инд]); + юТест.ПроверитьРавенство(СтрокаИзБуфера, Подстроки[Инд], "сравнение строк после разделения буфера"); + + КонецЦикла; + +КонецПроцедуры