| 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
 | 
| 
 |