Serenity Operating System
at hosted 201 lines 5.6 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/FlyString.h> 28#include <AK/HashTable.h> 29#include <AK/Memory.h> 30#include <AK/StdLibExtras.h> 31#include <AK/StringImpl.h> 32#include <AK/kmalloc.h> 33 34//#define DEBUG_STRINGIMPL 35 36#ifdef DEBUG_STRINGIMPL 37unsigned g_stringimpl_count; 38static HashTable<StringImpl*>* g_all_live_stringimpls; 39 40void dump_all_stringimpls() 41{ 42 unsigned i = 0; 43 for (auto& it : *g_all_live_stringimpls) { 44 dbgprsize_tf("%u: \"%s\"\n", i, (*it).characters()); 45 ++i; 46 } 47} 48#endif 49 50namespace AK { 51 52static StringImpl* s_the_empty_stringimpl = nullptr; 53 54StringImpl& StringImpl::the_empty_stringimpl() 55{ 56 if (!s_the_empty_stringimpl) { 57 void* slot = kmalloc(sizeof(StringImpl) + sizeof(char)); 58 s_the_empty_stringimpl = new (slot) StringImpl(ConstructTheEmptyStringImpl); 59 } 60 return *s_the_empty_stringimpl; 61} 62 63StringImpl::StringImpl(ConstructWithInlineBufferTag, size_t length) 64 : m_length(length) 65{ 66#ifdef DEBUG_STRINGIMPL 67 if (!g_all_live_stringimpls) 68 g_all_live_stringimpls = new HashTable<StringImpl*>; 69 ++g_stringimpl_count; 70 g_all_live_stringimpls->set(this); 71#endif 72} 73 74StringImpl::~StringImpl() 75{ 76 if (m_fly) 77 FlyString::did_destroy_impl({}, *this); 78#ifdef DEBUG_STRINGIMPL 79 --g_stringimpl_count; 80 g_all_live_stringimpls->remove(this); 81#endif 82} 83 84static inline size_t allocation_size_for_stringimpl(size_t length) 85{ 86 return sizeof(StringImpl) + (sizeof(char) * length) + sizeof(char); 87} 88 89NonnullRefPtr<StringImpl> StringImpl::create_uninitialized(size_t length, char*& buffer) 90{ 91 ASSERT(length); 92 void* slot = kmalloc(allocation_size_for_stringimpl(length)); 93 ASSERT(slot); 94 auto new_stringimpl = adopt(*new (slot) StringImpl(ConstructWithInlineBuffer, length)); 95 buffer = const_cast<char*>(new_stringimpl->characters()); 96 buffer[length] = '\0'; 97 return new_stringimpl; 98} 99 100RefPtr<StringImpl> StringImpl::create(const char* cstring, size_t length, ShouldChomp should_chomp) 101{ 102 if (!cstring) 103 return nullptr; 104 105 if (!length || !*cstring) 106 return the_empty_stringimpl(); 107 108 if (should_chomp) { 109 while (length) { 110 char last_ch = cstring[length - 1]; 111 if (!last_ch || last_ch == '\n' || last_ch == '\r') 112 --length; 113 else 114 break; 115 } 116 } 117 118 if (!length) 119 return the_empty_stringimpl(); 120 121 char* buffer; 122 auto new_stringimpl = create_uninitialized(length, buffer); 123 memcpy(buffer, cstring, length * sizeof(char)); 124 125 return new_stringimpl; 126} 127 128RefPtr<StringImpl> StringImpl::create(const char* cstring, ShouldChomp shouldChomp) 129{ 130 if (!cstring) 131 return nullptr; 132 133 return create(cstring, strlen(cstring), shouldChomp); 134} 135 136static inline bool is_ascii_lowercase(char c) 137{ 138 return c >= 'a' && c <= 'z'; 139} 140 141static inline bool is_ascii_uppercase(char c) 142{ 143 return c >= 'A' && c <= 'Z'; 144} 145 146static inline char to_ascii_lowercase(char c) 147{ 148 if (is_ascii_uppercase(c)) 149 return c | 0x20; 150 return c; 151} 152 153static inline char to_ascii_uppercase(char c) 154{ 155 if (is_ascii_lowercase(c)) 156 return c & ~0x20; 157 return c; 158} 159 160NonnullRefPtr<StringImpl> StringImpl::to_lowercase() const 161{ 162 for (size_t i = 0; i < m_length; ++i) { 163 if (!is_ascii_lowercase(characters()[i])) 164 goto slow_path; 165 } 166 return const_cast<StringImpl&>(*this); 167 168slow_path: 169 char* buffer; 170 auto lowercased = create_uninitialized(m_length, buffer); 171 for (size_t i = 0; i < m_length; ++i) 172 buffer[i] = to_ascii_lowercase(characters()[i]); 173 return lowercased; 174} 175 176NonnullRefPtr<StringImpl> StringImpl::to_uppercase() const 177{ 178 for (size_t i = 0; i < m_length; ++i) { 179 if (!is_ascii_uppercase(characters()[i])) 180 goto slow_path; 181 } 182 return const_cast<StringImpl&>(*this); 183 184slow_path: 185 char* buffer; 186 auto uppercased = create_uninitialized(m_length, buffer); 187 for (size_t i = 0; i < m_length; ++i) 188 buffer[i] = to_ascii_uppercase(characters()[i]); 189 return uppercased; 190} 191 192void StringImpl::compute_hash() const 193{ 194 if (!length()) 195 m_hash = 0; 196 else 197 m_hash = string_hash(characters(), m_length); 198 m_has_hash = true; 199} 200 201}