<Compile Include="$(CommonPath)\NotImplemented.cs" />
<Compile Include="$(CommonPath)\System\SR.cs" />
</ItemGroup>
- <ItemGroup>
- <Compile Include="src\System\Runtime\RuntimeImports.cs" />
- </ItemGroup>
<Import Project="shared\System.Private.CoreLib.Shared.projitems" />
<PropertyGroup>
<CheckCDefines Condition="'$(CheckCDefines)'==''">true</CheckCDefines>
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsPrimitiveTypeArray(Array array);
- // This method has a slightly different behavior on arm and other platforms.
- // On arm this method behaves like memcpy and does not handle overlapping buffers.
- // While on other platforms it behaves like memmove and handles overlapping buffers.
- // This behavioral difference is unfortunate but intentional because
- // 1. This method is given access to other internal dlls and this close to release we do not want to change it.
- // 2. It is difficult to get this right for arm and again due to release dates we would like to visit it later.
-#if ARM
+ // Non-inlinable wrapper around the QCall that avoids polluting the fast path
+ // with P/Invoke prolog/epilog.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal static unsafe void _ZeroMemory(ref byte b, nuint byteLength)
+ {
+ fixed (byte* bytePointer = &b)
+ {
+ __ZeroMemory(bytePointer, byteLength);
+ }
+ }
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ private static extern unsafe void __ZeroMemory(void* b, nuint byteLength);
+
[MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern unsafe void Memcpy(byte* dest, byte* src, int len);
-#else // ARM
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static extern void BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount);
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ private static extern unsafe void __Memmove(byte* dest, byte* src, nuint len);
+
internal static unsafe void Memcpy(byte* dest, byte* src, int len)
{
Debug.Assert(len >= 0, "Negative length in memcpy!");
Memmove(dest, src, (nuint)len);
}
-#endif // ARM
-
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- private static extern unsafe void __Memmove(byte* dest, byte* src, nuint len);
// Used by ilmarshalers.cpp
internal static unsafe void Memcpy(byte* pDest, int destIndex, byte[] src, int srcIndex, int len)
public static void FreeHGlobal(IntPtr hglobal)
{
- if (!IsWin32Atom(hglobal))
+ if (!IsNullOrWin32Atom(hglobal))
{
if (IntPtr.Zero != Interop.Kernel32.LocalFree(hglobal))
{
public static void FreeCoTaskMem(IntPtr ptr)
{
- if (!IsWin32Atom(ptr))
+ if (!IsNullOrWin32Atom(ptr))
{
Interop.Ole32.CoTaskMemFree(ptr);
}
public static void FreeBSTR(IntPtr ptr)
{
- if (!IsWin32Atom(ptr))
+ if (!IsNullOrWin32Atom(ptr))
{
Interop.OleAut32.SysFreeString(ptr);
}
return IntPtr.Zero;
}
- // Overflow checking
- if (s.Length + 1 < s.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(s));
- }
-
IntPtr bstr = Interop.OleAut32.SysAllocStringLen(s, s.Length);
if (bstr == IntPtr.Zero)
{
public static string PtrToStringBSTR(IntPtr ptr)
{
- return PtrToStringUni(ptr, (int)Interop.OleAut32.SysStringLen(ptr));
+ if (ptr == IntPtr.Zero)
+ {
+ throw new ArgumentNullException(nameof(ptr));
+ }
+
+ return PtrToStringUni(ptr, (int)(SysStringByteLen(ptr) / sizeof(char)));
}
#if FEATURE_COMINTEROP
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
-#if BIT64
-using nuint = System.UInt64;
-#else
-using nuint = System.UInt32;
-#endif
-
-namespace System.Runtime
-{
- public class RuntimeImports
- {
- // Non-inlinable wrapper around the QCall that avoids poluting the fast path
- // with P/Invoke prolog/epilog.
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal static unsafe void RhZeroMemory(ref byte b, nuint byteLength)
- {
- fixed (byte* bytePointer = &b)
- {
- RhZeroMemory(bytePointer, byteLength);
- }
- }
-
- internal static unsafe void RhZeroMemory(IntPtr p, UIntPtr byteLength)
- {
- RhZeroMemory((void*)p, (nuint)byteLength);
- }
-
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- private static extern unsafe void RhZeroMemory(void* b, nuint byteLength);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void RhBulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount);
- }
-}
/******************* NLS ****************************************/
-typedef
-enum tagMIMECONTF {
- MIMECONTF_MAILNEWS = 0x1,
- MIMECONTF_BROWSER = 0x2,
- MIMECONTF_MINIMAL = 0x4,
- MIMECONTF_IMPORT = 0x8,
- MIMECONTF_SAVABLE_MAILNEWS = 0x100,
- MIMECONTF_SAVABLE_BROWSER = 0x200,
- MIMECONTF_EXPORT = 0x400,
- MIMECONTF_PRIVCONVERTER = 0x10000,
- MIMECONTF_VALID = 0x20000,
- MIMECONTF_VALID_NLS = 0x40000,
- MIMECONTF_MIME_IE4 = 0x10000000,
- MIMECONTF_MIME_LATEST = 0x20000000,
- MIMECONTF_MIME_REGISTRY = 0x40000000
- } MIMECONTF;
-
#define LCMAP_LOWERCASE 0x00000100
#define LCMAP_UPPERCASE 0x00000200
-#define LCMAP_SORTKEY 0x00000400
-#define LCMAP_BYTEREV 0x00000800
-
-#define LCMAP_HIRAGANA 0x00100000
-#define LCMAP_KATAKANA 0x00200000
-#define LCMAP_HALFWIDTH 0x00400000
-#define LCMAP_FULLWIDTH 0x00800000
-
-#define LCMAP_LINGUISTIC_CASING 0x01000000
// 8 characters for language
// 8 characters for region
// 1 null termination
#define LOCALE_NAME_MAX_LENGTH 85
-#define LOCALE_SCOUNTRY 0x00000006
-#define LOCALE_SENGCOUNTRY 0x00001002
-
-#define LOCALE_SLANGUAGE 0x00000002
-#define LOCALE_SENGLANGUAGE 0x00001001
-
-#define LOCALE_SDATE 0x0000001D
-#define LOCALE_STIME 0x0000001E
-
#define CSTR_LESS_THAN 1
#define CSTR_EQUAL 2
#define CSTR_GREATER_THAN 3
-#define NORM_IGNORENONSPACE 0x00000002
-
-#define WC_COMPOSITECHECK 0x00000000 // NOTE: diff from winnls.h
-
/******************* shlwapi ************************************/
// note: diff in NULL handing and calling convetion
${ARCH_SOURCES_DIR}/asmhelpers.asm
${ARCH_SOURCES_DIR}/CrtHelpers.asm
${ARCH_SOURCES_DIR}/ehhelpers.asm
- ${ARCH_SOURCES_DIR}/memcpy.asm
${ARCH_SOURCES_DIR}/patchedcode.asm
${ARCH_SOURCES_DIR}/PInvokeStubs.asm
)
${ARCH_SOURCES_DIR}/asmhelpers.S
${ARCH_SOURCES_DIR}/crthelpers.S
${ARCH_SOURCES_DIR}/ehhelpers.S
- ${ARCH_SOURCES_DIR}/memcpy.S
${ARCH_SOURCES_DIR}/patchedcode.S
${ARCH_SOURCES_DIR}/pinvokestubs.S
)
return GetARMInstructionLength(*(WORD*)pInstr);
}
-EXTERN_C void FCallMemcpy(BYTE* dest, BYTE* src, int len);
-
#endif // __cgencpu_h__
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#include "unixasmmacros.inc"
-#include "asmconstants.h"
-
-.syntax unified
-.thumb
-
-//
-// void *memcpy(void *dst, const void *src, size_t length)
-//
-// Copy a block of memory in a forward direction.
-//
-
- NESTED_ENTRY FCallMemcpy, _TEXT, NoHandler
- cmp r2, #0
-
- beq LOCAL_LABEL(GC_POLL)
-
- PROLOG_PUSH "{r7, lr}"
- PROLOG_STACK_SAVE r7
-
- ldrb r3, [r0]
- ldrb r3, [r1]
-
- blx C_FUNC(memcpy)
-
- EPILOG_POP "{r7, pc}"
-
-LOCAL_LABEL(GC_POLL):
- ldr r0, =g_TrapReturningThreads
- ldr r0, [r0]
- cmp r0, #0
- it ne
- bne C_FUNC(FCallMemCpy_GCPoll)
- bx lr
- NESTED_END_MARKED FCallMemcpy, _TEXT
+++ /dev/null
-; Licensed to the .NET Foundation under one or more agreements.
-; The .NET Foundation licenses this file to you under the MIT license.
-; See the LICENSE file in the project root for more information.
-
-;
-
-;
-
-; This is the fast memcpy implementation for ARM stolen from the CRT (original location
-; vctools\crt\crtw32\string\arm\memcpy.asm) and modified to be compatible with CLR.
-;
-; For reference, the unmodified crt version of memcpy is preserved as memcpy_crt.asm
-
-#include "ksarm.h"
-#include "asmmacros.h"
-
- IMPORT FCallMemCpy_GCPoll
- IMPORT g_TrapReturningThreads
-
- AREA |.text|,ALIGN=5,CODE,READONLY
-
-;
-; void *memcpy(void *dst, const void *src, size_t length)
-;
-; Copy a block of memory in a forward direction.
-;
-
- ALIGN 32
- LEAF_ENTRY FCallMemcpy
-
- pld [r1] ; preload the first cache line
- cmp r2, #16 ; less than 16 bytes?
- mov r3, r0 ; use r3 as our destination
- bhs.W __FCallMemcpy_large ; go to the large copy case directly. ".W" indicates encoding using 32bits
-
-CpySmal tbb [pc, r2] ; branch to specialized bits for small copies
-__SwitchTable1_Copy
-CTable dcb (Copy0 - CTable) / 2 ; 0B
- dcb (Copy1 - CTable) / 2 ; 1B
- dcb (Copy2 - CTable) / 2 ; 2B
- dcb (Copy3 - CTable) / 2 ; 3B
- dcb (Copy4 - CTable) / 2 ; 4B
- dcb (Copy5 - CTable) / 2 ; 5B
- dcb (Copy6 - CTable) / 2 ; 6B
- dcb (Copy7 - CTable) / 2 ; 7B
- dcb (Copy8 - CTable) / 2 ; 8B
- dcb (Copy9 - CTable) / 2 ; 9B
- dcb (Copy10 - CTable) / 2 ; 10B
- dcb (Copy11 - CTable) / 2 ; 11B
- dcb (Copy12 - CTable) / 2 ; 12B
- dcb (Copy13 - CTable) / 2 ; 13B
- dcb (Copy14 - CTable) / 2 ; 14B
- dcb (Copy15 - CTable) / 2 ; 15B
-__SwitchTableEnd_Copy
-
-Copy1 ldrb r2, [r1]
- strb r2, [r3]
-Copy0 b GC_POLL
-
-Copy2 ldrh r2, [r1]
- strh r2, [r3]
- b GC_POLL
-
-Copy3 ldrh r2, [r1]
- ldrb r1, [r1, #2]
- strh r2, [r3]
- strb r1, [r3, #2]
- b GC_POLL
-
-Copy4 ldr r2, [r1]
- str r2, [r3]
- b GC_POLL
-
-Copy5 ldr r2, [r1]
- ldrb r1, [r1, #4]
- str r2, [r3]
- strb r1, [r3, #4]
- b GC_POLL
-
-Copy6 ldr r2, [r1]
- ldrh r1, [r1, #4]
- str r2, [r3]
- strh r1, [r3, #4]
- b GC_POLL
-
-Copy7 ldr r12, [r1]
- ldrh r2, [r1, #4]
- ldrb r1, [r1, #6]
- str r12, [r3]
- strh r2, [r3, #4]
- strb r1, [r3, #6]
- b GC_POLL
-
-Copy8 ldr r2, [r1]
- ldr r1, [r1, #4]
- str r2, [r3]
- str r1, [r3, #4]
- b GC_POLL
-
-Copy9 ldr r12, [r1]
- ldr r2, [r1, #4]
- ldrb r1, [r1, #8]
- str r12, [r3]
- str r2, [r3, #4]
- strb r1, [r3, #8]
- b GC_POLL
-
-Copy10 ldr r12, [r1]
- ldr r2, [r1, #4]
- ldrh r1, [r1, #8]
- str r12, [r3]
- str r2, [r3, #4]
- strh r1, [r3, #8]
- b GC_POLL
-
-Copy11 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldrh r2, [r1, #8]
- ldrb r1, [r1, #10]
- strh r2, [r3, #8]
- strb r1, [r3, #10]
- b GC_POLL
-
-Copy12 ldr r12, [r1]
- ldr r2, [r1, #4]
- ldr r1, [r1, #8]
- str r12, [r3]
- str r2, [r3, #4]
- str r1, [r3, #8]
- b GC_POLL
-
-Copy13 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldr r2, [r1, #8]
- ldrb r1, [r1, #12]
- str r2, [r3, #8]
- strb r1, [r3, #12]
- b GC_POLL
-
-Copy14 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldr r2, [r1, #8]
- ldrh r1, [r1, #12]
- str r2, [r3, #8]
- strh r1, [r3, #12]
- b GC_POLL
-
-Copy15 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldr r12, [r1, #8]
- ldrh r2, [r1, #12]
- ldrb r1, [r1, #14]
- str r12, [r3, #8]
- strh r2, [r3, #12]
- strb r1, [r3, #14]
-GC_POLL
- ldr r0, =g_TrapReturningThreads
- ldr r0, [r0]
- cmp r0, #0
- bne FCallMemCpy_GCPoll
-
- bx lr
-
- LEAF_END FCallMemcpy
-
-
-;
-; __memcpy_forward_large_integer (internal calling convention)
-;
-; Copy large (>= 16 bytes) blocks of memory in a forward direction,
-; using integer registers only.
-;
-
- ALIGN 32
- NESTED_ENTRY __FCallMemcpy_large
-
- PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0
- PROLOG_PUSH {r4-r9, r11, lr}
-
-;
-; Align destination to a word boundary
-;
-
- bpl %F1
- ldrb r4, [r1], #1 ; fetch byte
- subs r2, r2, #1 ; decrement count
- strb r4, [r3], #1 ; store byte
- lsls r12, r3, #31 ; compute updated status
-1
- bcc %F2 ; if already aligned, just skip ahead
- ldrh r4, [r1], #2 ; fetch halfword
- subs r2, r2, #2 ; decrement count
- strh r4, [r3], #2 ; store halfword
-2
- tst r1, #3 ; is the source now word-aligned?
- bne %F20 ; if not, we have to use the slow path
-
-;
-; Source is word-aligned; fast case
-;
-
-10
- subs r2, r2, #32 ; take 32 off the top
- blo %F13 ; if not enough, recover and do small copies
- subs r2, r2, #32 ; take off another 32
- pld [r1, #32] ; pre-load one block ahead
- blo %F12 ; skip the loop if that's all we have
-11
- pld [r1, #64] ; prefetch ahead
- subs r2, r2, #32 ; count the bytes for this block
- ldm r1!, {r4-r9, r12, lr} ; load 32 bytes
- stm r3!, {r4-r9, r12, lr} ; store 32 bytes
- bhs %B11 ; keep going until we're done
-12
- ldm r1!, {r4-r9, r12, lr} ; load 32 bytes
- stm r3!, {r4-r9, r12, lr} ; store 32 bytes
-13
- adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement
- blo %F15 ; if not enough remaining, skip this loop
-14
- subs r2, r2, #8 ; decrement count
- ldrd r4, r5, [r1], #8 ; fetch pair of words
- strd r4, r5, [r3], #8 ; store pair of words
- bhs %B14 ; loop while we still have data remaining
-15
- adds r2, r2, #8 ; recover final count
-
- EPILOG_POP {r4-r9, r11, lr}
- EPILOG_NOP bne CpySmal ; if some left, continue with small
- EPILOG_BRANCH GC_POLL
-
-;
-; Source is not word-aligned; slow case
-;
-
-20
- subs r2, r2, #64 ; pre-decrement to simplify the loop
- blo %23 ; skip over the loop if we don't have enough
- pld [r1, #32] ; pre-load one block ahead
-21
- pld [r1, #64] ; prefetch ahead
- ldr r4, [r1, #0] ; load 32 bytes
- ldr r5, [r1, #4] ;
- ldr r6, [r1, #8] ;
- ldr r7, [r1, #12] ;
- ldr r8, [r1, #16] ;
- ldr r9, [r1, #20] ;
- ldr r12, [r1, #24] ;
- ldr lr, [r1, #28] ;
- adds r1, r1, #32 ; update pointer
- subs r2, r2, #32 ; count the bytes for this block
- stm r3!, {r4-r9, r12, lr} ; store 32 bytes
- bhs %B21 ; keep going until we're done
-23
- adds r2, r2, #(64 - 8) ; recover original count, and pre-decrement
- blo %F25 ; if not enough remaining, skip this loop
-24
- ldr r4, [r1] ; fetch pair of words
- ldr r5, [r1, #4] ;
- adds r1, r1, #8 ; update pointer
- subs r2, r2, #8 ; decrement count
- strd r4, r5, [r3], #8 ; store pair of words
- bhs %B24 ; loop while we still have data remaining
-25
- adds r2, r2, #8 ; recover final count
-
- EPILOG_POP {r4-r9, r11, lr}
- EPILOG_NOP bne CpySmal ; if some left, continue with small
- EPILOG_BRANCH GC_POLL
-
- EXPORT FCallMemcpy_End ; this is used to place the entire
-FCallMemcpy_End ; implementation in av-exclusion list
-
- NESTED_END __FCallMemcpy_large
-
- END
+++ /dev/null
-; Licensed to the .NET Foundation under one or more agreements.
-; The .NET Foundation licenses this file to you under the MIT license.
-; See the LICENSE file in the project root for more information.
-
-;
-
-;
-
-#include "ksarm.h"
-
-#if !defined PF_ARM_EXTERNAL_CACHE_AVAILABLE
-#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 0x1a
-#endif
-
-#if !defined(_BOOTCRT_)
-
- DATAAREA
-
-__memcpy_forward_large_func dcd __memcpy_decide
- EXPORT __memcpy_forward_large_func
-__memcpy_reverse_large_func dcd __memcpy_decide
- EXPORT __memcpy_reverse_large_func
-
-#endif
-
- AREA |.text|,ALIGN=5,CODE,READONLY
-
-;
-; void *memcpy(void *dst, const void *src, size_t length)
-;
-; Copy a block of memory in a forward direction.
-;
-
- ALIGN 32
- LEAF_ENTRY memcpy
-
- ALTERNATE_ENTRY __memcpy_forward_new
-
- pld [r1] ; preload the first cache line
- cmp r2, #16 ; less than 16 bytes?
- mov r3, r0 ; use r3 as our destination
- bhs CpyLrge ; go to the small copy case directly
-
-CpySmal tbb [pc, r2] ; branch to specialized bits for small copies
-__SwitchTable1_Copy
-CTable dcb (Copy0 - CTable) / 2 ; 0B
- dcb (Copy1 - CTable) / 2 ; 1B
- dcb (Copy2 - CTable) / 2 ; 2B
- dcb (Copy3 - CTable) / 2 ; 3B
- dcb (Copy4 - CTable) / 2 ; 4B
- dcb (Copy5 - CTable) / 2 ; 5B
- dcb (Copy6 - CTable) / 2 ; 6B
- dcb (Copy7 - CTable) / 2 ; 7B
- dcb (Copy8 - CTable) / 2 ; 8B
- dcb (Copy9 - CTable) / 2 ; 9B
- dcb (Copy10 - CTable) / 2 ; 10B
- dcb (Copy11 - CTable) / 2 ; 11B
- dcb (Copy12 - CTable) / 2 ; 12B
- dcb (Copy13 - CTable) / 2 ; 13B
- dcb (Copy14 - CTable) / 2 ; 14B
- dcb (Copy15 - CTable) / 2 ; 15B
-__SwitchTableEnd_Copy
-
-Copy1 ldrb r2, [r1]
- strb r2, [r3]
-Copy0 bx lr
-
-Copy2 ldrh r2, [r1]
- strh r2, [r3]
- bx lr
-
-Copy3 ldrh r2, [r1]
- ldrb r1, [r1, #2]
- strh r2, [r3]
- strb r1, [r3, #2]
- bx lr
-
-Copy4 ldr r2, [r1]
- str r2, [r3]
- bx lr
-
-Copy5 ldr r2, [r1]
- ldrb r1, [r1, #4]
- str r2, [r3]
- strb r1, [r3, #4]
- bx lr
-
-Copy6 ldr r2, [r1]
- ldrh r1, [r1, #4]
- str r2, [r3]
- strh r1, [r3, #4]
- bx lr
-
-Copy7 ldr r12, [r1]
- ldrh r2, [r1, #4]
- ldrb r1, [r1, #6]
- str r12, [r3]
- strh r2, [r3, #4]
- strb r1, [r3, #6]
- bx lr
-
-Copy8 ldr r2, [r1]
- ldr r1, [r1, #4]
- str r2, [r3]
- str r1, [r3, #4]
- bx lr
-
-Copy9 ldr r12, [r1]
- ldr r2, [r1, #4]
- ldrb r1, [r1, #8]
- str r12, [r3]
- str r2, [r3, #4]
- strb r1, [r3, #8]
- bx lr
-
-Copy10 ldr r12, [r1]
- ldr r2, [r1, #4]
- ldrh r1, [r1, #8]
- str r12, [r3]
- str r2, [r3, #4]
- strh r1, [r3, #8]
- bx lr
-
-Copy11 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldrh r2, [r1, #8]
- ldrb r1, [r1, #10]
- strh r2, [r3, #8]
- strb r1, [r3, #10]
- bx lr
-
-Copy12 ldr r12, [r1]
- ldr r2, [r1, #4]
- ldr r1, [r1, #8]
- str r12, [r3]
- str r2, [r3, #4]
- str r1, [r3, #8]
- bx lr
-
-Copy13 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldr r2, [r1, #8]
- ldrb r1, [r1, #12]
- str r2, [r3, #8]
- strb r1, [r3, #12]
- bx lr
-
-Copy14 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldr r2, [r1, #8]
- ldrh r1, [r1, #12]
- str r2, [r3, #8]
- strh r1, [r3, #12]
- bx lr
-
-Copy15 ldr r12, [r1]
- ldr r2, [r1, #4]
- str r12, [r3]
- str r2, [r3, #4]
- ldr r12, [r1, #8]
- ldrh r2, [r1, #12]
- ldrb r1, [r1, #14]
- str r12, [r3, #8]
- strh r2, [r3, #12]
- strb r1, [r3, #14]
- bx lr
-
-CpyLrge
-
-#if defined(_BOOTCRT_)
-
- b __memcpy_forward_large_integer ; always use integer in boot code
-
-#else
-
- eor r12, r0, r1 ; see if src/dst are equally aligned
- tst r12, #3 ; at least to a 4 byte boundary
- bne __memcpy_forward_large_neon ; if not, always use NEON
- mov32 r12, __memcpy_forward_large_func ; otherwise, load the large function pointer
- ldr pc, [r12] ; and call it
-
-#endif
-
- LEAF_END memcpy
-
-
-;
-; __memcpy_forward_large_integer (internal calling convention)
-;
-; Copy large (>= 16 bytes) blocks of memory in a forward direction,
-; using integer registers only.
-;
-
- ALIGN 32
- NESTED_ENTRY __memcpy_forward_large_integer_wrapper
-
-__memcpy_forward_large_integer
-
- PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0
- PROLOG_PUSH {r4-r9, r11, lr}
-
-;
-; Align destination to a word boundary
-;
-
- bpl %F1
- ldrb r4, [r1], #1 ; fetch byte
- subs r2, r2, #1 ; decrement count
- strb r4, [r3], #1 ; store byte
- lsls r12, r3, #31 ; compute updated status
-1
- bcc %F2 ; if already aligned, just skip ahead
- ldrh r4, [r1], #2 ; fetch halfword
- subs r2, r2, #2 ; decrement count
- strh r4, [r3], #2 ; store halfword
-2
- tst r1, #3 ; is the source now word-aligned?
- bne %F20 ; if not, we have to use the slow path
-
-;
-; Source is word-aligned; fast case
-;
-
-10
- subs r2, r2, #32 ; take 32 off the top
- blo %F13 ; if not enough, recover and do small copies
- subs r2, r2, #32 ; take off another 32
- pld [r1, #32] ; pre-load one block ahead
- blo %F12 ; skip the loop if that's all we have
-11
- pld [r1, #64] ; prefetch ahead
- subs r2, r2, #32 ; count the bytes for this block
- ldm r1!, {r4-r9, r12, lr} ; load 32 bytes
- stm r3!, {r4-r9, r12, lr} ; store 32 bytes
- bhs %B11 ; keep going until we're done
-12
- ldm r1!, {r4-r9, r12, lr} ; load 32 bytes
- stm r3!, {r4-r9, r12, lr} ; store 32 bytes
-13
- adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement
- blo %F15 ; if not enough remaining, skip this loop
-14
- subs r2, r2, #8 ; decrement count
- ldrd r4, r5, [r1], #8 ; fetch pair of words
- strd r4, r5, [r3], #8 ; store pair of words
- bhs %B14 ; loop while we still have data remaining
-15
- adds r2, r2, #8 ; recover final count
-
- EPILOG_POP {r4-r9, r11, lr}
- EPILOG_NOP bne CpySmal ; if some left, continue with small
- EPILOG_RETURN ; else just return
-
-;
-; Source is not word-aligned; slow case
-;
-
-20
- subs r2, r2, #64 ; pre-decrement to simplify the loop
- blo %23 ; skip over the loop if we don't have enough
- pld [r1, #32] ; pre-load one block ahead
-21
- pld [r1, #64] ; prefetch ahead
- ldr r4, [r1, #0] ; load 32 bytes
- ldr r5, [r1, #4] ;
- ldr r6, [r1, #8] ;
- ldr r7, [r1, #12] ;
- ldr r8, [r1, #16] ;
- ldr r9, [r1, #20] ;
- ldr r12, [r1, #24] ;
- ldr lr, [r1, #28] ;
- adds r1, r1, #32 ; update pointer
- subs r2, r2, #32 ; count the bytes for this block
- stm r3!, {r4-r9, r12, lr} ; store 32 bytes
- bhs %B21 ; keep going until we're done
-23
- adds r2, r2, #(64 - 8) ; recover original count, and pre-decrement
- blo %F25 ; if not enough remaining, skip this loop
-24
- ldr r4, [r1] ; fetch pair of words
- ldr r5, [r1, #4] ;
- adds r1, r1, #8 ; update pointer
- subs r2, r2, #8 ; decrement count
- strd r4, r5, [r3], #8 ; store pair of words
- bhs %B24 ; loop while we still have data remaining
-25
- adds r2, r2, #8 ; recover final count
-
- EPILOG_POP {r4-r9, r11, lr}
- EPILOG_NOP bne CpySmal ; if some left, continue with small
- EPILOG_RETURN ; else just return
-
- NESTED_END __memcpy_forward_large_integer
-
-
-;
-; __memcpy_forward_large_neon (internal calling convention)
-;
-; Copy large (>= 16 bytes) blocks of memory in a forward direction,
-; using NEON registers.
-;
-
-#if !defined(_BOOTCRT_)
-
- ALIGN 32
- NESTED_ENTRY __memcpy_forward_large_neon_wrapper
-
-__memcpy_forward_large_neon
-
- PROLOG_PUSH {r4-r5, r11, lr}
-
- subs r2, r2, #32 ; pre-decrement to simplify the loop
- blo %F13 ; skip over the loop if we don't have enough
- subs r2, r2, #32 ; pre-decrement to simplify the loop
- pld [r1, #32] ; pre-load one block ahead
- blo %F12 ; skip over the loop if we don't have enough
-11
- pld [r1, #64] ; prefetch ahead
- subs r2, r2, #32 ; count the bytes for this block
- vld1.8 {d0-d3}, [r1]! ; load 32 bytes
- vst1.8 {d0-d3}, [r3]! ; store 32 bytes
- bhs %B11 ; keep going until we're done
-12
- vld1.8 {d0-d3}, [r1]! ; load 32 bytes
- vst1.8 {d0-d3}, [r3]! ; store 32 bytes
-13
- adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement
- blo %F15 ; if not enough remaining, skip this loop
-14
- ldr r4, [r1] ; fetch pair of words
- ldr r5, [r1, #4] ;
- adds r1, r1, #8 ; update pointer
- str r4, [r3] ; store pair of words
- str r5, [r3, #4] ;
- adds r3, r3, #8
- subs r2, r2, #8 ; decrement count
- bhs %B14 ; loop while we still have data remaining
-15
- adds r2, r2, #8 ; recover final count
-
- EPILOG_POP {r4-r5, r11, lr}
- EPILOG_NOP bne CpySmal ; if some left, continue with small
- EPILOG_RETURN ; else just return
-
- NESTED_END __memcpy_forward_large_neon
-
-#endif
-
-
-;
-; void *memmove(void *dst, const void *src, size_t length)
-;
-; Copy a block of memory in a forward or reverse direction, ensuring that
-; overlapping source/destination regions are copied correctly.
-;
-
- ALIGN 32
- LEAF_ENTRY memmove
-
- subs r3, r0, r1 ; compute dest - source
- cmp r3, r2 ; compare against size
- bhs memcpy ; if no overlap, we can just do memcpy
-
- ALTERNATE_ENTRY __memcpy_reverse_new
-
- cmp r2, #16 ; less than 16 bytes?
- pld [r1] ; preload the first cache line
- bhs MovLrge ; go to the small copy case directly
-
-MovSmal tbb [pc, r2] ; branch to specialized bits for small copies
-__SwitchTable1_Move
-MTable dcb (Move0 - MTable) / 2 ; 0B
- dcb (Move1 - MTable) / 2 ; 1B
- dcb (Move2 - MTable) / 2 ; 2B
- dcb (Move3 - MTable) / 2 ; 3B
- dcb (Move4 - MTable) / 2 ; 4B
- dcb (Move5 - MTable) / 2 ; 5B
- dcb (Move6 - MTable) / 2 ; 6B
- dcb (Move7 - MTable) / 2 ; 7B
- dcb (Move8 - MTable) / 2 ; 8B
- dcb (Move9 - MTable) / 2 ; 9B
- dcb (Move10 - MTable) / 2 ; 10B
- dcb (Move11 - MTable) / 2 ; 11B
- dcb (Move12 - MTable) / 2 ; 12B
- dcb (Move13 - MTable) / 2 ; 13B
- dcb (Move14 - MTable) / 2 ; 14B
- dcb (Move15 - MTable) / 2 ; 15B
-__SwitchTableEnd_Move
-
-Move1 ldrb r2, [r1]
- strb r2, [r0]
-Move0 bx lr
-
-Move2 ldrh r2, [r1]
- strh r2, [r0]
- bx lr
-
-Move3 ldrh r2, [r1]
- ldrb r1, [r1, #2]
- strh r2, [r0]
- strb r1, [r0, #2]
- bx lr
-
-Move4 ldr r2, [r1]
- str r2, [r0]
- bx lr
-
-Move5 ldr r2, [r1]
- ldrb r1, [r1, #4]
- str r2, [r0]
- strb r1, [r0, #4]
- bx lr
-
-Move6 ldr r2, [r1]
- ldrh r1, [r1, #4]
- str r2, [r0]
- strh r1, [r0, #4]
- bx lr
-
-Move7 ldr r3, [r1]
- ldrh r2, [r1, #4]
- ldrb r1, [r1, #6]
- str r3, [r0]
- strh r2, [r0, #4]
- strb r1, [r0, #6]
- bx lr
-
-Move8 ldr r2, [r1]
- ldr r1, [r1, #4]
- str r2, [r0]
- str r1, [r0, #4]
- bx lr
-
-Move9 ldr r3, [r1]
- ldr r2, [r1, #4]
- ldrb r1, [r1, #8]
- str r3, [r0]
- str r2, [r0, #4]
- strb r1, [r0, #8]
- bx lr
-
-Move10 ldr r3, [r1]
- ldr r2, [r1, #4]
- ldrh r1, [r1, #8]
- str r3, [r0]
- str r2, [r0, #4]
- strh r1, [r0, #8]
- bx lr
-
-Move11 ldr r12, [r1]
- ldr r3, [r1, #4]
- ldrh r2, [r1, #8]
- ldrb r1, [r1, #10]
- str r12, [r0]
- str r3, [r0, #4]
- strh r2, [r0, #8]
- strb r1, [r0, #10]
- bx lr
-
-Move12 ldr r12, [r1]
- ldr r2, [r1, #4]
- ldr r1, [r1, #8]
- str r12, [r0]
- str r2, [r0, #4]
- str r1, [r0, #8]
- bx lr
-
-Move13 ldr r12, [r1]
- ldr r3, [r1, #4]
- ldr r2, [r1, #8]
- ldrb r1, [r1, #12]
- str r12, [r0]
- str r3, [r0, #4]
- str r2, [r0, #8]
- strb r1, [r0, #12]
- bx lr
-
-Move14 ldr r12, [r1]
- ldr r3, [r1, #4]
- ldr r2, [r1, #8]
- ldrh r1, [r1, #12]
- str r12, [r0]
- str r3, [r0, #4]
- str r2, [r0, #8]
- strh r1, [r0, #12]
- bx lr
-
-Move15 ldrh r3, [r1, #12]
- ldrb r2, [r1, #14]
- strh r3, [r0, #12]
- strb r2, [r0, #14]
- ldr r3, [r1]
- ldr r2, [r1, #4]
- ldr r1, [r1, #8]
- str r3, [r0]
- str r2, [r0, #4]
- str r1, [r0, #8]
- bx lr
-
-MovLrge
-
-#if defined(_BOOTCRT_)
-
- b __memcpy_reverse_large_integer ; always use integer in boot code
-
-#else
-
- eor r12, r0, r1 ; see if src/dst are equally aligned
- tst r12, #3 ; at least to a 4 byte boundary
- bne __memcpy_reverse_large_neon ; if not, always use NEON
- mov32 r12, __memcpy_reverse_large_func
- ldr pc, [r12]
-
-#endif
-
- LEAF_END memmove
-
-
-;
-; __memcpy_reverse_large_integer (internal calling convention)
-;
-; Copy large (>= 16 bytes) block of memory in a reverse direction,
-; using NEON registers.
-;
-
- ALIGN 32
- NESTED_ENTRY __memcpy_reverse_large_integer_wrapper
-
-__memcpy_reverse_large_integer
-
- PROLOG_NOP adds r3, r0, r2 ; advance destination to end
- PROLOG_NOP adds r1, r1, r2 ; advance source to end
- PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0
- PROLOG_NOP pld [r1, #-32] ; pre-load one block ahead
- PROLOG_PUSH {r4-r9, r11, lr}
-
-;
-; Align destination to a word boundary
-;
-
- bpl %F1
- ldrb r4, [r1, #-1]! ; fetch byte
- subs r2, r2, #1 ; decrement count
- strb r4, [r3, #-1]! ; store byte
- lsls r12, r3, #31 ; compute updated status
-1
- bcc %F2 ; if already aligned, just skip ahead
- ldrh r4, [r1, #-2]! ; fetch halfword
- subs r2, r2, #2 ; decrement count
- strh r4, [r3, #-2]! ; store halfword
-2
- tst r1, #3 ; is the source now word-aligned?
- bne %F20 ; if not, we have to use the slow path
-
-;
-; Source is word-aligned; fast case
-;
-
-10
- subs r2, r2, #32 ; pre-decrement to simplify the loop
- blo %F13 ; skip over the loop if we don't have enough
- subs r2, r2, #32 ; pre-decrement to simplify the loop
- pld [r1, #-64] ; pre-load one block ahead
- blo %F12 ; skip over the loop if we don't have enough
-11
- pld [r1, #-96] ; prefetch ahead
- subs r2, r2, #32 ; count the bytes for this block
- ldmdb r1!, {r4-r9, r12, lr} ; load 32 bytes
- stmdb r3!, {r4-r9, r12, lr} ; store 32 bytes
- bhs %B11 ; keep going until we're done
-12
- ldmdb r1!, {r4-r9, r12, lr} ; load 32 bytes
- stmdb r3!, {r4-r9, r12, lr} ; store 32 bytes
-13
- adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement
- blo %F15 ; if not enough remaining, skip this loop
-14
- subs r2, r2, #8 ; decrement count
- ldrd r4, r5, [r1, #-8]! ; fetch pair of words
- strd r4, r5, [r3, #-8]! ; store pair of words
- bhs %B14 ; loop while we still have data remaining
-15
- adds r2, r2, #8 ; determine final count
- subs r1, r1, r2 ; recover original source
-
- EPILOG_POP {r4-r9, r11, lr}
- EPILOG_NOP bne MovSmal ; if some left, continue with small
- EPILOG_RETURN ; else just return
-
-
-;
-; Source is not word-aligned; slow case
-;
-
-20
- subs r2, r2, #64 ; pre-decrement to simplify the loop
- blo %F23 ; skip over the loop if we don't have enough
- pld [r1, #-64] ; pre-load one block ahead
-21
- pld [r1, #-96] ; prefetch ahead
- subs r2, r2, #32 ; count the bytes for this block
- ldr r4, [r1, #-32]! ; load 32 bytes
- ldr r5, [r1, #4] ;
- ldr r6, [r1, #8] ;
- ldr r7, [r1, #12] ;
- ldr r8, [r1, #16] ;
- ldr r9, [r1, #20] ;
- ldr r12, [r1, #24] ;
- ldr lr, [r1, #28] ;
- stmdb r3!, {r4-r9, r12, lr} ; store 32 bytes
- bhs %B21 ; keep going until we're done
-23
- adds r2, r2, #(64 - 8) ; recover original count, and pre-decrement
- blo %F25 ; if not enough remaining, skip this loop
-24
- subs r2, r2, #8 ; decrement count
- ldr r4, [r1, #-8]! ; fetch pair of words
- ldr r5, [r1, #4] ;
- strd r4, r5, [r3, #-8]! ; store pair of words
- bhs %B24 ; loop while we still have data remaining
-25
- adds r2, r2, #8 ; determine final count
- subs r1, r1, r2 ; recover original source
-
- EPILOG_POP {r4-r9, r11, lr}
- EPILOG_NOP bne MovSmal ; if some left, continue with small
- EPILOG_RETURN ; else just return
-
- NESTED_END __memcpy_reverse_large_integer
-
-
-;
-; __memcpy_reverse_large_neon (internal calling convention)
-;
-; Copy large (>= 16 bytes) block of memory in a reverse direction,
-; using NEON registers.
-;
-
-#if !defined(_BOOTCRT_)
-
- ALIGN 32
- NESTED_ENTRY __memcpy_reverse_large_neon_wrapper
-
-__memcpy_reverse_large_neon
-
- PROLOG_NOP adds r3, r0, r2 ; advance destination to end
- PROLOG_NOP adds r1, r1, r2 ; advance source to end
- PROLOG_NOP lsls r12, r3, #31 ; C = bit 1, N = bit 0
- PROLOG_NOP pld [r1, #-32] ; pre-load one block ahead
- PROLOG_PUSH {r4-r5, r11, lr}
-
-;
-; Align destination to a word boundary
-;
-
- bpl %F1
- ldrb r4, [r1, #-1]! ; fetch byte
- subs r2, r2, #1 ; decrement count
- strb r4, [r3, #-1]! ; store byte
- lsls r12, r3, #31 ; compute updated status
-1
- bcc %F2 ; if already aligned, just skip ahead
- ldrh r4, [r1, #-2]! ; fetch halfword
- subs r2, r2, #2 ; decrement count
- strh r4, [r3, #-2]! ; store halfword
-2
-
-;
-; Perform main copy
-;
-
- subs r2, r2, #32 ; pre-decrement to simplify the loop
- blo %F13 ; skip over the loop if we don't have enough
- subs r2, r2, #32 ; pre-decrement to simplify the loop
- pld [r1, #-64] ; pre-load one block ahead
- blo %F12 ; skip over the loop if we don't have enough
-11
- pld [r1, #-96] ; prefetch ahead
- subs r1, r1, #32
- subs r3, r3, #32
- subs r2, r2, #32 ; count the bytes for this block
- vld1.8 {d0-d3}, [r1] ; load 32 bytes
- vst1.8 {d0-d3}, [r3] ; store 32 bytes
- bhs %B11 ; keep going until we're done
-12
- subs r1, r1, #32
- subs r3, r3, #32
- vld1.8 {d0-d3}, [r1] ; load 32 bytes
- vst1.8 {d0-d3}, [r3] ; store 32 bytes
-13
- adds r2, r2, #(32 - 8) ; recover original count, and pre-decrement
- blo %F15 ; if not enough remaining, skip this loop
-14
- ldr r4, [r1, #-8]! ; fetch pair of words
- ldr r5, [r1, #4] ; fetch pair of words
- subs r2, r2, #8 ; decrement count
- str r4, [r3, #-8]! ; store pair of words
- str r5, [r3, #4]
- bhs %B14 ; loop while we still have data remaining
-15
- adds r2, r2, #8 ; determine final count
- subs r1, r1, r2 ; recover original source
-
- EPILOG_POP {r4-r5, r11, lr}
- EPILOG_NOP bne MovSmal ; if some left, continue with small
- EPILOG_RETURN ; else just return
-
- NESTED_END __memcpy_reverse_large_neon
-
-#endif
-
-
-;
-; __memcpy_decide (internal calling convention)
-;
-; Determine whether to use integer or NEON for future memcpy's.
-;
-
-#if !defined(_BOOTCRT_)
-
- ALIGN 32
- NESTED_ENTRY __memcpy_decide_wrapper
-
-__memcpy_decide
-
- PROLOG_PUSH {r4-r5, r11, lr}
-
- ;
- ; We want to use integer memcpy's on the A9, which has an external cache.
- ;
- ; First determine if we're in user or kernel mode. Reading CPSR
- ; from user mode will either return the proper 5 mode bits, or all 0s.
- ; Conveniently, user mode is 0x10, and there is no mode 0x00, so if
- ; we read CPSR and the low 4 bits are 0, that's good enough.
- ;
-
- mrs r4, cpsr ; get CPSR
- ands r4, r4, #0xf ; isolate the low 4 bits of the mode
- beq %F1 ; if 0, we're in user mode
-
- ;
- ; If we are in kernel mode, read the MIDR directly.
- ;
-
- CP_READ r4, CP15_MIDR ; read main ID register
- ubfx r5, r4, #24, #8 ; get implementer
- lsrs r4, r4, #4 ; shift off revision field
- cmp r5, #0x41 ; is implementer == ARM?
- bne %F3 ; if not, use NEON
- bfc r4, #12, #20 ; clear upper bits
- ldr r5, =0xc09 ; A9 signature
- cmp r4, r5 ; is this an A9?
- bne %F3 ; if not, use NEON
- b %F2 ; otherwise, use integer
-
- ;
- ; If we are in user mode, check the "external cache available" flag
- ;
-1
- ldr r4, =MM_SHARED_USER_DATA_VA + UsProcessorFeatures + PF_ARM_EXTERNAL_CACHE_AVAILABLE
- ldrb r4, [r4] ; get external cache bit
- cbz r4, %F3 ; if no external cache, do NEON
-
- ;
- ; Register for integer functions
- ;
-2
- ldr r4, =__memcpy_forward_large_integer ; select integer functions
- ldr r5, =__memcpy_forward_large_func ;
- str r4, [r5] ;
- ldr r4, =__memcpy_reverse_large_integer ; select integer functions
- ldr r5, =__memcpy_reverse_large_func ;
- str r4, [r5] ;
- b %F4
-
- ;
- ; Register for NEON functions
- ;
-3
- ldr r4, =__memcpy_forward_large_neon ; select NEON functions
- ldr r5, =__memcpy_forward_large_func ;
- str r4, [r5] ;
- ldr r4, =__memcpy_reverse_large_neon ; select NEON functions
- ldr r5, =__memcpy_reverse_large_func ;
- str r4, [r5] ;
-4
- EPILOG_POP {r4-r5, r11, lr} ; restore saved registers
- EPILOG_NOP ldr pc, [r12] ; jump to the appropriate target
-
- NESTED_END __memcpy_decide
-
-#endif
-
-
-;
-; void _memcpy_strict_align(void *dst, const void *src, size_t length)
-;
-; Copy a block of memory in a forward direction, only performing naturally-aligned
-; accesses.
-;
-
- ALIGN 32
- LEAF_ENTRY _memcpy_strict_align
-
-;
-; Verify alignment between source and destination
-;
-
- sub r3, r0, r1 ; get relative alignment of source and destination
- cbz r2, CopyExit ; exit if 0 count
- ands r3, r3, #3 ; check DWORD alignment
- bne CopyMisalignedHalf ; misaligned
-
-;
-; Source and destination are equally aligned: just align the
-; destination and the source will end up aligned as well
-;
-
- tst r0, #3 ; dword aligned at the dest?
- beq WordAligned_0 ; if so, skip ahead
- tst r0, #1 ; halfword aligned at the dest?
- beq HalfAligned_0 ; if so, skip ahead
-
- subs r2, r2, #1 ; decrement count
- ldrb r3, [r1], #1 ; fetch byte
- strb r3, [r0], #1 ; store it
- beq CopyExit ; stop if done
- tst r0, #3 ; word aligned now?
- beq WordAligned_0 ; if so, skip ahead
-
-HalfAligned_0
- cmp r2, #2 ; do we have at least 2 bytes left?
- blo CopyFinalBytes ; if not, copy bytes
- subs r2, r2, #2 ; decrement count
- ldrh r3, [r1], #2 ; fetch halfword
- strh r3, [r0], #2 ; store it
- beq CopyExit ; stop if done
-
-WordAligned_0
- subs r2, r2, #4 ; at least 4 bytes remaining?
- blt WordLoopEnd_0 ; if not, skip the main loop
-WordLoop_0
- subs r2, r2, #4 ; decrement count
- ldr r3, [r1], #4 ; fetch word
- str r3, [r0], #4 ; store it
- bge WordLoop_0 ; stop if done
-WordLoopEnd_0
- adds r2, r2, #4 ; recover the extra 4 we subtracted
- beq CopyExit ; stop if that's everything
-
-CopyFinalHalfwords
- subs r2, r2, #2 ; at least 2 bytes remaining?
- blt CopyFinalHalfwordsEnd ; if not, skip this
-CopyFinalHalfwordsLoop
- subs r2, r2, #2 ; decrement count
- ldrh r3, [r1], #2 ; fetch halfword
- strh r3, [r0], #2 ; store it
- bge CopyFinalHalfwordsLoop ; loop until done
-CopyFinalHalfwordsEnd
- adds r2, r2, #2 ; recover the extra 2 we subtracted
- beq CopyExit ; stop if that's everything
-
-CopyFinalBytes
- subs r2, r2, #1 ; decrement count
- ldrb r3, [r1], #1 ; fetch byte
- strb r3, [r0], #1 ; store it
- bne CopyFinalBytes ; loop until done
-CopyExit
- bx lr ; return
-
-
-;
-; Source and destination are misaligned by 2 bytes
-;
-
-CopyMisalignedHalf
- cmp r3, #2 ; misaligned by a halfword?
- bne CopyMisalignedByte ; if not, skip
-
- tst r0, #3 ; dword aligned at the dest?
- beq WordAligned_2 ; if so, skip ahead
- tst r0, #1 ; halfword aligned at the dest?
- beq HalfAligned_2 ; if so, skip ahead
-
- subs r2, r2, #1 ; decrement count
- ldrb r3, [r1], #1 ; fetch byte
- strb r3, [r0], #1 ; store it
- beq CopyExit ; stop if done
- tst r0, #3 ; word aligned now?
- beq WordAligned_2 ; if so, skip ahead
-
-HalfAligned_2
- cmp r2, #2 ; do we have at least 2 bytes left?
- blo CopyFinalBytes ; if not, copy bytes
- subs r2, r2, #2 ; decrement count
- ldrh r3, [r1], #2 ; fetch halfword
- strh r3, [r0], #2 ; store it
- beq CopyExit ; stop if done
-
-WordAligned_2
- subs r2, r2, #6 ; at least 6 bytes remaining?
- blt WordLoopEnd_2 ; if so, skip the main loop
- ldrh r12, [r1], #2 ; preload a halfword of source
- subs r2, r2, #2 ; count these 2 bytes
-WordLoop_2
- subs r2, r2, #4 ; decrement count
- ldr r3, [r1], #4 ; fetch word
- orr r12, r12, r3, lsl #16 ; copy low 16 bits to upper 16 of r12
- str r12, [r0], #4 ; store it
- lsr r12, r3, #16 ; copy upper 16 bits to lower 16 of r12
- bge WordLoop_2 ; stop if done
- strh r12, [r0], #2 ; store the extra halfword to the dest
-WordLoopEnd_2
- adds r2, r2, #6 ; recover the extra 6 we subtracted
- beq CopyExit ; stop if that's everything
- b CopyFinalHalfwords ; otherwise, copy remainder
-
-
-;
-; Source and destination are misaligned by 1 byte
-;
-
-CopyMisalignedByte
- cmp r3, #1 ; misaligned by a byte?
- bne CopyMisalignedByte3 ; if not, skip
-
- tst r0, #3 ; dword aligned at the dest?
- beq WordAligned_1 ; if so, skip ahead
-ByteAlign_1
- subs r2, r2, #1 ; decrement count
- ldrb r3, [r1], #1 ; fetch byte
- strb r3, [r0], #1 ; store it
- beq CopyExit ; stop if done
- tst r0, #3 ; word aligned now?
- bne ByteAlign_1 ; if not, keep copying bytes
-
-WordAligned_1
- subs r2, r2, #5 ; at least 5 bytes remaining?
- blt WordLoopEnd_1 ; if so, skip the main loop
- ldrb r12, [r1], #1 ; preload a byte of source
- subs r2, r2, #1 ; count this byte
-WordLoop_1
- subs r2, r2, #4 ; decrement count
- ldr r3, [r1], #4 ; fetch word
- orr r12, r12, r3, lsl #8 ; copy low 24 bits to upper 24 of r12
- str r12, [r0], #4 ; store it
- lsr r12, r3, #24 ; copy upper 8 bits to lower 8 of r12
- bge WordLoop_1 ; stop if done
- strb r12, [r0], #1 ; store the extra byte to the dest
-WordLoopEnd_1
- adds r2, r2, #5 ; recover the extra 5 we subtracted
- beq CopyExit ; stop if that's everything
- b CopyFinalBytes ; otherwise, copy remainder
-
-
-;
-; Source and destination are misaligned by 3 bytes
-;
-
-CopyMisalignedByte3
- tst r0, #3 ; dword aligned at the dest?
- beq WordAligned_3 ; if so, skip ahead
-ByteAlign_3
- subs r2, r2, #1 ; decrement count
- ldrb r3, [r1], #1 ; fetch byte
- strb r3, [r0], #1 ; store it
- beq CopyExit ; stop if done
- tst r0, #3 ; word aligned now?
- bne ByteAlign_3 ; if not, keep copying bytes
-
-WordAligned_3
- subs r2, r2, #7 ; at least 7 bytes remaining?
- blt WordLoopEnd_3 ; if so, skip the main loop
- ldrb r12, [r1], #1 ; preload a byte of source
- ldrh r3, [r1], #2 ; preload a halfword of source
- orr r12, r12, r3, lsl #8 ; OR in the halfword
- subs r2, r2, #3 ; count these 3 bytes
-WordLoop_3
- subs r2, r2, #4 ; decrement count
- ldr r3, [r1], #4 ; fetch word
- orr r12, r12, r3, lsl #24 ; copy low 8 bits to upper 8 of r12
- str r12, [r0], #4 ; store it
- lsr r12, r3, #8 ; copy upper 24 bits to lower 24 of r12
- bge WordLoop_3 ; stop if done
- strh r12, [r0], #2 ; store the extra halfword to the dest
- lsr r12, r12, #16 ; down to the final byte
- strb r12, [r0], #1 ; store the extra byte to the dest
-WordLoopEnd_3
- adds r2, r2, #7 ; recover the extra 7 we subtracted
- beq CopyExit ; stop if that's everything
- b CopyFinalBytes ; otherwise, copy remainder
-
- LEAF_END _memcpy_strict_align
-
- END
END_QCALL;
}
-void QCALLTYPE MemoryNative::Clear(void *dst, size_t length)
+void QCALLTYPE Buffer::Clear(void *dst, size_t length)
{
QCALL_CONTRACT;
memset(dst, 0, length);
}
-FCIMPL3(VOID, MemoryNative::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount)
+FCIMPL3(VOID, Buffer::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount)
{
FCALL_CONTRACT;
- InlinedMemmoveGCRefsHelper(dst, src, byteCount);
+ if (dst != src && byteCount != 0)
+ InlinedMemmoveGCRefsHelper(dst, src, byteCount);
FC_GC_POLL();
}
static FCDECL0(UINT32, GetExceptionCount);
};
-class MemoryNative
-{
-public:
- static void QCALLTYPE Clear(void *dst, size_t length);
- static FCDECL3(VOID, BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount);
-};
-
//
// Buffer
//
-class Buffer {
+class Buffer
+{
public:
-
- // BlockCopy
- // This method from one primitive array to another based
- // upon an offset into each an a byte count.
static FCDECL1(FC_BOOL_RET, IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE);
+ static FCDECL3(VOID, BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount);
+
static void QCALLTYPE MemMove(void *dst, void *src, size_t length);
+ static void QCALLTYPE Clear(void *dst, size_t length);
};
#define MIN_GC_MEMORYPRESSURE_THRESHOLD 100000
FCFuncStart(gBufferFuncs)
FCFuncElement("IsPrimitiveTypeArray", Buffer::IsPrimitiveTypeArray)
-#ifdef _TARGET_ARM_
- FCFuncElement("Memcpy", FCallMemcpy)
-#endif
+ QCFuncElement("__ZeroMemory", Buffer::Clear)
+ FCFuncElement("BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier)
QCFuncElement("__Memmove", Buffer::MemMove)
FCFuncEnd()
FCFuncEnd()
#endif // ifdef FEATURE_COMINTEROP
-FCFuncStart(gRuntimeImportsFuncs)
- QCFuncElement("RhZeroMemory", MemoryNative::Clear)
- FCFuncElement("RhBulkMoveWithWriteBarrier", MemoryNative::BulkMoveWithWriteBarrier)
-FCFuncEnd()
-
FCFuncStart(gWeakReferenceFuncs)
FCFuncElement("Create", WeakReferenceNative::Create)
FCFuncElement("Finalize", WeakReferenceNative::Finalize)
QCFuncElement("SysAllocStringByteLen", SysAllocStringByteLen)
QCFuncElement("SysAllocStringLen", SysAllocStringLen)
QCFuncElement("SysFreeString", SysFreeString)
- QCFuncElement("SysStringLen", SysStringLen)
FCFuncEnd()
#endif
#endif // FEATURE_COMINTEROP
FCClassElement("RuntimeFieldHandle", "System", gCOMFieldHandleNewFuncs)
FCClassElement("RuntimeHelpers", "System.Runtime.CompilerServices", gRuntimeHelpers)
-FCClassElement("RuntimeImports", "System.Runtime", gRuntimeImportsFuncs)
FCClassElement("RuntimeMethodHandle", "System", gRuntimeMethodHandle)
FCClassElement("RuntimeModule", "System.Reflection", gCOMModuleFuncs)
FCClassElement("RuntimeType", "System", gSystem_RuntimeType)
EXTERN_C void JIT_WriteBarrier_Debug_End();
#endif
-#ifdef _TARGET_ARM_
-EXTERN_C void FCallMemcpy_End();
-#endif
-
#ifdef VSD_STUB_CAN_THROW_AV
//Return TRUE if pContext->Pc is in VirtualStub
BOOL IsIPinVirtualStub(PCODE f_IP)
CHECK_RANGE(JIT_WriteBarrier_Debug)
#endif
-#ifdef _TARGET_ARM_
- CHECK_RANGE(FCallMemcpy)
-#endif
-
return false;
}
#endif // FEATURE_EH_FUNCLETS
//========================================================================
//
-// JIT HELPERS IMPLEMENTED AS FCALLS
-//
-//========================================================================
-
-#ifdef _TARGET_ARM_
-// This function is used from the FCallMemcpy for GC polling
-EXTERN_C VOID FCallMemCpy_GCPoll()
-{
- FC_INNER_PROLOG(FCallMemcpy);
-
- Thread *thread = GetThread();
- // CommonTripThread does this check, but doing this to avoid raising the frames
- if (thread->CatchAtSafePointOpportunistic())
- {
- HELPER_METHOD_FRAME_BEGIN_0();
- CommonTripThread();
- HELPER_METHOD_FRAME_END();
- }
-
- FC_INNER_EPILOG();
-}
-#endif // _TARGET_ARM_
-
-//========================================================================
-//
// JIT HELPERS INITIALIZATION
//
//========================================================================
internal const uint CRYPTPROTECTMEMORY_SAME_PROCESS = 0;
[DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)]
- internal static extern bool CryptProtectMemory(SafeBSTRHandle pData, uint cbData, uint dwFlags);
+ internal static extern bool CryptProtectMemory(SafeBuffer pData, uint cbData, uint dwFlags);
[DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)]
- internal static extern bool CryptUnprotectMemory(SafeBSTRHandle pData, uint cbData, uint dwFlags);
+ internal static extern bool CryptUnprotectMemory(SafeBuffer pData, uint cbData, uint dwFlags);
}
}
internal static partial class OleAut32
{
[DllImport(Libraries.OleAut32, CharSet = CharSet.Unicode)]
- internal static extern SafeBSTRHandle SysAllocStringLen(IntPtr src, uint len);
-
- [DllImport(Libraries.OleAut32, CharSet = CharSet.Unicode)]
internal static extern IntPtr SysAllocStringLen(string? src, int len);
}
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-using System.Security;
-
-internal static partial class Interop
-{
- internal static partial class OleAut32
- {
- [DllImport(Libraries.OleAut32)]
- internal static extern uint SysStringLen(SafeBSTRHandle bstr);
-
- [DllImport(Libraries.OleAut32)]
- internal static extern uint SysStringLen(IntPtr bstr);
- }
-}
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Errors.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysFreeString.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysStringLen.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Constants.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.EventWaitHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_IntPtr.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Variables.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Win32Marshal.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Security\SafeBSTRHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Mutex.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Semaphore.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\EventWaitHandle.Windows.cs" />
Unsafe.Add<byte>(ref array.GetRawArrayData(), index) = value;
}
- // This is currently used by System.IO.UnmanagedMemoryStream
- internal static unsafe void ZeroMemory(byte* dest, long len)
- {
- Debug.Assert((ulong)(len) == (nuint)(len));
- ZeroMemory(dest, (nuint)(len));
- }
-
// This method has different signature for x64 and other platforms and is done for performance reasons.
internal static unsafe void ZeroMemory(byte* dest, nuint len)
{
else
{
// Non-blittable memmove
-
- // Try to avoid calling RhBulkMoveWithWriteBarrier if we can get away
- // with a no-op.
- if (!Unsafe.AreSame(ref destination, ref source) && elementCount != 0)
- {
- RuntimeImports.RhBulkMoveWithWriteBarrier(
- ref Unsafe.As<T, byte>(ref destination),
- ref Unsafe.As<T, byte>(ref source),
- elementCount * (nuint)Unsafe.SizeOf<T>());
- }
+ BulkMoveWithWriteBarrier(
+ ref Unsafe.As<T, byte>(ref destination),
+ ref Unsafe.As<T, byte>(ref source),
+ elementCount * (nuint)Unsafe.SizeOf<T>());
}
}
using System.Threading;
using System.Threading.Tasks;
+#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
namespace System.IO
{
/*
{
unsafe
{
- Buffer.ZeroMemory(_mem + len, value - len);
+ Buffer.ZeroMemory(_mem + len, (nuint)(value - len));
}
}
Interlocked.Exchange(ref _length, value);
// zero any memory in the middle.
if (pos > len)
{
- Buffer.ZeroMemory(_mem + len, pos - len);
+ Buffer.ZeroMemory(_mem + len, (nuint)(pos - len));
}
// set length after zeroing memory to avoid race condition of accessing unzeroed memory
{
unsafe
{
- Buffer.ZeroMemory(_mem + len, pos - len);
+ Buffer.ZeroMemory(_mem + len, (nuint)(pos - len));
}
}
Debug.Assert(unchecked((uint)(maxResultLength)) <= MaxBlockCount);
// Zero out result internal blocks.
- Buffer.ZeroMemory((byte*)(result.GetBlocksPointer()), maxResultLength * sizeof(uint));
+ Buffer.ZeroMemory((byte*)result.GetBlocksPointer(), (uint)maxResultLength * sizeof(uint));
int smallIndex = 0;
int resultStartIndex = 0;
var result = new BigInteger(0);
Multiply(ref this, ref value, ref result);
- Buffer.Memcpy((byte*)(GetBlocksPointer()), (byte*)(result.GetBlocksPointer()), (result._length) * sizeof(uint));
+ Buffer.Memcpy((byte*)GetBlocksPointer(), (byte*)result.GetBlocksPointer(), result._length * sizeof(uint));
_length = result._length;
}
public void SetValue(ref BigInteger rhs)
{
int rhsLength = rhs._length;
- Buffer.Memcpy((byte*)(GetBlocksPointer()), (byte*)(rhs.GetBlocksPointer()), rhsLength * sizeof(uint));
+ Buffer.Memcpy((byte*)GetBlocksPointer(), (byte*)rhs.GetBlocksPointer(), rhsLength * sizeof(uint));
_length = rhsLength;
}
_length += (int)(blocksToShift);
// Zero the remaining low blocks
- Buffer.ZeroMemory((byte*)(GetBlocksPointer()), blocksToShift * sizeof(uint));
+ Buffer.ZeroMemory((byte*)GetBlocksPointer(), blocksToShift * sizeof(uint));
}
else
{
_blocks[writeIndex - 1] = block << (int)(remainingBitsToShift);
// Zero the remaining low blocks
- Buffer.ZeroMemory((byte*)(GetBlocksPointer()), blocksToShift * sizeof(uint));
+ Buffer.ZeroMemory((byte*)GetBlocksPointer(), blocksToShift * sizeof(uint));
// Check if the terminating block has no set bits
if (_blocks[_length - 1] == 0)
private static int GetSystemMaxDBCSCharSize() => 3;
- private static bool IsWin32Atom(IntPtr ptr) => false;
+ private static bool IsNullOrWin32Atom(IntPtr ptr) => ptr == IntPtr.Zero;
internal static unsafe int StringToAnsiString(string s, byte* buffer, int bufferLength, bool bestFit = false, bool throwOnUnmappableChar = false)
{
return convertedBytes;
}
+
+ // Returns number of bytes required to convert given string to Ansi string. The return value includes null terminator.
+ internal static unsafe int GetAnsiStringByteCount(ReadOnlySpan<char> chars)
+ {
+ int byteLength = Encoding.UTF8.GetByteCount(chars);
+ return checked(byteLength + 1);
+ }
+
+ // Converts given string to Ansi string. The destination buffer must be large enough to hold the converted value, including null terminator.
+ internal static unsafe void GetAnsiStringBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
+ {
+ int actualByteLength = Encoding.UTF8.GetBytes(chars, bytes);
+ bytes[actualByteLength] = 0;
+ }
}
}
// Win32 has the concept of Atoms, where a pointer can either be a pointer
// or an int. If it's less than 64K, this is guaranteed to NOT be a
// pointer since the bottom 64K bytes are reserved in a process' page table.
- // We should be careful about deallocating this stuff. Extracted to
- // a function to avoid C# problems with lack of support for IntPtr.
- // We have 2 of these methods for slightly different semantics for NULL.
- private static bool IsWin32Atom(IntPtr ptr)
+ // We should be careful about deallocating this stuff.
+ private static bool IsNullOrWin32Atom(IntPtr ptr)
{
const long HIWORDMASK = unchecked((long)0xffffffffffff0000L);
buffer[nb] = 0;
return nb;
}
+
+ // Returns number of bytes required to convert given string to Ansi string. The return value includes null terminator.
+ internal static unsafe int GetAnsiStringByteCount(ReadOnlySpan<char> chars)
+ {
+ int byteLength;
+
+ if (chars.Length == 0)
+ {
+ byteLength = 0;
+ }
+ else
+ {
+ fixed (char* pChars = chars)
+ {
+ byteLength = Interop.Kernel32.WideCharToMultiByte(
+ Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, pChars, chars.Length, null, 0, IntPtr.Zero, IntPtr.Zero);
+ if (byteLength <= 0)
+ throw new ArgumentException();
+ }
+ }
+
+ return checked(byteLength + 1);
+ }
+
+ // Converts given string to Ansi string. The destination buffer must be large enough to hold the converted value, including null terminator.
+ internal static unsafe void GetAnsiStringBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
+ {
+ int byteLength;
+
+ if (chars.Length == 0)
+ {
+ byteLength = 0;
+ }
+ else
+ {
+ fixed (char* pChars = chars)
+ fixed (byte* pBytes = bytes)
+ {
+ byteLength = Interop.Kernel32.WideCharToMultiByte(
+ Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, pChars, chars.Length, pBytes, bytes.Length, IntPtr.Zero, IntPtr.Zero);
+ if (byteLength <= 0)
+ throw new ArgumentException();
+ }
+ }
+
+ bytes[byteLength] = 0;
+ }
}
}
public static unsafe string? PtrToStringAnsi(IntPtr ptr)
{
- if (ptr == IntPtr.Zero || IsWin32Atom(ptr))
+ if (IsNullOrWin32Atom(ptr))
{
return null;
}
public static unsafe string? PtrToStringUni(IntPtr ptr)
{
- if (ptr == IntPtr.Zero || IsWin32Atom(ptr))
+ if (IsNullOrWin32Atom(ptr))
{
return null;
}
public static unsafe string? PtrToStringUTF8(IntPtr ptr)
{
- if (ptr == IntPtr.Zero || IsWin32Atom(ptr))
+ if (IsNullOrWin32Atom(ptr))
{
return null;
}
public static IntPtr /* IDispatch */ GetIDispatchForObject(object o) => throw new PlatformNotSupportedException();
- public static void ZeroFreeBSTR(IntPtr s)
+ public static unsafe void ZeroFreeBSTR(IntPtr s)
{
if (s == IntPtr.Zero)
{
return;
}
- RuntimeImports.RhZeroMemory(s, (UIntPtr)SysStringByteLen(s));
+ Buffer.ZeroMemory((byte*)s, SysStringByteLen(s));
FreeBSTR(s);
}
{
return;
}
- RuntimeImports.RhZeroMemory(s, (UIntPtr)(string.wcslen((char*)s) * 2));
+ Buffer.ZeroMemory((byte*)s, (nuint)string.wcslen((char*)s) * sizeof(char));
FreeCoTaskMem(s);
}
{
return;
}
- RuntimeImports.RhZeroMemory(s, (UIntPtr)string.strlen((byte*)s));
+ Buffer.ZeroMemory((byte*)s, (nuint)string.strlen((byte*)s));
FreeCoTaskMem(s);
}
{
return;
}
- RuntimeImports.RhZeroMemory(s, (UIntPtr)string.strlen((byte*)s));
+ Buffer.ZeroMemory((byte*)s, (nuint)string.strlen((byte*)s));
FreeHGlobal(s);
}
{
return;
}
- RuntimeImports.RhZeroMemory(s, (UIntPtr)(string.wcslen((char*)s) * 2));
+ Buffer.ZeroMemory((byte*)s, (nuint)string.wcslen((char*)s) * sizeof(char));
FreeHGlobal(s);
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace System.Security
-{
- internal sealed class SafeBSTRHandle : SafeBuffer
- {
- internal SafeBSTRHandle() : base(true) { }
-
- internal static SafeBSTRHandle Allocate(uint lenInChars)
- {
- ulong lenInBytes = (ulong)lenInChars * sizeof(char);
- SafeBSTRHandle bstr = Interop.OleAut32.SysAllocStringLen(IntPtr.Zero, lenInChars);
- if (bstr.IsInvalid) // SysAllocStringLen returns a NULL ptr when there's insufficient memory
- {
- throw new OutOfMemoryException();
- }
- bstr.Initialize(lenInBytes);
- return bstr;
- }
-
- protected override bool ReleaseHandle()
- {
- RuntimeImports.RhZeroMemory(handle, (UIntPtr)Marshal.SysStringByteLen(handle));
- Interop.OleAut32.SysFreeString(handle);
- return true;
- }
-
- internal unsafe void ClearBuffer()
- {
- byte* bufferPtr = null;
- try
- {
- AcquirePointer(ref bufferPtr);
- RuntimeImports.RhZeroMemory((IntPtr)bufferPtr, (UIntPtr)Marshal.SysStringByteLen((IntPtr)bufferPtr));
- }
- finally
- {
- if (bufferPtr != null)
- {
- ReleasePointer();
- }
- }
- }
-
- internal unsafe uint Length => Interop.OleAut32.SysStringLen(this);
-
- internal static unsafe void Copy(SafeBSTRHandle source, SafeBSTRHandle target, uint bytesToCopy)
- {
- if (bytesToCopy == 0)
- {
- return;
- }
-
- byte* sourcePtr = null, targetPtr = null;
- try
- {
- source.AcquirePointer(ref sourcePtr);
- target.AcquirePointer(ref targetPtr);
-
- Debug.Assert(source.ByteLength >= bytesToCopy, "Source buffer is too small.");
- Buffer.MemoryCopy(sourcePtr, targetPtr, target.ByteLength, bytesToCopy);
- }
- finally
- {
- if (targetPtr != null)
- {
- target.ReleasePointer();
- }
- if (sourcePtr != null)
- {
- source.ReleasePointer();
- }
- }
- }
- }
-}
public sealed partial class SecureString
{
- private UnmanagedBuffer? _buffer;
-
- internal SecureString(SecureString str)
- {
- // Allocate enough space to store the provided string
- EnsureCapacity(str._decryptedLength);
- _decryptedLength = str._decryptedLength;
-
- // Copy the string into the newly allocated space
- if (_decryptedLength > 0)
- {
- Debug.Assert(str._buffer != null && _buffer != null); ;
- UnmanagedBuffer.Copy(str._buffer, _buffer, (ulong)(str._decryptedLength * sizeof(char)));
- }
- }
-
- private unsafe void InitializeSecureString(char* value, int length)
- {
- // Allocate enough space to store the provided string
- EnsureCapacity(length);
- _decryptedLength = length;
- if (length == 0)
- {
- return;
- }
-
- Debug.Assert(_buffer != null);
- // Copy the string into the newly allocated space
- byte* ptr = null;
- try
- {
- _buffer.AcquirePointer(ref ptr);
- Buffer.MemoryCopy(value, ptr, _buffer.ByteLength, (ulong)(length * sizeof(char)));
- }
- finally
- {
- if (ptr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
-
- private void DisposeCore()
- {
- if (_buffer != null && !_buffer.IsInvalid)
- {
- _buffer.Dispose();
- _buffer = null;
- }
- }
-
- private void ClearCore()
+ private static int GetAlignedByteSize(int length)
{
- _decryptedLength = 0;
- Debug.Assert(_buffer != null);
- _buffer.Clear();
+ return Math.Max(length, 1) * sizeof(char);
}
- private unsafe void AppendCharCore(char c)
+ private void ProtectMemory()
{
- // Make sure we have enough space for the new character, then write it at the end.
- EnsureCapacity(_decryptedLength + 1);
- Debug.Assert(_buffer != null);
- _buffer.Write((ulong)(_decryptedLength * sizeof(char)), c);
- _decryptedLength++;
+ _encrypted = true;
}
- private unsafe void InsertAtCore(int index, char c)
+ private void UnprotectMemory()
{
- // Make sure we have enough space for the new character, then shift all of the characters above it and insert it.
- EnsureCapacity(_decryptedLength + 1);
- byte* ptr = null;
- Debug.Assert(_buffer != null);
- try
- {
- _buffer.AcquirePointer(ref ptr);
- ptr += index * sizeof(char);
- long bytesToShift = (_decryptedLength - index) * sizeof(char);
- Buffer.MemoryCopy(ptr, ptr + sizeof(char), bytesToShift, bytesToShift);
- *((char*)ptr) = c;
- ++_decryptedLength;
- }
- finally
- {
- if (ptr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
-
- private unsafe void RemoveAtCore(int index)
- {
- // Shift down all values above the specified index, then null out the empty space at the end.
- byte* ptr = null;
- Debug.Assert(_buffer != null);
- try
- {
- _buffer.AcquirePointer(ref ptr);
- ptr += index * sizeof(char);
- long bytesToShift = (_decryptedLength - index - 1) * sizeof(char);
- Buffer.MemoryCopy(ptr + sizeof(char), ptr, bytesToShift, bytesToShift);
- *((char*)(ptr + bytesToShift)) = (char)0;
- --_decryptedLength;
- }
- finally
- {
- if (ptr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
-
- private void SetAtCore(int index, char c)
- {
- // Overwrite the character at the specified index
- Debug.Assert(_buffer != null);
- _buffer.Write((ulong)(index * sizeof(char)), c);
- }
-
- internal unsafe IntPtr MarshalToBSTRCore()
- {
- int length = _decryptedLength;
- IntPtr ptr = IntPtr.Zero;
- IntPtr result = IntPtr.Zero;
- byte* bufferPtr = null;
- Debug.Assert(_buffer != null);
-
- try
- {
- _buffer.AcquirePointer(ref bufferPtr);
- int resultByteLength = (length + 1) * sizeof(char);
-
- ptr = Marshal.AllocBSTR(length);
-
- Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char));
-
- result = ptr;
- }
- finally
- {
- // If we failed for any reason, free the new buffer
- if (result == IntPtr.Zero && ptr != IntPtr.Zero)
- {
- RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char)));
- Marshal.FreeBSTR(ptr);
- }
-
- if (bufferPtr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- return result;
- }
-
- internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode)
- {
- int length = _decryptedLength;
-
- byte* bufferPtr = null;
- IntPtr stringPtr = IntPtr.Zero, result = IntPtr.Zero;
- Debug.Assert(_buffer != null);
- try
- {
- _buffer.AcquirePointer(ref bufferPtr);
- if (unicode)
- {
- int resultLength = (length + 1) * sizeof(char);
- stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength);
- Buffer.MemoryCopy(
- source: bufferPtr,
- destination: (byte*)stringPtr.ToPointer(),
- destinationSizeInBytes: resultLength,
- sourceBytesToCopy: length * sizeof(char));
- *(length + (char*)stringPtr) = '\0';
- }
- else
- {
- int resultLength = Encoding.UTF8.GetByteCount((char*)bufferPtr, length) + 1;
- stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength);
- int encodedLength = Encoding.UTF8.GetBytes((char*)bufferPtr, length, (byte*)stringPtr, resultLength);
- Debug.Assert(encodedLength + 1 == resultLength, $"Expected encoded length to match result, got {encodedLength} != {resultLength}");
- *(resultLength - 1 + (byte*)stringPtr) = 0;
- }
-
- result = stringPtr;
- }
- finally
- {
- // If there was a failure, such that result isn't initialized,
- // release the string if we had one.
- if (stringPtr != IntPtr.Zero && result == IntPtr.Zero)
- {
- RuntimeImports.RhZeroMemory(stringPtr, (UIntPtr)(length * sizeof(char)));
- MarshalFree(stringPtr, globalAlloc);
- }
-
- if (bufferPtr != null)
- {
- _buffer.ReleasePointer();
- }
- }
-
- return result;
- }
-
- // -----------------------------
- // ---- PAL layer ends here ----
- // -----------------------------
-
- private void EnsureCapacity(int capacity)
- {
- // Make sure the requested capacity doesn't exceed SecureString's defined limit
- if (capacity > MaxLength)
- {
- throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity);
- }
-
- // If we already have enough space allocated, we're done
- if (_buffer != null && (capacity * sizeof(char)) <= (int)_buffer.ByteLength)
- {
- return;
- }
-
- // We need more space, so allocate a new buffer, copy all our data into it,
- // and then swap the new for the old.
- UnmanagedBuffer newBuffer = UnmanagedBuffer.Allocate(capacity * sizeof(char));
- if (_buffer != null)
- {
- UnmanagedBuffer.Copy(_buffer, newBuffer, _buffer.ByteLength);
- _buffer.Dispose();
- }
- _buffer = newBuffer;
- }
-
- /// <summary>SafeBuffer for managing memory meant to be kept confidential.</summary>
- private sealed class UnmanagedBuffer : SafeBuffer
- {
- internal UnmanagedBuffer() : base(true) { }
-
- internal static UnmanagedBuffer Allocate(int bytes)
- {
- Debug.Assert(bytes >= 0);
- UnmanagedBuffer buffer = new UnmanagedBuffer();
- buffer.SetHandle(Marshal.AllocHGlobal(bytes));
- buffer.Initialize((ulong)bytes);
- return buffer;
- }
-
- internal unsafe void Clear()
- {
- byte* ptr = null;
- try
- {
- AcquirePointer(ref ptr);
- RuntimeImports.RhZeroMemory((IntPtr)ptr, (UIntPtr)ByteLength);
- }
- finally
- {
- if (ptr != null)
- {
- ReleasePointer();
- }
- }
- }
-
- internal static unsafe void Copy(UnmanagedBuffer source, UnmanagedBuffer destination, ulong bytesLength)
- {
- if (bytesLength == 0)
- {
- return;
- }
-
- byte* srcPtr = null, dstPtr = null;
- try
- {
- source.AcquirePointer(ref srcPtr);
- destination.AcquirePointer(ref dstPtr);
- Buffer.MemoryCopy(srcPtr, dstPtr, destination.ByteLength, bytesLength);
- }
- finally
- {
- if (dstPtr != null)
- {
- destination.ReleasePointer();
- }
- if (srcPtr != null)
- {
- source.ReleasePointer();
- }
- }
- }
-
- protected override unsafe bool ReleaseHandle()
- {
- Marshal.FreeHGlobal(handle);
- return true;
- }
+ _encrypted = false;
}
}
}
{
public sealed partial class SecureString
{
- internal SecureString(SecureString str)
+ private static int GetAlignedByteSize(int length)
{
- Debug.Assert(str != null, "Expected non-null SecureString");
- Debug.Assert(str._buffer != null, "Expected other SecureString's buffer to be non-null");
- Debug.Assert(str._encrypted, "Expected to be used only on encrypted SecureStrings");
+ int byteSize = Math.Max(length, 1) * sizeof(char);
- AllocateBuffer(str._buffer.Length);
- Debug.Assert(_buffer != null);
- SafeBSTRHandle.Copy(str._buffer, _buffer, str._buffer.Length * sizeof(char));
-
- _decryptedLength = str._decryptedLength;
- _encrypted = str._encrypted;
- }
-
- private unsafe void InitializeSecureString(char* value, int length)
- {
- Debug.Assert(length >= 0, $"Expected non-negative length, got {length}");
-
- AllocateBuffer((uint)length);
- _decryptedLength = length;
-
- byte* bufferPtr = null;
- Debug.Assert(_buffer != null);
- try
- {
- _buffer.AcquirePointer(ref bufferPtr);
- Buffer.MemoryCopy((byte*)value, bufferPtr, (long)_buffer.ByteLength, length * sizeof(char));
- }
- finally
- {
- if (bufferPtr != null)
- {
- _buffer.ReleasePointer();
- }
- }
-
- ProtectMemory();
- }
-
- private void AppendCharCore(char c)
- {
- UnprotectMemory();
- try
- {
- EnsureCapacity(_decryptedLength + 1);
- Debug.Assert(_buffer != null);
- _buffer.Write<char>((uint)_decryptedLength * sizeof(char), c);
- _decryptedLength++;
- }
- finally
- {
- ProtectMemory();
- }
- }
-
- private void ClearCore()
- {
- _decryptedLength = 0;
- Debug.Assert(_buffer != null);
- _buffer.ClearBuffer();
- }
-
- private void DisposeCore()
- {
- if (_buffer != null)
- {
- _buffer.Dispose();
- _buffer = null;
- }
- }
-
- private unsafe void InsertAtCore(int index, char c)
- {
- byte* bufferPtr = null;
- UnprotectMemory();
- Debug.Assert(_buffer != null);
- try
- {
- EnsureCapacity(_decryptedLength + 1);
-
- _buffer.AcquirePointer(ref bufferPtr);
- char* pBuffer = (char*)bufferPtr;
-
- for (int i = _decryptedLength; i > index; i--)
- {
- pBuffer[i] = pBuffer[i - 1];
- }
- pBuffer[index] = c;
- ++_decryptedLength;
- }
- finally
- {
- ProtectMemory();
- if (bufferPtr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
-
- private unsafe void RemoveAtCore(int index)
- {
- byte* bufferPtr = null;
- UnprotectMemory();
- Debug.Assert(_buffer != null);
- try
- {
- _buffer.AcquirePointer(ref bufferPtr);
- char* pBuffer = (char*)bufferPtr;
-
- for (int i = index; i < _decryptedLength - 1; i++)
- {
- pBuffer[i] = pBuffer[i + 1];
- }
- pBuffer[--_decryptedLength] = (char)0;
- }
- finally
- {
- ProtectMemory();
- if (bufferPtr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- }
-
- private void SetAtCore(int index, char c)
- {
- UnprotectMemory();
- try
- {
- Debug.Assert(_buffer != null);
- _buffer.Write<char>((uint)index * sizeof(char), c);
- }
- finally
- {
- ProtectMemory();
- }
- }
-
- internal unsafe IntPtr MarshalToBSTRCore()
- {
- int length = _decryptedLength;
- IntPtr ptr = IntPtr.Zero;
- IntPtr result = IntPtr.Zero;
- byte* bufferPtr = null;
-
- UnprotectMemory();
- Debug.Assert(_buffer != null);
- try
- {
- _buffer.AcquirePointer(ref bufferPtr);
- int resultByteLength = (length + 1) * sizeof(char);
-
- ptr = Marshal.AllocBSTR(length);
-
- Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char));
-
- result = ptr;
- }
- finally
- {
- ProtectMemory();
-
- // If we failed for any reason, free the new buffer
- if (result == IntPtr.Zero && ptr != IntPtr.Zero)
- {
- RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char)));
- Marshal.FreeBSTR(ptr);
- }
-
- if (bufferPtr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- return result;
- }
-
- internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode)
- {
- int length = _decryptedLength;
- IntPtr ptr = IntPtr.Zero;
- IntPtr result = IntPtr.Zero;
- byte* bufferPtr = null;
-
- UnprotectMemory();
- Debug.Assert(_buffer != null);
- try
- {
- _buffer.AcquirePointer(ref bufferPtr);
- if (unicode)
- {
- int resultByteLength = (length + 1) * sizeof(char);
- ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength);
- Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char));
- *(length + (char*)ptr) = '\0';
- }
- else
- {
- uint defaultChar = '?';
- int resultByteLength = 1 + Interop.Kernel32.WideCharToMultiByte(
- Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, null, 0, (IntPtr)(&defaultChar), IntPtr.Zero);
- ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength);
- Interop.Kernel32.WideCharToMultiByte(
- Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, (byte*)ptr, resultByteLength - 1, (IntPtr)(&defaultChar), IntPtr.Zero);
- *(resultByteLength - 1 + (byte*)ptr) = 0;
- }
- result = ptr;
- }
- finally
- {
- ProtectMemory();
-
- // If we failed for any reason, free the new buffer
- if (result == IntPtr.Zero && ptr != IntPtr.Zero)
- {
- RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char)));
- MarshalFree(ptr, globalAlloc);
- }
-
- if (bufferPtr != null)
- {
- _buffer.ReleasePointer();
- }
- }
- return result;
- }
-
- // -----------------------------
- // ---- PAL layer ends here ----
- // -----------------------------
-
- private const int BlockSize = (int)Interop.Crypt32.CRYPTPROTECTMEMORY_BLOCK_SIZE / sizeof(char);
- private SafeBSTRHandle? _buffer;
- private bool _encrypted;
-
- private void AllocateBuffer(uint size)
- {
- _buffer = SafeBSTRHandle.Allocate(GetAlignedSize(size));
- }
-
- private static uint GetAlignedSize(uint size) =>
- size == 0 || size % BlockSize != 0 ?
- BlockSize + ((size / BlockSize) * BlockSize) :
- size;
-
- private void EnsureCapacity(int capacity)
- {
- if (capacity > MaxLength)
- {
- throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity);
- }
-
- Debug.Assert(_buffer != null);
- if (((uint)capacity * sizeof(char)) <= _buffer.ByteLength)
- {
- return;
- }
-
- SafeBSTRHandle oldBuffer = _buffer;
- SafeBSTRHandle newBuffer = SafeBSTRHandle.Allocate(GetAlignedSize((uint)capacity));
- SafeBSTRHandle.Copy(oldBuffer, newBuffer, (uint)_decryptedLength * sizeof(char));
- _buffer = newBuffer;
- oldBuffer.Dispose();
+ const int blockSize = (int)Interop.Crypt32.CRYPTPROTECTMEMORY_BLOCK_SIZE;
+ return ((byteSize + (blockSize - 1)) / blockSize) * blockSize;
}
private void ProtectMemory()
if (_decryptedLength != 0 &&
!_encrypted &&
- !Interop.Crypt32.CryptProtectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS))
+ !Interop.Crypt32.CryptProtectMemory(_buffer, (uint)_buffer.ByteLength, Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS))
{
throw new CryptographicException(Marshal.GetLastWin32Error());
}
if (_decryptedLength != 0 &&
_encrypted &&
- !Interop.Crypt32.CryptUnprotectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS))
+ !Interop.Crypt32.CryptUnprotectMemory(_buffer, (uint)_buffer.ByteLength, Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS))
{
throw new CryptographicException(Marshal.GetLastWin32Error());
}
{
private const int MaxLength = 65536;
private readonly object _methodLock = new object();
- private bool _readOnly;
+ private UnmanagedBuffer? _buffer;
private int _decryptedLength;
+ private bool _encrypted;
+ private bool _readOnly;
- public unsafe SecureString()
+ public SecureString()
{
- InitializeSecureString(null, 0);
+ Initialize(ReadOnlySpan<char>.Empty);
}
[CLSCompliant(false)]
throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Length);
}
- InitializeSecureString(value, length);
+ Initialize(new ReadOnlySpan<char>(value, length));
+ }
+
+ private void Initialize(ReadOnlySpan<char> value)
+ {
+ _buffer = UnmanagedBuffer.Allocate(GetAlignedByteSize(value.Length));
+ _decryptedLength = value.Length;
+
+ SafeBuffer? bufferToRelease = null;
+ try
+ {
+ Span<char> span = AcquireSpan(ref bufferToRelease);
+ value.CopyTo(span);
+ }
+ finally
+ {
+ ProtectMemory();
+ bufferToRelease?.DangerousRelease();
+ }
+ }
+
+ private SecureString(SecureString str)
+ {
+ Debug.Assert(str._buffer != null, "Expected other SecureString's buffer to be non-null");
+ Debug.Assert(str._encrypted, "Expected to be used only on encrypted SecureStrings");
+
+ _buffer = UnmanagedBuffer.Allocate((int)str._buffer.ByteLength);
+ Debug.Assert(_buffer != null);
+ UnmanagedBuffer.Copy(str._buffer, _buffer, str._buffer.ByteLength);
+
+ _decryptedLength = str._decryptedLength;
+ _encrypted = str._encrypted;
}
public int Length
}
}
+ private void EnsureCapacity(int capacity)
+ {
+ if (capacity > MaxLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity);
+ }
+
+ Debug.Assert(_buffer != null);
+ if ((uint)capacity * sizeof(char) <= _buffer.ByteLength)
+ {
+ return;
+ }
+
+ UnmanagedBuffer oldBuffer = _buffer;
+ UnmanagedBuffer newBuffer = UnmanagedBuffer.Allocate(GetAlignedByteSize(capacity));
+ UnmanagedBuffer.Copy(oldBuffer, newBuffer, (uint)_decryptedLength * sizeof(char));
+ _buffer = newBuffer;
+ oldBuffer.Dispose();
+ }
+
public void AppendChar(char c)
{
lock (_methodLock)
{
EnsureNotDisposed();
EnsureNotReadOnly();
- AppendCharCore(c);
+
+ Debug.Assert(_buffer != null);
+
+ SafeBuffer? bufferToRelease = null;
+
+ try
+ {
+ UnprotectMemory();
+
+ EnsureCapacity(_decryptedLength + 1);
+
+ Span<char> span = AcquireSpan(ref bufferToRelease);
+ span[_decryptedLength] = c;
+ _decryptedLength++;
+ }
+ finally
+ {
+ ProtectMemory();
+ bufferToRelease?.DangerousRelease();
+ }
}
}
{
EnsureNotDisposed();
EnsureNotReadOnly();
- ClearCore();
+
+ Debug.Assert(_buffer != null);
+
+ _decryptedLength = 0;
+
+ SafeBuffer? bufferToRelease = null;
+ try
+ {
+ Span<char> span = AcquireSpan(ref bufferToRelease);
+ span.Clear();
+ }
+ finally
+ {
+ bufferToRelease?.DangerousRelease();
+ }
}
}
{
lock (_methodLock)
{
- DisposeCore();
+ if (_buffer != null)
+ {
+ _buffer.Dispose();
+ _buffer = null;
+ }
}
}
EnsureNotDisposed();
EnsureNotReadOnly();
- InsertAtCore(index, c);
+ Debug.Assert(_buffer != null);
+
+ SafeBuffer? bufferToRelease = null;
+
+ try
+ {
+ UnprotectMemory();
+
+ EnsureCapacity(_decryptedLength + 1);
+
+ Span<char> span = AcquireSpan(ref bufferToRelease);
+ span.Slice(index, _decryptedLength - index).CopyTo(span.Slice(index + 1));
+ span[index] = c;
+ _decryptedLength++;
+ }
+ finally
+ {
+ ProtectMemory();
+ bufferToRelease?.DangerousRelease();
+ }
}
}
{
lock (_methodLock)
{
- EnsureNotDisposed();
- EnsureNotReadOnly();
-
if (index < 0 || index >= _decryptedLength)
{
throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString);
}
- RemoveAtCore(index);
+ EnsureNotDisposed();
+ EnsureNotReadOnly();
+
+ Debug.Assert(_buffer != null);
+
+ SafeBuffer? bufferToRelease = null;
+
+ try
+ {
+ UnprotectMemory();
+
+ Span<char> span = AcquireSpan(ref bufferToRelease);
+ span.Slice(index + 1, _decryptedLength - (index + 1)).CopyTo(span.Slice(index));
+ _decryptedLength--;
+ }
+ finally
+ {
+ ProtectMemory();
+ bufferToRelease?.DangerousRelease();
+ }
}
}
{
throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString);
}
- Debug.Assert(index <= int.MaxValue / sizeof(char));
EnsureNotDisposed();
EnsureNotReadOnly();
- SetAtCore(index, c);
+ Debug.Assert(_buffer != null);
+
+ SafeBuffer? bufferToRelease = null;
+
+ try
+ {
+ UnprotectMemory();
+
+ Span<char> span = AcquireSpan(ref bufferToRelease);
+ span[index] = c;
+ }
+ finally
+ {
+ ProtectMemory();
+ bufferToRelease?.DangerousRelease();
+ }
}
}
+ private unsafe Span<char> AcquireSpan(ref SafeBuffer? bufferToRelease)
+ {
+ SafeBuffer buffer = _buffer!;
+
+ bool ignore = false;
+ buffer.DangerousAddRef(ref ignore);
+
+ bufferToRelease = buffer;
+
+ return new Span<char>((byte*)buffer.DangerousGetHandle(), (int)(buffer.ByteLength / 2));
+ }
+
private void EnsureNotReadOnly()
{
if (_readOnly)
}
}
- internal IntPtr MarshalToBSTR()
+ internal unsafe IntPtr MarshalToBSTR()
{
lock (_methodLock)
{
EnsureNotDisposed();
- return MarshalToBSTRCore();
+
+ UnprotectMemory();
+
+ SafeBuffer? bufferToRelease = null;
+ IntPtr ptr = IntPtr.Zero;
+ int length = 0;
+ try
+ {
+ Span<char> span = AcquireSpan(ref bufferToRelease);
+
+ length = _decryptedLength;
+ ptr = Marshal.AllocBSTR(length);
+ span.Slice(0, length).CopyTo(new Span<char>((void*)ptr, length));
+
+ IntPtr result = ptr;
+ ptr = IntPtr.Zero;
+ return result;
+ }
+ finally
+ {
+ // If we failed for any reason, free the new buffer
+ if (ptr != IntPtr.Zero)
+ {
+ new Span<char>((void*)ptr, length).Clear();
+ Marshal.FreeBSTR(ptr);
+ }
+
+ ProtectMemory();
+ bufferToRelease?.DangerousRelease();
+ }
}
}
lock (_methodLock)
{
EnsureNotDisposed();
- return MarshalToStringCore(globalAlloc, unicode);
+
+ UnprotectMemory();
+
+ SafeBuffer? bufferToRelease = null;
+ IntPtr ptr = IntPtr.Zero;
+ int byteLength = 0;
+ try
+ {
+ Span<char> span = AcquireSpan(ref bufferToRelease).Slice(0, _decryptedLength);
+
+ if (unicode)
+ {
+ byteLength = (span.Length + 1) * sizeof(char);
+ }
+ else
+ {
+ byteLength = Marshal.GetAnsiStringByteCount(span);
+ }
+
+ if (globalAlloc)
+ {
+ ptr = Marshal.AllocHGlobal(byteLength);
+ }
+ else
+ {
+ ptr = Marshal.AllocCoTaskMem(byteLength);
+ }
+
+ if (unicode)
+ {
+ Span<char> resultSpan = new Span<char>((void*)ptr, byteLength / sizeof(char));
+ span.CopyTo(resultSpan);
+ resultSpan[resultSpan.Length - 1] = '\0';
+ }
+ else
+ {
+ Marshal.GetAnsiStringBytes(span, new Span<byte>((void*)ptr, byteLength));
+ }
+
+ IntPtr result = ptr;
+ ptr = IntPtr.Zero;
+ return result;
+ }
+ finally
+ {
+ // If we failed for any reason, free the new buffer
+ if (ptr != IntPtr.Zero)
+ {
+ new Span<byte>((void*)ptr, byteLength).Clear();
+
+ if (globalAlloc)
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ else
+ {
+ Marshal.FreeCoTaskMem(ptr);
+ }
+ }
+
+ ProtectMemory();
+ bufferToRelease?.DangerousRelease();
+ }
}
}
- private static void MarshalFree(IntPtr ptr, bool globalAlloc)
+ /// <summary>SafeBuffer for managing memory meant to be kept confidential.</summary>
+ private sealed class UnmanagedBuffer : SafeBuffer
{
- if (globalAlloc)
+ // A local copy of byte length to be able to access it in ReleaseHandle without the risk of throwing exceptions
+ private int _byteLength;
+
+ private UnmanagedBuffer() : base(true) { }
+
+ public static UnmanagedBuffer Allocate(int byteLength)
{
- Marshal.FreeHGlobal(ptr);
+ Debug.Assert(byteLength >= 0);
+ UnmanagedBuffer buffer = new UnmanagedBuffer();
+ buffer.SetHandle(Marshal.AllocHGlobal(byteLength));
+ buffer.Initialize((ulong)byteLength);
+ buffer._byteLength = byteLength;
+ return buffer;
}
- else
+
+ internal static unsafe void Copy(UnmanagedBuffer source, UnmanagedBuffer destination, ulong bytesLength)
+ {
+ if (bytesLength == 0)
+ {
+ return;
+ }
+
+ byte* srcPtr = null, dstPtr = null;
+ try
+ {
+ source.AcquirePointer(ref srcPtr);
+ destination.AcquirePointer(ref dstPtr);
+ Buffer.MemoryCopy(srcPtr, dstPtr, destination.ByteLength, bytesLength);
+ }
+ finally
+ {
+ if (dstPtr != null)
+ {
+ destination.ReleasePointer();
+ }
+ if (srcPtr != null)
+ {
+ source.ReleasePointer();
+ }
+ }
+ }
+
+ protected override unsafe bool ReleaseHandle()
{
- Marshal.FreeCoTaskMem(ptr);
+ new Span<byte>((void*)handle, _byteLength).Clear();
+ Marshal.FreeHGlobal(handle);
+ return true;
}
}
}
else
{
// Find bitflag offset of first match and add to current offset
- return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char)));
+ return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
}
}
// Find bitflag offset of first match and add to current offset,
// flags are in bytes so divide for chars
- return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char)));
+ return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
} while (lengthToExamine > 0);
}
{
// Find bitflag offset of first match and add to current offset,
// flags are in bytes so divide for chars
- return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char)));
+ return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
}
}
// Find bitflag offset of first match and add to current offset,
// flags are in bytes so divide for chars
- return (int)(offset + (BitOperations.TrailingZeroCount(matches) / sizeof(char)));
+ return (int)(offset + ((uint)BitOperations.TrailingZeroCount(matches) / sizeof(char)));
} while (lengthToExamine > 0);
}
return;
#if AMD64 || ARM64
- // The exact matrix on when RhZeroMemory is faster than InitBlockUnaligned is very complex. The factors to consider include
+ // The exact matrix on when ZeroMemory is faster than InitBlockUnaligned is very complex. The factors to consider include
// type of hardware and memory aligment. This threshold was chosen as a good balance accross different configurations.
if (byteLength > 768)
goto PInvoke;
#endif
PInvoke:
- RuntimeImports.RhZeroMemory(ref b, byteLength);
+ Buffer._ZeroMemory(ref b, byteLength);
}
public static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)