Browse Source

Added string contains and replace methods.

Marco Bambini 7 years ago
parent
commit
22b115182f
4 changed files with 124 additions and 5 deletions
  1. 43 4
      src/runtime/gravity_core.c
  2. 1 1
      src/shared/gravity_value.c
  3. 79 0
      src/utils/gravity_utils.c
  4. 1 0
      src/utils/gravity_utils.h

+ 43 - 4
src/runtime/gravity_core.c

@@ -2079,6 +2079,24 @@ static bool string_index (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     }
 }
 
+static bool string_contains (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm)
+    
+    // sanity check
+    if ((nargs != 2) || (!VALUE_ISA_STRING(GET_VALUE(1)))) {
+        RETURN_ERROR("String.index() expects a string as an argument");
+    }
+    
+    gravity_string_t *main_str = VALUE_AS_STRING(GET_VALUE(0));
+    gravity_string_t *str_to_index = VALUE_AS_STRING(GET_VALUE(1));
+    
+    // search for the string
+    char *ptr = string_strnstr(main_str->s, str_to_index->s, (size_t)main_str->len);
+    
+    // return a Bool value
+    RETURN_VALUE(VALUE_FROM_BOOL(ptr != NULL), rindex);
+}
+
 static bool string_count (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     #pragma unused(vm)
 
@@ -2343,19 +2361,38 @@ static bool string_split (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     while (1) {
         char *p = string_strnstr(original, sep, (size_t)slen);
         if (p == NULL) {
-            if (marray_size(list->array) == 0) slen = 0;
-            marray_push(gravity_value_t, list->array, VALUE_FROM_STRING(vm, original, string->len - slen));
+			marray_push(gravity_value_t, list->array, VALUE_FROM_STRING(vm, original, slen));
             break;
         }
-        marray_push(gravity_value_t, list->array, VALUE_FROM_STRING(vm, original, (uint32_t)(p-original)));
+        uint32_t vlen = (uint32_t)(p-original);
+		marray_push(gravity_value_t, list->array, VALUE_FROM_STRING(vm, original, vlen));
 
         // update pointer and slen
         original = p + seplen;
-        slen = (uint32_t)(original - string->s);
+        slen -= vlen + seplen;
     }
+    
     RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
 }
 
+static bool string_find_replace (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    // sanity check
+    if ((nargs != 3) || (!VALUE_ISA_STRING(GET_VALUE(1))) || (!VALUE_ISA_STRING(GET_VALUE(2)))) RETURN_ERROR("String.replace() expects 2 string arguments.");
+    
+    // setup arguments
+    gravity_string_t *string = VALUE_AS_STRING(GET_VALUE(0));
+    gravity_string_t *from = VALUE_AS_STRING(GET_VALUE(1));
+    gravity_string_t *to = VALUE_AS_STRING(GET_VALUE(2));
+    size_t len = 0;
+    
+    // perform search and replace
+    char *s = string_replace(string->s, from->s, to->s, &len);
+    
+    // return result
+    if (s && len) RETURN_VALUE(VALUE_FROM_STRING(vm, s, (uint32_t)len), rindex);
+    RETURN_VALUE(VALUE_FROM_NULL, rindex);
+}
+
 static bool string_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     if (nargs < 2) RETURN_ERROR("Incorrect number of arguments.");
     if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
@@ -2991,6 +3028,8 @@ static void gravity_core_init (void) {
     closure = computed_property_create(NULL, NEW_FUNCTION(string_length), NULL);
     gravity_class_bind(gravity_class_string, "length", VALUE_FROM_OBJECT(closure));
     gravity_class_bind(gravity_class_string, "index", NEW_CLOSURE_VALUE(string_index));
+    gravity_class_bind(gravity_class_string, "contains", NEW_CLOSURE_VALUE(string_contains));
+    gravity_class_bind(gravity_class_string, "replace", NEW_CLOSURE_VALUE(string_find_replace));
     gravity_class_bind(gravity_class_string, "count", NEW_CLOSURE_VALUE(string_count));
     gravity_class_bind(gravity_class_string, "repeat", NEW_CLOSURE_VALUE(string_repeat));
     gravity_class_bind(gravity_class_string, "upper", NEW_CLOSURE_VALUE(string_upper));

+ 1 - 1
src/shared/gravity_value.c

@@ -1685,7 +1685,7 @@ uint32_t gravity_value_hash (gravity_value_t value) {
 
 inline gravity_class_t *gravity_value_getclass (gravity_value_t v) {
     if ((v.isa == gravity_class_class) && (v.p->objclass == gravity_class_object)) return (gravity_class_t *)v.p;
-    if ((v.isa == gravity_class_instance) || (v.isa == gravity_class_class)) return v.p->objclass;
+    if ((v.isa == gravity_class_instance) || (v.isa == gravity_class_class)) return (v.p) ? v.p->objclass : NULL;
     return v.isa;
 }
 

+ 79 - 0
src/utils/gravity_utils.c

@@ -368,6 +368,85 @@ char *string_strnstr(const char *s, const char *find, size_t slen) {
     return ((char *)s);
 }
 
+// From: https://creativeandcritical.net/str-replace-c
+char *string_replace(const char *str, const char *from, const char *to, size_t *rlen) {
+    // cache related settings
+    size_t cache_sz_inc = 16;
+    const size_t cache_sz_inc_factor = 3;
+    const size_t cache_sz_inc_max = 1048576;
+    
+    char *pret, *ret = NULL;
+    const char *pstr2, *pstr = str;
+    size_t i, count = 0;
+    uintptr_t *pos_cache_tmp, *pos_cache = NULL;
+    size_t cache_sz = 0;
+    size_t cpylen, orglen, retlen = 0, tolen = 0, fromlen = strlen(from);
+    if (rlen) *rlen = 0;
+    
+    // find all matches and cache their positions
+    while ((pstr2 = strstr(pstr, from)) != NULL) {
+        ++count;
+        
+        // Increase the cache size when necessary
+        if (cache_sz < count) {
+            cache_sz += cache_sz_inc;
+            pos_cache_tmp = mem_realloc(NULL, pos_cache, sizeof(*pos_cache) * cache_sz);
+            if (pos_cache_tmp == NULL) {
+                goto end_repl_str;
+            } else {
+                pos_cache = pos_cache_tmp;
+            }
+            
+            cache_sz_inc *= cache_sz_inc_factor;
+            if (cache_sz_inc > cache_sz_inc_max) {
+                cache_sz_inc = cache_sz_inc_max;
+            }
+        }
+        
+        pos_cache[count-1] = pstr2 - str;
+        pstr = pstr2 + fromlen;
+    }
+    
+    orglen = pstr - str + strlen(pstr);
+    
+    // allocate memory for the post-replacement string
+    if (count > 0) {
+        tolen = strlen(to);
+        retlen = orglen + (tolen - fromlen) * count;
+    } else {
+        retlen = orglen;
+    }
+    ret = mem_alloc(NULL, retlen + 1);
+    if (ret == NULL) {
+        goto end_repl_str;
+    }
+    
+    if (count == 0) {
+        // if no matches, then just duplicate the string
+        strcpy(ret, str);
+    } else {
+        //therwise, duplicate the string while performing the replacements using the position cache
+        pret = ret;
+        memcpy(pret, str, pos_cache[0]);
+        pret += pos_cache[0];
+        for (i = 0; i < count; ++i) {
+            memcpy(pret, to, tolen);
+            pret += tolen;
+            pstr = str + pos_cache[i] + fromlen;
+            cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen;
+            memcpy(pret, pstr, cpylen);
+            pret += cpylen;
+        }
+        ret[retlen] = '\0';
+    }
+    
+end_repl_str:
+    // free the cache and return the post-replacement string which will be NULL in the event of an error
+    mem_free(pos_cache);
+    if (rlen && ret) *rlen = retlen;
+    return ret;
+}
+
 // MARK: - UTF-8 Functions -
 
 /*

+ 1 - 0
src/utils/gravity_utils.h

@@ -51,6 +51,7 @@ const char  *string_ndup (const char *s1, size_t n);
 void        string_reverse (char *p);
 uint32_t    string_size (const char *p);
 char        *string_strnstr(const char *s, const char *find, size_t slen);
+char        *string_replace(const char *str, const char *from, const char *to, size_t *rlen);
 
 // UTF-8
 uint32_t    utf8_charbytes (const char *s, uint32_t i);