From e3d39ae2c98b52fc5071e1db282cc49a74203116 Mon Sep 17 00:00:00 2001
From: Per Hedbor <ph@opera.com>
Date: Fri, 17 May 2013 18:05:31 +0200
Subject: [PATCH] Changed ordo of array-({item}) from O(n log n) to O(n).

This especially helps when you have arrays with objects with `< and
`== operator overloading, where the constant is also changed
dramatically.

Also, it is now enough to have == operator overloading to get - to
work (given that there is at most one object in the array getting
items removed.)
---
 src/array.c | 132 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 114 insertions(+), 18 deletions(-)

diff --git a/src/array.c b/src/array.c
index 1939261286..be6a437049 100644
--- a/src/array.c
+++ b/src/array.c
@@ -649,19 +649,10 @@ PMOD_EXPORT struct array *array_remove(struct array *v,INT32 index)
 static ptrdiff_t fast_array_search( struct array *v, struct svalue *s, ptrdiff_t start )
 {
   ptrdiff_t e;
-  /* Why search for something that is not there?
-   * however, we must explicitly check for searches
-   * for destructed objects/functions
-   */
-  if((v->type_field & (1 << TYPEOF(*s)))  ||
-     (UNSAFE_IS_ZERO(s) && (v->type_field & (BIT_FUNCTION|BIT_OBJECT))) ||
-     ( (v->type_field | (1<<TYPEOF(*s)))  & BIT_OBJECT )) /* for overloading */
-  {
-    struct svalue *ip = ITEM(v);
-    for(e=start;e<v->size;e++)
-      if(is_eq(ip+e,s))
-        return e;
-  }
+  struct svalue *ip = ITEM(v);
+  for(e=start;e<v->size;e++)
+    if(is_eq(ip+e,s))
+      return e;
   return -1;
 }
 
@@ -683,7 +674,16 @@ PMOD_EXPORT ptrdiff_t array_search(struct array *v, struct svalue *s,
   if(d_flag > 1)  array_check_type_field(v);
 #endif
   check_destructed(s);
-  return fast_array_search( v, s, start );
+
+  /* Why search for something that is not there?
+   * however, we must explicitly check for searches
+   * for destructed objects/functions
+   */
+  if((v->type_field & (1 << TYPEOF(*s)))  ||
+     (UNSAFE_IS_ZERO(s) && (v->type_field & (BIT_FUNCTION|BIT_OBJECT))) ||
+     ( (v->type_field | (1<<TYPEOF(*s)))  & BIT_OBJECT )) /* for overloading */
+    return fast_array_search( v, s, start );
+  return -1;
 }
 
 /**
@@ -2034,6 +2034,99 @@ PMOD_EXPORT struct array *merge_array_without_order(struct array *a,
 #endif
 }
 
+/** Remove all instances of an svalue from an array
+*/
+static struct array *subtract_array_svalue(struct array *a, struct svalue *b)
+{
+  size_t size = a->size;
+  size_t from=0, to=0;
+  TYPE_FIELD to_type = 1<<TYPEOF(*b);
+  TYPE_FIELD type_field = 0;
+  ONERROR ouch;
+  struct svalue *ip=ITEM(a), *dp=ip;
+  int destructive = 1;
+
+  if( size == 0 )
+    return copy_array(a);
+
+  if( a->refs > 1 )
+  {
+    /* We only need to do anything if the value exists in the array. */
+    ssize_t off  = fast_array_search( a, b, 0 );
+
+    if( off == -1 )
+      /* We still need to return a new array. */
+      return copy_array(a);
+
+    /* In this case we generate a new array and modify that one. */
+    destructive = 0;
+    from = (size_t)off;
+    a = allocate_array_no_init(size-1,0);
+    SET_ONERROR( ouch, do_free_array, a );
+    dp = ITEM(a);
+
+    /* Copy the part of the array that is not modified first.. */
+    for( to=0; to<from; to++, ip++, dp++)
+    {
+      assign_svalue_no_free(dp, ip);
+      type_field |= 1<<TYPEOF(*dp);
+    }
+    a->size = from;
+  }
+
+#define MATCH_COPY(X)  do {                                                 \
+    if( X )                                                                 \
+    {  /* include entry */                                                  \
+      type_field|=1<<TYPEOF(*ip);                                           \
+      if(!destructive)                                                      \
+        assign_svalue_no_free(dp,ip);                                       \
+      else if(ip!=dp)                                                       \
+        *dp=*ip;                                                            \
+      dp++;                                                                 \
+      if( !destructive ) a->size++;                                         \
+    }                                                                       \
+    else if( destructive )                                                  \
+      free_svalue( ip );                                                    \
+  } while(0)
+
+
+  if( UNSAFE_IS_ZERO( b ) )
+  {
+    /* Remove 0-valued elements.
+       Rather common, so a special case is motivated.
+
+       This saves time becase there is no need to check if 'b' is zero
+       for each loop.
+    */
+    for( ;from<size; from++, ip++ )
+      MATCH_COPY( !UNSAFE_IS_ZERO(ip) );
+  }
+  else if((a->type_field & to_type) || ((a->type_field | to_type) & BIT_OBJECT))
+  {
+    for( ; from<size; from++, ip++ )
+      MATCH_COPY( !is_eq(ip,b) );
+  }
+  else /* b does not exist in the array. */
+  {
+    a->refs++;
+    return a;
+  }
+#undef MATCH_COPY
+
+  if( dp != ip )
+  {
+    a->type_field = type_field;
+    a->size = dp-ITEM(a);
+  }
+
+  if( !destructive )
+    UNSET_ONERROR( ouch );
+  else
+    a->refs++;
+
+  return a;
+}
+
 /** Subtract an array from another.
 */
 PMOD_EXPORT struct array *subtract_arrays(struct array *a, struct array *b)
@@ -2044,10 +2137,12 @@ PMOD_EXPORT struct array *subtract_arrays(struct array *a, struct array *b)
     array_check_type_field(b);
   }
 #endif
-  check_array_for_destruct(a);
+  if( b->size == 1 )
+    return subtract_array_svalue( a, ITEM(b) );
 
-  if((a->type_field & b->type_field) ||
-     ((a->type_field | b->type_field) & BIT_OBJECT))
+  if(b->size && 
+     ((a->type_field & b->type_field) ||
+      ((a->type_field | b->type_field) & BIT_OBJECT)))
   {
     return merge_array_with_order(a, b, PIKE_ARRAY_OP_SUB);
   }else{
@@ -2056,10 +2151,11 @@ PMOD_EXPORT struct array *subtract_arrays(struct array *a, struct array *b)
       add_ref(a);
       return a;
     }
-    return slice_array(a,0,a->size);
+    return copy_array(a);
   }
 }
 
+
 /** And two arrays together.
  */
 PMOD_EXPORT struct array *and_arrays(struct array *a, struct array *b)
-- 
GitLab