<commit_before>﻿/*----------------------------------------------------------
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.Text;

using ScriptEngine.Machine;
using ScriptEngine.Machine.Contexts;
using ScriptEngine.HostedScript.Library.Binary;

namespace ScriptEngine.HostedScript.Library
{
    /// <summary>
    /// Глобальный контекст. Операции с двоичными данными.
    /// </summary>
    [GlobalContext(Category = "Процедуры и функции работы с двоичными данными")]
    public sealed class GlobalBinaryData : GlobalContextBase<GlobalBinaryData>
    {
        private static byte[] HexStringToByteArray(String hex)
        {
            var newHex = System.Text.RegularExpressions.Regex.Replace(hex, @"[^0-9A-Fa-f]", "");
            int numberChars = newHex.Length;
            if (numberChars % 2 == 1)
                throw new FormatException("Неверный формат шестнадцатеричной строки");

            byte[] bytes = new byte[numberChars / 2];
            for (int i = 0; i < numberChars; i += 2)
                bytes[i / 2] = Convert.ToByte(newHex.Substring(i, 2), 16);

            return bytes;
        }

        private static int[] hexDigitsValues = new[]
        {
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
                -1,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
        };
        private static int CharCodeToHex(byte code)
        {
            return hexDigitsValues[code];
        }

        private static byte[] HexArrayToByteArray(byte[] hex)
        {
            var bytes = new byte[hex.Length / 2];
            int pos = 0;

            int hexDig1;
            int hexDig2 =-1;
            for (int i = 0; i < hex.Length; ++i)
            {
                hexDig1 = CharCodeToHex(hex[i]);
                if (hexDig1 < 0)
                    continue;

                if (hexDig2 < 0)
                {
                    hexDig2 = hexDig1;
                    continue;
                }

                bytes[pos] = (byte)(hexDig2 * 16 + hexDig1);
                ++pos;
                hexDig2 = -1;
            }

            if (pos < bytes.Length)
                Array.Resize(ref bytes, pos);
            return bytes;
        }

        public static IAttachableContext CreateInstance()
        {
            return new GlobalBinaryData();
        }

        /// <summary>
        /// Объединяет несколько объектов типа ДвоичныеДанные в один.
        /// </summary>
        /// <param name="array">Массив объектов типа ДвоичныеДанные.</param>
        /// <returns>Тип: ДвоичныеДанные.</returns>
        [ContextMethod("СоединитьДвоичныеДанные")]
        public BinaryDataContext ConcatenateBinaryData(ArrayImpl array)
        {
            // Сделано на int т.к. BinaryContext.Size имеет тип int;

            using (var stream = new System.IO.MemoryStream())
            {

                foreach (var cbd in array)
                {
                    byte[] buffer = ((BinaryDataContext) cbd.AsObject()).Buffer;
                    stream.Write(buffer, 0, buffer.Length);
                }
                stream.Position = 0;
                return new BinaryDataContext(stream);
            }
        }

        /// <summary>
        /// Разделяет двоичные данные на части заданного размера. Размер задается в байтах.
        /// </summary>
        /// <param name="data">Объект типа ДвоичныеДанные.</param>
        /// <param name="size">Размер одной части данных.</param>
        /// <returns>Массив объектов типа ДвоичныеДанные.</returns>
        [ContextMethod("РазделитьДвоичныеДанные")]
        public ArrayImpl SplitBinaryData(BinaryDataContext data, long size)
        {
            if (size <= 0 || size > Int32.MaxValue)
                throw RuntimeException.InvalidNthArgumentValue(2);

            ArrayImpl array = new ArrayImpl();
            long dataSize = data.Size();

            if (dataSize < size)
            {
                array.Add(data);
                return array;
            }

            int readedBytes = 0;
            var dataStream = data.GetStream();

            while (readedBytes < dataSize)
            {
                int bytesToRead = (int)size;
                if (bytesToRead > dataSize - readedBytes)
                    bytesToRead = (int)(dataSize - readedBytes);

                byte[] buffer = new byte[bytesToRead];
                dataStream.Read(buffer, 0, bytesToRead);
                readedBytes += bytesToRead;
                array.Add(new BinaryDataContext(buffer));
            }

            return array;
        }

        /// <summary>
        /// Преобразует строку в значение типа ДвоичныеДанные с учетом кодировки текста.
        /// </summary>
        /// <param name="str">Строка, которую требуется преобразовать в ДвоичныеДанные.</param>
        /// <param name="encoding">Кодировка текста</param>
        /// <param name="addBOM">Определяет, будет ли добавлена метка порядка байт (BOM) кодировки текста в начало данных.</param>
        /// <returns>Тип: ДвоичныеДанные.</returns>
        [ContextMethod("ПолучитьДвоичныеДанныеИзСтроки")]
        public BinaryDataContext GetBinaryDataFromString(string str, IValue encoding = null, bool addBOM = false)
        {
            // Получаем кодировку
            // Из синтаксис помощника. Если кодировка не задана, используем UTF8

            System.Text.Encoding enc = System.Text.Encoding.UTF8;
            if (encoding != null)
                enc = TextEncodingEnum.GetEncoding(encoding, addBOM);

            var stream = new System.IO.MemoryStream();

            var bom = enc.GetPreamble();
            var inputString = enc.GetBytes(str);

            stream.Write(bom, 0, bom.Length);
            stream.Write(inputString, 0, inputString.Length);
            stream.Position = 0;
            
            return new BinaryDataContext(stream);
        }

        /// <summary>
        /// Преобразует строку в буфер двоичных данных с учетом кодировки текста.
        /// </summary>
        /// <param name="str">Строка, которую требуется преобразовать в БуферДвоичныхДанных.</param>
        /// <param name="encoding">Кодировка текста</param><commit_msg>fix: Кодировка по умолчанию. Теперь UTF-8 без BOM.<commit_after>﻿/*----------------------------------------------------------
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.Text;

using ScriptEngine.Machine;
using ScriptEngine.Machine.Contexts;
using ScriptEngine.HostedScript.Library.Binary;

namespace ScriptEngine.HostedScript.Library
{
    /// <summary>
    /// Глобальный контекст. Операции с двоичными данными.
    /// </summary>
    [GlobalContext(Category = "Процедуры и функции работы с двоичными данными")]
    public sealed class GlobalBinaryData : GlobalContextBase<GlobalBinaryData>
    {
        private static byte[] HexStringToByteArray(String hex)
        {
            var newHex = System.Text.RegularExpressions.Regex.Replace(hex, @"[^0-9A-Fa-f]", "");
            int numberChars = newHex.Length;
            if (numberChars % 2 == 1)
                throw new FormatException("Неверный формат шестнадцатеричной строки");

            byte[] bytes = new byte[numberChars / 2];
            for (int i = 0; i < numberChars; i += 2)
                bytes[i / 2] = Convert.ToByte(newHex.Substring(i, 2), 16);

            return bytes;
        }

        private static int[] hexDigitsValues = new[]
        {
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
                -1,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
        };
        private static int CharCodeToHex(byte code)
        {
            return hexDigitsValues[code];
        }

        private static byte[] HexArrayToByteArray(byte[] hex)
        {
            var bytes = new byte[hex.Length / 2];
            int pos = 0;

            int hexDig1;
            int hexDig2 =-1;
            for (int i = 0; i < hex.Length; ++i)
            {
                hexDig1 = CharCodeToHex(hex[i]);
                if (hexDig1 < 0)
                    continue;

                if (hexDig2 < 0)
                {
                    hexDig2 = hexDig1;
                    continue;
                }

                bytes[pos] = (byte)(hexDig2 * 16 + hexDig1);
                ++pos;
                hexDig2 = -1;
            }

            if (pos < bytes.Length)
                Array.Resize(ref bytes, pos);
            return bytes;
        }

        public static IAttachableContext CreateInstance()
        {
            return new GlobalBinaryData();
        }

        /// <summary>
        /// Объединяет несколько объектов типа ДвоичныеДанные в один.
        /// </summary>
        /// <param name="array">Массив объектов типа ДвоичныеДанные.</param>
        /// <returns>Тип: ДвоичныеДанные.</returns>
        [ContextMethod("СоединитьДвоичныеДанные")]
        public BinaryDataContext ConcatenateBinaryData(ArrayImpl array)
        {
            // Сделано на int т.к. BinaryContext.Size имеет тип int;

            using (var stream = new System.IO.MemoryStream())
            {

                foreach (var cbd in array)
                {
                    byte[] buffer = ((BinaryDataContext) cbd.AsObject()).Buffer;
                    stream.Write(buffer, 0, buffer.Length);
                }
                stream.Position = 0;
                return new BinaryDataContext(stream);
            }
        }

        /// <summary>
        /// Разделяет двоичные данные на части заданного размера. Размер задается в байтах.
        /// </summary>
        /// <param name="data">Объект типа ДвоичныеДанные.</param>
        /// <param name="size">Размер одной части данных.</param>
        /// <returns>Массив объектов типа ДвоичныеДанные.</returns>
        [ContextMethod("РазделитьДвоичныеДанные")]
        public ArrayImpl SplitBinaryData(BinaryDataContext data, long size)
        {
            if (size <= 0 || size > Int32.MaxValue)
                throw RuntimeException.InvalidNthArgumentValue(2);

            ArrayImpl array = new ArrayImpl();
            long dataSize = data.Size();

            if (dataSize < size)
            {
                array.Add(data);
                return array;
            }

            int readedBytes = 0;
            var dataStream = data.GetStream();

            while (readedBytes < dataSize)
            {
                int bytesToRead = (int)size;
                if (bytesToRead > dataSize - readedBytes)
                    bytesToRead = (int)(dataSize - readedBytes);

                byte[] buffer = new byte[bytesToRead];
                dataStream.Read(buffer, 0, bytesToRead);
                readedBytes += bytesToRead;
                array.Add(new BinaryDataContext(buffer));
            }

            return array;
        }

        /// <summary>
        /// Преобразует строку в значение типа ДвоичныеДанные с учетом кодировки текста.
        /// </summary>
        /// <param name="str">Строка, которую требуется преобразовать в ДвоичныеДанные.</param>
        /// <param name="encoding">Кодировка текста</param>
        /// <param name="addBOM">Определяет, будет ли добавлена метка порядка байт (BOM) кодировки текста в начало данных.</param>
        /// <returns>Тип: ДвоичныеДанные.</returns>
        [ContextMethod("ПолучитьДвоичныеДанныеИзСтроки")]
        public BinaryDataContext GetBinaryDataFromString(string str, IValue encoding = null, bool addBOM = false)
        {
            // Получаем кодировку
            // Из синтаксис помощника. Если кодировка не задана, используем UTF8 без BOM.

            System.Text.Encoding enc = new UTF8Encoding(false);
            if (encoding != null)
                enc = TextEncodingEnum.GetEncoding(encoding, addBOM);

            var stream = new System.IO.MemoryStream();

            var bom = enc.GetPreamble();
            var inputString = enc.GetBytes(str);

            stream.Write(bom, 0, bom.Length);
            stream.Write(inputString, 0, inputString.Length);
            stream.Position = 0;
            
            return new BinaryDataContext(stream);
        }

        /// <summary>
        /// Преобразует строку в буфер двоичных данных с учетом кодировки текста.
        /// </summary>
        /// <param name="str">Строка, которую требуется преобразовать в БуферДвоичныхДанных.</param>
        /// <param name="encoding">Кодировка текста</param>
{'fraction_non_alphanumeric': 0.16937133506068458, 'fraction_numerical': 0.043297422610118645, 'mean_word_length': 1.525309917355372, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4820848', 'n_tokens_mistral': 4971, 'n_tokens_neox': 4920, 'n_words': 915}