Backed out 4 changesets (bug 1522790) - backout the remaining changesets. CLOSED...
[gecko.git] / xpcom / string / nsTextFormatter.cpp
blob4db5338e2bddcfae93da16a8d10bd6385adc1d38
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Portable safe sprintf code.
10 * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
12 * Contributor(s):
13 * Kipp E.B. Hickman <kipp@netscape.com> (original author)
14 * Frank Yung-Fong Tang <ftang@netscape.com>
15 * Daniele Nicolodi <daniele@grinta.net>
18 #include <stddef.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include "prdtoa.h"
22 #include "mozilla/Logging.h"
23 #include "mozilla/Sprintf.h"
24 #include "nsCRTGlue.h"
25 #include "nsTextFormatter.h"
27 struct nsTextFormatter::SprintfStateStr {
28 int (*stuff)(SprintfStateStr* aState, const char16_t* aStr, uint32_t aLen);
30 char16_t* base;
31 char16_t* cur;
32 uint32_t maxlen;
34 void* stuffclosure;
37 #define _LEFT 0x1
38 #define _SIGNED 0x2
39 #define _SPACED 0x4
40 #define _ZEROS 0x8
41 #define _NEG 0x10
42 #define _UNSIGNED 0x20
44 #define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
47 ** Fill into the buffer using the data in src
49 int nsTextFormatter::fill2(SprintfStateStr* aState, const char16_t* aSrc,
50 int aSrcLen, int aWidth, int aFlags) {
51 char16_t space = ' ';
52 int rv;
54 aWidth -= aSrcLen;
55 /* Right adjusting */
56 if ((aWidth > 0) && ((aFlags & _LEFT) == 0)) {
57 if (aFlags & _ZEROS) {
58 space = '0';
60 while (--aWidth >= 0) {
61 rv = (*aState->stuff)(aState, &space, 1);
62 if (rv < 0) {
63 return rv;
68 /* Copy out the source data */
69 rv = (*aState->stuff)(aState, aSrc, aSrcLen);
70 if (rv < 0) {
71 return rv;
74 /* Left adjusting */
75 if ((aWidth > 0) && ((aFlags & _LEFT) != 0)) {
76 while (--aWidth >= 0) {
77 rv = (*aState->stuff)(aState, &space, 1);
78 if (rv < 0) {
79 return rv;
83 return 0;
87 ** Fill a number. The order is: optional-sign zero-filling conversion-digits
89 int nsTextFormatter::fill_n(nsTextFormatter::SprintfStateStr* aState,
90 const char16_t* aSrc, int aSrcLen, int aWidth,
91 int aPrec, int aFlags) {
92 int zerowidth = 0;
93 int precwidth = 0;
94 int signwidth = 0;
95 int leftspaces = 0;
96 int rightspaces = 0;
97 int cvtwidth;
98 int rv;
99 char16_t sign;
100 char16_t space = ' ';
101 char16_t zero = '0';
103 if ((aFlags & _UNSIGNED) == 0) {
104 if (aFlags & _NEG) {
105 sign = '-';
106 signwidth = 1;
107 } else if (aFlags & _SIGNED) {
108 sign = '+';
109 signwidth = 1;
110 } else if (aFlags & _SPACED) {
111 sign = ' ';
112 signwidth = 1;
115 cvtwidth = signwidth + aSrcLen;
117 if (aPrec > 0) {
118 if (aPrec > aSrcLen) {
119 /* Need zero filling */
120 precwidth = aPrec - aSrcLen;
121 cvtwidth += precwidth;
125 if ((aFlags & _ZEROS) && (aPrec < 0)) {
126 if (aWidth > cvtwidth) {
127 /* Zero filling */
128 zerowidth = aWidth - cvtwidth;
129 cvtwidth += zerowidth;
133 if (aFlags & _LEFT) {
134 if (aWidth > cvtwidth) {
135 /* Space filling on the right (i.e. left adjusting) */
136 rightspaces = aWidth - cvtwidth;
138 } else {
139 if (aWidth > cvtwidth) {
140 /* Space filling on the left (i.e. right adjusting) */
141 leftspaces = aWidth - cvtwidth;
144 while (--leftspaces >= 0) {
145 rv = (*aState->stuff)(aState, &space, 1);
146 if (rv < 0) {
147 return rv;
150 if (signwidth) {
151 rv = (*aState->stuff)(aState, &sign, 1);
152 if (rv < 0) {
153 return rv;
156 while (--precwidth >= 0) {
157 rv = (*aState->stuff)(aState, &space, 1);
158 if (rv < 0) {
159 return rv;
162 while (--zerowidth >= 0) {
163 rv = (*aState->stuff)(aState, &zero, 1);
164 if (rv < 0) {
165 return rv;
168 rv = (*aState->stuff)(aState, aSrc, aSrcLen);
169 if (rv < 0) {
170 return rv;
172 while (--rightspaces >= 0) {
173 rv = (*aState->stuff)(aState, &space, 1);
174 if (rv < 0) {
175 return rv;
178 return 0;
182 ** Convert a 64-bit integer into its printable form
184 int nsTextFormatter::cvt_ll(SprintfStateStr* aState, uint64_t aNum, int aWidth,
185 int aPrec, int aRadix, int aFlags,
186 const char16_t* aHexStr) {
187 char16_t cvtbuf[100];
188 char16_t* cvt;
189 int digits;
191 /* according to the man page this needs to happen */
192 if (aPrec == 0 && aNum == 0) {
193 return 0;
197 ** Converting decimal is a little tricky. In the unsigned case we
198 ** need to stop when we hit 10 digits. In the signed case, we can
199 ** stop when the number is zero.
201 cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
202 digits = 0;
203 while (aNum != 0) {
204 uint64_t quot = aNum / aRadix;
205 uint64_t rem = aNum % aRadix;
206 *--cvt = aHexStr[rem & 0xf];
207 digits++;
208 aNum = quot;
210 if (digits == 0) {
211 *--cvt = '0';
212 digits++;
216 ** Now that we have the number converted without its sign, deal with
217 ** the sign and zero padding.
219 return fill_n(aState, cvt, digits, aWidth, aPrec, aFlags);
223 ** Convert a double precision floating point number into its printable
224 ** form.
226 int nsTextFormatter::cvt_f(SprintfStateStr* aState, double aDouble, int aWidth,
227 int aPrec, const char16_t aType, int aFlags) {
228 int mode = 2;
229 int decpt;
230 int sign;
231 char buf[256];
232 char* bufp = buf;
233 int bufsz = 256;
234 char num[256];
235 char* nump;
236 char* endnum;
237 int numdigits = 0;
238 char exp = 'e';
240 if (aPrec == -1) {
241 aPrec = 6;
242 } else if (aPrec > 50) {
243 // limit precision to avoid PR_dtoa bug 108335
244 // and to prevent buffers overflows
245 aPrec = 50;
248 switch (aType) {
249 case 'f':
250 numdigits = aPrec;
251 mode = 3;
252 break;
253 case 'E':
254 exp = 'E';
255 [[fallthrough]];
256 case 'e':
257 numdigits = aPrec + 1;
258 mode = 2;
259 break;
260 case 'G':
261 exp = 'E';
262 [[fallthrough]];
263 case 'g':
264 if (aPrec == 0) {
265 aPrec = 1;
267 numdigits = aPrec;
268 mode = 2;
269 break;
270 default:
271 NS_ERROR("invalid aType passed to cvt_f");
274 if (PR_dtoa(aDouble, mode, numdigits, &decpt, &sign, &endnum, num, bufsz) ==
275 PR_FAILURE) {
276 buf[0] = '\0';
277 return -1;
279 numdigits = endnum - num;
280 nump = num;
282 if (sign) {
283 *bufp++ = '-';
284 } else if (aFlags & _SIGNED) {
285 *bufp++ = '+';
288 if (decpt == 9999) {
289 while ((*bufp++ = *nump++)) {
291 } else {
292 switch (aType) {
293 case 'E':
294 case 'e':
296 *bufp++ = *nump++;
297 if (aPrec > 0) {
298 *bufp++ = '.';
299 while (*nump) {
300 *bufp++ = *nump++;
301 aPrec--;
303 while (aPrec-- > 0) {
304 *bufp++ = '0';
307 *bufp++ = exp;
309 ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
310 break;
312 case 'f':
314 if (decpt < 1) {
315 *bufp++ = '0';
316 if (aPrec > 0) {
317 *bufp++ = '.';
318 while (decpt++ && aPrec-- > 0) {
319 *bufp++ = '0';
321 while (*nump && aPrec-- > 0) {
322 *bufp++ = *nump++;
324 while (aPrec-- > 0) {
325 *bufp++ = '0';
328 } else {
329 while (*nump && decpt-- > 0) {
330 *bufp++ = *nump++;
332 while (decpt-- > 0) {
333 *bufp++ = '0';
335 if (aPrec > 0) {
336 *bufp++ = '.';
337 while (*nump && aPrec-- > 0) {
338 *bufp++ = *nump++;
340 while (aPrec-- > 0) {
341 *bufp++ = '0';
345 *bufp = '\0';
346 break;
348 case 'G':
349 case 'g':
351 if ((decpt < -3) || ((decpt - 1) >= aPrec)) {
352 *bufp++ = *nump++;
353 numdigits--;
354 if (numdigits > 0) {
355 *bufp++ = '.';
356 while (*nump) {
357 *bufp++ = *nump++;
360 *bufp++ = exp;
361 ::snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
362 } else {
363 if (decpt < 1) {
364 *bufp++ = '0';
365 if (aPrec > 0) {
366 *bufp++ = '.';
367 while (decpt++) {
368 *bufp++ = '0';
370 while (*nump) {
371 *bufp++ = *nump++;
374 } else {
375 while (*nump && decpt-- > 0) {
376 *bufp++ = *nump++;
377 numdigits--;
379 while (decpt-- > 0) {
380 *bufp++ = '0';
382 if (numdigits > 0) {
383 *bufp++ = '.';
384 while (*nump) {
385 *bufp++ = *nump++;
389 *bufp = '\0';
394 char16_t rbuf[256];
395 char16_t* rbufp = rbuf;
396 bufp = buf;
397 // cast to char16_t
398 while ((*rbufp++ = *bufp++)) {
400 *rbufp = '\0';
402 return fill2(aState, rbuf, NS_strlen(rbuf), aWidth, aFlags);
406 ** Convert a string into its printable form. |aWidth| is the output
407 ** width. |aPrec| is the maximum number of characters of |aStr| to output,
408 ** where -1 means until NUL.
410 int nsTextFormatter::cvt_S(SprintfStateStr* aState, const char16_t* aStr,
411 int aWidth, int aPrec, int aFlags) {
412 int slen;
414 if (aPrec == 0) {
415 return 0;
418 /* Limit string length by precision value */
419 slen = aStr ? NS_strlen(aStr) : 6;
420 if (aPrec > 0) {
421 if (aPrec < slen) {
422 slen = aPrec;
426 /* and away we go */
427 return fill2(aState, aStr ? aStr : u"(null)", slen, aWidth, aFlags);
431 ** Convert a string into its printable form. |aWidth| is the output
432 ** width. |aPrec| is the maximum number of characters of |aStr| to output,
433 ** where -1 means until NUL.
435 int nsTextFormatter::cvt_s(nsTextFormatter::SprintfStateStr* aState,
436 const char* aStr, int aWidth, int aPrec,
437 int aFlags) {
438 // Be sure to handle null the same way as %S.
439 if (aStr == nullptr) {
440 return cvt_S(aState, nullptr, aWidth, aPrec, aFlags);
442 NS_ConvertUTF8toUTF16 utf16Val(aStr);
443 return cvt_S(aState, utf16Val.get(), aWidth, aPrec, aFlags);
447 ** The workhorse sprintf code.
449 int nsTextFormatter::dosprintf(SprintfStateStr* aState, const char16_t* aFmt,
450 mozilla::Span<BoxedValue> aValues) {
451 static const char16_t space = ' ';
452 static const char16_t hex[] = u"0123456789abcdef";
453 static const char16_t HEX[] = u"0123456789ABCDEF";
454 static const BoxedValue emptyString(u"");
456 char16_t c;
457 int flags, width, prec, radix;
459 const char16_t* hexp;
461 // Next argument for non-numbered arguments.
462 size_t nextNaturalArg = 0;
463 // True if we ever saw a numbered argument.
464 bool sawNumberedArg = false;
466 while ((c = *aFmt++) != 0) {
467 int rv;
469 if (c != '%') {
470 rv = (*aState->stuff)(aState, aFmt - 1, 1);
471 if (rv < 0) {
472 return rv;
474 continue;
477 // Save the location of the "%" in case we decide it isn't a
478 // format and want to just emit the text from the format string.
479 const char16_t* percentPointer = aFmt - 1;
482 ** Gobble up the % format string. Hopefully we have handled all
483 ** of the strange cases!
485 flags = 0;
486 c = *aFmt++;
487 if (c == '%') {
488 /* quoting a % with %% */
489 rv = (*aState->stuff)(aState, aFmt - 1, 1);
490 if (rv < 0) {
491 return rv;
493 continue;
496 // Check for a numbered argument.
497 bool sawWidth = false;
498 const BoxedValue* thisArg = nullptr;
499 if (c >= '0' && c <= '9') {
500 size_t argNumber = 0;
501 while (c && c >= '0' && c <= '9') {
502 argNumber = (argNumber * 10) + (c - '0');
503 c = *aFmt++;
506 if (c == '$') {
507 // Mixing numbered arguments and implicit arguments is
508 // disallowed.
509 if (nextNaturalArg > 0) {
510 return -1;
513 c = *aFmt++;
515 // Numbered arguments start at 1.
516 --argNumber;
517 if (argNumber >= aValues.Length()) {
518 // A correctness issue but not a safety issue.
519 MOZ_ASSERT(false);
520 thisArg = &emptyString;
521 } else {
522 thisArg = &aValues[argNumber];
524 sawNumberedArg = true;
525 } else {
526 width = argNumber;
527 sawWidth = true;
531 if (!sawWidth) {
533 * Examine optional flags. Note that we do not implement the
534 * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
535 * somewhat ambiguous and not ideal, which is perhaps why
536 * the various sprintf() implementations are inconsistent
537 * on this feature.
539 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
540 if (c == '-') {
541 flags |= _LEFT;
543 if (c == '+') {
544 flags |= _SIGNED;
546 if (c == ' ') {
547 flags |= _SPACED;
549 if (c == '0') {
550 flags |= _ZEROS;
552 c = *aFmt++;
554 if (flags & _SIGNED) {
555 flags &= ~_SPACED;
557 if (flags & _LEFT) {
558 flags &= ~_ZEROS;
561 /* width */
562 if (c == '*') {
563 // Not supported with numbered arguments.
564 if (sawNumberedArg) {
565 return -1;
568 if (nextNaturalArg >= aValues.Length() ||
569 !aValues[nextNaturalArg].IntCompatible()) {
570 // A correctness issue but not a safety issue.
571 MOZ_ASSERT(false);
572 width = 0;
573 } else {
574 width = aValues[nextNaturalArg++].mValue.mInt;
576 c = *aFmt++;
577 } else {
578 width = 0;
579 while ((c >= '0') && (c <= '9')) {
580 width = (width * 10) + (c - '0');
581 c = *aFmt++;
586 /* precision */
587 prec = -1;
588 if (c == '.') {
589 c = *aFmt++;
590 if (c == '*') {
591 // Not supported with numbered arguments.
592 if (sawNumberedArg) {
593 return -1;
596 if (nextNaturalArg >= aValues.Length() ||
597 !aValues[nextNaturalArg].IntCompatible()) {
598 // A correctness issue but not a safety issue.
599 MOZ_ASSERT(false);
600 } else {
601 prec = aValues[nextNaturalArg++].mValue.mInt;
603 c = *aFmt++;
604 } else {
605 prec = 0;
606 while ((c >= '0') && (c <= '9')) {
607 prec = (prec * 10) + (c - '0');
608 c = *aFmt++;
613 // If the argument isn't known yet, find it now. This is done
614 // after the width and precision code, in case '*' was used.
615 if (thisArg == nullptr) {
616 // Mixing numbered arguments and implicit arguments is
617 // disallowed.
618 if (sawNumberedArg) {
619 return -1;
622 if (nextNaturalArg >= aValues.Length()) {
623 // A correctness issue but not a safety issue.
624 MOZ_ASSERT(false);
625 thisArg = &emptyString;
626 } else {
627 thisArg = &aValues[nextNaturalArg++];
631 /* Size. Defaults to 32 bits. */
632 uint64_t mask = UINT32_MAX;
633 if (c == 'h') {
634 c = *aFmt++;
635 mask = UINT16_MAX;
636 } else if (c == 'L') {
637 c = *aFmt++;
638 mask = UINT64_MAX;
639 } else if (c == 'l') {
640 c = *aFmt++;
641 if (c == 'l') {
642 c = *aFmt++;
643 mask = UINT64_MAX;
644 } else {
645 mask = UINT32_MAX;
649 /* format */
650 hexp = hex;
651 radix = 10;
652 // Several `MOZ_ASSERT`s below check for argument compatibility
653 // with the format specifier. These are only debug assertions,
654 // not release assertions, and exist to catch problems in C++
655 // callers of `nsTextFormatter`, as we do not have compile-time
656 // checking of format strings. In release mode, these assertions
657 // will be no-ops, and we will fall through to printing the
658 // argument based on the known type of the argument.
659 switch (c) {
660 case 'd':
661 case 'i': /* decimal/integer */
662 MOZ_ASSERT(thisArg->IntCompatible());
663 break;
665 case 'o': /* octal */
666 MOZ_ASSERT(thisArg->IntCompatible());
667 radix = 8;
668 flags |= _UNSIGNED;
669 break;
671 case 'u': /* unsigned decimal */
672 MOZ_ASSERT(thisArg->IntCompatible());
673 radix = 10;
674 flags |= _UNSIGNED;
675 break;
677 case 'x': /* unsigned hex */
678 MOZ_ASSERT(thisArg->IntCompatible());
679 radix = 16;
680 flags |= _UNSIGNED;
681 break;
683 case 'X': /* unsigned HEX */
684 MOZ_ASSERT(thisArg->IntCompatible());
685 radix = 16;
686 hexp = HEX;
687 flags |= _UNSIGNED;
688 break;
690 case 'e':
691 case 'E':
692 case 'f':
693 case 'g':
694 case 'G':
695 MOZ_ASSERT(thisArg->mKind == DOUBLE);
696 // Type-based printing below.
697 break;
699 case 'S':
700 case 's':
701 MOZ_ASSERT(thisArg->mKind == STRING || thisArg->mKind == STRING16);
702 // Type-based printing below.
703 break;
705 case 'c': {
706 if (!thisArg->IntCompatible()) {
707 MOZ_ASSERT(false);
708 // Type-based printing below.
709 break;
712 if ((flags & _LEFT) == 0) {
713 while (width-- > 1) {
714 rv = (*aState->stuff)(aState, &space, 1);
715 if (rv < 0) {
716 return rv;
720 char16_t ch = thisArg->mValue.mInt;
721 rv = (*aState->stuff)(aState, &ch, 1);
722 if (rv < 0) {
723 return rv;
725 if (flags & _LEFT) {
726 while (width-- > 1) {
727 rv = (*aState->stuff)(aState, &space, 1);
728 if (rv < 0) {
729 return rv;
734 continue;
736 case 'p':
737 if (!thisArg->PointerCompatible()) {
738 MOZ_ASSERT(false);
739 break;
741 static_assert(sizeof(uint64_t) >= sizeof(void*),
742 "pointers are larger than 64 bits");
743 rv = cvt_ll(aState, uintptr_t(thisArg->mValue.mPtr), width, prec, 16,
744 flags | _UNSIGNED, hexp);
745 if (rv < 0) {
746 return rv;
748 continue;
750 case 'n':
751 if (thisArg->mKind != INTPOINTER) {
752 return -1;
755 if (thisArg->mValue.mIntPtr != nullptr) {
756 *thisArg->mValue.mIntPtr = aState->cur - aState->base;
758 continue;
760 default:
761 /* Not a % token after all... skip it */
762 rv = (*aState->stuff)(aState, percentPointer, aFmt - percentPointer);
763 if (rv < 0) {
764 return rv;
766 continue;
769 // If we get here, we want to handle the argument according to its
770 // actual type; modified by the flags as appropriate.
771 switch (thisArg->mKind) {
772 case INT:
773 case UINT: {
774 int64_t val = thisArg->mValue.mInt;
775 if ((flags & _UNSIGNED) == 0 && val < 0) {
776 val = -val;
777 flags |= _NEG;
779 rv = cvt_ll(aState, uint64_t(val) & mask, width, prec, radix, flags,
780 hexp);
781 } break;
782 case INTPOINTER:
783 case POINTER:
784 // Always treat these as unsigned hex, no matter the format.
785 static_assert(sizeof(uint64_t) >= sizeof(void*),
786 "pointers are larger than 64 bits");
787 rv = cvt_ll(aState, uintptr_t(thisArg->mValue.mPtr), width, prec, 16,
788 flags | _UNSIGNED, hexp);
789 break;
790 case DOUBLE:
791 if (c != 'f' && c != 'E' && c != 'e' && c != 'G' && c != 'g') {
792 // Pick some default.
793 c = 'g';
795 rv = cvt_f(aState, thisArg->mValue.mDouble, width, prec, c, flags);
796 break;
797 case STRING:
798 rv = cvt_s(aState, thisArg->mValue.mString, width, prec, flags);
799 break;
800 case STRING16:
801 rv = cvt_S(aState, thisArg->mValue.mString16, width, prec, flags);
802 break;
803 default:
804 // Can't happen.
805 MOZ_ASSERT(0);
808 if (rv < 0) {
809 return rv;
813 return 0;
816 /************************************************************************/
818 int nsTextFormatter::StringStuff(nsTextFormatter::SprintfStateStr* aState,
819 const char16_t* aStr, uint32_t aLen) {
820 ptrdiff_t off = aState->cur - aState->base;
822 nsAString* str = static_cast<nsAString*>(aState->stuffclosure);
823 str->Append(aStr, aLen);
825 aState->base = str->BeginWriting();
826 aState->cur = aState->base + off;
828 return 0;
831 void nsTextFormatter::vssprintf(nsAString& aOut, const char16_t* aFmt,
832 mozilla::Span<BoxedValue> aValues) {
833 SprintfStateStr ss;
834 ss.stuff = StringStuff;
835 ss.base = 0;
836 ss.cur = 0;
837 ss.maxlen = 0;
838 ss.stuffclosure = &aOut;
840 aOut.Truncate();
841 dosprintf(&ss, aFmt, aValues);
845 ** Stuff routine that discards overflow data
847 int nsTextFormatter::LimitStuff(SprintfStateStr* aState, const char16_t* aStr,
848 uint32_t aLen) {
849 uint32_t limit = aState->maxlen - (aState->cur - aState->base);
851 if (aLen > limit) {
852 aLen = limit;
854 while (aLen) {
855 --aLen;
856 *aState->cur++ = *aStr++;
858 return 0;
861 uint32_t nsTextFormatter::vsnprintf(char16_t* aOut, uint32_t aOutLen,
862 const char16_t* aFmt,
863 mozilla::Span<BoxedValue> aValues) {
864 SprintfStateStr ss;
866 MOZ_ASSERT((int32_t)aOutLen > 0);
867 if ((int32_t)aOutLen <= 0) {
868 return 0;
871 ss.stuff = LimitStuff;
872 ss.base = aOut;
873 ss.cur = aOut;
874 ss.maxlen = aOutLen;
875 int result = dosprintf(&ss, aFmt, aValues);
877 if (ss.cur == ss.base) {
878 return 0;
881 // Append a NUL. However, be sure not to count it in the returned
882 // length.
883 if (ss.cur - ss.base >= ptrdiff_t(ss.maxlen)) {
884 --ss.cur;
886 *ss.cur = '\0';
888 // Check the result now, so that an unterminated string can't
889 // possibly escape.
890 if (result < 0) {
891 return -1;
894 return ss.cur - ss.base;