Browse Source

Replace Dictionary with unordered_flat_map

Michael Ragazzon 6 years ago
parent
commit
b04b4e58ba
37 changed files with 172 additions and 942 deletions
  1. 3 0
      Build/CMakeLists.txt
  2. 0 2
      Build/cmake/FileList.cmake
  3. 0 1
      Include/Rocket/Core/Context.h
  4. 19 109
      Include/Rocket/Core/Dictionary.h
  5. 0 63
      Include/Rocket/Core/Dictionary.inl
  6. 5 10
      Include/Rocket/Core/Element.h
  7. 2 8
      Include/Rocket/Core/Element.inl
  8. 1 1
      Include/Rocket/Core/Event.h
  9. 3 2
      Include/Rocket/Core/Types.h
  10. 2 1
      Samples/basic/animation/src/main.cpp
  11. 4 1
      Samples/invaders/src/Game.cpp
  12. 1 0
      Samples/invaders/src/Game.h
  13. 1 1
      Source/Controls/ElementDataGrid.cpp
  14. 6 6
      Source/Controls/ElementDataGridRow.cpp
  15. 4 4
      Source/Controls/ElementForm.cpp
  16. 1 1
      Source/Controls/ElementFormControl.cpp
  17. 1 1
      Source/Controls/ElementTabSet.cpp
  18. 1 1
      Source/Controls/InputTypeCheckbox.cpp
  19. 1 1
      Source/Controls/InputTypeRadio.cpp
  20. 1 1
      Source/Controls/WidgetDropDown.cpp
  21. 1 1
      Source/Controls/WidgetSlider.cpp
  22. 2 2
      Source/Controls/WidgetTextInput.cpp
  23. 3 2
      Source/Controls/XMLNodeHandlerDataGrid.cpp
  24. 1 1
      Source/Core/BaseXMLParser.cpp
  25. 24 24
      Source/Core/BitmapFont/FontParser.cpp
  26. 11 11
      Source/Core/Context.cpp
  27. 0 606
      Source/Core/Dictionary.cpp
  28. 17 21
      Source/Core/Element.cpp
  29. 2 2
      Source/Core/ElementHandle.cpp
  30. 3 6
      Source/Core/ElementUtilities.cpp
  31. 4 4
      Source/Core/Event.cpp
  32. 1 1
      Source/Core/WidgetSlider.cpp
  33. 1 1
      Source/Core/XMLNodeHandlerBody.cpp
  34. 3 3
      Source/Core/XMLNodeHandlerHead.cpp
  35. 1 1
      Source/Core/XMLNodeHandlerTemplate.cpp
  36. 2 5
      Source/Core/XMLParser.cpp
  37. 40 37
      Source/Debugger/ElementInfo.cpp

+ 3 - 0
Build/CMakeLists.txt

@@ -706,6 +706,9 @@ endif(NOT BUILD_FRAMEWORK)
     # Build and install the tutorials
     foreach(tutorial ${tutorials})
         bl_sample(${tutorial} ${sample_LIBRARIES})
+		
+		set_property(TARGET ${tutorial} PROPERTY CXX_STANDARD 17)
+		set_property(TARGET ${tutorial} PROPERTY CXX_STANDARD_REQUIRED ON)
 
         # The tutorials always set this as their current working directory
         install(DIRECTORY DESTINATION ${SAMPLES_DIR}/tutorial/${tutorial})

+ 0 - 2
Build/cmake/FileList.cmake

@@ -121,7 +121,6 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Decorator.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/DecoratorInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Dictionary.h
-    ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Dictionary.inl
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Element.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Element.inl
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/ElementDocument.h
@@ -222,7 +221,6 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVertical.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVerticalInstancer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/Dictionary.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DocumentHeader.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Element.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementAnimation.cpp

+ 0 - 1
Include/Rocket/Core/Context.h

@@ -41,7 +41,6 @@ namespace Rocket {
 namespace Core {
 
 class Stream;
-class Dictionary;
 
 }
 }

+ 19 - 109
Include/Rocket/Core/Dictionary.h

@@ -34,116 +34,26 @@
 namespace Rocket {
 namespace Core {
 
-/**
-	A dictionay is a container of variants.
-	It uses a hash table to maintain a string key to variant mapping.
-
-	@author Lloyd Weehuizen
- */
-
-class ROCKETCORE_API Dictionary
+inline Variant* GetIf(Dictionary& dictionary, const String& key)
 {
-public:
-	Dictionary();
-	Dictionary(const Dictionary &dict);
-	~Dictionary();
-
-	/// Store an item in the dictionary
-	void Set(const String& key, const Variant &value);
-  
-	/// Templated set eases setting of values
-	template <typename T>
-	inline void Set(const String& key, const T& value);
-	
-	/// Get an item from the dictionary
-	Variant* Get(const String& key) const;
-	Variant* operator[](const String& key) const;
-	
-	/// Get a value from the dictionary, if it doesn't exist
-	/// use the supplied default value
-	template <typename T>
-	inline T Get(const String& key, const T& default_val) const;
-	
-	/// Get a value from the dictionary, returns if the
-	/// value was found or not.
-	template <typename T>
-	inline bool GetInto(const String& key, T& value) const;
-
-	/// Remove an item from the dictionary
-	bool Remove(const String& key);
-
-	/// Iterate through a dictionary
-	bool Iterate(int &pos, String& key, Variant* &value) const;
-	template <typename T>
-	bool Iterate(int &pos, String& key, T& value) const;
-
-	/// Reserve the specified number of entries in the dictionary
-	bool Reserve(int size);
-
-	/// Empty the dictionary
-	void Clear();
-
-	/// Is the dictionary empty?
-	bool IsEmpty() const;
-
-	/// Items in the dict
-	int Size() const;
-
-	/// Merges another dictionary into this one. Any existing values stored against similar keys will be updated.
-	void Merge(const Dictionary& dict);
-
-	// Copy
-	void operator=(const Dictionary &dict);
-
-private:
-	unsigned int num_full;  // Active + # Dummy
-	unsigned int num_used;  // Active
-
-	/* DICTIONARY_MINSIZE is the minimum size of a dictionary.  This many slots are
-	 * allocated directly in the dict object (in the small_table member).
-	 * It must be a power of 2, and at least 4.  8 allows dicts with no more
-	 * than 5 active entries to live in small_table (and so avoid an
-	 * additional malloc); instrumentation suggested this suffices for the
-	 * majority of dicts (consisting mostly of usually-small instance dicts and
-	 * usually-small dicts created to pass keyword arguments).
-	 */
-	static const int DICTIONARY_MINSIZE = 8;
-
-	// Individual entry in a dictionary
-	struct DictionaryEntry
-	{
-		DictionaryEntry() : hash(0) {}
-		Hash hash;		// Cached hash of key
-		String key;		// key in plain text
-		Variant value;	// Value for this entry
-	};
-  
-  /* The table contains mask + 1 slots, and that's a power of 2.
-	 * We store the mask instead of the size because the mask is more
-	 * frequently needed.
-	 */
-	unsigned int mask;
-
-	// Small dictionaries just use this, saves mallocs for small tables
-	DictionaryEntry small_table[DICTIONARY_MINSIZE];
-
-	/// Pointer to table in use, may be malloc'd or may point to smallTable
-	DictionaryEntry* table;
-
-	/// Insert an item
-	void Insert(const String& key, Hash hash, const Variant& value);
-
-	/// Retrieve an item
-	DictionaryEntry* Retrieve(const String& key, Hash hash) const;
-
-	/// Reset to small dictionary
-	void ResetToMinimumSize();  
-
-	// Copy another dict
-	void Copy(const Dictionary &dict);
-};
-
-#include "Dictionary.inl"
+	if (auto it = dictionary.find(key); it != dictionary.end())
+		return &(it->second);
+	return nullptr;
+}
+inline const Variant* GetIf(const Dictionary& dictionary, const String& key)
+{
+	if (auto it = dictionary.find(key); it != dictionary.end())
+		return &(it->second);
+	return nullptr;
+}
+template<typename T>
+inline T Get(const Dictionary& dictionary, const String& key, const T& default_value)
+{
+	T result = default_value;
+	if (auto it = dictionary.find(key); it != dictionary.end())
+		it->second.GetInto(result);
+	return result;
+}
 
 }
 }

+ 0 - 63
Include/Rocket/Core/Dictionary.inl

@@ -1,63 +0,0 @@
-/*
- * This source file is part of libRocket, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://www.librocket.com
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-template< typename T >
-inline void Dictionary::Set(const String& key, const T& value)
-{
-	Set(key, Variant(value));
-}
-
-template< typename T >
-inline T Dictionary::Get(const String& key, const T& default_value) const
-{
-	T value;
-	if (!GetInto(key, value))
-		return default_value;
-
-	return value;
-}
-
-
-template <typename T>
-inline bool Dictionary::GetInto(const String& key, T& value) const
-{
-	Variant* variant = Get(key);
-	if (!variant)
-		return false;
-		
-	return variant->GetInto<T>(value);	
-}
-
-template <typename T>
-inline bool Dictionary::Iterate(int &pos, String& key, T& value) const
-{
-	Variant* variant;
-	bool iterate = Iterate(pos, key, variant);
-	if (iterate)
-		variant->GetInto(value);
-	return iterate;
-}

+ 5 - 10
Include/Rocket/Core/Element.h

@@ -46,7 +46,6 @@ namespace Core {
 
 class Context;
 class Decorator;
-class Dictionary;
 class ElementInstancer;
 class EventDispatcher;
 class EventListener;
@@ -343,7 +342,7 @@ public:
 	/// Gets the specified attribute.
 	/// @param[in] name Name of the attribute to retrieve.
 	/// @return A variant representing the attribute, or NULL if the attribute doesn't exist.
-	Variant* GetAttribute(const String& name) const;
+	Variant* GetAttribute(const String& name);
 	/// Gets the specified attribute, with default value.
 	/// @param[in] name Name of the attribute to retrieve.
 	/// @param[in] default_value Value to return if the attribute doesn't exist.
@@ -352,20 +351,16 @@ public:
 	/// Checks if the element has a certain attribute.
 	/// @param[in] name The name of the attribute to check for.
 	/// @return True if the element has the given attribute, false if not.
-	bool HasAttribute(const String& name);
+	bool HasAttribute(const String& name) const;
 	/// Removes the attribute from the element.
 	/// @param[in] name Name of the attribute.
 	void RemoveAttribute(const String& name);
 	/// Set a group of attributes.
 	/// @param[in] attributes Attributes to set.
 	void SetAttributes(const ElementAttributes* attributes);
-	/// Iterates over the attributes.
-	/// @param[inout] index Index to fetch. This is incremented after the fetch.
-	/// @param[out] name Name of the attribute at the specified index
-	/// @param[out] value Value of the attribute at the specified index.
-	/// @return True if an attribute was successfully fetched.
-	template< typename T >
-	bool IterateAttributes(int& index, String& name, T& value) const;
+	/// Get the attributes of the element.
+	/// @return The attributes
+	const ElementAttributes& GetAttributes() const { return attributes; }
 	/// Returns the number of attributes on the element.
 	/// @return The number of attributes on the element.
 	int GetNumAttributes() const;

+ 2 - 8
Include/Rocket/Core/Element.inl

@@ -39,7 +39,7 @@ T Element::GetProperty(const String& name)
 template< typename T >
 void Element::SetAttribute(const String& name, const T& value)
 {
-	attributes.Set(name, value);
+	attributes.emplace(name, Variant(value));
 	AttributeNameList changed_attributes;
 	changed_attributes.insert(name);
 
@@ -50,12 +50,6 @@ void Element::SetAttribute(const String& name, const T& value)
 template< typename T >
 T Element::GetAttribute(const String& name, const T& default_value) const
 {			
-	return attributes.Get(name, default_value);
+	return Get(attributes, name, default_value);
 }
 
-// Iterates over the attributes.
-template< typename T >
-bool Element::IterateAttributes(int& index, String& name, T& value) const
-{
-	return attributes.Iterate(index, name, value);
-}

+ 1 - 1
Include/Rocket/Core/Event.h

@@ -99,7 +99,7 @@ public:
 	template < typename T >
 	T GetParameter(const String& key, const T& default_value)
 	{
-		return parameters.Get(key, default_value);
+		return Get(parameters, key, default_value);
 	}
 	/// Access the dictionary of parameters
 	/// @return The dictionary of parameters

+ 3 - 2
Include/Rocket/Core/Types.h

@@ -92,9 +92,9 @@ typedef Matrix4< float, RowMajorStorage< float > > RowMajorMatrix4f;
 typedef ColumnMajorMatrix4f Matrix4f;
 
 class Element;
-class Dictionary;
 class ElementAnimation;
 class Property;
+class Variant;
 
 // Types for external interfaces.
 typedef uintptr_t FileHandle;
@@ -107,13 +107,14 @@ typedef std::vector< Element* > ElementList;
 typedef std::set< String > PseudoClassList;
 typedef std::unordered_set< String > PropertyNameList;
 typedef std::unordered_set< String > AttributeNameList;
-typedef Dictionary ElementAttributes;
 typedef std::vector< ElementAnimation > ElementAnimationList;
 
 // Unordered map
 template < typename Key, typename Value>
 using UnorderedMap = robin_hood::unordered_flat_map< Key, Value >;
 typedef UnorderedMap< String, Property > PropertyMap;
+using Dictionary = UnorderedMap< String, Variant >;
+typedef Dictionary ElementAttributes;
 
 // Reference types
 typedef std::shared_ptr< Transform > TransformRef;

+ 2 - 1
Samples/basic/animation/src/main.cpp

@@ -136,7 +136,8 @@ public:
 		  robin_hood unordered_flat_map: 24.0  [709852f]
 		  Avoid dirtying em's: 27.5
 		  Restructuring update loop: 34.5  [f9892a9]
-		  Element constructor, remove geometry database, remove update() from Context::render: 38.0
+		  Element constructor, remove geometry database, remove update() from Context::render: 38.0  [1aab59e]
+		  Replace Dictionary with unordered_flat_map: 40.0
 		
 		*/
 		

+ 4 - 1
Samples/invaders/src/Game.cpp

@@ -67,6 +67,7 @@ extern Rocket::Core::Context* context;
 
 Game::Game()
 {
+	initialized = false;
 	invader_frame_start = 0;
 	defender_lives = 3;	
 	current_invader_direction = 1.0f;	
@@ -113,11 +114,13 @@ void Game::Initialise()
 
 	// Create a new wave
 	InitialiseWave();
+
+	initialized = true;
 }
 
 void Game::Update()
 {
-	if (!GameDetails::GetPaused())
+	if (!GameDetails::GetPaused() && initialized)
 	{
 		if (defender_lives <= 0)
 			return;

+ 1 - 0
Samples/invaders/src/Game.h

@@ -92,6 +92,7 @@ public:
 
 private:
 
+	bool initialized;
 	// The current invaders
 	Invader** invaders;
 	// The direction they're moving

+ 1 - 1
Source/Controls/ElementDataGrid.cpp

@@ -138,7 +138,7 @@ void ElementDataGrid::AddColumn(const Rocket::Core::String& fields, const Rocket
 	columns.push_back(column);
 
 	Rocket::Core::Dictionary parameters;
-	parameters.Set("index", (int)(columns.size() - 1)); 
+	parameters.emplace("index", (int)(columns.size() - 1)); 
 	DispatchEvent("columnadd", parameters);
 }
 

+ 6 - 6
Source/Controls/ElementDataGridRow.cpp

@@ -409,8 +409,8 @@ void ElementDataGridRow::AddChildren(int first_row_added, int num_rows_added)
 	DirtyRow();
 
 	Rocket::Core::Dictionary parameters;
-	parameters.Set("first_row_added", GetChildTableRelativeIndex(first_row_added));
-	parameters.Set("num_rows_added", num_rows_added);
+	parameters.emplace("first_row_added", GetChildTableRelativeIndex(first_row_added));
+	parameters.emplace("num_rows_added", num_rows_added);
 	parent_grid->DispatchEvent("rowadd", parameters);
 }
 
@@ -441,8 +441,8 @@ void ElementDataGridRow::RemoveChildren(int first_row_removed, int num_rows_remo
 	document->LockLayout(false);
 
 	Rocket::Core::Dictionary parameters;
-	parameters.Set("first_row_removed", GetChildTableRelativeIndex(first_row_removed));
-	parameters.Set("num_rows_removed", num_rows_removed);
+	parameters.emplace("first_row_removed", GetChildTableRelativeIndex(first_row_removed));
+	parameters.emplace("num_rows_removed", num_rows_removed);
 	parent_grid->DispatchEvent("rowremove", parameters);
 }
 
@@ -452,8 +452,8 @@ void ElementDataGridRow::ChangeChildren(int first_row_changed, int num_rows_chan
 		children[i]->DirtyCells();
 
 	Rocket::Core::Dictionary parameters;
-	parameters.Set("first_row_changed", GetChildTableRelativeIndex(first_row_changed));
-	parameters.Set("num_rows_changed", num_rows_changed);
+	parameters.emplace("first_row_changed", GetChildTableRelativeIndex(first_row_changed));
+	parameters.emplace("num_rows_changed", num_rows_changed);
 	parent_grid->DispatchEvent("rowchange", parameters);
 }
 

+ 4 - 4
Source/Controls/ElementForm.cpp

@@ -47,9 +47,9 @@ void ElementForm::Submit(const Rocket::Core::String& name, const Rocket::Core::S
 {
 	Rocket::Core::Dictionary values;
 	if (name.empty())
-		values.Set("submit", submit_value);
+		values.emplace("submit", submit_value);
 	else
-		values.Set(name, submit_value);
+		values.emplace(name, submit_value);
 
 	Core::ElementList form_controls;
 	Core::ElementUtilities::GetElementsByTagName(form_controls, this, "input");
@@ -79,11 +79,11 @@ void ElementForm::Submit(const Rocket::Core::String& name, const Rocket::Core::S
 			continue;
 
 		// If the item already exists, append to it.
-		Rocket::Core::Variant* value = values.Get(control_name);
+		Rocket::Core::Variant* value = GetIf(values, control_name);
 		if (value != NULL)
 			value->Reset(value->Get< Rocket::Core::String >() + ", " + control_value);
 		else
-			values.Set< Rocket::Core::String >(control_name, control_value);					
+			values.emplace(control_name, control_value);
 	}
 
 	DispatchEvent("submit", values);

+ 1 - 1
Source/Controls/ElementFormControl.cpp

@@ -60,7 +60,7 @@ bool ElementFormControl::IsSubmitted()
 // Returns the disabled status of the form control.
 bool ElementFormControl::IsDisabled() const
 {
-	return GetAttribute("disabled") != NULL;
+	return HasAttribute("disabled");
 }
 
 // Sets the disabled status of the form control.

+ 1 - 1
Source/Controls/ElementTabSet.cpp

@@ -131,7 +131,7 @@ void ElementTabSet::SetActiveTab(int tab_index)
 		active_tab = tab_index;
 
 		Rocket::Core::Dictionary parameters;
-		parameters.Set("tab_index", active_tab);
+		parameters.emplace("tab_index", active_tab);
 		DispatchEvent("tabchange", parameters);
 	}
 }

+ 1 - 1
Source/Controls/InputTypeCheckbox.cpp

@@ -55,7 +55,7 @@ bool InputTypeCheckbox::OnAttributeChange(const Core::AttributeNameList& changed
 		element->SetPseudoClass("checked", checked);
 
 		Rocket::Core::Dictionary parameters;
-		parameters.Set("value", Rocket::Core::String(checked ? GetValue() : ""));
+		parameters.emplace("value", Rocket::Core::String(checked ? GetValue() : ""));
 		element->DispatchEvent("change", parameters);
 	}
 

+ 1 - 1
Source/Controls/InputTypeRadio.cpp

@@ -62,7 +62,7 @@ bool InputTypeRadio::OnAttributeChange(const Core::AttributeNameList& changed_at
 			PopRadioSet();
 
 		Rocket::Core::Dictionary parameters;
-		parameters.Set("value", Rocket::Core::String(checked ? GetValue() : ""));
+		parameters.emplace("value", Rocket::Core::String(checked ? GetValue() : ""));
 		element->DispatchEvent("change", parameters);
 	}
 

+ 1 - 1
Source/Controls/WidgetDropDown.cpp

@@ -204,7 +204,7 @@ void WidgetDropDown::SetSelection(int selection, bool force)
 		value_layout_dirty = true;
 
 		Rocket::Core::Dictionary parameters;
-		parameters.Set("value", value);
+		parameters.emplace("value", value);
 		parent_element->DispatchEvent("change", parameters);
 	}
 }

+ 1 - 1
Source/Controls/WidgetSlider.cpp

@@ -184,7 +184,7 @@ void WidgetSlider::SetBarPosition(float _bar_position)
 	PositionBar();
 
 	Rocket::Core::Dictionary parameters;
-	parameters.Set("value", bar_position);
+	parameters.emplace("value", bar_position);
 	parent->DispatchEvent("change", parameters);
 }
 

+ 2 - 2
Source/Controls/WidgetTextInput.cpp

@@ -240,8 +240,8 @@ Core::Element* WidgetTextInput::GetElement()
 void WidgetTextInput::DispatchChangeEvent(bool linebreak)
 {
 	Rocket::Core::Dictionary parameters;
-	parameters.Set("value", GetElement()->GetAttribute< Rocket::Core::String >("value", ""));
-	parameters.Set("linebreak", linebreak);
+	parameters.emplace("value", GetElement()->GetAttribute< Rocket::Core::String >("value", ""));
+	parameters.emplace("linebreak", linebreak);
 	GetElement()->DispatchEvent("change", parameters);
 }
 

+ 3 - 2
Source/Controls/XMLNodeHandlerDataGrid.cpp

@@ -29,6 +29,7 @@
 #include "../../Include/Rocket/Core/StreamMemory.h"
 #include "../../Include/Rocket/Core/Log.h"
 #include "../../Include/Rocket/Core/Factory.h"
+#include "../../Include/Rocket/Core/String.h"
 #include "../../Include/Rocket/Core/XMLParser.h"
 #include "../../Include/Rocket/Controls/ElementDataGrid.h"
 
@@ -66,7 +67,7 @@ Core::Element* XMLNodeHandlerDataGrid::ElementStart(Core::XMLParser* parser, con
 		}
 
 		// Set the data source and table on the data grid.
-		Rocket::Core::String data_source = attributes.Get< Rocket::Core::String >("source", "");
+		Rocket::Core::String data_source = Core::Get<Core::String>(attributes, "source", "");
 		grid->SetDataSource(data_source);
 
 		parent->AppendChild(grid);
@@ -85,7 +86,7 @@ Core::Element* XMLNodeHandlerDataGrid::ElementStart(Core::XMLParser* parser, con
 		ElementDataGrid* grid = dynamic_cast< ElementDataGrid* >(parent);
 		if (grid != NULL)
 		{
-			grid->AddColumn(attributes.Get< Rocket::Core::String >("fields", ""), attributes.Get< Rocket::Core::String >("formatter", ""), attributes.Get< float >("width", 0), element);
+			grid->AddColumn(Core::Get<Core::String>(attributes, "fields", ""), Core::Get<Core::String>(attributes, "formatter", ""), Core::Get(attributes, "width", 0.0f), element);
 			element->RemoveReference();
 		}
 

+ 1 - 1
Source/Core/BaseXMLParser.cpp

@@ -299,7 +299,7 @@ bool BaseXMLParser::ReadAttributes(XMLAttributes& attributes)
 			}
 		}
 
- 		attributes.Set(attribute.c_str(), value);
+ 		attributes.emplace(attribute, value);
 
 		// Check for the end of the tag.
 		if (PeekString((const unsigned char*) "/", false) ||

+ 24 - 24
Source/Core/BitmapFont/FontParser.cpp

@@ -49,49 +49,49 @@ void FontParser::HandleElementStart(const String& name, const XMLAttributes& att
 {
 	if ( name == "info" )
 	{
-		bm_face->Face.FamilyName = attributes.Get( "face" )->Get< String >();
-		bm_face->Face.Size = attributes.Get( "size" )->Get< int >();
-		bm_face->Face.Weight = attributes.Get( "bold" )->Get< bool >() ? Font::WEIGHT_BOLD : Font::WEIGHT_NORMAL;
-		bm_face->Face.Style = attributes.Get( "italic" )->Get< bool >() ? Font::STYLE_ITALIC : Font::STYLE_NORMAL;
-		bm_face->Face.BitmapSource = attributes.Get( "src" )->Get< String >();
+		bm_face->Face.FamilyName = Get(attributes, "face", String());
+		bm_face->Face.Size = Get(attributes, "size", 0);
+		bm_face->Face.Weight = Get(attributes, "bold", false ) ? Font::WEIGHT_BOLD : Font::WEIGHT_NORMAL;
+		bm_face->Face.Style = Get(attributes, "italic", false ) ? Font::STYLE_ITALIC : Font::STYLE_NORMAL;
+		bm_face->Face.BitmapSource = Get(attributes, "src", String());
 	}
 	else if ( name == "common" )
 	{
-		bm_face->CommonCharactersInfo.LineHeight = attributes.Get( "lineHeight" )->Get< int >();
-		bm_face->CommonCharactersInfo.BaseLine = attributes.Get( "base" )->Get< int >() * -1;
-		bm_face->CommonCharactersInfo.ScaleWidth = attributes.Get( "scaleW" )->Get< int >();
-		bm_face->CommonCharactersInfo.ScaleHeight = attributes.Get( "scaleH" )->Get< int >();
+		bm_face->CommonCharactersInfo.LineHeight = Get(attributes, "lineHeight", 0);
+		bm_face->CommonCharactersInfo.BaseLine = Get(attributes, "base", 0) * -1;
+		bm_face->CommonCharactersInfo.ScaleWidth = Get(attributes, "scaleW", 0);
+		bm_face->CommonCharactersInfo.ScaleHeight = Get(attributes, "scaleH", 0);
 		bm_face->CommonCharactersInfo.CharacterCount = 0;
 		bm_face->CommonCharactersInfo.KerningCount = 0;
 	}
 	else if ( name == "chars" )
 	{
-		bm_face->CommonCharactersInfo.CharacterCount = attributes.Get( "count" )->Get< int >();
-		bm_face->CharactersInfo = new CharacterInfo[ attributes.Get( "count" )->Get< int >() ];
+		bm_face->CommonCharactersInfo.CharacterCount = Get(attributes, "count", 0);
+		bm_face->CharactersInfo = new CharacterInfo[ Get(attributes, "count", 0) ];
 	}
 	else if ( name == "char" )
 	{
-		bm_face->CharactersInfo[ char_id ].Id = attributes.Get( "id" )->Get< int >();
-		bm_face->CharactersInfo[ char_id ].X = attributes.Get( "x" )->Get< int >(); //The left position of the character image in the texture.
-		bm_face->CharactersInfo[ char_id ].Y = attributes.Get( "y" )->Get< int >(); //The top position of the character image in the texture.
-		bm_face->CharactersInfo[ char_id ].Width = attributes.Get( "width" )->Get< int >(); //The width of the character image in the texture.
-		bm_face->CharactersInfo[ char_id ].Height = attributes.Get( "height" )->Get< int >(); //The height of the character image in the texture.
-		bm_face->CharactersInfo[ char_id ].XOffset = attributes.Get( "xoffset" )->Get< int >();
-		bm_face->CharactersInfo[ char_id ].YOffset = attributes.Get( "yoffset" )->Get< int >();
-		bm_face->CharactersInfo[ char_id ].Advance = attributes.Get( "xadvance" )->Get< int >();
+		bm_face->CharactersInfo[ char_id ].Id = Get(attributes, "id", 0);
+		bm_face->CharactersInfo[ char_id ].X = Get(attributes, "x", 0); //The left position of the character image in the texture.
+		bm_face->CharactersInfo[ char_id ].Y = Get(attributes, "y", 0); //The top position of the character image in the texture.
+		bm_face->CharactersInfo[ char_id ].Width = Get(attributes, "width", 0); //The width of the character image in the texture.
+		bm_face->CharactersInfo[ char_id ].Height = Get(attributes, "height", 0); //The height of the character image in the texture.
+		bm_face->CharactersInfo[ char_id ].XOffset = Get(attributes, "xoffset", 0);
+		bm_face->CharactersInfo[ char_id ].YOffset = Get(attributes, "yoffset", 0);
+		bm_face->CharactersInfo[ char_id ].Advance = Get(attributes, "xadvance", 0);
 
 		char_id++;
 	}
 	else if ( name == "kernings" )
 	{
-		bm_face->CommonCharactersInfo.KerningCount = attributes.Get( "count" )->Get< int >();
-		bm_face->KerningsInfo = new KerningInfo[ attributes.Get( "count" )->Get< int >() ];
+		bm_face->CommonCharactersInfo.KerningCount = Get(attributes, "count", 0);
+		bm_face->KerningsInfo = new KerningInfo[ Get(attributes, "count", 0) ];
 	}
 	else if ( name == "kerning" )
 	{
-		bm_face->KerningsInfo[ kern_id ].FirstCharacterId = attributes.Get( "first" )->Get< int >();
-		bm_face->KerningsInfo[ kern_id ].SecondCharacterId = attributes.Get( "second" )->Get< int >();
-		bm_face->KerningsInfo[ kern_id ].KerningAmount = attributes.Get( "amount" )->Get< int >();
+		bm_face->KerningsInfo[ kern_id ].FirstCharacterId = Get(attributes, "first", 0);
+		bm_face->KerningsInfo[ kern_id ].SecondCharacterId = Get(attributes, "second", 0);
+		bm_face->KerningsInfo[ kern_id ].KerningAmount = Get(attributes, "amount", 0);
 
 		kern_id++;
 	}

+ 11 - 11
Source/Core/Context.cpp

@@ -499,7 +499,7 @@ bool Context::ProcessTextInput(word character)
 {
 	// Generate the parameters for the key event.
 	Dictionary parameters;
-	parameters.Set("data", character);
+	parameters.emplace("data", character);
 
 	if (focus)
 		return focus->DispatchEvent(TEXTINPUT, parameters, true);
@@ -516,7 +516,7 @@ bool Context::ProcessTextInput(const String& string)
 	{
 		// Generate the parameters for the key event.
 		Dictionary parameters;
-		parameters.Set("data", string[i]);
+		parameters.emplace("data", string[i]);
 
 		if (focus)
 			consumed = focus->DispatchEvent(TEXTINPUT, parameters, true) && consumed;
@@ -733,7 +733,7 @@ bool Context::ProcessMouseWheel(int wheel_delta, int key_modifier_state)
 	{
 		Dictionary scroll_parameters;
 		GenerateKeyModifierEventParameters(scroll_parameters, key_modifier_state);
-		scroll_parameters.Set("wheel_delta", wheel_delta);
+		scroll_parameters.emplace("wheel_delta", wheel_delta);
 
 		return hover->DispatchEvent(MOUSESCROLL, scroll_parameters, true);
 	}
@@ -909,8 +909,8 @@ void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& d
 			if (!drag_started)
 			{
 				Dictionary drag_start_parameters = drag_parameters;
-				drag_start_parameters.Set("mouse_x", old_mouse_position.x);
-				drag_start_parameters.Set("mouse_y", old_mouse_position.y);
+				drag_start_parameters.emplace("mouse_x", old_mouse_position.x);
+				drag_start_parameters.emplace("mouse_y", old_mouse_position.y);
 				drag->DispatchEvent(DRAGSTART, drag_start_parameters);
 				drag_started = true;
 
@@ -1118,16 +1118,16 @@ void Context::ReleaseDragClone()
 // Builds the parameters for a generic key event.
 void Context::GenerateKeyEventParameters(Dictionary& parameters, Input::KeyIdentifier key_identifier)
 {
-	parameters.Set("key_identifier", (int) key_identifier);
+	parameters.emplace("key_identifier", (int) key_identifier);
 }
 
 // Builds the parameters for a generic mouse event.
 void Context::GenerateMouseEventParameters(Dictionary& parameters, int button_index)
 {
-	parameters.Set("mouse_x", mouse_position.x);
-	parameters.Set("mouse_y", mouse_position.y);
+	parameters.emplace("mouse_x", mouse_position.x);
+	parameters.emplace("mouse_y", mouse_position.y);
 	if (button_index >= 0)
-		parameters.Set("button", button_index);
+		parameters.emplace("button", button_index);
 }
 
 // Builds the parameters for the key modifier state.
@@ -1144,13 +1144,13 @@ void Context::GenerateKeyModifierEventParameters(Dictionary& parameters, int key
 	};
 
 	for (int i = 0; i < 7; i++)
-		parameters.Set(property_names[i], (int) ((key_modifier_state & (1 << i)) > 0));
+		parameters.emplace(property_names[i], (int) ((key_modifier_state & (1 << i)) > 0));
 }
 
 // Builds the parameters for a drag event.
 void Context::GenerateDragEventParameters(Dictionary& parameters)
 {	
-	parameters.Set("drag_element", (void*) *drag);
+	parameters.emplace("drag_element", (void*) *drag);
 }
 
 // Releases all unloaded documents pending destruction.

+ 0 - 606
Source/Core/Dictionary.cpp

@@ -1,606 +0,0 @@
-/*
- * This source file is part of libRocket, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://www.librocket.com
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#include "precompiled.h"
-#include "../../Include/Rocket/Core/Dictionary.h"
-
-/* NOTE: This is dictionary implementation is copied from PYTHON 
-   which is well known for its dictionary SPEED.
-
-   It uses an optimised hash table implementation. */
-
-/*
- There are three kinds of slots in the table:
- 
- 1. Unused.  me_key == me_value == NULL
- Does not hold an active (key, value) pair now and never did.  Unused can
- transition to Active upon key insertion.  This is the only case in which
- me_key is NULL, and is each slot's initial state.
- 
- 2. Active.  me_key != NULL and me_key != dummy and me_value != NULL
- Holds an active (key, value) pair.  Active can transition to Dummy upon
- key deletion.  This is the only case in which me_value != NULL.
- 
- 3. Dummy.  me_key == dummy and me_value == NULL
- Previously held an active (key, value) pair, but that was deleted and an
- active pair has not yet overwritten the slot.  Dummy can transition to
- Active upon key insertion.  Dummy slots cannot be made Unused again
- (cannot have me_key set to NULL), else the probe sequence in case of
- collision would have no way to know they were once active.
- 
- Note: .popitem() abuses the me_hash field of an Unused or Dummy slot to
- hold a search finger.  The me_hash field of Unused or Dummy slots has no
- meaning otherwise.
- 
- 
- To ensure the lookup algorithm terminates, there must be at least one Unused
- slot (NULL key) in the table.
- The value ma_fill is the number of non-NULL keys (sum of Active and Dummy);
- ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL
-													 values == the number of Active items).
- To avoid slowing down lookups on a near-full table, we resize the table when
- it's two-thirds full.
-*/
-
-namespace Rocket {
-namespace Core {
-
-
-/* See large comment block below.  This must be >= 1. */
-#define PERTURB_SHIFT 5
-
-/* switch these defines if you want dumps all dictionary access */
-#define DICTIONARY_DEBUG_CODE(x)
-//#define DICTIONARY_DEBUG_CODE(x) x
-
-/*
- Major subtleties ahead:  Most hash schemes depend on having a "good" hash
- function, in the sense of simulating randomness.  Python doesn't:  its most
- important hash functions (for strings and ints) are very regular in common
- cases:
- 
- >>> map(hash, (0, 1, 2, 3))
- [0, 1, 2, 3]
- >>> map(hash, ("namea", "nameb", "namec", "named"))
- [-1658398457, -1658398460, -1658398459, -1658398462]
- >>>
- 
- This isn't necessarily bad!  To the contrary, in a table of size 2**i, taking
- the low-order i bits as the initial table index is extremely fast, and there
- are no collisions at all for dicts indexed by a contiguous range of ints.
- The same is approximately true when keys are "consecutive" strings.  So this
- gives better-than-random behavior in common cases, and that's very desirable.
- 
- OTOH, when collisions occur, the tendency to fill contiguous slices of the
- hash table makes a good collision resolution strategy crucial.  Taking only
- the last i bits of the hash code is also vulnerable:  for example, consider
- [i << 16 for i in range(20000)] as a set of keys.  Since ints are their own
- hash codes, and this fits in a dict of size 2**15, the last 15 bits of every
- hash code are all 0:  they *all* map to the same table index.
- 
- But catering to unusual cases should not slow the usual ones, so we just take
- the last i bits anyway.  It's up to collision resolution to do the rest.  If
- we *usually* find the key we're looking for on the first try (and, it turns
- out, we usually do -- the table load factor is kept under 2/3, so the odds
- are solidly in our favor), then it makes best sense to keep the initial index
- computation dirt cheap.
- 
- The first half of collision resolution is to visit table indices via this
- recurrence:
- 
- j = ((5*j) + 1) mod 2**i
- 
- For any initial j in range(2**i), repeating that 2**i times generates each
- int in range(2**i) exactly once (see any text on random-number generation for
- proof).  By itself, this doesn't help much:  like linear probing (setting
- j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed
- order.  This would be bad, except that's not the only thing we do, and it's
- actually *good* in the common cases where hash keys are consecutive.  In an
- example that's really too small to make this entirely clear, for a table of
- size 2**3 the order of indices is:
- 
- 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating]
- 
- If two things come in at index 5, the first place we look after is index 2,
- not 6, so if another comes in at index 6 the collision at 5 didn't hurt it.
- Linear probing is deadly in this case because there the fixed probe order
- is the *same* as the order consecutive keys are likely to arrive.  But it's
- extremely unlikely hash codes will follow a 5*j+1 recurrence by accident,
- and certain that consecutive hash codes do not.
- 
- The other half of the strategy is to get the other bits of the hash code
- into play.  This is done by initializing a (unsigned) vrbl "perturb" to the
- full hash code, and changing the recurrence to:
- 
- j = (5*j) + 1 + perturb;
- perturb >>= PERTURB_SHIFT;
- use j % 2**i as the next table index;
- 
- Now the probe sequence depends (eventually) on every bit in the hash code,
- and the pseudo-scrambling property of recurring on 5*j+1 is more valuable,
- because it quickly magnifies small differences in the bits that didn't affect
- the initial index.  Note that because perturb is unsigned, if the recurrence
- is executed often enough perturb eventually becomes and remains 0.  At that
- point (very rarely reached) the recurrence is on (just) 5*j+1 again, and
- that's certain to find an empty slot eventually (since it generates every int
- in range(2**i), and we make sure there's always at least one empty slot).
- 
- Selecting a good value for PERTURB_SHIFT is a balancing act.  You want it
- small so that the high bits of the hash code continue to affect the probe
- sequence across iterations; but you want it large so that in really bad cases
- the high-order hash bits have an effect on early iterations.  5 was "the
- best" in minimizing total collisions across experiments Tim Peters ran (on
- both normal and pathological cases), but 4 and 6 weren't significantly worse.
- 
- Historical:  Reimer Behrends contributed the idea of using a polynomial-based
- approach, using repeated multiplication by x in GF(2**n) where an irreducible
- polynomial for each table size was chosen such that x was a primitive root.
- Christian Tismer later extended that to use division by x instead, as an
- efficient way to get the high bits of the hash code into play.  This scheme
- also gave excellent collision statistics, but was more expensive:  two
- if-tests were required inside the loop; computing "the next" index took about
- the same number of operations but without as much potential parallelism
- (e.g., computing 5*j can go on at the same time as computing 1+perturb in the
-  above, and then shifting perturb can be done while the table index is being
-  masked); and the dictobject struct required a member to hold the table's
- polynomial.  In Tim's experiments the current scheme ran faster, produced
- equally good collision statistics, needed less code & used less memory.
- */
-
-static String dummy_key = CreateString(128, "###DUMMYROCKETDICTKEY%d###", &dummy_key);
-
-Dictionary::Dictionary() 
-{ 
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS,"Dictionary::New Dict"); )
-	ResetToMinimumSize();  
-}
-
-Dictionary::Dictionary(const Dictionary &dict) 
-{
-	ResetToMinimumSize();
-	Copy(dict);
-}
-
-Dictionary::~Dictionary() 
-{
-	Clear();
-}
-
-/*
- The basic lookup function used by all operations.
- This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
- Open addressing is preferred over chaining since the link overhead for
- chaining would be substantial (100% with typical malloc overhead).
- 
- The initial probe index is computed as hash mod the table size. Subsequent
- probe indices are computed as explained earlier.
- 
- All arithmetic on hash should ignore overflow.
- 
- (The details in this version are due to Tim Peters, building on many past
-  contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and
-  Christian Tismer).
- 
- This function must never return NULL; failures are indicated by returning
- a dictentry* for which the me_value field is NULL.  Exceptions are never
- reported by this function, and outstanding exceptions are maintained.
- */
-
-/*
- * Hacked up version of lookdict which can assume keys are always strings;
- * this assumption allows testing for errors during PyObject_Compare() to
- * be dropped; string-string comparisons never raise exceptions.  This also
- * means we don't need to go through PyObject_Compare(); we can always use
- * _PyString_Eq directly.
- *
- * This is valuable because the general-case error handling in lookdict() is
- * expensive, and dicts with pure-string keys are very common.
- */
-Dictionary::DictionaryEntry* Dictionary::Retrieve(const String& key, Hash hash) const
-{
-	Hash i = 0;
-	unsigned int perturb;
-	DictionaryEntry *freeslot;
-	unsigned int mask = this->mask;
-	DictionaryEntry *ep0 = table;
-	DictionaryEntry *ep;
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Retreive %s", key); )
-		/* Make sure this function doesn't have to handle non-string keys,
-		including subclasses of str; e.g., one reason to subclass
-		strings is to override __eq__, and for speed we don't cater to
-		that here. */
-		
-	i = hash & mask;
-	ep = &ep0[i];
-	if (ep->key.empty() || ep->key == key)
-		return ep;
-	if (ep->key == dummy_key)
-		freeslot = ep;
-	else {
-		if (ep->hash == hash && ep->key == key) {
-			return ep;
-		}
-		freeslot = NULL;
-	}
-	
-	/* In the loop, me_key == dummy_key is by far (factor of 100s) the
-		least likely outcome, so test for that last. */
-	for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
-		i = (i << 2) + i + perturb + 1;
-		ep = &ep0[i & mask];
-		if (ep->key.empty())    
-			return freeslot == NULL ? ep : freeslot;
-		/*if (ep->me_key == key
-		    || (ep->me_hash == hash
-		        && ep->me_key != dummy_key
-				&& _PyString_Eq(ep->me_key, key)))*/
-		if (ep->key == key)
-			return ep;
-		if (ep->key == dummy_key && freeslot == NULL)
-			freeslot = ep;
-	}
-}
-
-/*
- Internal routine to insert a new item into the table.
- Used both by the internal resize routine and by the public insert routine.
- */
-void Dictionary::Insert(const String& key, Hash hash, const Variant& value)
-{	
-	DictionaryEntry *ep;	
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Insert %s", key); );
-	ep = Retrieve(key, hash);
-	if (ep->value.GetType() !=Variant::NONE) 
-	{
-		ep->value = value;
-	} 
-	else 
-	{
-		if (ep->key.empty())
-		{
-			num_full++;
-		}
-		else if ( ep->key != dummy_key )
-		{
-			//delete ep->key;
-		}
-		
-		ep->key = key;		
-		ep->hash = hash;
-		ep->value = value;
-		num_used++;
-	}  
-}
-
-/*
- Restructure the table by allocating a new table and reinserting all
- items again.  When entries have been deleted, the new table may
- actually be smaller than the old one.
- */
-bool Dictionary::Reserve(int minused)
-{
-	int newsize;
-	DictionaryEntry *oldtable, *newtable, *ep;
-	int i = 0;
-	bool is_oldtable_malloced;
-	DictionaryEntry small_copy[DICTIONARY_MINSIZE];
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Reserve %d", minused); )
-	ROCKET_ASSERT(minused >= 0);
-	
-	/* Find the smallest table size > minused. */
-	newsize = DICTIONARY_MINSIZE;
-	while(newsize <= minused) {
-		newsize <<= 1;	// double the table size and test again
-		if(newsize <= 0) {
-			ROCKET_ASSERT(newsize > 0);
-			return false; // newsize has overflowed the max size for this platform, abort
-		}
-	}
-
-	// If its the same size, ignore the request
-	if ((unsigned)newsize == mask + 1)
-		return true;
-	
-	/* Get space for a new table. */
-	oldtable = table;
-	ROCKET_ASSERT(oldtable != NULL);
-	is_oldtable_malloced = oldtable != small_table;
-	
-	if (newsize == DICTIONARY_MINSIZE) {
-		/* A large table is shrinking, or we can't get any smaller. */
-		newtable = small_table;
-		if (newtable == oldtable) {
-			if (num_full == num_used) {
-				/* No dummies, so no point doing anything. */
-				return true;
-			}
-			/* We're not going to resize it, but rebuild the
-			table anyway to purge old dummy_key entries.
-			Subtle:  This is *necessary* if fill==size,
-			as lookdict needs at least one virgin slot to
-			terminate failing searches.  If fill < size, it's
-			merely desirable, as dummies slow searches. */
-			ROCKET_ASSERT(num_full > num_used);
-			memcpy(small_copy, oldtable, sizeof(small_copy));
-			oldtable = small_copy;
-		}
-	}	else {
-		newtable = new DictionaryEntry[newsize];
-		
-		ROCKET_ASSERT(newtable);
-		
-		if (newtable == NULL) {
-			return false;
-		}
-	}
-	
-	/* Make the dict empty, using the new table. */
-	ROCKET_ASSERT(newtable != oldtable);
-	table = newtable;
-	mask = newsize - 1;
-	//memset(newtable, 0, sizeof(DictionaryEntry) * newsize);
-	num_used = 0;
-	i = num_full;
-	num_full = 0;
-	
-	/* Copy the data over; this is refcount-neutral for active entries;
-	   dummy_key entries aren't copied over, of course */
-	for (ep = oldtable; i > 0; ep++) {
-		if (ep->value.GetType() != Variant::NONE) {	/* active entry */
-			--i;
-			Insert(ep->key, ep->hash, ep->value);
-			
-			//delete[] ep->key;
-		}
-		else if (!ep->key.empty()) {	/* dummy_key entry */
-			--i;
-			ROCKET_ASSERT(ep->key == dummy_key);
-		}
-/* else key == value == NULL:  nothing to do */
-	}
-
-	if (is_oldtable_malloced)
-		delete[] oldtable;
-	return true;
-}
-
-Variant* Dictionary::Get(const String& key) const
-{
-	Hash hash;
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Get %s", key); );
-	hash = StringUtilities::FNVHash( key.c_str() );
-	
-	DictionaryEntry* result = Retrieve(key, hash);
-	if (!result || result->key.empty() || result->key == dummy_key)
-	{
-		return NULL;
-	}
-
-	return &result->value;
-}
-
-Variant* Dictionary::operator[](const String& key) const
-{
-	return Get(key);
-}
-
-/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
-* dictionary if it is merely replacing the value for an existing key.
-* This is means that it's safe to loop over a dictionary with
-* PyDict_Next() and occasionally replace a value -- but you can't
-* insert new keys or remove them.
-*/
-void Dictionary::Set(const String& key, const Variant &value)
-{
-	if (key.empty())
-	{
-		Log::Message(Log::LT_WARNING, "Unable to set value on dictionary, empty key specified.");
-		return;
-	}
-
-	Hash hash;
-	unsigned int n_used;  
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Set %s", key); );
-	hash = StringUtilities::FNVHash( key.c_str() );
-	
-	ROCKET_ASSERT(num_full <= mask);  /* at least one empty slot */
-	n_used = num_used;  
-	
-	Insert( key, hash, value );  
-	
-	/* If we added a key, we can safely resize.  Otherwise skip this!
-		* If fill >= 2/3 size, adjust size.  Normally, this doubles the
-		* size, but it's also possible for the dict to shrink (if ma_fill is
-															   * much larger than ma_used, meaning a lot of dict keys have been
-															   * deleted).
-		*/
-	if ((num_used > n_used) && (num_full * 3) >= (mask + 1) * 2) 
-	{
-		if (!Reserve(num_used * 2)) 
-		{
-			Log::Message(Log::LT_ALWAYS, "Dictionary::Error resizing dictionary after insert");
-		}
-	}
-}
-
-bool Dictionary::Remove(const String& key)
-{
-	Hash hash;
-	DictionaryEntry *ep;  
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Remove %s", key) );
-	hash = StringUtilities::FNVHash( key.c_str() );
-	
-	ep = Retrieve(key, hash);
-	
-	if (ep->value.GetType() == Variant::NONE) 
-	{
-		return false;
-	}
-		
-	ep->key = dummy_key;
-	ep->value.Clear();
-	num_used--;	
-	
-	return true;
-}
-
-void Dictionary::Clear()
-{	
-	DictionaryEntry *ep;
-	bool table_is_malloced;
-	int n_full;
-	int i, n;
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Clear") );
-	n = mask + 1;
-	i = 0;
-	
-	table_is_malloced = table != small_table;
-	n_full = num_full;
-	
-	// Clear things up
-	for (ep = table; n_full > 0; ++ep) {
-		
-		ROCKET_ASSERT(i < n);
-		++i;
-
-		if (!ep->key.empty()) {
-			--n_full;
-			ep->key.clear();		
-			ep->value.Clear();
-		} else {
-			ROCKET_ASSERT(ep->value.GetType() == Variant::NONE);
-		}
-	}
-
-	if (table_is_malloced)
-		delete [] table;
-
-	ResetToMinimumSize();
-}
-
-bool Dictionary::IsEmpty() const
-{ 
-	return num_used == 0; 
-}
-
-int Dictionary::Size() const
-{ 
-	return num_used; 
-}
-
-// Merges another dictionary into this one. Any existing values stored against similar keys will be updated.
-void Dictionary::Merge(const Dictionary& dict)
-{
-	int index = 0;
-	String key;
-	Variant* value;
-
-	while (dict.Iterate(index, key, value))
-	{
-		Set(key, *value);
-	}
-}
-
-
-/* CAUTION:  In general, it isn't safe to use PyDict_Next in a loop that
-* mutates the dict.  One exception:  it is safe if the loop merely changes
-* the values associated with the keys (but doesn't insert new keys or
-									   * delete keys), via PyDict_SetItem().
-*/
-bool Dictionary::Iterate(int &pos, String& key, Variant* &value) const
-{
-	unsigned int i;
-
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Next %d", pos); )
-		i = pos;
-	while (i <= mask && table[i].value.GetType() == Variant::NONE)
-		i++;
-	pos = i+1;
-	if (i > mask)
-		return false;
-
-	key = table[i].key;
-
-	value = &table[i].value;
-
-	return true;
-}
-
-void Dictionary::operator=(const Dictionary &dict) {
-	Copy(dict);
-}
-
-void Dictionary::Copy(const Dictionary &dict) {
-	unsigned int i;
-	
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Copy Dict (Size: %d)", dict.num_used); )
-
-	// Clear our current state
-	Clear();
-
-	// Resize our table
-	Reserve(dict.mask);  
-
-	// Copy elements across
-	for ( i = 0; i < dict.mask + 1; i++ )
-	{
-		table[i].hash = dict.table[i].hash;		
-		table[i].key = dict.table[i].key;			
-		table[i].value = dict.table[i].value;		
-	}
-
-	// Set properties
-	num_used = dict.num_used;
-	num_full = dict.num_full;
-	mask = dict.mask;
-}  
-
-void Dictionary::ResetToMinimumSize() 
-{
-	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::ResetToMinimumSize"); )
-	// Reset to small table
-	for ( size_t i = 0; i < DICTIONARY_MINSIZE; i++ )
-	{		
-		small_table[i].hash = 0;
-		small_table[i].key.clear();
-		small_table[i].value.Clear();
-	}
-	num_used = 0;
-	num_full = 0;
-	table = small_table;
-	mask = DICTIONARY_MINSIZE - 1; 
-}
-
-}
-}

+ 17 - 21
Source/Core/Element.cpp

@@ -940,22 +940,24 @@ const PseudoClassList& Element::GetActivePseudoClasses() const
 }
 
 /// Get the named attribute
-Variant* Element::GetAttribute(const String& name) const
+Variant* Element::GetAttribute(const String& name)
 {
-	return attributes.Get(name);
+	return GetIf(attributes, name);
 }
 
 // Checks if the element has a certain attribute.
-bool Element::HasAttribute(const String& name)
+bool Element::HasAttribute(const String& name) const
 {
-	return attributes.Get(name) != NULL;
+	return attributes.find(name) != attributes.end();
 }
 
 // Removes an attribute from the element
 void Element::RemoveAttribute(const String& name)
 {
-	if (attributes.Remove(name))
+	if (auto it = attributes.find(name); it != attributes.end())
 	{
+		attributes.erase(it);
+
 		AttributeNameList changed_attributes;
 		changed_attributes.insert(name);
 
@@ -993,17 +995,14 @@ Context* Element::GetContext()
 // Set a group of attributes
 void Element::SetAttributes(const ElementAttributes* _attributes)
 {
-	int index = 0;
-	String key;
-	Variant* value;
-
 	AttributeNameList changed_attributes;
-	changed_attributes.reserve(_attributes->Size());
+	changed_attributes.reserve(_attributes->size());
 
-	while (_attributes->Iterate(index, key, value))
-	{		
+	for (auto& [key, value] : *_attributes)
+	{
 		changed_attributes.insert(key);
-		attributes.Set(key, *value);
+		attributes.emplace(key, value);
+
 	}
 
 	OnAttributeChange(changed_attributes);
@@ -1012,7 +1011,7 @@ void Element::SetAttributes(const ElementAttributes* _attributes)
 // Returns the number of attributes on the element.
 int Element::GetNumAttributes() const
 {
-	return attributes.Size();
+	return (int)attributes.size();
 }
 
 // Iterates over all decorators attached to the element.
@@ -2129,14 +2128,11 @@ void Element::GetRML(String& content)
 	content += "<";
 	content += tag;
 
-	int index = 0;
-	String name;
 	String value;
-	while (IterateAttributes(index, name, value))	
+	for( auto& [name, variant] : attributes) // IterateAttributes(index, name, value))	
 	{
-		size_t length = name.size() + value.size() + 8;
-		String attribute = CreateString(length, " %s=\"%s\"", name.c_str(), value.c_str());
-		content += attribute;
+		if (variant.GetInto(value))
+			content += " " + name + "=\"" + value + "\"";
 	}
 
 	if (HasChildNodes())
@@ -2572,7 +2568,7 @@ void Element::AdvanceAnimations()
 
 		for (auto it = it_completed; it != animations.end(); ++it)
 		{
-			dictionary_list.emplace_back().Set("property", it->GetPropertyName());
+			dictionary_list.emplace_back().emplace("property", it->GetPropertyName());
 			is_transition.push_back(it->IsTransition());
 		}
 

+ 2 - 2
Source/Core/ElementHandle.cpp

@@ -134,8 +134,8 @@ void ElementHandle::ProcessEvent(Event& event)
 			}
 
 			Dictionary parameters;
-			parameters.Set("handle_x", x);
-			parameters.Set("handle_y", y);
+			parameters.emplace("handle_x", x);
+			parameters.emplace("handle_y", y);
 			DispatchEvent("handledrag", parameters);
 		}
 	}

+ 3 - 6
Source/Core/ElementUtilities.cpp

@@ -186,16 +186,13 @@ int ElementUtilities::GetStringWidth(Element* element, const WString& string)
 
 void ElementUtilities::BindEventAttributes(Element* element)
 {
-	int index = 0;
-	String name;
-	String value;
-
 	// Check for and instance the on* events
-	while(element->IterateAttributes(index, name, value))
+	//while(element->IterateAttributes(index, name, value))
+	for (auto& [name, variant] : element->GetAttributes())
 	{
 		if (name.substr(0, 2) == "on")
 		{
-			EventListener* listener = Factory::InstanceEventListener(value, element);
+			EventListener* listener = Factory::InstanceEventListener(variant.Get<String>(), element);
 			if (listener)
 				element->AddEventListener(&name[2], listener, false);
 		}

+ 4 - 4
Source/Core/Event.cpp

@@ -116,16 +116,16 @@ void Event::ProjectMouse(Element* element)
 {
 	if (element)
 	{
-		Variant *old_mouse_x = parameters_backup.Get("mouse_x");
-		Variant *old_mouse_y = parameters_backup.Get("mouse_y");
+		Variant *old_mouse_x = GetIf(parameters_backup, "mouse_x");
+		Variant *old_mouse_y = GetIf(parameters_backup, "mouse_y");
 		if (!old_mouse_x || !old_mouse_y)
 		{
 			// This is not a mouse event.
 			return;
 		}
 
-		Variant *mouse_x = parameters.Get("mouse_x");
-		Variant *mouse_y = parameters.Get("mouse_y");
+		Variant *mouse_x = GetIf(parameters, "mouse_x");
+		Variant *mouse_y = GetIf(parameters, "mouse_y");
 		if (!mouse_x || !mouse_y)
 		{
 			// This should not happen.

+ 1 - 1
Source/Core/WidgetSlider.cpp

@@ -192,7 +192,7 @@ void WidgetSlider::SetBarPosition(float _bar_position)
 	PositionBar();
 
 	Dictionary parameters;
-	parameters.Set("value", bar_position);
+	parameters.emplace("value", bar_position);
 	parent->DispatchEvent("scrollchange", parameters);
 }
 

+ 1 - 1
Source/Core/XMLNodeHandlerBody.cpp

@@ -49,7 +49,7 @@ Element* XMLNodeHandlerBody::ElementStart(XMLParser* parser, const String& ROCKE
 	Element* element = parser->GetParseFrame()->element;
 
 	// Check for and apply any template
-	String template_name = attributes.Get<String>("template", "");
+	String template_name = Get<String>(attributes, "template", "");
 	if (!template_name.empty())
 	{
 		element = XMLParseTools::ParseTemplate(element, template_name);

+ 3 - 3
Source/Core/XMLNodeHandlerHead.cpp

@@ -54,8 +54,8 @@ Element* XMLNodeHandlerHead::ElementStart(XMLParser* parser, const String& name,
 	else if (name == "link")
 	{
 		// Lookup the type and href
-		String type = ToLower(attributes.Get<String>("type", ""));
-		String href = attributes.Get<String>("href", "");
+		String type = ToLower(Get<String>(attributes, "type", ""));
+		String href = Get<String>(attributes, "href", "");
 
 		if (!type.empty() && !href.empty())
 		{
@@ -87,7 +87,7 @@ Element* XMLNodeHandlerHead::ElementStart(XMLParser* parser, const String& name,
 	else if (name == "script")
 	{
 		// Check if its an external string
-		String src = attributes.Get<String>("src", "");
+		String src = Get<String>(attributes, "src", "");
 		if (src.size() > 0)
 		{
 			parser->GetDocumentHeader()->scripts_external.push_back(src);

+ 1 - 1
Source/Core/XMLNodeHandlerTemplate.cpp

@@ -48,7 +48,7 @@ Element* XMLNodeHandlerTemplate::ElementStart(XMLParser* parser, const String& R
 	ROCKET_UNUSED_ASSERT(name);
 	ROCKET_ASSERT(name == "template");
 
-	String template_name = attributes.Get<String>("src", "");
+	String template_name = Get<String>(attributes, "src", "");
 
 	// Tell the parser to use the element handler for all child nodes
 	parser->PushDefaultHandler();

+ 2 - 5
Source/Core/XMLParser.cpp

@@ -139,12 +139,9 @@ void XMLParser::HandleElementStart(const String& _name, const XMLAttributes& _at
 	String name = ToLower(_name);
 	XMLAttributes attributes;
 	
-	String key;
-	Variant* value;
-	int pos = 0;
-	while (_attributes.Iterate(pos, key, value))
+	for (auto& [key, variant] : _attributes)
 	{
-		attributes.Set(ToLower(key), *value);
+		attributes.emplace(key, variant.Get<String>());
 	}
 
 	// Check for a specific handler that will override the child handler.

+ 40 - 37
Source/Debugger/ElementInfo.cpp

@@ -240,51 +240,54 @@ void ElementInfo::UpdateSourceElement()
 
 		if (source_element != NULL)
 		{
-			Core::String name;
-			Core::String value;
-
-			// The element's attribute list is not always synchronized with its internal values, fetch  
-			// them manually here (see e.g. Element::OnAttributeChange for relevant attributes)
-			{
-				name = "id";
-				value = source_element->GetId();
-				if (!value.empty())
-					attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
-			}
-			{
-				name = "class";
-				value = source_element->GetClassNames();
-				if (!value.empty())
-					attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
-			}
-			{
-				// Not actually an attribute, but may be useful
-				name = "pseudo";
-				value.clear();
-				for (auto str : source_element->GetActivePseudoClasses())
-					value += " :" + str;
-				if (!value.empty())
-					attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
-			}
 			{
-				name = "style";
-				value = "";
-				auto local_properties = source_element->GetLocalProperties();
-				if (local_properties)
+				Core::String name;
+				Core::String value;
+
+				// The element's attribute list is not always synchronized with its internal values, fetch  
+				// them manually here (see e.g. Element::OnAttributeChange for relevant attributes)
+				{
+					name = "id";
+					value = source_element->GetId();
+					if (!value.empty())
+						attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
+				}
+				{
+					name = "class";
+					value = source_element->GetClassNames();
+					if (!value.empty())
+						attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
+				}
 				{
-					for (auto nvp : *local_properties)
+					// Not actually an attribute, but may be useful
+					name = "pseudo";
+					value.clear();
+					for (auto str : source_element->GetActivePseudoClasses())
+						value += " :" + str;
+					if (!value.empty())
+						attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
+				}
+				{
+					name = "style";
+					value = "";
+					auto local_properties = source_element->GetLocalProperties();
+					if (local_properties)
 					{
-						auto& prop_name = nvp.first;
-						auto prop_value = nvp.second.ToString();
-						value += Core::CreateString(prop_name.size() + prop_value.size() + 12, "%s: %s; ", prop_name.c_str(), prop_value.c_str());
+						for (auto nvp : *local_properties)
+						{
+							auto& prop_name = nvp.first;
+							auto prop_value = nvp.second.ToString();
+							value += Core::CreateString(prop_name.size() + prop_value.size() + 12, "%s: %s; ", prop_name.c_str(), prop_value.c_str());
+						}
 					}
+					if (!value.empty())
+						attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
 				}
-				if (!value.empty())
-					attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
 			}
 
-			while (source_element->IterateAttributes(index, name, value))
+			for(auto& [name, variant] : source_element->GetAttributes())
 			{
+				Core::String value = variant.Get<Core::String>();
 				if(name != "class" && name != "style" && name != "id") 
 					attributes += Core::CreateString(name.size() + value.size() + 32, "%s: <em>%s</em><br />", name.c_str(), value.c_str());
 			}