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 }