/// <summary> /// Applies a callback function on specified tuples one by one storing its results to an array. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="map"> /// A callback to be called on tuples. The number of arguments should be the same as /// the number of arrays specified by <pramref name="arrays"/>. /// Arguments passed by reference modifies elements of <pramref name="arrays"/>. /// A <B>null</B> means default callback which makes integer indexed arrays from the tuples is used. /// </param> /// <param name="arrays">Arrays where to load tuples from. </param> /// <returns>An array of return values of the callback /// keyed by keys of the <paramref name="arrays"/> if it /// is a single array or by integer keys starting from 0.</returns> /// <remarks> /// <para> /// In the <I>i</I>-th call the <I>j</I>-th parameter of the callback will be /// the <I>i</I>-th value of the <I>j</I>-the array or a <B>null</B> if that array /// has less then <I>i</I> entries. /// </para> /// <para> /// If the callback assigns a value to a parameter passed by reference in the <I>i</I>-the call /// and the respective array contains at least <I>i</I> elements the assigned value is propagated /// to the array. /// </para> /// </remarks> public static PhpArray array_map(Context ctx /*, caller*/, IPhpCallable map, [In, Out] params PhpArray[] arrays) { //if (!PhpArgument.CheckCallback(map, caller, "map", 0, true)) return null; if (arrays == null || arrays.Length == 0) { //PhpException.InvalidArgument("arrays", LibResources.GetString("arg:null_or_emtpy")); //return null; throw new ArgumentException(); } // if callback has not been specified uses the default one: if (map == null) { map = _mapIdentity; } int count = arrays.Length; bool preserve_keys = count == 1; var args = new PhpValue[count]; var iterators = new OrderedDictionary.FastEnumerator[count]; PhpArray result; // initializes iterators and args array, computes length of the longest array: int max_count = 0; for (int i = 0; i < arrays.Length; i++) { var array = arrays[i]; if (array == null) { //PhpException.Throw(PhpError.Warning, LibResources.GetString("argument_not_array", i + 2));// +2 (first arg is callback) //return null; throw new ArgumentException(); } args[i] = PhpValue.CreateAlias(); iterators[i] = array.GetFastEnumerator(); if (array.Count > max_count) max_count = array.Count; } // keys are preserved in a case of a single array and re-indexed otherwise: if (preserve_keys) result = new PhpArray(arrays[0].IntegerCount, arrays[0].StringCount); else result = new PhpArray(max_count, 0); for (;;) { bool hasvalid = false; // fills args[] with items from arrays: for (int i = 0; i < arrays.Length; i++) { if (iterators[i].IsValid) { hasvalid = true; // note: deep copy is not necessary since a function copies its arguments if needed: args[i].Alias.Value = iterators[i].CurrentValue.GetValue(); // TODO: throws if the current Value is PhpReference } else { args[i].Alias.Value = PhpValue.Null; } } if (!hasvalid) break; // invokes callback: var return_value = map.Invoke(ctx, args); // return value is not deeply copied: if (preserve_keys) result.Add(iterators[0].CurrentKey, return_value); else result.Add(return_value); // loads new values (callback may modify some by ref arguments): for (int i = 0; i < arrays.Length; i++) { if (iterators[i].IsValid) { var item = iterators[i].CurrentValue; if (item.IsAlias) { item.Alias.Value = args[i].Alias.Value; } else { iterators[i].CurrentValue = args[i].Alias.Value; } // iterators[i].MoveNext(); } } } return result; }
public DictionaryAdapter(PhpHashtable /*!*/ table) { Debug.Assert(table != null); _enumerator = table.GetFastEnumerator(); }