UString UString::spliceSubstringsWithSeparators(const Range *substringRanges, int rangeCount, const UString *separators, int separatorCount) const { int totalLength = 0; for (int i = 0; i < rangeCount; i++) { totalLength += substringRanges[i].length; } for (int i = 0; i < separatorCount; i++) { totalLength += separators[i].size(); } UChar *buffer = static_cast<UChar *>(malloc(totalLength * sizeof(UChar))); int maxCount = MAX(rangeCount, separatorCount); int bufferPos = 0; for (int i = 0; i < maxCount; i++) { if (i < rangeCount) { memcpy(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length * sizeof(UChar)); bufferPos += substringRanges[i].length; } if (i < separatorCount) { memcpy(buffer + bufferPos, separators[i].data(), separators[i].size() * sizeof(UChar)); bufferPos += separators[i].size(); } } UString::Rep *rep = UString::Rep::create(buffer, totalLength); UString result = UString(rep); rep->deref(); return result; }
void PropertyMap::clear() { if (!_table) { #if USE_SINGLE_ENTRY UString::Rep *key = _singleEntry.key; if (key) { key->deref(); _singleEntry.key = 0; } #endif return; } int size = _table->size; Entry *entries = _table->entries; for (int i = 0; i < size; i++) { UString::Rep *key = entries[i].key; if (isValid(key)) { key->deref(); entries[i].key = 0; entries[i].value = 0; } } _table->keyCount = 0; _table->sentinelCount = 0; }
void PropertyMap::checkConsistency() { if (!_table) return; int count = 0; int sentinelCount = 0; for (int j = 0; j != _table->size; ++j) { UString::Rep *rep = _table->entries[j].key; if (!rep) continue; if (rep == deletedSentinel()) { ++sentinelCount; continue; } unsigned h = rep->hash(); int i = h & _table->sizeMask; int k = 0; while (UString::Rep *key = _table->entries[i].key) { if (rep == key) break; if (k == 0) k = 1 | (h % _table->sizeMask); i = (i + k) & _table->sizeMask; } assert(i == j); ++count; } assert(count == _table->keyCount); assert(sentinelCount == _table->sentinelCount); assert(_table->size >= 16); assert(_table->sizeMask); assert(_table->size == _table->sizeMask + 1); }
size_t JSStringGetMaximumUTF8CStringSize(JSStringRef string) { UString::Rep* rep = toJS(string); // Any UTF8 character > 3 bytes encodes as a UTF16 surrogate pair. return rep->size() * 3 + 1; // + 1 for terminating '\0' }
EXPORT void JSStringRelease(JSStringRef string) { JSLock lock; UString::Rep* rep = toJS(string); rep->deref(); }
void Structure::checkConsistency() { if (!m_propertyTable) return; ASSERT(m_propertyTable->size >= newTableSize); ASSERT(m_propertyTable->sizeMask); ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1); ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask)); ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2); ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4); ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2); unsigned indexCount = 0; unsigned deletedIndexCount = 0; for (unsigned a = 0; a != m_propertyTable->size; ++a) { unsigned entryIndex = m_propertyTable->entryIndices[a]; if (entryIndex == emptyEntryIndex) continue; if (entryIndex == deletedSentinelIndex) { ++deletedIndexCount; continue; } ASSERT(entryIndex > deletedSentinelIndex); ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount); ++indexCount; for (unsigned b = a + 1; b != m_propertyTable->size; ++b) ASSERT(m_propertyTable->entryIndices[b] != entryIndex); } ASSERT(indexCount == m_propertyTable->keyCount); ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount); ASSERT(m_propertyTable->entries()[0].key == 0); unsigned nonEmptyEntryCount = 0; for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) { UString::Rep* rep = m_propertyTable->entries()[c].key; if (!rep) continue; ++nonEmptyEntryCount; unsigned i = rep->computedHash(); unsigned k = 0; unsigned entryIndex; while (1) { entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; ASSERT(entryIndex != emptyEntryIndex); if (rep == m_propertyTable->entries()[entryIndex - 1].key) break; if (k == 0) k = 1 | doubleHash(rep->computedHash()); i += k; } ASSERT(entryIndex == c + 1); } ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount); }
static void translate(UString::Rep*& location, const UCharBuffer& buf, unsigned hash) { UChar* d; UString::Rep* r = UString::Rep::createUninitialized(buf.length, d).releaseRef(); for (unsigned i = 0; i != buf.length; i++) d[i] = buf.s[i]; r->setHash(hash); location = r; }
static void translate(UString::Rep*& location, const char* c, unsigned hash) { size_t length = strlen(c); UChar* d; UString::Rep* r = UString::Rep::createUninitialized(length, d).releaseRef(); for (size_t i = 0; i != length; i++) d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend r->setHash(hash); location = r; }
bool Structure::despecifyFunction(const Identifier& propertyName) { ASSERT(!propertyName.isNull()); materializePropertyMapIfNecessary(); if (!m_propertyTable) return false; UString::Rep* rep = propertyName._ustring.rep(); unsigned i = rep->existingHash(); #if DUMP_PROPERTYMAP_STATS ++numProbes; #endif unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return false; if (rep == m_propertyTable->entries()[entryIndex - 1].key) { ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); m_propertyTable->entries()[entryIndex - 1].specificValue = 0; return true; } #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif unsigned k = 1 | doubleHash(rep->existingHash()); while (1) { i += k; #if DUMP_PROPERTYMAP_STATS ++numRehashes; #endif entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return false; if (rep == m_propertyTable->entries()[entryIndex - 1].key) { ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); m_propertyTable->entries()[entryIndex - 1].specificValue = 0; return true; } } }
size_t Structure::get(const Identifier& propertyName, unsigned& attributes) { ASSERT(!propertyName.isNull()); materializePropertyMapIfNecessary(); if (!m_propertyTable) return notFound; UString::Rep* rep = propertyName._ustring.rep(); unsigned i = rep->computedHash(); #if DUMP_PROPERTYMAP_STATS ++numProbes; #endif unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return notFound; if (rep == m_propertyTable->entries()[entryIndex - 1].key) { attributes = m_propertyTable->entries()[entryIndex - 1].attributes; return m_propertyTable->entries()[entryIndex - 1].offset; } #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif unsigned k = 1 | doubleHash(rep->computedHash()); while (1) { i += k; #if DUMP_PROPERTYMAP_STATS ++numRehashes; #endif entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return notFound; if (rep == m_propertyTable->entries()[entryIndex - 1].key) { attributes = m_propertyTable->entries()[entryIndex - 1].attributes; return m_propertyTable->entries()[entryIndex - 1].offset; } } }
AtomicStringImpl* AtomicString::find(const KJS::Identifier& identifier) { if (identifier.isNull()) return 0; UString::Rep* string = identifier.ustring().rep(); unsigned length = string->size(); if (!length) return static_cast<AtomicStringImpl*>(StringImpl::empty()); HashAndCharacters buffer = { string->computedHash(), string->data(), length }; HashSet<StringImpl*>::iterator iterator = stringTable->find<HashAndCharacters, HashAndCharactersTranslator>(buffer); if (iterator == stringTable->end()) return 0; return static_cast<AtomicStringImpl*>(*iterator); }
PassRefPtr<StringImpl> AtomicString::add(const KJS::UString& ustring) { if (ustring.isNull()) return 0; UString::Rep* string = ustring.rep(); unsigned length = string->size(); if (!length) return StringImpl::empty(); HashAndCharacters buffer = { string->hash(), string->data(), length }; pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable->add<HashAndCharacters, HashAndCharactersTranslator>(buffer); if (!addResult.second) return *addResult.first; return adoptRef(*addResult.first); }
void HashTable::createTable(JSGlobalData* globalData) const { ASSERT(!table); HashEntry* entries = new HashEntry[hashSizeMask + 1]; for (int i = 0; i <= hashSizeMask; ++i) entries[i].key = 0; for (int i = 0; values[i].key; ++i) { UString::Rep* identifier = Identifier::add(globalData, values[i].key).releaseRef(); int hashIndex = identifier->computedHash() & hashSizeMask; ASSERT(!entries[hashIndex].key); entries[hashIndex].key = identifier; entries[hashIndex].integerValue = values[i].value; entries[hashIndex].attributes = values[i].attributes; entries[hashIndex].length = values[i].length; } table = entries; }
UString UString::substr(int pos, int len) const { if (pos < 0) pos = 0; else if (pos >= (int) size()) pos = size(); if (len < 0) len = size(); if (pos + len >= (int) size()) len = size() - pos; UString::Rep *newRep = Rep::create(rep, pos, len); UString result(newRep); newRep->deref(); return result; }
JSValue *PropertyMap::get(const Identifier &name, unsigned &attributes) const { assert(!name.isNull()); UString::Rep *rep = name._ustring.rep(); if (!_table) { #if USE_SINGLE_ENTRY UString::Rep *key = _singleEntry.key; if (rep == key) { attributes = _singleEntry.attributes; return _singleEntry.value; } #endif return 0; } unsigned h = rep->hash(); int sizeMask = _table->sizeMask; Entry *entries = _table->entries; int i = h & sizeMask; int k = 0; #if DUMP_STATISTICS ++numProbes; numCollisions += entries[i].key && entries[i].key != rep; #endif while (UString::Rep *key = entries[i].key) { if (rep == key) { attributes = entries[i].attributes; return entries[i].value; } if (k == 0) k = 1 | (h % sizeMask); i = (i + k) & sizeMask; #if DUMP_STATISTICS ++numRehashes; #endif } return 0; }
UString UString::substr(int pos, int len) const { int s = size(); if (pos < 0) pos = 0; else if (pos >= s) pos = s; if (len < 0) len = s; if (pos + len >= s) len = s - pos; if (pos == 0 && len == s) return *this; UString::Rep *newRep = Rep::create(rep, pos, len); UString result(newRep); newRep->deref(); return result; }
void HashTable::createTable(JSGlobalData* globalData) const { #if ENABLE(PERFECT_HASH_SIZE) ASSERT(!table); HashEntry* entries = new HashEntry[hashSizeMask + 1]; for (int i = 0; i <= hashSizeMask; ++i) entries[i].setKey(0); for (int i = 0; values[i].key; ++i) { UString::Rep* identifier = Identifier::add(globalData, values[i].key).releaseRef(); int hashIndex = identifier->computedHash() & hashSizeMask; ASSERT(!entries[hashIndex].key()); entries[hashIndex].initialize(identifier, values[i].attributes, values[i].value1, values[i].value2); } table = entries; #else ASSERT(!table); int linkIndex = compactHashSizeMask + 1; HashEntry* entries = new HashEntry[compactSize]; for (int i = 0; i < compactSize; ++i) entries[i].setKey(0); for (int i = 0; values[i].key; ++i) { UString::Rep* identifier = Identifier::add(globalData, values[i].key).releaseRef(); int hashIndex = identifier->computedHash() & compactHashSizeMask; HashEntry* entry = &entries[hashIndex]; if (entry->key()) { while (entry->next()) { entry = entry->next(); } ASSERT(linkIndex < compactSize); entry->setNext(&entries[linkIndex++]); entry = entry->next(); } entry->initialize(identifier, values[i].attributes, values[i].value1, values[i].value2); } table = entries; #endif }
PropertyMap::~PropertyMap() { if (!_table) { #if USE_SINGLE_ENTRY UString::Rep *key = _singleEntry.key; if (key) key->deref(); #endif return; } int minimumKeysToProcess = _table->keyCount + _table->sentinelCount; Entry *entries = _table->entries; for (int i = 0; i < minimumKeysToProcess; i++) { UString::Rep *key = entries[i].key; if (key) { if (key != deletedSentinel()) key->deref(); } else ++minimumKeysToProcess; } fastFree(_table); }
// Overview: this methods converts a JSString from holding a string in rope form // down to a simple UString representation. It does so by building up the string // backwards, since we want to avoid recursion, we expect that the tree structure // representing the rope is likely imbalanced with more nodes down the left side // (since appending to the string is likely more common) - and as such resolving // in this fashion should minimize work queue size. (If we built the queue forwards // we would likely have to place all of the constituent UString::Reps into the // Vector before performing any concatenation, but by working backwards we likely // only fill the queue with the number of substrings at any given level in a // rope-of-ropes.) void JSString::resolveRope(ExecState* exec) const { ASSERT(isRope()); // Allocate the buffer to hold the final string, position initially points to the end. UChar* buffer; if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_stringLength, buffer)) m_value = newImpl; else { for (unsigned i = 0; i < m_ropeLength; ++i) { m_fibers[i].deref(); m_fibers[i] = static_cast<void*>(0); } m_ropeLength = 0; ASSERT(!isRope()); ASSERT(m_value == UString()); throwOutOfMemoryError(exec); return; } UChar* position = buffer + m_stringLength; // Start with the current Rope. Vector<Rope::Fiber, 32> workQueue; Rope::Fiber currentFiber; for (unsigned i = 0; i < (m_ropeLength - 1); ++i) workQueue.append(m_fibers[i]); currentFiber = m_fibers[m_ropeLength - 1]; while (true) { if (currentFiber.isRope()) { Rope* rope = currentFiber.rope(); // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber' // (we will be working backwards over the rope). unsigned ropeLengthMinusOne = rope->ropeLength() - 1; for (unsigned i = 0; i < ropeLengthMinusOne; ++i) workQueue.append(rope->fibers(i)); currentFiber = rope->fibers(ropeLengthMinusOne); } else { UString::Rep* string = currentFiber.string(); unsigned length = string->size(); position -= length; UStringImpl::copyChars(position, string->data(), length); // Was this the last item in the work queue? if (workQueue.isEmpty()) { // Create a string from the UChar buffer, clear the rope RefPtr. ASSERT(buffer == position); for (unsigned i = 0; i < m_ropeLength; ++i) { m_fibers[i].deref(); m_fibers[i] = static_cast<void*>(0); } m_ropeLength = 0; ASSERT(!isRope()); return; } // No! - set the next item up to process. currentFiber = workQueue.last(); workQueue.removeLast(); } } }
CFStringRef JSStringCopyCFString(CFAllocatorRef alloc, JSStringRef string) { UString::Rep* rep = toJS(string); return CFStringCreateWithCharacters(alloc, reinterpret_cast<const UniChar*>(rep->data()), rep->size()); }
JSStringRef JSStringRetain(JSStringRef string) { JSLock lock; UString::Rep* rep = toJS(string); return toRef(rep->ref()); }
size_t Structure::remove(const Identifier& propertyName) { ASSERT(!propertyName.isNull()); checkConsistency(); UString::Rep* rep = propertyName._ustring.rep(); if (!m_propertyTable) return notFound; #if DUMP_PROPERTYMAP_STATS ++numProbes; ++numRemoves; #endif // Find the thing to remove. unsigned i = rep->computedHash(); unsigned k = 0; unsigned entryIndex; UString::Rep* key = 0; while (1) { entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) return notFound; key = m_propertyTable->entries()[entryIndex - 1].key; if (rep == key) break; if (k == 0) { k = 1 | doubleHash(rep->computedHash()); #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif } i += k; #if DUMP_PROPERTYMAP_STATS ++numRehashes; #endif } // Replace this one element with the deleted sentinel. Also clear out // the entry so we can iterate all the entries as needed. m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex; size_t offset = m_propertyTable->entries()[entryIndex - 1].offset; key->deref(); m_propertyTable->entries()[entryIndex - 1].key = 0; m_propertyTable->entries()[entryIndex - 1].attributes = 0; m_propertyTable->entries()[entryIndex - 1].offset = 0; if (!m_propertyTable->deletedOffsets) m_propertyTable->deletedOffsets = new Vector<unsigned>; m_propertyTable->deletedOffsets->append(offset); ASSERT(m_propertyTable->keyCount >= 1); --m_propertyTable->keyCount; ++m_propertyTable->deletedSentinelCount; if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size) rehashPropertyMapHashTable(); checkConsistency(); return offset; }
size_t Structure::put(const Identifier& propertyName, unsigned attributes) { ASSERT(!propertyName.isNull()); ASSERT(get(propertyName) == notFound); checkConsistency(); UString::Rep* rep = propertyName._ustring.rep(); if (!m_propertyTable) createPropertyMapHashTable(); // FIXME: Consider a fast case for tables with no deleted sentinels. unsigned i = rep->computedHash(); unsigned k = 0; bool foundDeletedElement = false; unsigned deletedElementIndex = 0; // initialize to make the compiler happy #if DUMP_PROPERTYMAP_STATS ++numProbes; #endif while (1) { unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) break; if (entryIndex == deletedSentinelIndex) { // If we find a deleted-element sentinel, remember it for use later. if (!foundDeletedElement) { foundDeletedElement = true; deletedElementIndex = i; } } if (k == 0) { k = 1 | doubleHash(rep->computedHash()); #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif } i += k; #if DUMP_PROPERTYMAP_STATS ++numRehashes; #endif } // Figure out which entry to use. unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2; if (foundDeletedElement) { i = deletedElementIndex; --m_propertyTable->deletedSentinelCount; // Since we're not making the table bigger, we can't use the entry one past // the end that we were planning on using, so search backwards for the empty // slot that we can use. We know it will be there because we did at least one // deletion in the past that left an entry empty. while (m_propertyTable->entries()[--entryIndex - 1].key) { } } // Create a new hash table entry. m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; // Create a new hash table entry. rep->ref(); m_propertyTable->entries()[entryIndex - 1].key = rep; m_propertyTable->entries()[entryIndex - 1].attributes = attributes; m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed; unsigned newOffset; if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) { newOffset = m_propertyTable->deletedOffsets->last(); m_propertyTable->deletedOffsets->removeLast(); } else newOffset = m_propertyTable->keyCount; m_propertyTable->entries()[entryIndex - 1].offset = newOffset; ++m_propertyTable->keyCount; if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size) expandPropertyMapHashTable(); checkConsistency(); return newOffset; }
EXPORT size_t JSStringGetLength(JSStringRef string) { UString::Rep* rep = toJS(string); return rep->size(); }
EXPORT const JSChar* JSStringGetCharactersPtr(JSStringRef string) { UString::Rep* rep = toJS(string); return reinterpret_cast<const JSChar*>(rep->data()); }
void PropertyMap::remove(const Identifier &name) { assert(!name.isNull()); checkConsistency(); UString::Rep *rep = name._ustring.rep(); UString::Rep *key; if (!_table) { #if USE_SINGLE_ENTRY key = _singleEntry.key; if (rep == key) { key->deref(); _singleEntry.key = 0; checkConsistency(); } #endif return; } // Find the thing to remove. unsigned h = rep->hash(); int sizeMask = _table->sizeMask; Entry *entries = _table->entries; int i = h & sizeMask; int k = 0; #if DUMP_STATISTICS ++numProbes; ++numRemoves; numCollisions += entries[i].key && entries[i].key != rep; #endif while ((key = entries[i].key)) { if (rep == key) break; if (k == 0) k = 1 | (h % sizeMask); i = (i + k) & sizeMask; #if DUMP_STATISTICS ++numRehashes; #endif } if (!key) return; // Replace this one element with the deleted sentinel. Also set value to 0 and attributes to DontEnum // to help callers that iterate all keys not have to check for the sentinel. key->deref(); key = deletedSentinel(); entries[i].key = key; entries[i].value = 0; entries[i].attributes = DontEnum; assert(_table->keyCount >= 1); --_table->keyCount; ++_table->sentinelCount; if (_table->sentinelCount * 4 >= _table->size) rehash(); checkConsistency(); }
void PropertyMap::put(const Identifier &name, JSValue *value, int attributes, bool roCheck) { assert(!name.isNull()); assert(value != 0); checkConsistency(); UString::Rep *rep = name._ustring.rep(); #if DEBUG_PROPERTIES printf("adding property %s, attributes = 0x%08x (", name.ascii(), attributes); printAttributes(attributes); printf(")\n"); #endif #if USE_SINGLE_ENTRY if (!_table) { UString::Rep *key = _singleEntry.key; if (key) { if (rep == key && !(roCheck && (_singleEntry.attributes & ReadOnly))) { _singleEntry.value = value; return; } } else { rep->ref(); _singleEntry.key = rep; _singleEntry.value = value; _singleEntry.attributes = static_cast<short>(attributes); checkConsistency(); return; } } #endif if (!_table || _table->keyCount * 2 >= _table->size) expand(); unsigned h = rep->hash(); int sizeMask = _table->sizeMask; Entry *entries = _table->entries; int i = h & sizeMask; int k = 0; bool foundDeletedElement = false; int deletedElementIndex = 0; /* initialize to make the compiler happy */ #if DUMP_STATISTICS ++numProbes; numCollisions += entries[i].key && entries[i].key != rep; #endif while (UString::Rep *key = entries[i].key) { if (rep == key) { if (roCheck && (_table->entries[i].attributes & ReadOnly)) return; // Put a new value in an existing hash table entry. entries[i].value = value; // Attributes are intentionally not updated. return; } // If we find the deleted-element sentinel, remember it for use later. if (key == deletedSentinel() && !foundDeletedElement) { foundDeletedElement = true; deletedElementIndex = i; } if (k == 0) k = 1 | (h % sizeMask); i = (i + k) & sizeMask; #if DUMP_STATISTICS ++numRehashes; #endif } // Use either the deleted element or the 0 at the end of the chain. if (foundDeletedElement) { i = deletedElementIndex; --_table->sentinelCount; } // Create a new hash table entry. rep->ref(); entries[i].key = rep; entries[i].value = value; entries[i].attributes = static_cast<short>(attributes); entries[i].index = ++_table->lastIndexUsed; ++_table->keyCount; checkConsistency(); }