Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit d071e26

Browse files
TIHannosami
authored andcommitted
Fixed LOH byte array allocations in the IDE from metadata (dotnet#7971)
* Fixed LOH byte array allocations in the IDE. Unified memory handling for IL and FSharp metadata. * minor cleanup * minor cleanup * rename EmitBytes to EmitByteMemory * Fixed ByteArrayMemory * fixing build * Fixing build again * Better ReadInt32/ReadUInt16 * ILResourceLocation.Local should use a ReadOnlyByteMemory * Able to have a ReadOnlyStream * Using OutOfRangeException according to feedback
1 parent 9f26d11 commit d071e26

File tree

15 files changed

+449
-168
lines changed

15 files changed

+449
-168
lines changed

src/absil/bytes.fs

Lines changed: 296 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
/// Byte arrays
44
namespace FSharp.Compiler.AbstractIL.Internal
55

6+
open System
7+
open System.IO
8+
open System.IO.MemoryMappedFiles
9+
open System.Runtime.InteropServices
10+
open System.Runtime.CompilerServices
11+
open FSharp.NativeInterop
612

13+
#nowarn "9"
714

815
module internal Bytes =
916
let b0 n = (n &&& 0xFF)
@@ -26,10 +33,286 @@ module internal Bytes =
2633
Array.append (System.Text.Encoding.UTF8.GetBytes s) (ofInt32Array [| 0x0 |])
2734

2835
let stringAsUnicodeNullTerminated (s:string) =
29-
Array.append (System.Text.Encoding.Unicode.GetBytes s) (ofInt32Array [| 0x0;0x0 |])
36+
Array.append (System.Text.Encoding.Unicode.GetBytes s) (ofInt32Array [| 0x0;0x0 |])
37+
38+
[<AbstractClass>]
39+
type ByteMemory () =
40+
41+
abstract Item: int -> byte with get, set
42+
43+
abstract Length: int
44+
45+
abstract ReadBytes: pos: int * count: int -> byte[]
46+
47+
abstract ReadInt32: pos: int -> int
48+
49+
abstract ReadUInt16: pos: int -> uint16
50+
51+
abstract ReadUtf8String: pos: int * count: int -> string
52+
53+
abstract Slice: pos: int * count: int -> ByteMemory
54+
55+
abstract CopyTo: Stream -> unit
56+
57+
abstract Copy: srcOffset: int * dest: byte[] * destOffset: int * count: int -> unit
58+
59+
abstract ToArray: unit -> byte[]
60+
61+
abstract AsStream: unit -> Stream
62+
63+
abstract AsReadOnlyStream: unit -> Stream
64+
65+
[<Sealed>]
66+
type ByteArrayMemory(bytes: byte[], offset, length) =
67+
inherit ByteMemory()
68+
69+
do
70+
if length <= 0 || length > bytes.Length then
71+
raise (ArgumentOutOfRangeException("length"))
72+
73+
if offset < 0 || (offset + length) > bytes.Length then
74+
raise (ArgumentOutOfRangeException("offset"))
75+
76+
override _.Item
77+
with get i = bytes.[offset + i]
78+
and set i v = bytes.[offset + i] <- v
79+
80+
override _.Length = length
81+
82+
override _.ReadBytes(pos, count) =
83+
Array.sub bytes (offset + pos) count
84+
85+
override _.ReadInt32 pos =
86+
let finalOffset = offset + pos
87+
(uint32 bytes.[finalOffset]) |||
88+
((uint32 bytes.[finalOffset + 1]) <<< 8) |||
89+
((uint32 bytes.[finalOffset + 2]) <<< 16) |||
90+
((uint32 bytes.[finalOffset + 3]) <<< 24)
91+
|> int
92+
93+
override _.ReadUInt16 pos =
94+
let finalOffset = offset + pos
95+
(uint16 bytes.[finalOffset]) |||
96+
((uint16 bytes.[finalOffset + 1]) <<< 8)
97+
98+
override _.ReadUtf8String(pos, count) =
99+
System.Text.Encoding.UTF8.GetString(bytes, offset + pos, count)
100+
101+
override _.Slice(pos, count) =
102+
ByteArrayMemory(bytes, offset + pos, count) :> ByteMemory
103+
104+
override _.CopyTo stream =
105+
stream.Write(bytes, offset, length)
106+
107+
override _.Copy(srcOffset, dest, destOffset, count) =
108+
Array.blit bytes (offset + srcOffset) dest destOffset count
109+
110+
override _.ToArray() =
111+
Array.sub bytes offset length
112+
113+
override _.AsStream() =
114+
new MemoryStream(bytes, offset, length) :> Stream
115+
116+
override _.AsReadOnlyStream() =
117+
new MemoryStream(bytes, offset, length, false) :> Stream
118+
119+
[<Sealed>]
120+
type RawByteMemory(addr: nativeptr<byte>, length: int, hold: obj) =
121+
inherit ByteMemory ()
122+
123+
let check i =
124+
if i < 0 || i >= length then
125+
raise (ArgumentOutOfRangeException("i"))
126+
127+
do
128+
if length <= 0 then
129+
raise (ArgumentOutOfRangeException("length"))
130+
131+
override _.Item
132+
with get i =
133+
check i
134+
NativePtr.add addr i
135+
|> NativePtr.read
136+
and set i v =
137+
check i
138+
NativePtr.set addr i v
139+
140+
override _.Length = length
141+
142+
override _.ReadUtf8String(pos, count) =
143+
check pos
144+
check (pos + count - 1)
145+
System.Text.Encoding.UTF8.GetString(NativePtr.add addr pos, count)
146+
147+
override _.ReadBytes(pos, count) =
148+
check pos
149+
check (pos + count - 1)
150+
let res = Bytes.zeroCreate count
151+
Marshal.Copy(NativePtr.toNativeInt addr + nativeint pos, res, 0, count)
152+
res
153+
154+
override _.ReadInt32 pos =
155+
check pos
156+
check (pos + 3)
157+
Marshal.ReadInt32(NativePtr.toNativeInt addr + nativeint pos)
158+
159+
override _.ReadUInt16 pos =
160+
check pos
161+
check (pos + 1)
162+
uint16(Marshal.ReadInt16(NativePtr.toNativeInt addr + nativeint pos))
163+
164+
override _.Slice(pos, count) =
165+
check pos
166+
check (pos + count - 1)
167+
RawByteMemory(NativePtr.add addr pos, count, hold) :> ByteMemory
168+
169+
override x.CopyTo stream =
170+
use stream2 = x.AsStream()
171+
stream2.CopyTo stream
172+
173+
override x.Copy(srcOffset, dest, destOffset, count) =
174+
check srcOffset
175+
Marshal.Copy(NativePtr.toNativeInt addr + nativeint srcOffset, dest, destOffset, count)
176+
177+
override _.ToArray() =
178+
let res = Array.zeroCreate<byte> length
179+
Marshal.Copy(NativePtr.toNativeInt addr, res, 0, res.Length)
180+
res
181+
182+
override _.AsStream() =
183+
new UnmanagedMemoryStream(addr, int64 length) :> Stream
184+
185+
override _.AsReadOnlyStream() =
186+
new UnmanagedMemoryStream(addr, int64 length, int64 length, FileAccess.Read) :> Stream
187+
188+
[<Struct;NoEquality;NoComparison>]
189+
type ReadOnlyByteMemory(bytes: ByteMemory) =
190+
191+
member _.Item with [<MethodImpl(MethodImplOptions.AggressiveInlining)>] get i = bytes.[i]
192+
193+
member _.Length with [<MethodImpl(MethodImplOptions.AggressiveInlining)>] get () = bytes.Length
194+
195+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
196+
member _.ReadBytes(pos, count) = bytes.ReadBytes(pos, count)
197+
198+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
199+
member _.ReadInt32 pos = bytes.ReadInt32 pos
200+
201+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
202+
member _.ReadUInt16 pos = bytes.ReadUInt16 pos
203+
204+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
205+
member _.ReadUtf8String(pos, count) = bytes.ReadUtf8String(pos, count)
206+
207+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
208+
member _.Slice(pos, count) = bytes.Slice(pos, count) |> ReadOnlyByteMemory
209+
210+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
211+
member _.CopyTo stream = bytes.CopyTo stream
212+
213+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
214+
member _.Copy(srcOffset, dest, destOffset, count) = bytes.Copy(srcOffset, dest, destOffset, count)
215+
216+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
217+
member _.ToArray() = bytes.ToArray()
218+
219+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
220+
member _.AsStream() = bytes.AsReadOnlyStream()
221+
222+
type ByteMemory with
223+
224+
member x.AsReadOnly() = ReadOnlyByteMemory x
225+
226+
static member CreateMemoryMappedFile(bytes: ReadOnlyByteMemory) =
227+
let length = int64 bytes.Length
228+
let mmf =
229+
let mmf =
230+
MemoryMappedFile.CreateNew(
231+
null,
232+
length,
233+
MemoryMappedFileAccess.ReadWrite,
234+
MemoryMappedFileOptions.None,
235+
HandleInheritability.None)
236+
use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.ReadWrite)
237+
bytes.CopyTo stream
238+
mmf
239+
240+
let accessor = mmf.CreateViewAccessor(0L, length, MemoryMappedFileAccess.ReadWrite)
241+
242+
let safeHolder =
243+
{ new obj() with
244+
override x.Finalize() =
245+
(x :?> IDisposable).Dispose()
246+
interface IDisposable with
247+
member x.Dispose() =
248+
GC.SuppressFinalize x
249+
accessor.Dispose()
250+
mmf.Dispose() }
251+
RawByteMemory.FromUnsafePointer(accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), int length, safeHolder)
252+
253+
static member FromFile(path, access, ?canShadowCopy: bool) =
254+
let canShadowCopy = defaultArg canShadowCopy false
255+
256+
let memoryMappedFileAccess =
257+
match access with
258+
| FileAccess.Read -> MemoryMappedFileAccess.Read
259+
| FileAccess.Write -> MemoryMappedFileAccess.Write
260+
| _ -> MemoryMappedFileAccess.ReadWrite
261+
262+
let mmf, accessor, length =
263+
let fileStream = File.Open(path, FileMode.Open, access, FileShare.Read)
264+
let length = fileStream.Length
265+
let mmf =
266+
if canShadowCopy then
267+
let mmf =
268+
MemoryMappedFile.CreateNew(
269+
null,
270+
length,
271+
MemoryMappedFileAccess.ReadWrite,
272+
MemoryMappedFileOptions.None,
273+
HandleInheritability.None)
274+
use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.ReadWrite)
275+
fileStream.CopyTo(stream)
276+
fileStream.Dispose()
277+
mmf
278+
else
279+
MemoryMappedFile.CreateFromFile(
280+
fileStream,
281+
null,
282+
length,
283+
memoryMappedFileAccess,
284+
HandleInheritability.None,
285+
leaveOpen=false)
286+
mmf, mmf.CreateViewAccessor(0L, length, memoryMappedFileAccess), length
287+
288+
match access with
289+
| FileAccess.Read when not accessor.CanRead -> failwith "Cannot read file"
290+
| FileAccess.Write when not accessor.CanWrite -> failwith "Cannot write file"
291+
| _ when not accessor.CanRead || not accessor.CanWrite -> failwith "Cannot read or write file"
292+
| _ -> ()
293+
294+
let safeHolder =
295+
{ new obj() with
296+
override x.Finalize() =
297+
(x :?> IDisposable).Dispose()
298+
interface IDisposable with
299+
member x.Dispose() =
300+
GC.SuppressFinalize x
301+
accessor.Dispose()
302+
mmf.Dispose() }
303+
RawByteMemory.FromUnsafePointer(accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), int length, safeHolder)
304+
305+
static member FromUnsafePointer(addr, length, hold: obj) =
306+
RawByteMemory(NativePtr.ofNativeInt addr, length, hold) :> ByteMemory
307+
308+
static member FromArray(bytes, offset, length) =
309+
ByteArrayMemory(bytes, offset, length) :> ByteMemory
310+
311+
static member FromArray bytes =
312+
ByteArrayMemory.FromArray(bytes, 0, bytes.Length)
30313

31314
type internal ByteStream =
32-
{ bytes: byte[]
315+
{ bytes: ReadOnlyByteMemory
33316
mutable pos: int
34317
max: int }
35318
member b.ReadByte() =
@@ -38,18 +321,18 @@ type internal ByteStream =
38321
b.pos <- b.pos + 1
39322
res
40323
member b.ReadUtf8String n =
41-
let res = System.Text.Encoding.UTF8.GetString(b.bytes,b.pos,n)
324+
let res = b.bytes.ReadUtf8String(b.pos,n)
42325
b.pos <- b.pos + n; res
43326

44-
static member FromBytes (b:byte[],n,len) =
327+
static member FromBytes (b: ReadOnlyByteMemory,n,len) =
45328
if n < 0 || (n+len) > b.Length then failwith "FromBytes"
46329
{ bytes = b; pos = n; max = n+len }
47330

48331
member b.ReadBytes n =
49332
if b.pos + n > b.max then failwith "ReadBytes: end of stream"
50-
let res = Bytes.sub b.bytes b.pos n
333+
let res = b.bytes.Slice(b.pos, n)
51334
b.pos <- b.pos + n
52-
res
335+
res
53336

54337
member b.Position = b.pos
55338
#if LAZY_UNPICKLE
@@ -108,6 +391,13 @@ type internal ByteBuffer =
108391
Bytes.blit i 0 buf.bbArray buf.bbCurrent n
109392
buf.bbCurrent <- newSize
110393

394+
member buf.EmitByteMemory (i:ReadOnlyByteMemory) =
395+
let n = i.Length
396+
let newSize = buf.bbCurrent + n
397+
buf.Ensure newSize
398+
i.Copy(0, buf.bbArray, buf.bbCurrent, n)
399+
buf.bbCurrent <- newSize
400+
111401
member buf.EmitInt32AsUInt16 n =
112402
let newSize = buf.bbCurrent + 2
113403
buf.Ensure newSize

0 commit comments

Comments
 (0)