-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Don't fail in case of const field in Collection source and extended support for type conversion #555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't fail in case of const field in Collection source and extended support for type conversion #555
Changes from 5 commits
e8ea5c4
581cb42
62091c9
ef950eb
49323f2
d556d13
a7bb6e1
d5143e6
5b7dc4a
5f69a4c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -119,7 +119,7 @@ private Delegate CreateGetter(int index) | |
|
|
||
| var column = DataView._schema.SchemaDefn.Columns[index]; | ||
| var outputType = column.IsComputed ? column.ReturnType : column.FieldInfo.FieldType; | ||
|
|
||
| var genericType = outputType; | ||
| Func<int, Delegate> del; | ||
|
|
||
| if (outputType.IsArray) | ||
|
|
@@ -129,112 +129,192 @@ private Delegate CreateGetter(int index) | |
| if (outputType.GetElementType() == typeof(string)) | ||
| { | ||
| Ch.Assert(colType.ItemType.IsText); | ||
| return CreateStringArrayToVBufferGetter(index); | ||
| return CreateArrayGetterDelegate<String, DvText>(index, x => new DvText(x)); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(int)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I4); | ||
| return CreateArrayGetterDelegate<int, DvInt4>(index, x => x); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(int?)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I4); | ||
| return CreateArrayGetterDelegate<int?, DvInt4>(index, x => x ?? DvInt4.NA); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(long)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I8); | ||
| return CreateArrayGetterDelegate<long, DvInt8>(index, x => x); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(long?)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I8); | ||
| return CreateArrayGetterDelegate<long?, DvInt8>(index, x => x ?? DvInt8.NA); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(short)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I2); | ||
| return CreateArrayGetterDelegate<short, DvInt2>(index, x => x); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(short?)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I2); | ||
| return CreateArrayGetterDelegate<short?, DvInt2>(index, x => x ?? DvInt2.NA); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(sbyte)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I1); | ||
| return CreateArrayGetterDelegate<sbyte, DvInt1>(index, x => x); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(sbyte?)) | ||
| { | ||
| Ch.Assert(colType.ItemType == NumberType.I1); | ||
| return CreateArrayGetterDelegate<sbyte?, DvInt1>(index, x => x ?? DvInt1.NA); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(bool)) | ||
| { | ||
| Ch.Assert(colType.ItemType.IsBool); | ||
| return CreateArrayGetterDelegate<bool, DvBool>(index, x => x); | ||
| } | ||
| else if (outputType.GetElementType() == typeof(bool?)) | ||
| { | ||
| Ch.Assert(colType.ItemType.IsBool); | ||
| return CreateArrayGetterDelegate<bool?, DvBool>(index, x => x ?? DvBool.NA); | ||
| } | ||
|
|
||
| // T[] -> VBuffer<T> | ||
| Ch.Assert(outputType.GetElementType() == colType.ItemType.RawType); | ||
| del = CreateArrayToVBufferGetter<int>; | ||
| if (outputType.GetElementType().IsGenericType && outputType.GetElementType().GetGenericTypeDefinition() == typeof(Nullable<>)) | ||
| Ch.Assert(Nullable.GetUnderlyingType(outputType.GetElementType()) == colType.ItemType.RawType); | ||
| else | ||
| Ch.Assert(outputType.GetElementType() == colType.ItemType.RawType); | ||
| del = CreateDirectArrayGetterDelegate<int>; | ||
| genericType = outputType.GetElementType(); | ||
| } | ||
| else if (colType.IsVector) | ||
| { | ||
| // VBuffer<T> -> VBuffer<T> | ||
| // REVIEW: Do we care about accomodating VBuffer<string> -> VBuffer<DvText>? | ||
| // REVIEW: why it's int and not long? | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is a strange place to add this comment. |
||
| Ch.Assert(outputType.IsGenericType); | ||
| Ch.Assert(outputType.GetGenericTypeDefinition() == typeof(VBuffer<>)); | ||
| Ch.Assert(outputType.GetGenericArguments()[0] == colType.ItemType.RawType); | ||
| del = CreateVBufferToVBufferDelegate<int>; | ||
| del = CreateDirectVBufferGetterDelegate<int>; | ||
| } | ||
| else if (colType.IsPrimitive) | ||
| { | ||
| if (outputType == typeof(string)) | ||
| { | ||
| // String -> DvText | ||
| Ch.Assert(colType.IsText); | ||
| return CreateStringToTextGetter(index); | ||
| return CreateGetterDelegate<String, DvText>(index, (x) => x == null ? DvText.NA : new DvText(x)); | ||
| } | ||
| else if (outputType == typeof(bool)) | ||
| { | ||
| // Bool -> DvBool | ||
| Ch.Assert(colType.IsBool); | ||
| return CreateBooleanToDvBoolGetter(index); | ||
| return CreateGetterDelegate<bool, DvBool>(index, x => x); | ||
| } | ||
| else if (outputType == typeof(bool?)) | ||
| { | ||
| // Bool? -> DvBool | ||
| Ch.Assert(colType.IsBool); | ||
| return CreateNullableBooleanToDvBoolGetter(index); | ||
| return CreateGetterDelegate<bool?, DvBool>(index, x => x ?? DvBool.NA); | ||
| } | ||
| else if (outputType == typeof(int)) | ||
| { | ||
| // int -> DvInt4 | ||
| Ch.Assert(colType == NumberType.I4); | ||
| return CreateGetterDelegate<int, DvInt4>(index, x => x); | ||
| } | ||
| else if (outputType == typeof(int?)) | ||
| { | ||
| // int -> DvInt4 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
int? #Closed |
||
| Ch.Assert(colType == NumberType.I4); | ||
| return CreateGetterDelegate<int?, DvInt4>(index, x => x ?? DvInt4.NA); | ||
| } | ||
| else if (outputType == typeof(short)) | ||
| { | ||
| // short -> DvInt2 | ||
| Ch.Assert(colType == NumberType.I2); | ||
| return CreateGetterDelegate<short, DvInt2>(index, x => x); | ||
| } | ||
| else if (outputType == typeof(short?)) | ||
| { | ||
| // short? -> DvInt2 | ||
| Ch.Assert(colType == NumberType.I2); | ||
| return CreateGetterDelegate<short?, DvInt2>(index, x => x ?? DvInt2.NA); | ||
| } | ||
| else if (outputType == typeof(long)) | ||
| { | ||
| // long -> DvInt8 | ||
| Ch.Assert(colType == NumberType.I8); | ||
| return CreateGetterDelegate<long, DvInt8>(index, x => x); | ||
| } | ||
| else if (outputType == typeof(long?)) | ||
| { | ||
| // long? -> DvInt8 | ||
| Ch.Assert(colType == NumberType.I8); | ||
| return CreateGetterDelegate<long?, DvInt8>(index, (x) => x.HasValue ? x.Value : DvInt8.NA); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
why not |
||
| } | ||
| else if (outputType == typeof(sbyte)) | ||
| { | ||
| // sbyte -> DvInt1 | ||
| Ch.Assert(colType == NumberType.I1); | ||
| return CreateGetterDelegate<sbyte, DvInt1>(index, (x) => (DvInt1)x); | ||
| } | ||
| else if (outputType == typeof(sbyte?)) | ||
| { | ||
| // sbyte -> DvInt1 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
sbyte? #Closed |
||
| Ch.Assert(colType == NumberType.I1); | ||
| return CreateGetterDelegate<sbyte?, DvInt1>(index, (x) => x.HasValue ? x.Value : DvInt1.NA); | ||
| } | ||
|
|
||
| // T -> T | ||
| Ch.Assert(colType.RawType == outputType); | ||
| del = CreateDirectGetter<int>; | ||
| if (outputType.IsGenericType && outputType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||
| Ch.Assert(colType.RawType == Nullable.GetUnderlyingType(outputType)); | ||
| else | ||
| Ch.Assert(colType.RawType == outputType); | ||
| del = CreateDirectGetterDelegate<int>; | ||
| } | ||
| else | ||
| { | ||
| // REVIEW: Is this even possible? | ||
| throw Ch.ExceptNotImpl("Type '{0}' is not yet supported.", outputType.FullName); | ||
| } | ||
| MethodInfo meth = | ||
| del.GetMethodInfo().GetGenericMethodDefinition().MakeGenericMethod(colType.ItemType.RawType); | ||
| del.GetMethodInfo().GetGenericMethodDefinition().MakeGenericMethod(genericType); | ||
| return (Delegate)meth.Invoke(this, new object[] { index }); | ||
| } | ||
|
|
||
| private Delegate CreateStringArrayToVBufferGetter(int index) | ||
| private Delegate CreateArrayGetterDelegate<TSrc, TDst>(int index, Func<TSrc, TDst> convert) | ||
| { | ||
| var peek = DataView._peeks[index] as Peek<TRow, string[]>; | ||
| var peek = DataView._peeks[index] as Peek<TRow, TSrc[]>; | ||
| Ch.AssertValue(peek); | ||
|
|
||
| string[] buf = null; | ||
|
|
||
| return (ValueGetter<VBuffer<DvText>>)((ref VBuffer<DvText> dst) => | ||
| TSrc[] buf = default; | ||
| return (ValueGetter<VBuffer<TDst>>)((ref VBuffer<TDst> dst) => | ||
| { | ||
| peek(GetCurrentRowObject(), Position, ref buf); | ||
| var n = Utils.Size(buf); | ||
| dst = new VBuffer<DvText>(n, Utils.Size(dst.Values) < n | ||
| ? new DvText[n] | ||
| dst = new VBuffer<TDst>(n, Utils.Size(dst.Values) < n | ||
| ? new TDst[n] | ||
| : dst.Values, dst.Indices); | ||
| for (int i = 0; i < n; i++) | ||
| dst.Values[i] = new DvText(buf[i]); | ||
| }); | ||
| } | ||
|
|
||
| private Delegate CreateStringToTextGetter(int index) | ||
| { | ||
| var peek = DataView._peeks[index] as Peek<TRow, string>; | ||
| Ch.AssertValue(peek); | ||
| string buf = null; | ||
| return (ValueGetter<DvText>)((ref DvText dst) => | ||
| { | ||
| peek(GetCurrentRowObject(), Position, ref buf); | ||
| dst = new DvText(buf); | ||
| }); | ||
| } | ||
|
|
||
| private Delegate CreateBooleanToDvBoolGetter(int index) | ||
| { | ||
| var peek = DataView._peeks[index] as Peek<TRow, bool>; | ||
| Ch.AssertValue(peek); | ||
| bool buf = false; | ||
| return (ValueGetter<DvBool>)((ref DvBool dst) => | ||
| { | ||
| peek(GetCurrentRowObject(), Position, ref buf); | ||
| dst = (DvBool)buf; | ||
| dst.Values[i] = convert(buf[i]); | ||
| }); | ||
| } | ||
|
|
||
| private Delegate CreateNullableBooleanToDvBoolGetter(int index) | ||
| private Delegate CreateGetterDelegate<TSrc, TDst>(int index, Func<TSrc, TDst> convert) | ||
| { | ||
| var peek = DataView._peeks[index] as Peek<TRow, bool?>; | ||
| var peek = DataView._peeks[index] as Peek<TRow, TSrc>; | ||
| Ch.AssertValue(peek); | ||
| bool? buf = null; | ||
| return (ValueGetter<DvBool>)((ref DvBool dst) => | ||
| TSrc buf = default; | ||
| return (ValueGetter<TDst>)((ref TDst dst) => | ||
| { | ||
| peek(GetCurrentRowObject(), Position, ref buf); | ||
| dst = buf.HasValue ? (DvBool)buf.Value : DvBool.NA; | ||
| dst = convert(buf); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have identity converts. even if I pass is x=>x I convert from TSrc to TDst class and call implicit converter. Which I'm not sure how to express through generic classes. In reply to: 203875078 [](ancestors = 203875078)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I see now. Ok, why don't we call this In reply to: 203876308 [](ancestors = 203876308,203875078)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I seem to recall this conversation with Shon, that any 'helper' mechanism that would introduce these expensive conversions better doesn't exist at all. This is part of a conversation about why we even have DvInts. Maybe a REVIEW comment alongside this converting getter/setter would be in order? In reply to: 203877031 [](ancestors = 203877031,203876308,203875078) |
||
| }); | ||
| } | ||
|
|
||
| private Delegate CreateArrayToVBufferGetter<TDst>(int index) | ||
| private Delegate CreateDirectArrayGetterDelegate<TDst>(int index) | ||
| { | ||
| var peek = DataView._peeks[index] as Peek<TRow, TDst[]>; | ||
| Ch.AssertValue(peek); | ||
|
|
@@ -250,26 +330,29 @@ private Delegate CreateArrayToVBufferGetter<TDst>(int index) | |
| }); | ||
| } | ||
|
|
||
| private Delegate CreateVBufferToVBufferDelegate<TDst>(int index) | ||
| private Delegate CreateDirectVBufferGetterDelegate<TDst>(int index) | ||
| { | ||
| var peek = DataView._peeks[index] as Peek<TRow, VBuffer<TDst>>; | ||
| Ch.AssertValue(peek); | ||
| VBuffer<TDst> buf = default(VBuffer<TDst>); | ||
| return (ValueGetter<VBuffer<TDst>>)((ref VBuffer<TDst> dst) => | ||
| { | ||
| // The peek for a VBuffer is just a simple assignment, so there is | ||
| // no copy going on in the peek, so we must do that as a second | ||
| // step to the destination. | ||
| peek(GetCurrentRowObject(), Position, ref buf); | ||
| buf.CopyTo(ref dst); | ||
| }); | ||
| { | ||
| // The peek for a VBuffer is just a simple assignment, so there is | ||
| // no copy going on in the peek, so we must do that as a second | ||
| // step to the destination. | ||
| peek(GetCurrentRowObject(), Position, ref buf); | ||
| buf.CopyTo(ref dst); | ||
| }); | ||
| } | ||
|
|
||
| private Delegate CreateDirectGetter<TDst>(int index) | ||
| private Delegate CreateDirectGetterDelegate<TDst>(int index) | ||
| { | ||
| var peek = DataView._peeks[index] as Peek<TRow, TDst>; | ||
| Ch.AssertValue(peek); | ||
| return (ValueGetter<TDst>)((ref TDst dst) => { peek(GetCurrentRowObject(), Position, ref dst); }); | ||
| return (ValueGetter<TDst>)((ref TDst dst) => | ||
| { | ||
| peek(GetCurrentRowObject(), Position, ref dst); | ||
| }); | ||
| } | ||
|
|
||
| protected abstract TRow GetCurrentRowObject(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -327,7 +327,8 @@ public static SchemaDefinition Create(Type userType) | |
| // This field does not need a column. | ||
| // REVIEW: maybe validate the channel attribute now, instead | ||
| // of later at cursor creation. | ||
| if (fieldInfo.FieldType == typeof(IChannel)) | ||
| // Const fields not need to be mapped. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
do not need #Closed
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, this comment only applies to the IsLiteral clause. I suggest to decouple the 2 clauses and have a different comment for each. In reply to: 203871821 [](ancestors = 203871821) |
||
| if (fieldInfo.FieldType == typeof(IChannel) || fieldInfo.IsLiteral) | ||
| continue; | ||
|
|
||
| if (fieldInfo.GetCustomAttribute<NoColumnAttribute>() != null) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the comment #Closed