From 4b17e283cce40d86b1e2a928c8b11e609e56d9df Mon Sep 17 00:00:00 2001 From: reg Date: Fri, 12 Aug 2016 19:28:01 +0300 Subject: [PATCH] Native C/C++ struct feature #2 now is part of common developments of Conari: not so bad... so it makes sense to continue improve :) +IConfig.CacheDLR to cache dynamic types & BReader to get values from byte-sequence. also fixed Compiler Warning (level 1) C4190 --- Conari/Conari.csproj | 1 + Conari/ConariL.cs | 6 +- Conari/Core/Config.cs | 14 +- Conari/Core/IConfig.cs | 5 + Conari/Core/ProviderDLR.cs | 4 +- Conari/Native/Core/BReader.cs | 211 ++++++++++++++++++++++++++ Conari/Native/Core/BType.cs | 144 ++---------------- Conari/Native/NativeData.cs | 17 ++- ConariTest/BindingTest.cs | 76 ++++++++++ ConariTest/ConariTest.csproj | 1 + ConariTest/Native/Core/BReaderTest.cs | 61 ++++++++ UnLib/UnLibAPI.cpp | 21 ++- UnLib/UnLibAPI.h | 6 +- 13 files changed, 425 insertions(+), 142 deletions(-) create mode 100644 Conari/Native/Core/BReader.cs create mode 100644 ConariTest/Native/Core/BReaderTest.cs diff --git a/Conari/Conari.csproj b/Conari/Conari.csproj index 6845063..b07bf2d 100644 --- a/Conari/Conari.csproj +++ b/Conari/Conari.csproj @@ -65,6 +65,7 @@ + diff --git a/Conari/ConariL.cs b/Conari/ConariL.cs index 9ffa512..a0dc461 100644 --- a/Conari/ConariL.cs +++ b/Conari/ConariL.cs @@ -50,8 +50,6 @@ public dynamic DLR /// Optional prefix to use via `bind<>` public ConariL(IConfig cfg, CallingConvention conv, string prefix = null) { - DLR = new ProviderDLR(this); - Prefix = prefix; Convention = conv; init(cfg); @@ -97,6 +95,10 @@ protected void init(IConfig cfg) throw new ArgumentException("Configuration cannot be null."); } + DLR = new ProviderDLR(this) { + Cache = cfg.CacheDLR + }; + if(cfg.LazyLoading) { Library = new Link(cfg.LibName); } diff --git a/Conari/Core/Config.cs b/Conari/Core/Config.cs index 92871ef..2926a62 100644 --- a/Conari/Core/Config.cs +++ b/Conari/Core/Config.cs @@ -56,6 +56,15 @@ public bool LazyLoading set; } + /// + /// To cache dynamic types. + /// + public bool CacheDLR + { + get; + set; + } + public static explicit operator String(Config cfg) { return cfg.LibName; @@ -63,14 +72,15 @@ public static explicit operator String(Config cfg) public static explicit operator Config(String lib) { - return new Config() { LibName = lib }; + return new Config(lib); } /// The library. public Config(string lib) : this() { - LibName = lib; + LibName = lib; + CacheDLR = true; } } } diff --git a/Conari/Core/IConfig.cs b/Conari/Core/IConfig.cs index 37b188a..cd0b39e 100644 --- a/Conari/Core/IConfig.cs +++ b/Conari/Core/IConfig.cs @@ -40,5 +40,10 @@ public interface IConfig /// To load library only when it required. /// bool LazyLoading { get; set; } + + /// + /// To cache dynamic types. + /// + bool CacheDLR { get; set; } } } diff --git a/Conari/Core/ProviderDLR.cs b/Conari/Core/ProviderDLR.cs index 21e4bfb..56b0dbf 100644 --- a/Conari/Core/ProviderDLR.cs +++ b/Conari/Core/ProviderDLR.cs @@ -55,10 +55,10 @@ public bool Cache /// /// Magic methods. Invoking. - /// < + /// ([{argument_types}])` /// ]]> - /// /// /// /// diff --git a/Conari/Native/Core/BReader.cs b/Conari/Native/Core/BReader.cs new file mode 100644 index 0000000..07a4839 --- /dev/null +++ b/Conari/Native/Core/BReader.cs @@ -0,0 +1,211 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Denis Kuzmin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +using System; + +namespace net.r_eg.Conari.Native.Core +{ + public sealed class BReader + { + private byte[] data; + private int offset; + + /// + /// To get manually the value from byte-sequence. + /// + /// The type of result value. + /// Actual size of value in byte-sequence. + /// Offset at left. + /// + /// + /// + public static dynamic GetValue(Type type, int tsize, ref int offset, ref byte[] data) + { + // the System.IO.BinaryReader as variant, but we will use own implementation ... + + byte[] val = range(ref data, offset, tsize); + offset += tsize; + + if(type == typeof(Byte)) { + return (Byte)val[0]; + } + + if(type == typeof(SByte)) { + return (SByte)val[0]; + } + + if(type == typeof(Int16)) { + return BitConverter.ToInt16(val, 0); + } + + if(type == typeof(UInt16)) { + return BitConverter.ToUInt16(val, 0); + } + + if(type == typeof(Int32)) { + return BitConverter.ToInt32(val, 0); + } + + if(type == typeof(UInt32)) { + return BitConverter.ToUInt32(val, 0); + } + + if(type == typeof(Int64)) { + return BitConverter.ToInt64(val, 0); + } + + if(type == typeof(UInt64)) { + return BitConverter.ToUInt64(val, 0); + } + + if(type == typeof(IntPtr)) + { + if(IntPtr.Size == sizeof(Int64)) { + return BitConverter.ToInt64(val, 0); + } + return BitConverter.ToInt32(val, 0); + } + + if(type == typeof(UIntPtr)) + { + if(UIntPtr.Size == sizeof(UInt64)) { + return BitConverter.ToUInt64(val, 0); + } + return BitConverter.ToUInt32(val, 0); + } + + if(type == typeof(Single)) { + return BitConverter.ToSingle(val, 0); + } + + if(type == typeof(Double)) { + return BitConverter.ToDouble(val, 0); + } + + if(type == typeof(Boolean)) { + return BitConverter.ToBoolean(val, 0); + } + + if(type == typeof(Char)) { + return BitConverter.ToChar(val, 0); + } + + if(type == typeof(String)) + { + //return new Types.CharPtr((IntPtr)field.value); + + if(IntPtr.Size == sizeof(Int64)) { + return BitConverter.ToInt64(val, 0); + } + return BitConverter.ToInt32(val, 0); + } + + throw new NotSupportedException($"The type `{type}` is not supported."); + } + + /// + /// Try to get the next value in byte-sequence. + /// + /// The type of value. + /// Actual size of value. + /// + public bool tryNext(Type type, int tsize, out dynamic value) + { + if(offset + tsize > data.Length) { + value = null; + return false; + } + + value = GetValue(type, tsize, ref offset, ref data); + return true; + } + + /// + /// Gets next value in byte-sequence. + /// + /// + /// + /// + /// + public dynamic next(Type type, int tsize) + { + dynamic value; + if(tryNext(type, tsize, out value)) { + return value; + } + + throw new IndexOutOfRangeException( + $"The type `{type}` with selected size '{tsize}' cannot be read from byte-sequence. Offset: {offset}" + ); + } + + /// + /// Alias to `next(Type type, int tsize)` + /// + /// + /// + /// + public dynamic next(int tsize) + { + return next(typeof(T), tsize); + } + + ///// + ///// Gets next value in byte-sequence. + ///// + ///// The type of value. + ///// + //public dynamic next(Type type) + //{ + // return next(type, NativeData.SizeOf(type)); + //} + + /// + /// To reset position. + /// + /// + public void reset(int offset = 0) + { + this.offset = offset; + } + + public BReader(byte[] raw, int offset = 0) + { + data = raw; + this.offset = offset; + } + + private static byte[] range(ref byte[] data, int offset, int len) + { + int idx = 0; + byte[] ret = new byte[Math.Max(0, len)]; + + len = offset + len; + while(offset < len) { + ret[idx++] = data[offset++]; + } + return ret; + } + } +} diff --git a/Conari/Native/Core/BType.cs b/Conari/Native/Core/BType.cs index b8b4c51..b555809 100644 --- a/Conari/Native/Core/BType.cs +++ b/Conari/Native/Core/BType.cs @@ -112,130 +112,6 @@ public BType(byte[] data, TFields fields) Fields = assign(fields, data); } - protected TFields assign(TFields fields, byte[] data) - { - var ret = new TFields(); - - if(fields == null || fields.Count < 1) { - return fields; - } - - int offset = 0; - foreach(var f in fields) - { - var val = new Field(f); - setValue(val, ref offset, ref data); - ret.Add(val); - } - - return ret; - } - - protected void setValue(Field field, ref int offset, ref byte[] data) - { - // the BinaryReader as variant... - - byte[] val = range(ref data, offset, field.tsize); - offset += field.tsize; - - if(field.type == typeof(Byte)) { - field.value = (Byte)val[0]; - return; - } - - if(field.type == typeof(SByte)) { - field.value = (SByte)val[0]; - return; - } - - if(field.type == typeof(Int16)) { - field.value = BitConverter.ToInt16(val, 0); - return; - } - - if(field.type == typeof(UInt16)) { - field.value = BitConverter.ToUInt16(val, 0); - return; - } - - if(field.type == typeof(Int32)) { - field.value = BitConverter.ToInt32(val, 0); - return; - } - - if(field.type == typeof(UInt32)) { - field.value = BitConverter.ToUInt32(val, 0); - return; - } - - if(field.type == typeof(Int64)) { - field.value = BitConverter.ToInt64(val, 0); - return; - } - - if(field.type == typeof(UInt64)) { - field.value = BitConverter.ToUInt64(val, 0); - return; - } - - if(field.type == typeof(IntPtr)) - { - if(IntPtr.Size == sizeof(Int64)) { - field.value = BitConverter.ToInt64(val, 0); - } - else { - field.value = BitConverter.ToInt32(val, 0); - } - - return; - } - - if(field.type == typeof(UIntPtr)) - { - if(UIntPtr.Size == sizeof(UInt64)) { - field.value = BitConverter.ToUInt64(val, 0); - } - else { - field.value = BitConverter.ToUInt32(val, 0); - } - - return; - } - - if(field.type == typeof(Single)) { - field.value = BitConverter.ToSingle(val, 0); - return; - } - - if(field.type == typeof(Double)) { - field.value = BitConverter.ToDouble(val, 0); - return; - } - - if(field.type == typeof(Boolean)) { - field.value = BitConverter.ToBoolean(val, 0); - return; - } - - if(field.type == typeof(Char)) { - field.value = BitConverter.ToChar(val, 0); - return; - } - - if(field.type == typeof(String)) - { - if(IntPtr.Size == sizeof(Int64)) { - field.value = BitConverter.ToInt64(val, 0); - } - else { - field.value = BitConverter.ToInt32(val, 0); - } - - //field.value = new Types.CharPtr((IntPtr)field.value); - return; - } - } - protected virtual bool isNoname(string name) { // (name.IndexOf('<') != -1) @@ -254,15 +130,23 @@ protected virtual string getNonameIndex(string name) return name.Substring(left, len); } - private byte[] range(ref byte[] data, int offset, int len) + protected TFields assign(TFields fields, byte[] data) { - int idx = 0; - byte[] ret = new byte[Math.Max(0, len)]; + var ret = new TFields(); - len = offset + len; - while(offset < len) { - ret[idx++] = data[offset++]; + if(fields == null || fields.Count < 1) { + return fields; } + + BReader br = new BReader(data, 0); + + foreach(var f in fields) + { + var val = new Field(f); + val.value = br.next(val.type, val.tsize); + ret.Add(val); + } + return ret; } } diff --git a/Conari/Native/NativeData.cs b/Conari/Native/NativeData.cs index 90b67cf..c59a2d9 100644 --- a/Conari/Native/NativeData.cs +++ b/Conari/Native/NativeData.cs @@ -73,7 +73,7 @@ public static int SizeOf(params Type[] types) } return sum; } - + /// /// Gets size of selected type in bytes that's should be considered as unmanaged type. /// @@ -88,6 +88,16 @@ public static int SizeOf(Type type) return Marshal.SizeOf(type); } + /// + /// Alias to `int SizeOf(Type type)` + /// + /// + /// + public static int SizeOf() + { + return SizeOf(typeof(T)); + } + /// /// Align size by specific type. /// @@ -230,6 +240,11 @@ protected int track(string[] names, params Type[] types) protected virtual int track(string name, Type type) { int size = SizeOf(type); + + if(type == typeof(IntPtr)) { // retype to use the fixed size + type = (size == sizeof(Int64)) ? typeof(Int64) : typeof(Int32); + } + map.Add(new Field(type, size) { name = fieldName(name) }); return size; diff --git a/ConariTest/BindingTest.cs b/ConariTest/BindingTest.cs index 622f664..7d219c7 100644 --- a/ConariTest/BindingTest.cs +++ b/ConariTest/BindingTest.cs @@ -3,6 +3,8 @@ using net.r_eg.Conari; using net.r_eg.Conari.Core; using net.r_eg.Conari.Exceptions; +using net.r_eg.Conari.Native; +using net.r_eg.Conari.Native.Core; using net.r_eg.Conari.Types; namespace net.r_eg.ConariTest @@ -259,5 +261,79 @@ public void echoTest5() } } } + + [TestMethod] + public void complexTest1() + { + using(var l = new ConariL(UNLIB_DLL)) + { + IntPtr ptr1 = l.DLR.get_TSpec(); + IntPtr ptr2 = l.bind>("get_TSpec")(); + + var dyn = l.bind(Dynamic.GetMethodInfo(typeof(IntPtr)), "get_TSpec"); + IntPtr ptr3 = (IntPtr)dyn.dynamic.Invoke(null, new object[0]); + + Assert.AreNotEqual(IntPtr.Zero, ptr1); + Assert.IsTrue(ptr1 == ptr2 && ptr2 == ptr3); + + /* + struct TSpec + { + BYTE a; + int b; + char* name; + }; + + s->a = 2; + s->b = 4; + s->name = "Conari"; + + */ + var TSpecPtr = NativeData + ._(ptr1) + .align(3, "a", "b", "name"); + + byte[] bytes = TSpecPtr.Raw.Values; + dynamic dlr = TSpecPtr.Raw.Type; + var fields = TSpecPtr.Raw.Type.Fields; + + Assert.AreEqual(3, fields.Count); + + int expA = 2; + int expB = 4; + string expName = "Conari"; + + Type fxIntPtr = (IntPtr.Size == sizeof(Int64)) ? typeof(Int64) : typeof(Int32); + + // a + Assert.AreEqual("a", fields[0].name); + Assert.AreEqual(NativeData.SizeOf(fxIntPtr), fields[0].tsize); + Assert.AreEqual(fxIntPtr, fields[0].type); + Assert.AreEqual(expA, fields[0].value); + + // b + Assert.AreEqual("b", fields[1].name); + Assert.AreEqual(NativeData.SizeOf(fxIntPtr), fields[1].tsize); + Assert.AreEqual(fxIntPtr, fields[1].type); + Assert.AreEqual(expB, fields[1].value); + + // name + Assert.AreEqual("name", fields[2].name); + Assert.AreEqual(IntPtr.Size, fields[2].tsize); + Assert.AreEqual(fxIntPtr, fields[2].type); + Assert.AreEqual(expName, (CharPtr)fields[2].value); + + // DLR + Assert.AreEqual(expA, dlr.a); + Assert.AreEqual(expB, dlr.b); + Assert.AreEqual(expName, (CharPtr)dlr.name); + + // byte-seq + var br = new BReader(bytes); + Assert.AreEqual(expA, br.next(fxIntPtr, NativeData.SizeOf(fxIntPtr))); + Assert.AreEqual(expB, br.next(fxIntPtr, NativeData.SizeOf(fxIntPtr))); + Assert.AreEqual(expName, (CharPtr)br.next(fxIntPtr, NativeData.SizeOf(fxIntPtr))); + } + } } } diff --git a/ConariTest/ConariTest.csproj b/ConariTest/ConariTest.csproj index 7fea869..edc0569 100644 --- a/ConariTest/ConariTest.csproj +++ b/ConariTest/ConariTest.csproj @@ -61,6 +61,7 @@ + diff --git a/ConariTest/Native/Core/BReaderTest.cs b/ConariTest/Native/Core/BReaderTest.cs new file mode 100644 index 0000000..4ed4c92 --- /dev/null +++ b/ConariTest/Native/Core/BReaderTest.cs @@ -0,0 +1,61 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using net.r_eg.Conari.Native.Core; + +namespace net.r_eg.ConariTest.Native.Core +{ + [TestClass] + public class BReaderTest + { + [TestMethod] + public void nextValTest1() + { + var br = new BReader(new byte[] { + 5, 0, 0, 0, // Int32 = 5 + 7, 0, 0, 0, // Int32 = 7 + 31 // SByte = 31 + }); + + Assert.AreEqual(5, br.next(typeof(Int32), 4)); + Assert.AreEqual(7, br.next(typeof(Int32), 4)); + Assert.AreEqual(31, br.next(typeof(sbyte), 1)); + } + + [TestMethod] + [ExpectedException(typeof(IndexOutOfRangeException))] + public void nextValTest2() + { + var br = new BReader(new byte[] { + 5, 0, 0, 0, // Int32 = 5 + 31 // SByte = 31 + }); + + br.next(typeof(Int32), 4); + br.next(typeof(Int32), 4); + } + + [TestMethod] + public void nextValTest3() + { + var br = new BReader(new byte[] { + 5, 0, 0, 0, // Int32 = 5 + }); + + Assert.AreEqual(5, br.next(typeof(Int32), 4)); + br.reset(); + Assert.AreEqual(5, br.next(typeof(Int32), 4)); + + dynamic value; + Assert.AreEqual(false, br.tryNext(typeof(Int32), 4, out value)); + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void GetValTest1() + { + int offset = 0; + byte[] data = new byte[] { 1, 2, 3, 4 }; + BReader.GetValue(typeof(UserSpecUintType), 1, ref offset, ref data); + } + } +} diff --git a/UnLib/UnLibAPI.cpp b/UnLib/UnLibAPI.cpp index b80c763..c9a5d21 100644 --- a/UnLib/UnLibAPI.cpp +++ b/UnLib/UnLibAPI.cpp @@ -83,12 +83,12 @@ namespace NS_UNLIB_API_ return bstr; } - LIBAPI std::string get_StringVal(std::string str) + LIBAPI_CPP std::string get_StringVal(std::string str) { return str; } - LIBAPI std::wstring get_WStringVal(std::wstring wstr) + LIBAPI_CPP std::wstring get_WStringVal(std::wstring wstr) { return wstr; } @@ -96,6 +96,23 @@ namespace NS_UNLIB_API_ /* complex */ + std::shared_ptr g_sharedTSpec; // to check DLR, delegates and MI + LIBAPI TSpec* get_TSpec() + { + if (g_sharedTSpec != nullptr) { + return g_sharedTSpec.get(); + } + + auto s = std::make_shared(); + + s->a = 2; + s->b = 4; + s->name = "Conari"; + + g_sharedTSpec = s; + + return s.get(); + } } _NS_UNLIB_API \ No newline at end of file diff --git a/UnLib/UnLibAPI.h b/UnLib/UnLibAPI.h index 3f182e7..3556f0f 100644 --- a/UnLib/UnLibAPI.h +++ b/UnLib/UnLibAPI.h @@ -29,12 +29,12 @@ namespace NS_UNLIB_API_ LIBAPI const wchar_t* get_WCharPtrVal(const wchar_t* wstr); LIBAPI const BSTR* get_BSTRVal(const BSTR* bstr); - LIBAPI std::string get_StringVal(std::string str); - LIBAPI std::wstring get_WStringVal(std::wstring wstr); + LIBAPI_CPP std::string get_StringVal(std::string str); + LIBAPI_CPP std::wstring get_WStringVal(std::wstring wstr); /* complex */ - //LIBAPI TSpec* get_TSpec(); + LIBAPI TSpec* get_TSpec(); //LIBAPI TSpec* get_TSpec(BYTE a, int b, const char* name); //LIBAPI TSpecB* get_TSpecB_A_ptr();