e576bb2002-10-11Martin Nilsson /* -*- c -*- || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information.
d191622006-03-22Henrik Grubbström (Grubba) || $Id: builtin.cmod,v 1.182 2006/03/22 19:08:39 grubba Exp $
e576bb2002-10-11Martin Nilsson */
c3da7f2000-07-04Martin Stjernholm 
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) #include "global.h" #include "interpret.h" #include "svalue.h" #include "pike_macros.h" #include "object.h" #include "program.h" #include "array.h"
bb8a782000-12-01Fredrik Hübinette (Hubbe) #include "pike_error.h"
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) #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"
098c802000-05-24Fredrik Hübinette (Hubbe) #include "main.h"
b8c5b22000-05-25Fredrik Hübinette (Hubbe) #include "operators.h"
9da7f42001-06-05Martin Stjernholm #include "builtin_functions.h"
fed7de2001-06-28Henrik Grubbström (Grubba) #include "fsort.h"
812f9a2002-04-06Henrik Grubbström (Grubba) #include "port.h"
51adb82003-01-12Martin Stjernholm #include "gc.h"
db628a2004-09-10Henrik Grubbström (Grubba) #include "block_alloc.h"
73b08f2003-01-31Martin Stjernholm #include <assert.h>
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) 
7faf1e2005-01-08Henrik Grubbström (Grubba) /*! @decl array(array(int|string|type)) describe_program(program p)
2d8e642002-11-25Martin Nilsson  *! @belongs Debug
7d1b3b2001-12-21Henrik Grubbström (Grubba)  *! *! Debug function for showing the symbol table of a program.
7faf1e2005-01-08Henrik Grubbström (Grubba)  *! *! @returns *! Returns an array of arrays with the following information *! for each symbol in @[p]: *! @array *! @elem int modifiers *! Bitfield with the modifiers for the symbol. *! @elem string symbol_name *! Name of the symbol. *! @elem type value_type *! Value type for the symbol. *! @elem int symbol_type *! Type of symbol. *! @elem int symbol_offset *! Offset into the code or data area for the symbol. *! @elem int inherit_offset *! Offset in the inherit table to the inherit containing *! the symbol. *! @elem int inherit_level *! Depth in the inherit tree for the inherit containing *! the symbol. *! @endarray *! *! @note *! The API for this function is not fixed, and has changed *! since Pike 7.6. In particular it would make sense to return *! an array of objects instead, and more information about the *! symbols might be added.
7d1b3b2001-12-21Henrik Grubbström (Grubba)  */ PIKEFUN array(array(int|string)) _describe_program(mixed x) efun; { struct program *p; struct array *res; int i;
d4ecd72003-01-05Martin Nilsson  if (!(p = program_from_svalue(Pike_sp - args))) SIMPLE_BAD_ARG_ERROR("_describe_program", 1, "program");
7d1b3b2001-12-21Henrik Grubbström (Grubba)  for (i=0; i < (int)p->num_identifier_references;i++) { struct reference *ref = p->identifier_references + i; struct identifier *id = ID_FROM_PTR(p, ref);
cab69e2002-04-24Henrik Grubbström (Grubba)  struct inherit *inh = INHERIT_FROM_PTR(p, ref);
7d1b3b2001-12-21Henrik Grubbström (Grubba)  push_int(ref->id_flags); ref_push_string(id->name);
7faf1e2005-01-08Henrik Grubbström (Grubba)  ref_push_type_value(id->type);
cab69e2002-04-24Henrik Grubbström (Grubba)  push_int(id->identifier_flags);
c278f72003-08-03Martin Stjernholm  if (IDENTIFIER_IS_C_FUNCTION(id->identifier_flags)) {
cab69e2002-04-24Henrik Grubbström (Grubba)  push_int(-2); } else { push_int(id->func.offset); } push_int(ref->inherit_offset); push_int(inh->inherit_level);
7faf1e2005-01-08Henrik Grubbström (Grubba)  f_aggregate(7);
7d1b3b2001-12-21Henrik Grubbström (Grubba)  } f_aggregate(p->num_identifier_references);
cdf18a2003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
cbaa3a2002-05-24Henrik Grubbström (Grubba)  res = Pike_sp[-1].u.array; Pike_sp--;
7d1b3b2001-12-21Henrik Grubbström (Grubba)  pop_n_elems(args); push_array(res); }
d9a93b2001-07-01Fredrik Hübinette (Hubbe) /*! @decl string basetype(mixed x) *! *! Same as sprintf("%t",x); *! *! @seealso *! @[sprintf()] */ PIKEFUN string basetype(mixed x) efun; optflags OPT_TRY_OPTIMIZE; { int t=x->type;
2a8be12004-12-21Henrik Grubbström (Grubba)  struct program *p; if(x->type == T_OBJECT && (p = x->u.object->prog))
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  {
2a8be12004-12-21Henrik Grubbström (Grubba)  ptrdiff_t fun = FIND_LFUN(p->inherits[x->subtype].prog, LFUN__SPRINTF);
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  if(fun != -1) { push_int('t'); f_aggregate_mapping(0);
2a8be12004-12-21Henrik Grubbström (Grubba)  apply_low(x->u.object, fun + p->inherits[x->subtype].identifier_level, 2);
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  if(Pike_sp[-1].type == T_STRING) { stack_swap(); pop_stack(); return;
9f516a2001-12-16Martin Stjernholm  } else if (UNSAFE_IS_ZERO(Pike_sp-1)) {
63a7302001-07-02Henrik Grubbström (Grubba)  pop_n_elems(2);
9db2bf2001-07-02Henrik Grubbström (Grubba)  push_constant_text("object"); return; } else { Pike_error("Non-string returned from _sprintf()\n");
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  } } } pop_stack(); switch(t) { case T_ARRAY: push_constant_text("array"); break; case T_FLOAT: push_constant_text("float"); break; case T_FUNCTION: push_constant_text("function"); break; case T_INT: push_constant_text("int"); break; case T_MAPPING: push_constant_text("mapping"); break; case T_MULTISET: push_constant_text("multiset"); break; case T_OBJECT: push_constant_text("object"); break; case T_PROGRAM: push_constant_text("program"); break; case T_STRING: push_constant_text("string"); break; case T_TYPE: push_constant_text("type"); break; case T_ZERO: push_constant_text("zero"); break; case T_VOID: push_constant_text("void"); break;
a903032003-02-16Martin Stjernholm  /* The following are internal and shouldn't be applicable in normal use. */ case T_SVALUE_PTR: push_constant_text("svalue_ptr"); break; case T_OBJ_INDEX: push_constant_text("obj_index"); break;
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  case T_MAPPING_DATA: push_constant_text("mapping_data"); break;
a903032003-02-16Martin Stjernholm  case T_PIKE_FRAME: push_constant_text("pike_frame"); break; case T_MULTISET_DATA: push_constant_text("multiset_data"); break;
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  default: push_constant_text("unknown"); break; } } /*! @decl string int2char(int x)
ba24802002-04-07Martin Nilsson  *! @appears String.int2char
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  *! *! Same as sprintf("%c",x); *! *! @seealso *! @[sprintf()] */ PIKEFUN string int2char(int|object x) efun; optflags OPT_TRY_OPTIMIZE; { int c;
2a8be12004-12-21Henrik Grubbström (Grubba)  struct program *p; if(x->type == T_OBJECT && (p = x->u.object->prog))
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  {
2a8be12004-12-21Henrik Grubbström (Grubba)  ptrdiff_t fun = FIND_LFUN(p->inherits[x->subtype].prog, LFUN__SPRINTF);
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  if(fun != -1) { push_int('c'); f_aggregate_mapping(0);
2a8be12004-12-21Henrik Grubbström (Grubba)  apply_low(x->u.object, fun + p->inherits[x->subtype].identifier_level, 2);
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  if(Pike_sp[-1].type == T_STRING) { stack_swap(); pop_stack(); return; }
8f5b5b2001-07-02Henrik Grubbström (Grubba)  Pike_error("Non-string returned from _sprintf()\n");
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  } } if(x->type != T_INT)
d4ecd72003-01-05Martin Nilsson  SIMPLE_BAD_ARG_ERROR("int2char", 1, "int");
d9a93b2001-07-01Fredrik Hübinette (Hubbe)  c=x->u.integer; if(c>=0 && c<256) { struct pike_string *s; s=begin_shared_string(1); s->str[0]=c; RETURN end_shared_string(s); }else{ struct string_builder tmp; init_string_builder(&tmp,0); string_builder_putchar(&tmp, c); RETURN finish_string_builder(&tmp); } }
7b45292001-08-15Fredrik Hübinette (Hubbe) /*! @decl string int2hex(int x)
ba24802002-04-07Martin Nilsson  *! @appears String.int2hex
7b45292001-08-15Fredrik Hübinette (Hubbe)  *!
e97b362004-04-25Martin Nilsson  *! Same as @expr{sprintf("%x",x);@}, i.e. returns the integer @[x] in *! hexadecimal base using lower cased symbols.
7b45292001-08-15Fredrik Hübinette (Hubbe)  *! *! @seealso *! @[sprintf()] */ PIKEFUN string int2hex(int|object x) efun; optflags OPT_TRY_OPTIMIZE; { INT_TYPE c;
a973872003-01-26Mirar (Pontus Hagland)  unsigned INT_TYPE n;
7b45292001-08-15Fredrik Hübinette (Hubbe)  int len; struct pike_string *s;
2a8be12004-12-21Henrik Grubbström (Grubba)  struct program *p;
7b45292001-08-15Fredrik Hübinette (Hubbe) 
2a8be12004-12-21Henrik Grubbström (Grubba)  if(x->type == T_OBJECT && (p = x->u.object->prog))
7b45292001-08-15Fredrik Hübinette (Hubbe)  {
2a8be12004-12-21Henrik Grubbström (Grubba)  ptrdiff_t fun = FIND_LFUN(p->inherits[x->subtype].prog, LFUN__SPRINTF);
7b45292001-08-15Fredrik Hübinette (Hubbe)  if(fun != -1) { push_int('x'); f_aggregate_mapping(0);
2a8be12004-12-21Henrik Grubbström (Grubba)  apply_low(x->u.object, fun + p->inherits[x->subtype].identifier_level, 2);
7b45292001-08-15Fredrik Hübinette (Hubbe)  if(Pike_sp[-1].type == T_STRING) { stack_swap(); pop_stack(); return; } Pike_error("Non-string returned from _sprintf()\n"); } } if(x->type != T_INT)
d4ecd72003-01-05Martin Nilsson  SIMPLE_BAD_ARG_ERROR("int2hex", 1, "int");
7b45292001-08-15Fredrik Hübinette (Hubbe)  c=x->u.integer; len=1; if(c<0) { len++;
9c3a7c2003-01-26Mirar (Pontus Hagland)  n=(-c)&((unsigned INT_TYPE)(-1));
7b45292001-08-15Fredrik Hübinette (Hubbe)  }else{ n=c; }
301e982002-01-18Martin Nilsson  while(n>65535) { n>>=16; len+=4; } while(n>15) { n>>=4; len++; }
7b45292001-08-15Fredrik Hübinette (Hubbe)  s=begin_shared_string(len); if(!c) { s->str[0]='0'; }else{ if(c<0) { s->str[0]='-';
9c3a7c2003-01-26Mirar (Pontus Hagland)  n=(-c)&((unsigned INT_TYPE)(-1));
7b45292001-08-15Fredrik Hübinette (Hubbe)  }else{ n=c; }
301e982002-01-18Martin Nilsson  while(len && n)
7b45292001-08-15Fredrik Hübinette (Hubbe)  { s->str[--len]="0123456789abcdef"[n&0xf]; n>>=4; } } RETURN end_shared_string(s); }
8d251e2003-09-05Martin Nilsson 
54a1302004-06-02Martin Nilsson static INLINE int hexchar( int v )
8d251e2003-09-05Martin Nilsson { return v<10 ? v+'0' : (v-10)+'a'; }
b2d3ca2003-08-26Martin Nilsson /*! @decl string string2hex(string data) *! @appears String.string2hex *! *! Convert a string of binary data to a hexadecimal string. *! *! @seealso *! @[hex2string()] */ PIKEFUN string string2hex(string s) errname String.string2hex; optflags OPT_TRY_OPTIMIZE; { struct pike_string *hex;
8d251e2003-09-05Martin Nilsson  unsigned char *st = s->str; int i;
b2d3ca2003-08-26Martin Nilsson  if (s->size_shift) Pike_error("Bad argument 1 to string2hex(), expected 8-bit string.\n"); hex = begin_shared_string(2 * s->len);
8d251e2003-09-05Martin Nilsson  for (i=0; i<s->len; i++) { hex->str[i<<1] = hexchar(st[i]>>4); hex->str[i<<1|1] = hexchar(st[i]&15); }
b2d3ca2003-08-26Martin Nilsson  RETURN end_shared_string(hex); } /*! @decl string hex2string(string hex) *! @appears String.hex2string *! *! Convert a string of hexadecimal digits to binary data. *! *! @seealso *! @[string2hex()] */ PIKEFUN string hex2string(string hex) errname String.hex2string; optflags OPT_TRY_OPTIMIZE; { struct pike_string *s;
8d251e2003-09-05Martin Nilsson  int i, o=0; unsigned char *q = hex->str; int l = hex->len>>1; if(hex->size_shift) Pike_error("Only hex digits allowed.\n"); if(hex->len&1) Pike_error("Can't have odd number of digits.\n");
b2d3ca2003-08-26Martin Nilsson 
8d251e2003-09-05Martin Nilsson  s = begin_shared_string(l); for (i=0; i<l; i++)
b2d3ca2003-08-26Martin Nilsson  {
8d251e2003-09-05Martin Nilsson  s->str[i] = (q[o]<='9' ? q[o]-'0' :((q[o]+9)&15))<<4; o++; s->str[i] |= (q[o]<='9' ? q[o]-'0': ((q[o]+9)&15)); o++;
b2d3ca2003-08-26Martin Nilsson  } RETURN end_shared_string(s); }
0498332001-02-10Henrik Grubbström (Grubba) /*! @decl array column(array data, mixed index) *! *! Extract a column from a two-dimensional array. *! *! This function is exactly equivalent to:
f79bd82003-04-01Martin Nilsson  *! @code *! map(@[data], lambda(mixed x,mixed y) { return x[y]; }, @[index]) *! @endcode
0498332001-02-10Henrik Grubbström (Grubba)  *! *! 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()] */
b0f8352001-01-07Henrik Grubbström (Grubba) PIKEFUN array column(array data, mixed index)
3a5b1d2000-05-24Fredrik Hübinette (Hubbe)  efun; optflags OPT_TRY_OPTIMIZE; {
bcd8012003-04-28Martin Stjernholm  RETURN array_column (data, index, 1);
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) }
0498332001-02-10Henrik Grubbström (Grubba) /*! @decl multiset mkmultiset(array a) *! *! This function creates a multiset from an array. *! *! @seealso *! @[aggregate_multiset()] *! */
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) PIKEFUN multiset(1) mkmultiset(array(1=mixed) a) efun;
8f998d2000-08-31Henrik Grubbström (Grubba)  optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND;
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) { RETURN mkmultiset(a); }
50d97a2003-02-01Martin Stjernholm /*! @decl int trace(int level, void|string facility, void|int all_threads)
0498332001-02-10Henrik Grubbström (Grubba)  *!
50d97a2003-02-01Martin Stjernholm  *! This function changes the trace level for the subsystem identified *! by @[facility] to @[level]. If @[facility] is zero or left out, it *! changes the global trace level which affects all subsystems.
0498332001-02-10Henrik Grubbström (Grubba)  *!
50d97a2003-02-01Martin Stjernholm  *! Enabling tracing causes messages to be printed to stderr. A higher *! trace level includes the output from all lower levels. The lowest *! level is zero which disables all trace messages.
0498332001-02-10Henrik Grubbström (Grubba)  *! *! See the @tt{-t@} command-line option for more information.
6198092003-01-08Martin Stjernholm  *!
50d97a2003-02-01Martin Stjernholm  *! @param level *! If @[facility] is specified then there is typically only one *! trace level for it, i.e. it's an on-or-off toggle. The global *! trace levels, when @[facility] isn't specified, are: *! *! @int
2fe0542003-02-01Martin Stjernholm  *! @value 1
50d97a2003-02-01Martin Stjernholm  *! Trace calls to Pike functions and garbage collector runs.
2fe0542003-02-01Martin Stjernholm  *! @value 2
50d97a2003-02-01Martin Stjernholm  *! Trace calls to builtin functions.
2fe0542003-02-01Martin Stjernholm  *! @value 3
50d97a2003-02-01Martin Stjernholm  *! Trace every interpreted opcode.
2fe0542003-02-01Martin Stjernholm  *! @value 4
50d97a2003-02-01Martin Stjernholm  *! Also trace the opcode arguments. *! @endint *! *! @param facility *! Valid facilities are: *! *! @string
2fe0542003-02-01Martin Stjernholm  *! @value "gc"
50d97a2003-02-01Martin Stjernholm  *! Trace the start and end of each run of the garbage collector. *! The setting is never thread local. *! @endstring
6198092003-01-08Martin Stjernholm  *! *! @param all_threads
50d97a2003-02-01Martin Stjernholm  *! Trace levels are normally thread local, so changes affect only *! the current thread. To change the level in all threads, pass a *! nonzero value in this argument.
6198092003-01-08Martin Stjernholm  *! *! @returns
50d97a2003-02-01Martin Stjernholm  *! The old trace level in the current thread is returned.
0498332001-02-10Henrik Grubbström (Grubba)  */
50d97a2003-02-01Martin Stjernholm PIKEFUN int trace(int level, void|string facility, void|int all_threads)
098c802000-05-24Fredrik Hübinette (Hubbe)  efun; optflags OPT_SIDE_EFFECT; {
50d97a2003-02-01Martin Stjernholm  INT32 old_level; if (facility) { struct pike_string *gc_str;
de56ec2003-02-08Martin Stjernholm  MAKE_CONST_STRING(gc_str, "gc");
fe21442004-09-02Henrik Grubbström (Grubba)  if (facility == gc_str) {
50d97a2003-02-01Martin Stjernholm  old_level = gc_trace; gc_trace = level; } else { bad_arg_error("trace", Pike_sp-args, args, 2, "trace facility identifier", Pike_sp-args+1, "Bad argument 2 to trace(). Unknown trace facility."); }
6198092003-01-08Martin Stjernholm  }
50d97a2003-02-01Martin Stjernholm  else { old_level = Pike_interpreter.trace_level; #ifdef PIKE_THREADS if (!all_threads || UNSAFE_IS_ZERO (all_threads)) Pike_interpreter.trace_level = level; else { struct thread_state *s; FOR_EACH_THREAD(s, s->state.trace_level = level); }
6198092003-01-08Martin Stjernholm #else
50d97a2003-02-01Martin Stjernholm  Pike_interpreter.trace_level = level;
6198092003-01-08Martin Stjernholm #endif
50d97a2003-02-01Martin Stjernholm  } RETURN old_level;
098c802000-05-24Fredrik Hübinette (Hubbe) }
51adb82003-01-12Martin Stjernholm /*! @decl mapping(string:float) gc_parameters (void|mapping(string:mixed) params) *! @belongs Pike *! *! Set and get various parameters that control the operation of the *! garbage collector. The passed mapping contains the parameters to *! set. If a parameter is missing from the mapping, the current value *! will be filled in instead. The same mapping is returned. Thus an *! empty mapping, or no argument at all, causes a mapping with all *! current settings to be returned. *! *! The following parameters are recognized: *! *! @mapping *! @member int "enabled"
0d9f932003-01-14Martin Stjernholm  *! If this is 1 then the gc is enabled as usual. If it's 0 then all *! automatically scheduled gc runs are disabled and the parameters *! below have no effect, but explicit runs through the @[gc] *! function still works as usual. If it's -1 then the gc is *! completely disabled so that even explicit @[gc] calls won't do *! anything.
51adb82003-01-12Martin Stjernholm  *! @member float "garbage_ratio_low" *! As long as the gc time is less than gc_time_ratio, aim to run *! the gc approximately every time the ratio between the garbage *! and the total amount of allocated things is this. *! @member float "time_ratio" *! When more than this fraction of the cpu time is spent in the gc, *! aim for gc_garbage_ratio_high instead of gc_garbage_ratio_low. *! @member float "garbage_ratio_high" *! Upper limit for the garbage ratio - run the gc as often as it *! takes to keep it below this. *! @member float "average_slowness" *! When predicting the next gc interval, use a decaying average *! with this slowness factor. It should be a value between 0.0 and *! 1.0 that specifies the weight to give to the old average value. *! The remaining weight up to 1.0 is given to the last reading. *! @endmapping *! *! @seealso *! @[gc], @[Debug.gc_status] */ PIKEFUN mapping(string:mixed) gc_parameters (void|mapping(string:mixed) params) errname Pike.gc_parameters; optflags OPT_SIDE_EFFECT; { struct pike_string *str; struct svalue *set; struct svalue get; if (!params) { push_mapping (allocate_mapping (5));
fe21442004-09-02Henrik Grubbström (Grubba)  params = Pike_sp[-1].u.mapping;
51adb82003-01-12Martin Stjernholm  } #define HANDLE_PARAM(NAME, CHECK_AND_SET, GET) do { \
de56ec2003-02-08Martin Stjernholm  MAKE_CONST_STRING (str, NAME); \
fe21442004-09-02Henrik Grubbström (Grubba)  if ((set = low_mapping_string_lookup (params, str))) { \
51adb82003-01-12Martin Stjernholm  CHECK_AND_SET; \ } \ else { \ GET; \
fe21442004-09-02Henrik Grubbström (Grubba)  mapping_string_insert (params, str, &get); \
51adb82003-01-12Martin Stjernholm  } \ } while (0) #define HANDLE_FLOAT_FACTOR(NAME, VAR) \ HANDLE_PARAM (NAME, { \ if (set->type != T_FLOAT || \ set->u.float_number < 0.0 || set->u.float_number > 1.0) \ SIMPLE_BAD_ARG_ERROR ("Pike.gc_parameters", 1, \
0d9f932003-01-14Martin Stjernholm  "float between 0.0 and 1.0 for " NAME); \
51adb82003-01-12Martin Stjernholm  VAR = set->u.float_number; \ }, { \ get.type = T_FLOAT; \ get.u.float_number = VAR; \ }); HANDLE_PARAM ("enabled", {
0d9f932003-01-14Martin Stjernholm  if (set->type != T_INT || set->u.integer < -1 || set->u.integer > 1) SIMPLE_BAD_ARG_ERROR ("Pike.gc_parameters", 1, "integer in the range -1..1 for 'enabled'"); if (gc_enabled != set->u.integer) {
bbd8162003-01-15Martin Stjernholm  if (gc_enabled > 0)
0d9f932003-01-14Martin Stjernholm  gc_enabled = set->u.integer;
51adb82003-01-12Martin Stjernholm  else { gc_enabled = 1;
bbd8162003-01-15Martin Stjernholm  if (alloc_threshold == GC_MAX_ALLOC_THRESHOLD) alloc_threshold = GC_MIN_ALLOC_THRESHOLD;
51adb82003-01-12Martin Stjernholm  } } }, { get.type = T_INT; get.u.integer = gc_enabled; }); HANDLE_FLOAT_FACTOR ("garbage_ratio_low", gc_garbage_ratio_low); HANDLE_FLOAT_FACTOR ("time_ratio", gc_time_ratio); HANDLE_FLOAT_FACTOR ("garbage_ratio_high", gc_garbage_ratio_high); HANDLE_FLOAT_FACTOR ("average_slowness", gc_average_slowness); #undef HANDLE_PARAM #undef HANDLE_FLOAT_FACTOR
fe21442004-09-02Henrik Grubbström (Grubba)  REF_RETURN params;
51adb82003-01-12Martin Stjernholm }
d6fd962001-02-10Henrik Grubbström (Grubba) /*! @decl string ctime(int timestamp) *!
0498332001-02-10Henrik Grubbström (Grubba)  *! Convert the output from a previous call to @[time()] into a readable *! string containing the current year, month, day and time. *!
f917a32002-10-03Martin Stjernholm  *! Like @[localtime], this function might throw an error if the *! ctime(2) call failed on the system. It's platform dependent what *! time ranges that function can handle, e.g. Windows doesn't handle *! a negative @[timestamp].
21f3f62002-10-03Martin Stjernholm  *!
0498332001-02-10Henrik Grubbström (Grubba)  *! @seealso *! @[time()], @[localtime()], @[mktime()], @[gmtime()] */
b0f8352001-01-07Henrik Grubbström (Grubba) PIKEFUN string ctime(int timestamp)
098c802000-05-24Fredrik Hübinette (Hubbe)  efun; optflags OPT_TRY_OPTIMIZE; {
b0f8352001-01-07Henrik Grubbström (Grubba)  time_t i=(time_t)timestamp;
21f3f62002-10-03Martin Stjernholm  char *s = ctime (&i);
f917a32002-10-03Martin Stjernholm  if (!s) Pike_error ("ctime() on this system cannot handle " "the timestamp %ld.\n", (long) i);
21f3f62002-10-03Martin Stjernholm  RETURN make_shared_string(s);
098c802000-05-24Fredrik Hübinette (Hubbe) }
0498332001-02-10Henrik Grubbström (Grubba) /*! @decl mapping mkmapping(array ind, array val) *! *! 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()] */
b0f8352001-01-07Henrik Grubbström (Grubba) PIKEFUN mapping(1:2) mkmapping(array(1=mixed) ind, array(2=mixed) val)
098c802000-05-24Fredrik Hübinette (Hubbe)  efun;
8f998d2000-08-31Henrik Grubbström (Grubba)  optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND;
098c802000-05-24Fredrik Hübinette (Hubbe) {
b0f8352001-01-07Henrik Grubbström (Grubba)  if(ind->size != val->size)
cbaa3a2002-05-24Henrik Grubbström (Grubba)  bad_arg_error("mkmapping", Pike_sp-args, args, 2, "array", Pike_sp+1-args,
098c802000-05-24Fredrik Hübinette (Hubbe)  "mkmapping called on arrays of different sizes (%d != %d)\n",
b0f8352001-01-07Henrik Grubbström (Grubba)  ind->size, val->size);
098c802000-05-24Fredrik Hübinette (Hubbe) 
b0f8352001-01-07Henrik Grubbström (Grubba)  RETURN mkmapping(ind, val);
098c802000-05-24Fredrik Hübinette (Hubbe) }
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) 
31acb72001-07-26Martin Nilsson /*! @decl int count(string haystack, string needle) *! @belongs String
0498332001-02-10Henrik Grubbström (Grubba)  *!
c5e2a42004-04-30Martin Nilsson  *! Count the number of non-overlapping times the string @[needle] *! occurs in the string @[haystack]. The special cases for the needle *! @expr{""@} is that it occurs one time in the empty string, zero *! times in a one character string and between every character *! (length-1) in any other string.
0498332001-02-10Henrik Grubbström (Grubba)  *! *! @seealso *! @[search()], @[`/()] */
6613052000-08-10Henrik Grubbström (Grubba) PIKEFUN int string_count(string haystack, string needle)
991fdf2000-05-25Fredrik Hübinette (Hubbe)  errname String.count; optflags OPT_TRY_OPTIMIZE; {
89fc4c2000-08-10Henrik Grubbström (Grubba)  ptrdiff_t c = 0; ptrdiff_t i, j;
991fdf2000-05-25Fredrik Hübinette (Hubbe)  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? */
c5e2a42004-04-30Martin Nilsson  /* It is already fairly optimized in pike_search_engine. */
991fdf2000-05-25Fredrik Hübinette (Hubbe)  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; }
6613052000-08-10Henrik Grubbström (Grubba)  RETURN DO_NOT_WARN((INT_TYPE)c);
991fdf2000-05-25Fredrik Hübinette (Hubbe) }
31acb72001-07-26Martin Nilsson /*! @decl string trim_whites (string s) *! @belongs String
5117f12001-04-16Martin Stjernholm  *! *! Trim leading and trailing spaces and tabs from the string @[s]. */ PIKEFUN string string_trim_whites (string s) errname String.trim_whites; optflags OPT_TRY_OPTIMIZE; { ptrdiff_t start = 0, end = s->len; int chr; switch (s->size_shift) { #define DO_IT(TYPE) \ { \ for (; start < s->len; start++) { \ chr = ((TYPE *) s->str)[start]; \ if (chr != ' ' && chr != '\t') break; \ } \ while (--end > start) { \ chr = ((TYPE *) s->str)[end]; \ if (chr != ' ' && chr != '\t') break; \ } \ } case 0: DO_IT (p_wchar0); break; case 1: DO_IT (p_wchar1); break; case 2: DO_IT (p_wchar2); break; #undef DO_IT } RETURN string_slice (s, start, end + 1 - start); }
31acb72001-07-26Martin Nilsson /*! @decl string trim_all_whites (string s) *! @belongs String
5117f12001-04-16Martin Stjernholm  *! *! Trim leading and trailing white spaces characters (space, tab, *! newline and carriage return) from the string @[s]. */ PIKEFUN string string_trim_all_whites (string s) errname String.trim_all_whites; optflags OPT_TRY_OPTIMIZE; { ptrdiff_t start = 0, end = s->len; int chr; switch (s->size_shift) { #define DO_IT(TYPE) \ { \ for (; start < s->len; start++) { \ chr = ((TYPE *) s->str)[start]; \ if (chr != ' ' && chr != '\t' && chr != '\n' && chr != '\r') \ break; \ } \ while (--end > start) { \ chr = ((TYPE *) s->str)[end]; \ if (chr != ' ' && chr != '\t' && chr != '\n' && chr != '\r') \ break; \ } \ } case 0: DO_IT (p_wchar0); break; case 1: DO_IT (p_wchar1); break; case 2: DO_IT (p_wchar2); break; #undef DO_IT } RETURN string_slice (s, start, end + 1 - start); }
31acb72001-07-26Martin Nilsson /*! @decl int implements(program prog, program api) *! @belongs Program
0498332001-02-10Henrik Grubbström (Grubba)  *! *! Returns 1 if @[prog] implements @[api]. */
b0f8352001-01-07Henrik Grubbström (Grubba) PIKEFUN int program_implements(program prog, program api)
991fdf2000-05-25Fredrik Hübinette (Hubbe)  errname Program.implements; optflags OPT_TRY_OPTIMIZE; {
b0f8352001-01-07Henrik Grubbström (Grubba)  RETURN implements(prog, api);
991fdf2000-05-25Fredrik Hübinette (Hubbe) }
31acb72001-07-26Martin Nilsson /*! @decl int inherits(program child, program parent) *! @belongs Program
0498332001-02-10Henrik Grubbström (Grubba)  *! *! Returns 1 if @[child] has inherited @[parent]. */
f3c7152001-04-14Fredrik Hübinette (Hubbe) PIKEFUN int program_inherits(program parent, program child)
991fdf2000-05-25Fredrik Hübinette (Hubbe)  errname Program.inherits; optflags OPT_TRY_OPTIMIZE; {
f3c7152001-04-14Fredrik Hübinette (Hubbe)  RETURN low_get_storage(parent, child) != -1;
991fdf2000-05-25Fredrik Hübinette (Hubbe) }
31acb72001-07-26Martin Nilsson /*! @decl string defined(program p) *! @belongs Program
0498332001-02-10Henrik Grubbström (Grubba)  *! *! Returns a string with filename and linenumber describing where *! the program @[p] was defined. *!
cbe8c92003-04-07Martin Nilsson  *! The returned string is of the format @expr{"filename:linenumber"@}.
0498332001-02-10Henrik Grubbström (Grubba)  *!
cbe8c92003-04-07Martin Nilsson  *! If it cannot be determined where the program was defined, @expr{0@} *! (zero) will be returned.
0498332001-02-10Henrik Grubbström (Grubba)  */
b8c5b22000-05-25Fredrik Hübinette (Hubbe) PIKEFUN string program_defined(program p) errname Program.defined; optflags OPT_TRY_OPTIMIZE; {
9de5ff2002-12-01Martin Stjernholm  INT32 line; struct pike_string *tmp = low_get_program_line(p, &line);
b8c5b22000-05-25Fredrik Hübinette (Hubbe) 
9de5ff2002-12-01Martin Stjernholm  pop_n_elems(args);
50edc82001-07-13Henrik Grubbström (Grubba) 
9de5ff2002-12-01Martin Stjernholm  if (tmp) {
50edc82001-07-13Henrik Grubbström (Grubba)  push_string(tmp); if(line >= 1) { push_constant_text(":"); push_int(line); f_add(3);
b8c5b22000-05-25Fredrik Hübinette (Hubbe)  } }
9de5ff2002-12-01Martin Stjernholm  else push_int(0);
b8c5b22000-05-25Fredrik Hübinette (Hubbe) }
31acb72001-07-26Martin Nilsson /*! @decl int(8..8)|int(16..16)|int(32..32) width(string s) *! @belongs String
0498332001-02-10Henrik Grubbström (Grubba)  *! *! 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 */
d6fd962001-02-10Henrik Grubbström (Grubba) PIKEFUN int(8 .. 8)|int(16 .. 16)|int(32 .. 32) string_width(string s)
991fdf2000-05-25Fredrik Hübinette (Hubbe)  errname String.width; optflags OPT_TRY_OPTIMIZE; { RETURN 8 * (1 << s->size_shift); }
0498332001-02-10Henrik Grubbström (Grubba) /*! @decl mixed m_delete(object|mapping map, mixed index) *! *! If @[map] is an object that implements @[lfun::_m_delete()],
dd5cba2003-02-10Marek Habersack  *! that function will be called with @[index] as its single argument.
0498332001-02-10Henrik Grubbström (Grubba)  *!
dd5cba2003-02-10Marek Habersack  *! Otherwise if @[map] is a mapping the entry with index @[index]
0498332001-02-10Henrik Grubbström (Grubba)  *! will be removed from @[map] destructively. *! *! If the mapping does not have an entry with index @[index], nothing is done. *! *! @returns
52ced62003-02-11Henrik Grubbström (Grubba)  *! The value that was removed will be returned.
0498332001-02-10Henrik Grubbström (Grubba)  *! *! @note *! Note that @[m_delete()] changes @[map] destructively. *! *! @seealso *! @[mappingp()] */
a3453e2001-02-05Per Hedbor PIKEFUN mixed m_delete(object|mapping map, mixed index)
7f80d42000-06-19Fredrik Hübinette (Hubbe)  efun; optflags OPT_SIDE_EFFECT;
d8a04b2005-11-14Martin Nilsson  rawtype tOr(tFunc(tMap(tSetvar(0,tMix),tSetvar(1,tMix)) tVar(0),tVar(1)),tFunc(tObj tMix,tMix))
7f80d42000-06-19Fredrik Hübinette (Hubbe) {
2a8be12004-12-21Henrik Grubbström (Grubba)  struct program *p;
a3453e2001-02-05Per Hedbor  if( map->type == T_MAPPING ) { struct svalue s; map_delete_no_free(map->u.mapping, index, &s); pop_n_elems(args);
cbaa3a2002-05-24Henrik Grubbström (Grubba)  *Pike_sp=s; Pike_sp++;
cdf18a2003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
a3453e2001-02-05Per Hedbor  }
2a8be12004-12-21Henrik Grubbström (Grubba)  else if (map->type == T_OBJECT && (p = map->u.object->prog))
a3453e2001-02-05Per Hedbor  {
2a8be12004-12-21Henrik Grubbström (Grubba)  int id = FIND_LFUN(p->inherits[map->subtype].prog, LFUN__M_DELETE);
ea56012001-02-09Per Hedbor  if( id == -1 )
e786012003-02-11Marek Habersack  SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object containing the _m_delete method");
ea56012001-02-09Per Hedbor 
2a8be12004-12-21Henrik Grubbström (Grubba)  apply_low(map->u.object, id + p->inherits[map->subtype].identifier_level, 1);
a3453e2001-02-05Per Hedbor  stack_swap(); pop_stack();
79f6982001-02-05Henrik Grubbström (Grubba)  } else { SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object|mapping");
a3453e2001-02-05Per Hedbor  }
7f80d42000-06-19Fredrik Hübinette (Hubbe) }
9da7f42001-06-05Martin Stjernholm /*! @decl int get_weak_flag(array|mapping|multiset m)
0498332001-02-10Henrik Grubbström (Grubba)  *!
9da7f42001-06-05Martin Stjernholm  *! Returns the weak flag settings for @[m]. It's a combination of *! @[Pike.WEAK_INDICES] and @[Pike.WEAK_VALUES].
0498332001-02-10Henrik Grubbström (Grubba)  */
e1b4192001-06-06Fredrik Hübinette (Hubbe) PIKEFUN int get_weak_flag(array m)
ee9fa92000-07-06Martin Stjernholm  efun;
8f998d2000-08-31Henrik Grubbström (Grubba)  optflags OPT_EXTERNAL_DEPEND;
ee9fa92000-07-06Martin Stjernholm {
e1b4192001-06-06Fredrik Hübinette (Hubbe)  RETURN (m->flags & ARRAY_WEAK_FLAG) ? PIKE_WEAK_VALUES : 0; } PIKEFUN int get_weak_flag(mapping m) { RETURN mapping_get_flags(m) & MAPPING_WEAK; } PIKEFUN int get_weak_flag(multiset m) {
5b15bb2001-12-10Martin Stjernholm  RETURN multiset_get_flags(m) & MULTISET_WEAK;
ee9fa92000-07-06Martin Stjernholm }
18c2252003-04-10Martin Nilsson /*! @decl program __empty_program(int|void line, string|void file) */
9de5ff2002-12-01Martin Stjernholm PIKEFUN program __empty_program(int|void line, string|void file)
aa68b12001-03-19Fredrik Hübinette (Hubbe)  efun; optflags OPT_EXTERNAL_DEPEND; {
9de5ff2002-12-01Martin Stjernholm  if (line && line->type != T_INT) SIMPLE_BAD_ARG_ERROR("__empty_program", 1, "int|void"); else { struct program *prog = low_allocate_program();
fe21442004-09-02Henrik Grubbström (Grubba)  if (file) ext_store_program_line (prog, line->u.integer, file);
9de5ff2002-12-01Martin Stjernholm  RETURN prog; }
aa68b12001-03-19Fredrik Hübinette (Hubbe) }
1c1c5e2001-04-08Fredrik Hübinette (Hubbe) /*! @decl string function_name(function f) *! *! Return the name of the function @[f]. *!
cbe8c92003-04-07Martin Nilsson  *! If @[f] is a global function defined in the runtime @expr{0@} *! (zero) will be returned.
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  *! *! @seealso *! @[function_object()] */ PIKEFUN string function_name(program|function func) efun; optflags OPT_TRY_OPTIMIZE; { switch(func->type) { default:
d4ecd72003-01-05Martin Nilsson  SIMPLE_BAD_ARG_ERROR("function_name", 1, "function|program");
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  return; /* NOTREACHED */ case PIKE_T_PROGRAM: { struct program *p=func->u.program; if(p->parent) { int e; p=p->parent; /* search constants in parent for this * program... */ for(e = p->num_identifier_references; e--; ) { struct identifier *id; if (p->identifier_references[e].id_flags & ID_HIDDEN) continue; id = ID_FROM_INT(p, e); if (IDENTIFIER_IS_CONSTANT(id->identifier_flags) &&
d12fe42003-08-20Henrik Grubbström (Grubba)  (id->func.offset >= 0) &&
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  is_eq( & PROG_FROM_INT(p, e)->constants[id->func.offset].sval, func)) REF_RETURN id->name; }
52ced62003-02-11Henrik Grubbström (Grubba) #ifdef PIKE_DEBUG if (d_flag>5) { fprintf(stderr,
2523ce2003-04-28Martin Stjernholm  "Failed to find symbol for program %p\n"
52ced62003-02-11Henrik Grubbström (Grubba)  "Parent program info:\n", func->u.program); dump_program_tables(func->u.program->parent, 0); } #endif
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  } break; } case PIKE_T_FUNCTION: if(func->subtype == FUNCTION_BUILTIN) break; if(!func->u.object->prog) bad_arg_error("function_name", Pike_sp-args, args, 1, "function", Pike_sp-args, "Destructed object.\n");
5a6d7d2001-04-10Fredrik Hübinette (Hubbe)  if(func->u.object->prog == pike_trampoline_program) { struct pike_trampoline *t; t=((struct pike_trampoline *)func->u.object->storage); if(t->frame->current_object->prog) REF_RETURN ID_FROM_INT(t->frame->current_object->prog, t->func)->name; }
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  REF_RETURN ID_FROM_INT(func->u.object->prog, func->subtype)->name; } pop_n_elems(args); push_int(0); }
6410612003-01-08Henrik Grubbström (Grubba) /*! @decl object function_object(function f)
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  *!
6410612003-01-08Henrik Grubbström (Grubba)  *! Return the object the function @[f] is in.
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  *!
cbe8c92003-04-07Martin Nilsson  *! If @[f] is a global function defined in the runtime @expr{0@} *! (zero) will be returned.
6410612003-01-08Henrik Grubbström (Grubba)  *! *! Zero will also be returned if @[f] is a constant in the *! parent class. In that case @[function_program()] can be *! used to get the parent program.
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  *! *! @seealso
6410612003-01-08Henrik Grubbström (Grubba)  *! @[function_name()], @[function_program()]
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  */
51adb82003-01-12Martin Stjernholm PIKEFUN object function_object(function|program func)
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  efun; optflags OPT_TRY_OPTIMIZE;
6410612003-01-08Henrik Grubbström (Grubba)  type function(function:object);
1c1c5e2001-04-08Fredrik Hübinette (Hubbe) { switch(func->type) { case PIKE_T_PROGRAM:
6410612003-01-08Henrik Grubbström (Grubba)  break;
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  case PIKE_T_FUNCTION: if(func->subtype == FUNCTION_BUILTIN) break;
5a6d7d2001-04-10Fredrik Hübinette (Hubbe)  if(func->u.object->prog == pike_trampoline_program) { struct object *o; o=((struct pike_trampoline *)func->u.object->storage)->frame->current_object; add_ref(o); pop_n_elems(args); push_object(o); return; }
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  func->type=T_OBJECT;
f462562004-12-19Henrik Grubbström (Grubba)  func->subtype = 0;
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  return;
5a6d7d2001-04-10Fredrik Hübinette (Hubbe)  default: SIMPLE_BAD_ARG_ERROR("function_object",1,"function");
1c1c5e2001-04-08Fredrik Hübinette (Hubbe)  } pop_n_elems(args); push_int(0); }
6410612003-01-08Henrik Grubbström (Grubba) /*! @decl program function_program(function|program f) *! *! Return the program the function @[f] is in. *!
cbe8c92003-04-07Martin Nilsson  *! If @[f] is a global function defined in the runtime @expr{0@} *! (zero) will be returned.
6410612003-01-08Henrik Grubbström (Grubba)  *! *! @seealso *! @[function_name()], @[function_object()] */ PIKEFUN program function_program(program|function func) efun; optflags OPT_TRY_OPTIMIZE; { switch(func->type) { case PIKE_T_PROGRAM: { struct program *p; if(!(p=func->u.program->parent)) break; add_ref(p); free_program(func->u.program); func->u.program=p; return; } case PIKE_T_FUNCTION: { struct program *p;
48a6242003-05-31Martin Stjernholm  if(func->subtype == FUNCTION_BUILTIN) p = func->u.efun->prog; else p = func->u.object->prog;
6410612003-01-08Henrik Grubbström (Grubba)  if(p == pike_trampoline_program) { p = ((struct pike_trampoline *)func->u.object->storage)-> frame->current_object->prog; } if (p) { ref_push_program(p); stack_pop_n_elems_keep_top(args); return; } } break; default: SIMPLE_BAD_ARG_ERROR("function_program", 1, "function"); } pop_n_elems(args); push_int(0); }
1c1c5e2001-04-08Fredrik Hübinette (Hubbe) 
18c2252003-04-10Martin Nilsson /*! @decl mixed random(object o) *! If random is called with an object, @[lfun::random] will be *! called in the object.
d95fa82001-06-05Fredrik Hübinette (Hubbe)  *! @seealso
18c2252003-04-10Martin Nilsson  *! @[lfun::_random]
d95fa82001-06-05Fredrik Hübinette (Hubbe)  */
e1b4192001-06-06Fredrik Hübinette (Hubbe) 
18c2252003-04-10Martin Nilsson /*! @decl mixed lfun::_random() *! Called by @[random]. Typical uses is when the object implements *! a ADT, then a call to this lfun should return a random member of *! the ADT or range implied by the ADT. *! @seealso *! @[predef::random()] */
e1b4192001-06-06Fredrik Hübinette (Hubbe)  PIKEFUN mixed random(object o)
d95fa82001-06-05Fredrik Hübinette (Hubbe)  efun; optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; {
e1b4192001-06-06Fredrik Hübinette (Hubbe)  apply(o,"_random",0); stack_swap(); pop_stack(); }
d95fa82001-06-05Fredrik Hübinette (Hubbe) 
18c2252003-04-10Martin Nilsson /*! @decl int random(int max) *! @decl float random(float max) *! *! This function returns a random number in the range 0 - @[max]-1. *! *! @seealso *! @[random_seed()] */
e1b4192001-06-06Fredrik Hübinette (Hubbe) PIKEFUN int random(int i) { if(i <= 0) RETURN 0; RETURN my_rand() % i; } PIKEFUN float random(float f) { if(f<=0.0) RETURN 0.0;
d95fa82001-06-05Fredrik Hübinette (Hubbe) #define N 1048576
e1b4192001-06-06Fredrik Hübinette (Hubbe)  RETURN f * (my_rand()%N/((float)N)) + f * (my_rand()%N/( ((float)N) * ((float)N) ));
d95fa82001-06-05Fredrik Hübinette (Hubbe) 
e1b4192001-06-06Fredrik Hübinette (Hubbe) }
d95fa82001-06-05Fredrik Hübinette (Hubbe) 
7867852004-03-02Martin Nilsson /*! @decl mixed random(array|multiset x)
18c2252003-04-10Martin Nilsson  *! Returns a random element from @[x]. */
e1b4192001-06-06Fredrik Hübinette (Hubbe) PIKEFUN mixed random(array a)
3a2fe12004-02-14Martin Nilsson  rawtype tFunc(tArr(tSetvar(0,tMix)),tVar(0));
e1b4192001-06-06Fredrik Hübinette (Hubbe) { if(!a->size) SIMPLE_BAD_ARG_ERROR("random", 1, "array with elements in it"); push_svalue(a->item + (my_rand() % a->size)); stack_swap(); pop_stack(); }
d95fa82001-06-05Fredrik Hübinette (Hubbe) 
e1b4192001-06-06Fredrik Hübinette (Hubbe) PIKEFUN mixed random(multiset m)
3a2fe12004-02-14Martin Nilsson  rawtype tFunc(tSet(tSetvar(1,tMix)),tVar(1));
e1b4192001-06-06Fredrik Hübinette (Hubbe) {
5b15bb2001-12-10Martin Stjernholm  if(multiset_is_empty (m))
e1b4192001-06-06Fredrik Hübinette (Hubbe)  SIMPLE_BAD_ARG_ERROR("random", 1, "multiset with elements in it");
5b15bb2001-12-10Martin Stjernholm  if (multiset_indval (m)) { ptrdiff_t nodepos = multiset_get_nth (m, my_rand() % multiset_sizeof (m)); push_multiset_index (m, nodepos); push_multiset_value (m, nodepos); sub_msnode_ref (m); f_aggregate (2); } else {
eb82b82004-04-06Martin Nilsson  push_multiset_index (m, multiset_get_nth (m, my_rand() % multiset_sizeof (m)));
5b15bb2001-12-10Martin Stjernholm  sub_msnode_ref (m); }
e1b4192001-06-06Fredrik Hübinette (Hubbe)  stack_swap(); pop_stack(); }
d95fa82001-06-05Fredrik Hübinette (Hubbe) 
18c2252003-04-10Martin Nilsson /*! @decl array random(mapping m) *! Returns a random index-value pair from the mapping. */
5b15bb2001-12-10Martin Stjernholm PIKEFUN array random(mapping m)
e1b4192001-06-06Fredrik Hübinette (Hubbe) { struct mapping_data *md=m->data; size_t bucket, count; struct keypair *k; if(!m_sizeof(m)) SIMPLE_BAD_ARG_ERROR("random", 1, "mapping with elements in it"); /* Find a random, nonempty bucket */ bucket=my_rand() % md->hashsize; while(! md->hash[bucket] ) if(++bucket > (size_t)md->hashsize) bucket=0; /* Count entries in bucket */ count=0; for(k=md->hash[bucket];k;k=k->next) count++; /* Select a random entry in this bucket */ count = my_rand() % count; k=md->hash[bucket]; while(count-- > 0) k=k->next; /* Push result and return */ push_svalue(&k->ind); push_svalue(&k->val); f_aggregate(2); stack_swap(); pop_stack();
d95fa82001-06-05Fredrik Hübinette (Hubbe) }
d27df52001-06-18Henrik Grubbström (Grubba) /* * Backtrace handling. */
bcbce02001-08-15Martin Nilsson /*! @module Pike */
d27df52001-06-18Henrik Grubbström (Grubba) /*! @class BacktraceFrame */ PIKECLASS backtrace_frame {
f61a482001-06-19Henrik Grubbström (Grubba)  PIKEVAR mixed fun;
2aca9f2001-06-19Henrik Grubbström (Grubba)  PIKEVAR array args;
73b08f2003-01-31Martin Stjernholm  /* These are cleared when filename and lineno has been initialized * from them. */
588bd92004-04-18Martin Stjernholm  PIKEVAR program prog flags ID_STATIC|ID_PRIVATE;
90f8762002-04-08Martin Stjernholm  CVAR PIKE_OPCODE_T *pc;
73b08f2003-01-31Martin Stjernholm  /* These two are considered to be uninitialized from prog, pc and * fun as long as lineno == -1. */
d27df52001-06-18Henrik Grubbström (Grubba)  CVAR struct pike_string *filename;
69aa4b2003-01-26Mirar (Pontus Hagland)  CVAR INT32 lineno;
d27df52001-06-18Henrik Grubbström (Grubba)  INIT {
90f8762002-04-08Martin Stjernholm  THIS->pc = NULL;
73b08f2003-01-31Martin Stjernholm  THIS->lineno = -1;
0f47db2001-06-19Henrik Grubbström (Grubba)  THIS->filename = NULL;
d27df52001-06-18Henrik Grubbström (Grubba)  } EXIT { if (THIS->filename) { free_string(THIS->filename);
0f47db2001-06-19Henrik Grubbström (Grubba)  THIS->filename = NULL;
d27df52001-06-18Henrik Grubbström (Grubba)  }
1073bf2001-06-26Henrik Grubbström (Grubba)  THIS->pc = NULL;
73b08f2003-01-31Martin Stjernholm  THIS->lineno = -1;
d27df52001-06-18Henrik Grubbström (Grubba)  }
18c2252003-04-10Martin Nilsson  /*! @decl int(0..1) _is_type(string t) *! This object claims to be an array for backward compatibility. */
d2cd4e2001-06-18Henrik Grubbström (Grubba)  PIKEFUN int(0..1) _is_type(string t) { INT_TYPE res = (t == findstring("array")); pop_n_elems(args); push_int(res); }
73b08f2003-01-31Martin Stjernholm  static void fill_in_file_and_line() {
0469ba2004-02-10Martin Stjernholm  struct pike_string *file = NULL;
73b08f2003-01-31Martin Stjernholm  assert (THIS->lineno == -1); if (THIS->pc && THIS->prog) { file = low_get_line(THIS->pc, THIS->prog, &THIS->lineno); THIS->pc = NULL; }
d191622006-03-22Henrik Grubbström (Grubba)  else if (THIS->fun.type == PIKE_T_FUNCTION) {
73b08f2003-01-31Martin Stjernholm  file = low_get_function_line (THIS->fun.u.object, THIS->fun.subtype, &THIS->lineno);
d191622006-03-22Henrik Grubbström (Grubba)  } else if (THIS->prog) {
73b08f2003-01-31Martin Stjernholm  file = low_get_program_line (THIS->prog, &THIS->lineno);
d191622006-03-22Henrik Grubbström (Grubba)  }
73b08f2003-01-31Martin Stjernholm 
0469ba2004-02-10Martin Stjernholm  if (file) { if (!THIS->filename) THIS->filename = file; else free_string (file); }
73b08f2003-01-31Martin Stjernholm  if (THIS->prog) { free_program(THIS->prog); THIS->prog = NULL; } }
18c2252003-04-10Martin Nilsson  /*! @decl string _sprintf(int c, mapping|void opts) */
d27df52001-06-18Henrik Grubbström (Grubba)  PIKEFUN string _sprintf(int c, mapping|void opts) { pop_n_elems(args);
e6dbc22002-11-29Marcus Comstedt  if (c != 'O') { push_undefined (); return; }
d27df52001-06-18Henrik Grubbström (Grubba)  push_text("backtrace_frame(");
73b08f2003-01-31Martin Stjernholm  if (THIS->lineno == -1) fill_in_file_and_line();
1073bf2001-06-26Henrik Grubbström (Grubba)  if (THIS->filename) {
d27df52001-06-18Henrik Grubbström (Grubba)  ref_push_string(THIS->filename); push_text(":"); push_int(THIS->lineno); push_text(", "); f_add(4); } else { push_text("Unknown file, "); }
0f47db2001-06-19Henrik Grubbström (Grubba)  if (THIS->fun.type == PIKE_T_FUNCTION) { if (THIS->fun.u.object->prog) { push_svalue(&THIS->fun); f_function_name(1); push_text("(), "); f_add(2); } else { free_svalue(&THIS->fun); THIS->fun.type = PIKE_T_INT; THIS->fun.u.integer = 0; THIS->fun.subtype = NUMBER_DESTRUCTED; push_text("destructed_function(), "); }
d27df52001-06-18Henrik Grubbström (Grubba)  } else { push_text("destructed_function(), "); }
0f47db2001-06-19Henrik Grubbström (Grubba)  if (THIS->args) {
d27df52001-06-18Henrik Grubbström (Grubba)  push_text("Args: ");
0f47db2001-06-19Henrik Grubbström (Grubba)  push_int(THIS->args->size);
d27df52001-06-18Henrik Grubbström (Grubba)  f_add(2); } else { push_text("No args"); } push_text(")"); f_add(5); }
18c2252003-04-10Martin Nilsson  /*! @decl int(3..) _sizeof() */ PIKEFUN int(3..) _sizeof()
d27df52001-06-18Henrik Grubbström (Grubba)  {
0f47db2001-06-19Henrik Grubbström (Grubba)  if (THIS->args) { push_int(THIS->args->size + 3); } else { push_int(3);
d27df52001-06-18Henrik Grubbström (Grubba)  } }
18c2252003-04-10Martin Nilsson  /*! @decl mixed `[](int index, int|void end_or_none) *! The BacktraceFrame object can be indexed as an array. */
d2cd4e2001-06-18Henrik Grubbström (Grubba)  PIKEFUN mixed `[](int index, int|void end_or_none)
d27df52001-06-18Henrik Grubbström (Grubba)  {
d2cd4e2001-06-18Henrik Grubbström (Grubba)  INT_TYPE end = index; INT32 numargs = 0; INT32 i;
0f47db2001-06-19Henrik Grubbström (Grubba)  if (THIS->args) { numargs = THIS->args->size;
d27df52001-06-18Henrik Grubbström (Grubba)  }
d2cd4e2001-06-18Henrik Grubbström (Grubba)  numargs += 3; if (!end_or_none) { if (index < 0) { index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, "Indexing with negative index (%"PRINTPIKEINT"d)\n", index); } else if (index >= numargs) { index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, "Indexing with too large index (%"PRINTPIKEINT"d)\n", index); } } else { if (end_or_none->type != PIKE_T_INT) { SIMPLE_BAD_ARG_ERROR("`[]",2,"int|void"); } end = end_or_none->u.integer;
d27df52001-06-18Henrik Grubbström (Grubba)  } pop_n_elems(args);
d2cd4e2001-06-18Henrik Grubbström (Grubba)  if (end_or_none) {
0f47db2001-06-19Henrik Grubbström (Grubba)  if ((end < 0) || (end < index) || (index >= numargs)) {
d2cd4e2001-06-18Henrik Grubbström (Grubba)  f_aggregate(0); return;
d27df52001-06-18Henrik Grubbström (Grubba)  }
d2cd4e2001-06-18Henrik Grubbström (Grubba)  if (end >= numargs) { end = numargs-1;
d27df52001-06-18Henrik Grubbström (Grubba)  }
d2cd4e2001-06-18Henrik Grubbström (Grubba)  } for (i = index; i <= end; i++) { switch(i) { case 0: /* Filename */
73b08f2003-01-31Martin Stjernholm  if (THIS->lineno == -1) fill_in_file_and_line(); if (THIS->filename) { ref_push_string(THIS->filename);
d2cd4e2001-06-18Henrik Grubbström (Grubba)  } else {
73b08f2003-01-31Martin Stjernholm  push_int(0);
d2cd4e2001-06-18Henrik Grubbström (Grubba)  }
1073bf2001-06-26Henrik Grubbström (Grubba)  break;
73b08f2003-01-31Martin Stjernholm  case 1: /* Linenumber */ if (THIS->lineno == -1) fill_in_file_and_line(); push_int(THIS->lineno); break;
d2cd4e2001-06-18Henrik Grubbström (Grubba)  case 2: /* Function */
0f47db2001-06-19Henrik Grubbström (Grubba)  push_svalue(&THIS->fun);
d2cd4e2001-06-18Henrik Grubbström (Grubba)  break; default: /* Arguments */ {
0f47db2001-06-19Henrik Grubbström (Grubba)  if ((i > 2) && (THIS->args) && (i-3 < THIS->args->size)) { push_svalue(THIS->args->item + (i - 3));
d2cd4e2001-06-18Henrik Grubbström (Grubba)  break; } bad_arg_error("backtrace_frame->`[]", Pike_sp-args, args, 1, "int(0..)", Pike_sp-args, "Bad argument 1 to backtrace_frame->`[](): " "Expected int(0..%d)\n", numargs + 2); } /* NOT_REACHED */ break;
d27df52001-06-18Henrik Grubbström (Grubba)  }
d2cd4e2001-06-18Henrik Grubbström (Grubba)  } if (end_or_none) { f_aggregate(1 + end - index);
d27df52001-06-18Henrik Grubbström (Grubba)  } }
d2cd4e2001-06-18Henrik Grubbström (Grubba) 
18c2252003-04-10Martin Nilsson  /*! @decl mixed `[]=(int index, mixed value) */
1073bf2001-06-26Henrik Grubbström (Grubba)  PIKEFUN mixed `[]=(int index, mixed value) { INT32 numargs = 0; if (THIS->args) { numargs = THIS->args->size; } numargs += 3; if ((index < -numargs) || (index >= numargs)) { index_error("pike_frame->`[]=", Pike_sp-args, args, NULL, Pike_sp-args,
b99d882003-05-15Martin Stjernholm  "Index %"PRINTPIKEINT"d is out of array range 0..%d,\n",
1073bf2001-06-26Henrik Grubbström (Grubba)  index, numargs-1); } else if (index < 0) { index += numargs; } if (args > 2) { pop_n_elems(args - 2); args = 2; } switch(index) { case 0: /* Filename */
73b08f2003-01-31Martin Stjernholm  if (THIS->lineno == -1) fill_in_file_and_line(); if (value->type != PIKE_T_STRING) { if ((value->type != PIKE_T_INT) || (value->u.integer)) { SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2, "string|int(0..0)");
1073bf2001-06-26Henrik Grubbström (Grubba)  }
73b08f2003-01-31Martin Stjernholm  if (THIS->filename) { free_string(THIS->filename); THIS->filename = NULL;
1073bf2001-06-26Henrik Grubbström (Grubba)  } } else {
73b08f2003-01-31Martin Stjernholm  if (THIS->filename) { free_string(THIS->filename); THIS->filename = NULL;
1073bf2001-06-26Henrik Grubbström (Grubba)  }
73b08f2003-01-31Martin Stjernholm  copy_shared_string(THIS->filename, value->u.string);
1073bf2001-06-26Henrik Grubbström (Grubba)  } break;
73b08f2003-01-31Martin Stjernholm  case 1: /* Linenumber */ if (THIS->lineno == -1) fill_in_file_and_line(); if (value->type != PIKE_T_INT) { SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2, "int(1..)"); } THIS->lineno = value->u.integer; break;
1073bf2001-06-26Henrik Grubbström (Grubba)  case 2: /* Function */
73b08f2003-01-31Martin Stjernholm  if (THIS->lineno == -1) fill_in_file_and_line();
1073bf2001-06-26Henrik Grubbström (Grubba)  assign_svalue(&THIS->fun, value); break; default: /* Arguments */ assign_svalue(THIS->args->item + index - 3, value); break; } stack_swap(); pop_stack(); }
d27df52001-06-18Henrik Grubbström (Grubba) }; /*! @endclass */
bcbce02001-08-15Martin Nilsson /*! @endmodule */
a8e9892001-09-05Fredrik Hübinette (Hubbe) void low_backtrace(struct Pike_interpreter *i)
d27df52001-06-18Henrik Grubbström (Grubba) {
b85fa02003-02-24Martin Stjernholm  struct svalue *stack_top = i->stack_pointer;
c98dd22001-06-26Martin Stjernholm  struct pike_frame *f, *of = 0;
d27df52001-06-18Henrik Grubbström (Grubba)  int size = 0;
9906e32001-06-20Henrik Grubbström (Grubba)  struct array *res = NULL;
d27df52001-06-18Henrik Grubbström (Grubba) 
a8e9892001-09-05Fredrik Hübinette (Hubbe)  for (f = i->frame_pointer; f; f = f->next) {
9906e32001-06-20Henrik Grubbström (Grubba)  size++; } res = allocate_array_no_init(size, 0); push_array(res);
a8e9892001-09-05Fredrik Hübinette (Hubbe)  for (f = i->frame_pointer; f && size; f = (of = f)->next) {
d27df52001-06-18Henrik Grubbström (Grubba)  struct object *o = low_clone(backtrace_frame_program);
0f47db2001-06-19Henrik Grubbström (Grubba)  struct backtrace_frame_struct *bf;
e89d722002-01-04Henrik Grubbström (Grubba)  struct identifier *function = NULL;
0f47db2001-06-19Henrik Grubbström (Grubba) 
d27df52001-06-18Henrik Grubbström (Grubba)  call_c_initializers(o);
0f47db2001-06-19Henrik Grubbström (Grubba) 
9906e32001-06-20Henrik Grubbström (Grubba)  size--; res->item[size].u.object = o; res->item[size].type = PIKE_T_OBJECT; res->item[size].subtype = 0;
0f47db2001-06-19Henrik Grubbström (Grubba)  bf = OBJ2_BACKTRACE_FRAME(o);
d191622006-03-22Henrik Grubbström (Grubba)  bf->pc = NULL;
0f47db2001-06-19Henrik Grubbström (Grubba)  if ((bf->prog = f->context.prog)) { add_ref(bf->prog); } if ((bf->fun.u.object = f->current_object) && (bf->fun.u.object->prog)) { add_ref(bf->fun.u.object); bf->fun.subtype = f->fun; bf->fun.type = PIKE_T_FUNCTION;
e89d722002-01-04Henrik Grubbström (Grubba)  function = ID_FROM_INT(f->current_object->prog, f->fun);
d191622006-03-22Henrik Grubbström (Grubba)  if (IDENTIFIER_IS_PIKE_FUNCTION(function->identifier_flags)) { /* We don't trust pc if we aren't in a Pike function... */ bf->pc = f->pc; }
0f47db2001-06-19Henrik Grubbström (Grubba)  } else { bf->fun.u.integer = 0; bf->fun.subtype = NUMBER_DESTRUCTED; bf->fun.type = PIKE_T_INT; } if (f->locals) { INT32 numargs = DO_NOT_WARN((INT32) MINIMUM(f->num_args,
b85fa02003-02-24Martin Stjernholm  stack_top - f->locals));
e89d722002-01-04Henrik Grubbström (Grubba)  INT32 varargs = 0;
da90692002-11-26Henrik Grubbström (Grubba)  if(of) {
c98dd22001-06-26Martin Stjernholm  /* f->num_args can be too large, so this is necessary for some * reason. I don't know why. /mast */ numargs = DO_NOT_WARN((INT32)MINIMUM(f->num_args,of->locals - f->locals));
da90692002-11-26Henrik Grubbström (Grubba)  }
0f47db2001-06-19Henrik Grubbström (Grubba)  numargs = MAXIMUM(numargs, 0);
e89d722002-01-04Henrik Grubbström (Grubba)  /* Handle varargs... */ if (function && (function->identifier_flags & IDENTIFIER_VARARGS) &&
b85fa02003-02-24Martin Stjernholm  (f->locals + numargs < stack_top) &&
e89d722002-01-04Henrik Grubbström (Grubba)  (f->locals[numargs].type == T_ARRAY)) { varargs = f->locals[numargs].u.array->size; } if (numargs + varargs) { bf->args = allocate_array_no_init(numargs + varargs, 0);
2523ce2003-04-28Martin Stjernholm  bf->args->type_field = assign_svalues_no_free(bf->args->item, f->locals, numargs, BIT_MIXED);
e89d722002-01-04Henrik Grubbström (Grubba)  if (varargs) {
2523ce2003-04-28Martin Stjernholm  bf->args->type_field |= assign_svalues_no_free(bf->args->item + numargs, f->locals[numargs].u.array->item, varargs, BIT_MIXED);
e89d722002-01-04Henrik Grubbström (Grubba)  }
0f47db2001-06-19Henrik Grubbström (Grubba)  } }
d27df52001-06-18Henrik Grubbström (Grubba)  }
2523ce2003-04-28Martin Stjernholm  res->type_field = BIT_OBJECT;
9906e32001-06-20Henrik Grubbström (Grubba)  /* NOTE: res has already been pushed on the stack. */
d27df52001-06-18Henrik Grubbström (Grubba) }
e1b4192001-06-06Fredrik Hübinette (Hubbe) 
a8e9892001-09-05Fredrik Hübinette (Hubbe) /*! @decl array(Pike.BacktraceFrame) backtrace() *! *! FIXME: This documentation is not up to date! *! *! Get a description of the current call stack. *! *! The description is returned as an array with one entry for each call *! frame on the stack. *! *! Each entry has this format: *! @array *! @elem string file *! A string with the filename if known, else zero. *! @elem int line *! An integer containing the linenumber if known, else zero. *! @elem function fun *! The function that was called at this level. *! @elem mixed|void ... args *! The arguments that the function was called with. *! @endarray *! *! The current call frame will be last in the array. *! *! @note *! Please note that the frame order may be reversed in a later version
c717042003-03-12Marcus Agehall  *! (than 7.1) of Pike to accommodate for deferred backtraces.
a8e9892001-09-05Fredrik Hübinette (Hubbe)  *! *! Note that the arguments reported in the backtrace are the current *! values of the variables, and not the ones that were at call-time. *! This can be used to hide sensitive information from backtraces *! (eg passwords). *! *! @seealso *! @[catch()], @[throw()] */ PMOD_EXPORT PIKEFUN array(mixed) backtrace() efun; optflags OPT_EXTERNAL_DEPEND; { low_backtrace(& Pike_interpreter); }
a3c4332001-06-20Per Hedbor #define INITIAL_BUF_LEN 4096 /*! @module String */ /*! @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: *!
f79bd82003-04-01Martin Nilsson  *! @code *! String.Buffer b = String.Buffer( );
a3c4332001-06-20Per Hedbor  *!
f79bd82003-04-01Martin Nilsson  *! function add = b->add;
a3c4332001-06-20Per Hedbor  *!
f79bd82003-04-01Martin Nilsson  *! .. call add several times in code ...
a3c4332001-06-20Per Hedbor  *!
f79bd82003-04-01Martin Nilsson  *! string result = b->get(); // also clears the buffer *! @endcode
a3c4332001-06-20Per Hedbor  */
41730d2001-07-26Martin Nilsson PIKECLASS Buffer
a3c4332001-06-20Per Hedbor {
73b07a2001-06-21Per Hedbor  CVAR struct string_builder str; CVAR int initial;
a3c4332001-06-20Per Hedbor  void f_Buffer_get_copy( INT32 args ); void f_Buffer_get( INT32 args ); void f_Buffer_add( INT32 args );
a465852002-01-01Martin Nilsson  /*! @decl void create(int initial_size)
41730d2001-07-26Martin Nilsson  *! *! Initializes a new buffer. *!
1f88bf2001-09-24Henrik Grubbström (Grubba)  *! If no @[initial_size] is specified, 256 is used. If you
41730d2001-07-26Martin Nilsson  *! know approximately how big the buffer will be, you can optimize *! the operation of @[add()] (slightly) by passing the size to this *! function. */
a3c4332001-06-20Per Hedbor  PIKEFUN void create( int|void size ) { struct Buffer_struct *str = THIS; if( args ) str->initial = MAXIMUM( size->u.integer, 512 ); else {
73b07a2001-06-21Per Hedbor  str->initial = 256;
a3c4332001-06-20Per Hedbor  push_int(0); } }
18c2252003-04-10Martin Nilsson  /*! @decl string _sprintf( int flag, mapping flags ) *! It is possible to @[sprintf] a String.Buffer object *! as @tt{%s@} just as if it was a string. */
a3c4332001-06-20Per Hedbor  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 */)" );
73b07a2001-06-21Per Hedbor  if( str->str.s )
a3c4332001-06-20Per Hedbor  {
73b07a2001-06-21Per Hedbor  push_int(str->str.s->len); push_int(str->str.malloced);
a3c4332001-06-20Per Hedbor  } else { push_int( 0 ); push_int( 0 ); } f_sprintf( 3 );
cdf18a2003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
a3c4332001-06-20Per Hedbor  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; }
18c2252003-04-10Martin Nilsson  /*! @decl mixed cast( string type ) *! It is possible to cast a String.Buffer object to *! a @expr{string@} and an @expr{int@}. */
a3c4332001-06-20Per Hedbor  PIKEFUN mixed cast( string type ) { struct pike_string *string_t; struct pike_string *int_t;
de56ec2003-02-08Martin Stjernholm  MAKE_CONST_STRING( string_t, "string" ); MAKE_CONST_STRING( int_t, "int" );
a3c4332001-06-20Per Hedbor  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; } if( type == int_t ) { struct Buffer_struct *str = THIS; pop_stack(); if( Pike_fp->current_object->refs != 1 ) f_Buffer_get_copy( 0 ); else f_Buffer_get( 0 ); o_cast_to_int( ); return; }
9606eb2004-11-12Henrik Grubbström (Grubba)  Pike_error("Cannot cast to %S\n", type);
a3c4332001-06-20Per Hedbor  }
18c2252003-04-10Martin Nilsson  /*! @decl String.Buffer `+( string what ) */
a3c4332001-06-20Per Hedbor  PIKEFUN object `+( string what ) { struct Buffer_struct *str = THIS, *str2;
7906e02003-04-07Martin Stjernholm  struct object *res = fast_clone_object( Buffer_program ); str2 = OBJ2_BUFFER( res ); str2->initial = str->initial;
73b07a2001-06-21Per Hedbor  if( str->str.s )
7906e02003-04-07Martin Stjernholm  init_string_builder_copy (&str2->str, &str->str);
a3c4332001-06-20Per Hedbor  apply( res, "add", 1 ); RETURN res; }
173e872003-04-07Martin Stjernholm 
18c2252003-04-10Martin Nilsson  /*! @decl String.Buffer `+=( string what ) */
173e872003-04-07Martin Stjernholm  PIKEFUN object `+=( string what ) { f_Buffer_add( 1 ); REF_RETURN Pike_fp->current_object; }
a3c4332001-06-20Per Hedbor 
4e5f5d2001-10-04Martin Nilsson  /*! @decl int add(string ... data)
41730d2001-07-26Martin Nilsson  *! *! Adds @[data] to the buffer. Returns the size of the buffer. *! */
3301b12001-06-20Per Hedbor  PIKEFUN int add( string ... arg1 )
a3c4332001-06-20Per Hedbor  { struct Buffer_struct *str = THIS;
97c5582004-04-15Martin Stjernholm  int init_from_arg0 = 0, j;
b195b92001-09-21Henrik Grubbström (Grubba)  if (!str->str.s && args) {
97c5582004-04-15Martin Stjernholm  ptrdiff_t sum = 0;
b195b92001-09-21Henrik Grubbström (Grubba)  int shift = 0; for (j=0; j < args; j++) { struct pike_string *a = Pike_sp[j-args].u.string; sum += a->len; shift |= a->size_shift; } if (sum < str->initial) {
6ce0142003-12-08Martin Nilsson  sum = str->initial * 2;
b195b92001-09-21Henrik Grubbström (Grubba)  }
73b08f2003-01-31Martin Stjernholm  shift = shift & ~(shift >> 1);
97c5582004-04-15Martin Stjernholm  if (shift == Pike_sp[-args].u.string->size_shift && init_string_builder_with_string (&str->str, Pike_sp[-args].u.string)) { Pike_sp[-args].type = T_INT; if (sum > str->str.s->len) string_build_mkspace (&str->str, sum - str->str.s->len, shift); init_from_arg0 = 1; } else init_string_builder_alloc(&str->str, sum, shift);
73b08f2003-01-31Martin Stjernholm  /* We know it will be a string that really is this wide. */ str->str.known_shift = shift;
b195b92001-09-21Henrik Grubbström (Grubba)  }
a3c4332001-06-20Per Hedbor 
97c5582004-04-15Martin Stjernholm  for( j = init_from_arg0; j<args; j++ )
a3c4332001-06-20Per Hedbor  {
b195b92001-09-21Henrik Grubbström (Grubba)  struct pike_string *a = Pike_sp[j-args].u.string;
73b07a2001-06-21Per Hedbor  string_builder_shared_strcat( &str->str, a );
3301b12001-06-20Per Hedbor  }
b195b92001-09-21Henrik Grubbström (Grubba)  if (str->str.s) { RETURN str->str.s->len; } else { RETURN 0; }
a3c4332001-06-20Per Hedbor  }
6ce0142003-12-08Martin Nilsson  /*! @decl void putchar(int c) *! Appends the character @[c] at the end of the string. */ PIKEFUN void putchar(int c) { struct Buffer_struct *str = THIS; if(!str->str.s) init_string_builder_alloc(&str->str, str->initial, 0); string_builder_putchar(&str->str, c); }
41730d2001-07-26Martin Nilsson  /*! @decl string get_copy() *! *! Get the data from the buffer. Significantly slower than @[get], *! but does not clear the buffer. */
a3c4332001-06-20Per Hedbor  PIKEFUN string get_copy() {
73b07a2001-06-21Per Hedbor  struct pike_string *str = THIS->str.s; if( str )
a3c4332001-06-20Per Hedbor  {
73b07a2001-06-21Per Hedbor  ptrdiff_t len = str->len; if( len > 0 ) { char *d = (char *)str->str; switch( str->size_shift ) { case 0:
d807d52002-10-15Henrik Grubbström (Grubba)  RETURN make_shared_binary_string0((p_wchar0 *)d,len);
73b07a2001-06-21Per Hedbor  break; case 1:
28fe7d2003-07-28Martin Stjernholm  RETURN make_shared_binary_string1((p_wchar1 *)d,len);
73b07a2001-06-21Per Hedbor  break; case 2:
28fe7d2003-07-28Martin Stjernholm  RETURN make_shared_binary_string2((p_wchar2 *)d,len);
73b07a2001-06-21Per Hedbor  break; } }
a3c4332001-06-20Per Hedbor  }
7863d62005-05-06Martin Nilsson  push_empty_string();
73b07a2001-06-21Per Hedbor  return;
a3c4332001-06-20Per Hedbor  }
41730d2001-07-26Martin Nilsson  /*! @decl string get() *! *! Get the data from the buffer. *! *! @note *! This will clear the data in the buffer */
a3c4332001-06-20Per Hedbor  PIKEFUN string get( ) { struct Buffer_struct *str = THIS;
73b07a2001-06-21Per Hedbor  if( str->str.s )
a3c4332001-06-20Per Hedbor  {
73b07a2001-06-21Per Hedbor  struct pike_string *s = finish_string_builder( &str->str ); str->str.malloced = 0; str->str.s = 0; RETURN s;
a3c4332001-06-20Per Hedbor  }
73b07a2001-06-21Per Hedbor  pop_n_elems(args);
7863d62005-05-06Martin Nilsson  push_empty_string();
73b07a2001-06-21Per Hedbor  return;
a3c4332001-06-20Per Hedbor  }
edb0842001-07-11Martin Stjernholm  /*! @decl int _sizeof() *! *! Returns the size of the buffer. */
41730d2001-07-26Martin Nilsson  PIKEFUN int _sizeof()
edb0842001-07-11Martin Stjernholm  { struct Buffer_struct *str = THIS; RETURN str->str.s ? str->str.s->len : 0; }
a3c4332001-06-20Per Hedbor  INIT { struct Buffer_struct *str = THIS;
73b07a2001-06-21Per Hedbor  MEMSET( str, 0, sizeof( *str ) );
a3c4332001-06-20Per Hedbor  } EXIT { struct Buffer_struct *str = THIS;
73b07a2001-06-21Per Hedbor  if( str->str.s ) free_string_builder( &str->str );
a3c4332001-06-20Per Hedbor  } }
41730d2001-07-26Martin Nilsson /*! @endclass
fed7de2001-06-28Henrik Grubbström (Grubba)  */ /*! @class Replace
c5e2a42004-04-30Martin Nilsson  *! *! This is a "compiled" version of the @[replace] function applied on *! a string, with more than one replace string. The replace strings *! are given to the create method as a @i{from@} and @i{to@} array *! and are then analyzed. The @expr{`()@} is then called with a *! string and the replace rules in the Replace object will be *! applied. The Replace object is used internally by the Pike *! optimizer and need not be used manually.
fed7de2001-06-28Henrik Grubbström (Grubba)  */ PIKECLASS multi_string_replace {
6f6d2b2006-03-11Henrik Grubbström (Grubba)  CVAR struct replace_many_context ctx;
4530562006-03-11Henrik Grubbström (Grubba)  /* NOTE: from and to are only kept for _encode()'s use. */
6f6d2b2006-03-11Henrik Grubbström (Grubba)  CVAR struct array *from; CVAR struct array *to;
fed7de2001-06-28Henrik Grubbström (Grubba) 
6f6d2b2006-03-11Henrik Grubbström (Grubba)  /*! @decl void create(array(string)|mapping(string:string)|void from, @ *! array(string)|string|void to)
18c2252003-04-10Martin Nilsson  */
6f6d2b2006-03-11Henrik Grubbström (Grubba)  PIKEFUN void create(array(string)|mapping(string:string)|void from_arg, array(string)|string|void to_arg)
fed7de2001-06-28Henrik Grubbström (Grubba)  { int i;
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if (THIS->from) free_array(THIS->from); if (THIS->to) free_array(THIS->to); if (THIS->ctx.v) free_replace_many_context(&THIS->ctx);
f01b122001-07-01Henrik Grubbström (Grubba)  if (!args) { push_int(0); return; }
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if (from_arg && from_arg->type == T_MAPPING) { if (to_arg) { Pike_error("Bad number of arguments to create().\n"); } THIS->from = mapping_indices(from_arg->u.mapping); THIS->to = mapping_values(from_arg->u.mapping); pop_n_elems(args); args = 0; } else { /* FIXME: Why is from declared |void, when it isn't allowed * to be void? * /grubba 2004-09-02 */ if (!from_arg || !to_arg) { Pike_error("Bad number of arguments to create().\n"); } pop_n_elems(args-2); args = 2; if (from_arg->type != T_ARRAY) { SIMPLE_BAD_ARG_ERROR("Replace", 1, "array(string)|mapping(string:string)"); } if (to_arg->type == T_STRING) { push_int(from_arg->u.array->size); stack_swap(); f_allocate(2); } if (to_arg->type != T_ARRAY) { SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)|string"); } if (from_arg->u.array->size != to_arg->u.array->size) { Pike_error("Replace must have equal-sized from and to arrays.\n"); } add_ref(THIS->from = from_arg->u.array); add_ref(THIS->to = to_arg->u.array);
fed7de2001-06-28Henrik Grubbström (Grubba)  }
c5e2a42004-04-30Martin Nilsson 
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if (!THIS->from->size) {
d5c9e22005-01-07Henrik Grubbström (Grubba)  /* Enter no-op mode. */ pop_n_elems(args); push_int(0); return; }
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if( (THIS->from->type_field & ~BIT_STRING) && (array_fix_type_field(THIS->from) & ~BIT_STRING) ) SIMPLE_BAD_ARG_ERROR("Replace", 1, "array(string)|mapping(string:string)");
fed7de2001-06-28Henrik Grubbström (Grubba) 
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if( (THIS->to->type_field & ~BIT_STRING) && (array_fix_type_field(THIS->to) & ~BIT_STRING) ) SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)|string");
fed7de2001-06-28Henrik Grubbström (Grubba) 
6f6d2b2006-03-11Henrik Grubbström (Grubba)  compile_replace_many(&THIS->ctx, THIS->from, THIS->to, 1);
1922b12004-04-30Martin Nilsson 
6f6d2b2006-03-11Henrik Grubbström (Grubba)  pop_n_elems(args); push_int(0);
fed7de2001-06-28Henrik Grubbström (Grubba)  }
18c2252003-04-10Martin Nilsson  /*! @decl string `()(string str) */
fed7de2001-06-28Henrik Grubbström (Grubba)  PIKEFUN string `()(string str) {
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if (!THIS->ctx.v) {
4530562006-03-11Henrik Grubbström (Grubba)  /* The result is already on the stack in the correct place... */ return;
fed7de2001-06-28Henrik Grubbström (Grubba)  }
6f6d2b2006-03-11Henrik Grubbström (Grubba)  RETURN execute_replace_many(&THIS->ctx, str);
fed7de2001-06-28Henrik Grubbström (Grubba)  }
6f6d2b2006-03-11Henrik Grubbström (Grubba)  /*! @decl array(array(string)) _encode()
18c2252003-04-10Martin Nilsson  */
6f6d2b2006-03-11Henrik Grubbström (Grubba)  PIKEFUN array(array(string)) _encode()
9f91572001-07-01Henrik Grubbström (Grubba)  {
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if (THIS->from) { ref_push_array(THIS->from); } else { push_undefined();
9f91572001-07-01Henrik Grubbström (Grubba)  }
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if (THIS->to) { ref_push_array(THIS->to); } else { push_undefined();
9f91572001-07-01Henrik Grubbström (Grubba)  } f_aggregate(2); }
18c2252003-04-10Martin Nilsson  /*! @decl void _decode(array(array(string)) encoded) */
9f91572001-07-01Henrik Grubbström (Grubba)  PIKEFUN void _decode(array(array(string)) encoded) { INT32 i; for (i=0; i < encoded->size; i++) { push_svalue(encoded->item + i); stack_swap(); } pop_stack(); f_multi_string_replace_create(i); }
fed7de2001-06-28Henrik Grubbström (Grubba)  INIT {
6f6d2b2006-03-11Henrik Grubbström (Grubba)  MEMSET(&THIS->ctx, 0, sizeof(struct replace_many_context)); THIS->from = NULL; THIS->to = NULL;
fed7de2001-06-28Henrik Grubbström (Grubba)  } EXIT {
6f6d2b2006-03-11Henrik Grubbström (Grubba)  if (THIS->from) free_array(THIS->from); if (THIS->to) free_array(THIS->to); free_replace_many_context(&THIS->ctx);
fed7de2001-06-28Henrik Grubbström (Grubba)  } }
0c4ad02001-07-05Henrik Grubbström (Grubba) /*! @endclass */ /*! @class SingleReplace
c5e2a42004-04-30Martin Nilsson  *! *! This is a "compiled" version of the @[replace] function applied on *! a string, with just one replace string. The replace strings are *! given to the create method as a @i{from@} and @i{tom@} string and *! are then analyzed. The @expr{`()@} is then called with a string *! and the replace rule in the Replace object will be applied. The *! Replace object is used internally by the Pike optimizer and need *! not be used manually.
0c4ad02001-07-05Henrik Grubbström (Grubba)  */ PIKECLASS single_string_replace { CVAR SearchMojt mojt; CVAR struct pike_string *del; CVAR struct pike_string *to; INIT { THIS->mojt.vtab = NULL; THIS->mojt.data = NULL; THIS->del = NULL; THIS->to = NULL; } EXIT { if (THIS->mojt.vtab) { THIS->mojt.vtab->freeme(THIS->mojt.data); THIS->mojt.vtab = NULL; THIS->mojt.data = NULL; } if (THIS->del) { free_string(THIS->del); THIS->del = NULL; } if (THIS->to) { free_string(THIS->to); THIS->to = NULL; } }
c5e2a42004-04-30Martin Nilsson  /*! @decl void create(string|void from, string|void to)
fe21442004-09-02Henrik Grubbström (Grubba)  *! *! @note *! May be called with either zero or two arguments.
18c2252003-04-10Martin Nilsson  */
fe21442004-09-02Henrik Grubbström (Grubba)  PIKEFUN void create(string|void del, string|void to)
0c4ad02001-07-05Henrik Grubbström (Grubba)  { /* Clean up... */ exit_single_string_replace_struct();
fe21442004-09-02Henrik Grubbström (Grubba)  if (!del) return;
0c4ad02001-07-05Henrik Grubbström (Grubba) 
fe21442004-09-02Henrik Grubbström (Grubba)  if (!to) {
0c4ad02001-07-05Henrik Grubbström (Grubba)  SIMPLE_BAD_ARG_ERROR("String.SingleReplace->create", 2, "string"); }
fe21442004-09-02Henrik Grubbström (Grubba)  if (del == to) {
0c4ad02001-07-05Henrik Grubbström (Grubba)  /* No-op... */ return; }
fe21442004-09-02Henrik Grubbström (Grubba)  copy_shared_string(THIS->del, del); copy_shared_string(THIS->to, to);
0c4ad02001-07-05Henrik Grubbström (Grubba)  if (del->len) {
50edc82001-07-13Henrik Grubbström (Grubba)  THIS->mojt = simple_compile_memsearcher(del);
0c4ad02001-07-05Henrik Grubbström (Grubba)  } } /*** replace function ***/ typedef char *(* replace_searchfunc)(void *,void *,size_t);
18c2252003-04-10Martin Nilsson  /*! @decl string `()(string str) */
0c4ad02001-07-05Henrik Grubbström (Grubba)  PIKEFUN string `()(string str) { int shift; struct pike_string *del = THIS->del; struct pike_string *to = THIS->to; struct pike_string *ret = NULL; if (!str->len || !del || !to) { /* The result is already on the stack in the correct place... */ return; } shift = MAXIMUM(str->size_shift, to->size_shift); if (!del->len) { int e, pos; ret = begin_wide_shared_string(str->len + to->len * (str->len-1), shift); low_set_index(ret, 0, index_shared_string(str, 0)); for(pos=e=1;e<str->len;e++) { pike_string_cpy(MKPCHARP_STR_OFF(ret,pos), to); pos+=to->len; low_set_index(ret,pos++,index_shared_string(str,e)); } } else { char *s, *end, *tmp; replace_searchfunc f = (replace_searchfunc)0; void *mojt_data = THIS->mojt.data; PCHARP r; end = str->str+(str->len<<str->size_shift); switch(str->size_shift) { case 0: f = (replace_searchfunc)THIS->mojt.vtab->func0; break; case 1: f = (replace_searchfunc)THIS->mojt.vtab->func1; break; case 2: f = (replace_searchfunc)THIS->mojt.vtab->func2; break; #ifdef PIKE_DEBUG
5aad932002-08-15Marcus Comstedt  default: Pike_fatal("Illegal shift.\n");
0c4ad02001-07-05Henrik Grubbström (Grubba) #endif } if(del->len == to->len) { ret = begin_wide_shared_string(str->len, shift); } else { INT32 delimiters = 0; s = str->str; while((s = f(mojt_data, s, (end-s)>>str->size_shift))) { delimiters++; s += del->len << str->size_shift; } if (!delimiters) { /* The result is already on the stack in the correct place... */ return; } ret = begin_wide_shared_string(str->len + (to->len-del->len)*delimiters, shift); } s = str->str; r = MKPCHARP_STR(ret); while((tmp = f(mojt_data, s, (end-s)>>str->size_shift))) { #ifdef PIKE_DEBUG if(tmp + (del->len << str->size_shift) > end)
362d302004-03-08Martin Nilsson  Pike_fatal("SearchMojt found a match beyond end of string!\n");
0c4ad02001-07-05Henrik Grubbström (Grubba) #endif generic_memcpy(r,MKPCHARP(s,str->size_shift),(tmp-s)>>str->size_shift); INC_PCHARP(r,(tmp-s)>>str->size_shift); pike_string_cpy(r,to); INC_PCHARP(r,to->len); s=tmp+(del->len << str->size_shift); } generic_memcpy(r,MKPCHARP(s,str->size_shift),(end-s)>>str->size_shift); } RETURN end_shared_string(ret); }
18c2252003-04-10Martin Nilsson  /*! @decl array(string) _encode() */
0c4ad02001-07-05Henrik Grubbström (Grubba)  PIKEFUN array(string) _encode() { if (THIS->del) { ref_push_string(THIS->del); ref_push_string(THIS->to); f_aggregate(2); } else { push_int(0); } }
18c2252003-04-10Martin Nilsson  /*! @decl void _decode(array(string)|int(0..0) encoded) */
0c4ad02001-07-05Henrik Grubbström (Grubba)  PIKEFUN void _decode(array(string)|int(0..0) encoded_) { INT32 i = 0; if (encoded_->type == PIKE_T_ARRAY) { struct array *encoded = encoded_->u.array; for (i=0; i < encoded->size; i++) { push_svalue(encoded->item + i); stack_swap(); } } pop_stack(); f_single_string_replace_create(i); } }
19f7672003-02-18Marcus Comstedt /*! @endclass */ /*! @class Bootstring
3715ee2003-02-19Marcus Comstedt  *! *! This class implements the "Bootstring" string transcoder described in
abf31d2003-09-11Marcus Comstedt  *! @url{ftp://ftp.rfc-editor.org/in-notes/rfc3492.txt@}.
19f7672003-02-18Marcus Comstedt  */ PIKECLASS bootstring { CVAR INT_TYPE base, tmin, tmax, skew, damp; CVAR INT_TYPE initial_bias, initial_n; CVAR p_wchar2 delim;
0b775e2003-02-19Marcus Comstedt  PIKEVAR string digits flags ID_STATIC|ID_PRIVATE;
19f7672003-02-18Marcus Comstedt  static INT_TYPE bootstring_cp_to_digit(p_wchar2 ch) { ptrdiff_t digit = THIS->digits->len; PCHARP digits = MKPCHARP_STR( THIS->digits ); while (digit>=0) if (INDEX_PCHARP( digits, digit ) == ch) return digit; else --digit; return -1; } static INT_TYPE bootstring_adapt(INT_TYPE delta, INT_TYPE numpoints, int firsttime) { struct bootstring_struct *bs = THIS; INT_TYPE k = 0, b = bs->base; INT_TYPE a = b - bs->tmin; INT_TYPE limit = (a * bs->tmax) >> 1; if (firsttime) delta /= bs->damp; else delta >>= 1; delta += delta / numpoints; while (delta > limit) { delta /= a; k += b; } return k + (a + 1)*delta / (delta + bs->skew); }
3715ee2003-02-19Marcus Comstedt  /*! @decl string decode(string s) *! *! Decodes a Bootstring encoded string of "basic" code points back *! to the original string space. */
19f7672003-02-18Marcus Comstedt  PIKEFUN string decode(string s) { struct bootstring_struct *bs = THIS; INT_TYPE n = bs->initial_n; INT_TYPE i = 0; INT_TYPE bias = bs->initial_bias; ptrdiff_t pos, input_left; PCHARP input; struct string_builder output; init_string_builder( &output,0 ); input = MKPCHARP_STR( s ); input_left = s->len; for (pos = input_left-1; pos > 0; --pos) if (INDEX_PCHARP( input, pos ) == bs->delim) { string_builder_append( &output, input, pos ); INC_PCHARP( input, pos+1 ); input_left -= pos+1; break; } while (input_left > 0) { INT_TYPE oldi = i; INT_TYPE w = 1; INT_TYPE k; for (k=bs->base; ; k+=bs->base) { INT_TYPE digit, t; if (input_left < 1 || (digit = bootstring_cp_to_digit( EXTRACT_PCHARP( input ) )) < 0) { free_string_builder( &output ); Pike_error( "Invalid variable-length integer.\n" ); } INC_PCHARP( input, 1 ); --input_left; i += digit * w; /* fail on overflow... */ if (k <= bias + bs->tmin) t = bs->tmin; else if (k >= bias + bs->tmax) t = bs->tmax; else t = k - bias; if (digit < t) break; w *= (bs->base - t); } bias = bootstring_adapt( i - oldi, output.s->len+1, !oldi ); n += i / (output.s->len+1); i %= output.s->len+1; string_builder_putchar( &output, n ); if (i != output.s->len-1) switch (output.s->size_shift) { case 0: { p_wchar0 *s = STR0(output.s); INT_TYPE p = output.s->len; while (--p>i) s[p] = s[p-1]; s[p] = n; } break; case 1: { p_wchar1 *s = STR1(output.s); INT_TYPE p = output.s->len; while (--p>i) s[p] = s[p-1]; s[p] = n; } break; case 2: { p_wchar2 *s = STR2(output.s); INT_TYPE p = output.s->len; while (--p>i) s[p] = s[p-1]; s[p] = n; } break; default: Pike_fatal("Illegal shift size!\n"); } i++; } RETURN finish_string_builder( &output ); }
3715ee2003-02-19Marcus Comstedt  /*! @decl string encode(string s) *! *! Encodes a string using Bootstring encoding into a string constisting *! only of "basic" code points (< initial_n). */
19f7672003-02-18Marcus Comstedt  PIKEFUN string encode(string s) { struct bootstring_struct *bs = THIS; INT_TYPE n = bs->initial_n; INT_TYPE delta = 0; INT_TYPE bias = bs->initial_bias; INT_TYPE c, h, b = 0; ptrdiff_t pos, input_left; PCHARP input; struct string_builder output; init_string_builder( &output,0 ); input = MKPCHARP_STR( s ); input_left = s->len; for (pos=0; pos<input_left; pos++) if ((c = INDEX_PCHARP( input, pos )) < n) { string_builder_putchar( &output, c ); b++; } if ((h = b)) string_builder_putchar( &output, bs->delim ); while (h < input_left) { INT_TYPE m = -1; for (pos=0; pos<input_left; pos++) if ((c = INDEX_PCHARP( input, pos )) >= n && (m < 0 || c < m)) m = c; delta = delta + (m - n) * (h + 1); /* fail on overflow... */ n = m; for (pos=0; pos<input_left; pos++) if ((c = INDEX_PCHARP( input, pos )) < n) delta++; else if (c == n) { INT_TYPE k, q = delta; for (k=bs->base; ; k+=bs->base) { INT_TYPE t, bt; if (k <= bias + bs->tmin) t = bs->tmin; else if(k >= bias + bs->tmax) t = bs->tmax; else t = k-bias; if (q < t) break; bt = bs->base - t; string_builder_putchar( &output, index_shared_string( bs->digits, t + (q-t)%bt ) ); q = (q-t) / bt; } string_builder_putchar( &output, index_shared_string( bs->digits, q ) ); bias = bootstring_adapt( delta, h+1, h==b ); delta = 0; h++; } delta++; n++; } RETURN finish_string_builder( &output ); }
3715ee2003-02-19Marcus Comstedt  /*! @decl void create(int base, int tmin, int tmax, int skew, @ *! int damp, int initial_bias, int initial_n, @ *! int delim, string digits) *! *! Creates a Bootstring transcoder instance using the specified parameters. *! *! @param base *! The base used by the variable-length integers. *! @param tmin *! The minimum threshold digit value for the variable-length integers. *! Must be >=0 and <= tmax. *! @param tmax *! The maximum threshold digit value for the variable-length integers. *! Must be <= base-1. *! @param skew *! The skew term for the bias adapation. Must be >= 1. *! @param damp
1ae3d62003-02-19Henrik Grubbström (Grubba)  *! The damping factor for the bias adaption. Must be >= 2.
3715ee2003-02-19Marcus Comstedt  *! @param initial_bias *! The initial bias for the variable-length integer thresholding. *! initial_bias % base must be <= base - tmin. *! @param initial_n *! The first code point outside the "basic" set of code points. *! @param delim *! The "basic" code point used as the delimiter. *! @param digits *! The "basic" code points used as digits. The length of the string *! should be the same as the base parameter. */
19f7672003-02-18Marcus Comstedt  PIKEFUN void create( int base, int tmin, int tmax, int skew, int damp, int initial_bias, int initial_n, int delim, string digits )
3715ee2003-02-19Marcus Comstedt  flags ID_STATIC;
19f7672003-02-18Marcus Comstedt  { struct bootstring_struct *bs = THIS;
3715ee2003-02-19Marcus Comstedt  if (base<2) Pike_error("Bogus base\n"); if (tmin<0 || tmax<tmin || base-1<tmax) Pike_error("Parameters violate 0 <= tmin <= tmax <= base-1\n"); if (skew < 1) Pike_error("Parameters violate skew >= 1\n"); if (damp < 2) Pike_error("Parameters violate damp >= 2\n"); if (initial_bias%base > base-tmin) Pike_error("Parameters violate initial_bias%%base <= base-tmin\n"); if (digits->len != base) Pike_error("Length of digits string does not match base.\n");
19f7672003-02-18Marcus Comstedt  bs->base = base; bs->tmin = tmin; bs->tmax = tmax; bs->skew = skew; bs->damp = damp; bs->initial_bias = initial_bias; bs->initial_n = initial_n; bs->delim = delim; if (bs->digits) { free_string( bs->digits ); bs->digits = NULL; } copy_shared_string( bs->digits, digits ); } }
da90692002-11-26Henrik Grubbström (Grubba) /*! @endclass */ /*! @endmodule */ /*! @module System */ /*! @class Time *! *! The current time as a structure containing a sec and a usec *! member. */ PIKECLASS Time { CVAR int hard_update; /*! @decl int sec; *! @decl int usec; *! *! The number of seconds and microseconds since the epoch and the *! last whole second, respectively. (See also @[predef::time()]) *! *! Please note that these variables will continually update when *! they are requested, there is no need to create new Time() *! objects. */ /*! @decl int usec_full; *! *! The number of microseconds since the epoch. Please note that *! pike has to have been compiled with bignum support for this *! variable to contain sensible values. */ PIKEFUN int `->( string x ) { extern struct timeval current_time; struct pike_string *usec; struct pike_string *sec;
51adb82003-01-12Martin Stjernholm 
de56ec2003-02-08Martin Stjernholm  MAKE_CONST_STRING( sec, "sec" ); MAKE_CONST_STRING( usec, "usec" );
da90692002-11-26Henrik Grubbström (Grubba)  if( !x ) RETURN 0; if( THIS->hard_update ) GETTIMEOFDAY( &current_time ); if( x == usec ) RETURN current_time.tv_usec; if( x == sec ) RETURN current_time.tv_sec; #ifdef AUTO_BIGNUM pop_stack(); push_int( current_time.tv_sec ); push_int( 1000000 ); f_multiply( 2 ); push_int( current_time.tv_usec ); f_add( 2 ); return; #else RETURN (current_time.tv_sec * 1000000 + current_time.tv_usec); #endif } PIKEFUN int `[]( string x ) { f_Time_cq__backtick_2D_3E( args ); } /*! @decl static void create( int fast ); *! *! If fast is true, do not request a new time from the system, *! instead use the global current time variable. *! *! This will only work in callbacks, but can save significant amounts *! of CPU. */ PIKEFUN void create( int|void fast ) { THIS->hard_update = !fast; } } /*! @endclass */ /*! @class Timer */ PIKECLASS Timer { CVAR struct timeval last_time; CVAR int hard_update; /*! @decl float peek() *! Return the time in seconds since the last time @[get] was called. */ PIKEFUN float peek( ) { extern struct timeval current_time; FLOAT_TYPE res; if( THIS->hard_update ) GETTIMEOFDAY( &current_time ); res = current_time.tv_sec-THIS->last_time.tv_sec + (current_time.tv_usec-THIS->last_time.tv_usec)/1000000.0; RETURN res; } /*! @decl float get() *! Return the time in seconds since the last time get was called. *! The first time this method is called the time since the object *! was created is returned instead. */ PIKEFUN float get( ) { extern struct timeval current_time; f_Timer_peek( 0 ); THIS->last_time = current_time; return; } /*! @decl static void create( int|void fast ) *! Create a new timer object. The timer keeps track of relative time *! with sub-second precision. *! *! If fast is specified, the timer will not do system calls to get *! the current time but instead use the one maintained by pike. This *! will result in faster but somewhat more inexact timekeeping. *! Also, if your program never utilizes the pike event loop the pike *! maintained current time never change. */ PIKEFUN void create( int|void fast ) { extern struct timeval current_time; THIS->hard_update = !fast; if( THIS->hard_update ) GETTIMEOFDAY( &current_time ); THIS->last_time = current_time; } }
fed7de2001-06-28Henrik Grubbström (Grubba) /*! @endclass */ /*! @endmodule */
a3c4332001-06-20Per Hedbor 
8bef1b2001-09-27Fredrik Hübinette (Hubbe)  PIKECLASS automap_marker { PIKEVAR array arg; PIKEVAR int depth; PIKEFUN void create(array a, int d) { if(THIS->arg) free_array(THIS->arg); add_ref(THIS->arg=a); THIS->depth=d; } PIKEFUN string _sprintf(int mode, mapping flags) { pop_n_elems(args);
e6dbc22002-11-29Marcus Comstedt  if (mode != 'O') { push_undefined (); return; }
8bef1b2001-09-27Fredrik Hübinette (Hubbe)  push_text("%O%*'[*]'n"); if(THIS->arg) ref_push_array(THIS->arg); else push_int(0); push_int(THIS->depth*3); f_sprintf(3); } } static void low_automap(int d, int depth, struct svalue *fun, struct svalue *real_args, INT32 args) { INT32 x,e,tmp,size=0x7fffffff; struct svalue *tmpargs=Pike_sp - args; struct array *ret;
2523ce2003-04-28Martin Stjernholm  TYPE_FIELD types;
8bef1b2001-09-27Fredrik Hübinette (Hubbe)  for(e=0;e<args;e++) { if(real_args[e].type==T_OBJECT && real_args[e].u.object->prog == automap_marker_program && OBJ2_AUTOMAP_MARKER(real_args[e].u.object)->depth >= d) { if(tmpargs[e].type != T_ARRAY)
a241e02001-09-27Fredrik Hübinette (Hubbe)  index_error("__automap__", Pike_sp-args, args, tmpargs, NULL, "Automap on non-array.\n");
8bef1b2001-09-27Fredrik Hübinette (Hubbe)  tmp=tmpargs[e].u.array->size; if(tmp < size) size=tmp; } } #ifdef PIKE_DEBUG if(size == 0x7fffffff)
5aad932002-08-15Marcus Comstedt  Pike_fatal("No automap markers found in low_automap\n");
8bef1b2001-09-27Fredrik Hübinette (Hubbe) #endif push_array(ret=allocate_array(size));
2523ce2003-04-28Martin Stjernholm  types = 0;
8bef1b2001-09-27Fredrik Hübinette (Hubbe)  for(x=0;x<size;x++) { for(e=0;e<args;e++) { if(real_args[e].type==T_OBJECT && real_args[e].u.object->prog == automap_marker_program && OBJ2_AUTOMAP_MARKER(real_args[e].u.object)->depth >= d) { #ifdef PIKE_DEBUG if(x >= tmpargs[e].u.array->size)
5aad932002-08-15Marcus Comstedt  Pike_fatal("low_automap failed to determine size!\n");
8bef1b2001-09-27Fredrik Hübinette (Hubbe) #endif push_svalue(ITEM(tmpargs[e].u.array)+x); }else{ push_svalue(tmpargs+e); } } if(d == depth) apply_svalue(fun,args); else low_automap(d+1,depth,fun,real_args,args);
2523ce2003-04-28Martin Stjernholm  stack_pop_to_no_free (ITEM(ret) + x); types |= 1 << ITEM(ret)[x].type;
8bef1b2001-09-27Fredrik Hübinette (Hubbe)  }
2523ce2003-04-28Martin Stjernholm  ret->type_field = types;
8bef1b2001-09-27Fredrik Hübinette (Hubbe)  stack_unlink(args); } PIKEFUN array __automap__(mixed fun, mixed ... tmpargs) efun; { int e,depth=-1; check_stack(args); for(e=0;e<args-1;e++) { if(tmpargs[e].type==T_OBJECT && tmpargs[e].u.object->prog == automap_marker_program) { int tmp=OBJ2_AUTOMAP_MARKER(tmpargs[e].u.object)->depth; if(tmp > depth) depth=tmp; ref_push_array(OBJ2_AUTOMAP_MARKER(tmpargs[e].u.object)->arg); }else{ push_svalue(tmpargs+e); } } check_stack(depth * (args+1)); low_automap(1,depth,fun,tmpargs,args-1); stack_unlink(args); }
db628a2004-09-10Henrik Grubbström (Grubba) /* Linked list stuff. */ #undef INIT_BLOCK #define INIT_BLOCK(NODE) do { \ (NODE)->next = (NODE)->prev = NULL; \ (NODE)->refs = 1; \ (NODE)->val.type = T_INT; \ (NODE)->val.subtype = NUMBER_UNDEFINED; \ (NODE)->val.u.integer = 0; \ } while(0) #undef EXIT_BLOCK #define EXIT_BLOCK(NODE) do { \
cf8d5c2004-09-11Henrik Grubbström (Grubba)  if ((NODE)->prev) { \ free_list_node((NODE)->prev); \ } \ if ((NODE)->next) { \ free_list_node((NODE)->next); \ } \
db628a2004-09-10Henrik Grubbström (Grubba)  free_svalue(&(NODE)->val); \ } while(0) BLOCK_ALLOC_FILL_PAGES(list_node, 4); PMOD_EXPORT void free_list_node(struct list_node *node) { if (!sub_ref(node)) { really_free_list_node(node); } } PMOD_EXPORT void unlink_list_node(struct list_node *n) { #ifdef PIKE_DEBUG if (!n) { Pike_fatal("Unlinking NULL node.\n"); } if (!n->next || !n->prev) { Pike_fatal("Unlinking unlinked node.\n"); } #endif /* PIKE_DEBUG */
cf8d5c2004-09-11Henrik Grubbström (Grubba)  if (n->prev->next == n) { #ifdef PIKE_DEBUG if (n->next->prev != n) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ n->prev->next = n->next; n->next->prev = n->prev; n->next = n->prev = NULL;
db628a2004-09-10Henrik Grubbström (Grubba) 
cf8d5c2004-09-11Henrik Grubbström (Grubba)  /* We've lost two references. */ free_list_node(n); free_list_node(n); } else { #ifdef PIKE_DEBUG if (n->next->prev == n) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ /* The node is already detached. */ n->next = n->prev = NULL; } } PMOD_EXPORT void detach_list_node(struct list_node *n) { #ifdef PIKE_DEBUG if (!n) { Pike_fatal("Detaching NULL node.\n"); } if (!n->next || !n->prev) { Pike_fatal("Detaching unlinked node.\n"); } #endif /* PIKE_DEBUG */ if (n->prev->next == n) { #ifdef PIKE_DEBUG if (n->next->prev != n) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ n->prev->next = n->next; n->next->prev = n->prev; add_ref(n->next); add_ref(n->prev); /* We've lost two references. */ free_list_node(n); free_list_node(n); #ifdef PIKE_DEBUG } else if (n->next->prev == n) { Pike_fatal("Partially detached node.\n"); #endif /* PIKE_DEBUG */ }
db628a2004-09-10Henrik Grubbström (Grubba) } PMOD_EXPORT void prepend_list_node(struct list_node *node,
6699482004-09-28Henrik Grubbström (Grubba)  struct list_node *new_node)
db628a2004-09-10Henrik Grubbström (Grubba) { #ifdef PIKE_DEBUG if (!node) { Pike_fatal("No node to prepend.\n"); } if (!node->prev) { Pike_fatal("Prepending unhooked node.\n"); }
6699482004-09-28Henrik Grubbström (Grubba)  if (!new_node) {
db628a2004-09-10Henrik Grubbström (Grubba)  Pike_fatal("Prepending NULL node.\n"); }
6699482004-09-28Henrik Grubbström (Grubba)  if (new_node->next || new_node->prev) {
db628a2004-09-10Henrik Grubbström (Grubba)  Pike_fatal("Prepending hooked node.\n"); } #endif /* PIKE_DEBUG */
6699482004-09-28Henrik Grubbström (Grubba)  new_node->next = node; new_node->prev = node->prev; new_node->prev->next = node->prev = new_node; add_ref(new_node); add_ref(new_node);
db628a2004-09-10Henrik Grubbström (Grubba) } PMOD_EXPORT void append_list_node(struct list_node *node,
6699482004-09-28Henrik Grubbström (Grubba)  struct list_node *new_node)
db628a2004-09-10Henrik Grubbström (Grubba) { #ifdef PIKE_DEBUG if (!node) { Pike_fatal("No node to append.\n"); } if (!node->next) { Pike_fatal("Appending unhooked node.\n"); }
6699482004-09-28Henrik Grubbström (Grubba)  if (!new_node) {
db628a2004-09-10Henrik Grubbström (Grubba)  Pike_fatal("Appending NULL node.\n"); }
6699482004-09-28Henrik Grubbström (Grubba)  if (new_node->next || new_node->prev) {
db628a2004-09-10Henrik Grubbström (Grubba)  Pike_fatal("Appending hooked node.\n"); } #endif /* PIKE_DEBUG */
6699482004-09-28Henrik Grubbström (Grubba)  new_node->next = node->next; new_node->prev = node; new_node->next->prev = node->next = new_node; add_ref(new_node); add_ref(new_node);
db628a2004-09-10Henrik Grubbström (Grubba) }
83c38b2004-09-14Henrik Grubbström (Grubba) /*! @module ADT */
db628a2004-09-10Henrik Grubbström (Grubba) /*! @class List *! *! Linked list of values. */ PIKECLASS List {
cf8d5c2004-09-11Henrik Grubbström (Grubba)  CVAR struct list_node *head;
db628a2004-09-10Henrik Grubbström (Grubba)  CVAR INT32 head_sentinel_refs;
cf8d5c2004-09-11Henrik Grubbström (Grubba)  CVAR struct list_node *tail; /* Always NULL. */
db628a2004-09-10Henrik Grubbström (Grubba)  CVAR INT32 tail_sentinel_refs;
cf8d5c2004-09-11Henrik Grubbström (Grubba)  CVAR struct list_node *tail_pred;
ce2d402004-09-17Henrik Grubbström (Grubba)  CVAR INT32 num_elems;
db628a2004-09-10Henrik Grubbström (Grubba)  #define HEAD_SENTINEL(this) ((struct list_node *)(&this->head)) #define TAIL_SENTINEL(this) ((struct list_node *)(&this->tail))
cf8d5c2004-09-11Henrik Grubbström (Grubba)  /* Sentinel overlap description: * * List Head sentinel Tail sentinel * head next * head_sentinel_refs refs * tail prev next * tail_sentinel_refs refs * tail_pred prev */
d3438c2004-09-12Henrik Grubbström (Grubba)  /* Suggestions for future functionality: *
0319bd2004-09-14Henrik Grubbström (Grubba)  * o Pop tail
d3438c2004-09-12Henrik Grubbström (Grubba)  * o Join * o Copy segment * o Detach segment (requires new iterator implementation) * o Iterator copy
0319bd2004-09-14Henrik Grubbström (Grubba)  * o _equal() for iterators and lists. * o _values(), _search(), cast() * o _sizeof()?, _indices()??
d3438c2004-09-12Henrik Grubbström (Grubba)  * o Support for reverse(), filter() and map(). * o Initialization from array. */
db628a2004-09-10Henrik Grubbström (Grubba)  INIT { THIS->tail = NULL; THIS->head = TAIL_SENTINEL(THIS); THIS->tail_pred = HEAD_SENTINEL(THIS);
5d32cf2004-09-15Henrik Grubbström (Grubba)  THIS->head_sentinel_refs = THIS->tail_sentinel_refs = 2;
ce2d402004-09-17Henrik Grubbström (Grubba)  THIS->num_elems = 0;
db628a2004-09-10Henrik Grubbström (Grubba)  } EXIT { struct list_node *node = THIS->head; struct list_node *next; while ((next = node->next)) {
cf8d5c2004-09-11Henrik Grubbström (Grubba) #ifdef PIKE_DEBUG if (node->refs != 2) { Pike_fatal("Unexpected number of references for node: %d\n", node->refs); } #endif /* PIKE_DEBUG */
db628a2004-09-10Henrik Grubbström (Grubba)  unlink_list_node(node); node = next; } }
7468fd2004-09-14Henrik Grubbström (Grubba)  /* These two functions perform the same thing, * but are optimized to minimize recursion. */ static void gc_check_list_node_backward(struct list_node *node, const char *msg); static void gc_check_list_node_forward(struct list_node *node, const char *msg) { while (node && !debug_gc_check(&node->refs, msg)) { if (node->next) debug_gc_check_svalues(&node->val, 1, " as a list node value"); gc_check_list_node_backward(node->prev, msg); node = node->next; } } static void gc_check_list_node_backward(struct list_node *node, const char *msg) { while (node && !debug_gc_check(&node->refs, msg)) { if (node->prev) debug_gc_check_svalues(&node->val, 1, " as a list node value"); gc_check_list_node_forward(node->next, msg); node = node->prev; } }
cf8d5c2004-09-11Henrik Grubbström (Grubba)  /* Called at gc_check time. */ GC_CHECK {
7468fd2004-09-14Henrik Grubbström (Grubba)  gc_check_list_node_backward(HEAD_SENTINEL(THIS), " as a list node");
5d32cf2004-09-15Henrik Grubbström (Grubba)  gc_check_list_node_forward(TAIL_SENTINEL(THIS), " as a list node");
cf8d5c2004-09-11Henrik Grubbström (Grubba)  } /* Called at gc_mark time */ GC_RECURSE { struct list_node *node = THIS->head; struct list_node *next; while ((next = node->next)) { gc_recurse_svalues(&node->val, 1); node = next; } }
0319bd2004-09-14Henrik Grubbström (Grubba)  /*! @decl int(0..1) is_empty() *! *! Check if the list is empty. *! *! @returns *! Returns @expr{1@} if the list is empty, *! and @expr{0@} (zero) if there are elements in the list. */ PIKEFUN int(0..1) is_empty() { push_int(!THIS->head->next); }
ce2d402004-09-17Henrik Grubbström (Grubba)  /*! @decl int(0..) _sizeof() *! *! Returns the number of elements in the list. */ PIKEFUN int(0..) _sizeof() flags ID_STATIC; { push_int(THIS->num_elems); }
2122b62004-10-28Henrik Grubbström (Grubba)  /*! @decl string _sprintf(int c, mapping(string:mixed)|void attr) *! *! Describe the list. *! *! @seealso *! @[sprintf()], @[lfun::_sprintf()] */ PIKEFUN string _sprintf(int c, mapping(string:mixed)|void attr) flags ID_STATIC; { if (!THIS->num_elems) { push_constant_text("ADT.List(/* empty */)"); } else if (c == 'O') { struct list_node *node = THIS->head; if (THIS->num_elems == 1) { push_constant_text("ADT.List(/* 1 element */\n"); } else { push_constant_text("ADT.List(/* %d elements */\n"); push_int(THIS->num_elems); f_sprintf(2); } while (node->next) { if (node->next->next) { push_constant_text(" %O,\n"); } else { push_constant_text(" %O\n"); } push_svalue(&node->val); f_sprintf(2); node = node->next; } push_constant_text(")"); f_add(THIS->num_elems + 2); } else { if (THIS->num_elems == 1) { push_constant_text("ADT.List(/* 1 element */)"); } else { push_constant_text("ADT.List(/* %d elements */)"); push_int(THIS->num_elems); f_sprintf(2); } } stack_pop_n_elems_keep_top(args); }
0319bd2004-09-14Henrik Grubbström (Grubba)  /*! @decl mixed head() *! *! Get the element at the head of the list. *! *! @throws *! Throws an error if the list is empty. *! *! @seealso *! @[is_empty()], @[tail()], @[pop()] */ PIKEFUN mixed head() { if (THIS->head->next) { push_svalue(&THIS->head->val); } else { Pike_error("Empty list.\n"); } } /*! @decl mixed tail() *! *! Get the element at the tail of the list. *! *! @throws *! Throws an error if the list is empty. *! *! @seealso *! @[is_empty()], @[head()], @[pop()] */ PIKEFUN mixed tail() { if (THIS->tail->prev) { push_svalue(&THIS->tail->val); } else { Pike_error("Empty list.\n"); } } /*! @decl mixed pop() *! *! Pop the element at the head of the list from the list. *! *! @throws *! Throws an error if the list is empty. *! *! @seealso *! @[is_empty()], @[head()], @[tail()] */ PIKEFUN mixed pop() { if (THIS->head->next) { push_svalue(&THIS->head->val); if (THIS->head->refs == 2) { unlink_list_node(THIS->head); } else { detach_list_node(THIS->head); }
ce2d402004-09-17Henrik Grubbström (Grubba)  THIS->num_elems--;
0319bd2004-09-14Henrik Grubbström (Grubba)  } else { Pike_error("Empty list.\n"); } }
db628a2004-09-10Henrik Grubbström (Grubba)  /*! @decl void append(mixed ... values) *! *! Append @[values] to the end of the list. *! *! @seealso *! @[insert()] */ PIKEFUN void append(mixed ... values) { struct list_node *node = TAIL_SENTINEL(THIS); while (args--) {
6699482004-09-28Henrik Grubbström (Grubba)  struct list_node *new_node = alloc_list_node(); new_node->val = *(--Pike_sp); prepend_list_node(node, new_node); free_list_node(node = new_node);
ce2d402004-09-17Henrik Grubbström (Grubba)  THIS->num_elems++;
db628a2004-09-10Henrik Grubbström (Grubba)  } push_int(0); } /*! @decl void insert(mixed ... values) *! *! Insert @[values] at the front of the list. *! *! @seealso *! @[append()] */ PIKEFUN void insert(mixed ... values) { struct list_node *node = THIS->head; while (args--) {
6699482004-09-28Henrik Grubbström (Grubba)  struct list_node *new_node = alloc_list_node(); new_node->val = *(--Pike_sp); prepend_list_node(node, new_node); free_list_node(node = new_node);
ce2d402004-09-17Henrik Grubbström (Grubba)  THIS->num_elems++;
db628a2004-09-10Henrik Grubbström (Grubba)  } push_int(0); } /*! @decl void create(mixed ... values) *! *! Create a new @[List], and initialize it with @[values]. *! *! @fixme *! Ought to reset the @[List] if called multiple times. */ PIKEFUN void create(mixed ... values) flags ID_STATIC; { /* FIXME: Reset the list? */ apply_current(f_List_append_fun_num, args); } /*! @class _get_iterator *! *! @[Iterator] that loops over the @[List]. */ PIKECLASS _get_iterator program_flags PROGRAM_USES_PARENT; flags ID_STATIC; { CVAR struct list_node *cur;
cf8d5c2004-09-11Henrik Grubbström (Grubba)  CVAR INT32 ind; /* Not meaningful, but requred by the API. */ /* NOTE: cur may never refer to an unlinked node. * cur may however refer to a detached node, or to sentinels. */
db628a2004-09-10Henrik Grubbström (Grubba) 
ce2d402004-09-17Henrik Grubbström (Grubba)  static struct List_struct *List__get_iterator_find_parent()
db628a2004-09-10Henrik Grubbström (Grubba)  { struct external_variable_context loc; loc.o = Pike_fp->current_object; loc.parent_identifier = Pike_fp->fun; loc.inherit = INHERIT_FROM_INT(loc.o->prog, loc.parent_identifier); find_external_context(&loc, 1);
ce2d402004-09-17Henrik Grubbström (Grubba)  return (struct List_struct *)(loc.o->storage + loc.inherit->storage_offset); } INIT { add_ref(THIS->cur = List__get_iterator_find_parent()->head);
db628a2004-09-10Henrik Grubbström (Grubba)  THIS->ind = 0; } EXIT { if (THIS->cur) { free_list_node(THIS->cur); THIS->cur = NULL; } }
cf8d5c2004-09-11Henrik Grubbström (Grubba)  /* Called at gc_check time. */ GC_CHECK {
7468fd2004-09-14Henrik Grubbström (Grubba)  gc_check_list_node_forward(THIS->cur, " held by an iterator"); } /* These two functions perform the same thing, * but are optimized to minimize recursion. */ static void gc_recurse_list_node_tree_backward(struct list_node *node, struct list_node *back); static void gc_recurse_list_node_tree_forward(struct list_node *node, struct list_node *back) { if (!node || !node->next) return; if (node->next->prev == node) { /* List member. Recursed from the list recurse code. */
cf8d5c2004-09-11Henrik Grubbström (Grubba) #ifdef PIKE_DEBUG
7468fd2004-09-14Henrik Grubbström (Grubba)  if (node->prev->next != node) {
cf8d5c2004-09-11Henrik Grubbström (Grubba)  Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ return; } #ifdef PIKE_DEBUG
7468fd2004-09-14Henrik Grubbström (Grubba)  if (node->prev->next == node) {
cf8d5c2004-09-11Henrik Grubbström (Grubba)  Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */
7468fd2004-09-14Henrik Grubbström (Grubba)  while (1) { gc_recurse_svalues(&node->val, 1); if (node->prev != back) gc_recurse_list_node_tree_backward(node->prev, node->next); back = node->prev; node = node->next; if (!node->next || (node->next->prev == node)) { /* List member. Recursed from the list recurse code. */ #ifdef PIKE_DEBUG if (node->prev->next != node) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ break; } #ifdef PIKE_DEBUG if (node->prev->next == node) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ } } static void gc_recurse_list_node_tree_backward(struct list_node *node, struct list_node *next) { if (!node || !node->prev) return; if (node->prev->next == node) { /* List member. Checked from the list check code. */ #ifdef PIKE_DEBUG if (node->next->prev != node) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ return; } #ifdef PIKE_DEBUG if (node->next->prev == node) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ while (1) { gc_recurse_svalues(&node->val, 1); if (node->next != next) gc_recurse_list_node_tree_forward(node->next, node->prev); next = node->next; node = node->prev; if (!node->prev || (node->prev->next == node)) { /* List member. Recursed from the list recurse code. */ #ifdef PIKE_DEBUG if (node->next->prev != node) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ break; } #ifdef PIKE_DEBUG if (node->next->prev == node) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ } }
cf8d5c2004-09-11Henrik Grubbström (Grubba)  /* Called at gc_mark time */ GC_RECURSE { if (!THIS->cur->next || !THIS->cur->prev) return; if (THIS->cur->next->prev == THIS->cur) { #ifdef PIKE_DEBUG if (THIS->cur->prev->next != THIS->cur) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ return; } #ifdef PIKE_DEBUG if (THIS->cur->prev->next == THIS->cur) { Pike_fatal("Partially detached node.\n"); } #endif /* PIKE_DEBUG */ /* Detached node. */ gc_recurse_svalues(&THIS->cur->val, 1);
7468fd2004-09-14Henrik Grubbström (Grubba)  gc_recurse_list_node_tree_forward(THIS->cur->next, THIS->cur->prev); gc_recurse_list_node_tree_backward(THIS->cur->next, THIS->cur->prev);
cf8d5c2004-09-11Henrik Grubbström (Grubba)  }
db628a2004-09-10Henrik Grubbström (Grubba)  PIKEFUN int(0..1) `!() flags ID_STATIC; { pop_n_elems(args); push_int(!THIS->cur->next || !THIS->cur->prev); } PIKEFUN int(0..) index() { pop_n_elems(args); if (THIS->cur->next && THIS->cur->prev) { push_int(THIS->ind); } else { push_undefined(); } } /*! @decl mixed value() *! *! @returns *! Returns the value at the current position. */ PIKEFUN mixed value() { pop_n_elems(args); if (THIS->cur->next && THIS->cur->prev) { push_svalue(&THIS->cur->val); } else { push_undefined(); } } /*! @decl int(0..1) first() *! *! Reset the iterator. *! *! @returns *! Returns @expr{1@} if there are elements in the list, *! and @expr{0@} (zero) if the list is empty. */ PIKEFUN int(0..1) first() { struct external_variable_context loc; struct List_struct *parent; pop_n_elems(args); /* Find our parent. */ loc.o = Pike_fp->current_object; loc.parent_identifier = Pike_fp->fun; loc.inherit = INHERIT_FROM_INT(loc.o->prog, loc.parent_identifier); find_external_context(&loc, 1); parent = (struct List_struct *)(loc.o->storage + loc.inherit->storage_offset); free_list_node(THIS->cur); add_ref(THIS->cur = parent->head); THIS->ind = 0; pop_n_elems(args); if (THIS->cur->next) { push_int(1); } else { push_undefined(); } } /*! @decl int(0..1) next() *! *! Advance to the next element in the list. *! *! @returns *! Returns @expr{1@} on success, and @expr{0@} (zero) *! at the end of the list. *! *! @seealso *! @[prev()] */ PIKEFUN int(0..1) next() { struct list_node *next; if ((next = THIS->cur->next)) { free_list_node(THIS->cur); add_ref(THIS->cur = next); THIS->ind++; if (next->next) { pop_n_elems(args); push_int(1); return; } } pop_n_elems(args); push_int(0); } /*! @decl int(0..1) prev() *! *! Retrace to the previous element in the list. *! *! @returns *! Returns @expr{1@} on success, and @expr{0@} (zero) *! at the beginning of the list. *! *! @seealso *! @[next()] */ PIKEFUN int(0..1) prev() { struct list_node *prev; if ((prev = THIS->cur->prev)) { free_list_node(THIS->cur); add_ref(THIS->cur = prev); THIS->ind--; if (prev->prev) { pop_n_elems(args); push_int(1); return; } } pop_n_elems(args); push_int(0); } /*! @decl Iterator `+=(int steps) *! *! Advance or retrace the specified number of @[steps]. *! *! @seealso *! @[next()], @[prev] */ PIKEFUN Iterator `+=(int steps) { if (!steps) return; if (steps > 0) { while (steps--) { apply_current(f_List_cq__get_iterator_next_fun_num, 0); pop_stack(); } } else { while (steps++) { apply_current(f_List_cq__get_iterator_prev_fun_num, 0); pop_stack(); } } pop_n_elems(args); ref_push_object(Pike_fp->current_object); } /*! @decl void insert(mixed val) *! *! Insert @[val] at the current position. *! *! @seealso *! @[append()], @[delete()], @[set()] */ PIKEFUN void insert(mixed val) {
6699482004-09-28Henrik Grubbström (Grubba)  struct list_node *new_node;
db628a2004-09-10Henrik Grubbström (Grubba)  if (!THIS->cur->prev) { Pike_error("Attempt to insert before the start sentinel.\n"); }
6699482004-09-28Henrik Grubbström (Grubba)  new_node = alloc_list_node(); assign_svalue_no_free(&new_node->val, val); prepend_list_node(THIS->cur, new_node);
db628a2004-09-10Henrik Grubbström (Grubba)  free_list_node(THIS->cur);
6699482004-09-28Henrik Grubbström (Grubba)  THIS->cur = new_node;
ce2d402004-09-17Henrik Grubbström (Grubba)  List__get_iterator_find_parent()->num_elems++;
db628a2004-09-10Henrik Grubbström (Grubba)  pop_n_elems(args); push_int(0); } /*! @decl void append(mixed val) *! *! Append @[val] after the current position. *! *! @seealso *! @[insert()], @[delete()], @[set()] */ PIKEFUN void append(mixed val) {
6699482004-09-28Henrik Grubbström (Grubba)  struct list_node *new_node;
db628a2004-09-10Henrik Grubbström (Grubba)  if (!THIS->cur->next) { Pike_error("Attempt to append after the end sentinel.\n"); }
6699482004-09-28Henrik Grubbström (Grubba)  new_node = alloc_list_node(); assign_svalue_no_free(&new_node->val, val); append_list_node(THIS->cur, new_node); free_list_node(new_node);
ce2d402004-09-17Henrik Grubbström (Grubba)  List__get_iterator_find_parent()->num_elems++;
db628a2004-09-10Henrik Grubbström (Grubba)  pop_n_elems(args); push_int(0); } /*! @decl void delete() *! *! Delete the current node. *! *! The current position will advance to the next node. *! This function thus performes the reverse operation *! of @[insert()]. *! *! @seealso *! @[insert()], @[append()], @[set()] */ PIKEFUN void delete() { struct list_node *next; if (!(next = THIS->cur->next) || !THIS->cur->prev) { Pike_error("Attempt to delete a sentinel.\n"); }
cf8d5c2004-09-11Henrik Grubbström (Grubba)  add_ref(next);
ce2d402004-09-17Henrik Grubbström (Grubba)  if (next->prev == THIS->cur) { if (THIS->cur->refs == 3) { unlink_list_node(THIS->cur); } else { /* There's some other iterator holding references to this node. */ detach_list_node(THIS->cur); } List__get_iterator_find_parent()->num_elems--;
cf8d5c2004-09-11Henrik Grubbström (Grubba)  }
db628a2004-09-10Henrik Grubbström (Grubba)  free_list_node(THIS->cur);
cf8d5c2004-09-11Henrik Grubbström (Grubba)  THIS->cur = next;
db628a2004-09-10Henrik Grubbström (Grubba)  pop_n_elems(args); push_int(0); } /*! @decl void set(mixed val) *! *! Set the value of the current position to @[val]. *! *! @seealso *! @[insert()], @[append()], @[delete()] */ PIKEFUN void set(mixed val) { if (!THIS->cur->next || !THIS->cur->prev) { Pike_error("Attempt to set a sentinel.\n"); } assign_svalue(&THIS->cur->val, val); pop_n_elems(args); push_int(0); } } /*! @endclass */ } /*! @endclass */
83c38b2004-09-14Henrik Grubbström (Grubba) /*! @endmodule */
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) void init_builtin(void) {
7468fd2004-09-14Henrik Grubbström (Grubba)  init_list_node_blocks();
ab82822000-05-25Fredrik Hübinette (Hubbe) INIT
3a5b1d2000-05-24Fredrik Hübinette (Hubbe) }
8650752001-06-25Henrik Grubbström (Grubba)  void exit_builtin(void) { EXIT
7468fd2004-09-14Henrik Grubbström (Grubba)  free_all_list_node_blocks();
8650752001-06-25Henrik Grubbström (Grubba) }