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 /* JavaScript string operations. */
12 #include "js/shadow/String.h" // JS::shadow::String
14 #include "mozilla/Assertions.h" // MOZ_ASSERT
15 #include "mozilla/Attributes.h" // MOZ_ALWAYS_INLINE, MOZ_MUST_USE
16 #include "mozilla/Likely.h" // MOZ_LIKELY
18 #include <algorithm> // std::copy_n
19 #include <stddef.h> // size_t
20 #include <stdint.h> // uint32_t, uint64_t, INT32_MAX
22 #include "jstypes.h" // JS_PUBLIC_API
24 #include "js/TypeDecls.h" // JS::Latin1Char
26 class JS_PUBLIC_API JSAtom
;
28 class JS_PUBLIC_API JSString
;
32 class JS_PUBLIC_API AutoRequireNoGC
;
35 * Maximum length of a JS string. This is chosen so that the number of bytes
36 * allocated for a null-terminated TwoByte string still fits in int32_t.
38 static constexpr uint32_t MaxStringLength
= (1 << 30) - 2;
40 static_assert((uint64_t(MaxStringLength
) + 1) * sizeof(char16_t
) <= INT32_MAX
,
41 "size of null-terminated JSString char buffer must fit in "
44 /** Compute the length of a string. */
45 MOZ_ALWAYS_INLINE
size_t GetStringLength(JSString
* s
) {
46 return shadow::AsShadowString(s
)->length();
49 /** Compute the length of a linear string. */
50 MOZ_ALWAYS_INLINE
size_t GetLinearStringLength(JSLinearString
* s
) {
51 return shadow::AsShadowString(s
)->length();
54 /** Return true iff the given linear string uses Latin-1 storage. */
55 MOZ_ALWAYS_INLINE
bool LinearStringHasLatin1Chars(JSLinearString
* s
) {
56 return shadow::AsShadowString(s
)->hasLatin1Chars();
59 /** Return true iff the given string uses Latin-1 storage. */
60 MOZ_ALWAYS_INLINE
bool StringHasLatin1Chars(JSString
* s
) {
61 return shadow::AsShadowString(s
)->hasLatin1Chars();
65 * Given a linear string known to use Latin-1 storage, return a pointer to that
66 * storage. This pointer remains valid only as long as no GC occurs.
68 MOZ_ALWAYS_INLINE
const Latin1Char
* GetLatin1LinearStringChars(
69 const AutoRequireNoGC
& nogc
, JSLinearString
* linear
) {
70 return shadow::AsShadowString(linear
)->latin1LinearChars();
74 * Given a linear string known to use two-byte storage, return a pointer to that
75 * storage. This pointer remains valid only as long as no GC occurs.
77 MOZ_ALWAYS_INLINE
const char16_t
* GetTwoByteLinearStringChars(
78 const AutoRequireNoGC
& nogc
, JSLinearString
* linear
) {
79 return shadow::AsShadowString(linear
)->twoByteLinearChars();
83 * Given an in-range index into the provided string, return the character at
86 MOZ_ALWAYS_INLINE char16_t
GetLinearStringCharAt(JSLinearString
* linear
,
88 shadow::String
* s
= shadow::AsShadowString(linear
);
89 MOZ_ASSERT(index
< s
->length());
91 return s
->hasLatin1Chars() ? s
->latin1LinearChars()[index
]
92 : s
->twoByteLinearChars()[index
];
96 * Convert an atom to a linear string. All atoms are linear, so this
97 * operation is infallible.
99 MOZ_ALWAYS_INLINE JSLinearString
* AtomToLinearString(JSAtom
* atom
) {
100 return reinterpret_cast<JSLinearString
*>(atom
);
104 * If the provided string uses externally-managed storage, return true and set
105 * |*callbacks| to the external-string callbacks used to create it and |*chars|
106 * to a pointer to its two-byte storage. (These pointers remain valid as long
107 * as the provided string is kept alive.)
109 MOZ_ALWAYS_INLINE
bool IsExternalString(
110 JSString
* str
, const JSExternalStringCallbacks
** callbacks
,
111 const char16_t
** chars
) {
112 shadow::String
* s
= shadow::AsShadowString(str
);
114 if (!s
->isExternal()) {
118 *callbacks
= s
->externalCallbacks
;
119 *chars
= s
->nonInlineCharsTwoByte
;
125 extern JS_FRIEND_API JSLinearString
* StringToLinearStringSlow(JSContext
* cx
,
128 } // namespace detail
130 /** Convert a string to a linear string. */
131 MOZ_ALWAYS_INLINE JSLinearString
* StringToLinearString(JSContext
* cx
,
133 if (MOZ_LIKELY(shadow::AsShadowString(str
)->isLinear())) {
134 return reinterpret_cast<JSLinearString
*>(str
);
137 return detail::StringToLinearStringSlow(cx
, str
);
140 /** Copy characters in |s[start..start + len]| to |dest[0..len]|. */
141 MOZ_ALWAYS_INLINE
void CopyLinearStringChars(char16_t
* dest
, JSLinearString
* s
,
142 size_t len
, size_t start
= 0) {
144 size_t stringLen
= GetLinearStringLength(s
);
145 MOZ_ASSERT(start
<= stringLen
);
146 MOZ_ASSERT(len
<= stringLen
- start
);
149 shadow::String
* str
= shadow::AsShadowString(s
);
151 if (str
->hasLatin1Chars()) {
152 const Latin1Char
* src
= str
->latin1LinearChars();
153 for (size_t i
= 0; i
< len
; i
++) {
154 dest
[i
] = src
[start
+ i
];
157 const char16_t
* src
= str
->twoByteLinearChars();
158 std::copy_n(src
+ start
, len
, dest
);
163 * Copy characters in |s[start..start + len]| to |dest[0..len]|, lossily
164 * truncating 16-bit values to |char| if necessary.
166 MOZ_ALWAYS_INLINE
void LossyCopyLinearStringChars(char* dest
, JSLinearString
* s
,
170 size_t stringLen
= GetLinearStringLength(s
);
171 MOZ_ASSERT(start
<= stringLen
);
172 MOZ_ASSERT(len
<= stringLen
- start
);
175 shadow::String
* str
= shadow::AsShadowString(s
);
177 if (LinearStringHasLatin1Chars(s
)) {
178 const Latin1Char
* src
= str
->latin1LinearChars();
179 for (size_t i
= 0; i
< len
; i
++) {
180 dest
[i
] = char(src
[start
+ i
]);
183 const char16_t
* src
= str
->twoByteLinearChars();
184 for (size_t i
= 0; i
< len
; i
++) {
185 dest
[i
] = char(src
[start
+ i
]);
191 * Copy characters in |s[start..start + len]| to |dest[0..len]|.
193 * This function is fallible. If you already have a linear string, use the
194 * infallible |JS::CopyLinearStringChars| above instead.
196 inline MOZ_MUST_USE
bool CopyStringChars(JSContext
* cx
, char16_t
* dest
,
197 JSString
* s
, size_t len
,
199 JSLinearString
* linear
= StringToLinearString(cx
, s
);
204 CopyLinearStringChars(dest
, linear
, len
, start
);
209 * Copy characters in |s[start..start + len]| to |dest[0..len]|, lossily
210 * truncating 16-bit values to |char| if necessary.
212 * This function is fallible. If you already have a linear string, use the
213 * infallible |JS::LossyCopyLinearStringChars| above instead.
215 inline MOZ_MUST_USE
bool LossyCopyStringChars(JSContext
* cx
, char* dest
,
216 JSString
* s
, size_t len
,
218 JSLinearString
* linear
= StringToLinearString(cx
, s
);
223 LossyCopyLinearStringChars(dest
, linear
, len
, start
);
229 /** DO NOT USE, only present for Rust bindings as a temporary hack */
230 [[deprecated
]] extern JS_PUBLIC_API
bool JS_DeprecatedStringHasLatin1Chars(
233 #endif // js_String_h