varchar.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:16k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * varchar.c
  4.  *   Functions for the built-in type char() and varchar().
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.48.2.1 1999/08/02 05:24:58 scrappy Exp $
  11.  *
  12.  *-------------------------------------------------------------------------
  13.  */
  14. #include "postgres.h"
  15. #include "access/htup.h"
  16. #include "catalog/pg_type.h"
  17. #include "utils/builtins.h"
  18. #ifdef MULTIBYTE
  19. #include "mb/pg_wchar.h"
  20. #endif
  21. #ifdef CYR_RECODE
  22. char    *convertstr(char *, int, int);
  23. #endif
  24. /*
  25.  * CHAR() and VARCHAR() types are part of the ANSI SQL standard. CHAR()
  26.  * is for blank-padded string whose length is specified in CREATE TABLE.
  27.  * VARCHAR is for storing string whose length is at most the length specified
  28.  * at CREATE TABLE time.
  29.  *
  30.  * It's hard to implement these types because we cannot figure out what
  31.  * the length of the type from the type itself. I change (hopefully all) the
  32.  * fmgr calls that invoke input functions of a data type to supply the
  33.  * length also. (eg. in INSERTs, we have the tupleDescriptor which contains
  34.  * the length of the attributes and hence the exact length of the char() or
  35.  * varchar(). We pass this to bpcharin() or varcharin().) In the case where
  36.  * we cannot determine the length, we pass in -1 instead and the input string
  37.  * must be null-terminated.
  38.  *
  39.  * We actually implement this as a varlena so that we don't have to pass in
  40.  * the length for the comparison functions. (The difference between "text"
  41.  * is that we truncate and possibly blank-pad the string at insertion time.)
  42.  *
  43.  *   - ay 6/95
  44.  */
  45. /*****************************************************************************
  46.  *  bpchar - char()  *
  47.  *****************************************************************************/
  48. /*
  49.  * bpcharin -
  50.  *   converts a string of char() type to the internal representation.
  51.  *   len is the length specified in () plus VARHDRSZ bytes. (XXX dummy is here
  52.  *   because we pass typelem as the second argument for array_in.)
  53.  */
  54. char *
  55. bpcharin(char *s, int dummy, int32 atttypmod)
  56. {
  57. char    *result,
  58.    *r;
  59. int len;
  60. int i;
  61. if (s == NULL)
  62. return (char *) NULL;
  63. if (atttypmod == -1)
  64. {
  65. /*
  66.  * this is here because some functions can't supply the atttypmod
  67.  */
  68. len = strlen(s);
  69. atttypmod = len + VARHDRSZ;
  70. }
  71. else
  72. len = atttypmod - VARHDRSZ;
  73. if (len > MaxAttrSize)
  74. elog(ERROR, "bpcharin: length of char() must be less than %d",
  75. MaxAttrSize);
  76. result = (char *) palloc(atttypmod);
  77. VARSIZE(result) = atttypmod;
  78. r = VARDATA(result);
  79. for (i = 0; i < len; i++, r++, s++)
  80. {
  81. *r = *s;
  82. if (*r == '')
  83. break;
  84. }
  85. #ifdef CYR_RECODE
  86. convertstr(result + VARHDRSZ, len, 0);
  87. #endif
  88. /* blank pad the string if necessary */
  89. for (; i < len; i++)
  90. *r++ = ' ';
  91. return result;
  92. }
  93. char *
  94. bpcharout(char *s)
  95. {
  96. char    *result;
  97. int len;
  98. if (s == NULL)
  99. {
  100. result = (char *) palloc(2);
  101. result[0] = '-';
  102. result[1] = '';
  103. }
  104. else
  105. {
  106. len = VARSIZE(s) - VARHDRSZ;
  107. result = (char *) palloc(len + 1);
  108. StrNCpy(result, VARDATA(s), len + 1); /* these are blank-padded */
  109. }
  110. #ifdef CYR_RECODE
  111. convertstr(result, len, 1);
  112. #endif
  113. return result;
  114. }
  115. /* bpchar()
  116.  * Converts a char() type to a specific internal length.
  117.  * len is the length specified in () plus VARHDRSZ bytes.
  118.  */
  119. char *
  120. bpchar(char *s, int32 len)
  121. {
  122. char    *result,
  123.    *r;
  124. int rlen,
  125. slen;
  126. int i;
  127. if (s == NULL)
  128. return (char *) NULL;
  129. if ((len == -1) || (len == VARSIZE(s)))
  130. return s;
  131. rlen = len - VARHDRSZ;
  132. if (rlen > MaxAttrSize)
  133. elog(ERROR, "bpchar: length of char() must be less than %d",
  134. MaxAttrSize);
  135. #ifdef STRINGDEBUG
  136. printf("bpchar- convert string length %d (%d) ->%d (%d)n",
  137.    VARSIZE(s) - VARHDRSZ, VARSIZE(s), rlen, len);
  138. #endif
  139. result = (char *) palloc(len);
  140. VARSIZE(result) = len;
  141. r = VARDATA(result);
  142. #ifdef MULTIBYTE
  143. /*
  144.  * truncate multi-byte string in a way not to break multi-byte
  145.  * boundary
  146.  */
  147. if (VARSIZE(s) > len)
  148. slen = pg_mbcliplen(VARDATA(s), VARSIZE(s) - VARHDRSZ, rlen);
  149. else
  150. slen = VARSIZE(s) - VARHDRSZ;
  151. #else
  152. slen = VARSIZE(s) - VARHDRSZ;
  153. #endif
  154. s = VARDATA(s);
  155. #ifdef STRINGDEBUG
  156. printf("bpchar- string is '");
  157. #endif
  158. for (i = 0; (i < rlen) && (i < slen); i++)
  159. {
  160. if (*s == '')
  161. break;
  162. #ifdef STRINGDEBUG
  163. printf("%c", *s);
  164. #endif
  165. *r++ = *s++;
  166. }
  167. #ifdef STRINGDEBUG
  168. printf("'n");
  169. #endif
  170. /* blank pad the string if necessary */
  171. for (; i < rlen; i++)
  172. *r++ = ' ';
  173. return result;
  174. } /* bpchar() */
  175. /* _bpchar()
  176.  * Converts an array of char() type to a specific internal length.
  177.  * len is the length specified in () plus VARHDRSZ bytes.
  178.  */
  179. ArrayType  *
  180. _bpchar(ArrayType *v, int32 len)
  181. {
  182. return array_map(v, BPCHAROID, bpchar, BPCHAROID, 1, len);
  183. }
  184. /* bpchar_char()
  185.  * Convert bpchar(1) to char.
  186.  */
  187. int32
  188. bpchar_char(char *s)
  189. {
  190. return (int32) *VARDATA(s);
  191. } /* bpchar_char() */
  192. /* char_bpchar()
  193.  * Convert char to bpchar(1).
  194.  */
  195. char *
  196. char_bpchar(int32 c)
  197. {
  198. char    *result;
  199. result = palloc(VARHDRSZ + 1);
  200. VARSIZE(result) = VARHDRSZ + 1;
  201. *(VARDATA(result)) = (char) c;
  202. return result;
  203. } /* char_bpchar() */
  204. /* bpchar_name()
  205.  * Converts a bpchar() type to a NameData type.
  206.  */
  207. NameData   *
  208. bpchar_name(char *s)
  209. {
  210. NameData   *result;
  211. int len;
  212. if (s == NULL)
  213. return NULL;
  214. len = VARSIZE(s) - VARHDRSZ;
  215. if (len > NAMEDATALEN)
  216. len = NAMEDATALEN;
  217. while (len > 0)
  218. {
  219. if (*(VARDATA(s) + len - 1) != ' ')
  220. break;
  221. len--;
  222. }
  223. #ifdef STRINGDEBUG
  224. printf("bpchar- convert string length %d (%d) ->%dn",
  225.    VARSIZE(s) - VARHDRSZ, VARSIZE(s), len);
  226. #endif
  227. result = (NameData *) palloc(NAMEDATALEN);
  228. StrNCpy(result->data, VARDATA(s), NAMEDATALEN);
  229. /* now null pad to full length... */
  230. while (len < NAMEDATALEN)
  231. {
  232. *(result->data + len) = '';
  233. len++;
  234. }
  235. return result;
  236. } /* bpchar_name() */
  237. /* name_bpchar()
  238.  * Converts a NameData type to a bpchar type.
  239.  */
  240. char *
  241. name_bpchar(NameData *s)
  242. {
  243. char    *result;
  244. int len;
  245. if (s == NULL)
  246. return NULL;
  247. len = strlen(s->data);
  248. #ifdef STRINGDEBUG
  249. printf("bpchar- convert string length %d (%d) ->%dn",
  250.    VARSIZE(s) - VARHDRSZ, VARSIZE(s), len);
  251. #endif
  252. result = (char *) palloc(VARHDRSZ + len);
  253. strncpy(VARDATA(result), s->data, len);
  254. VARSIZE(result) = len + VARHDRSZ;
  255. return result;
  256. } /* name_bpchar() */
  257. /*****************************************************************************
  258.  *  varchar - varchar()  *
  259.  *****************************************************************************/
  260. /*
  261.  * varcharin -
  262.  *   converts a string of varchar() type to the internal representation.
  263.  *   len is the length specified in () plus VARHDRSZ bytes. (XXX dummy is here
  264.  *   because we pass typelem as the second argument for array_in.)
  265.  */
  266. char *
  267. varcharin(char *s, int dummy, int32 atttypmod)
  268. {
  269. char    *result;
  270. int len;
  271. if (s == NULL)
  272. return (char *) NULL;
  273. len = strlen(s) + VARHDRSZ;
  274. if (atttypmod != -1 && len > atttypmod)
  275. len = atttypmod; /* clip the string at max length */
  276. if (len > MaxAttrSize)
  277. elog(ERROR, "varcharin: length of char() must be less than %d",
  278. MaxAttrSize);
  279. result = (char *) palloc(len);
  280. VARSIZE(result) = len;
  281. strncpy(VARDATA(result), s, len - VARHDRSZ);
  282. #ifdef CYR_RECODE
  283. convertstr(result + VARHDRSZ, len, 0);
  284. #endif
  285. return result;
  286. }
  287. char *
  288. varcharout(char *s)
  289. {
  290. char    *result;
  291. int len;
  292. if (s == NULL)
  293. {
  294. result = (char *) palloc(2);
  295. result[0] = '-';
  296. result[1] = '';
  297. }
  298. else
  299. {
  300. len = VARSIZE(s) - VARHDRSZ;
  301. result = (char *) palloc(len + 1);
  302. StrNCpy(result, VARDATA(s), len + 1);
  303. }
  304. #ifdef CYR_RECODE
  305. convertstr(result, len, 1);
  306. #endif
  307. return result;
  308. }
  309. /* varchar()
  310.  * Converts a varchar() type to the specified size.
  311.  * slen is the length specified in () plus VARHDRSZ bytes.
  312.  */
  313. char *
  314. varchar(char *s, int32 slen)
  315. {
  316. char    *result;
  317. int len;
  318. if (s == NULL)
  319. return (char *) NULL;
  320. len = VARSIZE(s);
  321. if ((slen == -1) || (len <= slen))
  322. return (char *) s;
  323. /* only reach here if we need to truncate string... */
  324. #ifdef MULTIBYTE
  325. /*
  326.  * truncate multi-byte string in a way not to break multi-byte
  327.  * boundary
  328.  */
  329. len = pg_mbcliplen(VARDATA(s), slen - VARHDRSZ, slen - VARHDRSZ);
  330. slen = len + VARHDRSZ;
  331. #else
  332. len = slen - VARHDRSZ;
  333. #endif
  334. if (len > MaxAttrSize)
  335. elog(ERROR, "varchar: length of varchar() must be less than %d",
  336. MaxAttrSize);
  337. result = (char *) palloc(slen);
  338. VARSIZE(result) = slen;
  339. strncpy(VARDATA(result), VARDATA(s), len);
  340. return result;
  341. } /* varchar() */
  342. /* _varchar()
  343.  * Converts an array of varchar() type to the specified size.
  344.  * len is the length specified in () plus VARHDRSZ bytes.
  345.  */
  346. ArrayType  *
  347. _varchar(ArrayType *v, int32 len)
  348. {
  349. return array_map(v, VARCHAROID, varchar, VARCHAROID, 1, len);
  350. }
  351. /*****************************************************************************
  352.  * Comparison Functions used for bpchar
  353.  *****************************************************************************/
  354. static int
  355. bcTruelen(char *arg)
  356. {
  357. char    *s = VARDATA(arg);
  358. int i;
  359. int len;
  360. len = VARSIZE(arg) - VARHDRSZ;
  361. for (i = len - 1; i >= 0; i--)
  362. {
  363. if (s[i] != ' ')
  364. break;
  365. }
  366. return i + 1;
  367. }
  368. int32
  369. bpcharlen(char *arg)
  370. {
  371. #ifdef MULTIBYTE
  372. unsigned char *s;
  373. int len,
  374. l,
  375. wl;
  376. #endif
  377. if (!PointerIsValid(arg))
  378. elog(ERROR, "Bad (null) char() external representation", NULL);
  379. #ifdef MULTIBYTE
  380. l = bcTruelen(arg);
  381. len = 0;
  382. s = VARDATA(arg);
  383. while (l > 0)
  384. {
  385. wl = pg_mblen(s);
  386. l -= wl;
  387. s += wl;
  388. len++;
  389. }
  390. return (len);
  391. #else
  392. return bcTruelen(arg);
  393. #endif
  394. }
  395. int32
  396. bpcharoctetlen(char *arg)
  397. {
  398. if (!PointerIsValid(arg))
  399. elog(ERROR, "Bad (null) char() external representation", NULL);
  400. return bcTruelen(arg);
  401. }
  402. bool
  403. bpchareq(char *arg1, char *arg2)
  404. {
  405. int len1,
  406. len2;
  407. if (arg1 == NULL || arg2 == NULL)
  408. return (bool) 0;
  409. len1 = bcTruelen(arg1);
  410. len2 = bcTruelen(arg2);
  411. if (len1 != len2)
  412. return 0;
  413. return strncmp(VARDATA(arg1), VARDATA(arg2), len1) == 0;
  414. }
  415. bool
  416. bpcharne(char *arg1, char *arg2)
  417. {
  418. int len1,
  419. len2;
  420. if (arg1 == NULL || arg2 == NULL)
  421. return (bool) 0;
  422. len1 = bcTruelen(arg1);
  423. len2 = bcTruelen(arg2);
  424. if (len1 != len2)
  425. return 1;
  426. return strncmp(VARDATA(arg1), VARDATA(arg2), len1) != 0;
  427. }
  428. bool
  429. bpcharlt(char *arg1, char *arg2)
  430. {
  431. int len1,
  432. len2;
  433. int cmp;
  434. if (arg1 == NULL || arg2 == NULL)
  435. return (bool) 0;
  436. len1 = bcTruelen(arg1);
  437. len2 = bcTruelen(arg2);
  438. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  439. if (cmp == 0)
  440. return len1 < len2;
  441. else
  442. return cmp < 0;
  443. }
  444. bool
  445. bpcharle(char *arg1, char *arg2)
  446. {
  447. int len1,
  448. len2;
  449. int cmp;
  450. if (arg1 == NULL || arg2 == NULL)
  451. return (bool) 0;
  452. len1 = bcTruelen(arg1);
  453. len2 = bcTruelen(arg2);
  454. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  455. if (0 == cmp)
  456. return (bool) (len1 <= len2 ? 1 : 0);
  457. else
  458. return (bool) (cmp <= 0);
  459. }
  460. bool
  461. bpchargt(char *arg1, char *arg2)
  462. {
  463. int len1,
  464. len2;
  465. int cmp;
  466. if (arg1 == NULL || arg2 == NULL)
  467. return (bool) 0;
  468. len1 = bcTruelen(arg1);
  469. len2 = bcTruelen(arg2);
  470. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  471. if (cmp == 0)
  472. return len1 > len2;
  473. else
  474. return cmp > 0;
  475. }
  476. bool
  477. bpcharge(char *arg1, char *arg2)
  478. {
  479. int len1,
  480. len2;
  481. int cmp;
  482. if (arg1 == NULL || arg2 == NULL)
  483. return (bool) 0;
  484. len1 = bcTruelen(arg1);
  485. len2 = bcTruelen(arg2);
  486. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  487. if (0 == cmp)
  488. return (bool) (len1 >= len2 ? 1 : 0);
  489. else
  490. return (bool) (cmp >= 0);
  491. }
  492. int32
  493. bpcharcmp(char *arg1, char *arg2)
  494. {
  495. int len1,
  496. len2;
  497. int cmp;
  498. len1 = bcTruelen(arg1);
  499. len2 = bcTruelen(arg2);
  500. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  501. if ((0 == cmp) && (len1 != len2))
  502. return (int32) (len1 < len2 ? -1 : 1);
  503. else
  504. return cmp;
  505. }
  506. /*****************************************************************************
  507.  * Comparison Functions used for varchar
  508.  *****************************************************************************/
  509. int32
  510. varcharlen(char *arg)
  511. {
  512. #ifdef MULTIBYTE
  513. unsigned char *s;
  514. int len,
  515. l,
  516. wl;
  517. #endif
  518. if (!PointerIsValid(arg))
  519. elog(ERROR, "Bad (null) varchar() external representation", NULL);
  520. #ifdef MULTIBYTE
  521. len = 0;
  522. s = VARDATA(arg);
  523. l = VARSIZE(arg) - VARHDRSZ;
  524. while (l > 0)
  525. {
  526. wl = pg_mblen(s);
  527. l -= wl;
  528. s += wl;
  529. len++;
  530. }
  531. return (len);
  532. #else
  533. return VARSIZE(arg) - VARHDRSZ;
  534. #endif
  535. }
  536. int32
  537. varcharoctetlen(char *arg)
  538. {
  539. if (!PointerIsValid(arg))
  540. elog(ERROR, "Bad (null) varchar() external representation", NULL);
  541. return VARSIZE(arg) - VARHDRSZ;
  542. }
  543. bool
  544. varchareq(char *arg1, char *arg2)
  545. {
  546. int len1,
  547. len2;
  548. if (arg1 == NULL || arg2 == NULL)
  549. return (bool) 0;
  550. len1 = VARSIZE(arg1) - VARHDRSZ;
  551. len2 = VARSIZE(arg2) - VARHDRSZ;
  552. if (len1 != len2)
  553. return 0;
  554. return strncmp(VARDATA(arg1), VARDATA(arg2), len1) == 0;
  555. }
  556. bool
  557. varcharne(char *arg1, char *arg2)
  558. {
  559. int len1,
  560. len2;
  561. if (arg1 == NULL || arg2 == NULL)
  562. return (bool) 0;
  563. len1 = VARSIZE(arg1) - VARHDRSZ;
  564. len2 = VARSIZE(arg2) - VARHDRSZ;
  565. if (len1 != len2)
  566. return 1;
  567. return strncmp(VARDATA(arg1), VARDATA(arg2), len1) != 0;
  568. }
  569. bool
  570. varcharlt(char *arg1, char *arg2)
  571. {
  572. int len1,
  573. len2;
  574. int cmp;
  575. if (arg1 == NULL || arg2 == NULL)
  576. return (bool) 0;
  577. len1 = VARSIZE(arg1) - VARHDRSZ;
  578. len2 = VARSIZE(arg2) - VARHDRSZ;
  579. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  580. if (cmp == 0)
  581. return len1 < len2;
  582. else
  583. return cmp < 0;
  584. }
  585. bool
  586. varcharle(char *arg1, char *arg2)
  587. {
  588. int len1,
  589. len2;
  590. int cmp;
  591. if (arg1 == NULL || arg2 == NULL)
  592. return (bool) 0;
  593. len1 = VARSIZE(arg1) - VARHDRSZ;
  594. len2 = VARSIZE(arg2) - VARHDRSZ;
  595. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  596. if (0 == cmp)
  597. return (bool) (len1 <= len2 ? 1 : 0);
  598. else
  599. return (bool) (cmp <= 0);
  600. }
  601. bool
  602. varchargt(char *arg1, char *arg2)
  603. {
  604. int len1,
  605. len2;
  606. int cmp;
  607. if (arg1 == NULL || arg2 == NULL)
  608. return (bool) 0;
  609. len1 = VARSIZE(arg1) - VARHDRSZ;
  610. len2 = VARSIZE(arg2) - VARHDRSZ;
  611. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  612. if (cmp == 0)
  613. return len1 > len2;
  614. else
  615. return cmp > 0;
  616. }
  617. bool
  618. varcharge(char *arg1, char *arg2)
  619. {
  620. int len1,
  621. len2;
  622. int cmp;
  623. if (arg1 == NULL || arg2 == NULL)
  624. return (bool) 0;
  625. len1 = VARSIZE(arg1) - VARHDRSZ;
  626. len2 = VARSIZE(arg2) - VARHDRSZ;
  627. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  628. if (0 == cmp)
  629. return (bool) (len1 >= len2 ? 1 : 0);
  630. else
  631. return (bool) (cmp >= 0);
  632. }
  633. int32
  634. varcharcmp(char *arg1, char *arg2)
  635. {
  636. int len1,
  637. len2;
  638. int cmp;
  639. len1 = VARSIZE(arg1) - VARHDRSZ;
  640. len2 = VARSIZE(arg2) - VARHDRSZ;
  641. cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
  642. if ((0 == cmp) && (len1 != len2))
  643. return (int32) (len1 < len2 ? -1 : 1);
  644. else
  645. return (int32) (cmp);
  646. }
  647. /*****************************************************************************
  648.  * Hash functions (modified from hashtext in access/hash/hashfunc.c)
  649.  *****************************************************************************/
  650. uint32
  651. hashbpchar(struct varlena * key)
  652. {
  653. int keylen;
  654. char    *keydata;
  655. uint32 n;
  656. int loop;
  657. keydata = VARDATA(key);
  658. keylen = bcTruelen((char *) key);
  659. #define HASHC n = *keydata++ + 65599 * n
  660. n = 0;
  661. if (keylen > 0)
  662. {
  663. loop = (keylen + 8 - 1) >> 3;
  664. switch (keylen & (8 - 1))
  665. {
  666. case 0:
  667. do
  668. { /* All fall throughs */
  669. HASHC;
  670. case 7:
  671. HASHC;
  672. case 6:
  673. HASHC;
  674. case 5:
  675. HASHC;
  676. case 4:
  677. HASHC;
  678. case 3:
  679. HASHC;
  680. case 2:
  681. HASHC;
  682. case 1:
  683. HASHC;
  684. } while (--loop);
  685. }
  686. }
  687. return n;
  688. }
  689. uint32
  690. hashvarchar(struct varlena * key)
  691. {
  692. int keylen;
  693. char    *keydata;
  694. uint32 n;
  695. int loop;
  696. keydata = VARDATA(key);
  697. keylen = VARSIZE(key) - VARHDRSZ;
  698. #define HASHC n = *keydata++ + 65599 * n
  699. n = 0;
  700. if (keylen > 0)
  701. {
  702. loop = (keylen + 8 - 1) >> 3;
  703. switch (keylen & (8 - 1))
  704. {
  705. case 0:
  706. do
  707. { /* All fall throughs */
  708. HASHC;
  709. case 7:
  710. HASHC;
  711. case 6:
  712. HASHC;
  713. case 5:
  714. HASHC;
  715. case 4:
  716. HASHC;
  717. case 3:
  718. HASHC;
  719. case 2:
  720. HASHC;
  721. case 1:
  722. HASHC;
  723. } while (--loop);
  724. }
  725. }
  726. return n;
  727. }