From: Rico Tzschichholz Date: Mon, 25 Aug 2014 11:24:07 +0000 (+0200) Subject: Update internal gee from libgee 0.8.8+d531caa9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4eb0574b2ac034844b6d7a86aed34a7237a60c40;p=thirdparty%2Fvala.git Update internal gee from libgee 0.8.8+d531caa9 Based on patch by Maciej Piechotka https://bugzilla.gnome.org/show_bug.cgi?id=704754 --- diff --git a/ccode/valaccodefile.vala b/ccode/valaccodefile.vala index 128cd3938..0b256e518 100644 --- a/ccode/valaccodefile.vala +++ b/ccode/valaccodefile.vala @@ -24,8 +24,8 @@ public class Vala.CCodeFile { public bool is_header { get; set; } - Set declarations = new HashSet (str_hash, str_equal); - Set includes = new HashSet (str_hash, str_equal); + Set declarations = new HashSet (); + Set includes = new HashSet (); CCodeFragment comments = new CCodeFragment (); CCodeFragment include_directives = new CCodeFragment (); CCodeFragment type_declaration = new CCodeFragment (); diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index f083e9848..b9b15cd48 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -38,8 +38,8 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { public int next_temp_var_id; public bool current_method_inner_error; public bool current_method_return; - public Map variable_name_map = new HashMap (str_hash, str_equal); - public Map closure_variable_count_map = new HashMap (str_hash, str_equal); + public Map variable_name_map = new HashMap (); + public Map closure_variable_count_map = new HashMap (); public Map closure_variable_clash_map = new HashMap (); public EmitContext (Symbol? symbol = null) { @@ -342,7 +342,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { public static int ccode_attribute_cache_index = CodeNode.get_attribute_cache_index (); public CCodeBaseModule () { - predefined_marshal_set = new HashSet (str_hash, str_equal); + predefined_marshal_set = new HashSet (); predefined_marshal_set.add ("VOID:VOID"); predefined_marshal_set.add ("VOID:BOOLEAN"); predefined_marshal_set.add ("VOID:CHAR"); @@ -365,7 +365,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { predefined_marshal_set.add ("VOID:VARIANT"); predefined_marshal_set.add ("BOOLEAN:BOXED,BOXED"); - reserved_identifiers = new HashSet (str_hash, str_equal); + reserved_identifiers = new HashSet (); // C99 keywords reserved_identifiers.add ("_Bool"); @@ -716,7 +716,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { public override void visit_source_file (SourceFile source_file) { cfile = new CCodeFile (); - user_marshal_set = new HashSet (str_hash, str_equal); + user_marshal_set = new HashSet (); next_regex_id = 0; @@ -727,7 +727,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { requires_array_length = false; requires_clear_mutex = false; - wrappers = new HashSet (str_hash, str_equal); + wrappers = new HashSet (); generated_external_symbols = new HashSet (); header_file.add_include ("glib.h"); @@ -2308,7 +2308,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { return "result"; } // compiler-internal variable - if (!variable_name_map.contains (name)) { + if (!variable_name_map.has_key (name)) { variable_name_map.set (name, "_tmp%d_".printf (next_temp_var_id)); next_temp_var_id++; } @@ -4611,7 +4611,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { generate_type_declaration (expr.type_reference, cfile); - var in_arg_map = new HashMap (direct_hash, direct_equal); + var in_arg_map = new HashMap (); var out_arg_map = in_arg_map; if (m != null && m.coroutine) { @@ -4623,7 +4623,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { creation_call = finish_call; // output arguments used separately - out_arg_map = new HashMap (direct_hash, direct_equal); + out_arg_map = new HashMap (); // pass GAsyncResult stored in closure to finish function out_arg_map.set (get_param_pos (0.1), new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_res_")); } @@ -4764,7 +4764,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { last_pos = -1; while (true) { min_pos = -1; - foreach (int pos in out_arg_map.get_keys ()) { + foreach (int pos in out_arg_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } @@ -4781,7 +4781,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { last_pos = -1; while (true) { min_pos = -1; - foreach (int pos in in_arg_map.get_keys ()) { + foreach (int pos in in_arg_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } diff --git a/codegen/valaccodedelegatemodule.vala b/codegen/valaccodedelegatemodule.vala index 7150c4e4a..f1120ae9b 100644 --- a/codegen/valaccodedelegatemodule.vala +++ b/codegen/valaccodedelegatemodule.vala @@ -207,7 +207,7 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule { push_function (function); - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); if (d.has_target) { var cparam = new CCodeParameter ("self", "gpointer"); @@ -268,7 +268,7 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule { int min_pos; while (true) { min_pos = -1; - foreach (int pos in cparam_map.get_keys ()) { + foreach (int pos in cparam_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } @@ -283,7 +283,7 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule { // definition - var carg_map = new HashMap (direct_hash, direct_equal); + var carg_map = new HashMap (); int i = 0; if (m.binding == MemberBinding.INSTANCE || m.closure) { @@ -392,7 +392,7 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule { last_pos = -1; while (true) { min_pos = -1; - foreach (int pos in carg_map.get_keys ()) { + foreach (int pos in carg_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala index 6202f319b..b3e21a111 100644 --- a/codegen/valaccodemethodcallmodule.vala +++ b/codegen/valaccodemethodcallmodule.vala @@ -83,7 +83,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { deleg = ((DelegateType) itype).delegate_symbol; } - var in_arg_map = new HashMap (direct_hash, direct_equal); + var in_arg_map = new HashMap (); var out_arg_map = in_arg_map; if (m != null && m.coroutine) { @@ -125,7 +125,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { ccall = finish_call; // output arguments used separately - out_arg_map = new HashMap (direct_hash, direct_equal); + out_arg_map = new HashMap (); // pass GAsyncResult stored in closure to finish function out_arg_map.set (get_param_pos (0.1), new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_res_")); } @@ -672,7 +672,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { last_pos = -1; while (true) { min_pos = -1; - foreach (int pos in out_arg_map.get_keys ()) { + foreach (int pos in out_arg_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } @@ -689,7 +689,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { last_pos = -1; while (true) { min_pos = -1; - foreach (int pos in in_arg_map.get_keys ()) { + foreach (int pos in in_arg_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala index 700c7afdc..105ed967c 100644 --- a/codegen/valaccodemethodmodule.vala +++ b/codegen/valaccodemethodmodule.vala @@ -211,8 +211,8 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { function.modifiers |= CCodeModifiers.DEPRECATED; } - var cparam_map = new HashMap (direct_hash, direct_equal); - var carg_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); + var carg_map = new HashMap (); var cl = m.parent_symbol as Class; @@ -236,7 +236,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { function.modifiers |= CCodeModifiers.INTERNAL; } - cparam_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); bool etv_tmp = ellipses_to_valist; ellipses_to_valist = false; generate_cparameters (m, decl_space, cparam_map, function); @@ -248,7 +248,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { // _constructv function function = new CCodeFunction (get_ccode_constructv_name ((CreationMethod) m)); - cparam_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); generate_cparameters (m, decl_space, cparam_map, function); decl_space.add_function_declaration (function); @@ -454,7 +454,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { function.modifiers |= CCodeModifiers.INLINE; } - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); generate_cparameters (m, cfile, cparam_map, function); @@ -854,8 +854,8 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { * emitter! */ m.signal_reference == null) { - cparam_map = new HashMap (direct_hash, direct_equal); - var carg_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); + var carg_map = new HashMap (); generate_vfunc (m, creturn_type, cparam_map, carg_map); } @@ -1048,7 +1048,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { int min_pos; while (true) { min_pos = -1; - foreach (int pos in cparam_map.get_keys ()) { + foreach (int pos in cparam_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } @@ -1246,8 +1246,8 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { vfunc.modifiers |= CCodeModifiers.INTERNAL; } - var cparam_map = new HashMap (direct_hash, direct_equal); - var carg_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); + var carg_map = new HashMap (); push_function (vfunc); @@ -1267,7 +1267,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { if (m.is_variadic ()) { int last_pos = -1; int second_last_pos = -1; - foreach (int pos in cparam_map.get_keys ()) { + foreach (int pos in cparam_map.keys) { if (pos > last_pos) { second_last_pos = last_pos; last_pos = pos; diff --git a/codegen/valagasyncmodule.vala b/codegen/valagasyncmodule.vala index a077b4a54..73e86eacf 100644 --- a/codegen/valagasyncmodule.vala +++ b/codegen/valagasyncmodule.vala @@ -169,7 +169,7 @@ public class Vala.GAsyncModule : GtkModule { var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data"; var asyncfunc = new CCodeFunction (get_ccode_real_name (m), "void"); - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback")); cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer")); @@ -361,8 +361,8 @@ public class Vala.GAsyncModule : GtkModule { var cl = m.parent_symbol as Class; var asyncfunc = new CCodeFunction (get_ccode_name (m), "void"); - var cparam_map = new HashMap (direct_hash, direct_equal); - var carg_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); + var carg_map = new HashMap (); if (m.is_private_symbol ()) { asyncfunc.modifiers |= CCodeModifiers.STATIC; @@ -378,8 +378,8 @@ public class Vala.GAsyncModule : GtkModule { } var finishfunc = new CCodeFunction (get_ccode_finish_name (m)); - cparam_map = new HashMap (direct_hash, direct_equal); - carg_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); + carg_map = new HashMap (); if (m.is_private_symbol ()) { finishfunc.modifiers |= CCodeModifiers.STATIC; @@ -404,7 +404,7 @@ public class Vala.GAsyncModule : GtkModule { function.modifiers |= CCodeModifiers.INTERNAL; } - cparam_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 1); decl_space.add_function_declaration (function); @@ -417,7 +417,7 @@ public class Vala.GAsyncModule : GtkModule { function.modifiers |= CCodeModifiers.INTERNAL; } - cparam_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 2); decl_space.add_function_declaration (function); @@ -463,12 +463,12 @@ public class Vala.GAsyncModule : GtkModule { if (m.is_abstract || m.is_virtual) { // generate virtual function wrappers - var cparam_map = new HashMap (direct_hash, direct_equal); - var carg_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); + var carg_map = new HashMap (); generate_vfunc (m, new VoidType (), cparam_map, carg_map, "", 1); - cparam_map = new HashMap (direct_hash, direct_equal); - carg_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); + carg_map = new HashMap (); generate_vfunc (m, m.return_type, cparam_map, carg_map, "_finish", 2); } } else { @@ -494,8 +494,8 @@ public class Vala.GAsyncModule : GtkModule { if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) { var vfunc = new CCodeFunction (get_ccode_name (m)); - var cparam_map = new HashMap (direct_hash, direct_equal); - var carg_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); + var carg_map = new HashMap (); push_function (vfunc); @@ -516,8 +516,8 @@ public class Vala.GAsyncModule : GtkModule { vfunc = new CCodeFunction (get_ccode_finish_name (m)); - cparam_map = new HashMap (direct_hash, direct_equal); - carg_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); + carg_map = new HashMap (); push_function (vfunc); @@ -546,7 +546,7 @@ public class Vala.GAsyncModule : GtkModule { var finishfunc = new CCodeFunction (get_ccode_finish_real_name (m)); - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); cparam_map.set (get_param_pos (0.1), new CCodeParameter ("_res_", "GAsyncResult*")); @@ -739,7 +739,7 @@ public class Vala.GAsyncModule : GtkModule { // add vfunc field to the type struct var vdeclarator = new CCodeFunctionDeclarator (get_ccode_vfunc_name (m)); - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 1); @@ -749,7 +749,7 @@ public class Vala.GAsyncModule : GtkModule { // add vfunc field to the type struct vdeclarator = new CCodeFunctionDeclarator (get_ccode_finish_vfunc_name (m)); - cparam_map = new HashMap (direct_hash, direct_equal); + cparam_map = new HashMap (); generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 2); diff --git a/codegen/valagdbusclientmodule.vala b/codegen/valagdbusclientmodule.vala index 31cd12e55..bb38e8fe9 100644 --- a/codegen/valagdbusclientmodule.vala +++ b/codegen/valagdbusclientmodule.vala @@ -48,7 +48,7 @@ public class Vala.GDBusClientModule : GDBusModule { var func = new CCodeFunction (get_ccode_name (method)); func.modifiers = CCodeModifiers.STATIC; - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); generate_cparameters (method, cfile, cparam_map, func); @@ -900,7 +900,7 @@ public class Vala.GDBusClientModule : GDBusModule { var function = new CCodeFunction (proxy_name); function.modifiers = CCodeModifiers.STATIC; - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); generate_cparameters (m, cfile, cparam_map, function); @@ -924,7 +924,7 @@ public class Vala.GDBusClientModule : GDBusModule { var function = new CCodeFunction (proxy_name, "void"); function.modifiers = CCodeModifiers.STATIC; - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback")); cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer")); @@ -949,7 +949,7 @@ public class Vala.GDBusClientModule : GDBusModule { var function = new CCodeFunction (proxy_name); function.modifiers = CCodeModifiers.STATIC; - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); cparam_map.set (get_param_pos (0.1), new CCodeParameter ("_res_", "GAsyncResult*")); diff --git a/codegen/valagirwriter.vala b/codegen/valagirwriter.vala index c252b2d32..5e656f0ce 100644 --- a/codegen/valagirwriter.vala +++ b/codegen/valagirwriter.vala @@ -102,10 +102,10 @@ public class Vala.GIRWriter : CodeVisitor { StringBuilder buffer = new StringBuilder(); FileStream stream; - Vala.HashSet unannotated_namespaces = new Vala.HashSet(); - Vala.HashSet our_namespaces = new Vala.HashSet(); - Vala.ArrayList hierarchy = new Vala.ArrayList(); - Vala.ArrayList deferred = new Vala.ArrayList(); + HashSet unannotated_namespaces = new HashSet(); + HashSet our_namespaces = new HashSet(); + ArrayList hierarchy = new ArrayList(); + ArrayList deferred = new ArrayList(); int indent; @@ -123,7 +123,7 @@ public class Vala.GIRWriter : CodeVisitor { } } - private ArrayList externals = new ArrayList ((EqualFunc) GIRNamespace.equal); + private ArrayList externals = new ArrayList ((a, b) => { return a.equal (b); }); public void write_includes() { foreach (var i in externals) { @@ -214,11 +214,11 @@ public class Vala.GIRWriter : CodeVisitor { private void write_c_includes (Namespace ns) { // Collect C header filenames - Set header_filenames = new HashSet (str_hash, str_equal); + Set header_filenames = new HashSet (); foreach (unowned string c_header_filename in CCodeBaseModule.get_ccode_header_filenames (ns).split (",")) { header_filenames.add (c_header_filename); } - foreach (Symbol symbol in ns.scope.get_symbol_table ().get_values ()) { + foreach (Symbol symbol in ns.scope.get_symbol_table ().values) { foreach (unowned string c_header_filename in CCodeBaseModule.get_ccode_header_filenames (symbol).split (",")) { header_filenames.add (c_header_filename); } @@ -621,7 +621,7 @@ public class Vala.GIRWriter : CodeVisitor { private void visit_deferred () { var nodes = this.deferred; - this.deferred = new Vala.ArrayList(); + this.deferred = new ArrayList(); foreach (var node in nodes) { node.accept (this); @@ -1365,7 +1365,7 @@ public class Vala.GIRWriter : CodeVisitor { private void write_annotations (CodeNode node) { foreach (Attribute attr in node.attributes) { string name = camel_case_to_canonical (attr.name); - foreach (string arg_name in attr.args.get_keys ()) { + foreach (string arg_name in attr.args.keys) { string value = attr.args.get (arg_name); if (value.has_prefix ("\"")) { // eval string diff --git a/codegen/valagtkmodule.vala b/codegen/valagtkmodule.vala index 1558469a8..d8f09720c 100644 --- a/codegen/valagtkmodule.vala +++ b/codegen/valagtkmodule.vala @@ -28,9 +28,9 @@ public class Vala.GtkModule : GSignalModule { /* GResource name to real file name mapping */ private HashMap gresource_to_file_map = null; /* GtkBuilder xml handler to Vala signal mapping */ - private HashMap current_handler_to_signal_map = new HashMap(str_hash, str_equal); + private HashMap current_handler_to_signal_map = new HashMap(); /* GtkBuilder xml child to Vala class mapping */ - private HashMap current_child_to_class_map = new HashMap(str_hash, str_equal); + private HashMap current_child_to_class_map = new HashMap(); /* Required custom application-specific gtype classes to be ref'd before initializing the template */ private List current_required_app_classes = new ArrayList(); @@ -39,7 +39,7 @@ public class Vala.GtkModule : GSignalModule { if (cclass_to_vala_map != null) { return; } - cclass_to_vala_map = new HashMap(str_hash, str_equal); + cclass_to_vala_map = new HashMap(); recurse_cclass_to_vala_map (context.root); } @@ -59,7 +59,7 @@ public class Vala.GtkModule : GSignalModule { if (gresource_to_file_map != null) { return; } - gresource_to_file_map = new HashMap(str_hash, str_equal); + gresource_to_file_map = new HashMap(); foreach (var gresource in context.gresources) { if (!FileUtils.test (gresource, FileTest.EXISTS)) { Report.error (null, "GResources file `%s' does not exist".printf (gresource)); @@ -107,8 +107,8 @@ public class Vala.GtkModule : GSignalModule { Report.error (node.source_reference, "UI resource not found: `%s'. Please make sure to specify the proper GResources xml files with --gresources.".printf (ui_resource)); return; } - current_handler_to_signal_map = new HashMap(str_hash, str_equal); - current_child_to_class_map = new HashMap(str_hash, str_equal); + current_handler_to_signal_map = new HashMap(); + current_child_to_class_map = new HashMap(); MarkupReader reader = new MarkupReader (ui_file); Class current_class = null; diff --git a/codegen/valagtypemodule.vala b/codegen/valagtypemodule.vala index 6d0f0d2eb..02050a5aa 100644 --- a/codegen/valagtypemodule.vala +++ b/codegen/valagtypemodule.vala @@ -385,7 +385,7 @@ public class Vala.GTypeModule : GErrorModule { // add vfunc field to the type struct var vdeclarator = new CCodeFunctionDeclarator (get_ccode_vfunc_name (m)); - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); if (m.printf_format) { vdeclarator.modifiers |= CCodeModifiers.PRINTF; @@ -1520,7 +1520,7 @@ public class Vala.GTypeModule : GErrorModule { string cast_args = "%s *".printf (get_ccode_name (base_type)); var vdeclarator = new CCodeFunctionDeclarator (get_ccode_vfunc_name (m)); - var cparam_map = new HashMap (direct_hash, direct_equal); + var cparam_map = new HashMap (); generate_cparameters (m, cfile, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, direction); @@ -1529,7 +1529,7 @@ public class Vala.GTypeModule : GErrorModule { int min_pos; while (true) { min_pos = -1; - foreach (int pos in cparam_map.get_keys ()) { + foreach (int pos in cparam_map.keys) { if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { min_pos = pos; } diff --git a/gee/Makefile.am b/gee/Makefile.am index 04dcf3702..d1ddc681d 100644 --- a/gee/Makefile.am +++ b/gee/Makefile.am @@ -14,16 +14,68 @@ noinst_LTLIBRARIES = \ $(NULL) libgee_la_VALASOURCES = \ + abstractbidirlist.vala \ + abstractbidirsortedmap.vala \ + abstractbidirsortedset.vala \ + abstractcollection.vala \ + abstractlist.vala \ + abstractmap.vala \ + abstractmultimap.vala \ + abstractmultiset.vala \ + abstractqueue.vala \ + abstractset.vala \ + abstractsortedmap.vala \ + abstractsortedset.vala \ arraylist.vala \ + arrayqueue.vala \ + bidiriterator.vala \ + bidirlistiterator.vala \ + bidirlist.vala \ + bidirmapiterator.vala \ + bidirsortedmap.vala \ + bidirsortedset.vala \ collection.vala \ + comparable.vala \ + concurrentlist.vala \ + deque.vala \ + functions.vala \ + hashable.vala \ hashmap.vala \ + hashmultimap.vala \ + hashmultiset.vala \ hashset.vala \ + hazardpointer.vala \ iterable.vala \ - mapiterator.vala \ iterator.vala \ + lazy.vala \ + linkedlist.vala \ + listiterator.vala \ list.vala \ + mapiterator.vala \ map.vala \ + multimap.vala \ + multiset.vala \ + priorityqueue.vala \ + queue.vala \ + readonlybidirlist.vala \ + readonlybidirsortedmap.vala \ + readonlybidirsortedset.vala \ + readonlycollection.vala \ + readonlylist.vala \ + readonlymap.vala \ + readonlyset.vala \ + readonlysortedmap.vala \ + readonlysortedset.vala \ set.vala \ + sortedmap.vala \ + sortedset.vala \ + timsort.vala \ + traversable.vala \ + treemap.vala \ + treemultimap.vala \ + treemultiset.vala \ + treeset.vala \ + unfolditerator.vala \ $(NULL) libgee_la_SOURCES = \ diff --git a/gee/abstractbidirlist.vala b/gee/abstractbidirlist.vala new file mode 100644 index 000000000..41040f5b5 --- /dev/null +++ b/gee/abstractbidirlist.vala @@ -0,0 +1,46 @@ +/* bidirlistiterator.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +public abstract class Vala.AbstractBidirList : AbstractList, BidirList { + /** + * {@inheritDoc} + */ + public abstract BidirListIterator bidir_list_iterator (); + + private weak BidirList _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual new BidirList read_only_view { + owned get { + BidirList instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlyBidirList (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} + diff --git a/gee/abstractbidirsortedmap.vala b/gee/abstractbidirsortedmap.vala new file mode 100644 index 000000000..2290c17f8 --- /dev/null +++ b/gee/abstractbidirsortedmap.vala @@ -0,0 +1,53 @@ +/* abstractbidirsortedmap.vala + * + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Skeletal implementation of the {@link BidirSortedSet} interface. + * + * Contains common code shared by all set implementations. + * + * @see TreeSet + */ +public abstract class Vala.AbstractBidirSortedMap : Vala.AbstractSortedMap, BidirSortedMap { + /** + * {@inheritDoc} + */ + public abstract BidirMapIterator bidir_map_iterator (); + + private weak BidirSortedMap _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual new BidirSortedMap read_only_view { + owned get { + BidirSortedMap instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlyBidirSortedMap (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} + diff --git a/gee/abstractbidirsortedset.vala b/gee/abstractbidirsortedset.vala new file mode 100644 index 000000000..219664adc --- /dev/null +++ b/gee/abstractbidirsortedset.vala @@ -0,0 +1,53 @@ +/* abstractbidirsortedset.vala + * + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Skeletal implementation of the {@link BidirSortedSet} interface. + * + * Contains common code shared by all set implementations. + * + * @see TreeSet + */ +public abstract class Vala.AbstractBidirSortedSet : Vala.AbstractSortedSet, BidirSortedSet { + /** + * {@inheritDoc} + */ + public abstract BidirIterator bidir_iterator (); + + private weak BidirSortedSet _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual new BidirSortedSet read_only_view { + owned get { + BidirSortedSet instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlyBidirSortedSet (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} + diff --git a/gee/abstractcollection.vala b/gee/abstractcollection.vala new file mode 100644 index 000000000..a2421cef3 --- /dev/null +++ b/gee/abstractcollection.vala @@ -0,0 +1,89 @@ +/* abstractcollection.vala + * + * Copyright (C) 2007 Jürg Billeter + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes' Villevalois + */ + +/** + * Skeletal implementation of the {@link Collection} interface. + * + * Contains common code shared by all collection implementations. + * + * @see AbstractList + * @see AbstractSet + * @see AbstractMultiSet + */ +public abstract class Vala.AbstractCollection : Object, Traversable, Iterable, Collection { + /** + * {@inheritDoc} + */ + public abstract int size { get; } + + /** + * {@inheritDoc} + */ + public abstract bool read_only { get; } + + /** + * {@inheritDoc} + */ + public abstract bool contains (G item); + + /** + * {@inheritDoc} + */ + public abstract bool add (G item); + + /** + * {@inheritDoc} + */ + public abstract bool remove (G item); + + /** + * {@inheritDoc} + */ + public abstract void clear (); + + /** + * {@inheritDoc} + */ + public abstract Iterator iterator (); + + public virtual bool foreach (ForallFunc f) { + return iterator ().foreach (f); + } + + private weak Collection _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual Collection read_only_view { + owned get { + Collection instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlyCollection (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} diff --git a/gee/abstractlist.vala b/gee/abstractlist.vala new file mode 100644 index 000000000..581ad7f80 --- /dev/null +++ b/gee/abstractlist.vala @@ -0,0 +1,85 @@ +/* abstractlist.vala + * + * Copyright (C) 2007 Jürg Billeter + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes' Villevalois + */ + +/** + * Skeletal implementation of the {@link List} interface. + * + * Contains common code shared by all list implementations. + * + * @see ArrayList + * @see LinkedList + */ +public abstract class Vala.AbstractList : Vala.AbstractCollection, List { + + /** + * {@inheritDoc} + */ + public abstract ListIterator list_iterator (); + + /** + * {@inheritDoc} + */ + public abstract new G? get (int index); + + /** + * {@inheritDoc} + */ + public abstract new void set (int index, G item); + + /** + * {@inheritDoc} + */ + public abstract int index_of (G item); + + /** + * {@inheritDoc} + */ + public abstract void insert (int index, G item); + + /** + * {@inheritDoc} + */ + public abstract G remove_at (int index); + + /** + * {@inheritDoc} + */ + public abstract List? slice (int start, int stop); + + private weak List _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual new List read_only_view { + owned get { + List instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlyList (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} diff --git a/gee/abstractmap.vala b/gee/abstractmap.vala new file mode 100644 index 000000000..bc24604b3 --- /dev/null +++ b/gee/abstractmap.vala @@ -0,0 +1,131 @@ +/* abstractmap.vala + * + * Copyright (C) 2007 Jürg Billeter + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Tomaž Vajngerl + */ + +/** + * Skeletal implementation of the {@link Map} interface. + * + * Contains common code shared by all map implementations. + * + * @see HashMap + * @see TreeMap + */ +public abstract class Vala.AbstractMap : Object, Traversable>, Iterable>, Map { + + /** + * {@inheritDoc} + */ + public abstract int size { get; } + + /** + * {@inheritDoc} + */ + public abstract bool read_only { get; } + + /** + * {@inheritDoc} + */ + public abstract Set keys { owned get; } + + /** + * {@inheritDoc} + */ + public abstract Collection values { owned get; } + + /** + * {@inheritDoc} + */ + public abstract Set> entries { owned get; } + + /** + * {@inheritDoc} + */ + public abstract bool has_key (K key); + + /** + * {@inheritDoc} + */ + public abstract bool has (K key, V value); + + /** + * {@inheritDoc} + */ + public abstract new V? get (K key); + + /** + * {@inheritDoc} + */ + public abstract new void set (K key, V value); + + /** + * {@inheritDoc} + */ + public abstract bool unset (K key, out V? value = null); + + /** + * {@inheritDoc} + */ + public abstract MapIterator map_iterator (); + + /** + * {@inheritDoc} + */ + public abstract void clear (); + + private weak Map _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual Map read_only_view { + owned get { + Map instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlyMap (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } + + /** + * {@inheritDoc} + */ + public Iterator> iterator () { + return entries.iterator (); + } + + /** + * {@inheritDoc} + */ + public virtual bool foreach (ForallFunc> f) { + return iterator ().foreach (f); + } + + /** + * {@inheritDoc} + */ + public virtual Iterator stream (owned StreamFunc, A> f) { + return iterator ().stream ((owned) f); + } +} diff --git a/gee/abstractmultimap.vala b/gee/abstractmultimap.vala new file mode 100644 index 000000000..9c96c7075 --- /dev/null +++ b/gee/abstractmultimap.vala @@ -0,0 +1,311 @@ +/* abstractmultimap.vala + * + * Copyright (C) 2009 Ali Sabil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Ali Sabil + * Didier 'Ptitjes Villevalois + */ + +/** + * Skeletal implementation of the {@link MultiMap} interface. + * + * @see HashMultiMap + * @see TreeMultiMap + */ +public abstract class Vala.AbstractMultiMap : Object, MultiMap { + public int size { + get { return _nitems; } + } + + public bool read_only { + get { return false; } + } + + protected Map> _storage_map; + private int _nitems = 0; + + public AbstractMultiMap (Map> storage_map) { + this._storage_map = storage_map; + } + + public Set get_keys () { + return _storage_map.keys; + } + + public MultiSet get_all_keys () { + return new AllKeys (this); + } + + public Collection get_values () { + return new Values (this); + } + + public bool contains (K key) { + return _storage_map.has_key (key); + } + + public new Collection get (K key) { + Collection? col = _storage_map.get (key); + return col != null ? col.read_only_view : Set.empty (); + } + + public new void set (K key, V value) { + if (_storage_map.has_key (key)) { + if (_storage_map.get (key).add (value)) { + _nitems++; + } + } else { + var s = create_value_storage (); + s.add (value); + _storage_map.set (key, s); + _nitems++; + } + } + + public bool remove (K key, V value) { + if (_storage_map.has_key (key)) { + var values = _storage_map.get (key); + if (values.contains (value)) { + values.remove (value); + _nitems--; + if (values.size == 0) { + _storage_map.unset (key); + } + return true; + } + } + return false; + } + + public bool remove_all (K key) { + if (_storage_map.has_key (key)) { + int size = _storage_map.get (key).size; + if (_storage_map.unset (key)) { + _nitems -= size; + return true; + } + } + return false; + } + + public void clear () { + _storage_map.clear (); + _nitems = 0; + } + + public Vala.MapIterator map_iterator () { + return new MapIterator (_storage_map.map_iterator ()); + } + + protected abstract Collection create_value_storage (); + + protected abstract MultiSet create_multi_key_set (); + + protected abstract EqualDataFunc get_value_equal_func (); + + private class AllKeys : AbstractCollection, MultiSet { + protected AbstractMultiMap _multi_map; + + public AllKeys (AbstractMultiMap multi_map) { + _multi_map = multi_map; + } + + public override Vala.Iterator iterator () { + return new KeyIterator (_multi_map._storage_map.map_iterator ()); + } + + public override int size { get { return _multi_map.size; } } + + public override bool read_only { get { return true; } } + + public override bool contains (K key) { + return _multi_map._storage_map.has_key (key); + } + + public override bool add (K key) { + assert_not_reached (); + } + + public override bool remove (K item) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public int count (K item) { + Collection? collection = _multi_map._storage_map.get (item); + return collection != null ? collection.size : 0; + } + } + + private class Values : AbstractCollection { + protected AbstractMultiMap _multi_map; + + public Values (AbstractMultiMap multi_map) { + _multi_map = multi_map; + } + + public override Vala.Iterator iterator () { + return new ValueIterator (_multi_map._storage_map.map_iterator ()); + } + + public override int size { get { return _multi_map.size; } } + + public override bool read_only { get { return true; } } + + public override bool contains (V value) { + foreach (var col in _multi_map._storage_map.values) { + if (col.contains (value)) { + return true; + } + } + return false; + } + + public override bool add (V key) { + assert_not_reached (); + } + + public override bool remove (V item) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + } + + private class MappingIterator : Object { + protected Vala.MapIterator> outer; + protected Iterator? inner = null; + + public MappingIterator (Vala.MapIterator>? outer) { + this.outer = outer; + } + + public bool next () { + if (inner != null && inner.next ()) { + return true; + } else if (outer.next ()) { + inner = outer.get_value ().iterator (); + assert (inner.next ()); + return true; + } else { + return false; + } + } + + public bool has_next () { + return inner.has_next () || outer.has_next (); + } + + public void remove () { + assert_not_reached (); + } + + public virtual bool read_only { + get { + return true; + } + } + + public void unset () { + inner.remove (); + if (outer.get_value ().is_empty) { + outer.unset (); + } + } + + public bool valid { + get { + return inner != null && inner.valid; + } + } + } + + private class KeyIterator : MappingIterator, Traversable, Iterator { + public KeyIterator (Vala.MapIterator>? outer) { + base (outer); + } + + public new K get () { + assert (valid); + return outer.get_key (); + } + + public bool foreach (ForallFunc f) { + if (inner != null && outer.valid) { + K key = outer.get_key (); + if (!inner.foreach ((v) => {return f (key);})) { + return false; + } + outer.next (); + } + return outer.foreach ((key, col) => { + return col.foreach ((v) => {return f (key);}); + }); + } + } + + private class ValueIterator : MappingIterator, Traversable, Iterator { + public ValueIterator (Vala.MapIterator>? outer) { + base (outer); + } + + public new V get () { + assert (valid); + return inner.get (); + } + + public bool foreach (ForallFunc f) { + if (inner != null && outer.valid) { + if (!inner.foreach (f)) { + return false; + } + outer.next (); + } + return outer.foreach ((key, col) => { + return col.foreach (f); + }); + } + } + + private class MapIterator : MappingIterator, Vala.MapIterator { + public MapIterator (Vala.MapIterator>? outer) { + base (outer); + } + + public K get_key () { + assert (valid); + return outer.get_key (); + } + + public V get_value () { + assert (valid); + return inner.get (); + } + + public void set_value (V value) { + assert_not_reached (); + } + + public bool mutable { get { return false; } } + } +} diff --git a/gee/abstractmultiset.vala b/gee/abstractmultiset.vala new file mode 100644 index 000000000..4d2f781c0 --- /dev/null +++ b/gee/abstractmultiset.vala @@ -0,0 +1,180 @@ +/* abstractmultiset.vala + * + * Copyright (C) 2009 Ali Sabil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Ali Sabil + * Didier 'Ptitjes Villevalois + */ + +/** + * Skeletal implementation of the {@link MultiSet} interface. + * + * @see HashMultiSet + * @see TreeMultiSet + */ +public abstract class Vala.AbstractMultiSet : AbstractCollection, MultiSet { + public override int size { + get { return _nitems; } + } + + public override bool read_only { + get { return false; } + } + + protected Map _storage_map; + private int _nitems = 0; + + /** + * Constructs a new, empty abstract multi set. + */ + public AbstractMultiSet (Map storage_map) { + this._storage_map = storage_map; + } + + public int count (G item) { + int result = 0; + if (_storage_map.has_key (item)) { + result = _storage_map.get (item); + } + return result; + } + + public override bool contains (G item) { + return _storage_map.has_key (item); + } + + public override Vala.Iterator iterator () { + return new Iterator (this); + } + + public override bool add (G item) { + if (_storage_map.has_key (item)) { + int current_count = _storage_map.get (item); + _storage_map.set (item, current_count + 1); + } else { + _storage_map.set (item, 1); + } + _nitems++; + return true; + } + + public override bool remove (G item) { + if (_nitems > 0 && _storage_map.has_key (item)) { + int current_count = _storage_map.get (item); + if (current_count <= 1) { + _storage_map.unset (item); + } else { + _storage_map.set (item, current_count - 1); + } + _nitems--; + return true; + } + return false; + } + + public override void clear () { + _storage_map.clear (); + _nitems = 0; + } + + private class Iterator : Object, Traversable, Vala.Iterator { + private AbstractMultiSet _set; + + private MapIterator _iter; + private int _pending = 0; + private bool _removed = false; + + public Iterator (AbstractMultiSet set) { + _set = set; + _iter = _set._storage_map.map_iterator (); + } + + public bool next () { + _removed = false; + if (_pending == 0) { + if (_iter.next ()) { + _pending = _iter.get_value () - 1; + return true; + } + } else { + _pending--; + return true; + } + return false; + } + + public bool has_next () { + return _pending > 0 || _iter.has_next (); + } + + public new G get () { + assert (! _removed); + return _iter.get_key (); + } + + public void remove () { + assert (! _removed); + _iter.set_value (_pending = _iter.get_value () - 1); + if (_pending == 0) { + _iter.unset (); + } + _set._nitems--; + _removed = true; + } + + public bool read_only { + get { + return false; + } + } + + public bool valid { + get { + return ! _removed && _iter.valid; + } + } + + public bool foreach (ForallFunc f) { + if (_iter.valid) { + if (!_removed) { + if (!f(_iter.get_key())) { + return false; + } + } + for(int i = _pending - 1; i >= 0; --i) { + if (!f(_iter.get_key())) { + _pending = i; + return false; + } + } + } + while(_iter.next()) { + for(int i = _iter.get_value() - 1; i >= 0; --i) { + if (!f(_iter.get_key())) { + _removed = false; + _pending = i; + return false; + } + } + } + _removed = false; + _pending = 0; + return true; + } + } +} diff --git a/gee/abstractqueue.vala b/gee/abstractqueue.vala new file mode 100644 index 000000000..e36437868 --- /dev/null +++ b/gee/abstractqueue.vala @@ -0,0 +1,55 @@ +/* abstractqueue.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * Skeletal implementation of the {@link Queue} interface. + * + * Contains common code shared by all queue implementations. + * + * @see PriorityQueue + */ +public abstract class Vala.AbstractQueue : Vala.AbstractCollection, Queue { + /** + * {@inheritDoc} + */ + public abstract int capacity { get; } + + /** + * {@inheritDoc} + */ + public abstract int remaining_capacity { get; } + + /** + * {@inheritDoc} + */ + public abstract bool is_full { get; } + + /** + * {@inheritDoc} + */ + public abstract G? peek (); + + /** + * {@inheritDoc} + */ + public abstract G? poll (); +} diff --git a/gee/abstractset.vala b/gee/abstractset.vala new file mode 100644 index 000000000..71effc522 --- /dev/null +++ b/gee/abstractset.vala @@ -0,0 +1,50 @@ +/* abstractset.vala + * + * Copyright (C) 2007 Jürg Billeter + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Julien Peeters + */ + +/** + * Skeletal implementation of the {@link Set} interface. + * + * Contains common code shared by all set implementations. + * + * @see HashSet + * @see TreeSet + */ +public abstract class Vala.AbstractSet : Vala.AbstractCollection, Set { + + private weak Set _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual new Set read_only_view { + owned get { + Set instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlySet (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} diff --git a/gee/abstractsortedmap.vala b/gee/abstractsortedmap.vala new file mode 100644 index 000000000..876a4c3c5 --- /dev/null +++ b/gee/abstractsortedmap.vala @@ -0,0 +1,65 @@ +/* readonlysortedmap.vala + * + * Copyright (C) 2009-2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +public abstract class Vala.AbstractSortedMap : AbstractMap, SortedMap { + /** + * {@inheritDoc} + */ + public abstract SortedMap head_map (K before); + + /** + * {@inheritDoc} + */ + public abstract SortedMap tail_map (K after); + + /** + * {@inheritDoc} + */ + public abstract SortedMap sub_map (K before, K after); + + /** + * {@inheritDoc} + */ + public abstract SortedSet ascending_keys { owned get; } + + /** + * {@inheritDoc} + */ + public abstract SortedSet> ascending_entries { owned get; } + + private weak SortedMap _read_only_view; + /** + * The read-only view this map. + */ + public new SortedMap read_only_view { + owned get { + SortedMap instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlySortedMap (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} + diff --git a/gee/abstractsortedset.vala b/gee/abstractsortedset.vala new file mode 100644 index 000000000..03f02f34d --- /dev/null +++ b/gee/abstractsortedset.vala @@ -0,0 +1,98 @@ +/* abstractsortedset.vala + * + * Copyright (C) 2009-2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Skeletal implementation of the {@link SortedSet} interface. + * + * Contains common code shared by all set implementations. + * + * @see TreeSet + */ +public abstract class Vala.AbstractSortedSet : Vala.AbstractSet, SortedSet { + /** + * {@inheritDoc} + */ + public abstract G first (); + + /** + * {@inheritDoc} + */ + public abstract G last (); + + /** + * {@inheritDoc} + */ + public abstract Iterator? iterator_at (G element); + + /** + * {@inheritDoc} + */ + public abstract G? lower (G element); + + /** + * {@inheritDoc} + */ + public abstract G? higher (G element); + + /** + * {@inheritDoc} + */ + public abstract G? floor (G element); + + /** + * {@inheritDoc} + */ + public abstract G? ceil (G element); + + /** + * {@inheritDoc} + */ + public abstract SortedSet head_set (G before); + + /** + * {@inheritDoc} + */ + public abstract SortedSet tail_set (G after); + + /** + * {@inheritDoc} + */ + public abstract SortedSet sub_set (G from, G to); + + private weak SortedSet _read_only_view; + + /** + * {@inheritDoc} + */ + public virtual new SortedSet read_only_view { + owned get { + SortedSet instance = _read_only_view; + if (_read_only_view == null) { + instance = new ReadOnlySortedSet (this); + _read_only_view = instance; + instance.add_weak_pointer ((void**) (&_read_only_view)); + } + return instance; + } + } +} + diff --git a/gee/arraylist.vala b/gee/arraylist.vala index fe398e432..4bcb3e06b 100644 --- a/gee/arraylist.vala +++ b/gee/arraylist.vala @@ -3,6 +3,7 @@ * Copyright (C) 2004-2005 Novell, Inc * Copyright (C) 2005 David Waite * Copyright (C) 2007-2008 Jürg Billeter + * Copyright (C) 2009 Didier Villevalois * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,66 +21,139 @@ * * Author: * Jürg Billeter + * Didier 'Ptitjes Villevalois */ using GLib; /** - * Arrays of arbitrary elements which grow automatically as elements are added. + * Resizable array implementation of the {@link List} interface. + * + * The storage array grows automatically when needed. + * + * This implementation is pretty good for rarely modified data. Because they are + * stored in an array this structure does not fit for highly mutable data. For an + * alternative implementation see {@link LinkedList}. + * + * @see LinkedList */ -public class Vala.ArrayList : List { +public class Vala.ArrayList : AbstractBidirList { + /** + * {@inheritDoc} + */ public override int size { get { return _size; } } - public EqualFunc equal_func { - set { _equal_func = value; } + /** + * {@inheritDoc} + */ + public override bool read_only { + get { return false; } } - private G[] _items = new G[4]; - private int _size; - private EqualFunc _equal_func; + /** + * The elements' equality testing function. + */ + [CCode (notify = false)] + public EqualDataFunc equal_func { private set; get; } + + internal G[] _items = new G[4]; + internal int _size; // concurrent modification protection private int _stamp = 0; - public ArrayList (EqualFunc equal_func = GLib.direct_equal) { + /** + * Constructs a new, empty array list. + * + * If not provided, the function parameter is requested to the + * {@link Functions} function factory methods. + * + * @param equal_func an optional element equality testing function + */ + public ArrayList (owned EqualDataFunc? equal_func = null) { + if (equal_func == null) { + equal_func = Functions.get_equal_func_for (typeof (G)); + } this.equal_func = equal_func; } - public override Type get_element_type () { - return typeof (G); + /** + * {@inheritDoc} + */ + public override bool foreach(ForallFunc f) { + for (int i = 0; i < _size; i++) { + if (!f (_items[i])) { + return false; + } + } + return true; } + /** + * {@inheritDoc} + */ public override Vala.Iterator iterator () { return new Iterator (this); } + /** + * {@inheritDoc} + */ + public override ListIterator list_iterator () { + return new Iterator (this); + } + + /** + * {@inheritDoc} + */ + public override BidirListIterator bidir_list_iterator () { + return new Iterator (this); + } + + /** + * {@inheritDoc} + */ public override bool contains (G item) { return (index_of (item) != -1); } + /** + * {@inheritDoc} + */ public override int index_of (G item) { for (int index = 0; index < _size; index++) { - if (_equal_func (_items[index], item)) { + if (equal_func (_items[index], item)) { return index; } } return -1; } - public override G? get (int index) { - assert (index >= 0 && index < _size); + /** + * {@inheritDoc} + */ + public override G get (int index) { + assert (index >= 0); + assert (index < _size); return _items[index]; } + /** + * {@inheritDoc} + */ public override void set (int index, G item) { - assert (index >= 0 && index < _size); + assert (index >= 0); + assert (index < _size); _items[index] = item; } + /** + * {@inheritDoc} + */ public override bool add (G item) { if (_size == _items.length) { grow_if_needed (1); @@ -89,8 +163,12 @@ public class Vala.ArrayList : List { return true; } + /** + * {@inheritDoc} + */ public override void insert (int index, G item) { - assert (index >= 0 && index <= _size); + assert (index >= 0); + assert (index <= _size); if (_size == _items.length) { grow_if_needed (1); @@ -100,9 +178,12 @@ public class Vala.ArrayList : List { _stamp++; } + /** + * {@inheritDoc} + */ public override bool remove (G item) { for (int index = 0; index < _size; index++) { - if (_equal_func (_items[index], item)) { + if (equal_func (_items[index], item)) { remove_at (index); return true; } @@ -110,16 +191,25 @@ public class Vala.ArrayList : List { return false; } - public override void remove_at (int index) { - assert (index >= 0 && index < _size); + /** + * {@inheritDoc} + */ + public override G remove_at (int index) { + assert (index >= 0); + assert (index < _size); + G item = _items[index]; _items[index] = null; shift (index + 1, -1); _stamp++; + return item; } + /** + * {@inheritDoc} + */ public override void clear () { for (int index = 0; index < _size; index++) { _items[index] = null; @@ -128,8 +218,40 @@ public class Vala.ArrayList : List { _stamp++; } + /** + * {@inheritDoc} + */ + public override List? slice (int start, int stop) { + return_val_if_fail (start <= stop, null); + return_val_if_fail (start >= 0, null); + return_val_if_fail (stop <= _size, null); + + var slice = new ArrayList (this.equal_func); + for (int i = start; i < stop; i++) { + slice.add (this[i]); + } + + return slice; + } + + /** + * {@inheritDoc} + */ + public bool add_all (Collection collection) { + if (collection.is_empty) { + return false; + } + + grow_if_needed (collection.size); + collection.foreach ((item) => {_items[_size++] = item; return true;}); + _stamp++; + return true; + } + private void shift (int start, int delta) { - assert (start >= 0 && start <= _size && start >= -delta); + assert (start >= 0); + assert (start <= _size); + assert (start >= -delta); _items.move (start, start + delta, _size - start); @@ -152,40 +274,144 @@ public class Vala.ArrayList : List { _items.resize (value); } - private class Iterator : Vala.Iterator { - public ArrayList list { - set { - _list = value; - _stamp = _list._stamp; - } - } - + private class Iterator : Object, Traversable, Vala.Iterator, BidirIterator, ListIterator, BidirListIterator { private ArrayList _list; private int _index = -1; + private bool _removed = false; // concurrent modification protection - public int _stamp = 0; + private int _stamp = 0; public Iterator (ArrayList list) { - this.list = list; + _list = list; + _stamp = _list._stamp; } - public override bool next () { + public bool next () { assert (_stamp == _list._stamp); - if (_index < _list._size) { + if (_index + 1 < _list._size) { _index++; + _removed = false; + return true; + } + return false; + } + + public bool has_next () { + assert (_stamp == _list._stamp); + return (_index + 1 < _list._size); + } + + public bool first () { + assert (_stamp == _list._stamp); + if (_list.size == 0) { + return false; + } + _index = 0; + _removed = false; + return true; + } + + public new G get () { + assert (_stamp == _list._stamp); + assert (_index >= 0); + assert (_index < _list._size); + assert (! _removed); + return _list._items[_index]; + } + + public void remove () { + assert (_stamp == _list._stamp); + assert (_index >= 0); + assert (_index < _list._size); + assert (! _removed); + _list.remove_at (_index); + _index--; + _removed = true; + _stamp = _list._stamp; + } + + public bool previous () { + assert (_stamp == _list._stamp); + if (_index > 0) { + _index--; + return true; } - return (_index < _list._size); + return false; + } + + public bool has_previous () { + assert (_stamp == _list._stamp); + return (_index - 1 >= 0); + } + + public bool last () { + assert (_stamp == _list._stamp); + if (_list.size == 0) { + return false; + } + _index = _list._size - 1; + return true; + } + + public new void set (G item) { + assert (_stamp == _list._stamp); + assert (_index >= 0); + assert (_index < _list._size); + _list._items[_index] = item; + _stamp = ++_list._stamp; + } + + public void insert (G item) { + assert (_stamp == _list._stamp); + assert (_index >= 0); + assert (_index < _list._size); + _list.insert (_index, item); + _index++; + _stamp = _list._stamp; } - public override G? get () { + public void add (G item) { assert (_stamp == _list._stamp); + assert (_index >= 0); + assert (_index < _list._size); + _list.insert (_index + 1, item); + _index++; + _stamp = _list._stamp; + } + + public int index () { + assert (_stamp == _list._stamp); + assert (_index >= 0); + assert (_index < _list._size); + return _index; + } - if (_index < 0 || _index >= _list._size) { - return null; + public bool read_only { + get { + return false; } + } - return _list.get (_index); + public bool valid { + get { + return _index >= 0 && _index < _list._size && ! _removed; + } + } + + public bool foreach (ForallFunc f) { + assert (_stamp == _list._stamp); + if (_index < 0 || _removed) { + _index++; + } + while (_index < _list._size) { + if (!f (_list._items[_index])) { + return false; + } + _index++; + } + _index = _list._size - 1; + return true; } } } diff --git a/gee/arrayqueue.vala b/gee/arrayqueue.vala new file mode 100644 index 000000000..9eac3e623 --- /dev/null +++ b/gee/arrayqueue.vala @@ -0,0 +1,342 @@ +/* arrayqueue.vala + * + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Resizable array implementation of the {@link Deque} interface. + * + * The storage array grows automatically when needed. + * + * This implementation is pretty good for lookups at the end or random. + * Because they are stored in an array this structure does not fit for deleting + * arbitrary elements. For an alternative implementation see {@link LinkedList}. + * + * @see LinkedList + */ +public class Vala.ArrayQueue : Vala.AbstractQueue, Deque { + /** + * Constructs a new, empty array queue. + * + * If not provided, the function parameter is requested to the + * {@link Functions} function factory methods. + * + * @param equal_func an optional element equality testing function + */ + public ArrayQueue (owned EqualDataFunc? equal_func = null) { + if (equal_func == null) { + equal_func = Functions.get_equal_func_for (typeof (G)); + } + this.equal_func = equal_func; + this._items = new G[10]; + } + + [CCode (notify = false)] + public EqualDataFunc equal_func { private set; get; } + + /** + * {@inheritDoc} + */ + public override int size { get { return _length; } } + + public bool is_empty { get { return _length == 0; } } + + /** + * {@inheritDoc} + */ + public override bool read_only { get { return false; } } + + /** + * {@inheritDoc} + */ + public override int capacity { get {return Queue.UNBOUNDED_CAPACITY;} } + + /** + * {@inheritDoc} + */ + public override int remaining_capacity { get {return Queue.UNBOUNDED_CAPACITY;} } + + /** + * {@inheritDoc} + */ + public override bool is_full { get { return false; } } + + /** + * {@inheritDoc} + */ + public override Vala.Iterator iterator() { + return new Iterator (this); + } + + /** + * {@inheritDoc} + */ + public override bool add (G element) { + return offer_tail (element); + } + + /** + * {@inheritDoc} + */ + public override bool contains (G item) { + return find_index(item) != -1; + } + + /** + * {@inheritDoc} + */ + public override bool remove (G item) { + _stamp++; + int index = find_index (item); + if (index == -1) { + return false; + } else { + remove_at (index); + return true; + } + } + + /** + * {@inheritDoc} + */ + public override void clear() { + _stamp++; + for (int i = 0; i < _length; i++) { + _items[(_start + i) % _items.length] = null; + } + _start = _length = 0; + } + + /** + * {@inheritDoc} + */ + public override G? peek () { + return peek_head (); + } + + /** + * {@inheritDoc} + */ + public override G? poll () { + return poll_head (); + } + + /** + * {@inheritDoc} + */ + public bool offer_head (G element) { + grow_if_needed (); + _start = (_items.length + _start - 1) % _items.length; + _length++; + _items[_start] = element; + _stamp++; + return true; + } + + /** + * {@inheritDoc} + */ + public G? peek_head () { + return _items[_start]; + } + + /** + * {@inheritDoc} + */ + public G? poll_head () { + _stamp++; + if (_length == 0) { + _start = 0; + return null; + } else { + _length--; + G result = (owned)_items[_start]; + _start = (_start + 1) % _items.length; + return (owned)result; + } + } + + /** + * {@inheritDoc} + */ + public int drain_head (Collection recipient, int amount = -1) { + return drain (recipient, amount); + } + + /** + * {@inheritDoc} + */ + public bool offer_tail (G element) { + grow_if_needed(); + _items[(_start + _length++) % _items.length] = element; + _stamp++; + return true; + } + + /** + * {@inheritDoc} + */ + public G? peek_tail () { + return _items[(_items.length + _start + _length - 1) % _items.length]; + } + + /** + * {@inheritDoc} + */ + public G? poll_tail () { + _stamp++; + if (_length == 0) { + _start = 0; + return null; + } else { + return (owned)_items[(_items.length + _start + --_length) % _items.length]; + } + } + + /** + * {@inheritDoc} + */ + public int drain_tail (Collection recipient, int amount = -1) { + G? item = null; + int drained = 0; + while((amount == -1 || --amount >= 0) && (item = poll_tail ()) != null) { + recipient.add(item); + drained++; + } + return drained; + } + + /** + * {@inheritDoc} + */ + private void grow_if_needed () { + if (_items.length < _length +1 ) { + _items.resize (2 * _items.length); +#if 0 + _items.move (0, _length, _start); +#else + // See bug #667452 + for(int i = 0; i < _start; i++) + _items[_length + i] = (owned)_items[i]; +#endif + } + } + + private int find_index (G item) { + for (int i = _start; i < int.min(_items.length, _start + _length); i++) { + if (equal_func(item, _items[i])) { + return i; + } + } + for (int i = 0; i < _start + _length - _items.length; i++) { + if (equal_func(item, _items[i])) { + return i; + } + } + return -1; + } + + private void remove_at (int index) { + int end = (_items.length + _start + _length - 1) % _items.length + 1; + if (index == _start) { + _items[_start++] = null; + _length--; + return; + } else if (index > _start && end <= _start) { + _items[index] = null; + _items.move (index + 1, index, _items.length - 1); + _items[_items.length - 1] = (owned)_items[0]; + _items.move (1, 0, end - 1); + _length--; + } else { + _items[index] = null; + _items.move (index + 1, index, end - (index + 1)); + _length--; + } + } + + private class Iterator : GLib.Object, Traversable, Vala.Iterator { + public Iterator (ArrayQueue queue) { + _queue = queue; + _stamp = _queue._stamp; + } + + public bool next () { + assert (_queue._stamp == _stamp); + if (has_next ()) { + _offset++; + _removed = false; + return true; + } else { + return false; + } + } + + public bool has_next () { + assert (_queue._stamp == _stamp); + return _offset + 1 < _queue._length; + } + + public new G get () { + assert (_queue._stamp == _stamp); + assert (_offset != -1); + assert (!_removed); + return _queue._items[(_queue._start + _offset) % _queue._items.length]; + } + + public void remove () { + assert (_queue._stamp++ == _stamp++); + _queue.remove_at((_queue._start + _offset) % _queue._items.length); + _offset--; + _removed = true; + } + + public bool valid { get {return _offset != -1 && !_removed;} } + + public bool read_only { get {return false;} } + + public bool foreach (ForallFunc f) { + assert (_queue._stamp == _stamp); + if (!valid) { + _offset++; + _removed = false; + } + for (int i = _offset; i < _queue._length; i++) { + if (!f (_queue._items[(_queue._start + i) % _queue._items.length])) { + _offset = i; + return false; + } + } + _offset = _queue._length - 1; + return true; + } + + private ArrayQueue _queue; + private int _stamp; + private int _offset = -1; + private bool _removed = false; + } + + private G[] _items; + private int _start = 0; + private int _length = 0; + private int _stamp = 0; +} + diff --git a/gee/bidiriterator.vala b/gee/bidiriterator.vala new file mode 100644 index 000000000..01d3af213 --- /dev/null +++ b/gee/bidiriterator.vala @@ -0,0 +1,55 @@ +/* bidiriterator.vala + * + * Copyright (C) 2009 Didier Villevalois, Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * A bi-directional iterator. + */ +[GenericAccessors] +public interface Vala.BidirIterator : Vala.Iterator { + /** + * Rewinds to the previous element in the iteration. + * + * @return ``true`` if the iterator has a previous element + */ + public abstract bool previous (); + + /** + * Checks whether there is a previous element in the iteration. + * + * @return ``true`` if the iterator has a previous element + */ + public abstract bool has_previous (); + + /** + * Rewinds to the first element in the iteration. + * + * @return ``true`` if the iterator has a first element + */ + public abstract bool first (); + + /** + * Advances to the last element in the iteration. + * + * @return ``true`` if the iterator has a last element + */ + public abstract bool last (); +} diff --git a/gee/bidirlist.vala b/gee/bidirlist.vala new file mode 100644 index 000000000..f482adfb3 --- /dev/null +++ b/gee/bidirlist.vala @@ -0,0 +1,35 @@ +/* bidirlist.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ +[GenericAccessors] +public interface Vala.BidirList : Vala.List { + /** + * Returns a BidirListIterator that can be used for iteration over this list. + * + * @return a BidirListIterator that can be used for iteration over this list + */ + public abstract new BidirListIterator bidir_list_iterator (); + + /** + * The read-only view of this list. + */ + public abstract new BidirList read_only_view { owned get; } +} diff --git a/gee/bidirlistiterator.vala b/gee/bidirlistiterator.vala new file mode 100644 index 000000000..094a5f7c5 --- /dev/null +++ b/gee/bidirlistiterator.vala @@ -0,0 +1,30 @@ +/* bidirlistiterator.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ +[GenericAccessors] +public interface Vala.BidirListIterator : Vala.BidirIterator, Vala.ListIterator { + /** + * Inserts the specified item before the current item in the iteration. The + * cursor is let to point to the current item. + */ + public abstract void insert (G item); +} + diff --git a/gee/bidirmapiterator.vala b/gee/bidirmapiterator.vala new file mode 100644 index 000000000..32e2e6bcc --- /dev/null +++ b/gee/bidirmapiterator.vala @@ -0,0 +1,56 @@ +/* bidiriterator.vala + * + * Copyright (C) 2009 Didier Villevalois, Maciej Piechotka + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * A bi-directional Map iterator. + */ +[GenericAccessors] +public interface Vala.BidirMapIterator : Vala.MapIterator { + /** + * Rewinds to the previous element in the iteration. + * + * @return `true` if the iterator has a previous element + */ + public abstract bool previous (); + + /** + * Checks whether there is a previous element in the iteration. + * + * @return `true` if the iterator has a previous element + */ + public abstract bool has_previous (); + + /** + * Goes back to the first element. + * + * @return `true` if the iterator has a first element + */ + public abstract bool first (); + + /** + * Advances to the last element in the iteration. + * + * @return `true` if the iterator has a last element + */ + public abstract bool last (); +} diff --git a/gee/bidirsortedmap.vala b/gee/bidirsortedmap.vala new file mode 100644 index 000000000..1a70576dc --- /dev/null +++ b/gee/bidirsortedmap.vala @@ -0,0 +1,45 @@ +/* bidirsortedmap.vala + * + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ +[GenericAccessors] +public interface Vala.BidirSortedMap : SortedMap { + /** + * Returns a bi-directional iterator for this map. + * + * @return a bi-directional map iterator + */ + public abstract BidirMapIterator bidir_map_iterator (); + + /** + * The read-only view of this set. + */ + public abstract new BidirSortedMap read_only_view { owned get; } + + /** + * Returns an immutable empty sorted set. + * + * @return an immutable empty sorted set + */ + public static BidirSortedMap empty () { + return new TreeMap ().read_only_view; + } +} + diff --git a/gee/bidirsortedset.vala b/gee/bidirsortedset.vala new file mode 100644 index 000000000..b22172d45 --- /dev/null +++ b/gee/bidirsortedset.vala @@ -0,0 +1,46 @@ +/* bidirsortedset.vala + * + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ +[GenericAccessors] +public interface Vala.BidirSortedSet : SortedSet { + /** + * Returns a {@link BidirIterator} that can be used for bi-directional + * iteration over this sorted set. + * + * @return a {@link BidirIterator} over this sorted set + */ + public abstract BidirIterator bidir_iterator (); + + /** + * The read-only view of this set. + */ + public abstract new BidirSortedSet read_only_view { owned get; } + + /** + * Returns an immutable empty sorted set. + * + * @return an immutable empty sorted set + */ + public static BidirSortedSet empty () { + return new TreeSet ().read_only_view; + } +} + diff --git a/gee/collection.vala b/gee/collection.vala index 76f568a1f..112e6ac18 100644 --- a/gee/collection.vala +++ b/gee/collection.vala @@ -1,6 +1,6 @@ /* collection.vala * - * Copyright (C) 2007 Jürg Billeter + * Copyright (C) 2007-2009 Jürg Billeter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,21 +21,32 @@ */ /** - * Serves as the base interface for implementing collection classes. Defines - * size, iteration, and modification methods. + * A generic collection of objects. */ -public abstract class Vala.Collection : Iterable { +[GenericAccessors] +public interface Vala.Collection : Iterable { /** * The number of items in this collection. */ public abstract int size { get; } + /** + * Specifies whether this collection is empty. + */ + public virtual bool is_empty { get { return size == 0; } } + + /** + * Specifies whether this collection can change - i.e. wheather {@link add}, + * {@link remove} etc. are legal operations. + */ + public abstract bool read_only { get; } + /** * Determines whether this collection contains the specified item. * * @param item the item to locate in the collection * - * @return true if item is found, false otherwise + * @return ``true`` if item is found, ``false`` otherwise */ public abstract bool contains (G item); @@ -45,17 +56,17 @@ public abstract class Vala.Collection : Iterable { * * @param item the item to add to the collection * - * @return true if the collection has been changed, false otherwise + * @return ``true`` if the collection has been changed, ``false`` otherwise */ public abstract bool add (G item); /** - * Removes the first occurrence of an item from this collection. Must not + * Removes the first occurence of an item from this collection. Must not * be called on read-only collections. * * @param item the item to remove from the collection * - * @return true if the collection has been changed, false otherwise + * @return ``true`` if the collection has been changed, ``false`` otherwise */ public abstract bool remove (G item); @@ -64,5 +75,219 @@ public abstract class Vala.Collection : Iterable { * read-only collections. */ public abstract void clear (); + + /** + * Adds all items in the input collection to this collection. + * + * @param collection the collection which items will be added to this + * collection. + * + * @return ``true`` if the collection has been changed, ``false`` otherwise + */ + public virtual bool add_all (Collection collection) { + return collection.fold ((item, changed) => changed | add (item), false); + } + + /** + * Returns ``true`` it this collection contains all items as the input + * collection. + * + * @param collection the collection which items will be compared with + * this collection. + * + * @return ``true`` if the collection has been changed, ``false`` otherwise + */ + public virtual bool contains_all (Collection collection) { + return collection.foreach ((item) => contains (item)); + } + + /** + * Removes the subset of items in this collection corresponding to the + * elments in the input collection. If there is several occurrences of + * the same value in this collection they are decremented of the number + * of occurrences in the input collection. + * + * @param collection the collection which items will be compared with + * this collection. + * + * @return ``true`` if the collection has been changed, ``false`` otherwise + */ + public virtual bool remove_all (Collection collection) { + return collection.fold ((item, changed) => changed | remove (item), false); + } + + /** + * Removes all items in this collection that are not contained in the input + * collection. In other words all common items of both collections are + * retained in this collection. + * + * @param collection the collection which items will be compared with + * this collection. + * + * @return ``true`` if the collection has been changed, ``false`` otherwise + */ + public virtual bool retain_all (Collection collection) { + bool changed = false; + for (Iterator iter = iterator(); iter.next ();) { + G item = iter.get (); + if (!collection.contains (item)) { + iter.remove (); + changed = true; + } + } + return changed; + } + + /** + * Returns an array containing all of items from this collection. + * + * @return an array containing all of items from this collection + */ + public virtual G[] to_array () { + var t = typeof (G); + if (t == typeof (bool)) { + return (G[]) to_bool_array ((Collection) this); + } else if (t == typeof (char)) { + return (G[]) to_char_array ((Collection) this); + } else if (t == typeof (uchar)) { + return (G[]) to_uchar_array ((Collection) this); + } else if (t == typeof (int)) { + return (G[]) to_int_array ((Collection) this); + } else if (t == typeof (uint)) { + return (G[]) to_uint_array ((Collection) this); + } else if (t == typeof (int64)) { + return (G[]) to_int64_array ((Collection) this); + } else if (t == typeof (uint64)) { + return (G[]) to_uint64_array ((Collection) this); + } else if (t == typeof (long)) { + return (G[]) to_long_array ((Collection) this); + } else if (t == typeof (ulong)) { + return (G[]) to_ulong_array ((Collection) this); + } else if (t == typeof (float)) { + return (G[]) to_float_array ((Collection) this); + } else if (t == typeof (double)) { + return (G[]) to_double_array ((Collection) this); + } else { + G[] array = new G[size]; + int index = 0; + foreach (G element in this) { + array[index++] = element; + } + return array; + } + } + + private static bool[] to_bool_array (Collection coll) { + bool[] array = new bool[coll.size]; + int index = 0; + foreach (bool element in coll) { + array[index++] = element; + } + return array; + } + + private static char[] to_char_array (Collection coll) { + char[] array = new char[coll.size]; + int index = 0; + foreach (char element in coll) { + array[index++] = element; + } + return array; + } + + private static uchar[] to_uchar_array (Collection coll) { + uchar[] array = new uchar[coll.size]; + int index = 0; + foreach (uchar element in coll) { + array[index++] = element; + } + return array; + } + + private static int[] to_int_array (Collection coll) { + int[] array = new int[coll.size]; + int index = 0; + foreach (int element in coll) { + array[index++] = element; + } + return array; + } + + private static uint[] to_uint_array (Collection coll) { + uint[] array = new uint[coll.size]; + int index = 0; + foreach (uint element in coll) { + array[index++] = element; + } + return array; + } + + private static int64[] to_int64_array (Collection coll) { + int64[] array = new int64[coll.size]; + int index = 0; + foreach (int64 element in coll) { + array[index++] = element; + } + return array; + } + + private static uint64[] to_uint64_array (Collection coll) { + uint64[] array = new uint64[coll.size]; + int index = 0; + foreach (uint64 element in coll) { + array[index++] = element; + } + return array; + } + + private static long[] to_long_array (Collection coll) { + long[] array = new long[coll.size]; + int index = 0; + foreach (long element in coll) { + array[index++] = element; + } + return array; + } + + private static ulong[] to_ulong_array (Collection coll) { + ulong[] array = new ulong[coll.size]; + int index = 0; + foreach (ulong element in coll) { + array[index++] = element; + } + return array; + } + + private static float?[] to_float_array (Collection coll) { + float?[] array = new float?[coll.size]; + int index = 0; + foreach (float element in coll) { + array[index++] = element; + } + return array; + } + + private static double?[] to_double_array (Collection coll) { + double?[] array = new double?[coll.size]; + int index = 0; + foreach (double element in coll) { + array[index++] = element; + } + return array; + } + + /** + * The read-only view of this collection. + */ + public abstract Collection read_only_view { owned get; } + + /** + * Returns an immutable empty collection. + * + * @return an immutable empty collection + */ + public static Collection empty () { + return new HashSet ().read_only_view; + } } diff --git a/gee/comparable.vala b/gee/comparable.vala new file mode 100644 index 000000000..489a7c8a9 --- /dev/null +++ b/gee/comparable.vala @@ -0,0 +1,37 @@ +/* comparable.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * This interface defines a total ordering among instances of each class + * implementing it. + * + * @see Hashable + */ +public interface Vala.Comparable : Object { + /** + * Compares this object with the specifed object. + * + * @return a negative integer, zero, or a positive integer as this object + * is less than, equal to, or greater than the specified object + */ + public abstract int compare_to (G object); +} diff --git a/gee/concurrentlist.vala b/gee/concurrentlist.vala new file mode 100644 index 000000000..485e2d240 --- /dev/null +++ b/gee/concurrentlist.vala @@ -0,0 +1,587 @@ +/* concurrentlist.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * A single-linked list. This implementation is based on + * [[http://www.cse.yorku.ca/~ruppert/papers/lfll.pdf|Mikhail Fomitchev and Eric Ruppert paper ]]. + * + * Many threads are allowed to operate on the same structure as well as modification + * of structure during iteration is allowed. However the change may not be immidiatly + * visible to other threads. + */ +public class Vala.ConcurrentList : AbstractList { + /** + * The elements' equality testing function. + */ + [CCode (notify = false)] + public Vala.EqualDataFunc equal_func { private set; get; } + + /** + * Construct new, empty single linked list + * + * If not provided, the function parameter is requested to the + * {@link Functions} function factory methods. + * + * @param equal_func an optional element equality testing function + */ + public ConcurrentList (owned Vala.EqualDataFunc? equal_func = null) { + if (equal_func == null) + equal_func = Vala.Functions.get_equal_func_for (typeof (G)); + this.equal_func = (owned)equal_func; + _head = new Node.head (); + HazardPointer.set_pointer> (&_tail, _head); + } + + ~ConcurrentList () { + HazardPointer.Context ctx = new HazardPointer.Context (); + _head = null; + HazardPointer.set_pointer?> (&_tail, null); + } + + /** + * {@inheritDoc} + */ + public override bool read_only { + get { + return false; + } + } + + /** + * {@inheritDoc} + */ + public override int size { + get { + HazardPointer.Context ctx = new HazardPointer.Context (); + int result = 0; + for (var iter = iterator (); iter.next ();) + result++; + return result; + } + } + + /** + * {@inheritDoc} + */ + public bool is_empty { + get { + return !iterator ().next (); + } + } + + /** + * {@inheritDoc} + */ + public override bool contains (G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + for (var iter = iterator (); iter.next ();) + if (equal_func (item, iter.get ())) + return true; + return false; + } + + /** + * {@inheritDoc} + */ + public override bool add (G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + Node node = new Node (item); + node.insert (get_tail (), null); + return true; + } + + /** + * {@inheritDoc} + */ + public override bool remove (G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + Vala.Iterator iter = iterator (); + while (iter.next ()) { + if (equal_func (item, iter.get ())) { + iter.remove (); + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public override void clear () { + HazardPointer.Context ctx = new HazardPointer.Context (); + var iter = iterator (); + while (iter.next ()) + iter.remove (); + HazardPointer.set_pointer (&_tail, _head); + } + + /** + * {@inheritDoc} + */ + public override Vala.Iterator iterator () { + return new Iterator (_head); + } + + /** + * {@inheritDoc} + */ + public override ListIterator list_iterator () { + return new Iterator (_head); + } + + /** + * {@inheritDoc} + */ + public override G? get (int index) { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (index >= 0); + for (var iterator = iterator (); iterator.next ();) + if (index-- == 0) + return iterator.get (); + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public override void set (int index, G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (index >= 0); + for (var iterator = list_iterator (); iterator.next ();) { + if (index-- == 0) { + iterator.set (item); + return; + } + } + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public override int index_of (G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + int index = 0; + for (var iterator = list_iterator (); iterator.next (); index++) + if (equal_func (item, iterator.get ())) + return index; + return -1; + } + + /** + * {@inheritDoc} + */ + public override void insert (int index, G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (index >= 0); + if (index == 0) { + var prev = _head; + var next = _head.get_next (); + Node new_node = new Node (item); + new_node.insert (prev, next); + } else { + for (var iterator = list_iterator (); iterator.next ();) { + if (--index == 0) { + iterator.add (item); + return; + } + } + assert_not_reached (); + } + } + + /** + * {@inheritDoc} + */ + public override G remove_at (int index) { + HazardPointer.Context ctx = new HazardPointer.Context (); + for (var iterator = list_iterator (); iterator.next ();) { + if (index-- == 0) { + G data = iterator.get (); + iterator.remove (); + return data; + } + } + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public override List? slice (int start, int end) { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (0 <= start); + assert (start <= end); + var list = new ConcurrentList (equal_func); + var iterator = iterator (); + int idx = 0; + for (; iterator.next (); idx++) + if (idx >= start && idx < end) + list.add (iterator.get ()); + else if (idx >= end) + break; + assert (idx >= end); + return list; + } + + private inline Node update_tail () { + Node tail = HazardPointer.get_pointer (&_tail); + Node.backtrace (ref tail); + Node.search_for (null, ref tail); + HazardPointer.set_pointer> (&_tail, tail); + return tail; + } + + private inline Node get_tail () { + return update_tail (); + } + + private Node _head; + private Node *_tail; + + private class Iterator : Object, Vala.Traversable, Vala.Iterator, ListIterator { + public Iterator (Node head) { + _started = false; + _removed = false; + _index = -1; + _prev = null; + _curr = head; + } + + public bool next () { + HazardPointer.Context ctx = new HazardPointer.Context (); + Node? _old_prev = _removed ? _prev : null; + bool success = Node.proceed (ref _prev, ref _curr); + if (success) { + if (_removed) + _prev = _old_prev; + _removed = false; + _started = true; + _index++; + } + return success; + } + + public bool has_next () { + HazardPointer.Context ctx = new HazardPointer.Context (); + Node? prev = _prev; + Node? curr = _curr; + return Node.proceed (ref prev, ref curr); + } + + public new G get () { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (valid); + return HazardPointer.get_pointer (&_curr._data); + } + + public new void set (G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (valid); +#if DEBUG + G item_copy = item; + stderr.printf (" Setting data %p to %p\n", _curr, item_copy); + HazardPointer.set_pointer (&_curr._data, (owned)item_copy); +#else + HazardPointer.set_pointer (&_curr._data, item); +#endif + } + + public void remove () { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (valid); + _curr.remove (_prev); + _removed = true; + _index--; + } + + public bool valid { + get { return _started && !_removed && _curr != null; } + } + + public bool read_only { get { return false; } } + + public int index() { + assert (valid); + return _index; + } + + public void add (G item) { + HazardPointer.Context ctx = new HazardPointer.Context (); + assert (valid); + if (!Node.proceed (ref _prev, ref _curr)) { + _prev = (owned)_curr; + _curr = null; + } + Node new_node = new Node (item); + new_node.insert (_prev, _curr); + _curr = (owned)new_node; + _index++; + } + + public new bool foreach (ForallFunc f) { + HazardPointer.Context ctx = new HazardPointer.Context (); + if (_started && !_removed) { + if (!f (HazardPointer.get_pointer (&_curr._data))) { + return false; + } + } + Node? _old_prev = _removed ? _prev : null; + while (Node.proceed (ref _prev, ref _curr)) { + if (_removed) + _prev = _old_prev; + _removed = false; + _started = true; + _index++; + if (!f (HazardPointer.get_pointer (&_curr._data))) { + return false; + } + } + return true; + } + + private bool _started; + private bool _removed; + private int _index; + private Node _prev; + private Node? _curr; + } + + private class Node { + public inline Node (G data) { + AtomicPointer.set (&_succ, null); + AtomicPointer.set (&_backlink, null); + G data_copy = data; + G *data_ptr = (owned)data_copy; +#if DEBUG + stderr.printf (" Creating node %p with data %p\n", this, data_ptr); +#endif + AtomicPointer.set (&_data, (owned)data_ptr); + } + + public inline Node.head () { + AtomicPointer.set (&_succ, null); + AtomicPointer.set (&_backlink, null); + AtomicPointer.set (&_data, null); +#if DEBUG + stderr.printf (" Creating head node %p\n", this); +#endif + } + + inline ~Node () { + HazardPointer.set_pointer?> (&_succ, null, 3); + HazardPointer.set_pointer?> (&_backlink, null); +#if DEBUG + HazardPointer? old_data = HazardPointer.exchange_hazard_pointer (&_data, null); + stderr.printf (" Freeing node %p (with data %p)\n", this, old_data != null ? old_data.get() : null); + if (old_data != null) { + old_data.release (HazardPointer.get_destroy_notify ()); + } +#else + HazardPointer.set_pointer (&_data, null); +#endif + } + + public static inline bool proceed (ref Node? prev, ref Node curr, bool force = false) { + Node? next = curr.get_next (); + while (next != null) { + State next_state = next.get_state (); + State curr_state; + Node curr_next = curr.get_succ (out curr_state); + if (next_state != State.MARKED || (curr_state == State.MARKED && curr_next == next)) + break; + if (curr_next == next) + next.help_marked (curr); + next = curr_next; + } + bool success = next != null; + if (success || force) { + prev = (owned)curr; + curr = (owned)next; +#if DEBUG + stderr.printf (" Procceed to %p (previous %p)\n", curr, prev); +#endif + } + return success; + } + + public static inline bool search_for (Node? goal, ref Node? prev) { + Node? curr = prev.get_next (); + while ((curr != goal || curr != null) && proceed (ref prev, ref curr, true)); + return curr == goal; + } + + public inline bool remove (Node prev_node) { +#if DEBUG + stderr.printf (" Removing %p (previous %p)\n", this, prev_node); +#endif + Node? prev = prev_node; + bool result = try_flag (ref prev); + if (prev != null) + help_flagged (prev); + return result; + } + + public inline void insert (owned Node prev, Node? next) { +#if DEBUG + stderr.printf (" Inserting %p between %p and %p\n", this, prev, next); +#endif + while (true) { + State prev_state; + Node? prev_next = get_succ (out prev_state); + if (prev_state == State.FLAGGED) { + prev_next.help_flagged (prev); + } else { + set_succ (next, State.NONE); + bool result = prev.compare_and_exchange (next, State.NONE, this, State.NONE); + if (result) + return; + prev_next = get_succ (out prev_state); + if (prev_state == State.FLAGGED) + prev_next.help_flagged (prev); + backtrace (ref prev); + } + search_for (next, ref prev); + } + + } + + public inline void help_flagged (Node prev) { +#if DEBUG + stderr.printf (" Help flagging %p (previous %p)\n", this, prev); +#endif + set_backlink (prev); + if (get_state () != State.MARKED) + try_mark (); + help_marked (prev); + } + + public inline void try_mark () { +#if DEBUG + stderr.printf (" Try flagging %p\n", this); +#endif + do { + Node? next_node = get_next (); + bool result = compare_and_exchange (next_node, State.NONE, next_node, State.MARKED); + if (!result) { + State state; + next_node = get_succ (out state); + if (state == State.FLAGGED) + help_flagged (next_node); + } + } while (get_state () != State.MARKED); + } + + public inline void help_marked (Node prev_node) { +#if DEBUG + stderr.printf (" Help marking %p (previous %p)\n", this, prev_node); +#endif + prev_node.compare_and_exchange (this, State.FLAGGED, get_next (), State.NONE); + } + + public inline bool try_flag (ref Node? prev_node) { +#if DEBUG + stderr.printf (" Try flagging %p (previous %p)\n", this, prev_node); +#endif + while (true) { + if (prev_node.compare_succ (this, State.FLAGGED)) + return false; + bool result = prev_node.compare_and_exchange (this, State.NONE, this, State.FLAGGED); + if (result) + return true; + State result_state; + Node? result_node = prev_node.get_succ (out result_state); + if (result_node == this && result_state == State.FLAGGED) + return false; + backtrace (ref prev_node); + if (!search_for (this, ref prev_node)) { + prev_node = null; + return false; + } + } + } + + public static inline void backtrace (ref Node? curr) { + while (curr.get_state () == State.MARKED) + curr = curr.get_backlink (); + } + + public inline bool compare_and_exchange (Node? old_node, State old_state, Node? new_node, State new_state) { +#if DEBUG + bool b = HazardPointer.compare_and_exchange_pointer (&_succ, old_node, new_node, 3, (size_t)old_state, (size_t)new_state); + stderr.printf (" Setting %p.succ to (%p, %s) if %p.succ is (%p, %s): %s\n", this, new_node, new_state.to_string (), this, old_node, old_state.to_string (), b ? "success" : "failure"); + return b; +#else + return HazardPointer.compare_and_exchange_pointer> (&_succ, old_node, new_node, 3, (size_t)old_state, (size_t)new_state); +#endif + } + + public inline bool compare_succ (Node? next, State state) { + size_t cur = (size_t)AtomicPointer.get (&_succ); + return cur == ((size_t)next | (size_t)state); + } + + public inline Node? get_next () { + return get_succ (null); + } + + public inline State get_state () { + return (State)((size_t)AtomicPointer.get (&_succ) & 3); + } + + public inline Node? get_succ (out State state) { + size_t rstate; + Node? succ = HazardPointer.get_pointer> (&_succ, 3, out rstate); + state = (State)rstate; + return (owned)succ; + } + + public inline void set_succ (Node? next, State state) { +#if DEBUG + stderr.printf (" Setting %p.succ to (%p, %s)\n", this, next, state.to_string ()); +#endif + HazardPointer.set_pointer> (&_succ, next, 3, (size_t)state); + } + + public inline Node? get_backlink () { + return HazardPointer.get_pointer> (&_backlink); + } + + public inline void set_backlink (Node? backlink) { +#if DEBUG + stderr.printf (" Setting backlink from %p to %p\n", this, backlink); +#endif + HazardPointer.compare_and_exchange_pointer?> (&_backlink, null, backlink); + } + + public Node *_succ; + public Node *_backlink; + public G *_data; + } + + private enum State { + NONE = 0, + MARKED = 1, + FLAGGED = 2 + } +} diff --git a/gee/deque.vala b/gee/deque.vala new file mode 100644 index 000000000..f2ae3e137 --- /dev/null +++ b/gee/deque.vala @@ -0,0 +1,123 @@ +/* deque.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * A double-ended queue. + * + * A deque can be used either as a queue (First-In-First-Out behavior) or as a + * stack (Last-In-First-Out behavior). + * + * The methods defined by this interface behaves exactely in the same way as + * the {@link Queue} methods with respect to capacity bounds. + * + * The Deque interface inherits from the {@link Queue} interface. Thus, to use + * a deque as a queue, you can equivalently use the folowing method set: + * + * ||<)(> ''Queue method'' ||<)(> ''Deque method'' || + * || {@link Queue.offer} || {@link offer_tail} || + * || {@link Queue.peek} || {@link peek_head} || + * || {@link Queue.poll} || {@link poll_head} || + * || {@link Queue.drain} || {@link drain_head} || + * + * To use a deque as a stack, just use the method set that acts at the head of + * the deque: + * + * ||<)(> ''Operation'' ||<)(> ''Deque method'' || + * || push an element || {@link offer_head} || + * || peek an element || {@link peek_head} || + * || pop an element || {@link poll_head} || + */ +[GenericAccessors] +public interface Vala.Deque : Queue { + /** + * Offers the specified element to the head of this deque. + * + * @param element the element to offer to the queue + * + * @return ``true`` if the element was added to the queue + */ + public abstract bool offer_head (G element); + + /** + * Peeks (retrieves, but not remove) an element from this queue. + * + * @return the element peeked from the queue (or ``null`` if none was + * available) + */ + public abstract G? peek_head (); + + /** + * Polls (retrieves and remove) an element from the head of this queue. + * + * @return the element polled from the queue (or ``null`` if none was + * available) + */ + public abstract G? poll_head (); + + /** + * Drains the specified amount of elements from the head of this queue in + * the specified recipient collection. + * + * @param recipient the recipient collection to drain the elements to + * @param amount the amount of elements to drain + * + * @return the amount of elements that were actually drained + */ + public abstract int drain_head (Collection recipient, int amount = -1); + + /** + * Offers the specified element to the tail of this deque + * + * @param element the element to offer to the queue + * + * @return ``true`` if the element was added to the queue + */ + public abstract bool offer_tail (G element); + + /** + * Peeks (retrieves, but not remove) an element from the tail of this + * queue. + * + * @return the element peeked from the queue (or ``null`` if none was + * available) + */ + public abstract G? peek_tail (); + + /** + * Polls (retrieves and remove) an element from the tail of this queue. + * + * @return the element polled from the queue (or ``null`` if none was + * available) + */ + public abstract G? poll_tail (); + + /** + * Drains the specified amount of elements from the tail of this queue in + * the specified recipient collection. + * + * @param recipient the recipient collection to drain the elements to + * @param amount the amount of elements to drain + * + * @return the amount of elements that were actually drained + */ + public abstract int drain_tail (Collection recipient, int amount = -1); +} diff --git a/gee/functions.vala b/gee/functions.vala new file mode 100644 index 000000000..a805bc874 --- /dev/null +++ b/gee/functions.vala @@ -0,0 +1,152 @@ +/* functions.vala + * + * Copyright (C) 2009 Didier Villevalois, Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes' Villevalois + * Maciej Piechotka + */ + +using GLib; + +namespace Vala { + + /** + * Helpers for equal, hash and compare functions. + * + * With those functions, you can retrieve the equal, hash and compare + * functions that best match your element, key or value types. Supported + * types are (non-boxed) primitive, string and ``Object`` types. + * + * A special care is taken for classes inheriting from the + * {@link Comparable} interface. For such types, an appropriate compare + * function is returned that calls {@link Comparable.compare_to}. + * + */ + namespace Functions { + + /** + * Get a equality testing function for a given type. + * + * @param t the type which to get an equality testing function for. + * + * @return the equality testing function corresponding to the given type. + */ + public static EqualDataFunc get_equal_func_for (Type t) { + if (t == typeof (string)) { + return (a, b) => { + if (a == b) + return true; + else if (a == null || b == null) + return false; + else + return str_equal ((string) a, (string) b); + }; + } else if (t.is_a (typeof (Hashable))) { + return (a, b) => { + if (a == b) + return true; + else if (a == null || b == null) + return false; + else + return ((Hashable) a).equal_to ((Hashable) b); + }; + } else if (t.is_a (typeof (Comparable))) { + return (a, b) => { + if (a == b) + return true; + else if (a == null || b == null) + return false; + else + return ((Comparable) a).compare_to ((Comparable) b) == 0;}; + } else { + return (a, b) => {return direct_equal (a, b);}; + } + } + + /** + * Get a hash function for a given type. + * + * @param t the type which to get the hash function for. + * + * @return the hash function corresponding to the given type. + */ + public static HashDataFunc get_hash_func_for (Type t) { + if (t == typeof (string)) { + return (a) => { + if (a == null) + return (uint)0xdeadbeef; + else + return str_hash ((string) a); + }; + } else if (t.is_a (typeof (Hashable))) { + return (a) => { + if (a == null) + return (uint)0xdeadbeef; + else + return ((Hashable) a).hash(); + }; + } else { + return (a) => {return direct_hash (a);}; + } + } + + /** + * Get a comparator function for a given type. + * + * @param t the type which to get a comparator function for. + * + * @return the comparator function corresponding to the given type. + */ + public static CompareDataFunc get_compare_func_for (Type t) { + if (t == typeof (string)) { + return (a, b) => { + if (a == b) + return 0; + else if (a == null) + return -1; + else if (b == null) + return 1; + else + return strcmp((string) a, (string) b); + }; + } else if (t.is_a (typeof (Comparable))) { + return (a, b) => { + if (a == b) + return 0; + else if (a == null) + return -1; + else if (b == null) + return 1; + else + return ((Comparable) a).compare_to ((Comparable) b); + }; + } else { + return (_val1, _val2) => { + long val1 = (long)_val1, val2 = (long)_val2; + if (val1 > val2) { + return 1; + } else if (val1 == val2) { + return 0; + } else { + return -1; + } + }; + } + } + } +} diff --git a/gee/hashable.vala b/gee/hashable.vala new file mode 100644 index 000000000..9b806f166 --- /dev/null +++ b/gee/hashable.vala @@ -0,0 +1,44 @@ +/* hashable.vala + * + * Copyright (C) 2010 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * This interface defines a hash function amongs instances of each class + * implementing it. + * + * @see Comparable + */ +public interface Vala.Hashable : Object { + /** + * Computes hash for an objects. Two hashes of equal objects have to be + * equal. Hash have to not change during lifetime of object. + * + * @return hash of an object + */ + public abstract uint hash (); + + /** + * Compares this object with the specifed object. + * + * @return true if objects are equal + */ + public abstract bool equal_to (G object); +} diff --git a/gee/hashmap.vala b/gee/hashmap.vala index 9085f10e4..158e787c0 100644 --- a/gee/hashmap.vala +++ b/gee/hashmap.vala @@ -25,73 +25,162 @@ using GLib; /** - * Hashtable implementation of the Map interface. + * Hash table implementation of the {@link Map} interface. + * + * This implementation is better fit for highly heterogenous key values. + * In case of high key hashes redundancy or higher amount of data prefer using + * tree implementation like {@link TreeMap}. + * + * @see TreeMap */ -public class Vala.HashMap : Map { +public class Vala.HashMap : Vala.AbstractMap { + /** + * {@inheritDoc} + */ public override int size { get { return _nnodes; } } - public HashFunc key_hash_func { - set { _key_hash_func = value; } + /** + * {@inheritDoc} + */ + public override bool read_only { + get { return false; } } - public EqualFunc key_equal_func { - set { _key_equal_func = value; } + /** + * {@inheritDoc} + */ + public override Set keys { + owned get { + Set keys = _keys; + if (_keys == null) { + keys = new KeySet (this); + _keys = keys; + keys.add_weak_pointer ((void**) (&_keys)); + } + return keys; + } } - public EqualFunc value_equal_func { - set { _value_equal_func = value; } + /** + * {@inheritDoc} + */ + public override Collection values { + owned get { + Collection values = _values; + if (_values == null) { + values = new ValueCollection (this); + _values = values; + values.add_weak_pointer ((void**) (&_values)); + } + return values; + } } + /** + * {@inheritDoc} + */ + public override Set> entries { + owned get { + Set> entries = _entries; + if (_entries == null) { + entries = new EntrySet (this); + _entries = entries; + entries.add_weak_pointer ((void**) (&_entries)); + } + return entries; + } + } + + /** + * The keys' hash function. + */ + [CCode (notify = false)] + public HashDataFunc key_hash_func { private set; get; } + + /** + * The keys' equality testing function. + */ + [CCode (notify = false)] + public EqualDataFunc key_equal_func { private set; get; } + + /** + * The values' equality testing function. + */ + [CCode (notify = false)] + public EqualDataFunc value_equal_func { private set; get; } + private int _array_size; private int _nnodes; private Node[] _nodes; + private weak Set _keys; + private weak Collection _values; + private weak Set> _entries; + // concurrent modification protection private int _stamp = 0; - private HashFunc _key_hash_func; - private EqualFunc _key_equal_func; - private EqualFunc _value_equal_func; - private const int MIN_SIZE = 11; private const int MAX_SIZE = 13845163; - public HashMap (HashFunc key_hash_func = GLib.direct_hash, EqualFunc key_equal_func = GLib.direct_equal, EqualFunc value_equal_func = GLib.direct_equal) { + /** + * Constructs a new, empty hash map. + * + * If not provided, the functions parameters are requested to the + * {@link Functions} function factory methods. + * + * @param key_hash_func an optional key hash function + * @param key_equal_func an optional key equality testing function + * @param value_equal_func an optional value equality testing function + */ + public HashMap (owned HashDataFunc? key_hash_func = null, owned EqualDataFunc? key_equal_func = null, owned EqualDataFunc? value_equal_func = null) { + if (key_hash_func == null) { + key_hash_func = Functions.get_hash_func_for (typeof (K)); + } + if (key_equal_func == null) { + key_equal_func = Functions.get_equal_func_for (typeof (K)); + } + if (value_equal_func == null) { + value_equal_func = Functions.get_equal_func_for (typeof (V)); + } this.key_hash_func = key_hash_func; this.key_equal_func = key_equal_func; this.value_equal_func = value_equal_func; + _array_size = MIN_SIZE; _nodes = new Node[_array_size]; } - public override Set get_keys () { - return new KeySet (this); - } - - public override Collection get_values () { - return new ValueCollection (this); - } - - public override Vala.MapIterator map_iterator () { - return new MapIterator (this); - } - private Node** lookup_node (K key) { - uint hash_value = _key_hash_func (key); + uint hash_value = key_hash_func (key); Node** node = &_nodes[hash_value % _array_size]; - while ((*node) != null && (hash_value != (*node)->key_hash || !_key_equal_func ((*node)->key, key))) { + while ((*node) != null && (hash_value != (*node)->key_hash || !key_equal_func ((*node)->key, key))) { node = &((*node)->next); } return node; } - public override bool contains (K key) { + /** + * {@inheritDoc} + */ + public override bool has_key (K key) { Node** node = lookup_node (key); return (*node != null); } + /** + * {@inheritDoc} + */ + public override bool has (K key, V value) { + Node** node = lookup_node (key); + return (*node != null && value_equal_func ((*node)->value, value)); + } + + /** + * {@inheritDoc} + */ public override V? get (K key) { Node* node = (*lookup_node (key)); if (node != null) { @@ -101,12 +190,15 @@ public class Vala.HashMap : Map { } } + /** + * {@inheritDoc} + */ public override void set (K key, V value) { Node** node = lookup_node (key); if (*node != null) { (*node)->value = value; } else { - uint hash_value = _key_hash_func (key); + uint hash_value = key_hash_func (key); *node = new Node (key, value, hash_value); _nnodes++; resize (); @@ -114,25 +206,20 @@ public class Vala.HashMap : Map { _stamp++; } - public override bool remove (K key) { - Node** node = lookup_node (key); - if (*node != null) { - Node next = (owned) (*node)->next; - - (*node)->key = null; - (*node)->value = null; - delete *node; - - *node = (owned) next; - - _nnodes--; - resize (); - _stamp++; - return true; + /** + * {@inheritDoc} + */ + public override bool unset (K key, out V? value = null) { + bool b = unset_helper (key, out value); + if(b) { + resize(); } - return false; + return b; } + /** + * {@inheritDoc} + */ public override void clear () { for (int i = 0; i < _array_size; i++) { Node node = (owned) _nodes[i]; @@ -147,7 +234,36 @@ public class Vala.HashMap : Map { resize (); } - private void resize () { + /** + * {@inheritDoc} + */ + public override Vala.MapIterator map_iterator () { + return new MapIterator (this); + } + + private inline bool unset_helper (K key, out V? value = null) { + Node** node = lookup_node (key); + if (*node != null) { + Node next = (owned) (*node)->next; + + value = (owned) (*node)->value; + + (*node)->key = null; + (*node)->value = null; + delete *node; + + *node = (owned) next; + + _nnodes--; + _stamp++; + return true; + } else { + value = null; + } + return false; + } + + private inline void resize () { if ((_array_size >= 3 * _nnodes && _array_size >= MIN_SIZE) || (3 * _array_size <= _nnodes && _array_size < MAX_SIZE)) { int new_array_size = (int) SpacedPrimes.closest (_nnodes); @@ -180,27 +296,48 @@ public class Vala.HashMap : Map { public V value; public Node next; public uint key_hash; + public unowned Map.Entry? entry; public Node (owned K k, owned V v, uint hash) { key = (owned) k; value = (owned) v; key_hash = hash; + entry = null; } } - private class KeySet : Set { - public HashMap map { - set { _map = value; } + private class Entry : Map.Entry { + private unowned Node _node; + + public static Map.Entry entry_for (Node node) { + Map.Entry result = node.entry; + if (node.entry == null) { + result = new Entry (node); + node.entry = result; + result.add_weak_pointer ((void**) (&node.entry)); + } + return result; } - private HashMap _map; + public Entry (Node node) { + _node = node; + } - public KeySet (HashMap map) { - this.map = map; + public override K key { get { return _node.key; } } + + public override V value { + get { return _node.value; } + set { _node.value = value; } } - public override Type get_element_type () { - return typeof (K); + public override bool read_only { get { return false; } } + } + + private class KeySet : AbstractSet { + private HashMap _map; + + public KeySet (HashMap map) { + _map = map; } public override Iterator iterator () { @@ -211,6 +348,10 @@ public class Vala.HashMap : Map { get { return _map.size; } } + public override bool read_only { + get { return true; } + } + public override bool add (K key) { assert_not_reached (); } @@ -224,170 +365,343 @@ public class Vala.HashMap : Map { } public override bool contains (K key) { - return _map.contains (key); + return _map.has_key (key); + } + + public bool add_all (Collection collection) { + assert_not_reached (); + } + + public bool remove_all (Collection collection) { + assert_not_reached (); } + + public bool retain_all (Collection collection) { + assert_not_reached (); + } + } - private class MapIterator : Vala.MapIterator { - public HashMap map { - set { - _map = value; - _stamp = _map._stamp; + private class ValueCollection : AbstractCollection { + private HashMap _map; + + public ValueCollection (HashMap map) { + _map = map; + } + + public override Iterator iterator () { + return new ValueIterator (_map); + } + + public override int size { + get { return _map.size; } + } + + public override bool read_only { + get { return true; } + } + + public override bool add (V value) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (V value) { + assert_not_reached (); + } + + public override bool contains (V value) { + Iterator it = iterator (); + while (it.next ()) { + if (_map.value_equal_func (it.get (), value)) { + return true; + } } + return false; + } + + public bool add_all (Collection collection) { + assert_not_reached (); + } + + public bool remove_all (Collection collection) { + assert_not_reached (); + } + + public bool retain_all (Collection collection) { + assert_not_reached (); } + } + private class EntrySet : AbstractSet> { private HashMap _map; - private int _index = -1; - private weak Node _node; + + public EntrySet (HashMap map) { + _map = map; + } + + public override Iterator> iterator () { + return new EntryIterator (_map); + } + + public override int size { + get { return _map.size; } + } + + public override bool read_only { + get { return true; } + } + + public override bool add (Map.Entry entry) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (Map.Entry entry) { + assert_not_reached (); + } + + public override bool contains (Map.Entry entry) { + return _map.has (entry.key, entry.value); + } + + public bool add_all (Collection> entries) { + assert_not_reached (); + } + + public bool remove_all (Collection> entries) { + assert_not_reached (); + } + + public bool retain_all (Collection> entries) { + assert_not_reached (); + } + } + + private abstract class NodeIterator : Object { + protected HashMap _map; + protected int _index = -1; + protected weak Node _node; + protected weak Node _next; // concurrent modification protection - private int _stamp; + protected int _stamp; - public MapIterator (HashMap map) { - this.map = map; + public NodeIterator (HashMap map) { + _map = map; + _stamp = _map._stamp; } - public override bool next () { - if (_node != null) { - _node = _node.next; - } - while (_node == null && _index + 1 < _map._array_size) { - _index++; - _node = _map._nodes[_index]; + public bool next () { + assert (_stamp == _map._stamp); + if (!has_next ()) { + return false; } + _node = _next; + _next = null; return (_node != null); } - public override K? get_key () { + public bool has_next () { assert (_stamp == _map._stamp); - assert (_node != null); - return _node.key; + if (_next == null) { + _next = _node; + if (_next != null) { + _next = _next.next; + } + while (_next == null && _index + 1 < _map._array_size) { + _index++; + _next = _map._nodes[_index]; + } + } + return (_next != null); } - public override V? get_value () { - assert (_stamp == _map._stamp); - assert (_node != null); - return _node.value; + public virtual bool read_only { + get { + return true; + } } - } - private class KeyIterator : Iterator { - public HashMap map { - set { - _map = value; - _stamp = _map._stamp; + public bool valid { + get { + return _node != null; } } + } - private HashMap _map; - private int _index = -1; - private weak Node _node; + private class KeyIterator : NodeIterator, Traversable, Iterator { + public KeyIterator (HashMap map) { + base (map); + } - // concurrent modification protection - private int _stamp; + public new K get () { + assert (_stamp == _map._stamp); + assert (_node != null); + return _node.key; + } - public KeyIterator (HashMap map) { - this.map = map; + public void remove () { + assert_not_reached (); } - public override bool next () { + public bool foreach(ForallFunc f) { if (_node != null) { - _node = _node.next; - } - while (_node == null && _index + 1 < _map._array_size) { - _index++; - _node = _map._nodes[_index]; + if (!f(_node.key)) { + return false; + } + if(_next == null) { + _next = _node.next; + } } - return (_node != null); + do { + while(_next != null) { + _node = _next; + if (!f(_node.key)) { + return false; + } + _next = _next.next; + } + if (_index + 1 < _map._array_size) { + _next = _map._nodes[++_index]; + } else { + return true; + } + } while(true); + } + } + + private class MapIterator : NodeIterator, Vala.MapIterator { + public MapIterator (HashMap map) { + base (map); } - public override K? get () { + public new K get_key () { assert (_stamp == _map._stamp); assert (_node != null); return _node.key; } - } - private class ValueCollection : Collection { - public HashMap map { - set { _map = value; } + public void unset () { + assert (_stamp == _map._stamp); + assert (_node != null); + has_next (); + _map.unset_helper (_node.key); + _node = null; + _stamp = _map._stamp; } - private HashMap _map; - - public ValueCollection (HashMap map) { - this.map = map; + public V get_value () { + assert (_stamp == _map._stamp); + assert (_node != null); + return _node.value; } - public override Type get_element_type () { - return typeof (V); + public void set_value (V value) { + assert (_stamp == _map._stamp); + assert (_node != null); + _map.set (_node.key, value); + _stamp = _map._stamp; } - public override Iterator iterator () { - return new ValueIterator (_map); + public bool mutable { + get { + return true; + } } - public override int size { - get { return _map.size; } + public override bool read_only { + get { + return false; + } } + } - public override bool add (V value) { - assert_not_reached (); + private class ValueIterator : NodeIterator, Traversable, Iterator { + public ValueIterator (HashMap map) { + base (map); } - public override void clear () { - assert_not_reached (); + public new V get () { + assert (_stamp == _map._stamp); + assert (_node != null); + return _node.value; } - public override bool remove (V value) { + public void remove () { assert_not_reached (); } - public override bool contains (V value) { - Iterator it = iterator (); - while (it.next ()) { - if (_map._value_equal_func (it.get (), value)) { - return true; + public bool foreach(ForallFunc f) { + if (_node != null) { + if (!f(_node.value)) { + return false; + } + if(_next == null) { + _next = _node.next; } } - return false; + do { + while(_next != null) { + _node = _next; + if (!f(_node.value)) { + return false; + } + _next = _next.next; + } + if (_index + 1 < _map._array_size) { + _next = _map._nodes[++_index]; + } else { + return true; + } + } while(true); } } - private class ValueIterator : Iterator { - public HashMap map { - set { - _map = value; - _stamp = _map._stamp; - } + private class EntryIterator : NodeIterator, Traversable>, Iterator> { + public EntryIterator (HashMap map) { + base (map); } - private HashMap _map; - private int _index = -1; - private weak Node _node; - - // concurrent modification protection - private int _stamp; + public new Map.Entry get () { + assert (_stamp == _map._stamp); + assert (_node != null); + return Entry.entry_for (_node); + } - public ValueIterator (HashMap map) { - this.map = map; + public void remove () { + assert_not_reached (); } - public override bool next () { + public bool foreach(ForallFunc> f) { if (_node != null) { - _node = _node.next; - } - while (_node == null && _index + 1 < _map._array_size) { - _index++; - _node = _map._nodes[_index]; + if (!f(Entry.entry_for (_node))) { + return false; + } + if(_next == null) { + _next = _node.next; + } } - return (_node != null); - } - - public override V? get () { - assert (_stamp == _map._stamp); - assert (_node != null); - return _node.value; + do { + while(_next != null) { + _node = _next; + if (!f(Entry.entry_for (_node))) { + return false; + } + _next = _next.next; + } + if (_index + 1 < _map._array_size) { + _next = _map._nodes[++_index]; + } else { + return true; + } + } while(true); } } } diff --git a/gee/hashmultimap.vala b/gee/hashmultimap.vala new file mode 100644 index 000000000..ca57fb8ad --- /dev/null +++ b/gee/hashmultimap.vala @@ -0,0 +1,76 @@ +/* hashmultimap.vala + * + * Copyright (C) 2009 Ali Sabil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Ali Sabil + */ + +/** + * Hash table implementation of the {@link MultiMap} interface. + */ +public class Vala.HashMultiMap : AbstractMultiMap { + public HashDataFunc key_hash_func { + get { return ((HashMap>) _storage_map).key_hash_func; } + } + + public EqualDataFunc key_equal_func { + get { return ((HashMap>) _storage_map).key_equal_func; } + } + + [CCode (notify = false)] + public HashDataFunc value_hash_func { private set; get; } + + [CCode (notify = false)] + public EqualDataFunc value_equal_func { private set; get; } + + /** + * Constructs a new, empty hash multimap. + * + * If not provided, the functions parameters are requested to the + * {@link Functions} function factory methods. + * + * @param key_hash_func an optional key hash function + * @param key_equal_func an optional key equality testing function + * @param value_hash_func an optional value hash function + * @param value_equal_func an optional value equality testing function + */ + public HashMultiMap (owned HashDataFunc? key_hash_func = null, owned EqualDataFunc? key_equal_func = null, + owned HashDataFunc? value_hash_func = null, owned EqualDataFunc? value_equal_func = null) { + base (new HashMap> (key_hash_func, key_equal_func, Functions.get_equal_func_for (typeof (Set)))); + if (value_hash_func == null) { + value_hash_func = Functions.get_hash_func_for (typeof (V)); + } + if (value_equal_func == null) { + value_equal_func = Functions.get_equal_func_for (typeof (V)); + } + this.value_hash_func = value_hash_func; + this.value_equal_func = value_equal_func; + } + + protected override Collection create_value_storage () { + return new HashSet (_value_hash_func, _value_equal_func); + } + + protected override MultiSet create_multi_key_set () { + return new HashMultiSet (key_hash_func, key_equal_func); + } + + protected override EqualDataFunc get_value_equal_func () { + return _value_equal_func; + } +} diff --git a/gee/hashmultiset.vala b/gee/hashmultiset.vala new file mode 100644 index 000000000..78b850027 --- /dev/null +++ b/gee/hashmultiset.vala @@ -0,0 +1,47 @@ +/* hashmultiset.vala + * + * Copyright (C) 2009 Ali Sabil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Ali Sabil + */ + +/** + * Hash table implementation of the {@link MultiSet} interface. + */ +public class Vala.HashMultiSet : AbstractMultiSet { + public HashDataFunc hash_func { + get { return ((HashMap) _storage_map).key_hash_func; } + } + + public EqualDataFunc equal_func { + get { return ((HashMap) _storage_map).key_equal_func; } + } + + /** + * Constructs a new, empty hash multi set. + * + * If not provided, the functions parameters are requested to the + * {@link Functions} function factory methods. + * + * @param hash_func an optional element hash function + * @param equal_func an optional element equality testing function + */ + public HashMultiSet (HashDataFunc? hash_func = null, EqualDataFunc? equal_func = null) { + base (new HashMap (hash_func, equal_func)); + } +} diff --git a/gee/hashset.vala b/gee/hashset.vala index 6ca0dfa22..e4821d78e 100644 --- a/gee/hashset.vala +++ b/gee/hashset.vala @@ -24,21 +24,46 @@ using GLib; +namespace Vala { + public delegate uint HashDataFunc (T v); + public delegate bool EqualDataFunc (T a, T b); +} + /** - * Hashtable implementation of the Set interface. + * Hash table implementation of the {@link Set} interface. + * + * This implementation is better fit for highly heterogenous values. + * In case of high value hashes redundancy or higher amount of data prefer using + * tree implementation like {@link TreeSet}. + * + * @see TreeSet */ -public class Vala.HashSet : Set { +public class Vala.HashSet : AbstractSet { + /** + * {@inheritDoc} + */ public override int size { get { return _nnodes; } } - public HashFunc hash_func { - set { _hash_func = value; } + /** + * {@inheritDoc} + */ + public override bool read_only { + get { return false; } } - public EqualFunc equal_func { - set { _equal_func = value; } - } + /** + * The elements' hash function. + */ + [CCode (notify = false)] + public HashDataFunc hash_func { private set; get; } + + /** + * The elements' equality testing function. + */ + [CCode (notify = false)] + public EqualDataFunc equal_func { private set; get; } private int _array_size; private int _nnodes; @@ -47,13 +72,25 @@ public class Vala.HashSet : Set { // concurrent modification protection private int _stamp = 0; - private HashFunc _hash_func; - private EqualFunc _equal_func; - private const int MIN_SIZE = 11; private const int MAX_SIZE = 13845163; - public HashSet (HashFunc hash_func = GLib.direct_hash, EqualFunc equal_func = GLib.direct_equal) { + /** + * Constructs a new, empty hash set. + * + * If not provided, the functions parameters are requested to the + * {@link Functions} function factory methods. + * + * @param hash_func an optional hash function + * @param equal_func an optional equality testing function + */ + public HashSet (owned HashDataFunc? hash_func = null, owned EqualDataFunc? equal_func = null) { + if (hash_func == null) { + hash_func = Functions.get_hash_func_for (typeof (G)); + } + if (equal_func == null) { + equal_func = Functions.get_equal_func_for (typeof (G)); + } this.hash_func = hash_func; this.equal_func = equal_func; _array_size = MIN_SIZE; @@ -61,33 +98,38 @@ public class Vala.HashSet : Set { } private Node** lookup_node (G key) { - uint hash_value = _hash_func (key); + uint hash_value = hash_func (key); Node** node = &_nodes[hash_value % _array_size]; - while ((*node) != null && (hash_value != (*node)->key_hash || !_equal_func ((*node)->key, key))) { + while ((*node) != null && (hash_value != (*node)->key_hash || !equal_func ((*node)->key, key))) { node = &((*node)->next); } return node; } + /** + * {@inheritDoc} + */ public override bool contains (G key) { Node** node = lookup_node (key); return (*node != null); } - public override Type get_element_type () { - return typeof (G); - } - + /** + * {@inheritDoc} + */ public override Vala.Iterator iterator () { return new Iterator (this); } + /** + * {@inheritDoc} + */ public override bool add (G key) { Node** node = lookup_node (key); if (*node != null) { return false; } else { - uint hash_value = _hash_func (key); + uint hash_value = hash_func (key); *node = new Node (key, hash_value); _nnodes++; resize (); @@ -96,24 +138,20 @@ public class Vala.HashSet : Set { } } + /** + * {@inheritDoc} + */ public override bool remove (G key) { - Node** node = lookup_node (key); - if (*node != null) { - Node next = (owned) (*node)->next; - - (*node)->key = null; - delete *node; - - *node = (owned) next; - - _nnodes--; + bool b = remove_helper(key); + if(b) { resize (); - _stamp++; - return true; } - return false; + return b; } + /** + * {@inheritDoc} + */ public override void clear () { for (int i = 0; i < _array_size; i++) { Node node = (owned) _nodes[i]; @@ -127,6 +165,24 @@ public class Vala.HashSet : Set { resize (); } + private inline bool remove_helper (G key) { + Node** node = lookup_node (key); + if (*node != null) { + assert (*node != null); + Node next = (owned) (*node)->next; + + (*node)->key = null; + delete *node; + + *node = (owned) next; + + _nnodes--; + _stamp++; + return true; + } + return false; + } + private void resize () { if ((_array_size >= 3 * _nnodes && _array_size >= MIN_SIZE) || (3 * _array_size <= _nnodes && _array_size < MAX_SIZE)) { @@ -166,41 +222,110 @@ public class Vala.HashSet : Set { } } - private class Iterator : Vala.Iterator { - public HashSet set { - set { - _set = value; - _stamp = _set._stamp; - } - } - + private class Iterator : Object, Traversable, Vala.Iterator { private HashSet _set; private int _index = -1; private weak Node _node; + private weak Node _next; // concurrent modification protection private int _stamp = 0; public Iterator (HashSet set) { - this.set = set; + _set = set; + _stamp = _set._stamp; } - public override bool next () { - if (_node != null) { - _node = _node.next; - } - while (_node == null && _index + 1 < _set._array_size) { - _index++; - _node = _set._nodes[_index]; + public bool next () { + assert (_stamp == _set._stamp); + if (!has_next ()) { + return false; } + _node = _next; + _next = null; return (_node != null); } - public override G? get () { + public bool has_next () { + assert (_stamp == _set._stamp); + if (_next == null) { + _next = _node; + if (_next != null) { + _next = _next.next; + } + while (_next == null && _index + 1 < _set._array_size) { + _index++; + _next = _set._nodes[_index]; + } + } + return (_next != null); + } + + public new G get () { assert (_stamp == _set._stamp); assert (_node != null); return _node.key; } + + public void remove () { + assert (_stamp == _set._stamp); + assert (_node != null); + has_next (); + _set.remove_helper (_node.key); + _node = null; + _stamp = _set._stamp; + } + + public bool read_only { + get { + return false; + } + } + + public bool valid { + get { + return _node != null; + } + } + + public bool foreach (ForallFunc f) { + assert (_stamp == _set._stamp); + unowned Node? node = _node, next = _next, current = null, prev = null; + if (node != null) { + if (!f (node.key)) { + return false; + } + prev = node; + current = node.next; + } + if (next != null) { + if (!f (next.key)) { + _node = next; + _next = null; + return false; + } + prev = next; + current = next.next; + } + do { + while (current != null) { + if (!f (current.key)) { + _node = current; + _next = null; + return false; + } + prev = current; + current = current.next; + } + while (current == null && _index + 1 < _set._array_size) { + _index++; + current = _set._nodes[_index]; + } + } while (current != null); + _node = prev; + _next = null; + return true; + } } } diff --git a/gee/hazardpointer.vala b/gee/hazardpointer.vala new file mode 100644 index 000000000..a26fbebe2 --- /dev/null +++ b/gee/hazardpointer.vala @@ -0,0 +1,775 @@ +/* hazardpointer.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Hazard pointer is a method of protecting a pointer shared by many threads. + * If you want to use atomic pointer that may be freed you should use following code: + * + * {{{ + * string *shared_pointer = ...; + * HazardPointer hptr = HazardPointer.get_hazard_pointer (&shared_pointer); + * // my_string contains value from shared_pinter. It is valid as long as hptr is alive. + * unowned string my_string = ptr.get (); + * // instead of delete + * ptr.release ((ptr) => {string *sptr = ptr;string ref = (owned)sptr;}); + * }); + * }}} + * + * In some cases you may use helper methods which might involve copying of object (and are unsafe for unowned objects): + * {{{ + * Gtk.Window *window = ...; + * Gtk.Window? local_window = HazardPointer.get_pointer (&window); + * HazardPointer.set_pointer (&window, ...) + * local_window = HazardPointer.exchange_pointer (&window, null); + * HazardPointer.compare_and_exchange (&window, null, local_window); + * }}} + * + * The class also provides helper methods if least significant bits are used for storing flags. + * + * HazardPointers are not thread-safe (unless documentation states otherwise). + */ +[Compact] +public class Vala.HazardPointer { // FIXME: Make it a struct + /** + * Creates a hazard pointer for a pointer. + * + * @param ptr Protected pointer + */ + public HazardPointer (G *ptr) { + this._node = acquire (); + this._node.set ((void *)ptr); + } + + /** + * Create a hazard pointer from Node. + */ + internal HazardPointer.from_node (Node node) { + this._node = node; + } + + /** + * Gets hazard pointer from atomic pointer safely. + * + * @param aptr Atomic pointer. + * @param mask Mask of bits. + * @param mask_out Result of mask. + * @return Hazard pointer containing the element. + */ + public static HazardPointer? get_hazard_pointer (G **aptr, size_t mask = 0, out size_t mask_out = null) { + unowned Node node = acquire (); + void *rptr = null; + void *ptr = null; + mask_out = 0; + do { + rptr = AtomicPointer.get ((void **)aptr); + ptr = (void *)((size_t) rptr & ~mask); + mask_out = (size_t) rptr & mask; + node.set (ptr); + } while (rptr != AtomicPointer.get ((void **)aptr)); + if (ptr != null) { + return new HazardPointer.from_node (node); + } else { + node.release (); + return null; + } + } + + /** + * Copy an object from atomic pointer. + * + * @param aptr Atomic pointer. + * @param mask Mask of flags. + * @param mask_out Result of mask. + * @return A copy of object from atomic pointer. + */ + public static G? get_pointer (G **aptr, size_t mask = 0, out size_t mask_out = null) { + unowned Node node = acquire (); + void *rptr = null; + void *ptr = null; + mask_out = 0; + do { + rptr = AtomicPointer.get ((void **)aptr); + ptr = (void *)((size_t) rptr & ~mask); + mask_out = (size_t) rptr & mask; + node.set (ptr); + } while (rptr != AtomicPointer.get ((void **)aptr)); + G? res = (G *)ptr; + node.release (); + return res; + } + + /** + * Exchange objects safly. + * + * @param aptr Atomic pointer. + * @param new_ptr New value + * @param mask Mask of flags. + * @param new_mask New mask. + * @param old_mask Previous mask mask. + * @return Hazard pointer containing old value. + */ + public static HazardPointer? exchange_hazard_pointer (G **aptr, owned G? new_ptr, size_t mask = 0, size_t new_mask = 0, out size_t old_mask = null) { + unowned Node? new_node = null; + if (new_ptr != null) { + new_node = acquire (); + new_node.set (new_ptr); + } + old_mask = 0; + void *new_rptr = (void *)((size_t)((owned) new_ptr) | (mask & new_mask)); + unowned Node node = acquire (); + void *rptr = null; + void *ptr = null; + do { + rptr = AtomicPointer.get ((void **)aptr); + ptr = (void *)((size_t) rptr & ~mask); + old_mask = (size_t) rptr & mask; + node.set (ptr); + } while (!AtomicPointer.compare_and_exchange((void **)aptr, rptr, new_rptr)); + if (new_node != null) + new_node.release (); + if (ptr != null) { + return new HazardPointer.from_node (node); + } else { + node.release (); + return null; + } + } + + /** + * Sets object safely + * + * @param aptr Atomic pointer. + * @param new_ptr New value + * @param mask Mask of flags. + * @param new_mask New mask. + */ + public static void set_pointer (G **aptr, owned G? new_ptr, size_t mask = 0, size_t new_mask = 0) { + HazardPointer? ptr = exchange_hazard_pointer (aptr, new_ptr, mask, new_mask, null); + if (ptr != null) { + DestroyNotify notify = get_destroy_notify (); + ptr.release ((owned)notify); + } + } + + /** + * Exchange objects safly. + * + * @param aptr Atomic pointer. + * @param new_ptr New value + * @param mask Mask of flags. + * @param new_mask New mask. + * @param old_mask Previous mask mask. + * @return Value that was previously stored. + */ + public static G? exchange_pointer (G **aptr, owned G? new_ptr, size_t mask = 0, size_t new_mask = 0, out size_t old_mask = null) { + HazardPointer? ptr = exchange_hazard_pointer (aptr, new_ptr, mask, new_mask, out old_mask); + G? rptr = ptr != null ? ptr.get () : null; + return rptr; + } + + /** + * Compares and exchanges objects. + * + * @param aptr Atomic pointer. + * @param old_ptr Old pointer. + * @param _new_ptr New value. + * @param old_mask Old mask. + * @param new_mask New mask. + * @return Value that was previously stored. + */ + public static bool compare_and_exchange_pointer (G **aptr, G? old_ptr, owned G? _new_ptr, size_t mask = 0, size_t old_mask = 0, size_t new_mask = 0) { + G *new_ptr = (owned)_new_ptr; + void *new_rptr = (void *)((size_t)(new_ptr) | (mask & new_mask)); + void *old_rptr = (void *)((size_t)(old_ptr) | (mask & old_mask)); + bool success = AtomicPointer.compare_and_exchange((void **)aptr, old_rptr, new_rptr); + if (success) { + DestroyNotify notify = get_destroy_notify (); + if (old_ptr != null) { + Context.get_current_context ()->release_ptr (old_ptr, (owned)notify); + } + } else if (new_ptr != null) { + _new_ptr = (owned)new_ptr; + } + return success; + } + + ~HazardPointer () { + _node.release (); + } + + /** + * Gets the pointer hold by hazard pointer. + * + * @param other_thread Have to be set to ``true`` if accessed from thread that did not create this thread. + * @return The value hold by pointer. + */ + public inline new unowned G get (bool other_thread = false) { + return _node[other_thread]; + } + + /** + * Free the pointer. + * + * @param notify method freeing object + */ + public void release (owned DestroyNotify notify) { + unowned G item = _node[false]; + _node.set (null); + if (item != null) { + Context.get_current_context ()->release_ptr (item, (owned)notify); + } + } + + /** + * Sets default policy (i.e. default policy for user-created contexts). + * The policy must be concrete and should not be blocking. + * + * @param policy New default policy. + */ + public static void set_default_policy (Policy policy) requires (policy.is_concrete ()) { + if (policy.is_blocking ()) + warning ("Setting blocking defautl Vala.HazardPointer.Policy (there may be a deadlock).\n"); + AtomicInt.set(ref _default_policy, (int)policy); + } + + /** + * Sets thread exit policy (i.e. default policy for the top-most Context). + * The policy must be concrete and should not be unsafe. + * + * @param policy New thread policy. + */ + public static void set_thread_exit_policy (Policy policy) requires (policy.is_concrete ()) { + if (!policy.is_safe ()) + warning ("Setting unsafe globale thread-exit Vala.HazardPointer.Policy (there may be a memory leak).\n"); + AtomicInt.set(ref _thread_exit_policy, (int)policy); + } + + /** + * Sets release (i.e. how exactly the released objects arefreed). + * + * The method can be only set before any objects is released and is not thread-safe. + * + * @param policy New release policy. + */ + public static bool set_release_policy (ReleasePolicy policy) { + int old_policy = AtomicInt.get (ref release_policy); + if ((old_policy & (sizeof(int) * 8 - 1)) != 0) { + critical ("Attempt to change the policy of running helper. Failing."); + return false; + } + if (!AtomicInt.compare_and_exchange (ref release_policy, old_policy, (int)policy)) { + critical ("Concurrent access to release policy detected. Failing."); + return false; + } + return true; + } + + /** + * Policy determines what happens on exit from Context. + */ + public enum Policy { + /** + * Performs default action on exit from thread. + */ + DEFAULT, + /** + * Performs the same action as on exit from current thread. + */ + THREAD_EXIT, + /** + * Goes through the free list and attempts to free un-freed elements. + */ + TRY_FREE, + /** + * Goes through the free list and attempts to free un-freed elements + * untill all elements are freed. + */ + FREE, + /** + * Release the un-freed elements to either helper thread or to main loop. + * Please note if the operation would block it is not performed. + */ + TRY_RELEASE, + /** + * Release the un-freed elements to either helper thread or to main loop. + * Please note it may block while adding to queue. + */ + RELEASE; + + /** + * Checks if the policy is concrete or if it depends on global variables. + * + * @return ``true`` if this policy does not depend on global variables + */ + public bool is_concrete () { + switch (this) { + case DEFAULT: + case THREAD_EXIT: + return false; + case TRY_FREE: + case FREE: + case TRY_RELEASE: + case RELEASE: + return true; + default: + assert_not_reached (); + } + } + + /** + * Checks if policy blocks or is lock-free. + * Please note that it works on a concrete policy only. + * + * @return ``true`` if the policy may block the thread. + */ + public bool is_blocking () requires (this.is_concrete ()) { + switch (this) { + case TRY_FREE: + case TRY_RELEASE: + return false; + case FREE: + case RELEASE: + return true; + default: + assert_not_reached (); + } + } + + /** + * Checks if policy guarantees freeing all elements. + * Please note that it works on a concrete policy only. + * + * @return ``true`` if the policy guarantees freeing all elements. + */ + public bool is_safe () requires (this.is_concrete ()) { + switch (this) { + case TRY_FREE: + case TRY_RELEASE: + return false; + case FREE: + case RELEASE: + return true; + default: + assert_not_reached (); + } + } + + /** + * Finds concrete policy which corresponds to given policy. + * + * @return Policy that corresponds to given policy at given time in given thread. + */ + public Policy to_concrete () ensures (result.is_concrete ()) { + switch (this) { + case TRY_FREE: + case FREE: + case TRY_RELEASE: + case RELEASE: + return this; + case DEFAULT: + return (Policy) AtomicInt.get (ref _default_policy); + case THREAD_EXIT: + return (Policy) AtomicInt.get (ref _thread_exit_policy); + default: + assert_not_reached (); + + } + } + + /** + * Runs the policy. + * @param to_free List containing elements to free. + * @return Non-empty list of not freed elements or ``null`` if all elements have been disposed. + */ + internal ArrayList? perform (owned ArrayList to_free) { + switch (this.to_concrete ()) { + case TRY_FREE: + return try_free (to_free) ? (owned) to_free : null; + case FREE: + while (try_free (to_free)) { + Thread.yield (); + } + return null; + case TRY_RELEASE: + ReleasePolicy.ensure_start (); + if (_queue_mutex.trylock ()) { + _queue.offer ((owned) to_free); + _queue_mutex.unlock (); + return null; + } else { + return (owned) to_free; + } + case RELEASE: + ReleasePolicy.ensure_start (); + _queue_mutex.lock (); + _queue.offer ((owned) to_free); + _queue_mutex.unlock (); + return null; + default: + assert_not_reached (); + } + } + } + + public delegate void DestroyNotify (void *ptr); + + /** + * Release policy determines what happens with object freed by Policy.TRY_RELEASE + * and Policy.RELEASE. + */ + public enum ReleasePolicy { + /** + * Libgee spawns helper thread to free those elements. + * This is default. + */ + HELPER_THREAD, + /** + * Libgee uses GLib main loop. + * This is recommended for application using GLib main loop. + */ + MAIN_LOOP; + + private static void start (ReleasePolicy self) { // FIXME: Make it non-static [bug 659778] + switch (self) { + case HELPER_THREAD: + try { + new Thread ("<>", () => { + while (true) { + Thread.yield (); + attempt_free (); + } + }); + } catch (ThreadError error) { + assert_not_reached (); + } + break; + case MAIN_LOOP: + Idle.add (() => { + attempt_free (); + return true; + }, Priority.LOW); + break; + default: + assert_not_reached (); + } + } + + /** + * Ensures that helper methods are started. + */ + internal static inline void ensure_start () { + int policy = AtomicInt.get (ref release_policy); + if ((policy & (1 << (sizeof(int) * 8 - 1))) != 0) + return; + if (_queue_mutex.trylock ()) { + policy = AtomicInt.get (ref release_policy); + if ((policy & (1 << (sizeof(int) * 8 - 1))) == 0) { + _queue = new LinkedList> (); + // Hack to not lie about successfull setting policy + policy = AtomicInt.exchange_and_add (ref release_policy, (int)(1 << (sizeof(int) * 8 - 1))); + start ((ReleasePolicy) policy); + } + _queue_mutex.unlock (); + } + } + + private static inline void attempt_free () { + if (_queue_mutex.trylock ()) { + Collection> temp = new ArrayList> (); + _queue.drain (temp); + _queue_mutex.unlock (); + temp.foreach ((x) => {_global_to_free.add_all (x); return true;}); + } + try_free (_global_to_free); + } + } + + /** + * Create a new context. User does not need to create explicitly however it might be benefitial + * if he is about to issue bunch of commands he might consider it benefitial to fine-tune the creation of contexts. + * + * {{{ + * Context ctx = new Context (); + * lock_free_collection.operation1 (); + * // Normally on exit the thread exit operation would be executed but here the default operation of + * // child context is executed. + * lock_free_collection.operation2 (); + * }}} + * + * Please note that the Context in implicitly part of stack and: + * + * 1. It cannot be moved between threads. + * 2. If in given thread the child (created later) context is alive parent must be alive as well. + */ + [Compact] + public class Context { // FIXME: Should be struct + public Context (Policy? policy = null) { + this._to_free = new ArrayList (); + this._parent = _current_context.get (); + _current_context.set (this, null); + if (policy == null) { + if (_parent == null) { + _policy = (Policy)AtomicInt.get (ref _thread_exit_policy); + } else { + _policy = (Policy)AtomicInt.get (ref _default_policy); + } + } else { + this._policy = policy.to_concrete (); + } +#if DEBUG + stderr.printf ("Entering context %p (policy %s, parent %p)\n", this, _policy != null ? _policy.to_string () : null, _parent); +#endif + } + + ~Context () { +#if DEBUG + stderr.printf ("Exiting context %p (policy %s, parent %p)\n", this, _policy != null ? _policy.to_string () : null, _parent); +#endif + int size = _to_free.size; + bool clean_parent = false; + if (size > 0) { + ArrayList? remaining; + if (_parent == null || size >= THRESHOLD) + remaining = _policy.perform ((owned) _to_free); + else + remaining = (owned) _to_free; + if (remaining != null) { + assert (_parent != null); + _parent->_to_free.add_all (remaining); + clean_parent = true; + } + } +#if DEBUG + stderr.printf ("Setting current context to %p\n", _parent); +#endif + _current_context.set (_parent, null); + if (clean_parent) + HazardPointer.try_free (_parent->_to_free); + } + + /** + * Tries to free all freed pointer in current context. + */ + public void try_free () { + HazardPointer.try_free (_to_free); + } + + /** + * Ensure that whole context is freed. Plase note that it might block. + */ + public void free_all () { + while (HazardPointer.try_free (_to_free)) + Thread.yield (); + } + + /** + * Tries to push the current context to releaser. + */ + public void try_release () { + if (_queue_mutex.trylock ()) { + _queue.offer ((owned) _to_free); + _to_free = new ArrayList (); + _queue_mutex.unlock (); + } + } + + /** + * Pushes the current context to releaser. Plase note that it might block. + */ + public void release () { + _queue_mutex.lock (); + _queue.offer ((owned) _to_free); + _to_free = new ArrayList (); + _queue_mutex.unlock (); + } + + /** + * Add pointer to freed array. + */ + internal inline void release_ptr (void *ptr, owned DestroyNotify notify) { + FreeNode *node = new FreeNode (); + node->pointer = ptr; + node->destroy_notify = (owned)notify; + _to_free.add (node); + if (_to_free.size >= THRESHOLD) + HazardPointer.try_free (_to_free); + } + + /** + * Gets current context. + */ + internal inline static Context *get_current_context () { + return _current_context.get (); + } + + private inline bool _should_free () { + return (_parent == null && _to_free.size > 0) || _to_free.size >= THRESHOLD; + } + + internal Context *_parent; + internal ArrayList _to_free; + internal Policy? _policy; + internal static StaticPrivate _current_context; + internal static StaticPrivate _root_context; + private static uint THRESHOLD = 10; + } + + /** + * Gets a new hazard pointer node. + * + * @return new hazard pointer node. + */ + internal static inline unowned Node acquire () { + for (unowned Node? curr = get_head (); curr != null; curr = curr.get_next ()) + if (curr.activate ()) + return curr; + Node *node = new Node (); + Node *old_head = null; + do { + node->set_next (old_head = (Node *)AtomicPointer.get (&_head)); + } while (!AtomicPointer.compare_and_exchange (&_head, old_head, node)); + return node; + } + + /** + * Tries to free from list. + * + * @return ``true`` if list is empty. + */ + internal static bool try_free (ArrayList to_free) { + Collection used = new HashSet(); + for (unowned Node? current = get_head (); current != null; current = current.get_next ()) { + used.add (current.get ()); + } + for (int i = 0; i < to_free.size;) { + FreeNode *current = to_free[i]; + if (used.contains (current->pointer)) { +#if DEBUG + stderr.printf ("Skipping freeing %p\n", current->pointer); +#endif + i++; + } else { +#if DEBUG + stderr.printf ("Freeing %p\n", current->pointer); +#endif + FreeNode *cur = to_free.remove_at (to_free.size - 1); + if (i != to_free.size) { + FreeNode *temp = to_free[i]; + to_free[i] = cur; + cur = temp; + } + cur->destroy_notify (cur->pointer); + delete cur; + } + } + return to_free.size > 0; + } + + /** + * Gets head of hazard pointers. + * @return Hazard pointer head. + */ + internal static unowned Node? get_head () { + return (Node *)AtomicPointer.get(&_head); + } + + internal unowned Node _node; + + internal static Node *_head = null; + + internal static int _default_policy = (int)Policy.TRY_FREE; + internal static int _thread_exit_policy = (int)Policy.RELEASE; + + internal static int release_policy = 0; + + internal static Queue> _queue; + internal static StaticMutex _queue_mutex; + + internal static ArrayList _global_to_free; + + internal static DestroyNotify get_destroy_notify () { + return (ptr) => { + G *gptr = ptr; + G obj = (owned)gptr; + obj = null; + }; + } + + [Compact] + internal class FreeNode { + public void *pointer; + public DestroyNotify destroy_notify; + } + + /** + * List of used pointers. + */ + [Compact] + internal class Node { + public Node () { + AtomicPointer.set (&_hazard, null); + AtomicInt.set (ref _active, 1); + } + + inline ~Node () { + delete _next; + } + + public void release () { + AtomicPointer.set (&_hazard, null); + AtomicInt.set (ref _active, 0); + } + + public inline bool is_active () { + return AtomicInt.get (ref _active) != 0; + } + + public inline bool activate () { + return AtomicInt.compare_and_exchange (ref _active, 0, 1); + } + + public inline void set (void *ptr) { + AtomicPointer.set (&_hazard, ptr); + } + + public inline void *get (bool safe = true) { + if (safe) { + return (void *)AtomicPointer.get (&_hazard); + } else { + return (void *)_hazard; + } + } + + public inline unowned Node? get_next () { + return (Node *)AtomicPointer.get (&_next); + } + + public inline void set_next (Node *next) { + AtomicPointer.set (&_next, next); + } + + public Node *_next; + public int _active; + public void *_hazard; + } +} + diff --git a/gee/iterable.vala b/gee/iterable.vala index b0097ac11..67a302c84 100644 --- a/gee/iterable.vala +++ b/gee/iterable.vala @@ -23,17 +23,15 @@ using GLib; /** - * Implemented by classes that support a simple iteration over instances of the - * collection. + * An object that can provide an {@link Iterator}. */ -public abstract class Vala.Iterable { - public abstract Type get_element_type (); - +[GenericAccessors] +public interface Vala.Iterable : Object, Traversable { /** - * Returns a Iterator that can be used for simple iteration over a + * Returns a {@link Iterator} that can be used for simple iteration over a * collection. * - * @return a Iterator that can be used for simple iteration over a + * @return a {@link Iterator} that can be used for simple iteration over a * collection */ public abstract Iterator iterator (); diff --git a/gee/iterator.vala b/gee/iterator.vala index 5190fb652..8064e9644 100644 --- a/gee/iterator.vala +++ b/gee/iterator.vala @@ -1,6 +1,8 @@ /* iterator.vala * * Copyright (C) 2007-2008 Jürg Billeter + * Copyright (C) 2009 Didier Villevalois, Maciej Piechotka + * Copyright (C) 2010-2011 Maciej Piechotka * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,25 +20,99 @@ * * Author: * Jürg Billeter + * Maciej Piechotka + * Didier 'Ptitjes Villevalois */ /** - * Implemented by classes that support a simple iteration over instances of the - * collection. + * An iterator over a collection. + * + * Gee's iterators are "on-track" iterators. They always point to an item + * except before the first call to {@link next}, or, when an + * item has been removed, until the next call to {@link next}. + * + * Please note that when the iterator is out of track, neither {@link get} nor + * {@link remove} are defined and both will fail. After the next call to + * {@link next}, they will be defined again. */ -public abstract class Vala.Iterator { +public interface Vala.Iterator : Object, Traversable { /** * Advances to the next element in the iteration. * - * @return true if the iterator has a next element + * @return ``true`` if the iterator has a next element */ public abstract bool next (); + /** + * Checks whether there is a next element in the iteration. + * + * @return ``true`` if the iterator has a next element + */ + public abstract bool has_next (); + /** * Returns the current element in the iteration. * * @return the current element in the iteration */ - public abstract G? get (); + public abstract G get (); + + /** + * Removes the current element in the iteration. The cursor is set in an + * in-between state. Both {@link get} and {@link remove} will fail until + * the next move of the cursor (calling {@link next}). + */ + public abstract void remove (); + + /** + * Determines wheather the call to {@link get} is legal. It is false at the + * beginning and after {@link remove} call and true otherwise. + */ + public abstract bool valid { get; } + + /** + * Determines wheather the call to {@link remove} is legal assuming the + * iterator is valid. The value must not change in runtime hence the user + * of iterator may cache it. + */ + public abstract bool read_only { get; } + + /** + * Create iterator from unfolding function. The lazy value is + * force-evaluated before progressing to next element. + * + * @param f Unfolding function + * @param current If iterator is to be valid it contains the current value of it + */ + public static Iterator unfold (owned UnfoldFunc f, owned Lazy? current = null) { + return new UnfoldIterator ((owned) f, (owned) current); + } + + /** + * Concatinate iterators. + * + * @param iters Iterators of iterators + * @return Iterator containg values of each iterator + */ + public static Iterator concat (Iterator> iters) { + Iterator? current = null; + if (iters.valid) + current = iters.get (); + return unfold (() => { + while (true) { + if (current == null) { + if (iters.next ()) { + current = iters.get (); + } else { + return null; + } + } else if (current.next ()) { + return new Lazy.from_value (current.get ()); + } else { + current = null; + } + } + }); + } } diff --git a/gee/lazy.vala b/gee/lazy.vala new file mode 100644 index 000000000..ed1ac3cc1 --- /dev/null +++ b/gee/lazy.vala @@ -0,0 +1,60 @@ +/* lazy.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +namespace Vala { + public delegate G LazyFunc (); +} + +/** + * Represents a lazy value. I.e. value that is computed on demand. + */ +public class Vala.Lazy { + public Lazy (owned LazyFunc func) { + _func = (owned)func; + } + + public Lazy.from_value (G item) { + _value = item; + } + + public void eval () { + if (_func != null) { + _value = _func (); + _func = null; + } + } + + public new G get () { + eval (); + return _value; + } + + public new G value { + get { + eval (); + return _value; + } + } + + private LazyFunc? _func; + private G? _value; +} diff --git a/gee/linkedlist.vala b/gee/linkedlist.vala new file mode 100644 index 000000000..16d82da11 --- /dev/null +++ b/gee/linkedlist.vala @@ -0,0 +1,690 @@ +/* linkedlist.vala + * + * Copyright (C) 2004-2005 Novell, Inc + * Copyright (C) 2005 David Waite + * Copyright (C) 2007-2008 Jürg Billeter + * Copyright (C) 2009 Mark Lee, Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Mark Lee + * Didier 'Ptitjes Villevalois + */ + +/** + * Doubly-linked list implementation of the {@link List} interface. + * + * This implementation is pretty well designed for highly mutable data. When + * indexed access is privileged prefer using {@link ArrayList}. + * + * @see ArrayList + */ +public class Vala.LinkedList : AbstractBidirList, Queue, Deque { + private int _size = 0; + private int _stamp = 0; + private Node? _head = null; + private weak Node? _tail = null; + + /** + * The elements' equality testing function. + */ + [CCode (notify = false)] + public EqualDataFunc equal_func { private set; get; } + + /** + * Constructs a new, empty linked list. + * + * If not provided, the function parameter is requested to the + * {@link Functions} function factory methods. + * + * @param equal_func an optional element equality testing function + */ + public LinkedList (owned EqualDataFunc? equal_func = null) { + if (equal_func == null) { + equal_func = Functions.get_equal_func_for (typeof (G)); + } + this.equal_func = equal_func; + } + + ~LinkedList () { + this.clear (); + } + + /** + * {@inheritDoc} + */ + public override bool foreach(ForallFunc f) { + for (weak Node? node = _head; node != null; node = node.next) { + if (!f (node.data)) { + return false; + } + } + return true; + } + + /** + * {@inheritDoc} + */ + public override Vala.Iterator iterator () { + return new Iterator (this); + } + + /** + * {@inheritDoc} + */ + public override ListIterator list_iterator () { + return new Iterator (this); + } + + /** + * {@inheritDoc} + */ + public override BidirListIterator bidir_list_iterator () { + return new Iterator (this); + } + + /** + * {@inheritDoc} + */ + public override int size { + get { return this._size; } + } + + /** + * {@inheritDoc} + */ + public override bool read_only { + get { return false; } + } + + /** + * {@inheritDoc} + */ + public override bool contains (G item) { + return this.index_of (item) != -1; + } + + /** + * {@inheritDoc} + */ + public override bool add (G item) { + Node n = new Node (item); + if (this._head == null && this._tail == null) { + this._tail = n; + this._head = (owned) n; + } else { + n.prev = this._tail; + this._tail.next = (owned) n; + this._tail = this._tail.next; + } + + // Adding items to the list during iterations is allowed. + //++this._stamp; + + this._size++; + return true; + } + + /** + * {@inheritDoc} + */ + public override bool remove (G item) { // Should remove only the first occurence (a test should be added) + for (weak Node n = this._head; n != null; n = n.next) { + if (this.equal_func (item, n.data)) { + this._remove_node (n); + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public override void clear () { + while (_head != null) { + _remove_node (_head); + } + + ++this._stamp; + this._head = null; + this._tail = null; + this._size = 0; + } + + /** + * {@inheritDoc} + */ + public override G get (int index) { + assert (index >= 0); + assert (index < this._size); + + unowned Node? n = this._get_node_at (index); + assert (n != null); + return n.data; + } + + /** + * {@inheritDoc} + */ + public override void set (int index, G item) { + assert (index >= 0); + assert (index < this._size); + + unowned Node? n = this._get_node_at (index); + return_if_fail (n != null); + n.data = item; + } + + /** + * {@inheritDoc} + */ + public override int index_of (G item) { + int idx = 0; + for (weak Node? node = _head; node != null; node = node.next, idx++) { + if (this.equal_func (item, node.data)) { + return idx; + } + } + return -1; + } + + /** + * {@inheritDoc} + */ + public override void insert (int index, G item) { + assert (index >= 0); + assert (index <= this._size); + + if (index == this._size) { + this.add (item); + } else { + Node n = new Node (item); + if (index == 0) { + n.next = (owned) this._head; + n.next.prev = n; + this._head = (owned)n; + } else { + weak Node prev = this._head; + for (int i = 0; i < index - 1; i++) { + prev = prev.next; + } + n.prev = prev; + n.next = (owned) prev.next; + n.next.prev = n; + prev.next = (owned) n; + } + + // Adding items to the list during iterations is allowed. + //++this._stamp; + + this._size++; + } + } + + /** + * {@inheritDoc} + */ + public override G remove_at (int index) { + assert (index >= 0); + assert (index < this._size); + + unowned Node? n = this._get_node_at (index); + assert (n != null); + G element = n.data; + this._remove_node (n); + return element; + } + + /** + * {@inheritDoc} + */ + public override List? slice (int start, int stop) { + return_val_if_fail (start <= stop, null); + return_val_if_fail (start >= 0, null); + return_val_if_fail (stop <= this._size, null); + + List slice = new LinkedList (this.equal_func); + weak Node n = this._get_node_at (start); + for (int i = start; i < stop; i++) { + slice.add (n.data); + n = n.next; + } + + return slice; + } + + /** + * {@inheritDoc} + */ + public G first () { + assert (_size > 0); + return _head.data; + } + + /** + * {@inheritDoc} + */ + public G last () { + assert (_size > 0); + return _tail.data; + } + + /** + * {@inheritDoc} + */ + public int capacity { + get { return UNBOUNDED_CAPACITY; } + } + + /** + * {@inheritDoc} + */ + public int remaining_capacity { + get { return UNBOUNDED_CAPACITY; } + } + + /** + * {@inheritDoc} + */ + public bool is_full { + get { return false; } + } + + /** + * {@inheritDoc} + */ + public bool offer (G element) { + return offer_tail (element); + } + + /** + * {@inheritDoc} + */ + public G? peek () { + return peek_head (); + } + + /** + * {@inheritDoc} + */ + public G? poll () { + return poll_head (); + } + + /** + * {@inheritDoc} + */ + public int drain (Collection recipient, int amount = -1) { + return drain_head (recipient, amount); + } + + /** + * {@inheritDoc} + */ + public bool offer_head (G element) { + insert (0, element); + return true; + } + + /** + * {@inheritDoc} + */ + public G? peek_head () { + if (this._size == 0) { + return null; + } + return get (0); + } + + /** + * {@inheritDoc} + */ + public G? poll_head () { + if (this._size == 0) { + return null; + } + return remove_at (0); + } + + /** + * {@inheritDoc} + */ + public int drain_head (Collection recipient, int amount = -1) { + if (amount == -1) { + amount = this._size; + } + for (int i = 0; i < amount; i++) { + if (this._size == 0) { + return i; + } + recipient.add (remove_at (0)); + } + return amount; + } + + /** + * {@inheritDoc} + */ + public bool offer_tail (G element) { + return add (element); + } + + /** + * {@inheritDoc} + */ + public G? peek_tail () { + if (this._size == 0) { + return null; + } + return get (_size - 1); + } + + /** + * {@inheritDoc} + */ + public G? poll_tail () { + if (this._size == 0) { + return null; + } + return remove_at (_size - 1); + } + + /** + * {@inheritDoc} + */ + public int drain_tail (Collection recipient, int amount = -1) { + if (amount == -1) { + amount = this._size; + } + for (int i = 0; i < amount; i++) { + if (this._size == 0) { + return i; + } + recipient.add (remove_at (this._size - 1)); + } + return amount; + } + + [Compact] + private class Node { // Maybe a compact class should be used? + public G data; + public weak Node? prev = null; + public Node? next = null; + public Node (owned G data) { + this.data = data; + } + } + + private class Iterator : Object, Traversable, Vala.Iterator, BidirIterator, ListIterator, BidirListIterator { + private bool started = false; + private bool removed = false; + private unowned Node? position; + private int _stamp; + private LinkedList _list; + private int _index; + + public Iterator (LinkedList list) { + this._list = list; + this.position = null; + this._index = -1; + this._stamp = list._stamp; + } + + public bool next () { + assert (this._stamp == this._list._stamp); + + if (this.removed) { + if (this.position != null) { + this.removed = false; + return true; + } else { + return false; + } + } else if (!this.started) { + if (this._list._head != null) { + this.started = true; + this.position = this._list._head; + this._index++; + return true; + } else { + return false; + } + } else if (this.position != null) { + if (this.position.next != null) { + this.position = this.position.next; + this._index++; + return true; + } else { + return false; + } + } + return false; + } + + public bool has_next () { + assert (this._stamp == this._list._stamp); + + if (this.removed) { + return this.position != null; + } else if (!this.started) { + return this._list._head != null; + } else if (this.position != null) { + return this.position.next != null; + } + return false; + } + + public bool first () { + assert (this._stamp == this._list._stamp); + if (this._list.size == 0) { + return false; + } + this.position = this._list._head; + this.started = true; + this._index = 0; + this.removed = false; + return this.position != null; + } + + public new G get () { + assert (this._stamp == this._list._stamp); + assert (this.position != null); + + return this.position.data; + } + + public void remove () { + assert (this._stamp == this._list._stamp); + assert (this.position != null); + + unowned Node? new_position = this.position.next; + if (new_position == null) { + started = false; + } + _list._remove_node (this.position); + this.position = new_position; + this.removed = true; + this._stamp = this._list._stamp; + } + + public bool previous () { + assert (this._stamp == this._list._stamp); + + if (!this.started) { + this.position = null; + return false; + } else if (this.position != null && this.position.prev != null) { + this.position = this.position.prev; + this._index--; + return true; + } + return false; + } + + public bool has_previous () { + assert (this._stamp == this._list._stamp); + + if (!this.started) { + return false; + } else if (this.position != null) { + return this.position.prev != null; + } + return false; + } + + public bool last () { + assert (this._stamp == this._list._stamp); + + if (this._list.size == 0) { + return false; + } + this.position = this._list._tail; + this.started = true; + this._index = this._list._size - 1; + return this.position != null; + } + + public new void set (G item) { + assert (this._stamp == this._list._stamp); + assert (this.position != null); + + this.position.data = item; + } + + public void insert (G item) { + assert (this._stamp == this._list._stamp); + assert (this.position != null); + + Node n = new Node (item); + if (this.position.prev != null) { + Node position = (owned) this.position.prev.next; + n.prev = position.prev; + position.prev = n; + n.next = (owned) position; + weak Node _n = n; + _n.prev.next = (owned) n; + } else { + Node position = (owned) this._list._head; + position.prev = n; + n.next = (owned) position; + this._list._head = (owned) n; + } + this._list._size++; + this._index++; + _stamp = _list._stamp; + } + + public void add (G item) { + assert (this._stamp == this._list._stamp); + assert (this.position != null); + + Node n = new Node (item); + if (this.position.next != null) { + this.position.next.prev = n; + n.next = (owned) this.position.next; + } else { + this._list._tail = n; + } + this.position.next = (owned) n; + this.position.next.prev = this.position; + this.position = this.position.next; + this._list._size++; + this._index++; + _stamp = _list._stamp; + } + + public int index () { + assert (this._stamp == this._list._stamp); + assert (this.position != null); + + return this._index; + } + + public bool read_only { + get { + return false; + } + } + + public bool valid { + get { + return !this.removed && this.position != null; + } + } + + public bool foreach (ForallFunc f) { + assert (_stamp == _list._stamp); + if (!started) { + position = _list._head; + if (position != null) + started = true; + } + removed = false; + while (position != null) { + if (!f (position.data)) { + return false; + } + position = position.next; + } + position = _list._tail; + return true; + } + } + + private unowned Node? _get_node_at (int index) { + unowned Node? n = null;; + if (index == 0) { + n = this._head; + } else if (index == this._size - 1) { + n = this._tail; + } else if (index <= this._size / 2) { + n = this._head; + for (int i = 0; index != i; i++) { + n = n.next; + } + } else { + n = this._tail; + for (int i = this._size - 1; index != i; i--) { + n = n.prev; + } + } + return n; + } + + private void _remove_node (Node _n) { + Node n; + weak Node next; + if (_n == this._head) { + n = (owned) this._head; + next = this._head = (owned) n.next; + } else { + n = (owned) _n.prev.next; + next = n.prev.next = (owned) n.next; + } + if (n == this._tail) { + this._tail = n.prev; + } else { + next.prev = n.prev; + } + n.prev = null; + n.next = null; + n.data = null; + ++this._stamp; + this._size--; + } +} + diff --git a/gee/list.vala b/gee/list.vala index d73028337..b99d598b6 100644 --- a/gee/list.vala +++ b/gee/list.vala @@ -21,9 +21,17 @@ */ /** - * Represents a collection of items in a well-defined order. + * An ordered collection. */ -public abstract class Vala.List : Collection { +[GenericAccessors] +public interface Vala.List : Collection { + /** + * Returns a ListIterator that can be used for iteration over this list. + * + * @return a ListIterator that can be used for iteration over this list + */ + public abstract new ListIterator list_iterator (); + /** * Returns the item at the specified index in this list. * @@ -31,7 +39,7 @@ public abstract class Vala.List : Collection { * * @return the item at the specified index in the list */ - public abstract G? get (int index); + public abstract G get (int index); /** * Sets the item at the specified index in this list. @@ -41,10 +49,10 @@ public abstract class Vala.List : Collection { public abstract void set (int index, G item); /** - * Returns the index of the first occurrence of the specified item in + * Returns the index of the first occurence of the specified item in * this list. * - * @return the index of the first occurrence of the specified item, or + * @return the index of the first occurence of the specified item, or * -1 if the item could not be found */ public abstract int index_of (G item); @@ -61,7 +69,77 @@ public abstract class Vala.List : Collection { * Removes the item at the specified index of this list. * * @param index zero-based index of the item to be removed + * + * @return the removed element + */ + public abstract G remove_at (int index); + + /** + * Returns a slice of this list. + * + * @param start zero-based index of the begin of the slice + * @param stop zero-based index after the end of the slice + * + * @return A list containing a slice of this list + */ + public abstract List? slice (int start, int stop); + + /** + * Returns the first item of the list. Fails if the list is empty. + * + * @return first item in the list + */ + public virtual G first () { + return get (0); + } + + /** + * Returns the last item of the list. Fails if the list is empty. + * + * @return last item in the list + */ + public virtual G last () { + return get (size - 1); + } + + /** + * Inserts items into this list for the input collection at the + * specified position. + * + * @param index zero-based index of the items to be inserted + * @param collection collection of items to be inserted + */ + public virtual void insert_all (int index, Collection collection) { + foreach (G item in collection) { + insert(index, item); + index++; + } + } + + /** + * Sorts items by comparing with the specified compare function. + * + * @param compare_func compare function to use to compare items + */ + public virtual void sort (owned CompareDataFunc? compare_func = null) { + if (compare_func == null) { + compare_func = Functions.get_compare_func_for (typeof (G)); + } + TimSort.sort (this, compare_func); + } + + /** + * The read-only view of this list. + */ + public abstract new List read_only_view { owned get; } + + /** + * Returns an immutable empty list. + * + * @return an immutable empty list */ - public abstract void remove_at (int index); + public static List empty () { + return new LinkedList ().read_only_view; + } } diff --git a/gee/listiterator.vala b/gee/listiterator.vala new file mode 100644 index 000000000..a57a7df08 --- /dev/null +++ b/gee/listiterator.vala @@ -0,0 +1,44 @@ +/* listiterator.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * A list iterator. This supports bi-directionnal and index-based iteration. + */ +public interface Vala.ListIterator : Vala.Iterator { + /** + * Sets the current item in the iteration to the specified new item. + */ + public abstract void set (G item); + + /** + * Adds the specified item after the current item in the iteration. The + * cursor is moved to point to the new added item. + */ + public abstract void add (G item); + + /** + * Returns the current index in the iteration. + * + * @return the current index + */ + public abstract int index (); +} diff --git a/gee/map.vala b/gee/map.vala index 2c96a3d04..e48cb8a51 100644 --- a/gee/map.vala +++ b/gee/map.vala @@ -21,43 +21,100 @@ */ /** - * A map is a generic collection of key/value pairs. + * An object that maps keys to values. */ -public abstract class Vala.Map { +[GenericAccessors] +public interface Vala.Map : Object, Iterable> { /** * The number of items in this map. */ public abstract int size { get; } /** - * Returns the keys of this map as a read-only set. - * - * @return the keys of the map + * Specifies whether this map is empty. + */ + public virtual bool is_empty { get { return size == 0; } } + + /** + * Specifies whether this collection can change - i.e. wheather {@link set}, + * {@link remove} etc. are legal operations. + */ + public abstract bool read_only { get; } + + /** + * The read-only view of the keys of this map. + */ + public abstract Set keys { owned get; } + + /** + * The read-only view of the values of this map. */ - public abstract Set get_keys (); + public abstract Collection values { owned get; } + + /** + * The read-only view of the entries of this map. + */ + public abstract Set> entries { owned get; } + + /** + * An entry of a map. + */ + public abstract class Entry : Object { + /** + * The key of this entry. + */ + public abstract K key { get; } + + /** + * The value of this entry. + */ + public abstract V value { get; set; } + + /** + * ``true'' if the setting value is permitted. + */ + public abstract bool read_only { get; } + } /** - * Returns the values of this map as a read-only collection. + * Determines whether this map has the specified key. * - * @return the values of the map + * @param key the key to locate in the map + * + * @return ``true`` if key is found, ``false`` otherwise */ - public abstract Collection get_values (); + public abstract bool has_key (K key); /** * Determines whether this map contains the specified key. * * @param key the key to locate in the map * - * @return true if key is found, false otherwise + * @return ``true`` if key is found, ``false`` otherwise + * + * @deprecated Use {@link has_key} method instead. + */ + [Version (deprecated = true)] + public bool contains (K key) { + return has_key(key); + } + + /** + * Determines whether this map has the specified key/value entry. + * + * @param key the key to locate in the map + * @param value the corresponding value + * + * @return ``true`` if key is found, ``false`` otherwise */ - public abstract bool contains (K key); + public abstract bool has (K key, V value); /** * Returns the value of the specified key in this map. * * @param key the key whose value is to be retrieved * - * @return the value associated with the key, or null if the key + * @return the value associated with the key, or ``null`` if the key * couldn't be found */ public abstract V? get (K key); @@ -73,11 +130,27 @@ public abstract class Vala.Map { /** * Removes the specified key from this map. * - * @param key the key to remove from the map + * @param key the key to remove from the map + * @param value the receiver variable for the removed value * - * @return true if the map has been changed, false otherwise + * @return ``true`` if the map has been changed, ``false`` otherwise */ - public abstract bool remove (K key); + public abstract bool unset (K key, out V? value = null); + + /** + * Removes the specified key from this map. + * + * @param key the key to remove from the map + * @param value the receiver variable for the removed value + * + * @return ``true`` if the map has been changed, ``false`` otherwise + * + * @deprecated Use {@link unset} method instead. + */ + [Version (deprecated = true)] + public bool remove (K key, out V? value = null) { + return unset (key, out value); + } /** * Removes all items from this collection. Must not be called on @@ -86,12 +159,98 @@ public abstract class Vala.Map { public abstract void clear (); /** - * Returns a Iterator that can be used for simple iteration over a - * map. + * Returns an iterator for this map. * - * @return a Iterator that can be used for simple iteration over a - * map + * @return a map iterator */ public abstract MapIterator map_iterator (); + + /** + * Inserts all items that are contained in the input map to this map. + * + * @param map the map which items are inserted to this map + */ + public virtual void set_all (Map map) { + foreach (Map.Entry entry in map.entries) { + set (entry.key, entry.value); + } + } + + /** + * Removes all items from this map that are common to the input map + * and this map. + * + * @param map the map which common items are deleted from this map + */ + public virtual bool unset_all (Map map) { + bool changed = false; + foreach (K key in map.keys) { + changed = changed | unset (key); + } + return changed; + } + + /** + * Removes all items from this map that are common to the input map + * and this map. + * + * @param map the map which common items are deleted from this map + * + * @deprecated Use {@link unset_all} method instead. + */ + [Version (deprecated = true)] + public bool remove_all (Map map) { + return unset_all (map); + } + + /** + * Returns ``true`` it this map contains all items as the input map. + * + * @param map the map which items will be compared with this map + */ + public virtual bool has_all (Map map) { + foreach (Map.Entry entry in map.entries) { + if (!has (entry.key, entry.value)) { + return false; + } + } + return true; + } + + /** + * Returns ``true`` it this map contains all items as the input map. + * + * @param map the map which items will be compared with this map + * + * @deprecated Use {@link has_all} method instead. + */ + [Version (deprecated = true)] + public bool contains_all (Map map) { + return has_all (map); + } + + /** + * The read-only view this map. + */ + public abstract Map read_only_view { owned get; } + + /** + * The type of the keys in this map. + */ + public Type key_type { get { return typeof(K); } } + + /** + * The type of the values in this map. + */ + public Type value_type { get { return typeof(V); } } + + /** + * Returns an immutable empty map. + * + * @return an immutable empty map + */ + public static Map empty () { + return new HashMap ().read_only_view; + } } diff --git a/gee/mapiterator.vala b/gee/mapiterator.vala index 78243c2d6..2ad5890cb 100644 --- a/gee/mapiterator.vala +++ b/gee/mapiterator.vala @@ -1,6 +1,7 @@ /* mapiterator.vala * - * Copyright (C) 2011 Florian Brosch + * Copyright (C) 2009 Didier Villevalois + * Copyright (C) 2011 Maciej Piechotka * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,22 +18,42 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: - * Florian Brosch + * Didier 'Ptitjes Villevalois */ - +namespace Vala { + public delegate A FoldMapFunc (K k, V v, owned A a); + public delegate bool ForallMapFunc (K k, V v); +} /** * An iterator over a map. + * + * Gee's iterators are "on-track" iterators. They always point to an item + * except before the first call to {@link next}, or, when an + * item has been removed, until the next call to {@link next}. + * + * Please note that when the iterator is out of track, neither {@link get_key}, + * {@link get_value}, {@link set_value} nor {@link unset} are defined and all + * will fail. After the next call to {@link next}, they will + * be defined again. */ -public abstract class Vala.MapIterator { +[GenericAccessors] +public interface Vala.MapIterator : Object { /** - * Advances to the next element in the iteration. + * Advances to the next entry in the iteration. * - * @return true if the iterator has a next element + * @return ``true`` if the iterator has a next entry */ public abstract bool next (); + /** + * Checks whether there is a next entry in the iteration. + * + * @return ``true`` if the iterator has a next entry + */ + public abstract bool has_next (); + /** * Returns the current key in the iteration. * @@ -41,12 +62,84 @@ public abstract class Vala.MapIterator { public abstract K get_key (); /** - * Returns the current value in the iteration. + * Returns the value associated with the current key in the iteration. * - * @return the current value in the iteration + * @return the value for the current key */ public abstract V get_value (); -} + /** + * Sets the value associated with the current key in the iteration. + * + * @param value the new value for the current key + */ + public abstract void set_value (V value); + + /** + * Unsets the current entry in the iteration. The cursor is set in an + * in-between state. {@link get_key}, {@link get_value}, {@link set_value} + * and {@link unset} will fail until the next move of the cursor (calling + * {@link next}). + */ + public abstract void unset (); + /** + * Determines wheather the call to {@link get_key}, {@link get_value} and + * {@link set_value} is legal. It is false at the beginning and after + * {@link unset} call and true otherwise. + */ + public abstract bool valid { get; } + + /** + * Determines wheather the call to {@link set_value} is legal assuming the + * iterator is valid. The value must not change in runtime hence the user + * of iterator may cache it. + */ + public abstract bool mutable { get; } + + /** + * Determines wheather the call to {@link unset} is legal assuming the + * iterator is valid. The value must not change in runtime hence the user + * of iterator may cache it. + */ + public abstract bool read_only { get; } + + /** + * Standard aggragation function. + * + * It takes a function, seed and first element, returns the new seed and + * progress to next element when the operation repeats. + * + * Operation moves the iterator to last element in iteration. If iterator + * points at some element it will be included in iteration. + */ + public virtual A fold (FoldMapFunc f, owned A seed) + { + if (valid) + seed = f (get_key (), get_value (), (owned) seed); + while (next ()) + seed = f (get_key (), get_value (), (owned) seed); + return (owned) seed; + } + + /** + * Apply function to each element returned by iterator. + * + * Operation moves the iterator to last element in iteration. If iterator + * points at some element it will be included in iteration. + */ + public new virtual bool foreach (ForallMapFunc f) { + if (valid) { + if (!f (get_key (), get_value ())) { + return false; + } + } + while (next ()) { + if (!f (get_key (), get_value ())) { + return false; + } + } + return true; + } +} diff --git a/gee/multimap.vala b/gee/multimap.vala new file mode 100644 index 000000000..6c1748b84 --- /dev/null +++ b/gee/multimap.vala @@ -0,0 +1,127 @@ +/* multimap.vala + * + * Copyright (C) 2009 Ali Sabil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Ali Sabil + */ + +/** + * A map with multiple values per key. + */ +[GenericAccessors] +public interface Vala.MultiMap : Object { + /** + * The number of key/value pairs in this map. + */ + public abstract int size { get; } + + /** + * Specifies whether this collection can change - i.e. wheather {@link set}, + * {@link remove} etc. are legal operations. + */ + public abstract bool read_only { get; } + + /** + * Returns the keys of this multimap as a read-only set. + * + * @return the keys of the map + */ + public abstract Set get_keys (); + + /** + * Returns the keys of this multimap as a read-only set. + * + * @return the keys of the map + */ + public abstract MultiSet get_all_keys (); + + /** + * Returns the values of this map as a read-only collection. + * + * @return the values of the map + */ + public abstract Collection get_values (); + + /** + * Determines whether this map contains the specified key. + * + * @param key the key to locate in the map + * + * @return ``true`` if key is found, ``false`` otherwise + */ + public abstract bool contains (K key); + + /** + * Returns the values for the specified key in this map. + * + * @param key the key whose values are to be retrieved + * + * @return a Collection of values associated with the given key + */ + public abstract Collection get (K key); + + /** + * Inserts a key/value pair into this map. + * + * @param key the key to insert + * @param value the value to associate with the key + */ + public abstract void set (K key, V value); + + /** + * Removes the specified key/value pair from this multimap. + * + * @param key the key to remove from the map + * @param value the value to remove from the map + * + * @return ``true`` if the map has been changed, ``false`` otherwise + */ + public abstract bool remove (K key, V value); + + /** + * Removes the specified key and all the associated values from this + * multimap. + * + * @param key the key to remove from the map + * + * @return ``true`` if the map has been changed, ``false`` otherwise + */ + public abstract bool remove_all (K key); + + /** + * Removes all items from this collection. + */ + public abstract void clear (); + + /** + * Returns an iterator for this map. + * + * @return a map iterator + */ + public abstract MapIterator map_iterator (); + + /** + * The type of the keys in this multimap. + */ + public Type key_type { get { return typeof (K); } } + + /** + * The type of the values in this multimap. + */ + public Type value_type { get { return typeof (V); } } +} diff --git a/gee/multiset.vala b/gee/multiset.vala new file mode 100644 index 000000000..7b9f774f9 --- /dev/null +++ b/gee/multiset.vala @@ -0,0 +1,36 @@ +/* multiset.vala + * + * Copyright (C) 2009 Ali Sabil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Ali Sabil + */ + +/** + * A collection with duplicate elements. + */ +[GenericAccessors] +public interface Vala.MultiSet : Collection { + /** + * Returns the number of occurences of an item in this multiset. + * + * @param item the item to count occurences of + * + * @return the number of occurences of the item in this multiset. + */ + public abstract int count (G item); +} diff --git a/gee/priorityqueue.vala b/gee/priorityqueue.vala new file mode 100644 index 000000000..04d0a00c3 --- /dev/null +++ b/gee/priorityqueue.vala @@ -0,0 +1,1207 @@ +/* priorityqueue.vala + * + * Copyright (C) 2009 Didier Villevalois + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * Relaxed fibonacci heap priority queue implementation of the {@link Queue}. + * + * The elements of the priority queue are ordered according to their natural + * ordering, or by a compare_func provided at queue construction time. A + * priority queue does not permit null elements and does not have bounded + * capacity. + * + * This implementation provides O(1) time for offer and peek methods, and + * O(log n) for poll method. It is based on the algorithms described by + * Boyapati Chandra Sekhar in: + * + * "Worst Case Efficient Data Structures + * for Priority Queues and Deques with Heap Order" + * Boyapati Chandra Sekhar (under the guidance of Prof. C. Pandu Rangan) + * Department of Computer Science and Engineering + * Indian Institute of Technology, Madras + * May 1996 + */ +public class Vala.PriorityQueue : Vala.AbstractQueue { + + /** + * The elements' comparator function. + */ + [CCode (notify = false)] + public CompareDataFunc compare_func { private set; get; } + + private int _size = 0; + private int _stamp = 0; + private Type1Node? _r = null; + private Type2Node? _r_prime = null; + private Type2Node? _lm_head = null; + private Type2Node? _lm_tail = null; + private Type1Node? _p = null; + private Type1Node?[] _a = new Type1Node?[0]; + private NodePair? _lp_head = null; + private NodePair? _lp_tail = null; + private bool[] _b = new bool[0]; + private Type1Node? _ll_head = null; + private Type1Node? _ll_tail = null; + private unowned Node _iter_head = null; + private unowned Node _iter_tail = null; + + /** + * Constructs a new, empty priority queue. + * + * If not provided, the function parameter is requested to the + * {@link Functions} function factory methods. + * + * @param compare_func an optional element comparator function + */ + public PriorityQueue (owned CompareDataFunc? compare_func = null) { + if (compare_func == null) { + compare_func = Functions.get_compare_func_for (typeof (G)); + } + this.compare_func = compare_func; + } + + /** + * {@inheritDoc} + */ + public override int capacity { + get { return UNBOUNDED_CAPACITY; } + } + + /** + * {@inheritDoc} + */ + public override int remaining_capacity { + get { return UNBOUNDED_CAPACITY; } + } + + /** + * {@inheritDoc} + */ + public override bool is_full { + get { return false; } + } + + /** + * {@inheritDoc} + */ + public override bool read_only { + get { return false; } + } + + /** + * {@inheritDoc} + */ + public bool offer (G element) { + #if DEBUG + _dump ("Start offer: %s".printf ((string)element)); + #endif + if (_r == null) { + _r = new Type1Node (element, ref _iter_head, ref _iter_tail); + _p = _r; + } else if (_r_prime == null) { + _r_prime = new Type2Node (element, ref _iter_head, ref _iter_tail); + _r_prime.parent = _r; + _r.type2_child = _r_prime; + if (_compare (_r_prime, _r) < 0) + _swap_data (_r_prime, _r); + } else { + // Form a tree with a single node N of type I consisting of element e + Type1Node node = new Type1Node (element, ref _iter_head, ref _iter_tail); + + //Add(Q, N) + _add (node); + } + + _stamp++; + _size++; + #if DEBUG + _dump ("End offer: %s".printf ((string)element)); + #endif + return true; + } + + /** + * {@inheritDoc} + */ + public override G? peek () { + if (_r == null) { + return null; + } + return _r.data; + } + + /** + * {@inheritDoc} + */ + public override G? poll () { + #if DEBUG + _dump ("Start poll:"); + #endif + + // 1a. M inElement <- R.element + if (_r == null) { + return null; + } + G min = _r.data; + _r.pending_drop = false; + _stamp++; + _size--; + + // 1b. R.element = R'.element + if (_r_prime == null) { + if (_r.iter_next != null) { + _r.iter_next.iter_prev = _r.iter_prev; + } + if (_r.iter_prev != null) { + _r.iter_prev.iter_next = _r.iter_next; + } + if (_iter_head == _r) { + _iter_head = _r.iter_next; + } + if (_iter_tail == _r) { + _iter_tail = _r.iter_prev; + } + _r = null; + _p = null; + return min; + } + _move_data (_r, _r_prime); + + + // 1c. R'' <- The child of R' containing the minimum element among the children of R' + if (_r_prime.type1_children_head == null) { + _remove_type2_node (_r_prime, true); + _r_prime = null; + return min; + } + Type1Node? r_second = null; + Type1Node node = _r_prime.type1_children_head; + while (node != null) { + if (r_second == null || _compare (node, r_second) < 0) { + r_second = node; + } + node = node.brothers_next; + } + + // 1d. R'.element <- R''.element + _move_data (_r_prime, r_second); + + // 2a. Delete the subtree rooted at R'' from Q + _remove_type1_node (r_second, true); + + // 2b. For all children N of type I of R'' do make N a child of R' of Q + node = r_second.type1_children_head; + while (node != null) { + Type1Node next = node.brothers_next; + _remove_type1_node (node, false); + _add_in_r_prime (node); + node = next; + } + + // For now we can't have type2 node other than R' (left for reference) + #if false + // 3a. If R'' has no child of type II then goto Step 4. + if (r_second.type2_child != null) { + // 3b. Let M' be the child of type II of R''. Insert(Q, M'.element) + Type2Node m_prime = r_second.type2_child; + _remove_type2_node (m_prime); + offer (m_prime.data); + + // 3c. For all children N of M do make N a child of R' of Q + node = m_prime.type1_children_head; + while (node != null) { + Type1Node next = node.brothers_next; + _remove_type1_node (node); + _add_in_r_prime (node); + node = next; + } + } + #endif + + // 4. Adjust(Q, P, P) + _adjust (_p, _p); + + // For now we can't have type2 node other than R' (left for reference) + #if false + // 5a. if LM is empty then goto Step 6 + if (_lm_head != null) { + // 5b. M <- Head(LM); LM <- Tail(LM) + Type2Node m = _lm_head; + + // 5c. Delete M from Q + _remove_type2_node (m); + + // 5d. I nsert(Q, M.element) + offer (m.data); + + // 5e. For all children N of M do make M a child of R' of Q + node = m.type1_children_head; + while (node != null) { + Type1Node next = node.brothers_next; + _remove_type1_node (node); + _add_in_r_prime (node); + node = next; + } + } + #endif + + // 6. While among the children of R' there exist any two different nodes Ri and Rj + // such that Ri.degree = Rj.degree do Link(Q, Ri, Rj) + while (_check_linkable ()) {} + + // 7. Return MinElement + return min; + } + + /** + * {@inheritDoc} + */ + public int drain (Collection recipient, int amount = -1) { + if (amount == -1) { + amount = this._size; + } + for (int i = 0; i < amount; i++) { + if (this._size == 0) { + return i; + } + recipient.add (poll ()); + } + return amount; + } + + /** + * {@inheritDoc} + */ + public override int size { + get { return _size; } + } + + /** + * {@inheritDoc} + */ + public override bool contains (G item) { + foreach (G an_item in this) { + if (compare_func (item, an_item) == 0) { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public override bool add (G item) { + return offer (item); + } + + /** + * {@inheritDoc} + */ + public override bool remove (G item) { + #if DEBUG + _dump ("Start remove: %s".printf ((string) item)); + #endif + + Iterator iterator = new Iterator (this); + while (iterator.next ()) { + G an_item = iterator.get (); + if (compare_func (item, an_item) == 0) { + iterator.remove (); + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public override void clear () { + _size = 0; + _r = null; + _r_prime = null; + _lm_head = null; + _lm_tail = null; + _p = null; + _a = new Type1Node?[0]; + _lp_head = null; + _lp_tail = null; + _b = new bool[0]; + _ll_head = null; + _ll_tail = null; + _iter_head = null; + _iter_tail = null; + } + + /** + * {@inheritDoc} + */ + public override Vala.Iterator iterator () { + return new Iterator (this); + } + + private inline int _compare (Node node1, Node node2) { + // Assume there can't be two nodes pending drop + if (node1.pending_drop) { + return -1; + } else if (node2.pending_drop) { + return 1; + } else { + return compare_func (node1.data, node2.data); + } + } + + private inline void _swap_data (Node node1, Node node2) { + #if DEBUG + _dump ("Before swap: %p(%s) %p(%s)".printf(node1, (string)node1.data, node2, (string)node2.data)); + #endif + G temp_data = (owned) node1.data; + bool temp_pending_drop = node1.pending_drop; + node1.data = (owned) node2.data; + node1.pending_drop = node2.pending_drop; + node2.data = (owned) temp_data; + node2.pending_drop = temp_pending_drop; + + if (node1.iter_next == node2) { // Before swap: N1 N2 + unowned Node temp_iter_prev = node1.iter_prev; + unowned Node temp_iter_next = node2.iter_next; + + node1.iter_prev = node2; + node1.iter_next = temp_iter_next; + node2.iter_prev = temp_iter_prev; + node2.iter_next = node1; + } else if (node1.iter_prev == node2) { // Before swap: N2 N1 + unowned Node temp_iter_prev = node2.iter_prev; + unowned Node temp_iter_next = node1.iter_next; + + node1.iter_prev = temp_iter_prev; + node1.iter_next = node2; + node2.iter_prev = node1; + node2.iter_next = temp_iter_next; + } else { + unowned Node temp_iter_prev = node1.iter_prev; + unowned Node temp_iter_next = node1.iter_next; + + node1.iter_prev = node2.iter_prev; + node1.iter_next = node2.iter_next; + node2.iter_prev = temp_iter_prev; + node2.iter_next = temp_iter_next; + } + + if (node2 == _iter_head) { + _iter_head = node1; + } else if (node1 == _iter_head) { + _iter_head = node2; + } + if (node2 == _iter_tail) { + _iter_tail = node1; + } else if (node1 == _iter_tail) { + _iter_tail = node2; + } + + if (node1.iter_prev != null) { + node1.iter_prev.iter_next = node1; + } + if (node1.iter_next != null) { + node1.iter_next.iter_prev = node1; + } + if (node2.iter_prev != null) { + node2.iter_prev.iter_next = node2; + } + if (node2.iter_next != null) { + node2.iter_next.iter_prev = node2; + } + + #if DEBUG + _dump ("After swap: %p(%s) %p(%s)".printf(node1, (string)node1.data, node2, (string)node2.data)); + #endif + } + + private inline void _move_data (Node target, Node source) { + #if DEBUG + _dump ("Before move: %p(%s) <- %p(%s)".printf(target, (string)target.data, source, (string)source.data)); + #endif + + if (target.iter_next != null) { + target.iter_next.iter_prev = target.iter_prev; + } else if (_iter_tail == target) { + _iter_tail = target.iter_prev; + } + if (target.iter_prev != null) { + target.iter_prev.iter_next = target.iter_next; + } else if (_iter_head == target) { + _iter_head = target.iter_next; + } + + target.data = source.data; + target.pending_drop = source.pending_drop; + target.iter_next = source.iter_next; + target.iter_prev = source.iter_prev; + source.iter_next = null; + source.iter_prev = null; + + if (target.iter_next != null) { + target.iter_next.iter_prev = target; + } else if (_iter_tail == source) { + _iter_tail = target; + } + if (target.iter_prev != null) { + target.iter_prev.iter_next = target; + } else if (_iter_head == source) { + _iter_head = target; + } + #if DEBUG + _dump ("After move:"); + #endif + } + + private void _link (owned Type1Node ri, owned Type1Node rj) { + assert (ri.degree () == rj.degree ()); + + // Delete the subtrees rooted at Ri and Rj from Q + _remove_type1_node (ri, false); + _remove_type1_node (rj, false); + + // If Ri.element > Rj.element then Swap(Ri,Rj) + if (_compare (ri, rj) > 0) { + Type1Node temp = ri; + ri = rj; + rj = temp; + } + + // Make Rj the last child of Ri + _add_to (rj, ri); + + // Make Ri (whose degree now = d+1) a child of R' of Q + _add_in_r_prime (ri); + } + + private void _add (Type1Node n) { + // Make N a child of R' of Q + _add_in_r_prime (n); + + // If N.element < R'.element then Swap(N.element, R'.element) + if (_compare (n, _r_prime) < 0) { + _swap_data (n, _r_prime); + } + + // If R'.element < R.element then Swap(R'.element, R.element) + if (_compare (_r_prime, _r) < 0) { + _swap_data (_r_prime, _r); + } + + // If among the children of R' there exist any two different nodes Ri and Rj + // such that Ri.degree = Rj.degree then Link(Q, Ri, Rj) + _check_linkable (); + + #if DEBUG + _dump ("End _add: %p(%s)".printf (n, (string) n.data)); + #endif + } + + private bool _check_linkable () { + #if DEBUG + _dump ("Start _check_linkable:"); + #endif + + if (_lp_head != null) { + NodePair pair = _lp_head; + _link (pair.node1, pair.node2); + return true; + } + return false; + } + + private Node _re_insert (owned Type1Node n) { + assert (n != _r); + + #if DEBUG + _dump ("Start _re_insert: %p(%s)".printf (n, (string) n.data)); + #endif + + //Parent <- N.parent + Node parent = n.parent; + + // Delete the subtree rooted at N from Q + _remove_type1_node (n, false); + + // Add(Q, N) + _add (n); + + // Return Parent + return parent; + } + + private void _adjust (Type1Node p1, Type1Node p2) { + // If M.lost <= 1 for all nodes M in Q then return + if (_ll_head == null) { + return; + } + + #if DEBUG + _dump ("Start _adjust: %p(%s), %p(%s)".printf (p1, (string) p1.data, p2, (string) p2.data)); + #endif + + // If P1.lost > P2.lost then M <- P1 else M <- P2 + Type1Node m; + if (p1.lost > p2.lost) { + m = p1; + } else { + m = p2; + } + + // If M.lost <= 1 then M <- M' for some node M' in Q such that M'.lost > 1 + if (m.lost <= 1) { + m = _ll_head; + if (_ll_head.ll_next != null) { + _ll_head.ll_next.ll_prev = null; + } + _ll_head = _ll_head.ll_next; + } + + // P <- ReInsert(Q, M) + _p = (Type1Node) _re_insert (m); + + #if DEBUG + _dump ("End _adjust: %p(%s), %p(%s)".printf (p1, (string) p1.data, p2, (string) p2.data)); + #endif + } + + private void _delete (Node n) { + // DecreaseKey(Q, N, infinite) + _decrease_key (n); + + // DeleteMin(Q) + poll (); + } + + private void _decrease_key (Node n) { + #if DEBUG + _dump ("Start _decrease_key: %p(%s)".printf (n, (string) n.data)); + #endif + + if (n == _r || _r_prime == null) { + return; + } + + n.pending_drop = true; + + // If (N = R or R') and (R'.element < R.element) then + // Swap(R'.element, R.element); return + if (n == _r_prime && _compare (_r_prime, _r) < 0) { + _swap_data (_r_prime, _r); + return; + } + + // For now we can't have type2 node other than R' (left for reference) + #if false + // If (N is of type II) and (N.element < N.parent.element) then + // Swap(N.element, N.parent.element); N <- N.parent + if (n is Type2Node && _compare (n, n.parent) < 0) { + _swap_data (n, n.parent); + n = n.parent; + } + #endif + + // Can't occur as we made n be the most little (left for reference) + #if false + // If N.element >= N.parent.element then return + if (n.parent != null && _compare (n, n.parent) >= 0) { + return; + } + #endif + + // P' <- ReInsert(Q, N) + Node p_prime = _re_insert ((Type1Node) n); + + if (p_prime is Type2Node) { + // Adjust(Q, P, P); + _adjust (_p, _p); + } else { + // Adjust(Q, P, P'); + _adjust (_p, (Type1Node) p_prime); + } + } + + private void _add_to (Type1Node node, Type1Node parent) { + parent.add (node); + parent.lost = 0; + } + + private void _add_in_r_prime (Type1Node node) { + #if DEBUG + _dump ("Start _add_in_r_prime: %p(%s)".printf (node, (string) node.data)); + #endif + + int degree = node.degree (); + + Type1Node? insertion_point = null; + if (degree < _a.length) { + insertion_point = _a[degree]; + } + + if (insertion_point == null) { + if (_r_prime.type1_children_tail != null) { + node.brothers_prev = _r_prime.type1_children_tail; + _r_prime.type1_children_tail.brothers_next = node; + } else { + _r_prime.type1_children_head = node; + } + _r_prime.type1_children_tail = node; + } else { + if (insertion_point.brothers_prev != null) { + insertion_point.brothers_prev.brothers_next = node; + node.brothers_prev = insertion_point.brothers_prev; + } else { + _r_prime.type1_children_head = node; + } + node.brothers_next = insertion_point; + insertion_point.brothers_prev = node; + } + node.parent = _r_prime; + + // Maintain A, B and LP + if (degree >= _a.length) { + _a.resize (degree + 1); + _b.resize (degree + 1); + } + + // If there is already a child of such degree + if (_a[degree] == null) { + _b[degree] = true; + } else { + // Else if there is an odd number of child of such degree + if (_b[degree]) { + // Make a pair + NodePair pair = new NodePair (node, node.brothers_next); + node.brothers_next.pair = pair; + node.pair = pair; + if (_lp_head == null) { + _lp_head = pair; + _lp_tail = pair; + } else { + pair.lp_prev = _lp_tail; + _lp_tail.lp_next = pair; + _lp_tail = pair; + } + // There is now an even number of child of such degree + _b[degree] = false; + } else { + _b[degree] = true; + } + } + _a[degree] = node; + + #if DEBUG + _dump ("End _add_in_r_prime: %p(%s)".printf (node, (string) node.data)); + #endif + } + + private void _remove_type1_node (Type1Node node, bool with_iteration) { + #if DEBUG + _dump ("Start _remove_type1_node: %p(%s)".printf (node, (string) node.data)); + #endif + + if (node.parent == _r_prime) { + _updated_degree (node, false); + } else { + // Maintain LL + if (node.ll_prev != null) { + node.ll_prev.ll_next = node.ll_next; + } else if (_ll_head == node) { + _ll_head = node.ll_next; + } + if (node.ll_next != null) { + node.ll_next.ll_prev = node.ll_prev; + } else if (_ll_tail == node) { + _ll_tail = node.ll_prev; + } + + if (node.parent != null) { + if (node.parent.parent == _r_prime) { + _updated_degree ((Type1Node) node.parent, true); + } else if (node.parent.parent != null) { + Type1Node parent = (Type1Node) node.parent; + + // Increment parent's lost count + parent.lost++; + + // And add it to LL if needed + if (parent.lost > 1) { + if (_ll_tail != null) { + parent.ll_prev = _ll_tail; + _ll_tail.ll_next = parent; + } else { + _ll_head = parent; + } + _ll_tail = parent; + } + } + } + } + + // Check whether removed node is P + if (node == _p) { + _p = _r; + } + + // Maintain brothers list + node.remove (); + + // Maintain iteration + if (with_iteration) { + if (node.iter_prev != null) { + node.iter_prev.iter_next = node.iter_next; + } else if (_iter_head == node) { + _iter_head = node.iter_next; + } + if (node.iter_next != null) { + node.iter_next.iter_prev = node.iter_prev; + } else if (_iter_tail == node) { + _iter_tail = node.iter_prev; + } + } + #if DEBUG + _dump ("End _remove_type1_node: %p(%s)".printf (node, (string) node.data)); + #endif + } + + private void _updated_degree (Type1Node node, bool child_removed) { + int degree = node.degree (); + + // Ensure proper sizes of A and B + if (degree >= _a.length) { + _a.resize (degree + 1); + _b.resize (degree + 1); + } + + // Maintain A and B + if (child_removed && _a[degree - 1] == null) { + _a[degree - 1] = node; + _b[degree - 1] = ! _b[degree - 1]; + } + + _b[degree] = ! _b[degree]; + if (_a[degree] == node) { + Type1Node next = node.brothers_next; + if (next != null && next.degree () == degree) { + _a[degree] = next; + } else { + _a[degree] = null; + + int i = _a.length - 1; + while (i >= 0 && _a[i] == null) { + i--; + } + _a.resize (i + 1); + _b.resize (i + 1); + } + } + + // Maintain LP + if (node.pair != null) { + NodePair pair = node.pair; + Type1Node other = (pair.node1 == node ? pair.node2 : pair.node1); + node.pair = null; + other.pair = null; + if (pair.lp_prev != null) { + pair.lp_prev.lp_next = pair.lp_next; + } else { + _lp_head = pair.lp_next; + } + if (pair.lp_next != null) { + pair.lp_next.lp_prev = pair.lp_prev; + } else { + _lp_tail = pair.lp_prev; + } + } + } + + private void _remove_type2_node (Type2Node node, bool with_iteration) { + #if DEBUG + _dump ("Start _remove_type2_node: %p(%s)".printf (node, (string) node.data)); + #endif + ((Type1Node) node.parent).type2_child = null; + node.parent = null; + + // For now we can't have type2 node other than R' (left for reference) + #if false + // Maintain LM + if (node != _r_prime) { + if (node.lm_prev != null) { + node.lm_prev.lm_next = node.lm_next; + } else if (_lm_head == node) { + _lm_head = node.lm_next; + } + if (node.lm_next != null) { + node.lm_next.lm_prev = node.lm_prev; + } else if (_lm_tail == node) { + _lm_tail = node.lm_prev; + } + node.lm_next = null; + node.lm_prev = null; + } + #endif + + // Maintain iteration + if (with_iteration) { + if (node.iter_prev != null) { + node.iter_prev.iter_next = node.iter_next; + } else if (_iter_head == node) { + _iter_head = node.iter_next; + } + if (node.iter_next != null) { + node.iter_next.iter_prev = node.iter_prev; + } else if (_iter_tail == node) { + _iter_tail = node.iter_prev; + } + } + #if DEBUG + _dump ("End _remove_type2_node: %p(%s)".printf (node, (string) node.data)); + #endif + } + + #if DEBUG + public void _dump (string message) { + stdout.printf (">>>> %s\n", message); + + stdout.printf ("A.length = %d:", _a.length); + foreach (Node? node in _a) { + stdout.printf (" %p(%s);", node, node != null ? (string) node.data : null); + } + stdout.printf ("\n"); + + stdout.printf ("B.length = %d:", _b.length); + foreach (bool even in _b) { + stdout.printf (" %s;", even.to_string ()); + } + stdout.printf ("\n"); + + stdout.printf ("LP:"); + unowned NodePair? pair = _lp_head; + while (pair != null) { + stdout.printf (" (%p(%s),%p(%s));", pair.node1, (string) pair.node1.data, pair.node2, (string) pair.node2.data); + pair = pair.lp_next; + } + stdout.printf ("\n"); + + stdout.printf ("LL:"); + unowned Type1Node? node = _ll_head; + while (node != null) { + stdout.printf (" %p(%s);", node, (string) node.data); + node = node.ll_next; + } + stdout.printf ("\n"); + + stdout.printf ("ITER:"); + unowned Node? inode_prev = null; + unowned Node? inode = _iter_head; + while (inode != null) { + stdout.printf (" %p(%s);", inode, (string) inode.data); + assert (inode.iter_prev == inode_prev); + inode_prev = inode; + inode = inode.iter_next; + } + stdout.printf ("\n"); + + stdout.printf ("%s\n", _r != null ? _r.to_string () : null); + + stdout.printf ("\n"); + } + #endif + + private abstract class Node { + public G data; + public weak Node? parent = null; + + public int type1_children_count; + public Type1Node? type1_children_head = null; + public Type1Node? type1_children_tail = null; + + public unowned Node? iter_prev; + public unowned Node? iter_next = null; + + public bool pending_drop; + + protected Node (G data, ref unowned Node? head, ref unowned Node? tail) { + this.data = data; + iter_prev = tail; + tail = this; + if (iter_prev != null) { + iter_prev.iter_next = this; + } + if (head == null) { + head = this; + } + } + + public inline int degree () { + return type1_children_count; + } + + #if DEBUG + public string children_to_string (int level = 0) { + StringBuilder builder = new StringBuilder (); + bool first = true; + Type1Node child = type1_children_head; + while (child != null) { + if (!first) { + builder.append (",\n"); + } + first = false; + builder.append (child.to_string (level)); + child = child.brothers_next; + } + return builder.str; + } + + public abstract string to_string (int level = 0); + #endif + } + + private class Type1Node : Node { + public uint lost; + public weak Type1Node? brothers_prev = null; + public Type1Node? brothers_next = null; + public Type2Node? type2_child = null; + public weak Type1Node? ll_prev = null; + public Type1Node? ll_next = null; + public weak NodePair? pair = null; + + public Type1Node (G data, ref unowned Node? head, ref unowned Node? tail) { + base (data, ref head, ref tail); + } + + public inline void add (Type1Node node) { + node.parent = this; + if (type1_children_head == null) { + type1_children_head = node; + } else { + node.brothers_prev = type1_children_tail; + } + if (type1_children_tail != null) { + type1_children_tail.brothers_next = node; + } + type1_children_tail = node; + type1_children_count++; + } + + public inline void remove () { + if (brothers_prev == null) { + parent.type1_children_head = brothers_next; + } else { + brothers_prev.brothers_next = brothers_next; + } + if (brothers_next == null) { + parent.type1_children_tail = brothers_prev; + } else { + brothers_next.brothers_prev = brothers_prev; + } + parent.type1_children_count--; + parent = null; + brothers_prev = null; + brothers_next = null; + } + + #if DEBUG + public override string to_string (int level = 0) { + StringBuilder builder = new StringBuilder (); + builder.append (string.nfill (level, '\t')); + builder.append ("("); + builder.append_printf("%p(%s)/%u", this, (string)data, lost); + if (type1_children_head != null || type2_child != null) { + builder.append (":\n"); + } + if (type1_children_head != null) { + builder.append (children_to_string (level + 1)); + } + if (type1_children_head != null && type2_child != null) { + builder.append (",\n"); + } + if (type2_child != null) { + builder.append (type2_child.to_string (level + 1)); + } + if (type1_children_head != null || type2_child != null) { + builder.append ("\n"); + builder.append (string.nfill (level, '\t')); + } + builder.append (")"); + return builder.str; + } + #endif + } + + private class Type2Node : Node { + // For now we can't have type2 node other than R' (left for reference) + #if false + public weak Type2Node? lm_prev = null; + public Type2Node? lm_next = null; + #endif + + public Type2Node (G data, ref unowned Node? head, ref unowned Node? tail) { + base (data, ref head, ref tail); + } + + #if DEBUG + public override string to_string (int level = 0) { + StringBuilder builder = new StringBuilder (); + builder.append (string.nfill (level, '\t')); + builder.append_printf ("[%p(%s)", this, (string)data); + if (type1_children_head != null) { + builder.append (":\n"); + builder.append (children_to_string (level + 1)); + builder.append ("\n"); + builder.append (string.nfill (level, '\t')); + } + builder.append ("]"); + return builder.str; + } + #endif + } + + private class DummyNode : Node { + public DummyNode (ref unowned Node? prev_next, ref unowned Node? next_prev, Node? iter_prev, Node? iter_next) { + #if DEBUG + base ("<>", ref prev_next, ref next_prev); + #else + base (null, ref prev_next, ref next_prev); + #endif + this.iter_prev = iter_prev; + this.iter_next = iter_next; + prev_next = next_prev = this; + } + + #if DEBUG + public override string to_string (int level = 0) { + StringBuilder builder = new StringBuilder (); + builder.append (string.nfill (level, '\t')); + builder.append ("%p<>".printf(this)); + return builder.str; + } + #endif + } + + private class NodePair { + public weak NodePair? lp_prev = null; + public NodePair? lp_next = null; + public Type1Node node1 = null; + public Type1Node node2 = null; + + public NodePair (Type1Node node1, Type1Node node2) { + this.node1 = node1; + this.node2 = node2; + } + } + + private class Iterator : Object, Traversable, Vala.Iterator { + private PriorityQueue queue; + private unowned Node? position; + private unowned Node? previous; + private int stamp; + + public Iterator (PriorityQueue queue) { + this.queue = queue; + this.position = null; + this.previous = null; + this.stamp = queue._stamp; + } + + public bool next () { + unowned Node? next = _get_next_node (); + if (next != null) { + previous = position; + position = next; + } + return next != null; + } + + public bool has_next () { + return _get_next_node () != null; + } + + private inline unowned Node? _get_next_node () { + if (position != null) { + return position.iter_next; + } else { + return (previous != null) ? previous.iter_next : queue._iter_head; + } + } + + public new G get () { + assert (stamp == queue._stamp); + assert (position != null); + return position.data; + } + + public void remove () { + assert (stamp == queue._stamp); + assert (position != null); + DummyNode dn; + if (previous != null) { + dn = new DummyNode (ref previous.iter_next, ref position.iter_prev, previous, position); + } else { + dn = new DummyNode (ref queue._iter_head, ref position.iter_prev, null, position); + } + queue._delete (position); + position = null; + if (previous != null) { + previous.iter_next = dn.iter_next; + } + if (dn == queue._iter_head) { + queue._iter_head = dn.iter_next; + } + if (dn.iter_next != null) { + dn.iter_next.iter_prev = previous; + } + if (dn == queue._iter_tail) { + queue._iter_tail = previous; + } + stamp++; + assert (stamp == queue._stamp); + } + + public bool read_only { get { return false; } } + + public bool valid { get { return position != null; } } + + public bool foreach (ForallFunc f) { + if (position == null) { + position = (previous != null) ? previous.iter_next : queue._iter_head; + } + if (position == null) { + return true; + } + if (!f (position.data)) { + return false; + } + while (position.iter_next != null) { + previous = position; + position = position.iter_next; + if (!f (position.data)) { + return false; + } + } + return true; + } + } +} diff --git a/gee/queue.vala b/gee/queue.vala new file mode 100644 index 000000000..df678d1d3 --- /dev/null +++ b/gee/queue.vala @@ -0,0 +1,115 @@ +/* queue.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * A collection designed for holding elements prior to processing. + * + * Although all Queue implementations do not limit the amount of elements they + * can contain, this interface supports for capacity-bounded queues. When + * capacity is not bound, then the {@link capacity} and + * {@link remaining_capacity} both return {@link UNBOUNDED_CAPACITY}. + * + * This interface defines methods that will never fail whatever the state of + * the queue is. For capacity-bounded queues, those methods will either return + * ``false`` or ``null`` to specify that the insert or retrieval did not occur + * because the queue was full or empty. + * + * Queue implementations are not limited to First-In-First-Out behavior and can + * propose different ordering of their elements. Each Queue implementation have + * to specify how it orders its elements. + * + * Queue implementations do not allow insertion of ``null`` elements, although + * some implementations, such as {@link LinkedList}, do not prohibit insertion + * of ``null``. Even in the implementations that permit it, ``null`` should not be + * inserted into a Queue, as ``null`` is also used as a special return value by + * the poll method to indicate that the queue contains no elements. + */ +[GenericAccessors] +public interface Vala.Queue : Collection { + + /** + * The unbounded capacity value. + */ + public const int UNBOUNDED_CAPACITY = -1; + + /** + * The capacity of this queue (or ``null`` if capacity is not bound). + */ + public abstract int capacity { get; } + + /** + * The remaining capacity of this queue (or ``null`` if capacity is not + * bound). + */ + public abstract int remaining_capacity { get; } + + /** + * Specifies whether this queue is full. + */ + public abstract bool is_full { get; } + + /** + * Offers the specified element to this queue. + * + * @param element the element to offer to the queue + * + * @return ``true`` if the element was added to the queue + */ + public virtual bool offer (G element) { + return add (element); + } + + /** + * Peeks (retrieves, but not remove) an element from this queue. + * + * @return the element peeked from the queue (or ``null`` if none was + * available) + */ + public abstract G? peek (); + + /** + * Polls (retrieves and remove) an element from this queue. + * + * @return the element polled from the queue (or ``null`` if none was + * available) + */ + public abstract G? poll (); + + /** + * Drains the specified amount of elements from this queue in the specified + * recipient collection. + * + * @param recipient the recipient collection to drain the elements to + * @param amount the amount of elements to drain + * + * @return the amount of elements that were actually drained + */ + public virtual int drain (Collection recipient, int amount = -1) { + G? item = null; + int drained = 0; + while((amount == -1 || --amount >= 0) && (item = poll ()) != null) { + recipient.add (item); + drained++; + } + return drained; + } +} diff --git a/gee/readonlybidirlist.vala b/gee/readonlybidirlist.vala new file mode 100644 index 000000000..328795045 --- /dev/null +++ b/gee/readonlybidirlist.vala @@ -0,0 +1,73 @@ +/* readonlybidirlist.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +internal class Vala.ReadOnlyBidirList : Vala.ReadOnlyList, BidirList { + + /** + * Constructs a read-only list that mirrors the content of the specified + * list. + * + * @param list the list to decorate. + */ + public ReadOnlyBidirList (BidirList list) { + base (list); + } + + /** + * {@inheritDoc} + */ + public BidirListIterator bidir_list_iterator () { + return new Iterator (((Vala.BidirList) _collection).bidir_list_iterator ()); + } + + /** + * The read-only view of this list. + */ + public virtual new BidirList read_only_view { owned get { return this; } } + + private class Iterator : ReadOnlyList.Iterator, BidirIterator, BidirListIterator { + public Iterator (ListIterator iterator) { + base (iterator); + } + + public bool previous () { + return ((BidirIterator) _iter).previous (); + } + + public bool has_previous () { + return ((BidirIterator) _iter).has_previous (); + } + + public bool first () { + return ((BidirIterator) _iter).first (); + } + + public bool last () { + return ((BidirIterator) _iter).last (); + } + + public void insert (G item) { + assert_not_reached (); + } + } +} + diff --git a/gee/readonlybidirsortedmap.vala b/gee/readonlybidirsortedmap.vala new file mode 100644 index 000000000..a50c0364f --- /dev/null +++ b/gee/readonlybidirsortedmap.vala @@ -0,0 +1,80 @@ +/* readonlybidirsortedmap.vala + * + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Read-only view for {@link BidirSortedMap} collections. + * + * This class decorates any class which implements the {@link BidirSortedMap} + * interface by making it read only. Any method which normally modify data will + * throw an error. + * + * @see BidirSortedMap + */ +internal class Vala.ReadOnlyBidirSortedMap : ReadOnlySortedMap, BidirSortedMap { + /** + * Constructs a read-only map that mirrors the content of the specified map. + * + * @param set the set to decorate. + */ + public ReadOnlyBidirSortedMap (BidirSortedMap map) { + base (map); + } + + /** + * {@inheritDoc} + */ + public Vala.BidirMapIterator bidir_map_iterator () { + return new BidirMapIterator ((_map as BidirSortedMap).bidir_map_iterator ()); + } + + /** + * {@inheritDoc} + */ + public new BidirSortedMap read_only_view { + owned get { + return this; + } + } + + protected class BidirMapIterator : Vala.ReadOnlyMap.MapIterator, Vala.BidirMapIterator { + public BidirMapIterator (Vala.BidirMapIterator iterator) { + base (iterator); + } + + public bool first () { + return (_iter as Vala.BidirMapIterator).first (); + } + + public bool previous () { + return (_iter as Vala.BidirMapIterator).previous (); + } + + public bool has_previous () { + return (_iter as Vala.BidirMapIterator).has_previous (); + } + + public bool last () { + return (_iter as Vala.BidirMapIterator).last (); + } + } +} + diff --git a/gee/readonlybidirsortedset.vala b/gee/readonlybidirsortedset.vala new file mode 100644 index 000000000..f5b4ad716 --- /dev/null +++ b/gee/readonlybidirsortedset.vala @@ -0,0 +1,71 @@ +/* readonlybidirsortedset.vala + * + * Copyright (C) 2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Read-only view for {@link BidirSortedSet} collections. + * + * This class decorates any class which implements the {@link BidirSortedSet} + * interface by making it read only. Any method which normally modify data will + * throw an error. + * + * @see BidirSortedSet + */ +internal class Vala.ReadOnlyBidirSortedSet : ReadOnlySortedSet, BidirSortedSet { + /** + * Constructs a read-only set that mirrors the content of the specified set. + * + * @param set the set to decorate. + */ + public ReadOnlyBidirSortedSet (BidirSortedSet set) { + base (set); + } + + /** + * {@inheritDoc} + */ + public Vala.BidirIterator bidir_iterator () { + return new BidirIterator ((_collection as BidirSortedSet).bidir_iterator ()); + } + + protected class BidirIterator : Vala.ReadOnlyCollection.Iterator, Vala.BidirIterator { + public BidirIterator (Vala.BidirIterator iterator) { + base (iterator); + } + + public bool first () { + return (_iter as Vala.BidirIterator).first (); + } + + public bool previous () { + return (_iter as Vala.BidirIterator).previous (); + } + + public bool has_previous () { + return (_iter as Vala.BidirIterator).has_previous (); + } + + public bool last () { + return (_iter as Vala.BidirIterator).last (); + } + } +} + diff --git a/gee/readonlycollection.vala b/gee/readonlycollection.vala new file mode 100644 index 000000000..f810f206f --- /dev/null +++ b/gee/readonlycollection.vala @@ -0,0 +1,235 @@ +/* readonlycollection.vala + * + * Copyright (C) 2007-2008 Jürg Billeter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Jürg Billeter + */ + +using GLib; + +/** + * Read-only view for {@link Collection} collections. + * + * This class decorates any class which implements the {@link Collection} + * interface by making it read only. Any method which normally modify data will + * throw an error. + * + * @see Collection + */ +internal class Vala.ReadOnlyCollection : Object, Traversable, Iterable, Collection { + + /** + * {@inheritDoc} + */ + public int size { + get { return _collection.size; } + } + + /** + * {@inheritDoc} + */ + public bool is_empty { + get { return _collection.is_empty; } + } + + /** + * {@inheritDoc} + */ + public bool read_only { + get { return true; } + } + + protected Collection _collection; + + /** + * Constructs a read-only collection that mirrors the content of the + * specified collection. + * + * @param collection the collection to decorate. + */ + public ReadOnlyCollection (Collection collection) { + this._collection = collection; + } + + /** + * {@inheritDoc} + */ + public bool foreach (ForallFunc f) { + return _collection.foreach (f); + } + + /** + * {@inheritDoc} + */ + public Vala.Iterator stream (owned StreamFunc f) { + return _collection.stream ((owned)f); + } + + /** + * {@inheritDoc} + */ + public Vala.Iterator filter (owned Predicate f) { + return _collection.filter ((owned)f); + } + + /** + * {@inheritDoc} + */ + public Vala.Iterator chop (int offset, int length = -1) { + return _collection.chop (offset, length); + } + + /** + * {@inheritDoc} + */ + public Type element_type { + get { return typeof (G); } + } + + /** + * {@inheritDoc} + */ + public Vala.Iterator iterator () { + return new Iterator (_collection.iterator ()); + } + + /** + * {@inheritDoc} + */ + public bool contains (G item) { + return _collection.contains (item); + } + + /** + * Unimplemented method (read only collection). + */ + public bool add (G item) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only collection). + */ + public bool remove (G item) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only collection). + */ + public void clear () { + assert_not_reached (); + } + + /** + * Unimplemented method (read only collection). + */ + public bool add_all (Collection collection) { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public bool contains_all (Collection collection) { + return _collection.contains_all (collection); + } + + /** + * Unimplemented method (read only collection). + */ + public bool remove_all (Collection collection) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only collection). + */ + public bool retain_all (Collection collection) { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public G[] to_array () { + return _collection.to_array (); + } + + protected class Iterator : Object, Traversable, Vala.Iterator { + protected Vala.Iterator _iter; + + public Iterator (Vala.Iterator iterator) { + _iter = iterator; + } + + public bool next () { + return _iter.next (); + } + + public bool has_next () { + return _iter.has_next (); + } + + public new G get () { + return _iter.get (); + } + + public void remove () { + assert_not_reached (); + } + + public bool valid { + get { + return _iter.valid; + } + } + + public bool read_only { + get { + return true; + } + } + + public Type element_type { + get { return typeof (G); } + } + + public bool foreach (ForallFunc f) { + return _iter.foreach (f); + } + + public Vala.Iterator stream (owned StreamFunc f) { + return _iter.stream ((owned)f); + } + + public Vala.Iterator filter (owned Predicate f) { + return _iter.filter ((owned)f); + } + + public Vala.Iterator chop (int offset, int length = -1) { + return _iter.chop ( offset, length); + } + } + + public virtual Collection read_only_view { + owned get { return this; } + } + +} + diff --git a/gee/readonlylist.vala b/gee/readonlylist.vala new file mode 100644 index 000000000..de44c65c6 --- /dev/null +++ b/gee/readonlylist.vala @@ -0,0 +1,149 @@ +/* readonlylist.vala + * + * Copyright (C) 2007-2008 Jürg Billeter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Jürg Billeter + */ + +using GLib; + +/** + * Read-only view for {@link List} collections. + * + * This class decorates any class which implements the {@link List} + * interface by making it read only. Any method which normally modify data will + * throw an error. + * + * @see List + */ +internal class Vala.ReadOnlyList : Vala.ReadOnlyCollection, List { + + /** + * Constructs a read-only list that mirrors the content of the specified + * list. + * + * @param list the list to decorate. + */ + public ReadOnlyList (List list) { + base (list); + } + + /** + * {@inheritDoc} + */ + public ListIterator list_iterator () { + return new Iterator (((Vala.List) _collection).list_iterator ()); + } + + /** + * {@inheritDoc} + */ + public int index_of (G item) { + return ((Vala.List) _collection).index_of (item); + } + + /** + * Unimplemented method (read only list). + */ + public void insert (int index, G item) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only list). + */ + public G remove_at (int index) { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public new G? get (int index) { + return ((Vala.List) _collection).get (index); + } + + /** + * Unimplemented method (read only list). + */ + public new void set (int index, G o) { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public List? slice (int start, int stop) { + return ((Vala.List) _collection).slice (start, stop); + } + + /** + * {@inheritDoc} + */ + public G? first () { + return ((Vala.List) _collection).first (); + } + + /** + * {@inheritDoc} + */ + public G? last () { + return ((Vala.List) _collection).last (); + } + + /** + * Unimplemented method (read only list). + */ + public void insert_all (int index, Collection collection) { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public void sort (owned CompareDataFunc? compare = null) { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public virtual new List read_only_view { + owned get { return this; } + } + + + protected class Iterator : ReadOnlyCollection.Iterator, ListIterator { + public Iterator (ListIterator iterator) { + base (iterator); + } + + public new void set (G item) { + assert_not_reached (); + } + + public void add (G item) { + assert_not_reached (); + } + + public int index () { + return ((ListIterator) _iter).index (); + } + } +} + diff --git a/gee/readonlymap.vala b/gee/readonlymap.vala new file mode 100644 index 000000000..c14ebbed0 --- /dev/null +++ b/gee/readonlymap.vala @@ -0,0 +1,297 @@ +/* readonlymap.vala + * + * Copyright (C) 2007-2008 Jürg Billeter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Jürg Billeter + */ + +using GLib; + +/** + * Read-only view for {@link Map} collections. + * + * This class decorates any class which implements the {@link Map} interface + * by making it read only. Any method which normally modify data will throw an + * error. + * + * @see Map + */ +internal class Vala.ReadOnlyMap : Object, Traversable>, Iterable>, Map { + + /** + * {@inheritDoc} + */ + public int size { + get { return _map.size; } + } + + /** + * {@inheritDoc} + */ + public bool is_empty { + get { return _map.is_empty; } + } + + /** + * {@inheritDoc} + */ + public bool read_only { + get { return true; } + } + + /** + * {@inheritDoc} + */ + public Set keys { + owned get { + return _map.keys; + } + } + + /** + * {@inheritDoc} + */ + public Collection values { + owned get { + return _map.values; + } + } + + /** + * {@inheritDoc} + */ + public Set> entries { + owned get { + return _map.entries; + } + } + + protected Map _map; + + /** + * Constructs a read-only map that mirrors the content of the specified map. + * + * @param map the map to decorate. + */ + public ReadOnlyMap (Map map) { + this._map = map; + } + + /** + * {@inheritDoc} + */ + public bool has_key (K key) { + return _map.has_key (key); + } + + /** + * {@inheritDoc} + */ + public bool contains (K key) { + return _map.has_key (key); + } + + /** + * {@inheritDoc} + */ + public bool has (K key, V value) { + return _map.has (key, value); + } + + /** + * {@inheritDoc} + */ + public new V? get (K key) { + return _map.get (key); + } + + /** + * Unimplemented method (read only map). + */ + public new void set (K key, V value) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only map). + */ + public bool unset (K key, out V? value = null) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only map). + */ + public bool remove (K key, out V? value = null) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only map). + */ + public void clear () { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public Vala.MapIterator map_iterator () { + return new MapIterator (_map.map_iterator ()); + } + + /** + * Unimplemented method (read only map). + */ + public void set_all (Map map) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only map). + */ + public bool unset_all (Map map) { + assert_not_reached (); + } + + /** + * Unimplemented method (read only map). + */ + public bool remove_all (Map map) { + assert_not_reached (); + } + + /** + * {@inheritDoc} + */ + public bool has_all (Map map) { + return _map.has_all (map); + } + + /** + * {@inheritDoc} + */ + public bool contains_all (Map map) { + return _map.has_all (map); + } + + public virtual Map read_only_view { + owned get { return this; } + } + + /** + * {@inheritDoc} + */ + public Type key_type { + get { return typeof (K); } + } + + /** + * {@inheritDoc} + */ + public Type value_type { + get { return typeof (V); } + } + + /** + * {@inheritDoc} + */ + public Type element_type { + get { return typeof (Map.Entry); } + } + + /** + * {@inheritDoc} + */ + public Iterator> iterator () { + return entries.iterator (); + } + + /** + * {@inheritDoc} + */ + public bool foreach (ForallFunc> f) { + return _map.foreach (f); + } + + /** + * {@inheritDoc} + */ + public Iterator stream (owned StreamFunc, A> f) { + return _map.stream ((owned) f); + } + + public Iterator> filter (owned Predicate> f) { + return _map.filter ((owned)f); + } + + public Iterator> chop (int offset, int length = -1) { + return _map.chop (offset, length); + } + + protected class MapIterator : Object, Vala.MapIterator { + protected Vala.MapIterator _iter; + + public MapIterator (Vala.MapIterator iterator) { + _iter = iterator; + } + + public bool next () { + return _iter.next (); + } + + public bool has_next () { + return _iter.has_next (); + } + + public K get_key () { + return _iter.get_key (); + } + + public V get_value () { + return _iter.get_value (); + } + + public void set_value (V value) { + assert_not_reached (); + } + + public void unset () { + assert_not_reached (); + } + + public bool read_only { + get { + return true; + } + } + + public bool mutable { + get { + return false; + } + } + + public bool valid { + get { + return _iter.valid; + } + } + } +} + diff --git a/gee/readonlyset.vala b/gee/readonlyset.vala new file mode 100644 index 000000000..f8b1f9d6c --- /dev/null +++ b/gee/readonlyset.vala @@ -0,0 +1,50 @@ +/* readonlyset.vala + * + * Copyright (C) 2007-2008 Jürg Billeter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Jürg Billeter + */ + +using GLib; + +/** + * Read-only view for {@link Set} collections. + * + * This class decorates any class which implements the {@link Set} interface + * by making it read only. Any method which normally modify data will throw an + * error. + * + * @see Set + */ +internal class Vala.ReadOnlySet : Vala.ReadOnlyCollection, Set { + + /** + * Constructs a read-only set that mirrors the content of the specified set. + * + * @param set the set to decorate. + */ + public ReadOnlySet (Set set) { + base (set); + } + + public virtual new Set read_only_view { + owned get { return this; } + } + +} + diff --git a/gee/readonlysortedmap.vala b/gee/readonlysortedmap.vala new file mode 100644 index 000000000..3b87455af --- /dev/null +++ b/gee/readonlysortedmap.vala @@ -0,0 +1,90 @@ +/* readonlysortedmap.vala + * + * Copyright (C) 2009-2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Read-only view for {@link SortedMap} collections. + * + * This class decorates any class which implements the {@link SortedMap} interface + * by making it read only. Any method which normally modify data will throw an + * error. + * + * @see SortedMap + */ +internal class Vala.ReadOnlySortedMap : ReadOnlyMap, SortedMap { + /** + * Constructs a read-only map that mirrors the content of the specified map. + * + * @param map the map to decorate. + */ + public ReadOnlySortedMap (Map map) { + base (map); + } + + /** + * {@inheritDoc} + */ + public SortedMap head_map (K before) { + return (_map as SortedMap).head_map (before).read_only_view; + } + + /** + * {@inheritDoc} + */ + public SortedMap tail_map (K after) { + return (_map as SortedMap).tail_map (after).read_only_view; + } + + /** + * {@inheritDoc} + */ + public SortedMap sub_map (K from, K to) { + return (_map as SortedMap).sub_map (from, to).read_only_view; + } + + /** + * {@inheritDoc} + */ + public SortedSet ascending_keys { + owned get { + return (_map as SortedMap).ascending_keys.read_only_view; + } + } + + /** + * {@inheritDoc} + */ + public SortedSet> ascending_entries { + owned get { + return (_map as SortedMap).ascending_entries.read_only_view; + } + } + + /** + * {@inheritDoc} + */ + public new SortedMap read_only_view { + owned get { + return this; + } + } +} + diff --git a/gee/readonlysortedset.vala b/gee/readonlysortedset.vala new file mode 100644 index 000000000..85cd776b4 --- /dev/null +++ b/gee/readonlysortedset.vala @@ -0,0 +1,123 @@ +/* readonlysortedset.vala + * + * Copyright (C) 2009 Didier Villevalois, Maciej Piechotka + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * Read-only view for {@link SortedSet} collections. + * + * This class decorates any class which implements the {@link SortedSet} interface + * by making it read only. Any method which normally modify data will throw an + * error. + * + * @see SortedSet + */ +internal class Vala.ReadOnlySortedSet : ReadOnlySet, SortedSet { + /** + * Constructs a read-only set that mirrors the content of the specified set. + * + * @param set the set to decorate. + */ + public ReadOnlySortedSet (SortedSet set) { + base (set); + } + + /** + * {@inheritDoc} + */ + public G first () { + return (_collection as SortedSet).first (); + } + + /** + * {@inheritDoc} + */ + public G last () { + return (_collection as SortedSet).last (); + } + + /** + * {@inheritDoc} + */ + public Vala.Iterator? iterator_at (G element) { + var iter = (_collection as SortedSet).iterator_at (element); + return (iter != null) ? new Iterator (iter) : null; + } + + /** + * {@inheritDoc} + */ + public G? lower (G element) { + return (_collection as SortedSet).lower (element); + } + + /** + * {@inheritDoc} + */ + public G? higher (G element) { + return (_collection as SortedSet).higher (element); + } + + /** + * {@inheritDoc} + */ + public G? floor (G element) { + return (_collection as SortedSet).floor (element); + } + + /** + * {@inheritDoc} + */ + public G? ceil (G element) { + return (_collection as SortedSet).ceil (element); + } + + /** + * {@inheritDoc} + */ + public SortedSet head_set (G before) { + return (_collection as SortedSet).head_set (before).read_only_view; + } + + /** + * {@inheritDoc} + */ + public SortedSet tail_set (G after) { + return(_collection as SortedSet).tail_set (after).read_only_view; + } + + /** + * {@inheritDoc} + */ + public SortedSet sub_set (G from, G to) { + return (_collection as SortedSet).sub_set (from, to).read_only_view; + } + + /** + * {@inheritDoc} + */ + public new SortedSet read_only_view { + owned get { + return this; + } + } +} + diff --git a/gee/set.vala b/gee/set.vala index 6449023cf..ab946c204 100644 --- a/gee/set.vala +++ b/gee/set.vala @@ -21,8 +21,23 @@ */ /** - * A set is a collection without duplicates. + * A collection without duplicate elements. */ -public abstract class Vala.Set : Collection { +[GenericAccessors] +public interface Vala.Set : Collection { + + /** + * The read-only view of this set. + */ + public abstract new Set read_only_view { owned get; } + + /** + * Returns an immutable empty set. + * + * @return an immutable empty set + */ + public static Set empty () { + return new HashSet ().read_only_view; + } } diff --git a/gee/sortedmap.vala b/gee/sortedmap.vala new file mode 100644 index 000000000..8b2320144 --- /dev/null +++ b/gee/sortedmap.vala @@ -0,0 +1,66 @@ +/* sortedset.vala + * + * Copyright (C) 2009-2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +public interface Vala.SortedMap : Vala.Map { + /** + * Returns map containing pairs with key strictly lower the the argument. + */ + public abstract SortedMap head_map (K before); + + /** + * Returns map containing pairs with key equal or larger then the argument. + */ + public abstract SortedMap tail_map (K after); + + /** + * Returns right-open map (i.e. containing all pair which key is strictly + * lower then the second argument and equal or bigger then the first one). + * + * Null as one parameter means that it should include all from this side. + */ + public abstract SortedMap sub_map (K before, K after); + + /** + * Returns the keys in ascending order. + */ + public abstract SortedSet ascending_keys { owned get; } + + /** + * Returns the entries in ascending order. + */ + public abstract SortedSet> ascending_entries { owned get; } + + /** + * The read-only view this map. + */ + public new abstract SortedMap read_only_view { owned get; } + + /** + * Returns an immutable empty map. + * + * @return an immutable empty map + */ + public static Map empty () { + return new TreeMap ().read_only_view; + } +} + diff --git a/gee/sortedset.vala b/gee/sortedset.vala new file mode 100644 index 000000000..66ea3707a --- /dev/null +++ b/gee/sortedset.vala @@ -0,0 +1,137 @@ +/* sortedset.vala + * + * Copyright (C) 2009 Didier Villevalois, Maciej Piechotka + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +/** + * A sorted set, which you can navigate over and get sub-sets of. + */ +[GenericAccessors] +public interface Vala.SortedSet : Vala.Set { + /** + * Returns the first element of the sorted set. Set must not be empty. + * + * @return the first element in the sorted set + */ + public abstract G first (); + + /** + * Returns the last element of the sorted set. Set must not be empty. + * + * @return the last element in the sorted set + */ + public abstract G last (); + + /** + * Returns a {@link BidirIterator} initialy pointed at the specified + * element. + * + * @param element the element to point the iterator at + * + * @return a {@link BidirIterator} over this sorted set, or null if + * the specified element is not in this set + */ + public abstract Iterator? iterator_at (G element); + + /** + * Returns the element which is strictly lower than the specified element. + * + * @param element the element which you want the lower element for + * + * @return the corresponding element + */ + public abstract G? lower (G element); + + /** + * Returns the element which is strictly higher than the specified element. + * + * @param element the element which you want the strictly higher element + * for + * + * @return the corresponding element + */ + public abstract G? higher (G element); + + /** + * Returns the element which is lower or equal then the specified element. + * + * @param element the element which you want the lower or equal element for + * + * @return the corresponding element + */ + public abstract G? floor (G element); + + /** + * Returns the element which is higher or equal then the specified element. + * + * @param element the element which you want the higher or equal element + * for + * + * @return the corresponding element + */ + public abstract G? ceil (G element); + + /** + * Returns the sub-set of this sorted set containing elements strictly + * lower than the specified element. + * + * @param before the lower inclusive bound for the sub-set + * + * @return the corresponding sub-set of this sorted set + */ + public abstract SortedSet head_set (G before); + + /** + * Returns the sub-set of this sorted set containing elements equal or + * higher than the specified element. + * + * @param after the higher exclusive bound for the sub-set + * + * @return the corresponding sub-set of this sorted set + */ + public abstract SortedSet tail_set (G after); + + /** + * Returns the right-open sub-set of this sorted set, thus containing + * elements equal or higher than the specified ``from`` element, and stricly + * lower than the specified ``to`` element. + * + * @param from the lower inclusive bound for the sub-set + * @param to the higher exclusive bound for the sub-set + * + * @return the corresponding sub-set of this sorted set + */ + public abstract SortedSet sub_set (G from, G to); + + /** + * The read-only view of this set. + */ + public abstract new SortedSet read_only_view { owned get; } + + /** + * Returns an immutable empty sorted set. + * + * @return an immutable empty sorted set + */ + public static SortedSet empty () { + return new TreeSet ().read_only_view; + } +} diff --git a/gee/timsort.vala b/gee/timsort.vala new file mode 100644 index 000000000..5d1a862e5 --- /dev/null +++ b/gee/timsort.vala @@ -0,0 +1,713 @@ +/* timsort.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * A stable, adaptive, iterative mergesort that requires far fewer than n*lg(n) + * comparisons when running on partially sorted arrays, while offering + * performance comparable to a traditional mergesort when run on random arrays. + * Like all proper mergesorts, this sort is stable and runs O(n*log(n)) time + * (worst case). In the worst case, this sort requires temporary storage space + * for n/2 object references; in the best case, it requires only a small + * constant amount of space. + * + * This implementation was adapted from Tim Peters's list sort for Python, + * which is described in detail here: + * [[http://svn.python.org/projects/python/trunk/Objects/listsort.txt]] + * + * Tim's C code may be found here: + * [[http://svn.python.org/projects/python/trunk/Objects/listobject.c]] + * + * The underlying techniques are described in this paper (and may have even + * earlier origins): + * + * "Optimistic Sorting and Information Theoretic Complexity" + * Peter McIlroy + * SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), pp + * 467-474, Austin, Texas, 25-27 January 1993. + */ +internal class Vala.TimSort : Object { + + public static void sort (List list, CompareDataFunc compare) { + if (list is ArrayList) { + TimSort.sort_arraylist ((ArrayList) list, compare); + } else { + TimSort.sort_list (list, compare); + } + } + + private static void sort_list (List list, CompareDataFunc compare) { + TimSort helper = new TimSort (); + + helper.list_collection = list; + helper.array = list.to_array (); + helper.list = helper.array; + helper.index = 0; + helper.size = list.size; + helper.compare = compare; + + helper.do_sort (); + + // TODO Use a list iterator and use iter.set (item) + list.clear (); + foreach (G item in helper.array) { + list.add (item); + } + } + + private static void sort_arraylist (ArrayList list, CompareDataFunc compare) { + TimSort helper = new TimSort (); + + helper.list_collection = list; + helper.list = list._items; + helper.index = 0; + helper.size = list._size; + helper.compare = compare; + + helper.do_sort (); + } + + private const int MINIMUM_GALLOP = 7; + + private List list_collection; + private G[] array; + private void** list; + private int index; + private int size; + private Slice[] pending; + private int minimum_gallop; + private CompareDataFunc compare; + + private void do_sort () { + if (size < 2) { + return; + } + + pending = new Slice[0]; + minimum_gallop = MINIMUM_GALLOP; + + Slice remaining = new Slice (list, index, size); + int minimum_length = compute_minimum_run_length (remaining.length); + + while (remaining.length > 0) { + // Get the next run + bool descending; + Slice run = compute_longest_run (remaining, out descending); + #if DEBUG + message ("New run (%d, %d) %s", run.index, run.length, + descending ? "descending" : "ascending"); + #endif + if (descending) { + run.reverse (); + } + + // Extend it to minimum_length, if needed + if (run.length < minimum_length) { + int sorted_count = run.length; + run.length = int.min (minimum_length, remaining.length); + insertion_sort (run, sorted_count); + #if DEBUG + message ("Extended to (%d, %d) and sorted from index %d", + run.index, run.length, sorted_count); + #endif + } + + // Move remaining after run + remaining.shorten_start (run.length); + + // Add run to pending runs and try to merge + pending += (owned) run; + merge_collapse (); + } + + assert (remaining.index == size); + + merge_force_collapse (); + + assert (pending.length == 1); + assert (pending[0].index == 0); + assert (pending[0].length == size); + } + + private delegate bool LowerFunc (G left, G right); + + private inline bool lower_than (G left, G right) { + return compare (left, right) < 0; + } + + private inline bool lower_than_or_equal_to (G left, G right) { + return compare (left, right) <= 0; + } + + private int compute_minimum_run_length (int length) { + int run_length = 0; + while (length >= 64) { + run_length |= length & 1; + length >>= 1; + } + return length + run_length; + } + + private Slice compute_longest_run (Slice a, out bool descending) { + int run_length; + if (a.length <= 1) { + run_length = a.length; + descending = false; + } else { + run_length = 2; + if (lower_than (a.list[a.index + 1], a.list[a.index])) { + descending = true; + for (int i = a.index + 2; i < a.index + a.length; i++) { + if (lower_than (a.list[i], a.list[i-1])) { + run_length++; + } else { + break; + } + } + } else { + descending = false; + for (int i = a.index + 2; i < a.index + a.length; i++) { + if (lower_than (a.list[i], a.list[i-1])) { + break; + } else { + run_length++; + } + } + } + } + return new Slice (a.list, a.index, run_length); + } + + private void insertion_sort (Slice a, int offset) { + #if DEBUG + message ("Sorting (%d, %d) at %d", a.index, a.length, offset); + #endif + for (int start = a.index + offset; start < a.index + a.length; start++) { + int left = a.index; + int right = start; + void* pivot = a.list[right]; + + while (left < right) { + int p = left + ((right - left) >> 1); + if (lower_than (pivot, a.list[p])) { + right = p; + } else { + left = p + 1; + } + } + assert (left == right); + + Memory.move (&a.list[left + 1], &a.list[left], sizeof (G) * (start - left)); + a.list[left] = pivot; + } + } + + private void merge_collapse () { + #if DEBUG + message ("Merge Collapse"); + #endif + int count = pending.length; + while (count > 1) { + #if DEBUG + message ("Pending count: %d", count); + if (count >= 3) { + message ("pending[count-3]=%p; pending[count-2]=%p; pending[count-1]=%p", + pending[count-3], pending[count-2], pending[count-1]); + } + #endif + if (count >= 3 && pending[count-3].length <= pending[count-2].length + pending[count-1].length) { + if (pending[count-3].length < pending[count-1].length) { + merge_at (count-3); + } else { + merge_at (count-2); + } + } else if (pending[count-2].length <= pending[count-1].length) { + merge_at (count-2); + } else { + break; + } + count = pending.length; + #if DEBUG + message ("New pending count: %d", count); + #endif + } + } + + private void merge_force_collapse () { + #if DEBUG + message ("Merge Force Collapse"); + #endif + int count = pending.length; + #if DEBUG + message ("Pending count: %d", count); + #endif + while (count > 1) { + if (count >= 3 && pending[count-3].length < pending[count-1].length) { + merge_at (count-3); + } else { + merge_at (count-2); + } + count = pending.length; + #if DEBUG + message ("New pending count: %d", count); + #endif + } + } + + private void merge_at (int index) { + #if DEBUG + message ("Merge at %d", index); + #endif + Slice a = (owned) pending[index]; + Slice b = (owned) pending[index + 1]; + + assert (a.length > 0); + assert (b.length > 0); + assert (a.index + a.length == b.index); + + pending[index] = new Slice (list, a.index, a.length + b.length); + pending.move (index + 2, index + 1, pending.length - index - 2); + pending.length -= 1; + + int sorted_count = gallop_rightmost (b.peek_first (), a, 0); + a.shorten_start (sorted_count); + if (a.length == 0) { + return; + } + + b.length = gallop_leftmost (a.peek_last (), b, b.length - 1); + if (b.length == 0) { + return; + } + + if (a.length <= b.length) { + merge_low ((owned) a, (owned) b); + } else { + merge_high ((owned) a, (owned) b); + } + } + + private int gallop_leftmost (G key, Slice a, int hint) { + #if DEBUG + message ("Galop leftmost in (%d, %d), hint=%d", a.index, a.length, hint); + #endif + assert (0 <= hint); + assert (hint < a.length); + + int p = a.index + hint; + int last_offset = 0; + int offset = 1; + if (lower_than (a.list[p], key)) { + int max_offset = a.length - hint; + while (offset < max_offset) { + if (lower_than (a.list[p + offset], key)) { + last_offset = offset; + offset <<= 1; + offset++; + } else { + break; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + last_offset = hint + last_offset; + offset = hint + offset; + } else { + int max_offset = hint + 1; + while (offset < max_offset) { + if (lower_than (a.list[p - offset], key)) { + break; + } else { + last_offset = offset; + offset <<= 1; + offset++; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + int temp_last_offset = last_offset; + int temp_offset = offset; + last_offset = hint - temp_offset; + offset = hint - temp_last_offset; + } + + assert (-1 <= last_offset); + assert (last_offset < offset); + assert (offset <= a.length); + + last_offset += 1; + while (last_offset < offset) { + int m = last_offset + ((offset - last_offset) >> 1); + if (lower_than (a.list[a.index + m], key)) { + last_offset = m + 1; + } else { + offset = m; + } + } + + assert (last_offset == offset); + return offset; + } + + private int gallop_rightmost (G key, Slice a, int hint) { + #if DEBUG + message ("Galop rightmost in (%d, %d), hint=%d", a.index, a.length, hint); + #endif + assert (0 <= hint); + assert (hint < a.length); + + int p = a.index + hint; + int last_offset = 0; + int offset = 1; + if (lower_than_or_equal_to (a.list[p], key)) { + int max_offset = a.length - hint; + while (offset < max_offset) { + if (lower_than_or_equal_to (a.list[p + offset], key)) { + last_offset = offset; + offset <<= 1; + offset++; + } else { + break; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + last_offset = hint + last_offset; + offset = hint + offset; + } else { + int max_offset = hint + 1; + while (offset < max_offset) { + if (lower_than_or_equal_to (a.list[p - offset], key)) { + break; + } else { + last_offset = offset; + offset <<= 1; + offset++; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + int temp_last_offset = last_offset; + int temp_offset = offset; + last_offset = hint - temp_offset; + offset = hint - temp_last_offset; + } + + assert (-1 <= last_offset); + assert (last_offset < offset); + assert (offset <= a.length); + + last_offset += 1; + while (last_offset < offset) { + int m = last_offset + ((offset - last_offset) >> 1); + if (lower_than_or_equal_to (a.list[a.index + m], key)) { + last_offset = m + 1; + } else { + offset = m; + } + } + + assert (last_offset == offset); + return offset; + } + + private void merge_low (owned Slice a, owned Slice b) { + #if DEBUG + message ("Merge low (%d, %d) (%d, %d)", a.index, a.length, b.index, b.length); + #endif + assert (a.length > 0); + assert (b.length > 0); + assert (a.index + a.length == b.index); + + int minimum_gallop = this.minimum_gallop; + int dest = a.index; + a.copy (); + + try { + list[dest++] = b.pop_first (); + if (a.length == 1 || b.length == 0) { + return; + } + + while (true) { + int a_count = 0; + int b_count = 0; + + while (true) { + if (lower_than (b.peek_first (), a.peek_first ())) { + list[dest++] = b.pop_first (); + if (b.length == 0) { + return; + } + + b_count++; + a_count = 0; + if (b_count >= minimum_gallop) { + break; + } + } else { + list[dest++] = a.pop_first (); + if (a.length == 1) { + return; + } + + a_count++; + b_count = 0; + if (a_count >= minimum_gallop) { + break; + } + } + } + + minimum_gallop++; + + while (true) { + minimum_gallop -= (minimum_gallop > 1 ? 1 : 0); + this.minimum_gallop = minimum_gallop; + + a_count = gallop_rightmost (b.peek_first (), a, 0); + a.merge_in (list, a.index, dest, a_count); + dest += a_count; + a.shorten_start (a_count); + if (a.length <= 1) { + return; + } + + list[dest++] = b.pop_first (); + if (b.length == 0) { + return; + } + + b_count = gallop_leftmost (a.peek_first (), b, 0); + b.merge_in (list, b.index, dest, b_count); + dest += b_count; + b.shorten_start (b_count); + if (b.length == 0) { + return; + } + + list[dest++] = a.pop_first (); + if (a.length == 1) { + return; + } + + if (a_count < MINIMUM_GALLOP && b_count < MINIMUM_GALLOP) { + break; + } + } + + minimum_gallop++; + this.minimum_gallop = minimum_gallop; + } + } finally { + assert (a.length >= 0); + assert (b.length >= 0); + b.merge_in (list, b.index, dest, b.length); + a.merge_in (list, a.index, dest + b.length, a.length); + } + } + + private void merge_high (owned Slice a, owned Slice b) { + #if DEBUG + message ("Merge high (%d, %d) (%d, %d)", a.index, a.length, b.index, b.length); + #endif + assert (a.length > 0); + assert (b.length > 0); + assert (a.index + a.length == b.index); + + int minimum_gallop = this.minimum_gallop; + int dest = b.index + b.length; + b.copy (); + + try { + list[--dest] = a.pop_last (); + if (a.length == 0 || b.length == 1) { + return; + } + + while (true) { + int a_count = 0; + int b_count = 0; + + while (true) { + if (lower_than (b.peek_last (), a.peek_last ())) { + list[--dest] = a.pop_last (); + if (a.length == 0) { + return; + } + + a_count++; + b_count = 0; + if (a_count >= minimum_gallop) { + break; + } + } else { + list[--dest] = b.pop_last (); + if (b.length == 1) { + return; + } + + b_count++; + a_count = 0; + if (b_count >= minimum_gallop) { + break; + } + } + } + + minimum_gallop++; + + while (true) { + minimum_gallop -= (minimum_gallop > 1 ? 1 : 0); + this.minimum_gallop = minimum_gallop; + + int k = gallop_rightmost (b.peek_last (), a, a.length - 1); + a_count = a.length - k; + a.merge_in_reversed (list, a.index + k, dest - a_count, a_count); + dest -= a_count; + a.shorten_end (a_count); + if (a.length == 0) { + return; + } + + list[--dest] = b.pop_last (); + if (b.length == 1) { + return; + } + + k = gallop_leftmost (a.peek_last (), b, b.length - 1); + b_count = b.length - k; + b.merge_in_reversed (list, b.index + k, dest - b_count, b_count); + dest -= b_count; + b.shorten_end (b_count); + if (b.length <= 1) { + return; + } + + list[--dest] = a.pop_last (); + if (a.length == 0) { + return; + } + + if (a_count < MINIMUM_GALLOP && b_count < MINIMUM_GALLOP) { + break; + } + } + + minimum_gallop++; + this.minimum_gallop = minimum_gallop; + } + } finally { + assert (a.length >= 0); + assert (b.length >= 0); + a.merge_in_reversed (list, a.index, dest - a.length, a.length); + b.merge_in_reversed (list, b.index, dest - a.length - b.length, b.length); + } + } + + [Compact] + private class Slice { + + public void** list; + public void** new_list; + public int index; + public int length; + + public Slice (void** list, int index, int length) { + this.list = list; + this.index = index; + this.length = length; + } + + ~Slice () { + if (new_list != null) + free (new_list); + } + + public void copy () { + new_list = Memory.dup (&list[index], (uint) sizeof (G) * length); + list = new_list; + index = 0; + } + + public inline void merge_in (void** dest_array, int index, int dest_index, int count) { + Memory.move (&dest_array[dest_index], &list[index], sizeof (G) * count); + } + + public inline void merge_in_reversed (void** dest_array, int index, int dest_index, int count) { + Memory.move (&dest_array[dest_index], &list[index], sizeof (G) * count); + } + + public inline void shorten_start (int n) { + index += n; + length -= n; + } + + public inline void shorten_end (int n) { + length -= n; + } + + public inline void* pop_first () { + length--; + return list[index++]; + } + + public inline void* pop_last () { + length--; + return list[index + length]; + } + + public inline unowned void* peek_first () { + return list[index]; + } + + public inline unowned void* peek_last () { + return list[index + length - 1]; + } + + public void reverse () { + int low = index; + int high = index + length - 1; + while (low < high) { + swap (low++, high--); + } + } + + private inline void swap (int i, int j) { + void* temp = list[i]; + list[i] = list[j]; + list[j] = temp; + } + } +} + diff --git a/gee/traversable.vala b/gee/traversable.vala new file mode 100644 index 000000000..5589622d8 --- /dev/null +++ b/gee/traversable.vala @@ -0,0 +1,379 @@ +/* traversable.vala + * + * Copyright (C) 2011-2012 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +namespace Vala { + public delegate A FoldFunc (owned G g, owned A a); + public delegate bool ForallFunc (owned G g); + public delegate Lazy? UnfoldFunc (); + public delegate Traversable.Stream StreamFunc (Traversable.Stream state, owned Lazy? g, out Lazy? lazy); + public delegate A MapFunc (owned G g); + public delegate bool Predicate (G g); +} + +/** + * It's a common interface for {@link Iterator} and {@link Iterable}. It + * provides a fast, high level functions. + * + * ''{@link Iterator} implementation:'' Please note that most of the functions + * affect the state of the iterator by moving it forward. + * Even if the iterator is {@link BidirIterator} it ''must not'' + * rewind the state. + * + * ''{@link Iterable} implementation:'' validy ({@link Iterator.valid}) + * of returned iterator is the same as for invalid + * iterator. In other words the following code is semantically equivalent: + * + * {{{ + * var x = iterable.function (args); + * var x = iterable.iterator ().function(args); + * }}} + * + * @since 0.7.0 + */ +[GenericAccessors] +public interface Vala.Traversable : Object { + /** + * Apply function to each element returned by iterator untill last element + * or function return ''false''. + * + * ''{@link Iterator} implementation:'' Operation moves the iterator + * to last element in iteration or the first element that returned ''false''. + * If iterator points at some element it will be included in iteration. + * + * @return ''false'' if the argument returned ''false'' at last invocation and + * ''true'' otherwise. + */ + public new abstract bool foreach (ForallFunc f); + + /** + * Stream function is an abstract function allowing writing many + * operations. + * + * The stream function accepts three parameter: + * + * 1. state. It is usually the last returned value from function but + * it may be {@link Stream.END} when {@link Stream.CONTINUE} was + * returned and there was no more elements. + * 1. input. It is valid only if first argument is + * {@link Stream.CONTINUE} + * 1. output. It is valid only if result is Stream.YIELD + * + * It may return one of 3 results: + * + * 1. {@link Stream.YIELD}. It means that value was yielded and can + * be passed to outgoing iterator. + * 1. {@link Stream.CONTINUE}. It means that the function needs to be + * called with next element or with {@link Stream.END} if it is + * end of stream). If the state element was Stream.END during the + * current iteration function ''must not'' return {@link Stream.CONTINUE} + * 1. Stream.END. It means that the last argument was yielded. + * + * If the function yields the value immediately then the returning iterator + * is {@link Iterator.valid} and points to this value as well as in case when the + * parent iterator is {@link Iterator.valid} and function yields + * after consuming 1 input. In other case returned iterator is invalid. + * + * Note: In {@link Iterator} implementation: if iterator is + * {@link Iterator.valid} the current value should be fed + * immediately to function if during initial call function returns + * {@link Stream.CONTINUE}. The parent iterator cannot be used before + * the functions return {@link Stream.END} afterwards it points on the + * last element consumed. + * + * @param f function generating stream + * @return iterator containing values yielded by stream + */ + public virtual Iterator stream (owned StreamFunc f) { + Iterator? self; + Iterable? iself; + // Yes - I've heard of polimorphism ;) but I don't want users to need to implement the method. + if ((self = this as Iterator) != null) { + Traversable.Stream str; + Lazy? initial = null; + bool need_next = true; + str = f (Stream.YIELD, null, out initial); + switch (str) { + case Stream.CONTINUE: + if (self.valid) { + str = f (Stream.CONTINUE, new Lazy (() => {return self.get ();}), out initial); + switch (str) { + case Stream.YIELD: + case Stream.CONTINUE: + break; + case Stream.END: + return Iterator.unfold (() => {return null;}); + default: + assert_not_reached (); + } + } + break; + case Stream.YIELD: + if (self.valid) + need_next = false; + break; + case Stream.END: + return Iterator.unfold (() => {return null;}); + default: + assert_not_reached (); + } + return Iterator.unfold (() => { + Lazy? val = null; + if (str != Stream.CONTINUE) + str = f (Traversable.Stream.YIELD, null, out val); + while (str == Stream.CONTINUE) { + if (need_next) { + if (!self.next ()) { + str = f (Traversable.Stream.END, null, out val); + assert (str != Traversable.Stream.CONTINUE); + break; + } + } else { + need_next = true; + } + str = f (Stream.CONTINUE, new Lazy (() => {return self.get ();}), out val); + } + switch (str) { + case Stream.YIELD: + return val; + case Stream.END: + return null; + default: + assert_not_reached (); + } + }, initial); + } else if ((iself = this as Iterable) != null) { + return iself.iterator().stream ((owned) f); + } else { + assert_not_reached (); + } + } + + /** + * Standard aggregation function. + * + * It takes a function, seed and first element, returns the new seed and + * progress to next element when the operation repeats. + * + * Note: Default implementation uses {@link foreach}. + * + * Note: In {@link Iterator} implementation operation moves the + * iterator to last element in iteration. If iterator is + * {@link Iterator.valid} the current element will be considered + * as well. + * + */ + public virtual A fold (FoldFunc f, owned A seed) + { + this.foreach ((item) => {seed = f ((owned) item, (owned) seed); return true; }); + return (owned) seed; + } + + /** + * Produces an iterator pointing at elements generated by function passed. + * + * Iterator is lazy evaluated but value is force-evaluated when + * iterator moves to next element. ({@link Iterator.next}) + * + * Note: Default implementation uses {@link stream}. + * + * Note: In {@link Iterator} implementation if the parent iterator is + * {@link Iterator.valid} so is the returned one. Using the parent + * iterator is not allowed before the inner iterator {@link Iterator.next} + * return false and then it points on its last element. + * The resulting iterator is {@link Iterator.valid} if the parent + * iterator is. + * + * @param f Mapping function + * @return Iterator listing mapped value + */ + public virtual Iterator map (MapFunc f) { + return stream((state, item, out val) => { + switch (state) { + case Stream.YIELD: + val = null; + return Stream.CONTINUE; + case Stream.CONTINUE: + val = new Lazy(() => { + A tmp = item.get (); + item = null; + return (f ((owned)tmp)); + }); + return Stream.YIELD; + case Stream.END: + val = null; + return Stream.END; + default: + assert_not_reached (); + } + }); + } + + /** + * Creates a new iterator that is initially pointing to seed. Then + * subsequent values are obtained after applying the function to previous + * value and the subsequent items. + * + * The resulting iterator is always valid and it contains the seed value. + * + * Note: Default implementation uses {@link stream}. + * + * Note: When the method is called on {@link Iterator} using the parent + * iterator is not allowed befor the inner iterator + * {@link Iterator.next} return false and then it points on its last + * element. The resulting iterator is {@link Iterator.valid}. + * + * @param f Folding function + * @param seed original seed value + * @return Iterator containing values of subsequent values of seed + */ + public virtual Iterator scan (FoldFunc f, owned A seed) { + bool seed_emitted = false; + return stream((state, item, out val) => { + switch (state) { + case Stream.YIELD: + if (seed_emitted) { + val = null; + return Stream.CONTINUE; + } else { + val = new Lazy.from_value (seed); + seed_emitted = true; + return Stream.YIELD; + } + case Stream.CONTINUE: + val = new Lazy (() => { + A tmp = item.get (); + item = null; + seed = f ((owned) tmp, (owned) seed); + return seed; + }); + return Stream.YIELD; + case Stream.END: + val = null; + return Stream.END; + default: + assert_not_reached (); + } + }); + } + + /** + * Creates a new iterator that contains only values that fullfills the + * predicate. + * + * Note: When the method is called on {@link Iterator} using the parent + * iterator is not allowed befor the inner iterator + * {@link Iterator.next} return false and then it points on its last + * element. The resulting iterator is {@link Iterator.valid} if parent + * iterator is {@link Iterator.valid} and value it is pointing on + * fullfills the predicate. + * + * @param f Folding function + * @return Iterator containing values of subsequent values of seed + */ + public virtual Iterator filter (owned Predicate pred) { + return stream ((state, item, out val) => { + switch (state) { + case Stream.YIELD: + val = null; + return Stream.CONTINUE; + case Stream.CONTINUE: + G g = item.get (); + if (pred (g)) { + val = item; + return Stream.YIELD; + } else { + val = null; + return Stream.CONTINUE; + } + case Stream.END: + val = null; + return Stream.END; + default: + assert_not_reached (); + }; + }); + } + + /** + * Creates a new iterator which contains elements from iterable. The + * first argument states the offset i.e. number of elements the iterator + * skips by default. + * + * Note: In {@link Iterator} implementation resulting iterator is + * {@link Iterator.valid} when parent iterator is + * {@link Iterator.valid} and the offset is 0. Using the parent + * iterator is not allowed before the inner iterator + * {@link Iterator.next} return false and then it points on its last + * element. + * + * @param offset the offset to first element the iterator is pointing to + * @param length maximum number of elements iterator may return. Negative + * value means that the number is unbounded + */ + public virtual Iterator chop (int offset, int length = -1) { + assert (offset >= 0); + return stream ((state, item, out val) => { + switch (state) { + case Stream.YIELD: + val = null; + if (offset > 0) { + return Stream.CONTINUE; + } else if (length > 0) { + return length != 0 ? Stream.CONTINUE : Stream.END; + } else if (length == 0) { + return Stream.END; + } else { + return Stream.CONTINUE; + } + case Stream.CONTINUE: + if (offset == 0) { + val = item; + length--; + return Stream.YIELD; + } else { + val = null; + offset--; + return Stream.CONTINUE; + } + case Stream.END: + val = null; + return Stream.END; + default: + assert_not_reached (); + }; + }); + } + + + /** + * The type of the elements in this collection. + */ + public virtual Type element_type { get { return typeof (G); } } + + public enum Stream { + YIELD, + CONTINUE, + END + } + +} + diff --git a/gee/treemap.vala b/gee/treemap.vala new file mode 100644 index 000000000..bba480948 --- /dev/null +++ b/gee/treemap.vala @@ -0,0 +1,1914 @@ +/* treemap.vala + * + * Copyright (C) 2009-2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +using GLib; + +/** + * Left-leaning red-black tree implementation of the {@link Map} interface. + * + * This implementation is especially well designed for large quantity of + * data. The (balanced) tree implementation insure that the set and get + * methods are in logarithmic complexity. + * + * @see HashMap + */ +public class Vala.TreeMap : Vala.AbstractBidirSortedMap { + /** + * {@inheritDoc} + */ + public override int size { + get { return _size; } + } + + public override bool read_only { + get { return false; } + } + + /** + * {@inheritDoc} + */ + public override Set keys { + owned get { + var keys = _keys; + if (_keys == null) { + keys = new KeySet (this); + _keys = keys; + keys.add_weak_pointer ((void**) (&_keys)); + } + return keys; + } + } + + /** + * {@inheritDoc} + */ + public override Collection values { + owned get { + var values = _values; + if (_values == null) { + values = new ValueCollection (this); + _values = values; + values.add_weak_pointer ((void**) (&_values)); + } + return values; + } + } + + /** + * {@inheritDoc} + */ + public override Set> entries { + owned get { + var entries = _entries; + if (_entries == null) { + entries = new EntrySet (this); + _entries = entries; + entries.add_weak_pointer ((void**) (&_entries)); + } + return entries; + } + } + + /** + * The keys' comparator function. + */ + [CCode (notify = false)] + public CompareDataFunc key_compare_func { private set; get; } + + /** + * The values' equality testing function. + */ + [CCode (notify = false)] + public EqualDataFunc value_equal_func { private set; get; } + + private int _size = 0; + + private weak SortedSet _keys; + private weak Collection _values; + private weak SortedSet> _entries; + + /** + * Constructs a new, empty tree map sorted according to the specified + * comparator function. + * + * If not provided, the functions parameters are requested to the + * {@link Functions} function factory methods. + * + * @param key_compare_func an optional key comparator function + * @param value_equal_func an optional values equality testing function + */ + public TreeMap (owned CompareDataFunc? key_compare_func = null, owned EqualDataFunc? value_equal_func = null) { + if (key_compare_func == null) { + key_compare_func = Functions.get_compare_func_for (typeof (K)); + } + if (value_equal_func == null) { + value_equal_func = Functions.get_equal_func_for (typeof (V)); + } + this.key_compare_func = key_compare_func; + this.value_equal_func = value_equal_func; + } + + ~TreeMap () { + clear (); + } + + private void rotate_right (ref Node root) { + Node pivot = (owned) root.left; + pivot.color = root.color; + root.color = Node.Color.RED; + root.left = (owned) pivot.right; + pivot.right = (owned) root; + root = (owned) pivot; + } + + private void rotate_left (ref Node root) { + Node pivot = (owned) root.right; + pivot.color = root.color; + root.color = Node.Color.RED; + root.right = (owned) pivot.left; + pivot.left = (owned) root; + root = (owned) pivot; + } + + private bool is_red (Node? n) { + return n != null && n.color == Node.Color.RED; + } + + private bool is_black (Node? n) { + return n == null || n.color == Node.Color.BLACK; + } + + /** + * {@inheritDoc} + */ + public override bool has_key (K key) { + weak Node? cur = root; + while (cur != null) { + int res = key_compare_func (key, cur.key); + if (res == 0) { + return true; + } else if (res < 0) { + cur = cur.left; + } else { + cur = cur.right; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public override bool has (K key, V value) { + V? own_value = get (key); + return (own_value != null && value_equal_func (own_value, value)); + } + + /** + * {@inheritDoc} + */ + public override V? get (K key) { + weak Node? cur = root; + while (cur != null) { + int res = key_compare_func (key, cur.key); + if (res == 0) { + return cur.value; + } else if (res < 0) { + cur = cur.left; + } else { + cur = cur.right; + } + } + return null; + } + + private bool set_to_node (ref Node? node, K key, V value, out V? old_value, Node? prev, Node? next) { + if (node == null) { + old_value = null; + node = new Node (key, value, prev, next); + if (prev == null) { + first = node; + } + if (next == null) { + last = node; + } + _size++; + return true; + } + + int cmp = key_compare_func (key, node.key); + bool changed; + if (cmp == 0) { + if (value_equal_func (value, node.value)) { + old_value = null; + changed = false; + } else { + old_value = (owned) node.value; + node.value = value; + changed = true; + } + } else if (cmp < 0) { + changed = set_to_node (ref node.left, key, value, out old_value, node.prev, node); + } else { + changed = set_to_node (ref node.right, key, value, out old_value, node, node.next); + } + + fix_up (ref node); + return changed; + } + + /** + * {@inheritDoc} + */ + public override void set (K key, V value) { + V old_value; + set_to_node (ref root, key, value, out old_value, null, null); + root.color = Node.Color.BLACK; + stamp++; + } + + private void move_red_left (ref Node root) { + root.flip (); + if (is_red (root.right.left)) { + rotate_right (ref root.right); + rotate_left (ref root); + root.flip (); + } + } + + private void move_red_right (ref Node root) { + root.flip (); + if (is_red (root.left.left)) { + rotate_right (ref root); + root.flip (); + } + } + + private void fix_removal (ref Node node, out K? key = null, out V? value = null) { + Node n = (owned) node; + key = (owned) n.key; + value = (owned) n.value; + if (n.prev != null) { + n.prev.next = n.next; + } else { + first = n.next; + } + if (n.next != null) { + n.next.prev = n.prev; + } else { + last = n.next; + } + n.value = null; + node = null; + _size--; + } + + private void remove_minimal (ref Node node, out K key, out V value) { + if (node.left == null) { + fix_removal (ref node, out key, out value); + return; + } + + if (is_black (node.left) && is_black (node.left.left)) { + move_red_left (ref node); + } + + remove_minimal (ref node.left, out key, out value); + + fix_up (ref node); + } + + private bool remove_from_node (ref Node? node, K key, out V? value, out unowned Node? prev = null, out unowned Node? next = null) { + if (node == null) { + value = null; + next = null; + prev = null; + return false; + } else if (key_compare_func (key, node.key) < 0) { + weak Node left = node.left; + if (left == null) { + value = null; + next = null; + prev = null; + return false; + } + if (node.left != null && is_black (left) && is_black (left.left)) { + move_red_left (ref node); + } + bool r = remove_from_node (ref node.left, key, out value, out prev, out next); + fix_up (ref node); + return r; + } else { + if (is_red (node.left)) { + rotate_right (ref node); + } + + weak Node? r = node.right; + if (key_compare_func (key, node.key) == 0 && r == null) { + prev = node.prev; + next = node.next; + fix_removal (ref node, null, out value); + return true; + } + if (is_black (r) && r != null && is_black (r.left)) { + move_red_right (ref node); + } + if (key_compare_func (key, node.key) == 0) { + value = (owned) node.value; + prev = node.prev; + next = node; + remove_minimal (ref node.right, out node.key, out node.value); + fix_up (ref node); + return true; + } else { + bool re = remove_from_node (ref node.right, key, out value, out prev, out next); + fix_up (ref node); + return re; + } + } + } + + private void fix_up (ref Node node) { + if (is_black (node.left) && is_red (node.right)) { + rotate_left (ref node); + } + if (is_red (node.left) && is_red (node.left.left)) { + rotate_right (ref node); + } + if (is_red (node.left) && is_red (node.right)) { + node.flip (); + } + } + + /** + * {@inheritDoc} + */ + public override bool unset (K key, out V? value = null) { + bool b = remove_from_node (ref root, key, out value); + + if (root != null) { + root.color = Node.Color.BLACK; + } + stamp++; + return b; + } + + private inline void clear_subtree (owned Node node) { + node.key = null; + node.value = null; + if (node.left != null) + clear_subtree ((owned) node.left); + if (node.right != null) + clear_subtree ((owned) node.right); + } + + /** + * {@inheritDoc} + */ + public override void clear () { + if (root != null) { + clear_subtree ((owned) root); + first = last = null; + } + _size = 0; + stamp++; + } + + /** + * {@inheritDoc} + */ + public override SortedMap head_map (K before) { + return new SubMap (this, new Range.head (this, before)); + } + + /** + * {@inheritDoc} + */ + public override SortedMap tail_map (K after) { + return new SubMap (this, new Range.tail (this, after)); + } + + /** + * {@inheritDoc} + */ + public override SortedMap sub_map (K after, K before) { + return new SubMap (this, new Range (this, after, before)); + } + + /** + * {@inheritDoc} + */ + public override SortedSet ascending_keys { + owned get { + var keys = _keys; + if (_keys == null) { + keys = new KeySet (this); + _keys = keys; + keys.add_weak_pointer (&_keys); + } + return keys; + } + } + /** + * {@inheritDoc} + */ + public override SortedSet> ascending_entries { + owned get { + var entries = _entries; + if (_entries == null) { + entries = new EntrySet (this); + _entries = entries; + entries.add_weak_pointer (&_entries); + } + return entries; + } + } + + /** + * {@inheritDoc} + */ + public override Vala.MapIterator map_iterator () { + return new MapIterator (this); + } + + /** + * {@inheritDoc} + */ + public override Vala.BidirMapIterator bidir_map_iterator () { + return new MapIterator (this); + } + + [Compact] + private class Node { + public enum Color { + RED, + BLACK; + + public Color flip () { + if (this == RED) { + return BLACK; + } else { + return RED; + } + } + } + + public Node (owned K key, owned V value, Node? prev, Node? next) { + this.key = (owned) key; + this.value = (owned) value; + this.color = Color.RED; + this.prev = prev; + this.next = next; + if (prev != null) { + prev.next = this; + } + if (next != null) { + next.prev = this; + } + } + + public void flip () { + color = color.flip (); + if (left != null) { + left.color = left.color.flip (); + } + if (right != null) { + right.color = right.color.flip (); + } + } + + public K key; + public V value; + public Color color; + public Node? left; + public Node? right; + public weak Node? prev; + public weak Node? next; + public unowned Map.Entry? entry; + } + + private Node? root = null; + private weak Node? first = null; + private weak Node? last = null; + private int stamp = 0; + + private inline K min (K a, K b) { + return key_compare_func (a, b) <= 0 ? a : b; + } + + private inline K max (K a, K b) { + return key_compare_func (a, b) > 0 ? a : b; + } + + private inline unowned Node? find_node (K key) { + unowned Node? cur = root; + while (cur != null) { + int res = key_compare_func (key, cur.key); + if (res == 0) { + return cur; + } else if (res < 0) { + cur = cur.left; + } else { + cur = cur.right; + } + } + return null; + } + + private inline unowned Node? find_nearest (K key) { + unowned Node? cur = root; + while (cur != null) { + int res = key_compare_func (key, cur.key); + if (res == 0) { + return cur; + } else if (res < 0) { + if (cur.left == null) + return cur; + cur = cur.left; + } else { + if (cur.right == null) + return cur; + cur = cur.right; + } + } + return null; + } + + private class Entry : Map.Entry { + private unowned Node _node; + + public static Map.Entry entry_for (Node node) { + Map.Entry result = node.entry; + if (result == null) { + result = new Entry (node); + node.entry = result; + result.add_weak_pointer ((void**) (&node.entry)); + } + return result; + } + + public Entry (Node node) { + _node = node; + } + + public override K key { get { return _node.key; } } + + public override V value { + get { return _node.value; } + set { _node.value = value; } + } + + public override bool read_only { get { return false; } } + } + + private inline unowned Node? find_lower (K key) { + unowned Node? node = find_nearest (key); + if (node == null) + return null; + return key_compare_func (key, node.key) <= 0 ? node.prev : node; + } + + private inline unowned Node? find_higher (K key) { + unowned Node? node = find_nearest (key); + if (node == null) + return null; + return key_compare_func (key, node.key) >= 0 ? node.next : node; + } + + private inline unowned Node? find_floor (K key) { + unowned Node? node = find_nearest (key); + if (node == null) + return null; + return key_compare_func (key, node.key) < 0 ? node.prev : node; + } + + private inline unowned Node? find_ceil (K key) { + unowned Node? node = find_nearest (key); + if (node == null) + return null; + return key_compare_func (key, node.key) > 0 ? node.next : node; + } + + private inline K? lift_null_key (Node? node) { + return node != null ? node.key : null; + } + + private class Range { + public Range (TreeMap map, K after, K before) { + this.map = map; + if (map.key_compare_func (after, before) < 0) { + this.after = after; + this.before = before; + type = RangeType.BOUNDED; + } else { + type = RangeType.EMPTY; + } + } + + public Range.head (TreeMap map, K before) { + this.map = map; + this.before = before; + type = RangeType.HEAD; + } + + public Range.tail (TreeMap map, K after) { + this.map = map; + this.after = after; + type = RangeType.TAIL; + } + + public Range.empty (TreeMap map) { + this.map = map; + type = RangeType.EMPTY; + } + + public Range cut_head (K after) { + switch (type) { + case RangeType.HEAD: + return new Range (map, after, before); + case RangeType.TAIL: + return new Range.tail (map, map.max (after, this.after)); + case RangeType.EMPTY: + return this; + case RangeType.BOUNDED: + var _after = map.max (after, this.after); + return new Range (map, _after, before); + default: + assert_not_reached (); + } + } + + public Range cut_tail (K before) { + switch (type) { + case RangeType.HEAD: + return new Range.head (map, map.min (before, this.before)); + case RangeType.TAIL: + return new Range (map, after, before); + case RangeType.EMPTY: + return this; + case RangeType.BOUNDED: + var _before = map.min (before, this.before); + return new Range (map, after, _before); + default: + assert_not_reached (); + } + } + + public Range cut (K after, K before) { + if (type == RangeType.EMPTY) + return this; + var _before = (type == RangeType.HEAD || type == RangeType.BOUNDED) ? + map.min (before, this.before) : before; + var _after = (type == RangeType.TAIL || type == RangeType.BOUNDED) ? + map.max (after, this.after) : after; + return new Range (map, _after, _before); + } + + public bool in_range (K key) { + return type == RangeType.EMPTY ? false : compare_range(key) == 0; + } + + public int compare_range (K key) { + switch (type) { + case RangeType.HEAD: + return map.key_compare_func (key, before) < 0 ? 0 : 1; + case RangeType.TAIL: + return map.key_compare_func (key, after) >= 0 ? 0 : -1; + case RangeType.EMPTY: + return 0; // For simplicity - please make sure it does not break anything + case RangeType.BOUNDED: + return map.key_compare_func (key, after) >= 0 ? + (map.key_compare_func (key, before) < 0 ? 0 : 1) : -1; + default: + assert_not_reached (); + } + } + + public bool empty_submap () { + switch (type) { + case RangeType.HEAD: + return map.first == null || !in_range (map.first.key); + case RangeType.TAIL: + return map.last == null || !in_range (map.last.key); + case RangeType.EMPTY: + return true; + case RangeType.BOUNDED: + return first () == null; + default: + assert_not_reached (); + } + } + + public unowned Node? first () { + switch (type) { + case RangeType.EMPTY: + return null; + case RangeType.HEAD: + return map.first; + default: + return map.find_floor (after); + } + } + + public unowned Node? last () { + switch (type) { + case RangeType.EMPTY: + return null; + case RangeType.TAIL: + return map.last; + default: + return map.find_lower (before); + } + } + + private new TreeMap map; + private K after; + private K before; + private RangeType type; + } + + private enum RangeType { + HEAD, + TAIL, + EMPTY, + BOUNDED + } + + private class SubMap : AbstractBidirSortedMap { + public override int size { get { return keys.size; } } + public bool is_empty { get { return keys.is_empty; } } + + public SubMap (TreeMap map, Range range) { + this.map = map; + this.range = range; + } + + private weak SortedSet? _keys; + public override Set keys { + owned get { + var keys = _keys; + if (_keys == null) { + keys = new SubKeySet (map, range); + _keys = keys; + keys.add_weak_pointer(&_keys); + } + return keys; + } + } + + private weak Collection? _values; + public override Collection values { + owned get { + var values = _values; + if (_values == null) { + values = new SubValueCollection (map, range); + _values = values; + values.add_weak_pointer(&_values); + } + return values; + } + } + + private weak SortedSet>? _entries; + public override Set> entries { + owned get { + var entries = _entries; + if (_entries == null) { + entries = new SubEntrySet (map, range); + _entries = entries; + entries.add_weak_pointer(&_entries); + } + return entries; + } + } + + public override bool read_only { + get { + return true; + } + } + + public override bool has_key (K key) { + return range.in_range (key) && map.has_key (key); + } + + public override bool has (K key, V value) { + return range.in_range (key) && map.has (key, value); + } + + public override new V? get (K key) { + return range.in_range (key) ? map.get (key) : null; + } + + public override void set (K key, V value) { + if (range.in_range (key)) + map.set (key, value); + } + + public override bool unset (K key, out V? value = null) { + value = null; + return range.in_range (key) && map.unset (key, out value); + } + + public override void clear () { + for (var iterator = map_iterator (); iterator.next ();) + iterator.unset (); + } + + public override Vala.MapIterator map_iterator () { + return new SubMapIterator (map, range); + } + + public override BidirMapIterator bidir_map_iterator () { + return new SubMapIterator (map, range); + } + + public override SortedMap head_map (K before) { + return new SubMap (map, range.cut_tail (before)); + } + + public override SortedMap tail_map (K after) { + return new SubMap (map, range.cut_head (after)); + } + + public override SortedMap sub_map (K after, K before) { + return new SubMap (map, range.cut (after, before)); + } + + public override SortedSet ascending_keys { + owned get { + var keys = _keys; + if (_keys == null) { + keys = new SubKeySet (map, range); + _keys = keys; + keys.add_weak_pointer(&_keys); + } + return keys; + } + } + + public override SortedSet ascending_entries { + owned get { + var entries = _entries; + if (_entries == null) { + entries = new SubEntrySet (map, range); + _entries = entries; + entries.add_weak_pointer(&_entries); + } + return _entries; + } + } + + private TreeMap map; + private Range range; + } + + private class KeySet : AbstractBidirSortedSet { + private TreeMap _map; + + public KeySet (TreeMap map) { + _map = map; + } + + public override Iterator iterator () { + return new KeyIterator (_map); + } + + public override int size { + get { return _map.size; } + } + + public override bool read_only { + get { return true; } + } + + public override bool add (K key) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (K key) { + assert_not_reached (); + } + + public override bool contains (K key) { + return _map.has_key (key); + } + + public override K first () { + assert (_map.first != null); + return _map.first.key; + } + + public override K last () { + assert (_map.last != null); + return _map.last.key; + } + + public override BidirIterator bidir_iterator () { + return new KeyIterator (_map); + } + + public override SortedSet head_set (K before) { + return new SubKeySet (_map, new Range.head (_map, before)); + } + + public override SortedSet tail_set (K after) { + return new SubKeySet (_map, new Range.tail (_map, after)); + } + + public override SortedSet sub_set (K after, K before) { + return new SubKeySet (_map, new Range (_map, after, before)); + } + + public override Iterator? iterator_at (K item) { + weak Node? node = _map.find_node (item); + if (node == null) + return null; + return new KeyIterator.pointing (_map, node); + } + + public override K? lower (K item) { + return _map.lift_null_key (_map.find_lower (item)); + } + + public override K? higher (K item) { + return _map.lift_null_key (_map.find_higher (item)); + } + + public override K? floor (K item) { + return _map.lift_null_key (_map.find_floor (item)); + } + + public override K? ceil (K item) { + return _map.lift_null_key (_map.find_ceil (item)); + } + } + + private class SubKeySet : AbstractBidirSortedSet { + [CCode (notify = false)] + public TreeMap map { private set; get; } + [CCode (notify = false)] + public Range range { private set; get; } + + public SubKeySet (TreeMap map, Range range) { + this.map = map; + this.range = range; + } + + public override Iterator iterator () { + return new SubKeyIterator (map, range); + } + + public override int size { + get { + var i = 0; + Vala.Iterator iterator = iterator (); + while (iterator.next ()) + i++; + return i; + } + } + + public override bool read_only { + get { + return true; + } + } + + public bool is_empty { get { return range.empty_submap (); } } + + public override bool add (K key) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (K key) { + assert_not_reached (); + } + + public override bool contains (K key) { + return range.in_range(key) && map.has_key (key); + } + + public override BidirIterator bidir_iterator () { + return new SubKeyIterator (map, range); + } + + public override K first () { + weak Node? _first = range.first (); + assert (_first != null); + return _first.key; + } + + public override K last () { + weak Node? _last = range.last (); + assert (_last != null); + return _last.key; + } + + public override SortedSet head_set (K before) { + return new SubKeySet (map, range.cut_tail (before)); + } + + public override SortedSet tail_set (K after) { + return new SubKeySet (map, range.cut_head (after)); + } + + public override SortedSet sub_set (K after, K before) { + return new SubKeySet (map, range.cut (after, before)); + } + + public override Iterator? iterator_at (K key) { + if (!range.in_range (key)) + return null; + weak Node? n = map.find_node (key); + if (n == null) + return null; + return new SubKeyIterator.pointing (map, range, n); + } + + public override K? lower (K key) { + var res = range.compare_range (key); + if (res > 0) + return last (); + var l = map.lift_null_key (map.find_lower (key)); + return l != null && range.in_range (l) ? l : null; + } + + public override K? higher (K key) { + var res = range.compare_range (key); + if (res < 0) + return first (); + var h = map.lift_null_key (map.find_higher (key)); + return h != null && range.in_range (h) ? h : null; + } + + public override K? floor (K key) { + var res = range.compare_range (key); + if (res > 0) + return last (); + var l = map.lift_null_key (map.find_floor (key)); + return l != null && range.in_range (l) ? l : null; + } + + public override K? ceil (K key) { + var res = range.compare_range (key); + if (res < 0) + return first (); + var h = map.lift_null_key (map.find_ceil (key)); + return h != null && range.in_range (h) ? h : null; + } + } + + private class ValueCollection : AbstractCollection { + private TreeMap _map; + + public ValueCollection (TreeMap map) { + _map = map; + } + + public override Iterator iterator () { + return new ValueIterator (_map); + } + + public override int size { + get { return _map.size; } + } + + public override bool read_only { + get { return true; } + } + + public override bool add (V key) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (V key) { + assert_not_reached (); + } + + public override bool contains (V key) { + Iterator it = iterator (); + while (it.next ()) { + if (_map.value_equal_func (key, it.get ())) { + return true; + } + } + return false; + } + } + + private class SubValueCollection : AbstractCollection { + [CCode (notify = false)] + public TreeMap map { private set; get; } + [CCode (notify = false)] + public Range range { private set; get; } + + public SubValueCollection (TreeMap map, Range range) { + this.map = map; + this.range = range; + } + + public override Iterator iterator () { + return new SubValueIterator (map, range); + } + + public override bool read_only { + get { + return true; + } + } + + public override int size { + get { + var i = 0; + Vala.Iterator iterator = iterator (); + while (iterator.next ()) + i++; + return i; + } + } + + public bool is_empty { get { return range.empty_submap (); } } + + public override bool add (V key) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (V key) { + assert_not_reached (); + } + + public override bool contains (V key) { + Iterator it = iterator (); + while (it.next ()) { + if (map.value_equal_func (key, it.get ())) { + return true; + } + } + return false; + } + } + + private class EntrySet : AbstractBidirSortedSet> { + private TreeMap _map; + + public EntrySet (TreeMap map) { + _map = map; + } + + public override Iterator> iterator () { + return new EntryIterator (_map); + } + + public override int size { + get { return _map.size; } + } + + public override bool read_only { + get { return true; } + } + + public override bool add (Map.Entry entry) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (Map.Entry entry) { + assert_not_reached (); + } + + public override bool contains (Map.Entry entry) { + return _map.has (entry.key, entry.value); + } + + public override Map.Entry/*?*/ first () { + assert (_map.first != null); + return Entry.entry_for (_map.first); + } + + public override Map.Entry/*?*/ last () { + assert (_map.last != null); + return Entry.entry_for (_map.last); + } + + public override BidirIterator> bidir_iterator () { + return new EntryIterator (_map); + } + + public override SortedSet> head_set (Map.Entry before) { + return new SubEntrySet (_map, new Range.head (_map, before.key)); + } + + public override SortedSet> tail_set (Map.Entry after) { + return new SubEntrySet (_map, new Range.tail (_map, after.key)); + } + + public override SortedSet sub_set (Map.Entry after, Map.Entry before) { + return new SubEntrySet (_map, new Range (_map, after.key, before.key)); + } + + public override Iterator>? iterator_at (Map.Entry item) { + weak Node? node = _map.find_node (item.key); + if (node == null || !_map.value_equal_func (node.value, item.value)) + return null; + return new EntryIterator.pointing (_map, node); + } + + public override Map.Entry/*?*/ lower (Map.Entry item) { + weak Node? l = _map.find_lower (item.key); + return l != null ? Entry.entry_for (l) : null; + } + + public override Map.Entry/*?*/ higher (Map.Entry item) { + weak Node? l = _map.find_higher (item.key); + return l != null ? Entry.entry_for (l) : null; + } + + public override Map.Entry/*?*/ floor (Map.Entry item) { + weak Node? l = _map.find_floor (item.key); + return l != null ? Entry.entry_for (l) : null; + } + + public override Map.Entry/*?*/ ceil (Map.Entry item) { + weak Node? l = _map.find_ceil (item.key); + return l != null ? Entry.entry_for (l) : null; + } + } + + private class SubEntrySet : AbstractBidirSortedSet> { + [CCode (notify = false)] + public TreeMap map { private set; get; } + [CCode (notify = false)] + public Range range { private set; get; } + + public SubEntrySet (TreeMap map, Range range) { + this.map = map; + this.range = range; + } + + public override Iterator> iterator () { + return new SubEntryIterator (map, range); + } + + public override int size { + get { + var i = 0; + Vala.Iterator> iterator = iterator (); + while (iterator.next ()) + i++; + return i; + } + } + + public override bool read_only { + get { + return true; + } + } + + public bool is_empty { get { return range.empty_submap (); } } + + public override bool add (Map.Entry entry) { + assert_not_reached (); + } + + public override void clear () { + assert_not_reached (); + } + + public override bool remove (Map.Entry entry) { + assert_not_reached (); + } + + public override bool contains (Map.Entry entry) { + return range.in_range(entry.key) && map.has (entry.key, entry.value); + } + + public override BidirIterator bidir_iterator () { + return new SubEntryIterator (map, range); + } + + public override Map.Entry first () { + weak Node? _first = range.first (); + assert (_first != null); + return Entry.entry_for (_first); + } + + public override Map.Entry last () { + weak Node? _last = range.last (); + assert (_last != null); + return Entry.entry_for (_last); + } + + public override SortedSet head_set (Map.Entry before) { + return new SubEntrySet (map, range.cut_tail (before.key)); + } + + public override SortedSet tail_set (Map.Entry after) { + return new SubEntrySet (map, range.cut_head (after.key)); + } + + public override SortedSet sub_set (Map.Entry after, Map.Entry before) { + return new SubEntrySet (map, range.cut (after.key, before.key)); + } + + public override Iterator>? iterator_at (Map.Entry entry) { + if (!range.in_range (entry.key)) + return null; + weak Node? n = map.find_node (entry.key); + if (n == null || !map.value_equal_func (n.value, entry.value)) + return null; + return new SubEntryIterator.pointing (map, range, n); + } + + public override Map.Entry/*?*/ lower (Map.Entry entry) { + var res = range.compare_range (entry.key); + if (res > 0) + return last (); + weak Node? l = map.find_lower (entry.key); + return l != null && range.in_range (l.key) ? Entry.entry_for (l) : null; + } + + public override Map.Entry/*?*/ higher (Map.Entry entry) { + var res = range.compare_range (entry.key); + if (res < 0) + return first (); + weak Node? h = map.find_higher (entry.key); + return h != null && range.in_range (h.key) ? Entry.entry_for (h) : null; + } + + public override Map.Entry/*?*/ floor (Map.Entry entry) { + var res = range.compare_range (entry.key); + if (res > 0) + return last (); + weak Node? l = map.find_floor (entry.key); + return l != null && range.in_range (l.key) ? Entry.entry_for (l) : null; + } + + public override Map.Entry/*?*/ ceil (Map.Entry entry) { + var res = range.compare_range (entry.key); + if (res < 0) + return first (); + weak Node? h = map.find_ceil (entry.key); + return h != null && range.in_range (h.key) ? Entry.entry_for (h) : null; + } + } + + private class NodeIterator : Object { + protected TreeMap _map; + + // concurrent modification protection + protected int stamp; + + protected bool started = false; + + internal weak Node? current; + protected weak Node? _next; + protected weak Node? _prev; + + public NodeIterator (TreeMap map) { + _map = map; + this.stamp = _map.stamp; + } + + public NodeIterator.pointing (TreeMap map, Node current) { + _map = map; + stamp = _map.stamp; + this.current = current; + } + + public bool next () { + assert (stamp == _map.stamp); + if (current != null) { + if (current.next != null) { + current = current.next; + return true; + } else { + return false; + } + } else if (_next == null && _prev == null) { + current = _map.first; + started = true; + return current != null; + } else { + current = _next; + if (current != null) { + _next = null; + _prev = null; + } + return current != null; + } + } + + public bool has_next () { + assert (stamp == _map.stamp); + return (current == null && _next == null && _prev == null && _map.first != null) || + (current == null && _next != null) || + (current != null && current.next != null); + } + + public bool first () { + assert (stamp == _map.stamp); + current = _map.first; + _next = null; + _prev = null; + return current != null; // on false it is null anyway + } + + public bool previous () { + assert (stamp == _map.stamp); + if (current != null) { + if (current.prev != null) { + current = current.prev; + return true; + } else { + return false; + } + } else { + if (_prev != null) { + current = _prev; + _next = null; + _prev = null; + return true; + } else { + return false; + } + } + } + + public bool has_previous () { + assert (stamp == _map.stamp); + return (current == null && _prev != null) || + (current != null && current.prev != null); + } + + public bool last () { + assert (stamp == _map.stamp); + current = _map.last; + _next = null; + _prev = null; + return current != null; // on false it is null anyway + } + + public void remove () { + assert_not_reached (); + } + + public void unset () { + assert (stamp == _map.stamp); + assert (current != null); + V value; + bool success = _map.remove_from_node (ref _map.root, current.key, out value, out _prev, out _next); + assert (success); + if (_map.root != null) + _map.root.color = Node.Color.BLACK; + current = null; + stamp++; + _map.stamp++; + assert (stamp == _map.stamp); + } + + public virtual bool read_only { + get { + return true; + } + } + + public bool valid { + get { + return current != null; + } + } + + internal unowned Node? safe_next_get () { + return (current != null) ? current.next : _next; + } + + internal unowned Node? safe_previous_get () { + return (current != null) ? current.prev : _prev; + } + } + + private class SubNodeIterator : Object { + public SubNodeIterator (TreeMap map, Range range) { + _map = map; + this.range = range; + } + + public SubNodeIterator.pointing (TreeMap map, Range range, Node node) { + _map = map; + this.range = range; + this.iterator = new NodeIterator.pointing (_map, node); + } + + public bool next () { + if (iterator != null) { + weak Node? node= iterator.safe_next_get (); + if (node != null && range.in_range (node.key)) { + assert (iterator.next ()); + return true; + } else { + return false; + } + } else { + return first (); + } + } + + public bool has_next () { + if (iterator != null) { + weak Node? node = iterator.safe_next_get (); + return node != null && range.in_range (node.key); + } else { + return range.first () != null; + } + } + + public virtual bool first () { + weak Node? node = range.first (); + if (node == null) + return false; + iterator = iterator_pointing_at (node); + return true; + } + + public bool previous () { + if (iterator == null) + return false; + weak Node? node; + if ((node = iterator.safe_previous_get ()) != null && range.in_range (node.key)) { + assert (iterator.previous ()); + return true; + } else { + return false; + } + } + + public bool has_previous () { + if (iterator == null) + return false; + weak Node? node; + return (node = iterator.safe_previous_get ()) != null && range.in_range (node.key); + } + + public virtual bool last () { + weak Node? node = range.last (); + if (node == null) + return false; + iterator = iterator_pointing_at (node); + return true; + } + + public void remove () { + assert (valid); + iterator.remove (); + } + + public void unset () { + assert (valid); + iterator.unset (); + } + + public virtual bool read_only { + get { + return true; + } + } + + public bool valid { + get { + return iterator != null && iterator.valid; + } + } + + protected virtual NodeIterator iterator_pointing_at (Node node) { + return new NodeIterator.pointing (_map, node); + } + + protected new TreeMap _map; + protected Range range; + protected NodeIterator? iterator = null; + } + + private class KeyIterator : NodeIterator, Traversable, Vala.Iterator, BidirIterator { + public KeyIterator (TreeMap map) { + base (map); + } + + public KeyIterator.pointing (TreeMap map, Node current) { + base.pointing (map, current); + } + + public new K get () { + assert (stamp == _map.stamp); + assert (current != null); + return current.key; + } + + public bool foreach (ForallFunc f) { + if (current != null) { + if (!f (current.key)) { + return false; + } + current = current.next; + } else if (_next == null) { + current = _map.first; + started = true; + } else { + current = _next; + if (current != null) { + _next = null; + _prev = null; + } + } + for (; current != null; current = current.next) { + if (!f (current.key)) { + return false; + } + } + return true; + } + } + + private class SubKeyIterator : SubNodeIterator, Traversable, Vala.Iterator, BidirIterator { + public SubKeyIterator (TreeMap map, Range range) { + base (map, range); + } + + public SubKeyIterator.pointing (TreeMap map, Range range, Node node) { + base.pointing (map, range, node); + } + + public new K get () { + assert (valid); + return iterator.current.key; + } + + public bool foreach (ForallFunc f) { + if (valid) { + if (!f (iterator.current.key)) { + return false; + } + } + while (iterator.next ()) { + if (!f (iterator.current.key)) { + return false; + } + } + return true; + } + } + + private class ValueIterator : NodeIterator, Traversable, Vala.Iterator, Vala.BidirIterator { + public ValueIterator (TreeMap map) { + base (map); + } + + public ValueIterator.pointing (TreeMap map, Node current) { + base.pointing (map, current); + } + + public new V get () { + assert (stamp == _map.stamp); + assert (valid); + return current.value; + } + + public bool foreach (ForallFunc f) { + if (current != null) { + if (!f (current.value)) { + return false; + } + current = current.next; + } else if (_next == null) { + current = _map.first; + started = true; + } else { + current = _next; + if (current != null) { + _next = null; + _prev = null; + } + } + for (; current != null; current = current.next) { + if (!f (current.value)) { + return false; + } + } + return true; + } + } + + private class SubValueIterator : SubNodeIterator, Traversable, Vala.Iterator, BidirIterator { + public SubValueIterator (TreeMap map, Range range) { + base (map, range); + } + + public SubValueIterator.pointing (TreeMap map, Range range, Node node) { + base.pointing (map, range, node); + } + + public new V get () { + assert (valid); + return iterator.current.value; + } + + public bool foreach (ForallFunc f) { + if (valid) { + if (!f (iterator.current.key)) { + return false; + } + } + while (iterator.next ()) { + if (!f (iterator.current.key)) { + return false; + } + } + return true; + } + } + + private class EntryIterator : NodeIterator, Traversable>, Vala.Iterator>, Vala.BidirIterator> { + public EntryIterator (TreeMap map) { + base (map); + } + + public EntryIterator.pointing (TreeMap map, Node node) { + base.pointing (map, node); + } + + public new Map.Entry get () { + assert (stamp == _map.stamp); + assert (valid); + return Entry.entry_for (current); + } + + public new void remove () { + unset (); + } + + public bool foreach (ForallFunc> f) { + if (current != null) { + if (!f (Entry.entry_for (current))) { + return false; + } + current = current.next; + } else if (_next == null) { + current = _map.first; + started = true; + } else { + current = _next; + if (current != null) { + _next = null; + _prev = null; + } + } + for (; current != null; current = current.next) { + if (!f (Entry.entry_for (current))) { + return false; + } + } + return true; + } + } + + private class SubEntryIterator : SubNodeIterator, Traversable>, Vala.Iterator>, Vala.BidirIterator> { + public SubEntryIterator (TreeMap map, Range range) { + base (map, range); + } + + public SubEntryIterator.pointing (TreeMap map, Range range, Node node) { + base.pointing (map, range, node); + } + + public new Map.Entry get () { + assert (iterator != null); + return Entry.entry_for (iterator.current); + } + + public new void remove () { + unset (); + } + + public bool foreach (ForallFunc> f) { + if (valid) { + if (!f (Entry.entry_for (iterator.current))) { + return false; + } + } + while (iterator.next ()) { + if (!f (Entry.entry_for (iterator.current))) { + return false; + } + } + return true; + } + } + + private class MapIterator : NodeIterator, Vala.MapIterator, BidirMapIterator { + public MapIterator (TreeMap map) { + base (map); + } + + public K get_key () { + assert (stamp == _map.stamp); + assert (valid); + return current.key; + } + + public V get_value () { + assert (stamp == _map.stamp); + assert (valid); + return current.value; + } + + public void set_value (V value) { + assert (stamp == _map.stamp); + assert (valid); + current.value = value; + } + + public override bool read_only { + get { + return false; + } + } + + public bool mutable { + get { + return true; + } + } + } + + private class SubMapIterator : SubNodeIterator, Vala.MapIterator, BidirMapIterator { + public SubMapIterator (TreeMap map, Range range) { + base (map, range); + } + + public K get_key () { + assert (valid); + return iterator.current.key; + } + + public V get_value () { + assert (valid); + return iterator.current.value; + } + + public void set_value (V value) { + assert (valid); + iterator.current.value = value; + } + + public override bool read_only { + get { + return false; + } + } + + public bool mutable { + get { + return true; + } + } + } +} diff --git a/gee/treemultimap.vala b/gee/treemultimap.vala new file mode 100644 index 000000000..452dd971e --- /dev/null +++ b/gee/treemultimap.vala @@ -0,0 +1,63 @@ +/* treemultimap.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * Left-leaning red-black tree implementation of the {@link MultiMap} + * interface. + */ +public class Vala.TreeMultiMap : AbstractMultiMap { + public CompareDataFunc key_compare_func { + get { return ((TreeMap>) _storage_map).key_compare_func; } + } + + [CCode (notify = false)] + public CompareDataFunc value_compare_func { private set; get; } + + /** + * Constructs a new, empty tree multimap. + * + * If not provided, the functions parameters are requested to the + * {@link Functions} function factory methods. + * + * @param key_compare_func an optional key comparator function + * @param value_compare_func an optional value comparator function + */ + public TreeMultiMap (owned CompareDataFunc? key_compare_func = null, owned CompareDataFunc? value_compare_func = null) { + base (new TreeMap> (key_compare_func, Functions.get_equal_func_for (typeof (Set)))); + if (value_compare_func == null) { + value_compare_func = Functions.get_compare_func_for (typeof (V)); + } + this.value_compare_func = value_compare_func; + } + + protected override Collection create_value_storage () { + return new TreeSet (_value_compare_func); + } + + protected override MultiSet create_multi_key_set () { + return new TreeMultiSet (key_compare_func); + } + + protected override EqualDataFunc get_value_equal_func () { + return Functions.get_equal_func_for (typeof (V)); + } +} diff --git a/gee/treemultiset.vala b/gee/treemultiset.vala new file mode 100644 index 000000000..992d96d1b --- /dev/null +++ b/gee/treemultiset.vala @@ -0,0 +1,43 @@ +/* treemultiset.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois + */ + +/** + * Left-leaning red-black tree implementation of the {@link MultiSet} + * interface. + */ +public class Vala.TreeMultiSet : AbstractMultiSet { + public CompareDataFunc compare_func { + get { return ((TreeMap) _storage_map).key_compare_func; } + } + + /** + * Constructs a new, empty tree multi set. + * + * If not provided, the function parameter is requested to the + * {@link Functions} function factory methods. + * + * @param compare_func an optional element comparator function + */ + public TreeMultiSet (owned CompareDataFunc? compare_func = null) { + base (new TreeMap (compare_func)); + } +} diff --git a/gee/treeset.vala b/gee/treeset.vala new file mode 100644 index 000000000..0ea02aa9d --- /dev/null +++ b/gee/treeset.vala @@ -0,0 +1,1162 @@ +/* treeset.vala + * + * Copyright (C) 2009-2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +using GLib; + +/** + * Left-leaning red-black tree implementation of the {@link Set} interface. + * + * This implementation is especially well designed for large quantity of + * data. The (balanced) tree implementation insure that the set and get + * methods are in logarithmic complexity. For a linear implementation see + * {@link HashSet}. + * + * @see HashSet + */ +public class Vala.TreeSet : AbstractBidirSortedSet { + /** + * {@inheritDoc} + */ + public override int size { + get {return _size;} + } + + /** + * {@inheritDoc} + */ + public override bool read_only { + get { return false; } + } + + /** + * The elements' comparator function. + */ + [CCode (notify = false)] + public CompareDataFunc compare_func { private set; get; } + + private int _size = 0; + + /** + * Constructs a new, empty tree set sorted according to the specified + * comparator function. + * + * If not provided, the function parameter is requested to the + * {@link Functions} function factory methods. + * + * @param compare_func an optional element comparator function + */ + public TreeSet (owned CompareDataFunc? compare_func = null) { + if (compare_func == null) { + compare_func = Functions.get_compare_func_for (typeof (G)); + } + this.compare_func = compare_func; + } + + ~TreeSet () { + clear (); + } + + /** + * {@inheritDoc} + */ + public override bool contains (G item) { + weak Node? cur = root; + while (cur != null) { + int res = compare_func (item, cur.key); + if (res == 0) { + return true; + } else if (res < 0) { + cur = cur.left; + } else { + cur = cur.right; + } + } + return false; + } + + private inline void rotate_right (ref Node root) { + Node pivot = (owned) root.left; + pivot.color = root.color; + root.color = Node.Color.RED; + root.left = (owned) pivot.right; + pivot.right = (owned) root; + root = (owned) pivot; +#if DEBUG + stdout.printf (dump ("after rotate right on %s".printf ((string)root.right.key))); +#endif + } + + private inline void rotate_left (ref Node root) { + Node pivot = (owned) root.right; + pivot.color = root.color; + root.color = Node.Color.RED; + root.right = (owned) pivot.left; + pivot.left = (owned) root; + root = (owned) pivot; +#if DEBUG + stdout.printf (dump ("after rotate left on %s".printf ((string)root.left.key))); +#endif + } + + private inline bool is_red (Node? n) { + return n != null && n.color == Node.Color.RED; + } + + private inline bool is_black (Node? n) { + return n == null || n.color == Node.Color.BLACK; + } + + private inline void fix_up (ref Node node) { +#if DEBUG + var n = (string)node.key; +#endif + if (is_black (node.left) && is_red (node.right)) { + rotate_left (ref node); + } + if (is_red (node.left) && is_red (node.left.left)) { + rotate_right (ref node); + } + if (is_red (node.left) && is_red (node.right)) { + node.flip (); + } +#if DEBUG + stdout.printf (dump ("after fix up on %s".printf (n))); +#endif + } + + private bool add_to_node (ref Node? node, owned G item, Node? prev, Node? next) { +#if DEBUG + if (node != null) + stdout.printf ("Adding %s to %s\n".printf ((string) item, (string) node.key)); +#endif + if (node == null) { + node = new Node ((owned) item, prev, next); + if (prev == null) { + _first = node; + } + if (next == null) { + _last = node; + } + _size++; + return true; + } + + int cmp = compare_func (item, node.key); + if (cmp == 0) { + fix_up (ref node); + return false; + } else if (cmp < 0) { + bool r = add_to_node (ref node.left, item, node.prev, node); + fix_up (ref node); + return r; + } else { + bool r = add_to_node (ref node.right, item, node, node.next); + fix_up (ref node); + return r; + } + } + + /** + * {@inheritDoc} + * + * If the element already exists in the set it will not be added twice. + */ + public override bool add (G item) { +#if CONSISTENCY_CHECKS + check (); +#endif + bool r = add_to_node (ref root, item, null, null); + root.color = Node.Color.BLACK; +#if CONSISTENCY_CHECKS + check (); +#endif + stamp++; + return r; + } + + private inline void move_red_left (ref Node root) { +#if DEBUG + var n = (string)root.key; +#endif + root.flip (); + if (is_red (root.right.left)) { + rotate_right (ref root.right); + rotate_left (ref root); + root.flip (); + } +#if DEBUG + stdout.printf (dump ("after red left on %s".printf (n))); +#endif + } + + private inline void move_red_right (ref Node root) { +#if DEBUG + var n = (string)root.key; +#endif + root.flip (); + if (is_red (root.left.left)) { + rotate_right (ref root); + root.flip (); + } +#if DEBUG + stdout.printf (dump ("after red right on %s".printf (n))); +#endif + } + + private inline void fix_removal (ref Node node, out G? key = null) { + Node n = (owned)node; + key = (owned) n.key; + if (n.prev != null) { + n.prev.next = n.next; + } else { + _first = n.next; + } + if (n.next != null) { + n.next.prev = n.prev; + } else { + _last = n.prev; + } + node = null; + _size--; + } + + private void remove_minimal (ref Node node, out G key) { + if (node.left == null) { + fix_removal (ref node, out key); + return; + } + + if (is_black (node.left) && is_black (node.left.left)) { + move_red_left (ref node); + } + + remove_minimal (ref node.left, out key); + + fix_up (ref node); + } + + private bool remove_from_node (ref Node? node, G item, out unowned Node? prev = null, out unowned Node? next = null) { +#if DEBUG + stdout.printf ("Removing %s from %s\n", (string)item, node != null ? (string)node.key : null); +#endif + if (node == null) { + prev = null; + next = null; + return false; + } else if (compare_func (item, node.key) < 0) { + weak Node left = node.left; + if (left == null) { + prev = null; + next = null; + return false; + } + if (is_black (left) && is_black (left.left)) { + move_red_left (ref node); + } + bool r = remove_from_node (ref node.left, item, out prev, out next); + fix_up (ref node); + return r; + } else { + if (is_red (node.left)) { + rotate_right (ref node); + } + + weak Node? r = node.right; + if (compare_func (item, node.key) == 0 && r == null) { + prev = node.prev; + next = node.next; + fix_removal (ref node, null); + return true; + } + if (is_black (r) && r != null && is_black (r.left)) { + move_red_right (ref node); + } + if (compare_func (item, node.key) == 0) { + prev = node.prev; + next = node; + remove_minimal (ref node.right, out node.key); + fix_up (ref node); + return true; + } else { + bool re = remove_from_node (ref node.right, item, out prev, out next); + fix_up (ref node); + return re; + } + } + } + + /** + * {@inheritDoc} + */ + public override bool remove (G item) { +#if CONSISTENCY_CHECKS + check (); +#endif + bool b = remove_from_node (ref root, item); + if (root != null) { + root.color = Node.Color.BLACK; + } +#if CONSISTENCY_CHECKS + check (); +#endif + stamp++; + return b; + } + + private inline void clear_subtree (owned Node node) { + node.key = null; + if (node.left != null) + clear_subtree ((owned) node.left); + if (node.right != null) + clear_subtree ((owned) node.right); + } + + /** + * {@inheritDoc} + */ + public override void clear () { + if (root != null) { + clear_subtree ((owned) root); + _first = _last = null; + } + _size = 0; + stamp++; + } + + /** + * {@inheritDoc} + */ + public override Vala.Iterator iterator () { + return new Iterator (this); + } + + /** + * {@inheritDoc} + */ + public override BidirIterator bidir_iterator () { + return new Iterator (this); + } + + private inline G? lift_null_get (Node? node) { + return node != null ? node.key : null; + } + + /** + * {@inheritDoc} + */ + public override G first () { + assert (_first != null); + return _first.key; + } + + /** + * {@inheritDoc} + */ + public override G last () { + assert (_last != null); + return _last.key; + } + + /** + * {@inheritDoc} + */ + public override SortedSet head_set (G before) { + return new SubSet.head (this, before); + } + + /** + * {@inheritDoc} + */ + public override SortedSet tail_set (G after) { + return new SubSet.tail (this, after); + } + + /** + * {@inheritDoc} + */ + public override SortedSet sub_set (G after, G before) { + return new SubSet (this, after, before); + } + + private inline unowned Node? find_node (G item) { + weak Node? cur = root; + while (cur != null) { + int res = compare_func (item, cur.key); + if (res == 0) { + return cur; + } else if (res < 0) { + cur = cur.left; + } else { + cur = cur.right; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public override Vala.Iterator? iterator_at (G item) { + weak Node? node = find_node (item); + return node != null ? new Iterator.pointing (this, node) : null; + } + + private inline unowned Node? find_nearest (G item) { + weak Node? cur = root; + while (cur != null) { + int res = compare_func (item, cur.key); + if (res == 0) { + return cur; + } else if (res < 0) { + if (cur.left == null) + return cur; + cur = cur.left; + } else { + if (cur.right == null) + return cur; + cur = cur.right; + } + } + return null; + } + + private inline unowned Node? find_lower (G item) { + weak Node? node = find_nearest (item); + if (node == null) + return null; + return compare_func (item, node.key) <= 0 ? node.prev : node; + } + + private inline unowned Node? find_higher (G item) { + weak Node? node = find_nearest (item); + if (node == null) + return null; + return compare_func (item, node.key) >= 0 ? node.next : node; + } + + private inline unowned Node? find_floor (G item) { + weak Node? node = find_nearest (item); + if (node == null) + return null; + return compare_func (item, node.key) < 0 ? node.prev : node; + } + + private inline unowned Node? find_ceil (G item) { + weak Node? node = find_nearest (item); + if (node == null) + return null; + return compare_func (item, node.key) > 0 ? node.next : node; + } + + /** + * {@inheritDoc} + */ + public override G? lower (G item) { + return lift_null_get (find_lower (item)); + } + + /** + * {@inheritDoc} + */ + public override G? higher (G item) { + return lift_null_get (find_higher (item)); + } + + /** + * {@inheritDoc} + */ + public override G? floor (G item) { + return lift_null_get (find_floor (item)); + } + + /** + * {@inheritDoc} + */ + public override G? ceil (G item) { + return lift_null_get (find_ceil (item)); + } + +#if CONSISTENCY_CHECKS + public inline void check () { + check_subtree (root); + assert (root == null || root.color == Node.Color.BLACK); +#if DEBUG + stdout.printf ("%s\n", dump ()); +#endif + } + + private inline uint check_subtree (Node? node) { + if (node == null) + return 0; + assert (! (is_black (node.left) && is_red (node.right))); // Check left-leaning + assert (! (is_red (node) && is_red (node.left))); // Check red property + uint l = check_subtree (node.left); + uint r = check_subtree (node.right); + assert (l == r); + return l + (node.color == Node.Color.BLACK ? 1 : 0); + } +#endif +#if DEBUG + public string dump (string? when = null) { + return "TreeSet dump%s:\n%s".printf (when == null ? "" : (" " + when), dump_node (root)); + } + + private inline string dump_node (Node? node, uint depth = 0) { + if (node != null) + return dump_node (node.left, depth + 1) + + "%s%s%p(%s)\033[0m\n".printf (string.nfill (depth, ' '), + node.color == Node.Color.RED ? "\033[0;31m" : "", + node, (string)node.key) + + dump_node (node.right, depth + 1); + return ""; + } +#endif + + [Compact] + private class Node { + public enum Color { + RED, + BLACK; + + public Color flip () { + if (this == RED) { + return BLACK; + } else { + return RED; + } + } + } + + public Node (owned G node, Node? prev, Node? next) { + this.key = (owned) node; + this.color = Color.RED; + this.prev = prev; + this.next = next; + if (prev != null) { + prev.next = this; + } + if (next != null) { + next.prev = this; + } + } + + public void flip () { + color = color.flip (); + if (left != null) { + left.color = left.color.flip (); + } + if (right != null) { + right.color = right.color.flip (); + } + } + + public G key; + public Color color; + public Node? left; + public Node? right; + public weak Node? prev; + public weak Node? next; + } + + private class Iterator : Object, Traversable, Vala.Iterator, BidirIterator { + private TreeSet _set; + + // concurrent modification protection + private int stamp; + + public Iterator (TreeSet set) { + _set = set; + stamp = _set.stamp; + } + + public Iterator.pointing (TreeSet set, Node current) { + this._set = set; + this._current = current; + this.stamp = set.stamp; + this.started = true; + } + + public bool next () { + assert (stamp == _set.stamp); + if (_current != null) { + if (_current.next != null) { + _current = _current.next; + return true; + } else { + return false; + } + } else if (!started) { + _current = _set._first; + started = true; + return _current != null; + } else { + _current = _next; + if (_current != null) { + _next = null; + _prev = null; + } + return _current != null; + } + } + + public bool has_next () { + assert (stamp == _set.stamp); + return (!started && _set._first != null) || + (_current == null && _next != null) || + (_current != null && _current.next != null); + } + + public bool first () { + assert (stamp == _set.stamp); + _current = _set._first; + _next = null; + _prev = null; + started = true; + return _current != null; // on false it is null anyway + } + + public bool previous () { + assert (stamp == _set.stamp); + if (_current != null) { + if (_current.prev != null) { + _current = _current.prev; + return true; + } else { + return false; + } + } else { + if (_prev != null) { + _current = _prev; + _next = null; + _prev = null; + return true; + } else { + return false; + } + } + } + + public bool has_previous () { + assert (stamp == _set.stamp); + return (_current == null && _prev != null) || + (_current != null && _current.prev != null); + } + + public bool last () { + assert (stamp == _set.stamp); + _current = _set._last; + _next = null; + _prev = null; + started = true; + return _current != null; // on false it is null anyway + } + + public new G get () { + assert (stamp == _set.stamp); + assert (_current != null); + return _current.key; + } + + public void remove () { + assert (stamp == _set.stamp); + assert (_current != null); + bool success = _set.remove_from_node (ref _set.root, _current.key, out _prev, out _next); + assert (success); + if (_set.root != null) + _set.root.color = Node.Color.BLACK; + _current = null; + assert (stamp++ == _set.stamp++); + } + + internal bool safe_next_get (out G val) { + if (_current != null) { + val = _set.lift_null_get (_current.next); + return _current.next != null; + } else { + val = _set.lift_null_get (_next); + return _next != null; + } + } + + internal bool safe_previous_get (out G val) { + if (_current != null) { + val = _set.lift_null_get (_current.prev); + return _current.prev != null; + } else { + val = _set.lift_null_get (_prev); + return _next != null; + } + } + + public bool valid { + get { + assert (stamp == _set.stamp); + return _current != null; + } + } + + public bool read_only { + get { + return false; + } + } + + public bool foreach (ForallFunc f) { + assert (stamp == _set.stamp); + unowned Node? current = _current, next; + if (current != null) { + if (!f (current.key)) { + return false; + } + next = current.next; + } else if (!started) { + next = _set._first; + if (next != null) { + started = true; + } + } else { + next = _next; + if (next != null) { + _next = null; + _prev = null; + } + } + while (next != null) { + if (!f (next.key)) { + _current = next; + return false; + } + current = next; + next = current.next; + } + _current = current; + return true; + } + + private weak Node? _current = null; + private weak Node? _next = null; + private weak Node? _prev = null; + private bool started = false; + } + + private inline G min (G a, G b) { + return compare_func (a, b) <= 0 ? a : b; + } + + private inline G max (G a, G b) { + return compare_func (a, b) > 0 ? a : b; + } + + private class Range { + public Range (TreeSet set, G after, G before) { + this.set = set; + if (set.compare_func (after, before) < 0) { + this.after = after; + this.before = before; + type = RangeType.BOUNDED; + } else { + type = RangeType.EMPTY; + } + } + + public Range.head (TreeSet set, G before) { + this.set = set; + this.before = before; + type = RangeType.HEAD; + } + + public Range.tail (TreeSet set, G after) { + this.set = set; + this.after = after; + type = RangeType.TAIL; + } + +#if false + public Range.empty (TreeSet set) { + this.set = set; + type = RangeType.EMPTY; + } +#endif + + public Range cut_head (G after) { + switch (type) { + case RangeType.HEAD: + return new Range (set, after, before); + case RangeType.TAIL: + return new Range.tail (set, set.max (after, this.after)); + case RangeType.EMPTY: + return this; + case RangeType.BOUNDED: + var _after = set.max (after, this.after); + return new Range (set, _after, before); + default: + assert_not_reached (); + } + } + + public Range cut_tail (G before) { + switch (type) { + case RangeType.HEAD: + return new Range.head (set, set.min (before, this.before)); + case RangeType.TAIL: + return new Range (set, after, before); + case RangeType.EMPTY: + return this; + case RangeType.BOUNDED: + var _before = set.min (before, this.before); + return new Range (set, after, _before); + default: + assert_not_reached (); + } + } + + public Range cut (G after, G before) { + if (type == RangeType.EMPTY) + return this; + var _before = type != RangeType.TAIL ? set.min (before, this.before) : before; + var _after = type != RangeType.HEAD ? set.max (after, this.after) : after; + return new Range (set, _after, _before); + } + + public bool in_range (G item) { + return type == RangeType.EMPTY ? false : compare_range (item) == 0; + } + + public int compare_range (G item) { + switch (type) { + case RangeType.HEAD: + return set.compare_func (item, before) < 0 ? 0 : 1; + case RangeType.TAIL: + return set.compare_func (item, after) >= 0 ? 0 : -1; + case RangeType.EMPTY: + return 0; // For simplicity - please make sure it does not break anything + case RangeType.BOUNDED: + return set.compare_func (item, after) >= 0 ? + (set.compare_func (item, before) < 0 ? 0 : 1) : -1; + default: + assert_not_reached (); + } + } + + public bool empty_subset () { + switch (type) { + case RangeType.HEAD: + return set._first == null || !in_range (set._first.key); + case RangeType.TAIL: + return set._last == null || !in_range (set._last.key); + case RangeType.EMPTY: + return true; + case RangeType.BOUNDED: + return first () == null; + default: + assert_not_reached (); + } + } + + public unowned Node? first () { + switch (type) { + case RangeType.EMPTY: + return null; + case RangeType.HEAD: + return set._first; + default: + return set.find_floor (after); + } + } + + public unowned Node? last () { + switch (type) { + case RangeType.EMPTY: + return null; + case RangeType.TAIL: + return set._last; + default: + return set.find_lower (before); + } + } + + private new TreeSet set; + private G after; + private G before; + private RangeType type; + } + + private enum RangeType { + HEAD, + TAIL, + EMPTY, + BOUNDED + } + + private class SubSet : AbstractBidirSortedSet { + public SubSet (TreeSet set, G after, G before) { + this.set = set; + this.range = new Range (set, after, before); + } + + public SubSet.head (TreeSet set, G before) { + this.set = set; + this.range = new Range.head (set, before); + } + + public SubSet.tail (TreeSet set, G after) { + this.set = set; + this.range = new Range.tail (set, after); + } + + public SubSet.from_range (TreeSet set, Range range) { + this.set = set; + this.range = range; + } + + public override int size { + get { + var i = 0; + Vala.Iterator iterator = iterator (); + while (iterator.next ()) + i++; + return i; + } + } + + public override bool read_only { + get { return true; } + } + + public bool is_empty { + get { + return range.empty_subset (); + } + } + + public override bool contains (G item) { + return range.in_range (item) && set.contains (item); + } + + public override bool add (G item) { + return range.in_range (item) && set.add (item); + } + + public override bool remove (G item) { + return range.in_range (item) && set.remove (item); + } + + public override void clear () { + var iter = iterator (); + while (iter.next ()) { + iter.remove (); + } + } + + public override Vala.Iterator iterator () { + return new SubIterator (set, range); + } + + public override BidirIterator bidir_iterator () { + return new SubIterator (set, range); + } + + public override G first () { + weak Node? _first = range.first (); + assert (_first != null); + return _first.key; + } + + public override G last () { + weak Node? _last = range.last (); + assert (_last != null); + return _last.key; + } + + public override SortedSet head_set (G before) { + return new SubSet.from_range (set, range.cut_tail (before)); + } + + public override SortedSet tail_set (G after) { + return new SubSet.from_range (set, range.cut_head (after)); + } + + public override SortedSet sub_set (G after, G before) { + return new SubSet.from_range (set, range.cut (after, before)); + } + + public override Vala.Iterator? iterator_at (G item) { + if (!range.in_range (item)) + return null; + weak Node? n = set.find_node (item); + if (n == null) + return null; + return new SubIterator.pointing (set, range, n); + } + + public override G? lower (G item) { + var res = range.compare_range (item); + if (res > 0) + return last (); + var l = set.lower (item); + return l != null && range.in_range (l) ? l : null; + } + + public override G? higher (G item) { + var res = range.compare_range (item); + if (res < 0) + return first (); + var h = set.higher (item); + return h != null && range.in_range (h) ? h : null; + } + + public override G? floor (G item) { + var res = range.compare_range (item); + if (res > 0) + return last (); + var l = set.floor (item); + return l != null && range.in_range (l) ? l : null; + } + + public override G? ceil (G item) { + var res = range.compare_range (item); + if (res < 0) + return first (); + var h = set.ceil (item); + return h != null && range.in_range (h) ? h : null; + } + + private new TreeSet set; + private Range range; + } + + private class SubIterator : Object, Traversable, Vala.Iterator, BidirIterator { + public SubIterator (TreeSet set, Range range) { + this.set = set; + this.range = range; + } + + public SubIterator.pointing (TreeSet set, Range range, Node node) { + this.set = set; + this.range = range; + this.iterator = new Iterator.pointing (set, node); + } + + public bool next () { + if (iterator != null) { + G next; + if (iterator.safe_next_get (out next) && range.in_range (next)) { + assert (iterator.next ()); + return true; + } else { + return false; + } + } else { + return first (); + } + } + + public bool has_next () { + if (iterator != null) { + G next; + return (iterator.safe_next_get (out next) && range.in_range (next)); + } else { + return range.first () != null; + } + } + + public bool first () { + weak Node? node = range.first (); + if (node == null) + return false; + iterator = new Iterator.pointing (set, node); + return true; + } + + public bool previous () { + if (iterator == null) + return false; + G prev; + if (iterator.safe_previous_get (out prev) && range.in_range (prev)) { + assert (iterator.previous ()); + return true; + } else { + return false; + } + } + + public bool has_previous () { + if (iterator == null) + return false; + G prev; + return iterator.safe_previous_get (out prev) && range.in_range (prev); + } + + public bool last () { + weak Node? node = range.last (); + if (node == null) + return false; + iterator = new Iterator.pointing (set, node); + return true; + } + + public new G get () { + assert (iterator != null); + return iterator.get (); + } + + public void remove () { + assert (iterator != null); + iterator.remove (); + } + + public bool read_only { + get { + return false; + } + } + + public bool valid { + get { + return iterator.valid; + } + } + + public bool foreach(ForallFunc f) { + if(valid) { + if (!f(get())) { + return false; + } + } + while(next()) { + if (!f(get())) { + return false; + } + } + return true; + } + + private new TreeSet set; + private Range range; + private Iterator? iterator = null; + } + + private Node? root = null; + private weak Node? _first = null; + private weak Node? _last = null; + private int stamp = 0; +} diff --git a/gee/unfolditerator.vala b/gee/unfolditerator.vala new file mode 100644 index 000000000..b513f055f --- /dev/null +++ b/gee/unfolditerator.vala @@ -0,0 +1,102 @@ +/* unfolditerator.vala + * + * Copyright (C) 2011 Maciej Piechotka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Maciej Piechotka + */ + +internal class Vala.UnfoldIterator : Object, Traversable, Iterator { + public UnfoldIterator (owned UnfoldFunc func, owned Lazy? current = null) { + _current = (owned)current; + _func = (owned)func; + _end = false; + } + + public bool next () { + if (has_next ()) { + if (_current != null) + _current.eval (); + _current = (owned)_next; + return true; + } + return false; + } + + public bool has_next () { + if (_end) + return false; + if (_next != null) + return true; + _next = _func (); + if (_next == null) + _end = true; + return _next != null; + } + + public new G get () { + assert (_current != null); + return _current.value; + } + + public void remove () { + assert_not_reached (); + } + + public bool valid { get { return _current != null; } } + public bool read_only { get { return true; } } + + public bool foreach (ForallFunc f) { + if (_current != null) { + if (!f (_current.value)) { + return false; + } + } + if (_next != null) { + _current = (owned)_next; + if (!f (_current.value)) { + return false; + } + } else if (_end) { + return true; + } + if (_current == null) { + _current = _func (); + if (_current == null) { + _end = true; + return true; + } else { + if (!f (_current.value)) { + return false; + } + } + } + while ((_next = _func ()) != null) { + _current = (owned)_next; + if (!f (_current.value)) { + return false; + } + } + _end = true; + return true; + } + + private UnfoldFunc _func; + private Lazy? _current; + private Lazy? _next; + private bool _end; +} diff --git a/vala/valaattribute.vala b/vala/valaattribute.vala index f1ef73f81..889517ce5 100644 --- a/vala/valaattribute.vala +++ b/vala/valaattribute.vala @@ -34,7 +34,7 @@ public class Vala.Attribute : CodeNode { /** * Contains all specified attribute arguments. */ - public Vala.Map args = new HashMap (str_hash, str_equal); + public Map args = new HashMap (); /** * Creates a new attribute. @@ -73,7 +73,7 @@ public class Vala.Attribute : CodeNode { * @return true if the argument has been found, false otherwise */ public bool has_argument (string name) { - return args.contains (name); + return args.has_key (name); } /** diff --git a/vala/valacodecontext.vala b/vala/valacodecontext.vala index 4484c05b4..0b6a0aa66 100644 --- a/vala/valacodecontext.vala +++ b/vala/valacodecontext.vala @@ -204,9 +204,9 @@ public class Vala.CodeContext { private List c_source_files = new ArrayList (); private Namespace _root = new Namespace (null); - private List packages = new ArrayList (str_equal); + private List packages = new ArrayList (); - private Set defines = new HashSet (str_hash, str_equal); + private Set defines = new HashSet (); static StaticPrivate context_stack_key = StaticPrivate (); diff --git a/vala/valacodenode.vala b/vala/valacodenode.vala index 5413ed434..319b7613b 100644 --- a/vala/valacodenode.vala +++ b/vala/valacodenode.vala @@ -192,7 +192,7 @@ public abstract class Vala.CodeNode { public void remove_attribute_argument (string attribute, string argument) { var a = get_attribute (attribute); if (a != null) { - a.args.remove (argument); + a.args.unset (argument); if (a.args.size == 0) { attributes.remove (a); } diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala index 8c0e68804..3872b9b81 100644 --- a/vala/valacodewriter.vala +++ b/vala/valacodewriter.vala @@ -1644,7 +1644,7 @@ public class Vala.CodeWriter : CodeVisitor { iter = iter.next (); var keys = new GLib.Sequence (); - foreach (var key in attr.args.get_keys ()) { + foreach (var key in attr.args.keys) { if (key == "cheader_filename" && sym is Namespace) { continue; } diff --git a/vala/valaflowanalyzer.vala b/vala/valaflowanalyzer.vala index 9f94a42ca..9652c1537 100644 --- a/vala/valaflowanalyzer.vala +++ b/vala/valaflowanalyzer.vala @@ -367,7 +367,7 @@ public class Vala.FlowAnalyzer : CodeVisitor { phi.set (block, 0); } - foreach (Variable variable in assign.get_keys ()) { + foreach (Variable variable in assign.keys) { counter++; foreach (BasicBlock block in assign.get (variable)) { work_list.add (block); diff --git a/vala/valagirparser.vala b/vala/valagirparser.vala index 9b4c0e8dc..c3eff47b9 100644 --- a/vala/valagirparser.vala +++ b/vala/valagirparser.vala @@ -121,7 +121,7 @@ public class Vala.GirParser : CodeVisitor { add_child (child); } // merge arguments and take precedence - foreach (var key in metadata.args.get_keys ()) { + foreach (var key in metadata.args.keys) { args[key] = metadata.args[key]; } } @@ -143,7 +143,7 @@ public class Vala.GirParser : CodeVisitor { public SourceReference source_reference; public bool used = false; - public Vala.Map args = new HashMap (); + public Map args = new HashMap (); public ArrayList children = new ArrayList (); public Metadata (string pattern, string? selector = null, SourceReference? source_reference = null) { @@ -184,7 +184,7 @@ public class Vala.GirParser : CodeVisitor { } public bool has_argument (ArgumentType key) { - return args.contains (key); + return args.has_key (key); } public Expression? get_expression (ArgumentType arg) { @@ -511,7 +511,7 @@ public class Vala.GirParser : CodeVisitor { public Metadata metadata = Metadata.empty; public SourceReference source_reference = null; public ArrayList members = new ArrayList (); // guarantees fields order - public HashMap> scope = new HashMap> (str_hash, str_equal); + public HashMap> scope = new HashMap> (); public GirComment comment; public Symbol symbol; @@ -556,7 +556,7 @@ public class Vala.GirParser : CodeVisitor { var nodes = scope[node.name]; nodes.remove (node); if (nodes.size == 0) { - scope.remove (node.name); + scope.unset (node.name); } members.remove (node); node.parent = null; @@ -656,7 +656,7 @@ public class Vala.GirParser : CodeVisitor { } } - if (prefix == null && girdata != null && (girdata.contains ("c:symbol-prefix") || girdata.contains("c:symbol-prefixes"))) { + if (prefix == null && girdata != null && (girdata.has_key ("c:symbol-prefix") || girdata.has_key ("c:symbol-prefixes"))) { /* Use the prefix in the gir. We look up prefixes up to the root. If some node does not have girdata, we ignore it as i might be a namespace created due to reparenting. */ @@ -1283,8 +1283,8 @@ public class Vala.GirParser : CodeVisitor { Node current; Node old_current; - Set provided_namespaces = new HashSet (str_hash, str_equal); - HashMap unresolved_symbols_map = new HashMap (unresolved_symbol_hash, unresolved_symbol_equal); + Set provided_namespaces = new HashSet (); + HashMap unresolved_symbols_map = new HashMap ((a) => {return unresolved_symbol_hash(a);}, (a, b) => {return unresolved_symbol_equal(a, b);}); ArrayList unresolved_gir_symbols = new ArrayList (); ArrayList unresolved_type_arguments = new ArrayList (); @@ -3468,7 +3468,7 @@ public class Vala.GirParser : CodeVisitor { return; } - foreach (var arg_type in metadata.args.get_keys ()) { + foreach (var arg_type in metadata.args.keys) { var arg = metadata.args[arg_type]; if (!arg.used) { // if metadata is used and argument is not, then it's a unexpected argument diff --git a/vala/valamarkupreader.vala b/vala/valamarkupreader.vala index 2ecca0cd0..e06e331e4 100644 --- a/vala/valamarkupreader.vala +++ b/vala/valamarkupreader.vala @@ -41,7 +41,7 @@ public class Vala.MarkupReader { int line; int column; - Map attributes = new HashMap (str_hash, str_equal); + Map attributes = new HashMap (); bool empty_element; public MarkupReader (string filename) { @@ -71,8 +71,8 @@ public class Vala.MarkupReader { * @return map of current attributes */ public Map get_attributes () { - var result = new HashMap (str_hash, str_equal); - foreach (var key in attributes.get_keys ()) { + var result = new HashMap (); + foreach (var key in attributes.keys) { result.set (key, attributes.get (key)); } return result; diff --git a/vala/valascope.vala b/vala/valascope.vala index a2a946b09..d77ed9cee 100644 --- a/vala/valascope.vala +++ b/vala/valascope.vala @@ -58,7 +58,7 @@ public class Vala.Scope { public void add (string? name, Symbol sym) { if (name != null) { if (symbol_table == null) { - symbol_table = new HashMap (str_hash, str_equal); + symbol_table = new HashMap (); } else if (lookup (name) != null) { owner.error = true; if (owner.name == null && owner.parent_symbol == null) { @@ -82,7 +82,7 @@ public class Vala.Scope { } public void remove (string name) { - symbol_table.remove (name); + symbol_table.unset (name); } /** diff --git a/vala/valaswitchstatement.vala b/vala/valaswitchstatement.vala index c583f8db9..9c382be89 100644 --- a/vala/valaswitchstatement.vala +++ b/vala/valaswitchstatement.vala @@ -118,7 +118,7 @@ public class Vala.SwitchStatement : CodeNode, Statement { expression.target_type = expression.value_type.copy (); expression.target_type.nullable = false; - var labelset = new HashSet (str_hash, str_equal); + var labelset = new HashSet (); foreach (SwitchSection section in sections) { section.check (context); diff --git a/vala/valausedattr.vala b/vala/valausedattr.vala index 314495000..cc57aae45 100644 --- a/vala/valausedattr.vala +++ b/vala/valausedattr.vala @@ -27,7 +27,7 @@ using GLib; * Code visitor to warn about unused attributes */ public class Vala.UsedAttr : CodeVisitor { - public Vala.Map> marked = new HashMap> (str_hash, str_equal); + public Vala.Map> marked = new HashMap> (); const string[] valac_default_attrs = { "CCode", "type_signature", "default_value", "set_value_function", "type_id", "cprefix", "cheader_filename", @@ -103,7 +103,7 @@ public class Vala.UsedAttr : CodeVisitor { public void mark (string attribute, string? argument) { var set = marked.get (attribute); if (set == null) { - set = new HashSet (str_hash, str_equal); + set = new HashSet (); marked.set (attribute, set); } @@ -129,7 +129,7 @@ public class Vala.UsedAttr : CodeVisitor { if (set == null) { Report.warning (attr.source_reference, "attribute `%s' never used".printf (attr.name)); } else { - foreach (var arg in attr.args.get_keys()) { + foreach (var arg in attr.args.keys) { if (!set.contains (arg)) { Report.warning (attr.source_reference, "argument `%s' never used".printf (arg)); } diff --git a/vapigen/valagidlparser.vala b/vapigen/valagidlparser.vala index 86239f42e..965a30373 100644 --- a/vapigen/valagidlparser.vala +++ b/vapigen/valagidlparser.vala @@ -51,7 +51,7 @@ public class Vala.GIdlParser : CodeVisitor { * @param context a code context */ public void parse (CodeContext context) { - cname_type_map = new HashMap (str_hash, str_equal); + cname_type_map = new HashMap (); this.context = context; context.accept (this); @@ -88,7 +88,7 @@ public class Vala.GIdlParser : CodeVisitor { } private void visit_type (TypeSymbol t) { - if (!cname_type_map.contains (get_cname (t))) { + if (!cname_type_map.has_key (get_cname (t))) { cname_type_map[get_cname (t)] = t; } } @@ -104,8 +104,8 @@ public class Vala.GIdlParser : CodeVisitor { current_source_file = source_file; - codenode_attributes_map = new HashMap (str_hash, str_equal); - codenode_attributes_patterns = new HashMap (direct_hash, (EqualFunc) PatternSpec.equal); + codenode_attributes_map = new HashMap (); + codenode_attributes_patterns = new HashMap (); if (FileUtils.test (metadata_filename, FileTest.EXISTS)) { try { @@ -1586,9 +1586,9 @@ public class Vala.GIdlParser : CodeVisitor { current_data_type = cl; - current_type_symbol_set = new HashSet (str_hash, str_equal); - var current_type_func_map = new HashMap (str_hash, str_equal); - var current_type_vfunc_map = new HashMap (str_hash, str_equal); + current_type_symbol_set = new HashSet (); + var current_type_func_map = new HashMap (); + var current_type_vfunc_map = new HashMap (); foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { @@ -1602,7 +1602,7 @@ public class Vala.GIdlParser : CodeVisitor { foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { // Ignore if vfunc (handled below) - if (!current_type_vfunc_map.contains (member.name)) { + if (!current_type_vfunc_map.has_key (member.name)) { var m = parse_function ((IdlNodeFunction) member); if (m != null) { cl.add_method (m); @@ -1708,9 +1708,9 @@ public class Vala.GIdlParser : CodeVisitor { current_data_type = iface; - current_type_symbol_set = new HashSet (str_hash, str_equal); - var current_type_func_map = new HashMap (str_hash, str_equal); - var current_type_vfunc_map = new HashMap (str_hash, str_equal); + current_type_symbol_set = new HashSet (); + var current_type_func_map = new HashMap (); + var current_type_vfunc_map = new HashMap (); foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { @@ -1724,7 +1724,7 @@ public class Vala.GIdlParser : CodeVisitor { foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { // Ignore if vfunc (handled below) - if (!current_type_vfunc_map.contains (member.name)) { + if (!current_type_vfunc_map.has_key (member.name)) { var m = parse_function ((IdlNodeFunction) member, true); if (m != null) { iface.add_method (m); @@ -2985,7 +2985,7 @@ public class Vala.GIdlParser : CodeVisitor { var dot_required = (-1 != codenode.index_of_char ('.')); var colon_required = (-1 != codenode.index_of_char (':')); - var pattern_specs = codenode_attributes_patterns.get_keys (); + var pattern_specs = codenode_attributes_patterns.keys; foreach (PatternSpec* pattern in pattern_specs) { var pspec = codenode_attributes_patterns[pattern]; diff --git a/vapigen/valavapicheck.vala b/vapigen/valavapicheck.vala index 509645f4b..52c57ad0e 100644 --- a/vapigen/valavapicheck.vala +++ b/vapigen/valavapicheck.vala @@ -38,7 +38,7 @@ class Vala.VAPICheck : Object { private void parse_gidl () { _scope = new ArrayList (); - _symbols = new HashSet (str_hash, str_equal); + _symbols = new HashSet (); try { foreach (weak IdlModule module in Idl.parse_file (gidl.filename)) {