Merge pull request #1436 from ousttrue/fix/safe_marshal_copy

Marshal.Copy で問題が発生する前に throw する
This commit is contained in:
ousttrue 2021-12-22 16:46:53 +09:00 committed by GitHub
commit 4b61b3e5e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 113 additions and 69 deletions

View File

@ -89,15 +89,6 @@ namespace UniGLTF
public static class ArrayExtensions
{
public static int MarshalCopyTo<T>(this ArraySegment<byte> src, T[] dst) where T : struct
{
var size = dst.Length * Marshal.SizeOf(typeof(T));
using (var pin = Pin.Create(dst))
{
Marshal.Copy(src.Array, src.Offset, pin.Ptr, size);
}
return size;
}
public static T[] SelectInplace<T>(this T[] src, Func<T, T> pred)
{
@ -107,21 +98,6 @@ namespace UniGLTF
}
return src;
}
public static void Copy<TFrom, TTo>(ArraySegment<TFrom> src, ArraySegment<TTo> dst)
where TFrom : struct
where TTo : struct
{
var bytes = new byte[src.Count * Marshal.SizeOf(typeof(TFrom))];
using (var pin = Pin.Create(src))
{
Marshal.Copy(pin.Ptr, bytes, 0, bytes.Length);
};
using (var pin = Pin.Create(dst))
{
Marshal.Copy(bytes, 0, pin.Ptr, bytes.Length);
};
}
}
public static class ListExtensions
@ -134,7 +110,7 @@ namespace UniGLTF
}
public static class ArraySegmentExtensions
{
{
public static ArraySegment<T> Slice<T>(this ArraySegment<T> self, int start, int length)
{
if (start + length > self.Count)
@ -156,6 +132,6 @@ namespace UniGLTF
}
return self.Slice(start, self.Count - start);
}
}
}

View File

@ -89,30 +89,14 @@ namespace UniGLTF
{
public static int FromBytes<T>(this ArraySegment<byte> src, T[] dst) where T : struct
{
var dstSize = dst.Length * Marshal.SizeOf(typeof(T));
if (src.Count > dstSize)
{
throw new ArgumentOutOfRangeException();
}
using (var pin = ArrayPin.Create(dst))
{
Marshal.Copy(src.Array, src.Offset, pin.Ptr, src.Count);
}
SafeMarshalCopy.CopyBytesToArray(src, dst);
return src.Count;
}
public static int ToBytes<T>(this T[] src, ArraySegment<byte> dst) where T : struct
{
var srcSize = src.Length * Marshal.SizeOf(typeof(T));
if (srcSize > dst.Count)
{
throw new ArgumentOutOfRangeException();
}
using (var pin = ArrayPin.Create(src))
{
Marshal.Copy(pin.Ptr, dst.Array, dst.Offset, srcSize);
}
return srcSize;
SafeMarshalCopy.CopyArrayToToBytes(src, dst);
return dst.Count;
}
}
}

View File

@ -10,7 +10,7 @@ namespace UniGLTF
Byte[] m_bytes;
int m_pos;
public BytesReader(Byte[] bytes, int pos=0)
public BytesReader(Byte[] bytes, int pos = 0)
{
m_bytes = bytes;
m_pos = pos;
@ -63,8 +63,9 @@ namespace UniGLTF
public void ReadToArray<T>(T[] dst) where T : struct
{
var size = new ArraySegment<Byte>(m_bytes, m_pos, m_bytes.Length - m_pos).MarshalCopyTo(dst);
m_pos += size;
var bytes = new ArraySegment<Byte>(m_bytes, m_pos, m_bytes.Length - m_pos);
SafeMarshalCopy.CopyBytesToArray(bytes, dst);
m_pos += bytes.Count;
}
public T ReadStruct<T>() where T : struct

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace UniGLTF
{
@ -156,8 +157,8 @@ namespace UniGLTF
{
var segment = GetBytesFromBuffer(view.buffer);
var attrib = new T[count];
var bytes = new ArraySegment<Byte>(segment.Array, segment.Offset + view.byteOffset + byteOffset, count * view.byteStride);
bytes.MarshalCopyTo(attrib);
var bytes = new ArraySegment<Byte>(segment.Array, segment.Offset + view.byteOffset + byteOffset, count * Marshal.SizeOf<T>());
SafeMarshalCopy.CopyBytesToArray(bytes, attrib);
return attrib;
}
@ -288,8 +289,8 @@ namespace UniGLTF
var attrib = new float[vertexAccessor.count * vertexAccessor.TypeCount];
var view = GLTF.bufferViews[vertexAccessor.bufferView];
var segment = GetBytesFromBuffer(view.buffer);
var bytes = new ArraySegment<Byte>(segment.Array, segment.Offset + view.byteOffset + vertexAccessor.byteOffset, vertexAccessor.count * view.byteStride);
bytes.MarshalCopyTo(attrib);
var bytes = new ArraySegment<Byte>(segment.Array, segment.Offset + view.byteOffset + vertexAccessor.byteOffset, vertexAccessor.count * 4 * vertexAccessor.TypeCount);
SafeMarshalCopy.CopyBytesToArray(bytes, attrib);
result = attrib;
}
else

View File

@ -0,0 +1,77 @@
using System;
using System.Runtime.InteropServices;
namespace UniGLTF
{
/// <summary>
/// Marshal.Copy
/// * ptr to bytes
/// * bytes to ptr
/// の両方向がある。
/// ptr になったら範囲は分からん。
/// ptr にする前に範囲チェックするのを明確にするのがこの Utility の意図である。
///
/// Marshal.Copy を使わずにこの関数を使うべし
///
/// </summary>
public static class SafeMarshalCopy
{
/// <summary>
/// bytes から T[] へのコピー
/// </summary>
public static void CopyBytesToArray<T>(ArraySegment<byte> src, T[] dst) where T : struct
{
if (src.Array == null || dst == null || src.Count == 0)
{
throw new System.ArgumentNullException();
}
if (src.Offset < 0)
{
throw new System.AccessViolationException("CopyBytesToArray: ArraySegment: negative offset");
}
if ((src.Offset + src.Count) > src.Array.Length)
{
throw new System.AccessViolationException("CopyBytesToArray: ArraySegment: exceed");
}
var dstByteSize = dst.Length * Marshal.SizeOf(typeof(T));
if (src.Count > dstByteSize)
{
throw new System.AccessViolationException("CopyBytesToArray: src > dst");
}
using (var pin = Pin.Create(dst))
{
Marshal.Copy(src.Array, src.Offset, pin.Ptr, src.Count);
}
}
/// <summary>
/// T[] から bytes へのコピー
/// </summary>
public static void CopyArrayToToBytes<T>(T[] src, ArraySegment<byte> dst) where T : struct
{
if (dst.Array == null || src == null || dst.Count == 0)
{
throw new System.ArgumentNullException();
}
if (dst.Offset < 0)
{
throw new System.AccessViolationException("CopyArrayToToBytes: ArraySegment: negative offset");
}
if (dst.Offset + dst.Count > dst.Array.Length)
{
throw new System.AccessViolationException("CopyArrayToToBytes: ArraySegment: exceed");
}
var srcByteSize = src.Length * Marshal.SizeOf(typeof(T));
if (srcByteSize > dst.Count)
{
throw new System.AccessViolationException("CopyArrayToToBytes: src > dst");
}
using (var pin = Pin.Create(src))
{
Marshal.Copy(pin.Ptr, dst.Array, dst.Offset, srcByteSize);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 234ebccfeacecfe4da0351a092c985af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -32,8 +32,8 @@ TextureImporter:
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: -1
mipBias: -100
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0

View File

@ -32,8 +32,8 @@ TextureImporter:
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: -1
mipBias: -100
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0

View File

@ -32,11 +32,11 @@ TextureImporter:
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50

View File

@ -121,10 +121,7 @@ namespace UniVRM10
u.m33 = 15;
Assert.AreEqual(new UnityEngine.Vector4(0, 1, 2, 3), u.GetRow(0));
var bytes = new Byte[64];
using (var pin = Pin.Create(new[] { u }))
{
Marshal.Copy(pin.Ptr, bytes, 0, 64);
}
SafeMarshalCopy.CopyArrayToToBytes(new[] { u }, new ArraySegment<byte>(bytes));
Assert.AreEqual(1.0f, BitConverter.ToSingle(bytes, 16));
}
@ -149,10 +146,7 @@ namespace UniVRM10
u.M43 = 14;
u.M44 = 15;
var bytes = new Byte[64];
using (var pin = Pin.Create(new[] { u }))
{
Marshal.Copy(pin.Ptr, bytes, 0, 64);
}
SafeMarshalCopy.CopyArrayToToBytes(new[] { u }, new ArraySegment<byte>(bytes));
Assert.AreEqual(1.0f, BitConverter.ToSingle(bytes, 4));
}