diff options
| author | Stefan Monnier | 2012-03-25 16:37:21 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2012-03-25 16:37:21 -0400 |
| commit | 699c782b7668c44d0fa4446331b0590a6d5dac82 (patch) | |
| tree | 5dcce364741d0761920a3d274b0fc8aba4103d45 /src/gmalloc.c | |
| parent | 98fb480ee31bf74cf554044f60f21df16566dd7f (diff) | |
| parent | e99a9b8bdccadded1f6fae88ee7a2a93dfd4eacf (diff) | |
| download | emacs-pending.tar.gz emacs-pending.zip | |
Merge from trunkpending
Diffstat (limited to 'src/gmalloc.c')
| -rw-r--r-- | src/gmalloc.c | 224 |
1 files changed, 66 insertions, 158 deletions
diff --git a/src/gmalloc.c b/src/gmalloc.c index a023d2d78e5..7b5e6df009b 100644 --- a/src/gmalloc.c +++ b/src/gmalloc.c | |||
| @@ -37,41 +37,17 @@ Fifth Floor, Boston, MA 02110-1301, USA. | |||
| 37 | #include <config.h> | 37 | #include <config.h> |
| 38 | #endif | 38 | #endif |
| 39 | 39 | ||
| 40 | #ifdef HAVE_GTK_AND_PTHREAD | 40 | #ifdef HAVE_PTHREAD |
| 41 | #define USE_PTHREAD | 41 | #define USE_PTHREAD |
| 42 | #endif | 42 | #endif |
| 43 | 43 | ||
| 44 | #if ((defined __cplusplus || (defined (__STDC__) && __STDC__) \ | ||
| 45 | || defined STDC_HEADERS || defined PROTOTYPES)) | ||
| 46 | #undef PP | 44 | #undef PP |
| 47 | #define PP(args) args | 45 | #define PP(args) args |
| 48 | #undef __ptr_t | 46 | #undef __ptr_t |
| 49 | #define __ptr_t void * | 47 | #define __ptr_t void * |
| 50 | #else /* Not C++ or ANSI C. */ | ||
| 51 | #undef PP | ||
| 52 | #define PP(args) () | ||
| 53 | #undef __ptr_t | ||
| 54 | #define __ptr_t char * | ||
| 55 | #endif /* C++ or ANSI C. */ | ||
| 56 | 48 | ||
| 57 | #if defined(_LIBC) || defined(STDC_HEADERS) || defined(USG) | ||
| 58 | #include <string.h> | 49 | #include <string.h> |
| 59 | #else | ||
| 60 | #ifndef memset | ||
| 61 | #define memset(s, zero, n) bzero ((s), (n)) | ||
| 62 | #endif | ||
| 63 | #ifndef memcpy | ||
| 64 | #define memcpy(d, s, n) bcopy ((s), (d), (n)) | ||
| 65 | #endif | ||
| 66 | #endif | ||
| 67 | |||
| 68 | #ifdef HAVE_LIMITS_H | ||
| 69 | #include <limits.h> | 50 | #include <limits.h> |
| 70 | #endif | ||
| 71 | #ifndef CHAR_BIT | ||
| 72 | #define CHAR_BIT 8 | ||
| 73 | #endif | ||
| 74 | |||
| 75 | #include <unistd.h> | 51 | #include <unistd.h> |
| 76 | 52 | ||
| 77 | #ifdef USE_PTHREAD | 53 | #ifdef USE_PTHREAD |
| @@ -86,26 +62,9 @@ extern "C" | |||
| 86 | { | 62 | { |
| 87 | #endif | 63 | #endif |
| 88 | 64 | ||
| 89 | #ifdef STDC_HEADERS | ||
| 90 | #include <stddef.h> | 65 | #include <stddef.h> |
| 91 | #define __malloc_size_t size_t | 66 | #define __malloc_size_t size_t |
| 92 | #define __malloc_ptrdiff_t ptrdiff_t | 67 | #define __malloc_ptrdiff_t ptrdiff_t |
| 93 | #else | ||
| 94 | #ifdef __GNUC__ | ||
| 95 | #include <stddef.h> | ||
| 96 | #ifdef __SIZE_TYPE__ | ||
| 97 | #define __malloc_size_t __SIZE_TYPE__ | ||
| 98 | #endif | ||
| 99 | #endif | ||
| 100 | #ifndef __malloc_size_t | ||
| 101 | #define __malloc_size_t unsigned int | ||
| 102 | #endif | ||
| 103 | #define __malloc_ptrdiff_t int | ||
| 104 | #endif | ||
| 105 | |||
| 106 | #ifndef NULL | ||
| 107 | #define NULL 0 | ||
| 108 | #endif | ||
| 109 | 68 | ||
| 110 | 69 | ||
| 111 | /* Allocate SIZE bytes of memory. */ | 70 | /* Allocate SIZE bytes of memory. */ |
| @@ -143,7 +102,7 @@ extern void malloc_enable_thread PP ((void)); | |||
| 143 | receive a fragment of a block. Fragment sizes are powers of two, | 102 | receive a fragment of a block. Fragment sizes are powers of two, |
| 144 | and all fragments of a block are the same size. When all the | 103 | and all fragments of a block are the same size. When all the |
| 145 | fragments in a block have been freed, the block itself is freed. */ | 104 | fragments in a block have been freed, the block itself is freed. */ |
| 146 | #define INT_BIT (CHAR_BIT * sizeof(int)) | 105 | #define INT_BIT (CHAR_BIT * sizeof (int)) |
| 147 | #define BLOCKLOG (INT_BIT > 16 ? 12 : 9) | 106 | #define BLOCKLOG (INT_BIT > 16 ? 12 : 9) |
| 148 | #define BLOCKSIZE (1 << BLOCKLOG) | 107 | #define BLOCKSIZE (1 << BLOCKLOG) |
| 149 | #define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE) | 108 | #define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE) |
| @@ -392,10 +351,21 @@ Fifth Floor, Boston, MA 02110-1301, USA. | |||
| 392 | #endif | 351 | #endif |
| 393 | #include <errno.h> | 352 | #include <errno.h> |
| 394 | 353 | ||
| 395 | /* How to really get more memory. */ | 354 | /* On Cygwin there are two heaps. temacs uses the static heap |
| 396 | #if defined(CYGWIN) | 355 | (defined in sheap.c and managed with bss_sbrk), and the dumped |
| 356 | emacs uses the Cygwin heap (managed with sbrk). When emacs starts | ||
| 357 | on Cygwin, it reinitializes malloc, and we save the old info for | ||
| 358 | use by free and realloc if they're called with a pointer into the | ||
| 359 | static heap. | ||
| 360 | |||
| 361 | Currently (2011-08-16) the Cygwin build doesn't use ralloc.c; if | ||
| 362 | this is changed in the future, we'll have to similarly deal with | ||
| 363 | reinitializing ralloc. */ | ||
| 364 | #ifdef CYGWIN | ||
| 397 | extern __ptr_t bss_sbrk PP ((ptrdiff_t __size)); | 365 | extern __ptr_t bss_sbrk PP ((ptrdiff_t __size)); |
| 398 | extern int bss_sbrk_did_unexec; | 366 | extern int bss_sbrk_did_unexec; |
| 367 | char *bss_sbrk_heapbase; /* _heapbase for static heap */ | ||
| 368 | malloc_info *bss_sbrk_heapinfo; /* _heapinfo for static heap */ | ||
| 399 | #endif | 369 | #endif |
| 400 | __ptr_t (*__morecore) PP ((__malloc_ptrdiff_t __size)) = __default_morecore; | 370 | __ptr_t (*__morecore) PP ((__malloc_ptrdiff_t __size)) = __default_morecore; |
| 401 | 371 | ||
| @@ -475,7 +445,7 @@ protect_malloc_state (protect_p) | |||
| 475 | } | 445 | } |
| 476 | } | 446 | } |
| 477 | 447 | ||
| 478 | #define PROTECT_MALLOC_STATE(PROT) protect_malloc_state(PROT) | 448 | #define PROTECT_MALLOC_STATE(PROT) protect_malloc_state (PROT) |
| 479 | 449 | ||
| 480 | #else | 450 | #else |
| 481 | #define PROTECT_MALLOC_STATE(PROT) /* empty */ | 451 | #define PROTECT_MALLOC_STATE(PROT) /* empty */ |
| @@ -625,6 +595,16 @@ malloc_initialize_1 () | |||
| 625 | mcheck (NULL); | 595 | mcheck (NULL); |
| 626 | #endif | 596 | #endif |
| 627 | 597 | ||
| 598 | #ifdef CYGWIN | ||
| 599 | if (bss_sbrk_did_unexec) | ||
| 600 | /* we're reinitializing the dumped emacs */ | ||
| 601 | { | ||
| 602 | bss_sbrk_heapbase = _heapbase; | ||
| 603 | bss_sbrk_heapinfo = _heapinfo; | ||
| 604 | memset (_fraghead, 0, BLOCKLOG * sizeof (struct list)); | ||
| 605 | } | ||
| 606 | #endif | ||
| 607 | |||
| 628 | if (__malloc_initialize_hook) | 608 | if (__malloc_initialize_hook) |
| 629 | (*__malloc_initialize_hook) (); | 609 | (*__malloc_initialize_hook) (); |
| 630 | 610 | ||
| @@ -1069,20 +1049,6 @@ Fifth Floor, Boston, MA 02110-1301, USA. | |||
| 1069 | #endif | 1049 | #endif |
| 1070 | 1050 | ||
| 1071 | 1051 | ||
| 1072 | /* Cope with systems lacking `memmove'. */ | ||
| 1073 | #ifndef memmove | ||
| 1074 | #if (!defined(_LIBC) && !defined(STDC_HEADERS) && !defined(USG)) | ||
| 1075 | #ifdef emacs | ||
| 1076 | #undef __malloc_safe_bcopy | ||
| 1077 | #define __malloc_safe_bcopy safe_bcopy | ||
| 1078 | #endif | ||
| 1079 | /* This function is defined in realloc.c. */ | ||
| 1080 | extern void __malloc_safe_bcopy PP ((__ptr_t, __ptr_t, __malloc_size_t)); | ||
| 1081 | #define memmove(to, from, size) __malloc_safe_bcopy ((from), (to), (size)) | ||
| 1082 | #endif | ||
| 1083 | #endif | ||
| 1084 | |||
| 1085 | |||
| 1086 | /* Debugging hook for free. */ | 1052 | /* Debugging hook for free. */ |
| 1087 | void (*__free_hook) PP ((__ptr_t __ptr)); | 1053 | void (*__free_hook) PP ((__ptr_t __ptr)); |
| 1088 | 1054 | ||
| @@ -1109,6 +1075,12 @@ _free_internal_nolock (ptr) | |||
| 1109 | if (ptr == NULL) | 1075 | if (ptr == NULL) |
| 1110 | return; | 1076 | return; |
| 1111 | 1077 | ||
| 1078 | #ifdef CYGWIN | ||
| 1079 | if (ptr < _heapbase) | ||
| 1080 | /* We're being asked to free something in the static heap. */ | ||
| 1081 | return; | ||
| 1082 | #endif | ||
| 1083 | |||
| 1112 | PROTECT_MALLOC_STATE (0); | 1084 | PROTECT_MALLOC_STATE (0); |
| 1113 | 1085 | ||
| 1114 | LOCK_ALIGNED_BLOCKS (); | 1086 | LOCK_ALIGNED_BLOCKS (); |
| @@ -1402,87 +1374,33 @@ Fifth Floor, Boston, MA 02110-1301, USA. | |||
| 1402 | #endif | 1374 | #endif |
| 1403 | 1375 | ||
| 1404 | 1376 | ||
| 1377 | #define min(A, B) ((A) < (B) ? (A) : (B)) | ||
| 1405 | 1378 | ||
| 1406 | /* Cope with systems lacking `memmove'. */ | 1379 | /* On Cygwin the dumped emacs may try to realloc storage allocated in |
| 1407 | #if (!defined(_LIBC) && !defined(STDC_HEADERS) && !defined(USG)) | 1380 | the static heap. We just malloc space in the new heap and copy the |
| 1408 | 1381 | data. */ | |
| 1409 | #ifdef emacs | 1382 | #ifdef CYGWIN |
| 1410 | #undef __malloc_safe_bcopy | 1383 | __ptr_t |
| 1411 | #define __malloc_safe_bcopy safe_bcopy | 1384 | special_realloc (ptr, size) |
| 1412 | #else | 1385 | __ptr_t ptr; |
| 1413 | |||
| 1414 | /* Snarfed directly from Emacs src/dispnew.c: | ||
| 1415 | XXX Should use system bcopy if it handles overlap. */ | ||
| 1416 | |||
| 1417 | /* Like bcopy except never gets confused by overlap. */ | ||
| 1418 | |||
| 1419 | void | ||
| 1420 | __malloc_safe_bcopy (afrom, ato, size) | ||
| 1421 | __ptr_t afrom; | ||
| 1422 | __ptr_t ato; | ||
| 1423 | __malloc_size_t size; | 1386 | __malloc_size_t size; |
| 1424 | { | 1387 | { |
| 1425 | char *from = afrom, *to = ato; | 1388 | __ptr_t result; |
| 1426 | 1389 | int type; | |
| 1427 | if (size <= 0 || from == to) | 1390 | __malloc_size_t block, oldsize; |
| 1428 | return; | ||
| 1429 | |||
| 1430 | /* If the source and destination don't overlap, then bcopy can | ||
| 1431 | handle it. If they do overlap, but the destination is lower in | ||
| 1432 | memory than the source, we'll assume bcopy can handle that. */ | ||
| 1433 | if (to < from || from + size <= to) | ||
| 1434 | bcopy (from, to, size); | ||
| 1435 | |||
| 1436 | /* Otherwise, we'll copy from the end. */ | ||
| 1437 | else | ||
| 1438 | { | ||
| 1439 | register char *endf = from + size; | ||
| 1440 | register char *endt = to + size; | ||
| 1441 | |||
| 1442 | /* If TO - FROM is large, then we should break the copy into | ||
| 1443 | nonoverlapping chunks of TO - FROM bytes each. However, if | ||
| 1444 | TO - FROM is small, then the bcopy function call overhead | ||
| 1445 | makes this not worth it. The crossover point could be about | ||
| 1446 | anywhere. Since I don't think the obvious copy loop is too | ||
| 1447 | bad, I'm trying to err in its favor. */ | ||
| 1448 | if (to - from < 64) | ||
| 1449 | { | ||
| 1450 | do | ||
| 1451 | *--endt = *--endf; | ||
| 1452 | while (endf != from); | ||
| 1453 | } | ||
| 1454 | else | ||
| 1455 | { | ||
| 1456 | for (;;) | ||
| 1457 | { | ||
| 1458 | endt -= (to - from); | ||
| 1459 | endf -= (to - from); | ||
| 1460 | |||
| 1461 | if (endt < to) | ||
| 1462 | break; | ||
| 1463 | |||
| 1464 | bcopy (endf, endt, to - from); | ||
| 1465 | } | ||
| 1466 | 1391 | ||
| 1467 | /* If SIZE wasn't a multiple of TO - FROM, there will be a | 1392 | block = ((char *) ptr - bss_sbrk_heapbase) / BLOCKSIZE + 1; |
| 1468 | little left over. The amount left over is | 1393 | type = bss_sbrk_heapinfo[block].busy.type; |
| 1469 | (endt + (to - from)) - to, which is endt - from. */ | 1394 | oldsize = |
| 1470 | bcopy (from, to, endt - from); | 1395 | type == 0 ? bss_sbrk_heapinfo[block].busy.info.size * BLOCKSIZE |
| 1471 | } | 1396 | : (__malloc_size_t) 1 << type; |
| 1472 | } | 1397 | result = _malloc_internal_nolock (size); |
| 1398 | if (result != NULL) | ||
| 1399 | memcpy (result, ptr, min (oldsize, size)); | ||
| 1400 | return result; | ||
| 1473 | } | 1401 | } |
| 1474 | #endif /* emacs */ | ||
| 1475 | |||
| 1476 | #ifndef memmove | ||
| 1477 | extern void __malloc_safe_bcopy PP ((__ptr_t, __ptr_t, __malloc_size_t)); | ||
| 1478 | #define memmove(to, from, size) __malloc_safe_bcopy ((from), (to), (size)) | ||
| 1479 | #endif | ||
| 1480 | |||
| 1481 | #endif | 1402 | #endif |
| 1482 | 1403 | ||
| 1483 | |||
| 1484 | #define min(A, B) ((A) < (B) ? (A) : (B)) | ||
| 1485 | |||
| 1486 | /* Debugging hook for realloc. */ | 1404 | /* Debugging hook for realloc. */ |
| 1487 | __ptr_t (*__realloc_hook) PP ((__ptr_t __ptr, __malloc_size_t __size)); | 1405 | __ptr_t (*__realloc_hook) PP ((__ptr_t __ptr, __malloc_size_t __size)); |
| 1488 | 1406 | ||
| @@ -1509,6 +1427,12 @@ _realloc_internal_nolock (ptr, size) | |||
| 1509 | else if (ptr == NULL) | 1427 | else if (ptr == NULL) |
| 1510 | return _malloc_internal_nolock (size); | 1428 | return _malloc_internal_nolock (size); |
| 1511 | 1429 | ||
| 1430 | #ifdef CYGWIN | ||
| 1431 | if (ptr < _heapbase) | ||
| 1432 | /* ptr points into the static heap */ | ||
| 1433 | return special_realloc (ptr, size); | ||
| 1434 | #endif | ||
| 1435 | |||
| 1512 | block = BLOCK (ptr); | 1436 | block = BLOCK (ptr); |
| 1513 | 1437 | ||
| 1514 | PROTECT_MALLOC_STATE (0); | 1438 | PROTECT_MALLOC_STATE (0); |
| @@ -1617,7 +1541,7 @@ _realloc_internal (ptr, size) | |||
| 1617 | { | 1541 | { |
| 1618 | __ptr_t result; | 1542 | __ptr_t result; |
| 1619 | 1543 | ||
| 1620 | LOCK(); | 1544 | LOCK (); |
| 1621 | result = _realloc_internal_nolock (ptr, size); | 1545 | result = _realloc_internal_nolock (ptr, size); |
| 1622 | UNLOCK (); | 1546 | UNLOCK (); |
| 1623 | 1547 | ||
| @@ -1701,7 +1625,7 @@ MA 02110-1301, USA. */ | |||
| 1701 | 1625 | ||
| 1702 | /* uClibc defines __GNU_LIBRARY__, but it is not completely | 1626 | /* uClibc defines __GNU_LIBRARY__, but it is not completely |
| 1703 | compatible. */ | 1627 | compatible. */ |
| 1704 | #if !defined(__GNU_LIBRARY__) || defined(__UCLIBC__) | 1628 | #if !defined (__GNU_LIBRARY__) || defined (__UCLIBC__) |
| 1705 | #define __sbrk sbrk | 1629 | #define __sbrk sbrk |
| 1706 | #else /* __GNU_LIBRARY__ && ! defined (__UCLIBC__) */ | 1630 | #else /* __GNU_LIBRARY__ && ! defined (__UCLIBC__) */ |
| 1707 | /* It is best not to declare this and cast its result on foreign operating | 1631 | /* It is best not to declare this and cast its result on foreign operating |
| @@ -1723,7 +1647,7 @@ __default_morecore (increment) | |||
| 1723 | __malloc_ptrdiff_t increment; | 1647 | __malloc_ptrdiff_t increment; |
| 1724 | { | 1648 | { |
| 1725 | __ptr_t result; | 1649 | __ptr_t result; |
| 1726 | #if defined(CYGWIN) | 1650 | #if defined (CYGWIN) |
| 1727 | if (!bss_sbrk_did_unexec) | 1651 | if (!bss_sbrk_did_unexec) |
| 1728 | { | 1652 | { |
| 1729 | return bss_sbrk (increment); | 1653 | return bss_sbrk (increment); |
| @@ -1906,7 +1830,7 @@ extern size_t __getpagesize PP ((void)); | |||
| 1906 | #endif | 1830 | #endif |
| 1907 | #else | 1831 | #else |
| 1908 | #include "getpagesize.h" | 1832 | #include "getpagesize.h" |
| 1909 | #define __getpagesize() getpagesize() | 1833 | #define __getpagesize() getpagesize () |
| 1910 | #endif | 1834 | #endif |
| 1911 | 1835 | ||
| 1912 | #ifndef _MALLOC_INTERNAL | 1836 | #ifndef _MALLOC_INTERNAL |
| @@ -1983,22 +1907,6 @@ struct hdr | |||
| 1983 | unsigned long int magic; /* Magic number to check header integrity. */ | 1907 | unsigned long int magic; /* Magic number to check header integrity. */ |
| 1984 | }; | 1908 | }; |
| 1985 | 1909 | ||
| 1986 | #if defined(_LIBC) || defined(STDC_HEADERS) || defined(USG) | ||
| 1987 | #define flood memset | ||
| 1988 | #else | ||
| 1989 | static void flood (__ptr_t, int, __malloc_size_t); | ||
| 1990 | static void | ||
| 1991 | flood (ptr, val, size) | ||
| 1992 | __ptr_t ptr; | ||
| 1993 | int val; | ||
| 1994 | __malloc_size_t size; | ||
| 1995 | { | ||
| 1996 | char *cp = ptr; | ||
| 1997 | while (size--) | ||
| 1998 | *cp++ = val; | ||
| 1999 | } | ||
| 2000 | #endif | ||
| 2001 | |||
| 2002 | static enum mcheck_status checkhdr (const struct hdr *); | 1910 | static enum mcheck_status checkhdr (const struct hdr *); |
| 2003 | static enum mcheck_status | 1911 | static enum mcheck_status |
| 2004 | checkhdr (hdr) | 1912 | checkhdr (hdr) |
| @@ -2037,7 +1945,7 @@ freehook (ptr) | |||
| 2037 | hdr = ((struct hdr *) ptr) - 1; | 1945 | hdr = ((struct hdr *) ptr) - 1; |
| 2038 | checkhdr (hdr); | 1946 | checkhdr (hdr); |
| 2039 | hdr->magic = MAGICFREE; | 1947 | hdr->magic = MAGICFREE; |
| 2040 | flood (ptr, FREEFLOOD, hdr->size); | 1948 | memset (ptr, FREEFLOOD, hdr->size); |
| 2041 | } | 1949 | } |
| 2042 | else | 1950 | else |
| 2043 | hdr = NULL; | 1951 | hdr = NULL; |
| @@ -2063,7 +1971,7 @@ mallochook (size) | |||
| 2063 | hdr->size = size; | 1971 | hdr->size = size; |
| 2064 | hdr->magic = MAGICWORD; | 1972 | hdr->magic = MAGICWORD; |
| 2065 | ((char *) &hdr[1])[size] = MAGICBYTE; | 1973 | ((char *) &hdr[1])[size] = MAGICBYTE; |
| 2066 | flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size); | 1974 | memset ((__ptr_t) (hdr + 1), MALLOCFLOOD, size); |
| 2067 | return (__ptr_t) (hdr + 1); | 1975 | return (__ptr_t) (hdr + 1); |
| 2068 | } | 1976 | } |
| 2069 | 1977 | ||
| @@ -2083,7 +1991,7 @@ reallochook (ptr, size) | |||
| 2083 | 1991 | ||
| 2084 | checkhdr (hdr); | 1992 | checkhdr (hdr); |
| 2085 | if (size < osize) | 1993 | if (size < osize) |
| 2086 | flood ((char *) ptr + size, FREEFLOOD, osize - size); | 1994 | memset ((char *) ptr + size, FREEFLOOD, osize - size); |
| 2087 | } | 1995 | } |
| 2088 | 1996 | ||
| 2089 | __free_hook = old_free_hook; | 1997 | __free_hook = old_free_hook; |
| @@ -2100,7 +2008,7 @@ reallochook (ptr, size) | |||
| 2100 | hdr->magic = MAGICWORD; | 2008 | hdr->magic = MAGICWORD; |
| 2101 | ((char *) &hdr[1])[size] = MAGICBYTE; | 2009 | ((char *) &hdr[1])[size] = MAGICBYTE; |
| 2102 | if (size > osize) | 2010 | if (size > osize) |
| 2103 | flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize); | 2011 | memset ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize); |
| 2104 | return (__ptr_t) (hdr + 1); | 2012 | return (__ptr_t) (hdr + 1); |
| 2105 | } | 2013 | } |
| 2106 | 2014 | ||