/* -*- c -*- |
* $Id: builtin.cmod,v 1.21 2001/06/21 01:21:50 per Exp $ |
*/ |
|
#include "global.h" |
#include "interpret.h" |
#include "svalue.h" |
#include "opcodes.h" |
#include "pike_macros.h" |
#include "object.h" |
#include "program.h" |
#include "array.h" |
#include "pike_error.h" |
#include "constants.h" |
#include "mapping.h" |
#include "stralloc.h" |
#include "multiset.h" |
#include "pike_types.h" |
#include "pike_memory.h" |
#include "threads.h" |
#include <math.h> |
#include <ctype.h> |
#include "module_support.h" |
#include "cyclic.h" |
#include "bignum.h" |
#include "main.h" |
#include "operators.h" |
#include "builtin_functions.h" |
|
//! Extract a column from a two-dimensional array. |
//! |
//! This function is exactly equivalent to: |
//! @code{map(@[data], lambda(mixed x,mixed y) { return x[y]; }, @[index])@} |
//! |
//! Except of course it is a lot shorter and faster. |
//! That is, it indices every index in the array data on the value of |
//! the argument index and returns an array with the results. |
//! |
//! @seealso |
//! @[rows()] |
//! |
PIKEFUN array column(array data, mixed index) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
INT32 e; |
struct array *a; |
|
DECLARE_CYCLIC(); |
|
/* Optimization */ |
if(data->refs == 1) |
{ |
/* An array with one ref cannot possibly be cyclic */ |
struct svalue sval; |
data->type_field = BIT_MIXED | BIT_UNFINISHED; |
for(e=0;e<data->size;e++) |
{ |
index_no_free(&sval, ITEM(data)+e, index); |
free_svalue(ITEM(data)+e); |
ITEM(data)[e]=sval; |
} |
pop_stack(); |
return; |
} |
|
if((a=(struct array *)BEGIN_CYCLIC(data,0))) |
{ |
add_ref(a); |
}else{ |
push_array(a=allocate_array(data->size)); |
SET_CYCLIC_RET(a); |
|
for(e=0;e<a->size;e++) |
index_no_free(ITEM(a)+e, ITEM(data)+e, index); |
|
sp--; |
} |
END_CYCLIC(); |
RETURN a; |
} |
|
//! This function creates a multiset from an array. |
//! |
//! @seealso |
//! @[aggregate_multiset()] |
//! |
PIKEFUN multiset(1) mkmultiset(array(1=mixed) a) |
efun; |
optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; |
{ |
RETURN mkmultiset(a); |
} |
|
//! This function changes the debug trace level. |
//! |
//! The old level is returned. |
//! |
//! Trace level 1 or higher means that calls to Pike functions are |
//! printed to stderr, level 2 or higher means calls to builtin functions |
//! are printed, 3 means every opcode interpreted is printed, 4 means |
//! arguments to these opcodes are printed as well. |
//! |
//! See the @tt{-t@} command-line option for more information. |
//! |
PIKEFUN int trace(int t) |
efun; |
optflags OPT_SIDE_EFFECT; |
{ |
pop_n_elems(args); |
push_int(t_flag); |
t_flag=t; |
} |
|
//! Convert the output from a previous call to @[time()] into a readable |
//! string containing the current year, month, day and time. |
//! |
//! @seealso |
//! @[time()], @[localtime()], @[mktime()], @[gmtime()] |
//! |
PIKEFUN string ctime(int timestamp) |
efun; |
optflags OPT_TRY_OPTIMIZE; |
{ |
time_t i=(time_t)timestamp; |
RETURN make_shared_string(ctime(&i)); |
} |
|
//! Make a mapping from two arrays. |
//! |
//! Makes a mapping @[ind[x]]:@[val[x]], @tt{0 <= x < sizeof(ind)@}. |
//! |
//! @[ind] and @[val] must have the same size. |
//! |
//! This is the inverse operation of @[indices()] and @[values()]. |
//! |
//! @seealso |
//! @[indices()], @[values()] |
//! |
PIKEFUN mapping(1:2) mkmapping(array(1=mixed) ind, array(2=mixed) val) |
efun; |
optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; |
{ |
if(ind->size != val->size) |
bad_arg_error("mkmapping", sp-args, args, 2, "array", sp+1-args, |
"mkmapping called on arrays of different sizes (%d != %d)\n", |
ind->size, val->size); |
|
RETURN mkmapping(ind, val); |
} |
|
//! Count the number of non-overlapping times the string @[needle] occurrs |
//! in the string @[haystack]. |
//! |
//! @seealso |
//! @[search()], @[`/()] |
//! |
PIKEFUN int string_count(string haystack, string needle) |
errname String.count; |
optflags OPT_TRY_OPTIMIZE; |
{ |
ptrdiff_t c = 0; |
ptrdiff_t i, j; |
|
switch (needle->len) |
{ |
case 0: |
switch (haystack->len) |
{ |
case 0: c=1; break; /* "" appears one time in "" */ |
case 1: c=0; break; /* "" doesn't appear in "x" */ |
default: c=haystack->len-1; /* one time between each character */ |
} |
break; |
case 1: |
/* maybe optimize? */ |
default: |
for (i=0; i<haystack->len; i++) |
{ |
j=string_search(haystack,needle,i); |
if (j==-1) break; |
i=j+needle->len-1; |
c++; |
} |
break; |
} |
RETURN DO_NOT_WARN((INT_TYPE)c); |
} |
|
//! Returns 1 if @[prog] implements @[api]. |
//! |
PIKEFUN int program_implements(program prog, program api) |
errname Program.implements; |
optflags OPT_TRY_OPTIMIZE; |
{ |
RETURN implements(prog, api); |
} |
|
//! Returns 1 if @[child] has inherited @[parent]. |
//! |
PIKEFUN int program_inherits(program child, program parent) |
errname Program.inherits; |
optflags OPT_TRY_OPTIMIZE; |
{ |
RETURN !!low_get_storage(parent, child); |
} |
|
//! Returns a string with filename and linenumber describing where |
//! the program @[p] was defined. |
//! |
//! The returned string is of the format @tt{"@i{filename@}:@i{linenumber@}"@}. |
//! |
//! If it cannot be determined where the program was defined, @tt{0@} (zero) |
//! will be returned. |
//! |
PIKEFUN string program_defined(program p) |
errname Program.defined; |
optflags OPT_TRY_OPTIMIZE; |
{ |
if(p && p->num_linenumbers) |
{ |
char *tmp; |
INT32 line; |
if((tmp=get_line(p->program, p, &line))) |
{ |
struct pike_string *tmp2; |
tmp2=make_shared_string(tmp); |
pop_n_elems(args); |
|
push_string(tmp2); |
if(line > 1) |
{ |
push_constant_text(":"); |
push_int(line); |
f_add(3); |
} |
return; |
} |
} |
|
pop_n_elems(args); |
push_int(0); |
} |
|
//! Returns the width of a string. |
//! |
//! Three return values are possible: |
//! @int |
//! @value 8 |
//! The string @[s] only contains characters <= 255. |
//! @value 16 |
//! The string @[s] only contains characters <= 65535. |
//! @value 32 |
//! The string @[s] contains characters >= 65536. |
//! @endint |
//! |
PIKEFUN int string_width(string s) |
errname String.width; |
optflags OPT_TRY_OPTIMIZE; |
{ |
RETURN 8 * (1 << s->size_shift); |
} |
|
//! Removes the entry with index @[index] from mapping @[map] destructively. |
//! |
//! If the mapping does not have an entry with index @[index], nothing is done. |
//! |
//! @returns |
//! The value that was removed will be returned. |
//! |
//! @note |
//! Note that @[m_delete()] changes @[map] destructively. |
//! |
//! @seealso |
//! @[mappingp()] |
//! |
PIKEFUN mixed m_delete(object|mapping map, mixed index) |
efun; |
optflags OPT_SIDE_EFFECT; |
{ |
/*FIXME: Should be |
* type function(mapping(1=mixed:2=mixed),1:2)| |
* function(object,mixed:mixed); |
* |
* or similar |
*/ |
if( map->type == T_MAPPING ) |
{ |
struct svalue s; |
map_delete_no_free(map->u.mapping, index, &s); |
pop_n_elems(args); |
*sp=s; |
sp++; |
} |
else if (map->type == T_OBJECT) |
{ |
int id = FIND_LFUN(map->u.object->prog, LFUN__M_DELETE); |
|
if( id == -1 ) |
SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object with _m_delete"); |
|
apply_low( map->u.object, id, 1 ); |
stack_swap(); |
pop_stack(); |
} else { |
SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object|mapping"); |
} |
} |
|
//! Returns the weak flag settings for @[m]. It's a combination of |
//! @[Pike.WEAK_INDICES] and @[Pike.WEAK_VALUES]. |
//! |
PIKEFUN int get_weak_flag(array|mapping|multiset m) |
efun; |
optflags OPT_EXTERNAL_DEPEND; |
{ |
int flag = 0; |
switch (m->type) { |
case T_ARRAY: |
flag = (m->u.array->flags & ARRAY_WEAK_FLAG) ? PIKE_WEAK_VALUES : 0; |
break; |
case T_MAPPING: |
flag = mapping_get_flags(m->u.mapping) & MAPPING_WEAK; |
break; |
case T_MULTISET: |
flag = (m->u.multiset->ind->flags & (ARRAY_WEAK_FLAG|ARRAY_WEAK_SHRINK)) ? |
PIKE_WEAK_INDICES : 0; |
break; |
default: |
SIMPLE_BAD_ARG_ERROR("get_weak_flag",1,"array|mapping|multiset"); |
} |
pop_n_elems(args); |
push_int(flag); |
} |
|
#define INITIAL_BUF_LEN 4096 |
|
/*! @module String |
*/ |
|
PIKECLASS Buffer |
/*! @class Buffer |
*! A buffer, used for building strings. It's |
*! conceptually similar to a string, but you can only @[add] |
*! strings to it, and you can only @[get] the value from it once. |
*! |
*! There is a reason for those seemingly rather odd limitations, |
*! it makes it possible to do some optimizations that really speed |
*! things up. |
*! |
*! You do not need to use this class unless you add very many |
*! strings together, or very large strings. |
*! |
*! @example |
*! For the fastest possible operation, write your code like this: |
*! |
*! @code{ |
*! String.Buffer b = String.Buffer( ); |
*! |
*! function add = b->add; |
*! |
*! .. call add several times in code ... |
*! |
*! string result = b->get(); // also clears the buffer |
*! @} |
*/ |
{ |
CVAR struct string_builder str; |
CVAR int initial; |
|
void f_Buffer_get_copy( INT32 args ); |
void f_Buffer_get( INT32 args ); |
void f_Buffer_add( INT32 args ); |
|
|
PIKEFUN void create( int|void size ) |
/*! @decl void create() |
*! |
*! Initializes a new buffer. |
*! |
*! If no @[initial_size] is specified, 4096 is used. If you |
*! know approximately how big the buffer will be, you can optimize |
*! the operation of @[add()] (slightly) by passing the size to this |
*! function. |
*/ |
{ |
struct Buffer_struct *str = THIS; |
if( args ) |
str->initial = MAXIMUM( size->u.integer, 512 ); |
else |
{ |
str->initial = 256; |
push_int(0); |
} |
} |
|
PIKEFUN string _sprintf( int flag, mapping flags ) |
{ |
switch( flag ) |
{ |
case 'O': |
{ |
struct pike_string *res; |
struct Buffer_struct *str = THIS; |
push_text( "Buffer(%d /* %d */)" ); |
if( str->str.s ) |
{ |
push_int(str->str.s->len); |
push_int(str->str.malloced); |
} |
else |
{ |
push_int( 0 ); |
push_int( 0 ); |
} |
f_sprintf( 3 ); |
res = Pike_sp[-1].u.string; |
Pike_sp--; |
RETURN res; |
} |
|
case 's': |
{ |
pop_n_elems( args ); |
if( Pike_fp->current_object->refs != 1 ) |
f_Buffer_get_copy( 0 ); |
else |
f_Buffer_get( 0 ); |
} |
return; |
|
case 't': |
RETURN make_shared_binary_string("Buffer",6); |
} |
pop_n_elems( args ); |
push_int( 0 ); |
Pike_sp[-1].subtype = 1; |
} |
|
PIKEFUN mixed cast( string type ) |
{ |
struct pike_string *string_t; |
MAKE_CONSTANT_SHARED_STRING( string_t, "string" ); |
|
if( type == string_t ) |
{ |
pop_n_elems( args ); |
if( Pike_fp->current_object->refs != 1 ) |
f_Buffer_get_copy( 0 ); |
else |
f_Buffer_get( 0 ); |
return; |
} |
Pike_error("Cannot cast to %s\n", type->str ); |
} |
|
PIKEFUN object `+( string what ) |
{ |
struct Buffer_struct *str = THIS, *str2; |
struct object *res = clone_object( Buffer_program, 0 ); |
|
if( str->str.s ) |
{ |
str2 = OBJ2_BUFFER( res ); |
|
if( str2->str.s ) free_string_builder( &str2->str ); |
*str2 = *str; |
init_string_builder_alloc( &str2->str, |
str->str.malloced, |
str->str.s->size_shift ); |
MEMCPY( (void *)str2->str.s, (void *)str->str.s, |
str->str.malloced+sizeof(struct pike_string)); |
} |
apply( res, "add", 1 ); |
RETURN res; |
} |
|
PIKEFUN object `+=( string what ) |
{ |
f_Buffer_add( 1 ); |
REF_RETURN fp->current_object; |
} |
|
PIKEFUN int add( string ... arg1 ) |
/*! @decl void add(string ... data) |
*! |
*! Adds @[data] to the buffer. Returns the size of the buffer. |
*! |
*/ |
{ |
struct Buffer_struct *str = THIS; |
int j; |
struct pike_string *a; |
|
for( j = 0; j<args; j++ ) |
{ |
a = Pike_sp[-args+j].u.string; |
if( !str->str.s ) |
init_string_builder_alloc( &str->str, str->initial, a->size_shift ); |
string_builder_shared_strcat( &str->str, a ); |
} |
RETURN str->str.s->len; |
} |
|
PIKEFUN string get_copy() |
/*! @decl string get_copy() |
*! |
*! Get the data from the buffer. Significantly slower than @[get], |
*! but does not clear the buffer. |
*/ |
{ |
struct pike_string *str = THIS->str.s; |
if( str ) |
{ |
ptrdiff_t len = str->len; |
if( len > 0 ) |
{ |
char *d = (char *)str->str; |
switch( str->size_shift ) |
{ |
case 0: |
RETURN make_shared_binary_string(d,len); |
break; |
case 1: |
RETURN make_shared_binary_string1((short*)d,len>>1); |
break; |
case 2: |
RETURN make_shared_binary_string2((int*)d,len>>2); |
break; |
} |
} |
} |
push_text(""); |
return; |
} |
|
PIKEFUN string get( ) |
/*! @decl string get() |
*! |
*! Get the data from the buffer. |
*! |
*! @note |
*! This will clear the data in the buffer |
*/ |
{ |
struct Buffer_struct *str = THIS; |
if( str->str.s ) |
{ |
struct pike_string *s = finish_string_builder( &str->str ); |
str->str.malloced = 0; |
str->str.s = 0; |
RETURN s; |
} |
pop_n_elems(args); |
push_text(""); |
} |
|
INIT |
{ |
struct Buffer_struct *str = THIS; |
MEMSET( str, 0, sizeof( *str ) ); |
} |
|
EXIT |
{ |
struct Buffer_struct *str = THIS; |
if( str->str.s ) |
free_string_builder( &str->str ); |
} |
} |
|
/* @endmodule */ |
|
void init_builtin(void) |
{ |
INIT |
} |
|