1 /*
2  * Copyright (C) 2000-2005 Chris Ross and various contributors
3  * Copyright (C) 1999-2000 Chris Ross
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * o Redistributions of source code must retain the above copyright notice, this
10  *   list of conditions and the following disclaimer.
11  * o Redistributions in binary form must reproduce the above copyright notice,
12  *   this list of conditions and the following disclaimer in the documentation
13  *   and/or other materials provided with the distribution.
14  * o Neither the name of the ferite software nor the names of its contributors may
15  *   be used to endorse or promote products derived from this software without
16  *   specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #ifdef HAVE_CONFIG_HEADER
32 #include "../config.h"
33 #endif
34 
35 #include "ferite.h"
36 #include <math.h>
37 
38 /**
39  * @group Unified Array
40  * @description This group is to ferite array's what the String group is to ferite strings.
41  */
42 
43 /**
44  * @function ferite_uarray_create
45  * @declaration FeriteUnifiedArray *ferite_uarray_create()
46  * @brief Create a new empty array using the compiled in default size
47  * @return A newly allocated FeriteUnifiedArray structure
48  */
ferite_uarray_create()49 FeriteUnifiedArray *ferite_uarray_create()
50 {
51     FeriteUnifiedArray *out = NULL;
52 
53     FE_ENTER_FUNCTION;
54     out = fmalloc(sizeof(FeriteUnifiedArray));
55     out->size = 0;
56     out->actual_size = FE_ARRAY_DEFAULT_SIZE;
57     out->hash = ferite_create_hash( NULL, FE_ARRAY_DEFAULT_SIZE );
58     out->array = fmalloc( sizeof(FeriteVariable*) * out->actual_size );
59     out->iteration = -1;
60     out->iterator = NULL;
61     out->iterator_type = 0;
62     FE_LEAVE_FUNCTION( out );
63 }
64 
65 /**
66  * @function ferite_uarray_destroy
67  * @declaration void ferite_uarray_destroy( FeriteScript *script, FeriteUnifiedArray *array )
68  * @brief Destroy the array and free up any memory
69  * @param FeriteScript *script The script
70  * @param FeriteUnifiedArray *array The array to delete
71  * @description This function will iterate through the array and destroy each variable. It will then clear up any memory
72                 used by the array.
73  */
ferite_uarray_destroy(FeriteScript * script,FeriteUnifiedArray * array)74 void ferite_uarray_destroy( FeriteScript *script, FeriteUnifiedArray *array )
75 {
76     int i;
77 
78     FE_ENTER_FUNCTION;
79     FE_ASSERT( array != NULL );
80     FUD(("ARRAY: %p: Deleting array, size %d, actual size %d----------------------------\n", array, array->size, array->actual_size));
81     ferite_delete_hash( script, array->hash, NULL );
82     for( i = 0; i < array->size; i++ )
83     {
84         if( array->array[i] != NULL )
85         {
86             FUD(("ARRYA: Deleting array item %d\n", i ));
87             ferite_variable_destroy( script, array->array[i] );
88         }
89     }
90     if( array->iterator != NULL )
91       ffree( array->iterator );
92     ffree( array->array );
93     ffree( array );
94     FUD(("ARRAY: Done Deleteing array\n" ));
95     FE_LEAVE_FUNCTION( NOWT );
96 }
97 
98 /**
99  * @function ferite_uarray_add
100  * @declaration void ferite_uarray_add( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var, char *id, int pos )
101  * @brief Add a variable to the array
102  * @param FeriteScript *script The script
103  * @param FeriteUnifiedArray *array The array to add to
104  * @param FeriteVariable *var The variable to add
105  * @param char *id The hash id
106  * @param int pos The position in the array where the variable should be put
107  * @description The hash id can be NULL - in which case the variable wont be hashed. If it isn't NULL
108  *              the variable gets added to the array and also hashed in aswell. The variable can either
109  *              be placed at the beginning or at the end of the array. Use either FE_ARRAY_ADD_AT_END
110  *              or FE_ARRAY_ADD_AT_START to place the variable at the end or start respectively.
111  */
ferite_uarray_add(FeriteScript * script,FeriteUnifiedArray * array,FeriteVariable * var,char * id,int pos)112 void ferite_uarray_add( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var, char *id, int pos )
113 {
114     long i = 0;
115 
116     FE_ENTER_FUNCTION;
117     FE_ASSERT( array != NULL );
118    /* THIS SHOULD NOT BE ENABLED:
119     if(var->type == F_VAR_OBJ)
120     {
121         if(VAO(var) != NULL)
122         {
123             VAO(var)->refcount++;
124         }
125     } */
126     if( pos > 0 ) /* we either add from the beginning or the end */
127       pos = FE_ARRAY_ADD_AT_END;
128 
129     if( id != NULL ) /* add it to the hash */
130     {
131         ferite_set_variable_name( script, var, id );
132         /* We asume that the array is a hash, check against a magic number
133          * to see if we need to grow the number of buckets */
134         if( array->size > array->hash->size * 20 )
135           array->hash = ferite_hash_grow( script, array->hash );
136         ferite_hash_add( script, array->hash, var->name, var );
137     }
138     else
139     {
140         ferite_set_static_variable_name( script, var, "" );
141     }
142 
143     if( FE_VAR_IS_DISPOSABLE( var ) )
144     {
145         UNMARK_VARIABLE_AS_DISPOSABLE( var );
146     }
147 
148     if( pos == FE_ARRAY_ADD_AT_END )
149     {
150         if( array->size == array->actual_size ) /* we need to bump up the size of the array */
151         {
152             FUD(( "  resizing array from %d to %d\n", array->actual_size, array->actual_size * 2 ));
153             array->actual_size *= 2;
154             array->array = frealloc( array->array, sizeof(FeriteVariable*) * array->actual_size );
155         }
156         array->array[array->size] = var;
157         FUD(( " to end slot %d in array %p\n", array->size, array->array ));
158         var->index = array->size;
159         array->size++;
160 
161         FE_LEAVE_FUNCTION( NOWT );
162     }
163     if( pos == FE_ARRAY_ADD_AT_START )
164     {
165         if( array->size == array->actual_size ) /* we need to bump up the size of the array */
166         {
167             array->actual_size += FE_ARRAY_DEFAULT_SIZE;
168             array->array = frealloc( array->array, sizeof(FeriteVariable*) * array->actual_size );
169         }
170         /* we need to do a memory move here */
171         FUD(( " shifting array contents\n" ));
172         memmove( array->array + 1, array->array, sizeof(FeriteVariable*) * array->size );
173         /* and insert the stuff here */
174         array->array[0] = var;
175         FUD(( " to slot %d in array %p\n", 0, array->array ));
176         array->size++;
177 
178         /* we re-index the variables here. this makes it very very expesive. fun. */
179         for( i = 0; i < array->size; i++ )
180           array->array[i]->index = i;
181 
182         FE_LEAVE_FUNCTION( NOWT );
183     }
184     ferite_error( script, 0, "Invalid add position %d\n", pos );
185     FE_LEAVE_FUNCTION( NOWT );
186 }
187 
188 
189 /**
190  * @function ferite_uarray_set_size
191  * @declaration void ferite_uarray_set_size( FeriteScript *script, FeriteUnifiedArray *array, int size )
192  * @brief This will grow an array to a new size.
193  * @param FeriteScript *script The script context
194  * @param FeriteUnifiedArray *array The array to change
195  * @param int size The new size of the array
196  * @description This function is useful for pre-growing an array where rapid growth is expected - such as filling it
197                 with an enormous amount of data. This stops the array having to grow automatically when adding items
198                 and can provide a welcome perfomance gain in various scenarios.
199  */
ferite_uarray_set_size(FeriteScript * script,FeriteUnifiedArray * array,int size)200 void ferite_uarray_set_size( FeriteScript *script, FeriteUnifiedArray *array, int size )
201 {
202     int i = array->size;
203 
204     FE_ENTER_FUNCTION;
205     if( array->actual_size < size )
206     {
207         array->actual_size = size;
208         array->array = frealloc( array->array, sizeof(FeriteVariable*) * array->actual_size );
209     }
210     for( i = array->size; i < array->actual_size; i++ )
211         array->array[i] = NULL;
212     array->size = size;
213     FE_LEAVE_FUNCTION( NOWT );
214 }
215 /**
216  * @function ferite_uarray_get_index
217  * @declaration FeriteVariable *ferite_uarray_get_index( FeriteScript *script, FeriteUnifiedArray *array, int index )
218  * @brief Get a variable from the array based upon an index
219  * @param FeriteScript *script The script
220  * @param FeriteUnifiedArray *array The array to get the variable from
221  * @param int index The index to obtained
222  * @return The variable if it exists, or NULL otherwise
223  */
ferite_uarray_get_index(FeriteScript * script,FeriteUnifiedArray * array,int index)224 FeriteVariable *ferite_uarray_get_index( FeriteScript *script, FeriteUnifiedArray *array, int index )
225 {
226     FeriteVariable *v = NULL;
227 
228     FE_ENTER_FUNCTION;
229     FUD(( "trying to get index %d\n", index ));
230     if( array->size == 0 )
231     {
232         ferite_error( script, 0,"Invalid index: array size is 0\n");
233         FE_LEAVE_FUNCTION( NULL );
234     }
235 
236     if(index < 0)
237     {
238         index = array->size + index;
239     }
240 
241     if( index >= array->size )
242     {
243         ferite_error( script, 0,"Index %d is out of array's bounds [%d]\n", index, array->size );
244         FE_LEAVE_FUNCTION( NULL );
245     }
246 
247     if( array->array[index] == NULL )
248         array->array[index] = ferite_create_void_variable( script, "uvar", FE_STATIC );
249 
250     FE_LEAVE_FUNCTION( array->array[index] );
251 }
252 
253 /**
254  * @function ferite_uarray_get_from_string
255  * @declaration FeriteVariable *ferite_uarray_get_from_string( FeriteScript *script, FeriteUnifiedArray *array, char *id )
256  * @brief Get the variable from the array based upon a string
257  * @param FeriteScript *script The Script
258  * @param FeriteUnifiedArray *array The array to extract the variable from
259  * @param char *id The name of the variable to get out of the array
260  * @return The variable if it exists or NULL otherwise
261  */
ferite_uarray_get_from_string(FeriteScript * script,FeriteUnifiedArray * array,char * id)262 FeriteVariable *ferite_uarray_get_from_string( FeriteScript *script, FeriteUnifiedArray *array, char *id )
263 {
264     FeriteVariable *ptr = NULL;
265     FE_ENTER_FUNCTION;
266     ptr = ferite_hash_get( script, array->hash, id );
267     FE_LEAVE_FUNCTION(ptr);
268 }
269 
270 /**
271  * @function ferite_uarray_delete_from_string
272  * @declaration FeriteVariable *ferite_uarray_delete_from_string( FeriteScript *script, FeriteUnifiedArray *array, char *id )
273  * @brief Delete a value from the array based upon a string
274  * @param FeriteScript *script The Script
275  * @param FeriteUnifiedArray *array The array to delete the variable from
276  * @param char *id The name of the variable to delete out of the array
277  * @return The variable that has been deleted
278  */
ferite_uarray_delete_from_string(FeriteScript * script,FeriteUnifiedArray * array,char * id)279 FeriteVariable *ferite_uarray_delete_from_string( FeriteScript *script, FeriteUnifiedArray *array, char *id )
280 {
281     int real_index = 0;
282 
283     FeriteVariable *ptr = NULL;
284     FE_ENTER_FUNCTION;
285     ptr = ferite_hash_get( script, array->hash, id );
286     if( ptr == NULL )
287     {
288         ferite_error( script, 0, "Unknown index '%s'\n", id );
289         FE_LEAVE_FUNCTION(NULL);
290     }
291     real_index = ptr->index;
292     ferite_hash_delete( script, array->hash, id );
293     ferite_uarray_del_index( script, array, real_index );
294     FE_LEAVE_FUNCTION(ptr);
295 }
296 
297 /**
298  * @function ferite_uarray_get
299  * @declaration FeriteVariable *ferite_uarray_get( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var )
300  * @brief Get a variable from an array based upon the value of a FeriteVariable
301  * @param FeriteScript *script The script
302  * @param FeriteUnifiedArray *array The array to get the value from
303  * @param FeriteVariable *var The variable to take the value from
304  * @return The variable if it exists or NULL otherwise
305  */
ferite_uarray_get(FeriteScript * script,FeriteUnifiedArray * array,FeriteVariable * var)306 FeriteVariable *ferite_uarray_get( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var )
307 {
308     FE_ENTER_FUNCTION;
309     switch(var->type)
310     {
311       case F_VAR_LONG:
312         FE_LEAVE_FUNCTION( ferite_uarray_get_index( script, array, VAI(var)) );
313         break;
314       case F_VAR_DOUBLE:
315         FE_LEAVE_FUNCTION( ferite_uarray_get_index( script, array, (int)floor(VAF(var)) ) );
316         break;
317       case F_VAR_STR:
318         FE_LEAVE_FUNCTION( ferite_uarray_get_from_string( script, array, FE_STR2PTR(var)) );
319         break;
320       case F_VAR_OBJ:
321 	{
322 	    FeriteVariable *hash_value = NULL, *return_value = NULL;
323 	    FeriteFunction *hash = ferite_object_get_function_for_params( script, VAO(var), "hash", NULL );
324 	    hash_value = ferite_call_function( script, VAO(var), NULL, hash, NULL );
325 
326 	    if( hash_value->type != F_VAR_STR )
327 	    {
328 		FE_LEAVE_FUNCTION(NULL);
329 	    }
330 	    return_value = ferite_uarray_get_from_string( script, array, FE_STR2PTR(hash_value) );
331 	    ferite_variable_destroy( script, hash_value );
332 	    FE_LEAVE_FUNCTION( return_value );
333 	}
334     }
335     FE_LEAVE_FUNCTION( NULL );
336 }
337 
ferite_uarray_op(FeriteScript * script,FeriteUnifiedArray * array,FeriteVariable * index,void * rhs)338 FeriteVariable *ferite_uarray_op(FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *index, void *rhs )
339 {
340     FeriteVariable *ptr = NULL;
341 
342     FE_ENTER_FUNCTION;
343     if(index->type == F_VAR_VOID && FE_VAR_IS_PLACEHOLDER( index ) )
344     {
345         FUD(( "adding variable to array\n" ));
346         ptr = ferite_create_void_variable( script, "-1", FE_STATIC );
347         ferite_uarray_add( script, array, ptr, NULL, FE_ARRAY_ADD_AT_END );
348     }
349     else
350     {
351         ptr = ferite_uarray_get( script, array, index );
352         if(ptr == NULL)
353         {
354             ptr = ferite_create_void_variable( script, "uvar", FE_STATIC );
355             ferite_uarray_add( script, array, ptr, (index->type == F_VAR_STR ? FE_STR2PTR(index) : NULL ), FE_ARRAY_ADD_AT_END );
356         }
357     }
358     FE_LEAVE_FUNCTION(  ptr );
359 }
360 
361 /**
362  * @function ferite_uarray_del_var
363  * @declaration void ferite_uarray_del_var( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *index )
364  * @brief Delete a value from an array based upon the value of a FeriteVariable
365  * @param FeriteScript *script The script
366  * @param FeriteUnifiedArray *array The array to delete the value from
367  * @param FeriteVariable *var The variable to take the value from
368  */
ferite_uarray_del_var(FeriteScript * script,FeriteUnifiedArray * array,FeriteVariable * index)369 void ferite_uarray_del_var( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *index )
370 {
371     int  real_index = 0;
372     FeriteVariable *ptr = NULL;
373 
374     FE_ENTER_FUNCTION;
375     if( index->type == F_VAR_STR )
376     {
377         ptr = ferite_hash_get( script, array->hash, FE_STR2PTR(index) );
378         if( ptr == NULL )
379         {
380             ferite_error( script, 0, "Unknown index '%s'\n", FE_STR2PTR(index) );
381             FE_LEAVE_FUNCTION(NOWT);
382         }
383         real_index = ptr->index;
384     }
385     else if( index->type == F_VAR_LONG )
386     {
387         real_index = VAI(index);
388     }
389     else if( index->type == F_VAR_DOUBLE )
390     {
391         real_index = (int)floor( VAF(index) );
392     }
393     else
394     {
395         ferite_error( script, 0, "Invalid index type '%s' on array\n", ferite_variable_id_to_str( script, index->type ) );
396         FE_LEAVE_FUNCTION( NOWT );
397     }
398     ferite_uarray_del_index( script, array, real_index );
399     FE_LEAVE_FUNCTION( NOWT );
400 }
401 
402 /**
403  * @function ferite_uarray_del_index
404  * @declaration void ferite_uarray_del_index( FeriteScript *script, FeriteUnifiedArray *array, int index )
405  * @brief Delete a value from an array based upon an index
406  * @param FeriteScript *script The script
407  * @param FeriteUnifiedArray *array The array to delete from
408  * @param int index The index to delete
409  */
ferite_uarray_del_index(FeriteScript * script,FeriteUnifiedArray * array,int index)410 void ferite_uarray_del_index( FeriteScript *script, FeriteUnifiedArray *array, int index )
411 {
412     FeriteVariable *var = NULL;
413     long i = 0;
414 
415     FE_ENTER_FUNCTION;
416 
417     if( index >= array->size || index < 0 )
418     {
419         ferite_error( script, 0, "Index out of bounds %d, can't delete item\n", index );
420         FE_LEAVE_FUNCTION( NOWT );
421     }
422 
423    /* delete the entry in the array */
424     var = array->array[index];
425     if( ferite_hash_get( script, array->hash, var->name ) != NULL )
426       ferite_hash_delete( script, array->hash, var->name );
427 
428     ferite_variable_destroy( script, var );
429 
430    /* we shift the items left one */
431     memmove( array->array + index,
432              array->array + index + 1,
433              (array->size - index) * sizeof(FeriteVariable*) );
434 
435     array->size--;
436 
437    /* we re-index the variables here. this makes it very very expesive. fun. */
438     for( i = index; i < array->size; i++ )
439       array->array[i]->index = i;
440 
441     FE_LEAVE_FUNCTION( NOWT );
442 }
443 
444 /**
445  * @function ferite_uarray_dup
446  * @declaration FeriteUnifiedArray *ferite_uarray_dup( FeriteScript *script, FeriteUnifiedArray *array, void *(*ddup)(FeriteScript*,FeriteVariable *data,void*dup) )
447  * @brief Duplicate an array
448  * @param FeriteScript *script The script
449  * @param FeriteUnifiedArray *array The array to duplicate
450  * @param void *dup A function to call to duplicate the variables
451  * @return A copy of the array and it's contents.
452  */
ferite_uarray_dup(FeriteScript * script,FeriteUnifiedArray * array,void * (* ddup)(FeriteScript *,FeriteVariable * data,void * dup))453 FeriteUnifiedArray *ferite_uarray_dup( FeriteScript *script, FeriteUnifiedArray *array, void *(*ddup)(FeriteScript*,FeriteVariable *data,void*dup) )
454 {
455     FeriteUnifiedArray *out;
456     FeriteVariable *ptr = NULL;
457     int i;
458 
459     FE_ENTER_FUNCTION;
460     out = fmalloc(sizeof(FeriteUnifiedArray));
461     out->hash = ferite_create_hash( script, array->hash->size );
462     out->size = array->size;
463     out->actual_size = array->actual_size;
464     out->array = fmalloc( sizeof(FeriteVariable*) * out->actual_size );
465 
466    /* this will go through and copy the variables, and where needs be add them to the hash */
467     for( i = 0; i < array->size; i++ )
468     {
469         ptr = (ddup)( script, array->array[i], NULL );
470         out->array[i] = ptr;
471         if( ptr->index > -1 && ptr->name[0] != '\0' )
472           ferite_hash_add( script, out->hash, ptr->name, ptr );
473     }
474     out->iteration = -1;
475     out->iterator = NULL;
476     FE_LEAVE_FUNCTION( out );
477 }
478 
479 /**
480  * @function ferite_uarray_to_str
481  * @declaration FeriteString *ferite_uarray_to_str( FeriteScript *script, FeriteUnifiedArray *array)
482  * @brief Create a FeriteString based upon the contents of an array
483  * @param FeriteScript *script The script
484  * @param FeriteUnifiedArray *array The array to covert
485  * @return The string version of the array
486  * @description This is useful for converting an array to a string to write to disk, or print out.
487  *              The function will recusre and handle inbuild arrays correctly. The output format is
488                 the same as the format used when building arrays within a ferite script.
489  */
ferite_uarray_to_str(FeriteScript * script,FeriteUnifiedArray * array)490 FeriteString *ferite_uarray_to_str( FeriteScript *script, FeriteUnifiedArray *array )
491 {
492     FeriteVariable *var;
493     int i;
494     FeriteBuffer *buf;
495     FeriteString *str,*s;
496 
497     FE_ENTER_FUNCTION;
498 
499     buf = ferite_buffer_new(FE_DEFAULT_BUFFER_SIZE);
500 
501     ferite_buffer_add_char(buf, '[');
502 
503     for(i = 0; i < array->size; i++)
504     {
505         var = array->array[i];
506         s = ferite_variable_to_str( script, var, 1);
507         if(strcmp("",var->name))
508         {
509             ferite_buffer_printf(buf," '%s' => %.*s",var->name,s->length,s->data);
510         }
511         else
512         {
513             ferite_buffer_add_char(buf, ' ');
514             ferite_buffer_add(buf, s->data, s->length);
515         }
516         ferite_str_destroy( s );
517         if(i < array->size - 1)
518         {
519             ferite_buffer_add_char(buf, ',');
520         }
521     }
522     ferite_buffer_add_char(buf, ' ');
523     ferite_buffer_add_char(buf, ']');
524     str = ferite_buffer_to_str( buf );
525     ferite_buffer_delete(buf);
526     FE_LEAVE_FUNCTION( str );
527 }
528 
529 /**
530  * @function ferite_uarray_push
531  * @declaration void ferite_uarray_push( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var )
532  * @brief Push a value onto the end array like a stack
533  * @param FeriteScript *script The script
534  * @param FeriteUnifiedArray *array The array to push the value onto
535  * @param FeriteVariable *var The variable to push onto the array
536  */
ferite_uarray_push(FeriteScript * script,FeriteUnifiedArray * array,FeriteVariable * var)537 void ferite_uarray_push( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var )
538 {
539     FeriteVariable *ptr = NULL;
540 
541     FE_ENTER_FUNCTION;
542     ptr = ferite_duplicate_variable( script, var, NULL );
543     ferite_uarray_add( script, array, ptr, NULL, FE_ARRAY_ADD_AT_END );
544     FE_LEAVE_FUNCTION( NOWT );
545 }
546 
547 /**
548  * @function ferite_uarray_unshift
549  * @declaration void ferite_uarray_unshift( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var )
550  * @brief Shift a value onto the front of the array
551  * @param FeriteScript *script The script
552  * @param FeriteUnifiedArray *array The array to shift the value onto
553  * @param FeriteVariable *var The variable to shift onto the array
554  */
ferite_uarray_unshift(FeriteScript * script,FeriteUnifiedArray * array,FeriteVariable * var)555 void ferite_uarray_unshift( FeriteScript *script, FeriteUnifiedArray *array, FeriteVariable *var )
556 {
557     FeriteVariable *v;
558 
559     FE_ENTER_FUNCTION;
560     v = ferite_duplicate_variable( script, var, NULL );
561     ferite_uarray_add( script, array, v, NULL, FE_ARRAY_ADD_AT_START );
562     FE_LEAVE_FUNCTION( NOWT );
563 }
564 
565 /**
566  * @function ferite_uarray_pop
567  * @declaration FeriteVariable *ferite_uarray_pop( FeriteScript *script, FeriteUnifiedArray *array )
568  * @brief Pop a value off the end of the array and return it
569  * @param FeriteScript *script The script
570  * @param FeriteUnifiedArray *array The array to pop a value off the end
571  */
ferite_uarray_pop(FeriteScript * script,FeriteUnifiedArray * array)572 FeriteVariable *ferite_uarray_pop( FeriteScript *script, FeriteUnifiedArray *array )
573 {
574     FeriteVariable *out = NULL;
575 
576     FE_ENTER_FUNCTION;
577     if( array->size > 0 )
578     {
579         out = ferite_duplicate_variable( script, ferite_uarray_get_index( script, array, (array->size) - 1 ), NULL );
580         ferite_uarray_del_index( script, array, (array->size)-1);
581     }
582     else
583     {
584         ferite_warning( script, "Trying to pop element off an empty array!\n" );
585         out = ferite_create_void_variable( script, "no_value", FE_STATIC );
586     }
587     MARK_VARIABLE_AS_DISPOSABLE( out );
588     FE_LEAVE_FUNCTION( out );
589 }
590 
591 /**
592  * @function ferite_uarray_shift
593  * @declaration FeriteVariable *ferite_uarray_shift( FeriteScript *script, FeriteUnifiedArray *array )
594  * @brief Shift a value off the front of the array
595  * @param FeriteScript *script The script
596  * @param FeriteUnifiedArray *array The array to shift a value off the front
597  */
ferite_uarray_shift(FeriteScript * script,FeriteUnifiedArray * array)598 FeriteVariable *ferite_uarray_shift( FeriteScript *script, FeriteUnifiedArray *array )
599 {
600     FeriteVariable *out;
601 
602     FE_ENTER_FUNCTION;
603     if( array->size > 0 )
604     {
605         out = ferite_duplicate_variable( script, ferite_uarray_get_index( script, array, 0 ), NULL );
606         ferite_uarray_del_index( script, array, 0 );
607     }
608     else
609     {
610         ferite_warning( script, "Trying to shift element off an empty array!\n" );
611         out = ferite_create_void_variable( script, "no_value", FE_STATIC );
612     }
613     MARK_VARIABLE_AS_DISPOSABLE( out );
614     FE_LEAVE_FUNCTION( out );
615 }
616 
617 /**
618  * @function ferite_uarray_cmp
619  * @declaration int ferite_uarray_cmp( FeriteScript *script, FeriteUnifiedArray *left, FeriteUnifiedArray *right )
620  * @brief Compare two arrays
621  * @param FeriteScript *script The script
622  * @param FeriteUnifiedArray *left The first array
623  * @param FeriteUnifiedArray *right The second array
624  * @return FE_FALSE if the arrays differ, FE_TRUE otherwise
625  * @description This function checks not only the array elements but also the hash elements of the array.
626  */
ferite_uarray_cmp(FeriteScript * script,FeriteUnifiedArray * left,FeriteUnifiedArray * right)627 int ferite_uarray_cmp( FeriteScript *script, FeriteUnifiedArray *left, FeriteUnifiedArray *right )
628 {
629     int i = 0;
630 
631     FE_ENTER_FUNCTION;
632     if( left->size != right->size )
633     {
634         FE_LEAVE_FUNCTION(FE_FALSE);
635     }
636 
637    /* go through each element in the array */
638     for( i = 0; i < left->size; i++ )
639     {
640         /* check the type of the variables */
641         if( left->array[i]->type != right->array[i]->type )
642         {
643             FE_LEAVE_FUNCTION(FE_FALSE);
644         }
645 
646 #define FE_VAR_TEST( test ) \
647    if( !(test) ){           \
648     FE_LEAVE_FUNCTION(FE_FALSE); \
649    }
650         /* check names match, if they don't ignore it */
651         if( strcmp( left->array[i]->name, right->array[i]->name ) != 0 )
652         {
653             FE_LEAVE_FUNCTION(FE_FALSE);
654         }
655         if( strcmp( left->array[i]->name, "" ) != 0 )
656         {
657             /* they are hashed */
658             if( ferite_hash_get(script,left->hash,left->array[i]->name) == NULL || ferite_hash_get(script,right->hash,right->array[i]->name) == NULL )
659             {
660                 FE_LEAVE_FUNCTION(FE_FALSE);
661             }
662         }
663 
664         /* check their values */
665         switch( left->array[i]->type )
666         {
667           case F_VAR_LONG:
668             FE_VAR_TEST( VAI(left->array[i]) == VAI(right->array[i]) );
669             break;
670           case F_VAR_DOUBLE:
671             FE_VAR_TEST( VAF(left->array[i]) == VAF(right->array[i]) );
672             break;
673           case F_VAR_STR:
674             FE_VAR_TEST( ferite_str_cmp( VAS(left->array[i]), VAS(right->array[i]) ) == 1 );
675             break;
676           case F_VAR_OBJ:
677             FE_VAR_TEST( VAO(left->array[i]) == VAO(right->array[i]) );
678             break;
679           case F_VAR_UARRAY:
680             FE_VAR_TEST( ferite_uarray_cmp( script, VAUA(left->array[i]), VAUA(right->array[i]) ) == 1 );
681           default:
682             ferite_error( script, 0, "EEEK: unknown type %s in array comparison!\n", ferite_variable_id_to_str( script, left->array[i]->type ) );
683             FE_LEAVE_FUNCTION(FE_FALSE);
684         }
685 #undef FE_VAR_TEST
686     }
687    /* if we have got here they are the same :) */
688     FE_LEAVE_FUNCTION(FE_TRUE);
689 }
690 
691 /**
692  * @end
693  */
694