File: AppHost\BinaryUtils.cs
Web Access
Project: src\src\runtime\src\installer\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj (Microsoft.NET.HostModel)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using System.IO.MemoryMappedFiles;

namespace Microsoft.NET.HostModel.AppHost
{
    public static class BinaryUtils
    {
        internal static unsafe void SearchAndReplace(
            MemoryMappedViewAccessor accessor,
            ReadOnlySpan<byte> searchPattern,
            byte[] patternToReplace,
            bool pad0s = true)
        {
            byte* pointer = null;

            try
            {
                accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
                byte* bytes = pointer + accessor.PointerOffset;

                int position = KMPSearch(searchPattern, bytes, accessor.Capacity);
                if (position < 0)
                {
                    throw new PlaceHolderNotFoundInAppHostException(searchPattern);
                }

                accessor.WriteArray(
                    position: position,
                    array: patternToReplace,
                    offset: 0,
                    count: patternToReplace.Length);

                if (pad0s)
                {
                    Pad0(searchPattern, patternToReplace, bytes, position);
                }
            }
            finally
            {
                if (pointer != null)
                {
                    accessor.SafeMemoryMappedViewHandle.ReleasePointer();
                }
            }
        }

        private static unsafe void Pad0(ReadOnlySpan<byte> searchPattern, ReadOnlySpan<byte> patternToReplace, byte* bytes, int offset)
        {
            if (patternToReplace.Length < searchPattern.Length)
            {
                for (int i = patternToReplace.Length; i < searchPattern.Length; i++)
                {
                    bytes[i + offset] = 0x0;
                }
            }
        }

        public static unsafe void SearchAndReplace(
            string filePath,
            byte[] searchPattern,
            byte[] patternToReplace,
            bool pad0s = true)
        {
            using (var mappedFile = MemoryMappedFile.CreateFromFile(filePath))
            {
                using (var accessor = mappedFile.CreateViewAccessor())
                {
                    SearchAndReplace(accessor, searchPattern, patternToReplace, pad0s);
                }
            }
        }

        internal static unsafe int SearchInFile(MemoryMappedViewAccessor accessor, ReadOnlySpan<byte> searchPattern)
        {
            var safeBuffer = accessor.SafeMemoryMappedViewHandle;
            return KMPSearch(searchPattern, (byte*)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength);
        }

        public static unsafe int SearchInFile(string filePath, byte[] searchPattern)
        {
            using (var mappedFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read))
            {
                using (var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read))
                {
                    return SearchInFile(accessor, searchPattern);
                }
            }
        }

        // See: https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
        private static int[] ComputeKMPFailureFunction(ReadOnlySpan<byte> pattern)
        {
            int[] table = new int[pattern.Length];
            if (pattern.Length >= 1)
            {
                table[0] = -1;
            }
            if (pattern.Length >= 2)
            {
                table[1] = 0;
            }

            int pos = 2;
            int cnd = 0;
            while (pos < pattern.Length)
            {
                if (pattern[pos - 1] == pattern[cnd])
                {
                    table[pos] = cnd + 1;
                    cnd++;
                    pos++;
                }
                else if (cnd > 0)
                {
                    cnd = table[cnd];
                }
                else
                {
                    table[pos] = 0;
                    pos++;
                }
            }
            return table;
        }

        // See: https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
        private static unsafe int KMPSearch(ReadOnlySpan<byte> pattern, byte* bytes, long bytesLength)
        {
            int m = 0;
            int i = 0;
            int[] table = ComputeKMPFailureFunction(pattern);

            while (m + i < bytesLength)
            {
                if (pattern[i] == bytes[m + i])
                {
                    if (i == pattern.Length - 1)
                    {
                        return m;
                    }
                    i++;
                }
                else
                {
                    if (table[i] > -1)
                    {
                        m = m + i - table[i];
                        i = table[i];
                    }
                    else
                    {
                        m++;
                        i = 0;
                    }
                }
            }

            return -1;
        }

        public static void CopyFile(string sourcePath, string destinationPath)
        {
            var destinationDirectory = new FileInfo(destinationPath).Directory.FullName;
            if (!Directory.Exists(destinationDirectory))
            {
                Directory.CreateDirectory(destinationDirectory);
            }

            // Copy file to destination path so it inherits the same attributes/permissions.
            File.Copy(sourcePath, destinationPath, overwrite: true);
        }

        internal static void WriteToStream(MemoryMappedViewAccessor sourceViewAccessor, FileStream fileStream, long length)
        {
            int pos = 0;
            int bufSize = 16384; //16K

            byte[] buf = new byte[bufSize];
            length = Math.Min(length, sourceViewAccessor.Capacity);
            do
            {
                int bytesRequested = Math.Min((int)length - pos, bufSize);
                if (bytesRequested <= 0)
                {
                    break;
                }

                int bytesRead = sourceViewAccessor.ReadArray(pos, buf, 0, bytesRequested);
                if (bytesRead > 0)
                {
                    fileStream.Write(buf, 0, bytesRead);
                    pos += bytesRead;
                }
            }
            while (true);
        }
    }
}