/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* JavaScript string operations. */

#ifndef js_String_h
#define js_String_h

#include "js/shadow/String.h"  // JS::shadow::String

#include "mozilla/Assertions.h"    // MOZ_ASSERT
#include "mozilla/Attributes.h"    // MOZ_ALWAYS_INLINE
#include "mozilla/Likely.h"        // MOZ_LIKELY
#include "mozilla/Maybe.h"         // mozilla::Maybe
#include "mozilla/Range.h"         // mozilla::Range
#include "mozilla/RefPtr.h"        // RefPtr
#include "mozilla/Span.h"          // mozilla::Span
                                   // std::tuple
#include "mozilla/StringBuffer.h"  // mozilla::StringBuffer

#include <algorithm>  // std::copy_n
#include <stddef.h>   // size_t
#include <stdint.h>   // uint32_t, uint64_t, INT32_MAX

#include "jstypes.h"  // JS_PUBLIC_API

#include "js/CharacterEncoding.h"  // JS::UTF8Chars, JS::ConstUTF8CharsZ
#include "js/RootingAPI.h"         // JS::Handle
#include "js/TypeDecls.h"          // JS::Latin1Char
#include "js/UniquePtr.h"          // JS::UniquePtr
#include "js/Utility.h"            // JS::FreePolicy, JS::UniqueTwoByteChars
#include "js/Value.h"              // JS::Value

struct JS_PUBLIC_API JSContext;
class JS_PUBLIC_API JSAtom;
class JSLinearString;
class JS_PUBLIC_API JSString;

namespace JS {

class JS_PUBLIC_API AutoRequireNoGC;

}  // namespace JS

extern JS_PUBLIC_API JSString* JS_GetEmptyString(JSContext* cx);

// Don't want to export data, so provide accessors for non-inline Values.
extern JS_PUBLIC_API JS::Value JS_GetEmptyStringValue(JSContext* cx);

/*
 * String creation.
 *
 * NB: JS_NewUCString takes ownership of bytes on success, avoiding a copy;
 * but on error (signified by null return), it leaves chars owned by the
 * caller. So the caller must free bytes in the error case, if it has no use
 * for them. In contrast, all the JS_New*StringCopy* functions do not take
 * ownership of the character memory passed to them -- they copy it.
 */

extern JS_PUBLIC_API JSString* JS_NewStringCopyN(JSContext* cx, const char* s,
                                                 size_t n);

extern JS_PUBLIC_API JSString* JS_NewStringCopyZ(JSContext* cx, const char* s);

extern JS_PUBLIC_API JSString* JS_NewStringCopyUTF8Z(
    JSContext* cx, const JS::ConstUTF8CharsZ s);

extern JS_PUBLIC_API JSString* JS_NewStringCopyUTF8N(JSContext* cx,
                                                     const JS::UTF8Chars& s);

extern JS_PUBLIC_API JSString* JS_AtomizeStringN(JSContext* cx, const char* s,
                                                 size_t length);

extern JS_PUBLIC_API JSString* JS_AtomizeString(JSContext* cx, const char* s);

// Note: unlike the non-pinning JS_Atomize* functions, this can be called
// without entering a realm/zone.
extern JS_PUBLIC_API JSString* JS_AtomizeAndPinStringN(JSContext* cx,
                                                       const char* s,
                                                       size_t length);

// Note: unlike the non-pinning JS_Atomize* functions, this can be called
// without entering a realm/zone.
extern JS_PUBLIC_API JSString* JS_AtomizeAndPinString(JSContext* cx,
                                                      const char* s);

extern JS_PUBLIC_API JSString* JS_NewLatin1String(
    JSContext* cx, js::UniquePtr<JS::Latin1Char[], JS::FreePolicy> chars,
    size_t length);

extern JS_PUBLIC_API JSString* JS_NewUCString(JSContext* cx,
                                              JS::UniqueTwoByteChars chars,
                                              size_t length);

extern JS_PUBLIC_API JSString* JS_NewUCStringDontDeflate(
    JSContext* cx, JS::UniqueTwoByteChars chars, size_t length);

extern JS_PUBLIC_API JSString* JS_NewUCStringCopyN(JSContext* cx,
                                                   const char16_t* s, size_t n);

extern JS_PUBLIC_API JSString* JS_NewUCStringCopyZ(JSContext* cx,
                                                   const char16_t* s);

namespace JS {

/**
 * Create a new JSString possibly backed by |buffer|. The contents of |buffer|
 * will be interpreted as an array of Latin1 characters.
 *
 * Note that the returned string is not guaranteed to use |buffer|: as an
 * optimization, this API can return an inline string or a previously allocated
 * string.
 *
 * Increments the buffer's refcount iff the JS string holds a reference to it.
 */
extern JS_PUBLIC_API JSString* NewStringFromLatin1Buffer(
    JSContext* cx, RefPtr<mozilla::StringBuffer> buffer, size_t length);

/**
 * Like NewStringFromLatin1Buffer, but can be used to avoid refcounting overhead
 * in cases where the returned string doesn't use the buffer. The caller must
 * ensure the buffer outlives this call.
 */
extern JS_PUBLIC_API JSString* NewStringFromKnownLiveLatin1Buffer(
    JSContext* cx, mozilla::StringBuffer* buffer, size_t length);

/**
 * Similar to NewStringFromLatin1Buffer but for char16_t buffers.
 */
extern JS_PUBLIC_API JSString* NewStringFromTwoByteBuffer(
    JSContext* cx, RefPtr<mozilla::StringBuffer> buffer, size_t length);

/**
 * Similar to NewStringFromKnownLiveLatin1Buffer but for char16_t buffers.
 */
extern JS_PUBLIC_API JSString* NewStringFromKnownLiveTwoByteBuffer(
    JSContext* cx, mozilla::StringBuffer* buffer, size_t length);

/**
 * Similar to NewStringFromLatin1Buffer but for UTF8 buffers.
 *
 * This can create a Latin1 string backed by |buffer| iff the utf8 buffer
 * contains only ASCII chars. If there are non-ASCII chars, |buffer| can't be
 * used so this API will copy and inflate the characters for the new JS string.
 *
 * Note that |length| must be the (byte) length of the UTF8 buffer.
 */
extern JS_PUBLIC_API JSString* NewStringFromUTF8Buffer(
    JSContext* cx, RefPtr<mozilla::StringBuffer> buffer, size_t length);

/**
 * Like NewStringFromUTF8Buffer, but can be used to avoid refcounting overhead
 * in cases where the returned string doesn't use the buffer. The caller must
 * ensure the buffer outlives this call.
 */
extern JS_PUBLIC_API JSString* NewStringFromKnownLiveUTF8Buffer(
    JSContext* cx, mozilla::StringBuffer* buffer, size_t length);

}  // namespace JS

extern JS_PUBLIC_API JSString* JS_AtomizeUCStringN(JSContext* cx,
                                                   const char16_t* s,
                                                   size_t length);

extern JS_PUBLIC_API JSString* JS_AtomizeUCString(JSContext* cx,
                                                  const char16_t* s);

extern JS_PUBLIC_API bool JS_CompareStrings(JSContext* cx, JSString* str1,
                                            JSString* str2, int32_t* result);

[[nodiscard]] extern JS_PUBLIC_API bool JS_StringEqualsAscii(
    JSContext* cx, JSString* str, const char* asciiBytes, bool* match);

// Same as above, but when the length of asciiBytes (excluding the
// trailing null, if any) is known.
[[nodiscard]] extern JS_PUBLIC_API bool JS_StringEqualsAscii(
    JSContext* cx, JSString* str, const char* asciiBytes, size_t length,
    bool* match);

template <size_t N>
[[nodiscard]] bool JS_StringEqualsLiteral(JSContext* cx, JSString* str,
                                          const char (&asciiBytes)[N],
                                          bool* match) {
  MOZ_ASSERT(asciiBytes[N - 1] == '\0');
  return JS_StringEqualsAscii(cx, str, asciiBytes, N - 1, match);
}

extern JS_PUBLIC_API size_t JS_PutEscapedString(JSContext* cx, char* buffer,
                                                size_t size, JSString* str,
                                                char quote);

/*
 * Extracting string characters and length.
 *
 * While getting the length of a string is infallible, getting the chars can
 * fail. As indicated by the lack of a JSContext parameter, there are two
 * special cases where getting the chars is infallible:
 *
 * The first case is for strings that have been atomized, e.g. directly by
 * JS_AtomizeAndPinString or implicitly because it is stored in a jsid.
 *
 * The second case is "linear" strings that have been explicitly prepared in a
 * fallible context by JS_EnsureLinearString. To catch errors, a separate opaque
 * JSLinearString type is returned by JS_EnsureLinearString and expected by
 * JS_Get{Latin1,TwoByte}StringCharsAndLength. Note, though, that this is purely
 * a syntactic distinction: the input and output of JS_EnsureLinearString are
 * the same actual GC-thing. If a JSString is known to be linear,
 * JS_ASSERT_STRING_IS_LINEAR can be used to make a debug-checked cast. Example:
 *
 *   // In a fallible context.
 *   JSLinearString* lstr = JS_EnsureLinearString(cx, str);
 *   if (!lstr) {
 *     return false;
 *   }
 *   MOZ_ASSERT(lstr == JS_ASSERT_STRING_IS_LINEAR(str));
 *
 *   // In an infallible context, for the same 'str'.
 *   AutoCheckCannotGC nogc;
 *   const char16_t* chars = JS::GetTwoByteLinearStringChars(nogc, lstr)
 *   MOZ_ASSERT(chars);
 *
 * Note: JS strings (including linear strings and atoms) are not
 * null-terminated!
 *
 * Additionally, string characters are stored as either Latin1Char (8-bit)
 * or char16_t (16-bit). Clients can use JS::StringHasLatin1Chars and can then
 * call either the Latin1* or TwoByte* functions. Some functions like
 * JS_CopyStringChars and JS_GetStringCharAt accept both Latin1 and TwoByte
 * strings.
 */

extern JS_PUBLIC_API size_t JS_GetStringLength(JSString* str);

extern JS_PUBLIC_API bool JS_StringIsLinear(JSString* str);

extern JS_PUBLIC_API const JS::Latin1Char* JS_GetLatin1StringCharsAndLength(
    JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
    size_t* length);

extern JS_PUBLIC_API const char16_t* JS_GetTwoByteStringCharsAndLength(
    JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
    size_t* length);

extern JS_PUBLIC_API bool JS_GetStringCharAt(JSContext* cx, JSString* str,
                                             size_t index, char16_t* res);

extern JS_PUBLIC_API const char16_t* JS_GetTwoByteExternalStringChars(
    JSString* str);

extern JS_PUBLIC_API bool JS_CopyStringChars(
    JSContext* cx, const mozilla::Range<char16_t>& dest, JSString* str);

/**
 * Copies the string's characters to a null-terminated char16_t buffer.
 *
 * Returns nullptr on OOM.
 */
extern JS_PUBLIC_API JS::UniqueTwoByteChars JS_CopyStringCharsZ(JSContext* cx,
                                                                JSString* str);

extern JS_PUBLIC_API JSLinearString* JS_EnsureLinearString(JSContext* cx,
                                                           JSString* str);

static MOZ_ALWAYS_INLINE JSLinearString* JS_ASSERT_STRING_IS_LINEAR(
    JSString* str) {
  MOZ_ASSERT(JS_StringIsLinear(str));
  return reinterpret_cast<JSLinearString*>(str);
}

static MOZ_ALWAYS_INLINE JSString* JS_FORGET_STRING_LINEARNESS(
    JSLinearString* str) {
  return reinterpret_cast<JSString*>(str);
}

/*
 * Additional APIs that avoid fallibility when given a linear string.
 */

extern JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
                                                     const char* asciiBytes);
extern JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
                                                     const char* asciiBytes,
                                                     size_t length);

template <size_t N>
bool JS_LinearStringEqualsLiteral(JSLinearString* str,
                                  const char (&asciiBytes)[N]) {
  MOZ_ASSERT(asciiBytes[N - 1] == '\0');
  return JS_LinearStringEqualsAscii(str, asciiBytes, N - 1);
}

extern JS_PUBLIC_API size_t JS_PutEscapedLinearString(char* buffer, size_t size,
                                                      JSLinearString* str,
                                                      char quote);

/**
 * Create a dependent string, i.e., a string that owns no character storage,
 * but that refers to a slice of another string's chars.  Dependent strings
 * are mutable by definition, so the thread safety comments above apply.
 */
extern JS_PUBLIC_API JSString* JS_NewDependentString(JSContext* cx,
                                                     JS::Handle<JSString*> str,
                                                     size_t start,
                                                     size_t length);

/**
 * Concatenate two strings, possibly resulting in a rope.
 * See above for thread safety comments.
 */
extern JS_PUBLIC_API JSString* JS_ConcatStrings(JSContext* cx,
                                                JS::Handle<JSString*> left,
                                                JS::Handle<JSString*> right);

/**
 * For JS_DecodeBytes, set *dstlenp to the size of the destination buffer before
 * the call; on return, *dstlenp contains the number of characters actually
 * stored. To determine the necessary destination buffer size, make a sizing
 * call that passes nullptr for dst.
 *
 * On errors, the functions report the error. In that case, *dstlenp contains
 * the number of characters or bytes transferred so far.  If cx is nullptr, no
 * error is reported on failure, and the functions simply return false.
 *
 * NB: This function does not store an additional zero byte or char16_t after
 * the transcoded string.
 */
JS_PUBLIC_API bool JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen,
                                  char16_t* dst, size_t* dstlenp);

/**
 * Get number of bytes in the string encoding (without accounting for a
 * terminating zero bytes. The function returns (size_t) -1 if the string
 * can not be encoded into bytes and reports an error using cx accordingly.
 */
JS_PUBLIC_API size_t JS_GetStringEncodingLength(JSContext* cx, JSString* str);

/**
 * Encode string into a buffer. The function does not stores an additional
 * zero byte. The function returns (size_t) -1 if the string can not be
 * encoded into bytes with no error reported. Otherwise it returns the number
 * of bytes that are necessary to encode the string. If that exceeds the
 * length parameter, the string will be cut and only length bytes will be
 * written into the buffer.
 */
[[nodiscard]] JS_PUBLIC_API bool JS_EncodeStringToBuffer(JSContext* cx,
                                                         JSString* str,
                                                         char* buffer,
                                                         size_t length);

/**
 * Encode as many scalar values of the string as UTF-8 as can fit
 * into the caller-provided buffer replacing unpaired surrogates
 * with the REPLACEMENT CHARACTER.
 *
 * If JS::StringHasLatin1Chars(str) returns true, the function
 * is guaranteed to convert the entire string if
 * buffer.Length() >= 2 * JS_GetStringLength(str). Otherwise,
 * the function is guaranteed to convert the entire string if
 * buffer.Length() >= 3 * JS_GetStringLength(str).
 *
 * This function does not alter the representation of |str| or
 * any |JSString*| substring that is a constituent part of it.
 * Returns mozilla::Nothing() on OOM, without reporting an error;
 * some data may have been written to |buffer| when this happens.
 *
 * If there's no OOM, returns the number of code units read and
 * the number of code units written.
 *
 * The semantics of this method match the semantics of
 * TextEncoder.encodeInto().
 *
 * The function does not store an additional zero byte.
 */
JS_PUBLIC_API mozilla::Maybe<std::tuple<size_t, size_t>>
JS_EncodeStringToUTF8BufferPartial(JSContext* cx, JSString* str,
                                   mozilla::Span<char> buffer);

namespace JS {

/**
 * Maximum length of a JS string. This is chosen so that the number of bytes
 * allocated for a null-terminated TwoByte string still fits in int32_t.
 */
static constexpr uint32_t MaxStringLength = (1 << 30) - 2;

static_assert((uint64_t(MaxStringLength) + 1) * sizeof(char16_t) <= INT32_MAX,
              "size of null-terminated JSString char buffer must fit in "
              "INT32_MAX");

/** Compute the length of a string. */
MOZ_ALWAYS_INLINE size_t GetStringLength(JSString* s) {
  return shadow::AsShadowString(s)->length();
}

/** Compute the length of a linear string. */
MOZ_ALWAYS_INLINE size_t GetLinearStringLength(JSLinearString* s) {
  return shadow::AsShadowString(s)->length();
}

/** Return true iff the given linear string uses Latin-1 storage. */
MOZ_ALWAYS_INLINE bool LinearStringHasLatin1Chars(JSLinearString* s) {
  return shadow::AsShadowString(s)->hasLatin1Chars();
}

/** Return true iff the given string uses Latin-1 storage. */
MOZ_ALWAYS_INLINE bool StringHasLatin1Chars(JSString* s) {
  return shadow::AsShadowString(s)->hasLatin1Chars();
}

/**
 * Given a linear string known to use Latin-1 storage, return a pointer to that
 * storage.  This pointer remains valid only as long as no GC occurs.
 */
MOZ_ALWAYS_INLINE const Latin1Char* GetLatin1LinearStringChars(
    const AutoRequireNoGC& nogc, JSLinearString* linear) {
  return shadow::AsShadowString(linear)->latin1LinearChars();
}

/**
 * Given a linear string known to use two-byte storage, return a pointer to that
 * storage.  This pointer remains valid only as long as no GC occurs.
 */
MOZ_ALWAYS_INLINE const char16_t* GetTwoByteLinearStringChars(
    const AutoRequireNoGC& nogc, JSLinearString* linear) {
  return shadow::AsShadowString(linear)->twoByteLinearChars();
}

/**
 * Given an in-range index into the provided string, return the character at
 * that index.
 */
MOZ_ALWAYS_INLINE char16_t GetLinearStringCharAt(JSLinearString* linear,
                                                 size_t index) {
  shadow::String* s = shadow::AsShadowString(linear);
  MOZ_ASSERT(index < s->length());

  return s->hasLatin1Chars() ? s->latin1LinearChars()[index]
                             : s->twoByteLinearChars()[index];
}

/**
 * Convert an atom to a linear string.  All atoms are linear, so this
 * operation is infallible.
 */
MOZ_ALWAYS_INLINE JSLinearString* AtomToLinearString(JSAtom* atom) {
  return reinterpret_cast<JSLinearString*>(atom);
}

/**
 * If the provided string uses externally-managed latin-1 storage, return true
 * and set |*callbacks| to the external-string callbacks used to create it and
 * |*chars| to a pointer to its latin1 storage.  (These pointers remain valid
 * as long as the provided string is kept alive.)
 */
MOZ_ALWAYS_INLINE bool IsExternalStringLatin1(
    JSString* str, const JSExternalStringCallbacks** callbacks,
    const JS::Latin1Char** chars) {
  shadow::String* s = shadow::AsShadowString(str);

  if (!s->isExternal() || !s->hasLatin1Chars()) {
    return false;
  }

  *callbacks = s->externalCallbacks;
  *chars = s->nonInlineCharsLatin1;
  return true;
}

/**
 * If the provided string uses externally-managed two-byte storage, return true
 * and set |*callbacks| to the external-string callbacks used to create it and
 * |*chars| to a pointer to its two-byte storage.  (These pointers remain valid
 * as long as the provided string is kept alive.)
 */
MOZ_ALWAYS_INLINE bool IsExternalUCString(
    JSString* str, const JSExternalStringCallbacks** callbacks,
    const char16_t** chars) {
  shadow::String* s = shadow::AsShadowString(str);

  if (!s->isExternal() || s->hasLatin1Chars()) {
    return false;
  }

  *callbacks = s->externalCallbacks;
  *chars = s->nonInlineCharsTwoByte;
  return true;
}

/**
 * If the provided string is backed by a StringBuffer for latin-1 storage,
 * return true and set |*buffer| to the string buffer.
 *
 * Note: this function doesn't increment the buffer's refcount. The buffer
 * remains valid as long as the provided string is kept alive.
 */
MOZ_ALWAYS_INLINE bool IsLatin1StringWithStringBuffer(
    JSString* str, mozilla::StringBuffer** buffer) {
  shadow::String* s = shadow::AsShadowString(str);

  if (!s->hasStringBuffer() || !s->hasLatin1Chars()) {
    return false;
  }

  void* data = const_cast<JS::Latin1Char*>(s->nonInlineCharsLatin1);
  *buffer = mozilla::StringBuffer::FromData(data);
  return true;
}

/**
 * If the provided string is backed by a StringBuffer for char16_t storage,
 * return true and set |*buffer| to the string buffer.
 *
 * Note: this function doesn't increment the buffer's refcount. The buffer
 * remains valid as long as the provided string is kept alive.
 */
MOZ_ALWAYS_INLINE bool IsTwoByteStringWithStringBuffer(
    JSString* str, mozilla::StringBuffer** buffer) {
  shadow::String* s = shadow::AsShadowString(str);

  if (!s->hasStringBuffer() || s->hasLatin1Chars()) {
    return false;
  }

  void* data = const_cast<char16_t*>(s->nonInlineCharsTwoByte);
  *buffer = mozilla::StringBuffer::FromData(data);
  return true;
}

namespace detail {

extern JS_PUBLIC_API JSLinearString* StringToLinearStringSlow(JSContext* cx,
                                                              JSString* str);

}  // namespace detail

/** Convert a string to a linear string. */
MOZ_ALWAYS_INLINE JSLinearString* StringToLinearString(JSContext* cx,
                                                       JSString* str) {
  if (MOZ_LIKELY(shadow::AsShadowString(str)->isLinear())) {
    return reinterpret_cast<JSLinearString*>(str);
  }

  return detail::StringToLinearStringSlow(cx, str);
}

/** Copy characters in |s[start..start + len]| to |dest[0..len]|. */
MOZ_ALWAYS_INLINE void CopyLinearStringChars(char16_t* dest, JSLinearString* s,
                                             size_t len, size_t start = 0) {
#ifdef DEBUG
  size_t stringLen = GetLinearStringLength(s);
  MOZ_ASSERT(start <= stringLen);
  MOZ_ASSERT(len <= stringLen - start);
#endif

  shadow::String* str = shadow::AsShadowString(s);

  if (str->hasLatin1Chars()) {
    const Latin1Char* src = str->latin1LinearChars();
    for (size_t i = 0; i < len; i++) {
      dest[i] = src[start + i];
    }
  } else {
    const char16_t* src = str->twoByteLinearChars();
    std::copy_n(src + start, len, dest);
  }
}

/**
 * Copy characters in |s[start..start + len]| to |dest[0..len]|, lossily
 * truncating 16-bit values to |char| if necessary.
 */
MOZ_ALWAYS_INLINE void LossyCopyLinearStringChars(char* dest, JSLinearString* s,
                                                  size_t len,
                                                  size_t start = 0) {
#ifdef DEBUG
  size_t stringLen = GetLinearStringLength(s);
  MOZ_ASSERT(start <= stringLen);
  MOZ_ASSERT(len <= stringLen - start);
#endif

  shadow::String* str = shadow::AsShadowString(s);

  if (LinearStringHasLatin1Chars(s)) {
    const Latin1Char* src = str->latin1LinearChars();
    for (size_t i = 0; i < len; i++) {
      dest[i] = char(src[start + i]);
    }
  } else {
    const char16_t* src = str->twoByteLinearChars();
    for (size_t i = 0; i < len; i++) {
      dest[i] = char(src[start + i]);
    }
  }
}

/**
 * Copy characters in |s[start..start + len]| to |dest[0..len]|.
 *
 * This function is fallible.  If you already have a linear string, use the
 * infallible |JS::CopyLinearStringChars| above instead.
 */
[[nodiscard]] inline bool CopyStringChars(JSContext* cx, char16_t* dest,
                                          JSString* s, size_t len,
                                          size_t start = 0) {
  JSLinearString* linear = StringToLinearString(cx, s);
  if (!linear) {
    return false;
  }

  CopyLinearStringChars(dest, linear, len, start);
  return true;
}

/**
 * Copy characters in |s[start..start + len]| to |dest[0..len]|, lossily
 * truncating 16-bit values to |char| if necessary.
 *
 * This function is fallible.  If you already have a linear string, use the
 * infallible |JS::LossyCopyLinearStringChars| above instead.
 */
[[nodiscard]] inline bool LossyCopyStringChars(JSContext* cx, char* dest,
                                               JSString* s, size_t len,
                                               size_t start = 0) {
  JSLinearString* linear = StringToLinearString(cx, s);
  if (!linear) {
    return false;
  }

  LossyCopyLinearStringChars(dest, linear, len, start);
  return true;
}

}  // namespace JS

/** DO NOT USE, only present for Rust bindings as a temporary hack */
[[deprecated]] extern JS_PUBLIC_API bool JS_DeprecatedStringHasLatin1Chars(
    JSString* str);

// JSString* is an aligned pointer, but this information isn't available in the
// public header. We specialize HasFreeLSB here so that JS::Result<JSString*>
// compiles.

namespace mozilla {
namespace detail {
template <>
struct HasFreeLSB<JSString*> {
  static constexpr bool value = true;
};
}  // namespace detail
}  // namespace mozilla

#endif  // js_String_h
