/ runtime / JSStringJoiner.cpp
JSStringJoiner.cpp
  1  /*
  2   * Copyright (C) 2012-2019 Apple Inc. All rights reserved.
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer.
  9   * 2. Redistributions in binary form must reproduce the above copyright
 10   *    notice, this list of conditions and the following disclaimer in the
 11   *    documentation and/or other materials provided with the distribution.
 12   *
 13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #include "config.h"
 27  #include "JSStringJoiner.h"
 28  
 29  #include "JSCJSValueInlines.h"
 30  
 31  namespace JSC {
 32  
 33  JSStringJoiner::~JSStringJoiner()
 34  {
 35  }
 36  
 37  template<typename CharacterType>
 38  static inline void appendStringToData(CharacterType*& data, StringView string)
 39  {
 40      string.getCharactersWithUpconvert(data);
 41      data += string.length();
 42  }
 43  
 44  template<typename CharacterType>
 45  static inline String joinStrings(const Vector<StringViewWithUnderlyingString>& strings, StringView separator, unsigned joinedLength)
 46  {
 47      ASSERT(joinedLength);
 48  
 49      CharacterType* data;
 50      String result = StringImpl::tryCreateUninitialized(joinedLength, data);
 51      if (UNLIKELY(result.isNull()))
 52          return result;
 53  
 54      appendStringToData(data, strings[0].view);
 55  
 56      unsigned size = strings.size();
 57  
 58      switch (separator.length()) {
 59      case 0:
 60          for (unsigned i = 1; i < size; ++i)
 61              appendStringToData(data, strings[i].view);
 62          break;
 63      case 1: {
 64          CharacterType separatorCharacter = separator[0];
 65          for (unsigned i = 1; i < size; ++i) {
 66              *data++ = separatorCharacter;
 67              appendStringToData(data, strings[i].view);
 68          }
 69          break;
 70      }
 71      default:
 72          for (unsigned i = 1; i < size; ++i) {
 73              appendStringToData(data, separator);
 74              appendStringToData(data, strings[i].view);
 75          }
 76      }
 77      ASSERT(data == result.characters<CharacterType>() + joinedLength);
 78  
 79      return result;
 80  }
 81  
 82  inline unsigned JSStringJoiner::joinedLength(JSGlobalObject* globalObject) const
 83  {
 84      VM& vm = globalObject->vm();
 85      auto scope = DECLARE_THROW_SCOPE(vm);
 86  
 87      unsigned numberOfStrings = m_strings.size();
 88      if (!numberOfStrings)
 89          return 0;
 90  
 91      Checked<int32_t, RecordOverflow> separatorLength = m_separator.length();
 92      Checked<int32_t, RecordOverflow> totalSeparatorsLength = separatorLength * (numberOfStrings - 1);
 93      Checked<int32_t, RecordOverflow> totalLength = totalSeparatorsLength + m_accumulatedStringsLength;
 94  
 95      int32_t result;
 96      if (totalLength.safeGet(result) == CheckedState::DidOverflow) {
 97          throwOutOfMemoryError(globalObject, scope);
 98          return 0;
 99      }
100      return result;
101  }
102  
103  JSValue JSStringJoiner::join(JSGlobalObject* globalObject)
104  {
105      VM& vm = globalObject->vm();
106      auto scope = DECLARE_THROW_SCOPE(vm);
107  
108      ASSERT(m_strings.size() <= m_strings.capacity());
109  
110      unsigned length = joinedLength(globalObject);
111      RETURN_IF_EXCEPTION(scope, JSValue());
112  
113      if (!length)
114          return jsEmptyString(vm);
115  
116      String result;
117      if (m_isAll8Bit)
118          result = joinStrings<LChar>(m_strings, m_separator, length);
119      else
120          result = joinStrings<UChar>(m_strings, m_separator, length);
121  
122      if (result.isNull())
123          return throwOutOfMemoryError(globalObject, scope);
124  
125      return jsString(vm, WTFMove(result));
126  }
127  
128  }