1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 nxt_str_t *
nxt_str_alloc(nxt_mp_t * mp,size_t length)11 nxt_str_alloc(nxt_mp_t *mp, size_t length)
12 {
13     nxt_str_t  *s;
14 
15     /* The string start is allocated aligned to be close to nxt_str_t. */
16     s = nxt_mp_get(mp, sizeof(nxt_str_t) + length);
17 
18     if (nxt_fast_path(s != NULL)) {
19         s->length = length;
20         s->start = nxt_pointer_to(s, sizeof(nxt_str_t));
21     }
22 
23     return s;
24 }
25 
26 
27 /*
28  * nxt_str_dup() creates a new string with a copy of a source string.
29  * If length of the source string is zero, then the new string anyway
30  * gets a pointer somewhere in mem_pool.
31  */
32 
33 nxt_str_t *
nxt_str_dup(nxt_mp_t * mp,nxt_str_t * dst,const nxt_str_t * src)34 nxt_str_dup(nxt_mp_t *mp, nxt_str_t *dst, const nxt_str_t *src)
35 {
36     u_char  *p;
37 
38     if (dst == NULL) {
39         /* The string start is allocated aligned to be close to nxt_str_t. */
40         dst = nxt_mp_get(mp, sizeof(nxt_str_t) + src->length);
41         if (nxt_slow_path(dst == NULL)) {
42             return NULL;
43         }
44 
45         p = (u_char *) dst;
46         p += sizeof(nxt_str_t);
47         dst->start = p;
48 
49     } else {
50         dst->start = nxt_mp_nget(mp, src->length);
51         if (nxt_slow_path(dst->start == NULL)) {
52             return NULL;
53         }
54     }
55 
56     nxt_memcpy(dst->start, src->start, src->length);
57     dst->length = src->length;
58 
59     return dst;
60 }
61 
62 
63 /*
64  * nxt_str_cstrz() creates a C style zero-terminated copy of a source
65  * nxt_str_t.  The function is intended to create strings suitable
66  * for libc and kernel interfaces so result is pointer to char instead
67  * of u_char to minimize casts.
68  */
69 
70 char *
nxt_str_cstrz(nxt_mp_t * mp,const nxt_str_t * src)71 nxt_str_cstrz(nxt_mp_t *mp, const nxt_str_t *src)
72 {
73     char  *p, *dst;
74 
75     dst = nxt_mp_alloc(mp, src->length + 1);
76 
77     if (nxt_fast_path(dst != NULL)) {
78         p = nxt_cpymem(dst, src->start, src->length);
79         *p = '\0';
80     }
81 
82     return dst;
83 }
84 
85 
86 void
nxt_memcpy_lowcase(u_char * dst,const u_char * src,size_t length)87 nxt_memcpy_lowcase(u_char *dst, const u_char *src, size_t length)
88 {
89     u_char  c;
90 
91     while (length != 0) {
92         c = *src++;
93         *dst++ = nxt_lowcase(c);
94         length--;
95     }
96 }
97 
98 
99 void
nxt_memcpy_upcase(u_char * dst,const u_char * src,size_t length)100 nxt_memcpy_upcase(u_char *dst, const u_char *src, size_t length)
101 {
102     u_char  c;
103 
104     while (length != 0) {
105         c = *src++;
106         *dst++ = nxt_upcase(c);
107         length--;
108     }
109 }
110 
111 
112 u_char *
nxt_cpystr(u_char * dst,const u_char * src)113 nxt_cpystr(u_char *dst, const u_char *src)
114 {
115     for ( ;; ) {
116         *dst = *src;
117 
118         if (*dst == '\0') {
119             break;
120         }
121 
122         dst++;
123         src++;
124     }
125 
126     return dst;
127 }
128 
129 
130 u_char *
nxt_cpystrn(u_char * dst,const u_char * src,size_t length)131 nxt_cpystrn(u_char *dst, const u_char *src, size_t length)
132 {
133     if (length == 0) {
134         return dst;
135     }
136 
137     while (--length != 0) {
138         *dst = *src;
139 
140         if (*dst == '\0') {
141             return dst;
142         }
143 
144         dst++;
145         src++;
146     }
147 
148     *dst = '\0';
149 
150     return dst;
151 }
152 
153 
154 nxt_int_t
nxt_strcasecmp(const u_char * s1,const u_char * s2)155 nxt_strcasecmp(const u_char *s1, const u_char *s2)
156 {
157     u_char     c1, c2;
158     nxt_int_t  n;
159 
160     for ( ;; ) {
161         c1 = *s1++;
162         c2 = *s2++;
163 
164         c1 = nxt_lowcase(c1);
165         c2 = nxt_lowcase(c2);
166 
167         n = c1 - c2;
168 
169         if (n != 0) {
170             return n;
171         }
172 
173         if (c1 == 0) {
174             return 0;
175         }
176     }
177 }
178 
179 
180 nxt_int_t
nxt_strncasecmp(const u_char * s1,const u_char * s2,size_t length)181 nxt_strncasecmp(const u_char *s1, const u_char *s2, size_t length)
182 {
183     u_char     c1, c2;
184     nxt_int_t  n;
185 
186     while (length-- != 0) {
187         c1 = *s1++;
188         c2 = *s2++;
189 
190         c1 = nxt_lowcase(c1);
191         c2 = nxt_lowcase(c2);
192 
193         n = c1 - c2;
194 
195         if (n != 0) {
196             return n;
197         }
198 
199         if (c1 == 0) {
200             return 0;
201         }
202     }
203 
204     return 0;
205 }
206 
207 
208 nxt_int_t
nxt_memcasecmp(const void * p1,const void * p2,size_t length)209 nxt_memcasecmp(const void *p1, const void *p2, size_t length)
210 {
211     u_char        c1, c2;
212     nxt_int_t     n;
213     const u_char  *s1, *s2;
214 
215     s1 = p1;
216     s2 = p2;
217 
218     while (length-- != 0) {
219         c1 = *s1++;
220         c2 = *s2++;
221 
222         c1 = nxt_lowcase(c1);
223         c2 = nxt_lowcase(c2);
224 
225         n = c1 - c2;
226 
227         if (n != 0) {
228             return n;
229         }
230     }
231 
232     return 0;
233 }
234 
235 
236 /*
237  * nxt_memstrn() is intended for search of static substring "ss"
238  * with known length "length" in string "s" limited by parameter "end".
239  * Zeros are ignored in both strings.
240  */
241 
242 u_char *
nxt_memstrn(const u_char * s,const u_char * end,const char * ss,size_t length)243 nxt_memstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
244 {
245     u_char  c1, c2, *s2;
246 
247     s2 = (u_char *) ss;
248     c2 = *s2++;
249     length--;
250 
251     while (s < end) {
252         c1 = *s++;
253 
254         if (c1 == c2) {
255 
256             if (s + length > end) {
257                 return NULL;
258             }
259 
260             if (nxt_memcmp(s, s2, length) == 0) {
261                 return (u_char *) s - 1;
262             }
263         }
264     }
265 
266     return NULL;
267 }
268 
269 
270 /*
271  * nxt_strcasestrn() is intended for caseless search of static substring
272  * "ss" with known length "length" in string "s" limited by parameter "end".
273  * Zeros are ignored in both strings.
274  */
275 
276 u_char *
nxt_memcasestrn(const u_char * s,const u_char * end,const char * ss,size_t length)277 nxt_memcasestrn(const u_char *s, const u_char *end, const char *ss,
278     size_t length)
279 {
280     u_char  c1, c2, *s2;
281 
282     s2 = (u_char *) ss;
283     c2 = *s2++;
284     c2 = nxt_lowcase(c2);
285     length--;
286 
287     while (s < end) {
288         c1 = *s++;
289         c1 = nxt_lowcase(c1);
290 
291         if (c1 == c2) {
292 
293             if (s + length > end) {
294                 return NULL;
295             }
296 
297             if (nxt_memcasecmp(s, s2, length) == 0) {
298                 return (u_char *) s - 1;
299             }
300         }
301     }
302 
303     return NULL;
304 }
305 
306 
307 /*
308  * nxt_rstrstrn() is intended to search for static substring "ss"
309  * with known length "length" in string "s" limited by parameter "end"
310  * in reverse order.  Zeros are ignored in both strings.
311  */
312 
313 u_char *
nxt_rmemstrn(const u_char * s,const u_char * end,const char * ss,size_t length)314 nxt_rmemstrn(const u_char *s, const u_char *end, const char *ss, size_t length)
315 {
316     u_char        c1, c2;
317     const u_char  *s1, *s2;
318 
319     s1 = end - length;
320     s2 = (u_char *) ss;
321     c2 = *s2++;
322     length--;
323 
324     while (s < s1) {
325         c1 = *s1;
326 
327         if (c1 == c2) {
328             if (nxt_memcmp(s1 + 1, s2, length) == 0) {
329                 return (u_char *) s1;
330             }
331         }
332 
333         s1--;
334     }
335 
336     return NULL;
337 }
338 
339 
340 size_t
nxt_str_strip(u_char * start,u_char * end)341 nxt_str_strip(u_char *start, u_char *end)
342 {
343     u_char  *p;
344 
345     for (p = end - 1; p >= start; p--) {
346         if (*p != '\r' && *p != '\n') {
347             break;
348         }
349     }
350 
351     return (p + 1) - start;
352 }
353 
354 
355 nxt_int_t
nxt_strverscmp(const u_char * s1,const u_char * s2)356 nxt_strverscmp(const u_char *s1, const u_char *s2)
357 {
358     u_char     c1, c2;
359     nxt_int_t  diff;
360 
361     enum {
362         st_str = 0,
363         st_num,
364         st_zero,
365         st_frac,
366     } state;
367 
368     state = st_str;
369 
370     for ( ;; ) {
371         c1 = *s1++;
372         c2 = *s2++;
373 
374         diff = c1 - c2;
375 
376         if (diff != 0) {
377             break;
378         }
379 
380         if (c1 == '\0') {
381             return 0;
382         }
383 
384         if (!nxt_isdigit(c1)) {
385             state = st_str;
386             continue;
387         }
388 
389         if (state == st_str) {
390             state = (c1 != '0') ? st_num : st_zero;
391             continue;
392         }
393 
394         if (state == st_zero && c1 != '0') {
395             state = st_frac;
396             continue;
397         }
398     }
399 
400     switch (state) {
401 
402     case st_str:
403 
404         if ((u_char) (c1 - '1') > 8 || (u_char) (c2 - '1') > 8) {
405             return diff;
406         }
407 
408         c1 = *s1++;
409         c2 = *s2++;
410 
411         /* Fall through. */
412 
413     case st_num:
414 
415         while (nxt_isdigit(c1) && nxt_isdigit(c2)) {
416             c1 = *s1++;
417             c2 = *s2++;
418         }
419 
420         if (nxt_isdigit(c1)) {
421             return 1;
422         }
423 
424         if (nxt_isdigit(c2)) {
425             return -1;
426         }
427 
428         return diff;
429 
430     case st_zero:
431 
432         if (c1 == '0' || c2 == '\0') {
433             return -1;
434         }
435 
436         if (c2 == '0' || c1 == '\0') {
437             return 1;
438         }
439 
440         /* Fall through. */
441 
442     case st_frac:
443     default:
444         return diff;
445     }
446 }
447 
448 
449 nxt_bool_t
nxt_strvers_match(u_char * version,u_char * prefix,size_t length)450 nxt_strvers_match(u_char *version, u_char *prefix, size_t length)
451 {
452     u_char  next, last;
453 
454     if (length == 0) {
455         return 1;
456     }
457 
458     if (nxt_strncmp(version, prefix, length) == 0) {
459 
460         next = version[length];
461 
462         if (next == '\0') {
463             return 1;
464         }
465 
466         last = version[length - 1];
467 
468         if (nxt_isdigit(last) != nxt_isdigit(next)) {
469             /* This is a version part boundary. */
470             return 1;
471         }
472     }
473 
474     return 0;
475 }
476 
477 
478 const uint8_t  nxt_hex2int[256]
479     nxt_aligned(32) =
480 {
481     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
482     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
483     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
484      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 16, 16, 16, 16, 16, 16,
485     16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
486     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
487     16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16,
488     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
489     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
490     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
491     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
492     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
493     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
494     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
495     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
496     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
497 };
498 
499 
500 static const uint32_t  nxt_uri_escape[] = {
501     0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
502 
503                 /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
504     0xd000002d, /* 1101 0000 0000 0000 0000 0000 0010 1101 */
505 
506                 /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
507     0x50000000, /* 0101 0000 0000 0000 0000 0000 0000 0000 */
508 
509                 /*  ~}| {zyx wvut srqp onml kjih gfed cba` */
510     0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */
511 
512     0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
513     0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
514     0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
515     0xffffffff  /* 1111 1111 1111 1111 1111 1111 1111 1111 */
516 };
517 
518 
519 u_char *
nxt_decode_uri(u_char * dst,u_char * src,size_t length)520 nxt_decode_uri(u_char *dst, u_char *src, size_t length)
521 {
522     u_char   *end, ch;
523     uint8_t  d0, d1;
524 
525     nxt_prefetch(&nxt_hex2int['0']);
526 
527     end = src + length;
528 
529     while (src < end) {
530         ch = *src++;
531 
532         if (ch == '%') {
533             if (nxt_slow_path(end - src < 2)) {
534                 return NULL;
535             }
536 
537             d0 = nxt_hex2int[*src++];
538             d1 = nxt_hex2int[*src++];
539 
540             if (nxt_slow_path((d0 | d1) >= 16)) {
541                 return NULL;
542             }
543 
544             ch = (d0 << 4) + d1;
545         }
546 
547         *dst++ = ch;
548     }
549 
550     return dst;
551 }
552 
553 
554 u_char *
nxt_decode_uri_plus(u_char * dst,u_char * src,size_t length)555 nxt_decode_uri_plus(u_char *dst, u_char *src, size_t length)
556 {
557     u_char   *end, ch;
558     uint8_t  d0, d1;
559 
560     nxt_prefetch(&nxt_hex2int['0']);
561 
562     end = src + length;
563 
564     while (src < end) {
565         ch = *src++;
566 
567         switch (ch) {
568         case '%':
569             if (nxt_slow_path(end - src < 2)) {
570                 return NULL;
571             }
572 
573             d0 = nxt_hex2int[*src++];
574             d1 = nxt_hex2int[*src++];
575 
576             if (nxt_slow_path((d0 | d1) >= 16)) {
577                 return NULL;
578             }
579 
580             ch = (d0 << 4) + d1;
581             break;
582 
583         case '+':
584             ch = ' ';
585             break;
586         }
587 
588         *dst++ = ch;
589     }
590 
591     return dst;
592 }
593 
594 
595 uintptr_t
nxt_encode_uri(u_char * dst,u_char * src,size_t length)596 nxt_encode_uri(u_char *dst, u_char *src, size_t length)
597 {
598     u_char      *end;
599     nxt_uint_t  n;
600 
601     static const u_char  hex[16] = "0123456789ABCDEF";
602 
603     end = src + length;
604 
605     if (dst == NULL) {
606 
607         /* Find the number of the characters to be escaped. */
608 
609         n = 0;
610 
611         while (src < end) {
612 
613             if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
614                 n++;
615             }
616 
617             src++;
618         }
619 
620         return (uintptr_t) n;
621     }
622 
623     while (src < end) {
624 
625         if (nxt_uri_escape[*src >> 5] & (1U << (*src & 0x1f))) {
626             *dst++ = '%';
627             *dst++ = hex[*src >> 4];
628             *dst++ = hex[*src & 0xf];
629 
630         } else {
631             *dst++ = *src;
632         }
633 
634         src++;
635     }
636 
637     return (uintptr_t) dst;
638 }
639 
640 
641 uintptr_t
nxt_encode_complex_uri(u_char * dst,u_char * src,size_t length)642 nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length)
643 {
644     u_char      *reserved, *end, ch;
645     nxt_uint_t  n;
646 
647     static const u_char  hex[16] = "0123456789ABCDEF";
648 
649     reserved = (u_char *) "?#\0";
650 
651     end = src + length;
652 
653     if (dst == NULL) {
654 
655         /* Find the number of the characters to be escaped. */
656 
657         n = 0;
658 
659         while (src < end) {
660             ch = *src++;
661 
662             if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
663                 if (ch == reserved[0]) {
664                     reserved++;
665                     continue;
666                 }
667 
668                 if (ch == reserved[1]) {
669                     reserved += 2;
670                     continue;
671                 }
672 
673                 n++;
674             }
675         }
676 
677         return (uintptr_t) n;
678     }
679 
680     while (src < end) {
681         ch = *src++;
682 
683         if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
684             if (ch == reserved[0]) {
685                 reserved++;
686 
687             } else if (ch == reserved[1]) {
688                 reserved += 2;
689 
690             } else {
691                 *dst++ = '%';
692                 *dst++ = hex[ch >> 4];
693                 *dst++ = hex[ch & 0xf];
694                 continue;
695             }
696         }
697 
698         *dst++ = ch;
699     }
700 
701     return (uintptr_t) dst;
702 }
703 
704 
705 nxt_bool_t
nxt_is_complex_uri_encoded(u_char * src,size_t length)706 nxt_is_complex_uri_encoded(u_char *src, size_t length)
707 {
708     u_char   *reserved, *end, ch;
709     uint8_t  d0, d1;
710 
711     reserved = (u_char *) "?#\0";
712 
713     for (end = src + length; src < end; src++) {
714         ch = *src;
715 
716         if (nxt_uri_escape[ch >> 5] & (1U << (ch & 0x1f))) {
717             if (ch == '%') {
718                 if (end - src < 2) {
719                     return 0;
720                 }
721 
722                 d0 = nxt_hex2int[*++src];
723                 d1 = nxt_hex2int[*++src];
724 
725                 if ((d0 | d1) >= 16) {
726                     return 0;
727                 }
728 
729                 continue;
730             }
731 
732             if (ch == reserved[0]) {
733                 reserved++;
734                 continue;
735             }
736 
737             if (ch == reserved[1]) {
738                 reserved += 2;
739                 continue;
740             }
741 
742             return 0;
743         }
744     }
745 
746     return 1;
747 }
748 
749 
750 ssize_t
nxt_base64_decode(u_char * dst,u_char * src,size_t length)751 nxt_base64_decode(u_char *dst, u_char *src, size_t length)
752 {
753     u_char   *end, *p;
754     size_t   pad;
755     uint8_t  v1, v2, v3, v4;
756 
757     static const uint8_t  decode[] = {
758         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
759         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
760         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
761         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
762         77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
763         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,
764         77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
765         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
766 
767         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
768         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
769         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
770         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
771         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
772         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
773         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
774         77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
775     };
776 
777     end = src + length;
778     pad = (4 - (length % 4)) % 4;
779 
780     if (dst == NULL) {
781         if (pad > 2) {
782             return NXT_ERROR;
783         }
784 
785         while (src < end) {
786             if (decode[*src] != 77) {
787                 src++;
788                 continue;
789             }
790 
791             if (pad == 0) {
792                 pad = end - src;
793 
794                 if ((pad == 1 || (pad == 2 && src[1] == '=')) && src[0] == '=')
795                 {
796                     break;
797                 }
798             }
799 
800             return NXT_ERROR;
801         }
802 
803         return (length + 3) / 4 * 3 - pad;
804     }
805 
806     nxt_assert(length != 0);
807 
808     if (pad == 0) {
809         pad = (end[-1] == '=') + (end[-2] == '=');
810         end -= (pad + 3) & 4;
811 
812     } else {
813         end -= 4 - pad;
814     }
815 
816     p = dst;
817 
818     while (src < end) {
819         v1 = decode[src[0]];
820         v2 = decode[src[1]];
821         v3 = decode[src[2]];
822         v4 = decode[src[3]];
823 
824         *p++ = (v1 << 2 | v2 >> 4);
825         *p++ = (v2 << 4 | v3 >> 2);
826         *p++ = (v3 << 6 | v4);
827 
828         src += 4;
829     }
830 
831     if (pad > 0) {
832         v1 = decode[src[0]];
833         v2 = decode[src[1]];
834 
835         *p++ = (v1 << 2 | v2 >> 4);
836 
837         if (pad == 1) {
838             v3 = decode[src[2]];
839             *p++ = (v2 << 4 | v3 >> 2);
840         }
841     }
842 
843     return (p - dst);
844 }
845