public override Array GetArray(Type leafType, int depth, out string[] heads) { // The only type that must be forwarded to impl is the complex class, as the impl stores this class. // The only types that must _not_ be forwarded are any object types (impl is an IMathLink, and therefore cannot handle them). // Every type other than Complex could be handled by the base implementation here, but it is likely that the wrapped link // is a NativeLink, which can handle many primitive types (those that are native in the MathLink C API) in a // very efficient way, so we want to forward these. if (leafType == ComplexType || Utils.IsTrulyPrimitive(leafType)) { return(impl.GetArray(leafType, depth, out heads)); } else { return(base.GetArray(leafType, depth, out heads)); } }
// Can read any kind of array, except for jagged arrays that are multidimensional at the beginning, like [][,] // (jagged at start like [,][] or [,...][][] is OK, but only if we know the type, not for an Array slot). Recall // that mixed array types are read backwards, so [][,] is a 2-deep array of element type [] (i.e., it is multidimensional // at the start, not the end). One more limitation: For an Array argument slot, if the incoming array is 3-deep or deeper, // it must be rectangular, not jagged. // We know the incoming expression has head List. internal static Array readArbitraryArray(IMathLink ml, Type t) { int len; if (t == typeof(Array)) { ExpressionType leafExprType; Type leafType; // For the Array type, we have no clue about how the leaf elements will be read. Thus we need to peek into // the incoming data to decide what type it is. int depth = determineIncomingArrayDepth(ml); ILinkMark mark = ml.CreateMark(); ILoopbackLink loop = null; try { if (depth == 1) { len = ml.CheckFunction("List"); if (len == 0) { throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY); } leafExprType = ml.GetNextExpressionType(); ml.SeekMark(mark); leafType = netTypeFromExpressionType(leafExprType, ml); // Fail if data could only be read as Expr. This means that we cannot pass a list of arbitrary expressions // to an arg slot typed as Array. It would have to be typed as Expr[]. We make this choice to provide // more meaningful error reporting for the cases where the array has bogus data in it. We assume that // this convenience outweighs the very rare cases where a programmer would want to pass an array of exprs // for an Array slot (they could always create the array separately and pass it as an object reference). if (leafType == typeof(Expr)) { if (leafExprType == ExpressionType.Complex) { throw new MathLinkException(MathLinkException.MLE_NO_COMPLEX); } else { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY); } } return((Array)ml.GetArray(leafType, 1)); } else if (depth == 2) { // The loopback link is just a utility for quickly reading expressions off the link (via TransferExpression). // Nothing is ever read back off the loopback link. loop = MathLinkFactory.CreateLoopbackLink(); // Determine if the array is rectangular or jagged. len = ml.CheckFunction("List"); bool isJagged = false; bool foundLeafType = false; // This next assignment is strictly for the compiler. The value will never be used // unless it is set to an actual value below. We have an existing function that will get // the leafExprType of the incoming array (getLeafExprType()), but we also need to check if the array // is jagged here, so we will do both tasks at once and save a little time. leafExprType = ExpressionType.Integer; int lenAtLevel2 = ml.CheckFunction("List"); if (lenAtLevel2 > 0) { leafExprType = ml.GetNextExpressionType(); foundLeafType = true; } // peel off all the elements in the first sublist. for (int j = 0; j < lenAtLevel2; j++) { loop.TransferExpression(ml); } // For each remaining sublist, check its length and peel off its members. At this point, though, we // cannot be guaranteed that all elements are lists (or, at least, guaranteed that it is an error // if they are not). They could be Null if the array is jagged: {{1, 2}, Null}. for (int i = 1; i < len; i++) { ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Object) { isJagged = true; // OK to have null as an element in a jagged array. if (ml.GetObject() != null) { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } } else if (nextExprType == ExpressionType.Function) { int thisLength = ml.CheckFunction("List"); if (!foundLeafType && thisLength > 0) { leafExprType = ml.GetNextExpressionType(); foundLeafType = true; } if (thisLength != lenAtLevel2) { isJagged = true; break; } else { for (int j = 0; j < thisLength; j++) { loop.TransferExpression(ml); } } } else { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_DEPTH); } } // If the array is empty we cannot create a .NET array for it, as there is no type info for the array we are creating. if (!foundLeafType) { throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY); } leafType = netTypeFromExpressionType(leafExprType, ml); ml.SeekMark(mark); if (isJagged) { ml.CheckFunction("List"); Array result = Array.CreateInstance(Array.CreateInstance(leafType, 0).GetType(), len); for (int i = 0; i < len; i++) { // Have to check if elements are lists or Null. ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Function) { result.SetValue(ml.GetArray(leafType, 1), i); } else { string sym = ml.GetSymbol(); if (sym != "Null") { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } result.SetValue(null, i); } } return(result); } else { return(ml.GetArray(leafType, 2)); } } else { // For an Array argument slot, we only support passing >2-deep rectangular arrays, not jagged. for (int i = 0; i < depth; i++) { ml.CheckFunction("List"); } leafExprType = ml.GetNextExpressionType(); leafType = netTypeFromExpressionType(leafExprType, ml); ml.SeekMark(mark); return(ml.GetArray(leafType, depth)); } } finally { if (loop != null) { loop.Close(); } ml.DestroyMark(mark); } } else { // We have the actual array shape encoded in the type. Either [], [,..], or array-of-arrays: [][].... int arrayRank = t.GetArrayRank(); Type elementType = t.GetElementType(); if (elementType.IsArray) { if (arrayRank > 1) { // Don't support multidimensional array at start of jagged array: [][,]. Recall that mixed array types // are read backwards, so [][,] is a 2-deep array of element type []. throw new MathLinkException(MathLinkException.MLE_MULTIDIM_ARRAY_OF_ARRAY); } len = ml.CheckFunction("List"); Array result = Array.CreateInstance(elementType, len); for (int i = 0; i < len; i++) { ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Function) { result.SetValue(readArbitraryArray(ml, elementType), i); } else { string sym = ml.GetSymbol(); if (sym != "Null") { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } result.SetValue(null, i); } } return(result); } else if (elementType == typeof(Array)) { // Don't support Array[]. throw new MathLinkException(MathLinkException.MLE_ARRAY_OF_ARRAYCLASS); } else { return(ml.GetArray(elementType, arrayRank)); } } }