Index: vm/symbols.cc |
=================================================================== |
--- vm/symbols.cc (revision 0) |
+++ vm/symbols.cc (revision 0) |
@@ -0,0 +1,269 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+#include "vm/symbols.h" |
+ |
+#include "vm/isolate.h" |
+#include "vm/object.h" |
+#include "vm/object_store.h" |
+#include "vm/raw_object.h" |
+#include "vm/unicode.h" |
+#include "vm/visitor.h" |
+ |
+namespace dart { |
+ |
+const char* Symbols::kDot = "."; |
+RawString* Symbols::dot_ = String::null(); |
+ |
+ |
+void Symbols::InitOnce(Isolate* isolate) { |
+ // Should only be run by the vm isolate. |
+ ASSERT(isolate == Dart::vm_isolate()); |
+ |
+ // Create and setup a symbol table in the vm isolate. |
+ SetupSymbolTable(isolate); |
+ |
+ // Create all predefined symbols. |
+ const Array& symbol_table = |
+ Array::Handle(isolate->object_store()->symbol_table()); |
+ OneByteString& str = OneByteString::Handle(); |
+ |
+ str = OneByteString::New(kDot, Heap::kOld); |
+ Add(symbol_table, str); // "." |
+ dot_ = str.raw(); |
+} |
+ |
+ |
+void Symbols::SetupSymbolTable(Isolate* isolate) { |
+ ASSERT(isolate != NULL); |
+ |
+ // Setup the symbol table used within the String class. |
+ const Array& array = Array::Handle(Array::New(kInitialSymbolTableSize + 1)); |
+ |
+ // Last element contains the count of used slots. |
+ array.SetAt(kInitialSymbolTableSize, Smi::Handle(Smi::New(0))); |
+ isolate->object_store()->set_symbol_table(array); |
+} |
+ |
+ |
+void Symbols::Add(const Array& symbol_table, const String& str) { |
+ // Should only be run by the vm isolate. |
+ ASSERT(Isolate::Current() == Dart::vm_isolate()); |
+ intptr_t hash = str.Hash(); |
+ intptr_t index = FindIndex(symbol_table, str, 0, str.Length(), hash); |
+ ASSERT(symbol_table.At(index) == String::null()); |
+ InsertIntoSymbolTable(symbol_table, str, index); |
+} |
+ |
+ |
+RawString* Symbols::New(const char* str) { |
+ intptr_t width = 0; |
+ intptr_t len = Utf8::CodePointCount(str, &width); |
+ intptr_t size = len * width; |
+ Zone* zone = Isolate::Current()->current_zone(); |
+ if (len == 0) { |
+ return Symbols::New(reinterpret_cast<uint8_t*>(NULL), 0); |
+ } else if (width == 1) { |
+ uint8_t* characters = reinterpret_cast<uint8_t*>(zone->Allocate(size)); |
+ Utf8::Decode(str, characters, len); |
+ return New(characters, len); |
+ } else if (width == 2) { |
+ uint16_t* characters = reinterpret_cast<uint16_t*>(zone->Allocate(size)); |
+ Utf8::Decode(str, characters, len); |
+ return New(characters, len); |
+ } |
+ ASSERT(width == 4); |
+ uint32_t* characters = reinterpret_cast<uint32_t*>(zone->Allocate(size)); |
+ Utf8::Decode(str, characters, len); |
+ return New(characters, len); |
+} |
+ |
+ |
+template<typename T> |
+RawString* Symbols::New(const T* characters, intptr_t len) { |
+ Isolate* isolate = Isolate::Current(); |
+ ASSERT(isolate != Dart::vm_isolate()); |
+ String& symbol = String::Handle(isolate, String::null()); |
+ Array& symbol_table = Array::Handle(isolate, Array::null()); |
+ |
+ // Calculate the String hash for this sequence of characters. |
+ intptr_t hash = String::Hash(characters, len); |
+ |
+ // First check if a symbol exists in the vm isolate for these characters. |
+ symbol_table = Dart::vm_isolate()->object_store()->symbol_table(); |
+ intptr_t index = FindIndex(symbol_table, characters, len, hash); |
+ symbol ^= symbol_table.At(index); |
+ if (symbol.IsNull()) { |
+ // Now try in the symbol table of the current isolate. |
+ symbol_table = isolate->object_store()->symbol_table(); |
+ index = FindIndex(symbol_table, characters, len, hash); |
+ // Since we leave enough room in the table to guarantee, that we find an |
+ // empty spot, index is the insertion point if symbol is null. |
+ symbol ^= symbol_table.At(index); |
+ if (symbol.IsNull()) { |
+ // Allocate new result string. |
+ symbol = String::New(characters, len, Heap::kOld); |
+ symbol.SetHash(hash); // Remember the calculated hash value. |
+ InsertIntoSymbolTable(symbol_table, symbol, index); |
+ } |
+ } |
+ ASSERT(symbol.IsSymbol()); |
+ return symbol.raw(); |
+} |
+ |
+template RawString* Symbols::New(const uint8_t* characters, intptr_t len); |
+template RawString* Symbols::New(const uint16_t* characters, intptr_t len); |
+template RawString* Symbols::New(const uint32_t* characters, intptr_t len); |
+ |
+ |
+RawString* Symbols::New(const String& str) { |
+ if (str.IsSymbol()) { |
+ return str.raw(); |
+ } |
+ return New(str, 0, str.Length()); |
+} |
+ |
+ |
+RawString* Symbols::New(const String& str, intptr_t begin_index, intptr_t len) { |
+ ASSERT(begin_index >= 0); |
+ ASSERT(len >= 0); |
+ ASSERT((begin_index + len) <= str.Length()); |
+ Isolate* isolate = Isolate::Current(); |
+ ASSERT(isolate != Dart::vm_isolate()); |
+ String& symbol = String::Handle(isolate, String::null()); |
+ Array& symbol_table = Array::Handle(isolate, Array::null()); |
+ |
+ // Calculate the String hash for this sequence of characters. |
+ intptr_t hash = (begin_index == 0 && len == str.Length()) ? str.Hash() : |
+ String::Hash(str, begin_index, len); |
+ |
+ // First check if a symbol exists in the vm isolate for these characters. |
+ symbol_table = Dart::vm_isolate()->object_store()->symbol_table(); |
+ intptr_t index = FindIndex(symbol_table, str, begin_index, len, hash); |
+ symbol ^= symbol_table.At(index); |
+ if (symbol.IsNull()) { |
+ // Now try in the symbol table of the current isolate. |
+ symbol_table = isolate->object_store()->symbol_table(); |
+ index = FindIndex(symbol_table, str, begin_index, len, hash); |
+ // Since we leave enough room in the table to guarantee, that we find an |
+ // empty spot, index is the insertion point if symbol is null. |
+ symbol ^= symbol_table.At(index); |
+ if (symbol.IsNull()) { |
+ if (str.IsOld() && begin_index == 0 && len == str.Length()) { |
+ // Reuse the incoming str as the symbol value. |
+ symbol = str.raw(); |
+ } else { |
+ // Allocate a copy in old space. |
+ symbol = String::SubString(str, begin_index, len, Heap::kOld); |
+ symbol.SetHash(hash); |
+ } |
+ InsertIntoSymbolTable(symbol_table, symbol, index); |
+ } |
+ } |
+ ASSERT(symbol.IsSymbol()); |
+ return symbol.raw(); |
+} |
+ |
+ |
+void Symbols::GrowSymbolTable(const Array& symbol_table) { |
+ // TODO(iposva): Avoid exponential growth. |
+ intptr_t table_size = symbol_table.Length() - 1; |
+ intptr_t new_table_size = table_size * 2; |
+ Array& new_symbol_table = Array::Handle(Array::New(new_table_size + 1)); |
+ // Copy all elements from the original symbol table to the newly allocated |
+ // array. |
+ String& element = String::Handle(); |
+ Object& new_element = Object::Handle(); |
+ for (intptr_t i = 0; i < table_size; i++) { |
+ element ^= symbol_table.At(i); |
+ if (!element.IsNull()) { |
+ intptr_t hash = element.Hash(); |
+ intptr_t index = hash % new_table_size; |
+ new_element = new_symbol_table.At(index); |
+ while (!new_element.IsNull()) { |
+ index = (index + 1) % new_table_size; // Move to next element. |
+ new_element = new_symbol_table.At(index); |
+ } |
+ new_symbol_table.SetAt(index, element); |
+ } |
+ } |
+ // Copy used count. |
+ new_element = symbol_table.At(table_size); |
+ new_symbol_table.SetAt(new_table_size, new_element); |
+ // Remember the new symbol table now. |
+ Isolate::Current()->object_store()->set_symbol_table(new_symbol_table); |
+} |
+ |
+ |
+void Symbols::InsertIntoSymbolTable(const Array& symbol_table, |
+ const String& symbol, |
+ intptr_t index) { |
+ intptr_t table_size = symbol_table.Length() - 1; |
+ symbol.SetCanonical(); // Mark object as being canonical. |
+ symbol_table.SetAt(index, symbol); // Remember the new symbol. |
+ Smi& used = Smi::Handle(); |
+ used ^= symbol_table.At(table_size); |
+ intptr_t used_elements = used.Value() + 1; // One more element added. |
+ used = Smi::New(used_elements); |
+ symbol_table.SetAt(table_size, used); // Update used count. |
+ |
+ // Rehash if symbol_table is 75% full. |
+ if (used_elements > ((table_size / 4) * 3)) { |
+ GrowSymbolTable(symbol_table); |
+ } |
+} |
+ |
+ |
+template<typename T> |
+intptr_t Symbols::FindIndex(const Array& symbol_table, |
+ const T* characters, |
+ intptr_t len, |
+ intptr_t hash) { |
+ // Last element of the array is the number of used elements. |
+ intptr_t table_size = symbol_table.Length() - 1; |
+ intptr_t index = hash % table_size; |
+ |
+ String& symbol = String::Handle(); |
+ symbol ^= symbol_table.At(index); |
+ while (!symbol.IsNull() && !symbol.Equals(characters, len)) { |
+ index = (index + 1) % table_size; // Move to next element. |
+ symbol ^= symbol_table.At(index); |
+ } |
+ return index; // Index of symbol if found or slot into which to add symbol. |
+} |
+ |
+ |
+template intptr_t Symbols::FindIndex(const Array& symbol_table, |
+ const uint8_t* characters, |
+ intptr_t len, |
+ intptr_t hash); |
+template intptr_t Symbols::FindIndex(const Array& symbol_table, |
+ const uint16_t* characters, |
+ intptr_t len, |
+ intptr_t hash); |
+template intptr_t Symbols::FindIndex(const Array& symbol_table, |
+ const uint32_t* characters, |
+ intptr_t len, |
+ intptr_t hash); |
+ |
+ |
+intptr_t Symbols::FindIndex(const Array& symbol_table, |
+ const String& str, |
+ intptr_t begin_index, |
+ intptr_t len, |
+ intptr_t hash) { |
+ // Last element of the array is the number of used elements. |
+ intptr_t table_size = symbol_table.Length() - 1; |
+ intptr_t index = hash % table_size; |
+ |
+ String& symbol = String::Handle(); |
+ symbol ^= symbol_table.At(index); |
+ while (!symbol.IsNull() && !symbol.Equals(str, begin_index, len)) { |
+ index = (index + 1) % table_size; // Move to next element. |
+ symbol ^= symbol_table.At(index); |
+ } |
+ return index; // Index of symbol if found or slot into which to add symbol. |
+} |
+ |
+} // namespace dart |