diff options
| author | Paul Eggert | 2017-02-05 13:25:37 -0800 |
|---|---|---|
| committer | Paul Eggert | 2017-02-05 13:30:29 -0800 |
| commit | 14dd9101ec4838f75addf25bf6b06ef33f8a7e97 (patch) | |
| tree | 500eb382ab23735af5b722b5ad7882c05bdfa965 /src | |
| parent | b7fa6b1f1cee9d1b71553fa665843774d2e5cf3d (diff) | |
| download | emacs-14dd9101ec4838f75addf25bf6b06ef33f8a7e97.tar.gz emacs-14dd9101ec4838f75addf25bf6b06ef33f8a7e97.zip | |
Signal list cycles in ‘length’ etc.
Use macros like FOR_EACH_TAIL instead of maybe_quit to
catch list cycles automatically instead of relying on the
user becoming impatient and typing C-g (Bug#25606).
* src/fns.c (Flength, Fmember, Fmemq, Fmemql, Fassq, Fassoc, Frassq)
(Frassoc, Fdelete, Freverse):
Use FOR_EACH_TAIL instead of maybe_quit.
(Fnreverse): Use simple EQ to check for circular list instead
of rarely_quit, as this suffices in this unusual case.
(Fplist_put, Flax_plist_put, Flax_plist_put):
Use FOR_EACH_TAIL_CONS instead of maybe_quit.
(internal_equal): Use FOR_EACH_TAIL_CONS to check lists, instead
of by-hand tail recursion that did not catch cycles.
* src/fns.c (Fsafe_length, Fplist_get):
* src/xdisp.c (display_mode_element):
Use FOR_EACH_TAIL_SAFE instead of by-hand Floyd’s algorithm.
* src/lisp.h (QUIT_COUNT_HEURISTIC): Remove; no longer needed.
(rarely_quit): Simply count toward USHRT_MAX + 1, since the
fancier versions are no longer needed.
(FOR_EACH_TAIL_CONS, FOR_EACH_TAIL_SAFE)
(FOR_EACH_TAIL_INTERNAL): New macros, the last with definiens
mostly taken from FOR_EACH_TAIL.
(FOR_EACH_TAIL): Rewrite in terms of FOR_EACH_TAIL_INTERNAL.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 290 | ||||
| -rw-r--r-- | src/lisp.h | 35 | ||||
| -rw-r--r-- | src/xdisp.c | 37 |
3 files changed, 146 insertions, 216 deletions
| @@ -108,23 +108,11 @@ To get the number of bytes, use `string-bytes'. */) | |||
| 108 | XSETFASTINT (val, ASIZE (sequence) & PSEUDOVECTOR_SIZE_MASK); | 108 | XSETFASTINT (val, ASIZE (sequence) & PSEUDOVECTOR_SIZE_MASK); |
| 109 | else if (CONSP (sequence)) | 109 | else if (CONSP (sequence)) |
| 110 | { | 110 | { |
| 111 | EMACS_INT i = 0; | 111 | intptr_t i = 0; |
| 112 | 112 | FOR_EACH_TAIL (sequence) | |
| 113 | do | 113 | i++; |
| 114 | { | 114 | if (MOST_POSITIVE_FIXNUM < i) |
| 115 | ++i; | 115 | error ("List too long"); |
| 116 | if ((i & (QUIT_COUNT_HEURISTIC - 1)) == 0) | ||
| 117 | { | ||
| 118 | if (MOST_POSITIVE_FIXNUM < i) | ||
| 119 | error ("List too long"); | ||
| 120 | maybe_quit (); | ||
| 121 | } | ||
| 122 | sequence = XCDR (sequence); | ||
| 123 | } | ||
| 124 | while (CONSP (sequence)); | ||
| 125 | |||
| 126 | CHECK_LIST_END (sequence, sequence); | ||
| 127 | |||
| 128 | val = make_number (i); | 116 | val = make_number (i); |
| 129 | } | 117 | } |
| 130 | else if (NILP (sequence)) | 118 | else if (NILP (sequence)) |
| @@ -142,38 +130,10 @@ it returns 0. If LIST is circular, it returns a finite value | |||
| 142 | which is at least the number of distinct elements. */) | 130 | which is at least the number of distinct elements. */) |
| 143 | (Lisp_Object list) | 131 | (Lisp_Object list) |
| 144 | { | 132 | { |
| 145 | Lisp_Object tail, halftail; | 133 | intptr_t len = 0; |
| 146 | double hilen = 0; | 134 | FOR_EACH_TAIL_SAFE (list) |
| 147 | uintmax_t lolen = 1; | 135 | len++; |
| 148 | 136 | return make_fixnum_or_float (len); | |
| 149 | if (! CONSP (list)) | ||
| 150 | return make_number (0); | ||
| 151 | |||
| 152 | /* halftail is used to detect circular lists. */ | ||
| 153 | for (tail = halftail = list; ; ) | ||
| 154 | { | ||
| 155 | tail = XCDR (tail); | ||
| 156 | if (! CONSP (tail)) | ||
| 157 | break; | ||
| 158 | if (EQ (tail, halftail)) | ||
| 159 | break; | ||
| 160 | lolen++; | ||
| 161 | if ((lolen & 1) == 0) | ||
| 162 | { | ||
| 163 | halftail = XCDR (halftail); | ||
| 164 | if ((lolen & (QUIT_COUNT_HEURISTIC - 1)) == 0) | ||
| 165 | { | ||
| 166 | maybe_quit (); | ||
| 167 | if (lolen == 0) | ||
| 168 | hilen += UINTMAX_MAX + 1.0; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | /* If the length does not fit into a fixnum, return a float. | ||
| 174 | On all known practical machines this returns an upper bound on | ||
| 175 | the true length. */ | ||
| 176 | return hilen ? make_float (hilen + lolen) : make_fixnum_or_float (lolen); | ||
| 177 | } | 137 | } |
| 178 | 138 | ||
| 179 | DEFUN ("string-bytes", Fstring_bytes, Sstring_bytes, 1, 1, 0, | 139 | DEFUN ("string-bytes", Fstring_bytes, Sstring_bytes, 1, 1, 0, |
| @@ -1383,15 +1343,9 @@ DEFUN ("member", Fmember, Smember, 2, 2, 0, | |||
| 1383 | The value is actually the tail of LIST whose car is ELT. */) | 1343 | The value is actually the tail of LIST whose car is ELT. */) |
| 1384 | (Lisp_Object elt, Lisp_Object list) | 1344 | (Lisp_Object elt, Lisp_Object list) |
| 1385 | { | 1345 | { |
| 1386 | unsigned short int quit_count = 0; | 1346 | FOR_EACH_TAIL (list) |
| 1387 | Lisp_Object tail; | 1347 | if (! NILP (Fequal (elt, XCAR (li.tail)))) |
| 1388 | for (tail = list; CONSP (tail); tail = XCDR (tail)) | 1348 | return li.tail; |
| 1389 | { | ||
| 1390 | if (! NILP (Fequal (elt, XCAR (tail)))) | ||
| 1391 | return tail; | ||
| 1392 | rarely_quit (++quit_count); | ||
| 1393 | } | ||
| 1394 | CHECK_LIST_END (tail, list); | ||
| 1395 | return Qnil; | 1349 | return Qnil; |
| 1396 | } | 1350 | } |
| 1397 | 1351 | ||
| @@ -1400,15 +1354,9 @@ DEFUN ("memq", Fmemq, Smemq, 2, 2, 0, | |||
| 1400 | The value is actually the tail of LIST whose car is ELT. */) | 1354 | The value is actually the tail of LIST whose car is ELT. */) |
| 1401 | (Lisp_Object elt, Lisp_Object list) | 1355 | (Lisp_Object elt, Lisp_Object list) |
| 1402 | { | 1356 | { |
| 1403 | unsigned short int quit_count = 0; | 1357 | FOR_EACH_TAIL (list) |
| 1404 | Lisp_Object tail; | 1358 | if (EQ (XCAR (li.tail), elt)) |
| 1405 | for (tail = list; CONSP (tail); tail = XCDR (tail)) | 1359 | return li.tail; |
| 1406 | { | ||
| 1407 | if (EQ (XCAR (tail), elt)) | ||
| 1408 | return tail; | ||
| 1409 | rarely_quit (++quit_count); | ||
| 1410 | } | ||
| 1411 | CHECK_LIST_END (tail, list); | ||
| 1412 | return Qnil; | 1360 | return Qnil; |
| 1413 | } | 1361 | } |
| 1414 | 1362 | ||
| @@ -1420,16 +1368,12 @@ The value is actually the tail of LIST whose car is ELT. */) | |||
| 1420 | if (!FLOATP (elt)) | 1368 | if (!FLOATP (elt)) |
| 1421 | return Fmemq (elt, list); | 1369 | return Fmemq (elt, list); |
| 1422 | 1370 | ||
| 1423 | unsigned short int quit_count = 0; | 1371 | FOR_EACH_TAIL (list) |
| 1424 | Lisp_Object tail; | ||
| 1425 | for (tail = list; CONSP (tail); tail = XCDR (tail)) | ||
| 1426 | { | 1372 | { |
| 1427 | Lisp_Object tem = XCAR (tail); | 1373 | Lisp_Object tem = XCAR (li.tail); |
| 1428 | if (FLOATP (tem) && internal_equal (elt, tem, 0, 0, Qnil)) | 1374 | if (FLOATP (tem) && internal_equal (elt, tem, 0, 0, Qnil)) |
| 1429 | return tail; | 1375 | return li.tail; |
| 1430 | rarely_quit (++quit_count); | ||
| 1431 | } | 1376 | } |
| 1432 | CHECK_LIST_END (tail, list); | ||
| 1433 | return Qnil; | 1377 | return Qnil; |
| 1434 | } | 1378 | } |
| 1435 | 1379 | ||
| @@ -1439,15 +1383,9 @@ The value is actually the first element of LIST whose car is KEY. | |||
| 1439 | Elements of LIST that are not conses are ignored. */) | 1383 | Elements of LIST that are not conses are ignored. */) |
| 1440 | (Lisp_Object key, Lisp_Object list) | 1384 | (Lisp_Object key, Lisp_Object list) |
| 1441 | { | 1385 | { |
| 1442 | unsigned short int quit_count = 0; | 1386 | FOR_EACH_TAIL (list) |
| 1443 | Lisp_Object tail; | 1387 | if (CONSP (XCAR (li.tail)) && EQ (XCAR (XCAR (li.tail)), key)) |
| 1444 | for (tail = list; CONSP (tail); tail = XCDR (tail)) | 1388 | return XCAR (li.tail); |
| 1445 | { | ||
| 1446 | if (CONSP (XCAR (tail)) && EQ (XCAR (XCAR (tail)), key)) | ||
| 1447 | return XCAR (tail); | ||
| 1448 | rarely_quit (++quit_count); | ||
| 1449 | } | ||
| 1450 | CHECK_LIST_END (tail, list); | ||
| 1451 | return Qnil; | 1389 | return Qnil; |
| 1452 | } | 1390 | } |
| 1453 | 1391 | ||
| @@ -1468,17 +1406,13 @@ DEFUN ("assoc", Fassoc, Sassoc, 2, 2, 0, | |||
| 1468 | The value is actually the first element of LIST whose car equals KEY. */) | 1406 | The value is actually the first element of LIST whose car equals KEY. */) |
| 1469 | (Lisp_Object key, Lisp_Object list) | 1407 | (Lisp_Object key, Lisp_Object list) |
| 1470 | { | 1408 | { |
| 1471 | unsigned short int quit_count = 0; | 1409 | FOR_EACH_TAIL (list) |
| 1472 | Lisp_Object tail; | ||
| 1473 | for (tail = list; CONSP (tail); tail = XCDR (tail)) | ||
| 1474 | { | 1410 | { |
| 1475 | Lisp_Object car = XCAR (tail); | 1411 | Lisp_Object car = XCAR (li.tail); |
| 1476 | if (CONSP (car) | 1412 | if (CONSP (car) |
| 1477 | && (EQ (XCAR (car), key) || !NILP (Fequal (XCAR (car), key)))) | 1413 | && (EQ (XCAR (car), key) || !NILP (Fequal (XCAR (car), key)))) |
| 1478 | return car; | 1414 | return car; |
| 1479 | rarely_quit (++quit_count); | ||
| 1480 | } | 1415 | } |
| 1481 | CHECK_LIST_END (tail, list); | ||
| 1482 | return Qnil; | 1416 | return Qnil; |
| 1483 | } | 1417 | } |
| 1484 | 1418 | ||
| @@ -1503,15 +1437,9 @@ DEFUN ("rassq", Frassq, Srassq, 2, 2, 0, | |||
| 1503 | The value is actually the first element of LIST whose cdr is KEY. */) | 1437 | The value is actually the first element of LIST whose cdr is KEY. */) |
| 1504 | (Lisp_Object key, Lisp_Object list) | 1438 | (Lisp_Object key, Lisp_Object list) |
| 1505 | { | 1439 | { |
| 1506 | unsigned short int quit_count = 0; | 1440 | FOR_EACH_TAIL (list) |
| 1507 | Lisp_Object tail; | 1441 | if (CONSP (XCAR (li.tail)) && EQ (XCDR (XCAR (li.tail)), key)) |
| 1508 | for (tail = list; CONSP (tail); tail = XCDR (tail)) | 1442 | return XCAR (li.tail); |
| 1509 | { | ||
| 1510 | if (CONSP (XCAR (tail)) && EQ (XCDR (XCAR (tail)), key)) | ||
| 1511 | return XCAR (tail); | ||
| 1512 | rarely_quit (++quit_count); | ||
| 1513 | } | ||
| 1514 | CHECK_LIST_END (tail, list); | ||
| 1515 | return Qnil; | 1443 | return Qnil; |
| 1516 | } | 1444 | } |
| 1517 | 1445 | ||
| @@ -1520,17 +1448,13 @@ DEFUN ("rassoc", Frassoc, Srassoc, 2, 2, 0, | |||
| 1520 | The value is actually the first element of LIST whose cdr equals KEY. */) | 1448 | The value is actually the first element of LIST whose cdr equals KEY. */) |
| 1521 | (Lisp_Object key, Lisp_Object list) | 1449 | (Lisp_Object key, Lisp_Object list) |
| 1522 | { | 1450 | { |
| 1523 | unsigned short int quit_count = 0; | 1451 | FOR_EACH_TAIL (list) |
| 1524 | Lisp_Object tail; | ||
| 1525 | for (tail = list; CONSP (tail); tail = XCDR (tail)) | ||
| 1526 | { | 1452 | { |
| 1527 | Lisp_Object car = XCAR (tail); | 1453 | Lisp_Object car = XCAR (li.tail); |
| 1528 | if (CONSP (car) | 1454 | if (CONSP (car) |
| 1529 | && (EQ (XCDR (car), key) || !NILP (Fequal (XCDR (car), key)))) | 1455 | && (EQ (XCDR (car), key) || !NILP (Fequal (XCDR (car), key)))) |
| 1530 | return car; | 1456 | return car; |
| 1531 | rarely_quit (++quit_count); | ||
| 1532 | } | 1457 | } |
| 1533 | CHECK_LIST_END (tail, list); | ||
| 1534 | return Qnil; | 1458 | return Qnil; |
| 1535 | } | 1459 | } |
| 1536 | 1460 | ||
| @@ -1668,23 +1592,20 @@ changing the value of a sequence `foo'. */) | |||
| 1668 | } | 1592 | } |
| 1669 | else | 1593 | else |
| 1670 | { | 1594 | { |
| 1671 | unsigned short int quit_count = 0; | 1595 | Lisp_Object prev = Qnil; |
| 1672 | Lisp_Object tail, prev; | ||
| 1673 | 1596 | ||
| 1674 | for (tail = seq, prev = Qnil; CONSP (tail); tail = XCDR (tail)) | 1597 | FOR_EACH_TAIL (seq) |
| 1675 | { | 1598 | { |
| 1676 | if (!NILP (Fequal (elt, XCAR (tail)))) | 1599 | if (!NILP (Fequal (elt, (XCAR (li.tail))))) |
| 1677 | { | 1600 | { |
| 1678 | if (NILP (prev)) | 1601 | if (NILP (prev)) |
| 1679 | seq = XCDR (tail); | 1602 | seq = XCDR (li.tail); |
| 1680 | else | 1603 | else |
| 1681 | Fsetcdr (prev, XCDR (tail)); | 1604 | Fsetcdr (prev, XCDR (li.tail)); |
| 1682 | } | 1605 | } |
| 1683 | else | 1606 | else |
| 1684 | prev = tail; | 1607 | prev = li.tail; |
| 1685 | rarely_quit (++quit_count); | ||
| 1686 | } | 1608 | } |
| 1687 | CHECK_LIST_END (tail, seq); | ||
| 1688 | } | 1609 | } |
| 1689 | 1610 | ||
| 1690 | return seq; | 1611 | return seq; |
| @@ -1702,15 +1623,17 @@ This function may destructively modify SEQ to produce the value. */) | |||
| 1702 | return Freverse (seq); | 1623 | return Freverse (seq); |
| 1703 | else if (CONSP (seq)) | 1624 | else if (CONSP (seq)) |
| 1704 | { | 1625 | { |
| 1705 | unsigned short int quit_count = 0; | ||
| 1706 | Lisp_Object prev, tail, next; | 1626 | Lisp_Object prev, tail, next; |
| 1707 | 1627 | ||
| 1708 | for (prev = Qnil, tail = seq; CONSP (tail); tail = next) | 1628 | for (prev = Qnil, tail = seq; CONSP (tail); tail = next) |
| 1709 | { | 1629 | { |
| 1710 | next = XCDR (tail); | 1630 | next = XCDR (tail); |
| 1631 | /* If SEQ contains a cycle, attempting to reverse it | ||
| 1632 | in-place will inevitably come back to SEQ. */ | ||
| 1633 | if (EQ (next, seq)) | ||
| 1634 | circular_list (seq); | ||
| 1711 | Fsetcdr (tail, prev); | 1635 | Fsetcdr (tail, prev); |
| 1712 | prev = tail; | 1636 | prev = tail; |
| 1713 | rarely_quit (++quit_count); | ||
| 1714 | } | 1637 | } |
| 1715 | CHECK_LIST_END (tail, seq); | 1638 | CHECK_LIST_END (tail, seq); |
| 1716 | seq = prev; | 1639 | seq = prev; |
| @@ -1753,13 +1676,9 @@ See also the function `nreverse', which is used more often. */) | |||
| 1753 | return Qnil; | 1676 | return Qnil; |
| 1754 | else if (CONSP (seq)) | 1677 | else if (CONSP (seq)) |
| 1755 | { | 1678 | { |
| 1756 | unsigned short int quit_count = 0; | 1679 | new = Qnil; |
| 1757 | for (new = Qnil; CONSP (seq); seq = XCDR (seq)) | 1680 | FOR_EACH_TAIL (seq) |
| 1758 | { | 1681 | new = Fcons (XCAR (li.tail), new); |
| 1759 | new = Fcons (XCAR (seq), new); | ||
| 1760 | rarely_quit (++quit_count); | ||
| 1761 | } | ||
| 1762 | CHECK_LIST_END (seq, seq); | ||
| 1763 | } | 1682 | } |
| 1764 | else if (VECTORP (seq)) | 1683 | else if (VECTORP (seq)) |
| 1765 | { | 1684 | { |
| @@ -2011,18 +1930,14 @@ corresponding to the given PROP, or nil if PROP is not one of the | |||
| 2011 | properties on the list. This function never signals an error. */) | 1930 | properties on the list. This function never signals an error. */) |
| 2012 | (Lisp_Object plist, Lisp_Object prop) | 1931 | (Lisp_Object plist, Lisp_Object prop) |
| 2013 | { | 1932 | { |
| 2014 | Lisp_Object tail, halftail; | 1933 | FOR_EACH_TAIL_SAFE (plist) |
| 2015 | |||
| 2016 | /* halftail is used to detect circular lists. */ | ||
| 2017 | tail = halftail = plist; | ||
| 2018 | while (CONSP (tail) && CONSP (XCDR (tail))) | ||
| 2019 | { | 1934 | { |
| 2020 | if (EQ (prop, XCAR (tail))) | 1935 | if (! CONSP (XCDR (li.tail))) |
| 2021 | return XCAR (XCDR (tail)); | 1936 | break; |
| 2022 | 1937 | if (EQ (prop, XCAR (li.tail))) | |
| 2023 | tail = XCDR (XCDR (tail)); | 1938 | return XCAR (XCDR (li.tail)); |
| 2024 | halftail = XCDR (halftail); | 1939 | li.tail = XCDR (li.tail); |
| 2025 | if (EQ (tail, halftail)) | 1940 | if (EQ (li.tail, li.tortoise)) |
| 2026 | break; | 1941 | break; |
| 2027 | } | 1942 | } |
| 2028 | 1943 | ||
| @@ -2048,19 +1963,22 @@ use `(setq x (plist-put x prop val))' to be sure to use the new value. | |||
| 2048 | The PLIST is modified by side effects. */) | 1963 | The PLIST is modified by side effects. */) |
| 2049 | (Lisp_Object plist, Lisp_Object prop, Lisp_Object val) | 1964 | (Lisp_Object plist, Lisp_Object prop, Lisp_Object val) |
| 2050 | { | 1965 | { |
| 2051 | unsigned short int quit_count = 0; | ||
| 2052 | Lisp_Object prev = Qnil; | 1966 | Lisp_Object prev = Qnil; |
| 2053 | for (Lisp_Object tail = plist; CONSP (tail) && CONSP (XCDR (tail)); | 1967 | FOR_EACH_TAIL_CONS (plist) |
| 2054 | tail = XCDR (XCDR (tail))) | ||
| 2055 | { | 1968 | { |
| 2056 | if (EQ (prop, XCAR (tail))) | 1969 | if (! CONSP (XCDR (li.tail))) |
| 1970 | break; | ||
| 1971 | |||
| 1972 | if (EQ (prop, XCAR (li.tail))) | ||
| 2057 | { | 1973 | { |
| 2058 | Fsetcar (XCDR (tail), val); | 1974 | Fsetcar (XCDR (li.tail), val); |
| 2059 | return plist; | 1975 | return plist; |
| 2060 | } | 1976 | } |
| 2061 | 1977 | ||
| 2062 | prev = tail; | 1978 | prev = li.tail; |
| 2063 | rarely_quit (++quit_count); | 1979 | li.tail = XCDR (li.tail); |
| 1980 | if (EQ (li.tail, li.tortoise)) | ||
| 1981 | circular_list (plist); | ||
| 2064 | } | 1982 | } |
| 2065 | Lisp_Object newcell | 1983 | Lisp_Object newcell |
| 2066 | = Fcons (prop, Fcons (val, NILP (prev) ? plist : XCDR (XCDR (prev)))); | 1984 | = Fcons (prop, Fcons (val, NILP (prev) ? plist : XCDR (XCDR (prev)))); |
| @@ -2089,20 +2007,16 @@ corresponding to the given PROP, or nil if PROP is not | |||
| 2089 | one of the properties on the list. */) | 2007 | one of the properties on the list. */) |
| 2090 | (Lisp_Object plist, Lisp_Object prop) | 2008 | (Lisp_Object plist, Lisp_Object prop) |
| 2091 | { | 2009 | { |
| 2092 | unsigned short int quit_count = 0; | 2010 | FOR_EACH_TAIL_CONS (plist) |
| 2093 | Lisp_Object tail; | ||
| 2094 | |||
| 2095 | for (tail = plist; | ||
| 2096 | CONSP (tail) && CONSP (XCDR (tail)); | ||
| 2097 | tail = XCDR (XCDR (tail))) | ||
| 2098 | { | 2011 | { |
| 2099 | if (! NILP (Fequal (prop, XCAR (tail)))) | 2012 | if (! CONSP (XCDR (li.tail))) |
| 2100 | return XCAR (XCDR (tail)); | 2013 | break; |
| 2101 | rarely_quit (++quit_count); | 2014 | if (! NILP (Fequal (prop, XCAR (li.tail)))) |
| 2015 | return XCAR (XCDR (li.tail)); | ||
| 2016 | li.tail = XCDR (li.tail); | ||
| 2017 | if (EQ (li.tail, li.tortoise)) | ||
| 2018 | circular_list (plist); | ||
| 2102 | } | 2019 | } |
| 2103 | |||
| 2104 | CHECK_LIST_END (tail, prop); | ||
| 2105 | |||
| 2106 | return Qnil; | 2020 | return Qnil; |
| 2107 | } | 2021 | } |
| 2108 | 2022 | ||
| @@ -2116,19 +2030,22 @@ use `(setq x (lax-plist-put x prop val))' to be sure to use the new value. | |||
| 2116 | The PLIST is modified by side effects. */) | 2030 | The PLIST is modified by side effects. */) |
| 2117 | (Lisp_Object plist, Lisp_Object prop, Lisp_Object val) | 2031 | (Lisp_Object plist, Lisp_Object prop, Lisp_Object val) |
| 2118 | { | 2032 | { |
| 2119 | unsigned short int quit_count = 0; | ||
| 2120 | Lisp_Object prev = Qnil; | 2033 | Lisp_Object prev = Qnil; |
| 2121 | for (Lisp_Object tail = plist; CONSP (tail) && CONSP (XCDR (tail)); | 2034 | FOR_EACH_TAIL_CONS (plist) |
| 2122 | tail = XCDR (XCDR (tail))) | ||
| 2123 | { | 2035 | { |
| 2124 | if (! NILP (Fequal (prop, XCAR (tail)))) | 2036 | if (! CONSP (XCDR (li.tail))) |
| 2037 | break; | ||
| 2038 | |||
| 2039 | if (! NILP (Fequal (prop, XCAR (li.tail)))) | ||
| 2125 | { | 2040 | { |
| 2126 | Fsetcar (XCDR (tail), val); | 2041 | Fsetcar (XCDR (li.tail), val); |
| 2127 | return plist; | 2042 | return plist; |
| 2128 | } | 2043 | } |
| 2129 | 2044 | ||
| 2130 | prev = tail; | 2045 | prev = li.tail; |
| 2131 | rarely_quit (++quit_count); | 2046 | li.tail = XCDR (li.tail); |
| 2047 | if (EQ (li.tail, li.tortoise)) | ||
| 2048 | circular_list (plist); | ||
| 2132 | } | 2049 | } |
| 2133 | Lisp_Object newcell = list2 (prop, val); | 2050 | Lisp_Object newcell = list2 (prop, val); |
| 2134 | if (NILP (prev)) | 2051 | if (NILP (prev)) |
| @@ -2206,9 +2123,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props, | |||
| 2206 | } | 2123 | } |
| 2207 | } | 2124 | } |
| 2208 | 2125 | ||
| 2209 | unsigned short int quit_count = 0; | ||
| 2210 | tail_recurse: | 2126 | tail_recurse: |
| 2211 | rarely_quit (++quit_count); | ||
| 2212 | if (EQ (o1, o2)) | 2127 | if (EQ (o1, o2)) |
| 2213 | return 1; | 2128 | return 1; |
| 2214 | if (XTYPE (o1) != XTYPE (o2)) | 2129 | if (XTYPE (o1) != XTYPE (o2)) |
| @@ -2228,12 +2143,24 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props, | |||
| 2228 | } | 2143 | } |
| 2229 | 2144 | ||
| 2230 | case Lisp_Cons: | 2145 | case Lisp_Cons: |
| 2231 | if (!internal_equal (XCAR (o1), XCAR (o2), depth + 1, props, ht)) | 2146 | { |
| 2232 | return 0; | 2147 | Lisp_Object tail1 = o1; |
| 2233 | o1 = XCDR (o1); | 2148 | FOR_EACH_TAIL_CONS (o1) |
| 2234 | o2 = XCDR (o2); | 2149 | { |
| 2235 | /* FIXME: This inf-loops in a circular list! */ | 2150 | if (! CONSP (o2)) |
| 2236 | goto tail_recurse; | 2151 | return false; |
| 2152 | if (! internal_equal (XCAR (li.tail), XCAR (o2), depth + 1, | ||
| 2153 | props, ht)) | ||
| 2154 | return false; | ||
| 2155 | tail1 = XCDR (li.tail); | ||
| 2156 | o2 = XCDR (o2); | ||
| 2157 | if (EQ (tail1, o2)) | ||
| 2158 | return true; | ||
| 2159 | } | ||
| 2160 | o1 = tail1; | ||
| 2161 | depth++; | ||
| 2162 | goto tail_recurse; | ||
| 2163 | } | ||
| 2237 | 2164 | ||
| 2238 | case Lisp_Misc: | 2165 | case Lisp_Misc: |
| 2239 | if (XMISCTYPE (o1) != XMISCTYPE (o2)) | 2166 | if (XMISCTYPE (o1) != XMISCTYPE (o2)) |
| @@ -2247,6 +2174,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props, | |||
| 2247 | return 0; | 2174 | return 0; |
| 2248 | o1 = XOVERLAY (o1)->plist; | 2175 | o1 = XOVERLAY (o1)->plist; |
| 2249 | o2 = XOVERLAY (o2)->plist; | 2176 | o2 = XOVERLAY (o2)->plist; |
| 2177 | depth++; | ||
| 2250 | goto tail_recurse; | 2178 | goto tail_recurse; |
| 2251 | } | 2179 | } |
| 2252 | if (MARKERP (o1)) | 2180 | if (MARKERP (o1)) |
| @@ -2397,7 +2325,6 @@ Only the last argument is not altered, and need not be a list. | |||
| 2397 | usage: (nconc &rest LISTS) */) | 2325 | usage: (nconc &rest LISTS) */) |
| 2398 | (ptrdiff_t nargs, Lisp_Object *args) | 2326 | (ptrdiff_t nargs, Lisp_Object *args) |
| 2399 | { | 2327 | { |
| 2400 | unsigned short int quit_count = 0; | ||
| 2401 | Lisp_Object val = Qnil; | 2328 | Lisp_Object val = Qnil; |
| 2402 | 2329 | ||
| 2403 | for (ptrdiff_t argnum = 0; argnum < nargs; argnum++) | 2330 | for (ptrdiff_t argnum = 0; argnum < nargs; argnum++) |
| @@ -2413,13 +2340,8 @@ usage: (nconc &rest LISTS) */) | |||
| 2413 | CHECK_CONS (tem); | 2340 | CHECK_CONS (tem); |
| 2414 | 2341 | ||
| 2415 | Lisp_Object tail; | 2342 | Lisp_Object tail; |
| 2416 | do | 2343 | FOR_EACH_TAIL_CONS (tem) |
| 2417 | { | 2344 | tail = li.tail; |
| 2418 | tail = tem; | ||
| 2419 | tem = XCDR (tail); | ||
| 2420 | rarely_quit (++quit_count); | ||
| 2421 | } | ||
| 2422 | while (CONSP (tem)); | ||
| 2423 | 2345 | ||
| 2424 | tem = args[argnum + 1]; | 2346 | tem = args[argnum + 1]; |
| 2425 | Fsetcdr (tail, tem); | 2347 | Fsetcdr (tail, tem); |
| @@ -2841,14 +2763,20 @@ property and a property with the value nil. | |||
| 2841 | The value is actually the tail of PLIST whose car is PROP. */) | 2763 | The value is actually the tail of PLIST whose car is PROP. */) |
| 2842 | (Lisp_Object plist, Lisp_Object prop) | 2764 | (Lisp_Object plist, Lisp_Object prop) |
| 2843 | { | 2765 | { |
| 2844 | unsigned short int quit_count = 0; | 2766 | FOR_EACH_TAIL (plist) |
| 2845 | while (CONSP (plist) && !EQ (XCAR (plist), prop)) | ||
| 2846 | { | 2767 | { |
| 2847 | plist = XCDR (plist); | 2768 | if (EQ (XCAR (li.tail), prop)) |
| 2848 | plist = CDR (plist); | 2769 | return li.tail; |
| 2849 | rarely_quit (++quit_count); | 2770 | if (!CONSP (XCDR (li.tail))) |
| 2771 | { | ||
| 2772 | CHECK_LIST_END (XCDR (li.tail), plist); | ||
| 2773 | return Qnil; | ||
| 2774 | } | ||
| 2775 | li.tail = XCDR (li.tail); | ||
| 2776 | if (EQ (li.tail, li.tortoise)) | ||
| 2777 | circular_list (plist); | ||
| 2850 | } | 2778 | } |
| 2851 | return plist; | 2779 | return Qnil; |
| 2852 | } | 2780 | } |
| 2853 | 2781 | ||
| 2854 | DEFUN ("widget-put", Fwidget_put, Swidget_put, 3, 3, 0, | 2782 | DEFUN ("widget-put", Fwidget_put, Swidget_put, 3, 3, 0, |
diff --git a/src/lisp.h b/src/lisp.h index 102e8bd70ef..13fca0b29e0 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -3129,20 +3129,14 @@ extern void maybe_quit (void); | |||
| 3129 | 3129 | ||
| 3130 | #define QUITP (!NILP (Vquit_flag) && NILP (Vinhibit_quit)) | 3130 | #define QUITP (!NILP (Vquit_flag) && NILP (Vinhibit_quit)) |
| 3131 | 3131 | ||
| 3132 | /* Heuristic on how many iterations of a tight loop can be safely done | ||
| 3133 | before it's time to do a quit. This must be a power of 2. It | ||
| 3134 | is nice but not necessary for it to equal USHRT_MAX + 1. */ | ||
| 3135 | |||
| 3136 | enum { QUIT_COUNT_HEURISTIC = 1 << 16 }; | ||
| 3137 | |||
| 3138 | /* Process a quit rarely, based on a counter COUNT, for efficiency. | 3132 | /* Process a quit rarely, based on a counter COUNT, for efficiency. |
| 3139 | "Rarely" means once per QUIT_COUNT_HEURISTIC or per USHRT_MAX + 1 | 3133 | "Rarely" means once per USHRT_MAX + 1 times; this is somewhat |
| 3140 | times, whichever is smaller (somewhat arbitrary, but often faster). */ | 3134 | arbitrary, but efficient. */ |
| 3141 | 3135 | ||
| 3142 | INLINE void | 3136 | INLINE void |
| 3143 | rarely_quit (unsigned short int count) | 3137 | rarely_quit (unsigned short int count) |
| 3144 | { | 3138 | { |
| 3145 | if (! (count & (QUIT_COUNT_HEURISTIC - 1))) | 3139 | if (! count) |
| 3146 | maybe_quit (); | 3140 | maybe_quit (); |
| 3147 | } | 3141 | } |
| 3148 | 3142 | ||
| @@ -4598,13 +4592,32 @@ enum | |||
| 4598 | http://maths-people.anu.edu.au/~brent/pd/rpb051i.pdf */ | 4592 | http://maths-people.anu.edu.au/~brent/pd/rpb051i.pdf */ |
| 4599 | 4593 | ||
| 4600 | #define FOR_EACH_TAIL(list) \ | 4594 | #define FOR_EACH_TAIL(list) \ |
| 4595 | FOR_EACH_TAIL_INTERNAL (list, CHECK_LIST_END (li.tail, list), \ | ||
| 4596 | circular_list (list)) | ||
| 4597 | |||
| 4598 | /* Like FOR_EACH_TAIL (LIST), except check only for cycles. */ | ||
| 4599 | |||
| 4600 | #define FOR_EACH_TAIL_CONS(list) \ | ||
| 4601 | FOR_EACH_TAIL_INTERNAL (list, (void) 0, circular_list (list)) | ||
| 4602 | |||
| 4603 | /* Like FOR_EACH_TAIL (LIST), except check for neither dotted lists | ||
| 4604 | nor cycles. */ | ||
| 4605 | |||
| 4606 | #define FOR_EACH_TAIL_SAFE(list) \ | ||
| 4607 | FOR_EACH_TAIL_INTERNAL (list, (void) 0, (void) (li.tail = Qnil)) | ||
| 4608 | |||
| 4609 | /* Like FOR_EACH_TAIL (LIST), except evaluate DOTTED or CYCLE, | ||
| 4610 | respectively, if a dotted list or cycle is found. This is an | ||
| 4611 | internal macro intended for use only by the above macros. */ | ||
| 4612 | |||
| 4613 | #define FOR_EACH_TAIL_INTERNAL(list, dotted, cycle) \ | ||
| 4601 | for (struct { Lisp_Object tail, tortoise; intptr_t n, max; } li \ | 4614 | for (struct { Lisp_Object tail, tortoise; intptr_t n, max; } li \ |
| 4602 | = { list, list, 2, 2 }; \ | 4615 | = { list, list, 2, 2 }; \ |
| 4603 | CONSP (li.tail) || (CHECK_LIST_END (li.tail, list), false); \ | 4616 | CONSP (li.tail) || (dotted, false); \ |
| 4604 | (li.tail = XCDR (li.tail), \ | 4617 | (li.tail = XCDR (li.tail), \ |
| 4605 | (li.n-- == 0 \ | 4618 | (li.n-- == 0 \ |
| 4606 | ? (void) (li.n = li.max <<= 1, li.tortoise = li.tail) \ | 4619 | ? (void) (li.n = li.max <<= 1, li.tortoise = li.tail) \ |
| 4607 | : EQ (li.tail, li.tortoise) ? circular_list (list) : (void) 0))) | 4620 | : EQ (li.tail, li.tortoise) ? (cycle) : (void) 0))) |
| 4608 | 4621 | ||
| 4609 | /* Do a `for' loop over alist values. */ | 4622 | /* Do a `for' loop over alist values. */ |
| 4610 | 4623 | ||
diff --git a/src/xdisp.c b/src/xdisp.c index 0e329dfe6e9..5e1207f29e3 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -23033,30 +23033,19 @@ display_mode_element (struct it *it, int depth, int field_width, int precision, | |||
| 23033 | goto tail_recurse; | 23033 | goto tail_recurse; |
| 23034 | } | 23034 | } |
| 23035 | else if (STRINGP (car) || CONSP (car)) | 23035 | else if (STRINGP (car) || CONSP (car)) |
| 23036 | { | 23036 | FOR_EACH_TAIL_SAFE (elt) |
| 23037 | Lisp_Object halftail = elt; | 23037 | { |
| 23038 | int len = 0; | 23038 | if (0 < precision && precision <= n) |
| 23039 | 23039 | break; | |
| 23040 | while (CONSP (elt) | 23040 | n += display_mode_element (it, depth, |
| 23041 | && (precision <= 0 || n < precision)) | 23041 | /* Pad after only the last |
| 23042 | { | 23042 | list element. */ |
| 23043 | n += display_mode_element (it, depth, | 23043 | (! CONSP (XCDR (li.tail)) |
| 23044 | /* Do padding only after the last | 23044 | ? field_width - n |
| 23045 | element in the list. */ | 23045 | : 0), |
| 23046 | (! CONSP (XCDR (elt)) | 23046 | precision - n, XCAR (li.tail), |
| 23047 | ? field_width - n | 23047 | props, risky); |
| 23048 | : 0), | 23048 | } |
| 23049 | precision - n, XCAR (elt), | ||
| 23050 | props, risky); | ||
| 23051 | elt = XCDR (elt); | ||
| 23052 | len++; | ||
| 23053 | if ((len & 1) == 0) | ||
| 23054 | halftail = XCDR (halftail); | ||
| 23055 | /* Check for cycle. */ | ||
| 23056 | if (EQ (halftail, elt)) | ||
| 23057 | break; | ||
| 23058 | } | ||
| 23059 | } | ||
| 23060 | } | 23049 | } |
| 23061 | break; | 23050 | break; |
| 23062 | 23051 | ||