+/*
+ * ss_bp_is_unique:
+ *
+ * Reject breakpoint if it is a duplicate of one already in list or hash table.
+ */
+static gboolean
+ss_bp_is_unique (GSList *bps, GHashTable *ss_req_bp_cache, MonoMethod *method, guint32 il_offset)
+{
+ if (ss_req_bp_cache) {
+ MonoBreakpoint dummy = {method, il_offset, NULL, NULL};
+ return !g_hash_table_lookup (ss_req_bp_cache, &dummy);
+ }
+ for (GSList *l = bps; l; l = l->next) {
+ MonoBreakpoint *bp = (MonoBreakpoint *)l->data;
+ if (bp->method == method && bp->il_offset == il_offset)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * ss_bp_eq:
+ *
+ * GHashTable equality for a MonoBreakpoint (only care about method and il_offset fields)
+ */
+static gint
+ss_bp_eq (gconstpointer ka, gconstpointer kb)
+{
+ const MonoBreakpoint *s1 = (const MonoBreakpoint *)ka;
+ const MonoBreakpoint *s2 = (const MonoBreakpoint *)kb;
+ return (s1->method == s2->method && s1->il_offset == s2->il_offset) ? 1 : 0;
+}
+
+/*
+ * ss_bp_eq:
+ *
+ * GHashTable hash for a MonoBreakpoint (only care about method and il_offset fields)
+ */
+static guint
+ss_bp_hash (gconstpointer data)
+{
+ const MonoBreakpoint *s = (const MonoBreakpoint *)data;
+ guint hash = (guint) (uintptr_t) s->method;
+ hash ^= ((guint)s->il_offset) << 16; // Assume low bits are more interesting
+ hash ^= ((guint)s->il_offset) >> 16;
+ return hash;
+}
+
+#define MAX_LINEAR_SCAN_BPS 7
+
+/*
+ * ss_bp_add_one:
+ *
+ * Create a new breakpoint and add it to a step request.
+ * Will adjust the bp count and cache used by ss_start.
+ */
+static void
+ss_bp_add_one (SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_bp_cache,
+ MonoMethod *method, guint32 il_offset)
+{
+ // This list is getting too long, switch to using the hash table
+ if (!*ss_req_bp_cache && *ss_req_bp_count > MAX_LINEAR_SCAN_BPS) {
+ *ss_req_bp_cache = g_hash_table_new (ss_bp_hash, ss_bp_eq);
+ for (GSList *l = ss_req->bps; l; l = l->next)
+ g_hash_table_insert (*ss_req_bp_cache, l->data, l->data);
+ }
+
+ if (ss_bp_is_unique (ss_req->bps, *ss_req_bp_cache, method, il_offset)) {
+ // Create and add breakpoint
+ MonoBreakpoint *bp = set_breakpoint (method, il_offset, ss_req->req, NULL);
+ ss_req->bps = g_slist_append (ss_req->bps, bp);
+ if (*ss_req_bp_cache)
+ g_hash_table_insert (*ss_req_bp_cache, bp, bp);
+ (*ss_req_bp_count)++;
+ } else {
+ DEBUG_PRINTF (1, "[dbg] Candidate breakpoint at %s:[il=0x%x] is a duplicate for this step request, will not add.\n", mono_method_full_name (method, TRUE), (int)il_offset);
+ }
+}
+