text: /*
   * QuickJS Javascript Engine
   *
   * Copyright (c) 2017-2021 Fabrice Bellard
   * Copyright (c) 2017-2021 Charlie Gordon
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  #include "third_party/quickjs/internal.h"
  #include "third_party/quickjs/libregexp.h"
  #include "third_party/quickjs/quickjs.h"
  
  asm(".ident\t\"\\n\\n\
  QuickJS (MIT License)\\n\
  Copyright (c) 2017-2021 Fabrice Bellard\\n\
  Copyright (c) 2017-2021 Charlie Gordon\"");
  asm(".include \"libc/disclaimer.inc\"");
  /* clang-format off */
  
  /* It is valid to call string_buffer_end() and all string_buffer functions even
     if string_buffer_init() or another string_buffer function returns an error.
     If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
   */
  int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, int is_wide)
  {
      s->ctx = ctx;
      s->size = size;
      s->len = 0;
      s->is_wide_char = is_wide;
      s->error_status = 0;
      s->str = js_alloc_string(ctx, size, is_wide);
      if (UNLIKELY(!s->str)) {
          s->size = 0;
          return s->error_status = -1;
      }
  #ifdef DUMP_LEAKS
      /* the StringBuffer may reallocate the JSString, only link it at the end */
      list_del(&s->str->link);
  #endif
      return 0;
  }
  
  int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
  {
      return string_buffer_init2(ctx, s, size, 0);
  }
  
  void string_buffer_free(StringBuffer *s)
  {
      js_free(s->ctx, s->str);
      s->str = NULL;
  }
  
  int string_buffer_set_error(StringBuffer *s)
  {
      js_free(s->ctx, s->str);
      s->str = NULL;
      s->size = 0;
      s->len = 0;
      return s->error_status = -1;
  }
  
  int string_buffer_widen(StringBuffer *s, int size)
  {
      JSString *str;
      size_t slack;
      int i;
      if (s->error_status)
          return -1;
      str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
      if (!str)
          return string_buffer_set_error(s);
      size += slack >> 1;
      for(i = s->len; i-- > 0;) {
          str->u.str16[i] = str->u.str8[i];
      }
      s->is_wide_char = 1;
      s->size = size;
      s->str = str;
      return 0;
  }
  
  int string_buffer_realloc(StringBuffer *s, int new_len, int c)
  {
      JSString *new_str;
      int new_size;
      size_t new_size_bytes, slack;
      if (s->error_status)
          return -1;
      if (new_len > JS_STRING_LEN_MAX) {
          JS_ThrowInternalError(s->ctx, "string too long");
          return string_buffer_set_error(s);
      }
      new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
      if (!s->is_wide_char && c >= 0x100) {
          return string_buffer_widen(s, new_size);
      }
      new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
      new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
      if (!new_str)
          return string_buffer_set_error(s);
      new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
      s->size = new_size;
      s->str = new_str;
      return 0;
  }
  
  int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
  {
      if (UNLIKELY(s->len >= s->size)) {
          if (string_buffer_realloc(s, s->len + 1, c))
              return -1;
      }
      if (s->is_wide_char) {
          s->str->u.str16[s->len++] = c;
      } else if (c < 0x100) {
          s->str->u.str8[s->len++] = c;
      } else {
          if (string_buffer_widen(s, s->size))
              return -1;
          s->str->u.str16[s->len++] = c;
      }
      return 0;
  }
  
  /* 0 <= c <= 0xff */
  int string_buffer_putc8(StringBuffer *s, uint32_t c)
  {
      if (UNLIKELY(s->len >= s->size)) {
          if (string_buffer_realloc(s, s->len + 1, c))
              return -1;
      }
      if (s->is_wide_char) {
          s->str->u.str16[s->len++] = c;
      } else {
          s->str->u.str8[s->len++] = c;
      }
      return 0;
  }
  
  /* 0 <= c <= 0xffff */
  int string_buffer_putc16(StringBuffer *s, uint32_t c)
  {
      if (LIKELY(s->len < s->size)) {
          if (s->is_wide_char) {
              s->str->u.str16[s->len++] = c;
              return 0;
          } else if (c < 0x100) {
              s->str->u.str8[s->len++] = c;
              return 0;
          }
      }
      return string_buffer_putc_slow(s, c);
  }
  
  /* 0 <= c <= 0x10ffff */
  int string_buffer_putc(StringBuffer *s, uint32_t c)
  {
      if (UNLIKELY(c >= 0x10000)) {
          /* surrogate pair */
          c -= 0x10000;
          if (string_buffer_putc16(s, (c >> 10) + 0xd800))
              return -1;
          c = (c & 0x3ff) + 0xdc00;
      }
      return string_buffer_putc16(s, c);
  }
  
  int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
  {
      int i;
      if (s->len + len > s->size) {
          if (string_buffer_realloc(s, s->len + len, 0))
              return -1;
      }
      if (s->is_wide_char) {
          for (i = 0; i < len; i++) {
              s->str->u.str16[s->len + i] = p[i];
          }
          s->len += len;
      } else {
          memcpy(&s->str->u.str8[s->len], p, len);
          s->len += len;
      }
      return 0;
  }
  
  int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
  {
      int c = 0, i;
      for (i = 0; i < len; i++) {
          c |= p[i];
      }
      if (s->len + len > s->size) {
          if (string_buffer_realloc(s, s->len + len, c))
              return -1;
      } else if (!s->is_wide_char && c >= 0x100) {
          if (string_buffer_widen(s, s->size))
              return -1;
      }
      if (s->is_wide_char) {
          memcpy(&s->str->u.str16[s->len], p, len << 1);
          s->len += len;
      } else {
          for (i = 0; i < len; i++) {
              s->str->u.str8[s->len + i] = p[i];
          }
          s->len += len;
      }
      return 0;
  }
  
  /* appending an ASCII string */
  int string_buffer_puts8(StringBuffer *s, const char *str)
  {
      return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
  }
  
  int string_buffer_concat(StringBuffer *s, const JSString *p, uint32_t from, uint32_t to)
  {
      if (to <= from)
          return 0;
      if (p->is_wide_char)
          return string_buffer_write16(s, p->u.str16 + from, to - from);
      else
          return string_buffer_write8(s, p->u.str8 + from, to - from);
  }
  
  int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
  {
      JSString *p;
      JSValue v1;
      int res;
      if (s->error_status) {
          /* prevent exception overload */
          return -1;
      }
      if (UNLIKELY(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
          v1 = JS_ToString(s->ctx, v);
          if (JS_IsException(v1))
              return string_buffer_set_error(s);
          p = JS_VALUE_GET_STRING(v1);
          res = string_buffer_concat(s, p, 0, p->len);
          JS_FreeValue(s->ctx, v1);
          return res;
      }
      p = JS_VALUE_GET_STRING(v);
      return string_buffer_concat(s, p, 0, p->len);
  }
  
  int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
  {
      JSString *p;
      int res;
      if (s->error_status) {
          /* prevent exception overload */
          JS_FreeValue(s->ctx, v);
          return -1;
      }
      if (UNLIKELY(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
          v = JS_ToStringFree(s->ctx, v);
          if (JS_IsException(v))
              return string_buffer_set_error(s);
      }
      p = JS_VALUE_GET_STRING(v);
      res = string_buffer_concat(s, p, 0, p->len);
      JS_FreeValue(s->ctx, v);
      return res;
  }
  
  int string_buffer_fill(StringBuffer *s, int c, int count)
  {
      /* XXX: optimize */
      if (s->len + count > s->size) {
          if (string_buffer_realloc(s, s->len + count, c))
              return -1;
      }
      while (count-- > 0) {
          if (string_buffer_putc16(s, c))
              return -1;
      }
      return 0;
  }
  
  JSValue string_buffer_end(StringBuffer *s)
  {
      JSString *str;
      str = s->str;
      if (s->error_status)
          return JS_EXCEPTION;
      if (s->len == 0) {
          js_free(s->ctx, str);
          s->str = NULL;
          return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
      }
      if (s->len < s->size) {
          /* smaller size so js_realloc should not fail, but OK if it does */
          /* XXX: should add some slack to avoid unnecessary calls */
          /* XXX: might need to use malloc+free to ensure smaller size */
          str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
                              (s->len << s->is_wide_char) + 1 - s->is_wide_char);
          if (str == NULL)
              str = s->str;
          s->str = str;
      }
      if (!s->is_wide_char)
          str->u.str8[s->len] = 0;
  #ifdef DUMP_LEAKS
      list_add_tail(&str->link, &s->ctx->rt->string_list);
  #endif
      str->is_wide_char = s->is_wide_char;
      str->len = s->len;
      s->str = NULL;
      return JS_MKPTR(JS_TAG_STRING, str);
  }
  