comparison src/fns.c @ 5253:b6a398dbb403

Fewer algorithmic complexity surprises, nicer errors, mapcarX(), maplist() src/ChangeLog addition: 2010-09-01 Aidan Kehoe <kehoea@parhasard.net> * fns.c (list_merge, list_array_merge_into_list) (list_array_merge_into_array): Avoid algorithmic complexity surprises when checking for circularity in these functions. (Freduce): Fix some formatting, in passing. (mapcarX): Drop the SOME_OR_EVERY argument to this function; instead, take CALLER, a symbol reflecting the Lisp-visible function that called mapcarX(). Use CALLER with mapping_interaction_error() when sequences are modified illegally. Don't cons with #'some, #'every, not even a little. (Fmapconcat, FmapcarX, Fmapvector, Fmapcan, Fmapc, Fmap) (Fmap_into, Fsome, Fevery): Call mapcarX() with its new arguments. (Fmapcan): Don't unnecessarily complicate the nconc call. (maplist): Take CALLER, a symbol reflecting the Lisp-visible function that called maplist(), rather than having separate arguments to indicate mapl vs. mapcon. Avoid algorithmic complexity surprises when checking for circularity. In #'mapcon, check a given stretch of result for well-formedness once, which was not previously the case, despite what the comments said. (Fmaplist, Fmapl, Fmapcon): Call maplist() with its new arguments.
author Aidan Kehoe <kehoea@parhasard.net>
date Wed, 01 Sep 2010 12:51:32 +0100
parents d579d76f3dcc
children b5611afbcc76
comparison
equal deleted inserted replaced
5252:378a34562cbe 5253:b6a398dbb403
54 /* NOTE: This symbol is also used in lread.c */ 54 /* NOTE: This symbol is also used in lread.c */
55 #define FEATUREP_SYNTAX 55 #define FEATUREP_SYNTAX
56 56
57 Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill; 57 Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill;
58 Lisp_Object Qidentity; 58 Lisp_Object Qidentity;
59 Lisp_Object Qvector, Qarray, Qbit_vector, QsortX, Qreduce, Q_from_end, Q_initial_value; 59 Lisp_Object Qvector, Qarray, Qbit_vector, QsortX, Q_from_end, Q_initial_value;
60 Lisp_Object Qmapconcat, QmapcarX, Qmapvector, Qmapcan, Qmapc, Qmap, Qmap_into;
61 Lisp_Object Qsome, Qevery, Qmaplist, Qmapl, Qmapcon, Qreduce;
60 62
61 Lisp_Object Qbase64_conversion_error; 63 Lisp_Object Qbase64_conversion_error;
62 64
63 Lisp_Object Vpath_separator; 65 Lisp_Object Vpath_separator;
64 66
2061 { 2063 {
2062 Lisp_Object value; 2064 Lisp_Object value;
2063 Lisp_Object tail; 2065 Lisp_Object tail;
2064 Lisp_Object tem; 2066 Lisp_Object tem;
2065 Lisp_Object l1, l2; 2067 Lisp_Object l1, l2;
2066 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; 2068 Lisp_Object tortoises[2];
2069 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
2067 int looped = 0; 2070 int looped = 0;
2068 2071
2069 l1 = org_l1; 2072 l1 = org_l1;
2070 l2 = org_l2; 2073 l2 = org_l2;
2071 tail = Qnil; 2074 tail = Qnil;
2072 value = Qnil; 2075 value = Qnil;
2076 tortoises[0] = org_l1;
2077 tortoises[1] = org_l2;
2073 2078
2074 if (NULL == c_predicate) 2079 if (NULL == c_predicate)
2075 { 2080 {
2076 c_predicate = EQ (key_func, Qidentity) ? 2081 c_predicate = EQ (key_func, Qidentity) ?
2077 c_merge_predicate_nokey : c_merge_predicate_key; 2082 c_merge_predicate_nokey : c_merge_predicate_key;
2079 2084
2080 /* It is sufficient to protect org_l1 and org_l2. 2085 /* It is sufficient to protect org_l1 and org_l2.
2081 When l1 and l2 are updated, we copy the new values 2086 When l1 and l2 are updated, we copy the new values
2082 back into the org_ vars. */ 2087 back into the org_ vars. */
2083 2088
2084 GCPRO4 (org_l1, org_l2, predicate, value); 2089 GCPRO5 (org_l1, org_l2, predicate, value, tortoises[0]);
2090 gcpro5.nvars = 2;
2085 2091
2086 while (1) 2092 while (1)
2087 { 2093 {
2088 if (NILP (l1)) 2094 if (NILP (l1))
2089 { 2095 {
2118 value = tem; 2124 value = tem;
2119 else 2125 else
2120 Fsetcdr (tail, tem); 2126 Fsetcdr (tail, tem);
2121 tail = tem; 2127 tail = tem;
2122 2128
2123 if (++looped % CIRCULAR_LIST_SUSPICION_LENGTH) continue; 2129 if (++looped > CIRCULAR_LIST_SUSPICION_LENGTH)
2124 2130 {
2125 /* Just check the lists aren't circular:*/ 2131 if (looped & 1)
2126 { 2132 {
2127 EXTERNAL_LIST_LOOP_1 (l1) 2133 tortoises[0] = XCDR (tortoises[0]);
2128 { 2134 tortoises[1] = XCDR (tortoises[1]);
2129 } 2135 }
2130 } 2136
2131 { 2137 if (EQ (org_l1, tortoises[0]))
2132 EXTERNAL_LIST_LOOP_1 (l2) 2138 {
2133 { 2139 signal_circular_list_error (org_l1);
2134 } 2140 }
2135 } 2141
2142 if (EQ (org_l2, tortoises[1]))
2143 {
2144 signal_circular_list_error (org_l2);
2145 }
2146 }
2136 } 2147 }
2137 } 2148 }
2138 2149
2139 static void 2150 static void
2140 array_merge (Lisp_Object *dest, Elemcount dest_len, 2151 array_merge (Lisp_Object *dest, Elemcount dest_len,
2228 Lisp_Object, 2239 Lisp_Object,
2229 Lisp_Object), 2240 Lisp_Object),
2230 Lisp_Object predicate, Lisp_Object key_func, 2241 Lisp_Object predicate, Lisp_Object key_func,
2231 Boolint reverse_order) 2242 Boolint reverse_order)
2232 { 2243 {
2233 Lisp_Object tail = Qnil, value = Qnil; 2244 Lisp_Object tail = Qnil, value = Qnil, tortoise = list;
2234 struct gcpro gcpro1, gcpro2, gcpro3; 2245 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
2235 Elemcount array_index = 0; 2246 Elemcount array_index = 0;
2236 int looped = 0; 2247 int looped = 0;
2237 2248
2238 GCPRO3 (list, tail, value); 2249 GCPRO4 (list, tail, value, tortoise);
2239 2250
2240 while (1) 2251 while (1)
2241 { 2252 {
2242 if (NILP (list)) 2253 if (NILP (list))
2243 { 2254 {
2295 tail = XCDR (tail); 2306 tail = XCDR (tail);
2296 } 2307 }
2297 ++array_index; 2308 ++array_index;
2298 } 2309 }
2299 2310
2300 if (++looped % CIRCULAR_LIST_SUSPICION_LENGTH) continue; 2311 if (++looped > CIRCULAR_LIST_SUSPICION_LENGTH)
2301 2312 {
2302 { 2313 if (looped & 1)
2303 EXTERNAL_LIST_LOOP_1 (list) 2314 {
2304 { 2315 tortoise = XCDR (tortoise);
2305 } 2316 }
2306 } 2317
2318 if (EQ (list, tortoise))
2319 {
2320 signal_circular_list_error (list);
2321 }
2322 }
2307 } 2323 }
2308 } 2324 }
2309 2325
2310 static void 2326 static void
2311 list_list_merge_into_array (Lisp_Object *output, Elemcount output_len, 2327 list_list_merge_into_array (Lisp_Object *output, Elemcount output_len,
2375 { 2391 {
2376 if (NILP (list)) 2392 if (NILP (list))
2377 { 2393 {
2378 if (array_len - array_index != output_len - output_index) 2394 if (array_len - array_index != output_len - output_index)
2379 { 2395 {
2380 invalid_state ("List length modified during merge", Qunbound); 2396 mapping_interaction_error (Qmerge, list);
2381 } 2397 }
2382 2398
2383 while (array_index < array_len) 2399 while (array_index < array_len)
2384 { 2400 {
2385 output [output_index++] = array [array_index++]; 2401 output [output_index++] = array [array_index++];
4103 If VALS is non-NULL, NSEQUENCES is one, and SEQUENCES[0] is a cons, 4119 If VALS is non-NULL, NSEQUENCES is one, and SEQUENCES[0] is a cons,
4104 mapcarX will store the elements of SEQUENCES[0] in stack and GCPRO them, 4120 mapcarX will store the elements of SEQUENCES[0] in stack and GCPRO them,
4105 so FUNCTION cannot insert a non-cons into SEQUENCES[0] and throw off 4121 so FUNCTION cannot insert a non-cons into SEQUENCES[0] and throw off
4106 mapcarX. 4122 mapcarX.
4107 4123
4108 Otherwise, mapcarX signals a wrong-type-error if it encounters a 4124 Otherwise, mapcarX signals an invalid state error (see
4109 non-cons, non-array when traversing SEQUENCES. Common Lisp specifies in 4125 mapping_interaction_error(), above) if it encounters a non-cons,
4126 non-array when traversing SEQUENCES. Common Lisp specifies in
4110 MAPPING-DESTRUCTIVE-INTERACTION that it is an error when FUNCTION 4127 MAPPING-DESTRUCTIVE-INTERACTION that it is an error when FUNCTION
4111 destructively modifies SEQUENCES in a way that might affect the ongoing 4128 destructively modifies SEQUENCES in a way that might affect the ongoing
4112 traversal operation. 4129 traversal operation.
4113 4130
4114 If SOME_OR_EVERY is SOME_OR_EVERY_SOME, return the (possibly multiple) 4131 CALLER is a symbol describing the Lisp-visible function that was called,
4115 values given by FUNCTION the first time it is non-nil, and abandon the 4132 and any errors thrown because SEQUENCES was modified will reflect it.
4116 iterations. LISP_VALS must be a cons, and the return value will be 4133
4117 stored in its car. If SOME_OR_EVERY is SOME_OR_EVERY_EVERY, store Qnil 4134 If CALLER is Qsome, return the (possibly multiple) values given by
4118 in the car of LISP_VALS if FUNCTION gives nil; otherwise leave it 4135 FUNCTION the first time it is non-nil, and abandon the iterations.
4119 alone. */ 4136 LISP_VALS must be the result of calling STORE_VOID_IN_LISP on the address
4120 4137 of a Lisp object, and the return value will be stored at that address.
4121 #define SOME_OR_EVERY_NEITHER 0 4138 If CALLER is Qevery, LISP_VALS must also reflect a pointer to a Lisp
4122 #define SOME_OR_EVERY_SOME 1 4139 object, and Qnil will be stored at that address if FUNCTION gives nil;
4123 #define SOME_OR_EVERY_EVERY 2 4140 otherwise it will be left alone. */
4124 4141
4125 static void 4142 static void
4126 mapcarX (Elemcount call_count, Lisp_Object *vals, Lisp_Object lisp_vals, 4143 mapcarX (Elemcount call_count, Lisp_Object *vals, Lisp_Object lisp_vals,
4127 Lisp_Object function, int nsequences, Lisp_Object *sequences, 4144 Lisp_Object function, int nsequences, Lisp_Object *sequences,
4128 int some_or_every) 4145 Lisp_Object caller)
4129 { 4146 {
4130 Lisp_Object called, *args; 4147 Lisp_Object called, *args;
4131 struct gcpro gcpro1, gcpro2; 4148 struct gcpro gcpro1, gcpro2;
4132 int i, j; 4149 int i, j;
4133 enum lrecord_type lisp_vals_type; 4150
4134 4151 assert ((EQ (caller, Qsome) || EQ (caller, Qevery)) ? vals == NULL : 1);
4135 assert (LRECORDP (lisp_vals));
4136 lisp_vals_type = (enum lrecord_type) XRECORD_LHEADER (lisp_vals)->type;
4137 4152
4138 args = alloca_array (Lisp_Object, nsequences + 1); 4153 args = alloca_array (Lisp_Object, nsequences + 1);
4139 args[0] = function; 4154 args[0] = function;
4140 for (i = 1; i <= nsequences; ++i) 4155 for (i = 1; i <= nsequences; ++i)
4141 { 4156 {
4175 vals[i] = IGNORE_MULTIPLE_VALUES (Ffuncall (nsequences + 1, args)); 4190 vals[i] = IGNORE_MULTIPLE_VALUES (Ffuncall (nsequences + 1, args));
4176 } 4191 }
4177 } 4192 }
4178 else 4193 else
4179 { 4194 {
4195 enum lrecord_type lisp_vals_type;
4180 Binbyte *sequence_types = alloca_array (Binbyte, nsequences); 4196 Binbyte *sequence_types = alloca_array (Binbyte, nsequences);
4181 for (j = 0; j < nsequences; ++j) 4197 for (j = 0; j < nsequences; ++j)
4182 { 4198 {
4183 sequence_types[j] = XRECORD_LHEADER (sequences[j])->type; 4199 sequence_types[j] = XRECORD_LHEADER (sequences[j])->type;
4184 } 4200 }
4201
4202 if (!EQ (caller, Qsome) && !EQ (caller, Qevery))
4203 {
4204 assert (LRECORDP (lisp_vals));
4205 lisp_vals_type
4206 = (enum lrecord_type) XRECORD_LHEADER (lisp_vals)->type;
4207 assert (NILP (lisp_vals) || lisp_vals_type != lrecord_type_symbol);
4208 }
4185 4209
4186 for (i = 0; i < call_count; ++i) 4210 for (i = 0; i < call_count; ++i)
4187 { 4211 {
4188 for (j = 0; j < nsequences; ++j) 4212 for (j = 0; j < nsequences; ++j)
4189 { 4213 {
4191 { 4215 {
4192 case lrecord_type_cons: 4216 case lrecord_type_cons:
4193 { 4217 {
4194 if (!CONSP (sequences[j])) 4218 if (!CONSP (sequences[j]))
4195 { 4219 {
4196 /* This means FUNCTION has probably messed 4220 /* This means FUNCTION has messed around with a cons
4197 around with a cons in one of the sequences, 4221 in one of the sequences, since we checked the
4198 since we checked the type 4222 type (CHECK_SEQUENCE()) and the length and
4199 (CHECK_SEQUENCE()) and the length and
4200 structure (with Flength()) correctly in our 4223 structure (with Flength()) correctly in our
4201 callers. */ 4224 callers. */
4202 dead_wrong_type_argument (Qconsp, sequences[j]); 4225 mapping_interaction_error (caller, sequences[j]);
4203 } 4226 }
4204 args[j + 1] = XCAR (sequences[j]); 4227 args[j + 1] = XCAR (sequences[j]);
4205 sequences[j] = XCDR (sequences[j]); 4228 sequences[j] = XCDR (sequences[j]);
4206 break; 4229 break;
4207 } 4230 }
4230 if (vals != NULL) 4253 if (vals != NULL)
4231 { 4254 {
4232 vals[i] = IGNORE_MULTIPLE_VALUES (called); 4255 vals[i] = IGNORE_MULTIPLE_VALUES (called);
4233 gcpro2.nvars += 1; 4256 gcpro2.nvars += 1;
4234 } 4257 }
4235 else 4258 else if (EQ (Qsome, caller))
4236 { 4259 {
4237 switch (lisp_vals_type) 4260 if (!NILP (IGNORE_MULTIPLE_VALUES (called)))
4238 { 4261 {
4239 case lrecord_type_symbol: 4262 Lisp_Object *result
4240 break; 4263 = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals);
4241 case lrecord_type_cons: 4264 *result = called;
4242 { 4265 UNGCPRO;
4243 if (SOME_OR_EVERY_NEITHER == some_or_every) 4266 return;
4244 { 4267 }
4245 called = IGNORE_MULTIPLE_VALUES (called); 4268 }
4246 if (!CONSP (lisp_vals)) 4269 else if (EQ (Qevery, caller))
4247 { 4270 {
4248 /* If FUNCTION has inserted a non-cons non-nil 4271 if (NILP (IGNORE_MULTIPLE_VALUES (called)))
4249 cdr into the list before we've processed the 4272 {
4250 relevant part, error. */ 4273 Lisp_Object *result
4251 dead_wrong_type_argument (Qconsp, lisp_vals); 4274 = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals);
4252 } 4275 *result = Qnil;
4253 4276 UNGCPRO;
4254 XSETCAR (lisp_vals, called); 4277 return;
4255 lisp_vals = XCDR (lisp_vals); 4278 }
4256 break; 4279 }
4257 } 4280 else
4258 4281 {
4259 if (SOME_OR_EVERY_SOME == some_or_every) 4282 called = IGNORE_MULTIPLE_VALUES (called);
4260 { 4283 switch (lisp_vals_type)
4261 if (!NILP (IGNORE_MULTIPLE_VALUES (called))) 4284 {
4262 { 4285 case lrecord_type_symbol:
4263 XCAR (lisp_vals) = called; 4286 /* This is #'mapc; the result of the funcall is
4264 UNGCPRO; 4287 discarded. */
4265 return; 4288 break;
4266 } 4289 case lrecord_type_cons:
4267 break; 4290 {
4268 } 4291 if (!CONSP (lisp_vals))
4269 4292 {
4270 if (SOME_OR_EVERY_EVERY == some_or_every) 4293 /* If FUNCTION has inserted a non-cons non-nil
4271 { 4294 cdr into the list before we've processed the
4272 called = IGNORE_MULTIPLE_VALUES (called); 4295 relevant part, error. */
4273 if (NILP (called)) 4296 mapping_interaction_error (caller, lisp_vals);
4274 { 4297 }
4275 XCAR (lisp_vals) = Qnil; 4298 XSETCAR (lisp_vals, called);
4276 UNGCPRO; 4299 lisp_vals = XCDR (lisp_vals);
4277 return; 4300 break;
4278 } 4301 }
4279 break; 4302 case lrecord_type_vector:
4280 } 4303 {
4281 4304 i < XVECTOR_LENGTH (lisp_vals) ?
4282 goto bad_some_or_every_flag; 4305 (XVECTOR_DATA (lisp_vals)[i] = called) :
4283 } 4306 /* Let #'aset error. */
4284 case lrecord_type_vector: 4307 Faset (lisp_vals, make_int (i), called);
4285 { 4308 break;
4286 called = IGNORE_MULTIPLE_VALUES (called); 4309 }
4287 i < XVECTOR_LENGTH (lisp_vals) ? 4310 case lrecord_type_string:
4288 (XVECTOR_DATA (lisp_vals)[i] = called) : 4311 {
4289 /* Let #'aset error. */ 4312 /* If this ever becomes a code hotspot, we can keep
4290 Faset (lisp_vals, make_int (i), called); 4313 around pointers into the data of the string, checking
4291 break; 4314 each time that it hasn't been relocated. */
4292 } 4315 Faset (lisp_vals, make_int (i), called);
4293 case lrecord_type_string: 4316 break;
4294 { 4317 }
4295 /* If this ever becomes a code hotspot, we can keep 4318 case lrecord_type_bit_vector:
4296 around pointers into the data of the string, checking 4319 {
4297 each time that it hasn't been relocated. */ 4320 (BITP (called) &&
4298 called = IGNORE_MULTIPLE_VALUES (called); 4321 i < bit_vector_length (XBIT_VECTOR (lisp_vals))) ?
4299 Faset (lisp_vals, make_int (i), called); 4322 set_bit_vector_bit (XBIT_VECTOR (lisp_vals), i,
4300 break; 4323 XINT (called)) :
4301 } 4324 (void) Faset (lisp_vals, make_int (i), called);
4302 case lrecord_type_bit_vector: 4325 break;
4303 { 4326 }
4304 called = IGNORE_MULTIPLE_VALUES (called); 4327 default:
4305 (BITP (called) && 4328 {
4306 i < bit_vector_length (XBIT_VECTOR (lisp_vals))) ? 4329 ABORT();
4307 set_bit_vector_bit (XBIT_VECTOR (lisp_vals), i, 4330 break;
4308 XINT (called)) : 4331 }
4309 (void) Faset (lisp_vals, make_int (i), called); 4332 }
4310 break; 4333 }
4311 }
4312 bad_some_or_every_flag:
4313 default:
4314 {
4315 ABORT();
4316 break;
4317 }
4318 }
4319 }
4320 } 4334 }
4321 } 4335 }
4322 UNGCPRO; 4336 UNGCPRO;
4323 } 4337 }
4324 4338
4371 sequence = XCDR (sequence); 4385 sequence = XCDR (sequence);
4372 } 4386 }
4373 } 4387 }
4374 else 4388 else
4375 { 4389 {
4376 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, 4390 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmapconcat);
4377 SOME_OR_EVERY_NEITHER);
4378 } 4391 }
4379 4392
4380 for (i = len - 1; i >= 0; i--) 4393 for (i = len - 1; i >= 0; i--)
4381 args0[i + i] = args0[i]; 4394 args0[i + i] = args0[i];
4382 4395
4410 CHECK_SEQUENCE (args[i]); 4423 CHECK_SEQUENCE (args[i]);
4411 len = min (len, XINT (Flength (args[i]))); 4424 len = min (len, XINT (Flength (args[i])));
4412 } 4425 }
4413 4426
4414 args0 = alloca_array (Lisp_Object, len); 4427 args0 = alloca_array (Lisp_Object, len);
4415 mapcarX (len, args0, Qnil, function, nargs - 1, args + 1, 4428 mapcarX (len, args0, Qnil, function, nargs - 1, args + 1, QmapcarX);
4416 SOME_OR_EVERY_NEITHER);
4417 4429
4418 return Flist ((int) len, args0); 4430 return Flist ((int) len, args0);
4419 } 4431 }
4420 4432
4421 DEFUN ("mapvector", Fmapvector, 2, MANY, 0, /* 4433 DEFUN ("mapvector", Fmapvector, 2, MANY, 0, /*
4447 result = make_vector (len, Qnil); 4459 result = make_vector (len, Qnil);
4448 GCPRO1 (result); 4460 GCPRO1 (result);
4449 /* Don't pass result as the lisp_object argument, we want mapcarX to protect 4461 /* Don't pass result as the lisp_object argument, we want mapcarX to protect
4450 a single list argument's elements from being garbage-collected. */ 4462 a single list argument's elements from being garbage-collected. */
4451 mapcarX (len, XVECTOR_DATA (result), Qnil, function, nargs - 1, args +1, 4463 mapcarX (len, XVECTOR_DATA (result), Qnil, function, nargs - 1, args +1,
4452 SOME_OR_EVERY_NEITHER); 4464 Qmapvector);
4453 UNGCPRO; 4465 RETURN_UNGCPRO (result);
4454
4455 return result;
4456 } 4466 }
4457 4467
4458 DEFUN ("mapcan", Fmapcan, 2, MANY, 0, /* 4468 DEFUN ("mapcan", Fmapcan, 2, MANY, 0, /*
4459 Call FUNCTION on each element of SEQUENCE; chain the results together. 4469 Call FUNCTION on each element of SEQUENCE; chain the results together.
4460 4470
4468 4478
4469 arguments: (FUNCTION SEQUENCE &rest SEQUENCES) 4479 arguments: (FUNCTION SEQUENCE &rest SEQUENCES)
4470 */ 4480 */
4471 (int nargs, Lisp_Object *args)) 4481 (int nargs, Lisp_Object *args))
4472 { 4482 {
4473 Lisp_Object function = args[0], nconcing; 4483 Lisp_Object function = args[0], *result;
4474 Elemcount len = EMACS_INT_MAX; 4484 Elemcount result_len = EMACS_INT_MAX;
4475 Lisp_Object *args0;
4476 struct gcpro gcpro1;
4477 int i; 4485 int i;
4478 4486
4479 for (i = 1; i < nargs; ++i) 4487 for (i = 1; i < nargs; ++i)
4480 { 4488 {
4481 CHECK_SEQUENCE (args[i]); 4489 CHECK_SEQUENCE (args[i]);
4482 len = min (len, XINT (Flength (args[i]))); 4490 result_len = min (result_len, XINT (Flength (args[i])));
4483 } 4491 }
4484 4492
4485 args0 = alloca_array (Lisp_Object, len + 1); 4493 result = alloca_array (Lisp_Object, result_len);
4486 mapcarX (len, args0 + 1, Qnil, function, nargs - 1, args + 1, 4494 mapcarX (result_len, result, Qnil, function, nargs - 1, args + 1, Qmapcan);
4487 SOME_OR_EVERY_NEITHER); 4495
4488 4496 /* #'nconc GCPROs its args in case of signals and error. */
4489 if (len < 2) 4497 return Fnconc (result_len, result);
4490 {
4491 return len ? args0[1] : Qnil;
4492 }
4493
4494 /* bytecode_nconc2 can signal and return, we need to GCPRO the args, since
4495 mapcarX is no longer doing this for us. */
4496 args0[0] = Fcons (Qnil, Qnil);
4497 GCPRO1 (args0[0]);
4498 gcpro1.nvars = len + 1;
4499
4500 for (i = 0; i < len; ++i)
4501 {
4502 nconcing = bytecode_nconc2 (args0 + i);
4503 args0[i + 1] = nconcing;
4504 }
4505
4506 RETURN_UNGCPRO (XCDR (nconcing));
4507 } 4498 }
4508 4499
4509 DEFUN ("mapc", Fmapc, 2, MANY, 0, /* 4500 DEFUN ("mapc", Fmapc, 2, MANY, 0, /*
4510 Call FUNCTION on each element of SEQUENCE. 4501 Call FUNCTION on each element of SEQUENCE.
4511 4502
4537 4528
4538 /* We need to GCPRO sequence, because mapcarX will modify the 4529 /* We need to GCPRO sequence, because mapcarX will modify the
4539 elements of the args array handed to it, and this may involve 4530 elements of the args array handed to it, and this may involve
4540 elements of sequence getting garbage collected. */ 4531 elements of sequence getting garbage collected. */
4541 GCPRO1 (sequence); 4532 GCPRO1 (sequence);
4542 mapcarX (len, NULL, Qnil, args[0], nargs - 1, args + 1, 4533 mapcarX (len, NULL, Qnil, args[0], nargs - 1, args + 1, Qmapc);
4543 SOME_OR_EVERY_NEITHER);
4544 RETURN_UNGCPRO (sequence); 4534 RETURN_UNGCPRO (sequence);
4545 } 4535 }
4546 4536
4547 DEFUN ("map", Fmap, 3, MANY, 0, /* 4537 DEFUN ("map", Fmap, 3, MANY, 0, /*
4548 Map FUNCTION across one or more sequences, returning a sequence. 4538 Map FUNCTION across one or more sequences, returning a sequence.
4578 if (!NILP (type)) 4568 if (!NILP (type))
4579 { 4569 {
4580 args0 = alloca_array (Lisp_Object, len); 4570 args0 = alloca_array (Lisp_Object, len);
4581 } 4571 }
4582 4572
4583 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, 4573 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmap);
4584 SOME_OR_EVERY_NEITHER);
4585 4574
4586 if (EQ (type, Qnil)) 4575 if (EQ (type, Qnil))
4587 { 4576 {
4588 return result; 4577 return result;
4589 } 4578 }
4644 CHECK_SEQUENCE (args[i]); 4633 CHECK_SEQUENCE (args[i]);
4645 len = min (len, XINT (Flength (args[i]))); 4634 len = min (len, XINT (Flength (args[i])));
4646 } 4635 }
4647 4636
4648 mapcarX (len, NULL, result_sequence, function, nargs - 2, args + 2, 4637 mapcarX (len, NULL, result_sequence, function, nargs - 2, args + 2,
4649 SOME_OR_EVERY_NEITHER); 4638 Qmap_into);
4650 4639
4651 return result_sequence; 4640 return result_sequence;
4652 } 4641 }
4653 4642
4654 DEFUN ("some", Fsome, 2, MANY, 0, /* 4643 DEFUN ("some", Fsome, 2, MANY, 0, /*
4661 4650
4662 arguments: (PREDICATE SEQUENCE &rest SEQUENCES) 4651 arguments: (PREDICATE SEQUENCE &rest SEQUENCES)
4663 */ 4652 */
4664 (int nargs, Lisp_Object *args)) 4653 (int nargs, Lisp_Object *args))
4665 { 4654 {
4666 Lisp_Object result_box = Fcons (Qnil, Qnil); 4655 Lisp_Object result = Qnil,
4667 struct gcpro gcpro1; 4656 result_ptr = STORE_VOID_IN_LISP ((void *) &result);
4668 Elemcount len = EMACS_INT_MAX; 4657 Elemcount len = EMACS_INT_MAX;
4669 int i; 4658 int i;
4670 4659
4671 GCPRO1 (result_box);
4672
4673 for (i = 1; i < nargs; ++i) 4660 for (i = 1; i < nargs; ++i)
4674 { 4661 {
4675 CHECK_SEQUENCE (args[i]); 4662 CHECK_SEQUENCE (args[i]);
4676 len = min (len, XINT (Flength (args[i]))); 4663 len = min (len, XINT (Flength (args[i])));
4677 } 4664 }
4678 4665
4679 mapcarX (len, NULL, result_box, args[0], nargs - 1, args +1, 4666 mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qsome);
4680 SOME_OR_EVERY_SOME); 4667
4681 4668 return result;
4682 RETURN_UNGCPRO (XCAR (result_box));
4683 } 4669 }
4684 4670
4685 DEFUN ("every", Fevery, 2, MANY, 0, /* 4671 DEFUN ("every", Fevery, 2, MANY, 0, /*
4686 Return true if PREDICATE is true of every element of SEQUENCE. 4672 Return true if PREDICATE is true of every element of SEQUENCE.
4687 4673
4692 4678
4693 arguments: (PREDICATE SEQUENCE &rest SEQUENCES) 4679 arguments: (PREDICATE SEQUENCE &rest SEQUENCES)
4694 */ 4680 */
4695 (int nargs, Lisp_Object *args)) 4681 (int nargs, Lisp_Object *args))
4696 { 4682 {
4697 Lisp_Object result_box = Fcons (Qt, Qnil); 4683 Lisp_Object result = Qt, result_ptr = STORE_VOID_IN_LISP ((void *) &result);
4698 struct gcpro gcpro1;
4699 Elemcount len = EMACS_INT_MAX; 4684 Elemcount len = EMACS_INT_MAX;
4700 int i; 4685 int i;
4701 4686
4702 GCPRO1 (result_box);
4703
4704 for (i = 1; i < nargs; ++i) 4687 for (i = 1; i < nargs; ++i)
4705 { 4688 {
4706 CHECK_SEQUENCE (args[i]); 4689 CHECK_SEQUENCE (args[i]);
4707 len = min (len, XINT (Flength (args[i]))); 4690 len = min (len, XINT (Flength (args[i])));
4708 } 4691 }
4709 4692
4710 mapcarX (len, NULL, result_box, args[0], nargs - 1, args +1, 4693 mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qevery);
4711 SOME_OR_EVERY_EVERY); 4694
4712 4695 return result;
4713 RETURN_UNGCPRO (XCAR (result_box));
4714 } 4696 }
4715 4697
4716 /* Call FUNCTION with NLISTS arguments repeatedly, each Nth argument 4698 /* Call FUNCTION with NLISTS arguments repeatedly, each Nth argument
4717 corresponding to the result of calling (nthcdr ITERATION-COUNT LISTS[N]), 4699 corresponding to the result of calling (nthcdr ITERATION-COUNT LISTS[N]),
4718 until that #'nthcdr expression gives nil for some element of LISTS. 4700 until that #'nthcdr expression gives nil for some element of LISTS.
4719 4701
4720 If MAPLP is zero, return LISTS[0]. Otherwise, return a list of the return 4702 CALLER is a symbol reflecting the Lisp-visible function that was called,
4721 values from FUNCTION; if NCONCP is non-zero, nconc them together. 4703 and any errors thrown because SEQUENCES was modified will reflect it.
4704
4705 If CALLER is Qmapl, return LISTS[0]. Otherwise, return a list of the
4706 return values from FUNCTION; if caller is Qmapcan, nconc them together.
4722 4707
4723 In contrast to mapcarX, we don't require our callers to check LISTS for 4708 In contrast to mapcarX, we don't require our callers to check LISTS for
4724 well-formedness, we signal wrong-type-argument if it's not a list, or 4709 well-formedness, we signal wrong-type-argument if it's not a list, or
4725 circular-list if it's circular. */ 4710 circular-list if it's circular. */
4726 4711
4727 static Lisp_Object 4712 static Lisp_Object
4728 maplist (Lisp_Object function, int nlists, Lisp_Object *lists, int maplp, 4713 maplist (Lisp_Object function, int nlists, Lisp_Object *lists,
4729 int nconcp) 4714 Lisp_Object caller)
4730 { 4715 {
4731 Lisp_Object result = maplp ? lists[0] : Fcons (Qnil, Qnil), funcalled; 4716 Lisp_Object nconcing[2], accum = Qnil, *args, *tortoises, funcalled;
4732 Lisp_Object nconcing[2], accum = result, *args; 4717 Lisp_Object result = EQ (caller, Qmapl) ? lists[0] : Qnil;
4733 struct gcpro gcpro1, gcpro2, gcpro3; 4718 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
4734 int i, j, continuing = (nlists > 0), called_count = 0; 4719 int i, j, continuing = (nlists > 0), called_count = 0;
4735 4720
4736 args = alloca_array (Lisp_Object, nlists + 1); 4721 args = alloca_array (Lisp_Object, nlists + 1);
4737 args[0] = function; 4722 args[0] = function;
4738 for (i = 1; i <= nlists; ++i) 4723 for (i = 1; i <= nlists; ++i)
4739 { 4724 {
4740 args[i] = Qnil; 4725 args[i] = Qnil;
4741 } 4726 }
4742 4727
4743 if (nconcp) 4728 tortoises = alloca_array (Lisp_Object, nlists);
4744 { 4729 memcpy (tortoises, lists, nlists * sizeof (Lisp_Object));
4745 nconcing[0] = result; 4730
4731 if (EQ (caller, Qmapcon))
4732 {
4733 nconcing[0] = Qnil;
4746 nconcing[1] = Qnil; 4734 nconcing[1] = Qnil;
4747 GCPRO3 (args[0], nconcing[0], result); 4735 GCPRO4 (args[0], nconcing[0], tortoises[0], result);
4748 gcpro1.nvars = 1; 4736 gcpro1.nvars = 1;
4749 gcpro2.nvars = 2; 4737 gcpro2.nvars = 2;
4738 gcpro3.nvars = nlists;
4750 } 4739 }
4751 else 4740 else
4752 { 4741 {
4753 GCPRO2 (args[0], result); 4742 GCPRO3 (args[0], tortoises[0], result);
4754 gcpro1.nvars = 1; 4743 gcpro1.nvars = 1;
4744 gcpro2.nvars = nlists;
4755 } 4745 }
4756 4746
4757 while (continuing) 4747 while (continuing)
4758 { 4748 {
4759 for (j = 0; j < nlists; ++j) 4749 for (j = 0; j < nlists; ++j)
4768 continuing = 0; 4758 continuing = 0;
4769 break; 4759 break;
4770 } 4760 }
4771 else 4761 else
4772 { 4762 {
4773 dead_wrong_type_argument (Qlistp, lists[j]); 4763 lists[j] = wrong_type_argument (Qlistp, lists[j]);
4774 } 4764 }
4775 } 4765 }
4776 if (!continuing) break; 4766 if (!continuing) break;
4777 funcalled = IGNORE_MULTIPLE_VALUES (Ffuncall (nlists + 1, args)); 4767 funcalled = IGNORE_MULTIPLE_VALUES (Ffuncall (nlists + 1, args));
4778 if (!maplp) 4768
4769 if (EQ (caller, Qmapl))
4779 { 4770 {
4780 if (nconcp) 4771 DO_NOTHING;
4781 { 4772 }
4782 /* This order of calls means we check that each list is 4773 else if (EQ (caller, Qmapcon))
4783 well-formed once and once only. The last result does 4774 {
4784 not have to be a list. */ 4775 nconcing[1] = funcalled;
4785 nconcing[1] = funcalled; 4776 accum = bytecode_nconc2 (nconcing);
4786 nconcing[0] = bytecode_nconc2 (nconcing); 4777 if (NILP (result))
4787 } 4778 {
4788 else 4779 result = accum;
4789 { 4780 }
4790 /* Add to the end, avoiding the need to call nreverse 4781 /* Only check a given stretch of result for well-formedness
4791 once we're done: */ 4782 once: */
4792 XSETCDR (accum, Fcons (funcalled, Qnil)); 4783 nconcing[0] = funcalled;
4793 accum = XCDR (accum); 4784 }
4794 } 4785 else if (NILP (accum))
4786 {
4787 accum = result = Fcons (funcalled, Qnil);
4788 }
4789 else
4790 {
4791 /* Add to the end, avoiding the need to call nreverse
4792 once we're done: */
4793 XSETCDR (accum, Fcons (funcalled, Qnil));
4794 accum = XCDR (accum);
4795 } 4795 }
4796 4796
4797 if (++called_count % CIRCULAR_LIST_SUSPICION_LENGTH) continue; 4797 if (++called_count > CIRCULAR_LIST_SUSPICION_LENGTH)
4798 4798 {
4799 for (j = 0; j < nlists; ++j) 4799 if (called_count & 1)
4800 { 4800 {
4801 EXTERNAL_LIST_LOOP_1 (lists[j]) 4801 for (j = 0; j < nlists; ++j)
4802 { 4802 {
4803 /* Just check the lists aren't circular, using the 4803 tortoises[j] = XCDR (tortoises[j]);
4804 EXTERNAL_LIST_LOOP_1 macro. */ 4804 if (EQ (lists[j], tortoises[j]))
4805 } 4805 {
4806 } 4806 signal_circular_list_error (lists[j]);
4807 } 4807 }
4808 4808 }
4809 if (!maplp) 4809 }
4810 { 4810 else
4811 result = XCDR (result); 4811 {
4812 for (j = 0; j < nlists; ++j)
4813 {
4814 if (EQ (lists[j], tortoises[j]))
4815 {
4816 signal_circular_list_error (lists[j]);
4817 }
4818 }
4819 }
4820 }
4812 } 4821 }
4813 4822
4814 RETURN_UNGCPRO (result); 4823 RETURN_UNGCPRO (result);
4815 } 4824 }
4816 4825
4821 4830
4822 arguments: (FUNCTION LIST &rest LISTS) 4831 arguments: (FUNCTION LIST &rest LISTS)
4823 */ 4832 */
4824 (int nargs, Lisp_Object *args)) 4833 (int nargs, Lisp_Object *args))
4825 { 4834 {
4826 return maplist (args[0], nargs - 1, args + 1, 0, 0); 4835 return maplist (args[0], nargs - 1, args + 1, Qmaplist);
4827 } 4836 }
4828 4837
4829 DEFUN ("mapl", Fmapl, 2, MANY, 0, /* 4838 DEFUN ("mapl", Fmapl, 2, MANY, 0, /*
4830 Like `maplist', but do not accumulate values returned by the function. 4839 Like `maplist', but do not accumulate values returned by the function.
4831 4840
4832 arguments: (FUNCTION LIST &rest LISTS) 4841 arguments: (FUNCTION LIST &rest LISTS)
4833 */ 4842 */
4834 (int nargs, Lisp_Object *args)) 4843 (int nargs, Lisp_Object *args))
4835 { 4844 {
4836 return maplist (args[0], nargs - 1, args + 1, 1, 0); 4845 return maplist (args[0], nargs - 1, args + 1, Qmapl);
4837 } 4846 }
4838 4847
4839 DEFUN ("mapcon", Fmapcon, 2, MANY, 0, /* 4848 DEFUN ("mapcon", Fmapcon, 2, MANY, 0, /*
4840 Like `maplist', but chains together the values returned by FUNCTION. 4849 Like `maplist', but chains together the values returned by FUNCTION.
4841 4850
4844 4853
4845 arguments: (FUNCTION LIST &rest LISTS) 4854 arguments: (FUNCTION LIST &rest LISTS)
4846 */ 4855 */
4847 (int nargs, Lisp_Object *args)) 4856 (int nargs, Lisp_Object *args))
4848 { 4857 {
4849 return maplist (args[0], nargs - 1, args + 1, 0, 1); 4858 return maplist (args[0], nargs - 1, args + 1, Qmapcon);
4850 } 4859 }
4851 4860
4852 /* Extra random functions */ 4861 /* Extra random functions */
4853 4862
4854 DEFUN ("reduce", Freduce, 2, MANY, 0, /* 4863 DEFUN ("reduce", Freduce, 2, MANY, 0, /*
5147 Boolint need_accum = 0; 5156 Boolint need_accum = 0;
5148 Lisp_Object *subsequence = NULL; 5157 Lisp_Object *subsequence = NULL;
5149 Elemcount counting = 0, len = 0; 5158 Elemcount counting = 0, len = 0;
5150 struct gcpro gcpro1; 5159 struct gcpro gcpro1;
5151 5160
5152 if (ending - starting && starting < ending && EMACS_INT_MAX == ending) 5161 if (ending - starting && starting < ending
5162 && EMACS_INT_MAX == ending)
5153 { 5163 {
5154 ending = XINT (Flength (sequence)); 5164 ending = XINT (Flength (sequence));
5155 } 5165 }
5156 5166
5157 /* :from-end with a list; make an alloca copy of the relevant list 5167 /* :from-end with a list; make an alloca copy of the relevant list
5913 DEFSYMBOL (Qstring); 5923 DEFSYMBOL (Qstring);
5914 DEFSYMBOL (Qlist); 5924 DEFSYMBOL (Qlist);
5915 DEFSYMBOL (Qbit_vector); 5925 DEFSYMBOL (Qbit_vector);
5916 defsymbol (&QsortX, "sort*"); 5926 defsymbol (&QsortX, "sort*");
5917 DEFSYMBOL (Qreduce); 5927 DEFSYMBOL (Qreduce);
5928
5929 DEFSYMBOL (Qmapconcat);
5930 defsymbol (&QmapcarX, "mapcar*");
5931 DEFSYMBOL (Qmapvector);
5932 DEFSYMBOL (Qmapcan);
5933 DEFSYMBOL (Qmapc);
5934 DEFSYMBOL (Qmap);
5935 DEFSYMBOL (Qmap_into);
5936 DEFSYMBOL (Qsome);
5937 DEFSYMBOL (Qevery);
5938 DEFSYMBOL (Qmaplist);
5939 DEFSYMBOL (Qmapl);
5940 DEFSYMBOL (Qmapcon);
5918 5941
5919 DEFKEYWORD (Q_from_end); 5942 DEFKEYWORD (Q_from_end);
5920 DEFKEYWORD (Q_initial_value); 5943 DEFKEYWORD (Q_initial_value);
5921 5944
5922 DEFSYMBOL (Qyes_or_no_p); 5945 DEFSYMBOL (Qyes_or_no_p);