pike.git
/
src
/
builtin.cmod
version
»
Context lines:
10
20
40
80
file
none
3
pike.git/src/builtin.cmod:1:
-
/* -*- c -*-
+
/* -*-
mode:
c
;
encoding: utf
-
8; -
*-
|| 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.
-
|| $Id$
+
*/ #include "global.h" #include "interpret.h" #include "svalue.h" #include "pike_macros.h" #include "object.h" #include "program.h" #include "array.h" #include "pike_error.h"
pike.git/src/builtin.cmod:20:
#include "pike_types.h" #include "pike_memory.h" #include "threads.h" #include "module_support.h" #include "cyclic.h" #include "bignum.h" #include "main.h" #include "operators.h" #include "builtin_functions.h" #include "fsort.h"
-
#include "
port
.h"
+
#include "
stuff
.h"
#include "gc.h"
-
#include "block_
alloc
.h"
+
#include "block_
allocator
.h"
#include "pikecode.h"
-
+
#include "opcodes.h"
+
#include "whitespace.h"
#include <ctype.h> #include <errno.h> #include <math.h>
-
+
#ifdef HAVE_ARPA_INET_H
+
#include <arpa/inet.h>
+
#endif /* HAVE_ARPA_INET_H */
+
+
#define DEFAULT_CMOD_STORAGE
+
DECLARATIONS
-
+
+
/*! @module System
+
*/
+
+
/*! @class TM
+
*! A wrapper for the system struct tm time keeping structure.
+
*! This can be used as a (very) lightweight alternative to Calendar.
+
*/
+
PIKECLASS TM
+
{
+
CVAR struct tm t;
+
CVAR time_t unix_time;
+
CVAR int modified;
+
CVAR struct pike_string *set_zone;
+
+
#ifdef STRUCT_TM_HAS___TM_GMTOFF
+
#define tm_zone __tm_zone
+
#define tm_gmtoff __tm_gmtoff
+
#define GET_GMTOFF(TM) ((TM)->tm_gmtoff)
+
#define GET_ZONE(TM) ((TM)->tm_zone)
+
#define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL))
+
#define SET_ZONE(TM, VAL) (((TM)->tm_zone) = (VAL))
+
#elif defined(STRUCT_TM_HAS_GMTOFF)
+
#define GET_GMTOFF(TM) ((TM)->tm_gmtoff)
+
#define GET_ZONE(TM) ((TM)->tm_zone)
+
#define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL))
+
#define SET_ZONE(TM, VAL) (((TM)->tm_zone) = (VAL))
+
#else
+
#define GET_GMTOFF(TM) 0
+
#define GET_ZONE(TM) ((char*)NULL)
+
#define SET_GMTOFF(TM, VAL) (VAL)
+
#define SET_ZONE(TM, VAL) (VAL)
+
#endif
+
+
#define strftime_zone strftime
+
#define mktime_zone mktime
+
#define strptime_zone strptime
+
#define asctime_zone asctime
+
#define localtime_zone(X,Y) localtime(X)
+
#ifndef HAVE_EXTERNAL_TIMEZONE
+
#undef timezone
+
#endif
+
+
#define MODIFY(X) do{ THIS->modified = 1;THIS->t.X; }while(0)
+
#define FIX_THIS() do { \
+
if(THIS->modified){ \
+
THIS->unix_time = mktime_zone( &THIS->t ); \
+
THIS->modified = 0; \
+
} \
+
} while(0)
+
+
#ifdef HAVE_STRPTIME
+
/*! @decl int(0..1) strptime( string(1..255) format, string(1..255) data )
+
*!
+
*! Parse the given @[data] using the format in @[format] as a date.
+
*!
+
*! @dl
+
*! @item %%
+
*! The % character.
+
*!
+
*! @item %a or %A
+
*! The weekday name according to the C locale, in abbreviated
+
*! form or the full name.
+
*!
+
*! @item %b or %B or %h
+
*! The month name according to the C locale, in abbreviated form
+
*! or the full name.
+
*!
+
*! @item %c
+
*! The date and time representation for the C locale.
+
*!
+
*! @item %C
+
*! The century number (0-99).
+
*!
+
*! @item %d or %e
+
*! The day of month (1-31).
+
*!
+
*! @item %D
+
*! Equivalent to %m/%d/%y.
+
*!
+
*! @item %H
+
*! The hour (0-23).
+
*!
+
*! @item %I
+
*! The hour on a 12-hour clock (1-12).
+
*!
+
*! @item %j
+
*! The day number in the year (1-366).
+
*!
+
*! @item %m
+
*! The month number (1-12).
+
*!
+
*! @item %M
+
*! The minute (0-59).
+
*!
+
*! @item %n
+
*! Arbitrary whitespace.
+
*!
+
*! @item %p
+
*! The C locale's equivalent of AM or PM.
+
*!
+
*! @item %R
+
*! Equivalent to %H:%M.
+
*!
+
*! @item %S
+
*! The second (0-60; 60 may occur for leap seconds;
+
*! earlier also 61 was allowed).
+
*!
+
*! @item %t
+
*! Arbitrary whitespace.
+
*!
+
*! @item %T
+
*! Equivalent to %H:%M:%S.
+
*!
+
*! @item %U
+
*! The week number with Sunday the first day of the week (0-53).
+
*!
+
*! @item %w
+
*! The weekday number (0-6) with Sunday = 0.
+
*!
+
*! @item %W
+
*! The week number with Monday the first day of the week (0-53).
+
*!
+
*! @item %x
+
*! The date, using the C locale's date format.
+
*!
+
*! @item %X
+
*! The time, using the C locale's time format.
+
*!
+
*! @item %y
+
*! The year within century (0-99). When a century is not
+
*! otherwise specified, values in the range 69-99 refer to years
+
*! in the twentieth century (1969-1999); values in the range
+
*! 00-68 refer to years in the twenty-first century (2000-2068).
+
*!
+
*! @item %Y
+
*! The year, including century (for example, 1991).
+
*! @enddl
+
*!
+
*/
+
PIKEFUN int(0..1) strptime( string(1..255) format, string(1..255) data )
+
{
+
if( format->size_shift || data->size_shift )
+
Pike_error("Only 8bit strings are supported\n");
+
THIS->modified = 1;
+
if( strptime_zone( data->str, format->str, &THIS->t ) == NULL )
+
RETURN 0;
+
RETURN 1;
+
}
+
#endif /* HAVE_STRPTIME */
+
/*! @decl string(1..255) strftime( string(1..255) format )
+
*! See also @[Gettext.setlocale]
+
*!
+
*! Convert the structure to a string.
+
*!
+
*! @dl
+
*! @item %a
+
*! The abbreviated weekday name according to the current locale
+
*!
+
*! @item %A
+
*! The full weekday name according to the current locale.
+
*!
+
*! @item %b
+
*! The abbreviated month name according to the current locale.
+
*!
+
*! @item %B
+
*! The full month name according to the current locale.
+
*!
+
*! @item %c
+
*! The preferred date and time representation for the current locale.
+
*!
+
*! @item %C
+
*! The century number (year/100) as a 2-digit integer.
+
*!
+
*! @item %d
+
*! The day of the month as a decimal number (range 01 to 31).
+
*!
+
*! @item %D
+
*! Equivalent to @expr{%m/%d/%y@}. (for Americans only.
+
*! Americans should note that in other countries @expr{%d/%m/%y@}
+
*! is rather common. This means that in international context
+
*! this format is ambiguous and should not be used.)
+
*!
+
*! @item %e
+
*! Like @expr{%d@}, the day of the month as a decimal number,
+
*! but a leading zero is replaced by a space.
+
*!
+
*! @item %E
+
*! Modifier: use alternative format, see below.
+
*!
+
*! @item %F
+
*! Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99)
+
*!
+
*! @item %G
+
*! The ISO 8601 week-based year (see NOTES) with century as a
+
*! decimal number. The 4-digit year corresponding to the ISO
+
*! week number (see @expr{%V@}). This has the same format and
+
*! value as @expr{%Y@}, except that if the ISO week number
+
*! belongs to the previous or next year, that year is used instead.
+
*!
+
*! @item %g
+
*! Like @expr{%G@}, but without century, that is,
+
*! with a 2-digit year (00-99). (TZ)
+
*!
+
*! @item %h
+
*! Equivalent to %b.
+
*!
+
*! @item %H
+
*! The hour as a decimal number using a 24-hour clock (range 00 to 23).
+
*!
+
*! @item %I
+
*! The hour as a decimal number using a 12-hour clock (range 01 to 12).
+
*!
+
*! @item %j
+
*! The day of the year as a decimal number (range 001 to 366).
+
*!
+
*! @item %k
+
*! The hour (24-hour clock) as a decimal number (range 0 to 23);
+
*! single digits are preceded by a blank. (See also @expr{%H@}.)
+
*!
+
*! @item %l
+
*! The hour (12-hour clock) as a decimal number (range 1 to 12);
+
*! single digits are preceded by a blank. (See also @expr{%I@}.)
+
*!
+
*! @item %m
+
*! The month as a decimal number (range 01 to 12).
+
*!
+
*! @item %M
+
*! The minute as a decimal number (range 00 to 59).
+
*!
+
*! @item %n
+
*! A newline character. (SU)
+
*!
+
*! @item %O
+
*! Modifier: use alternative format, see below. (SU)
+
*!
+
*! @item %p
+
*! Either @expr{"AM"@} or @expr{"PM"@} according to the given time
+
*! value, or the corresponding strings for the current locale.
+
*! Noon is treated as @expr{"PM"@} and midnight as @expr{"AM"@}.
+
*!
+
*! @item %P
+
*! Like @expr{%p@} but in lowercase: @expr{"am"@} or @expr{"pm"@}
+
*! or a corresponding string for the current locale.
+
*!
+
*! @item %r
+
*! The time in a.m. or p.m. notation. In the POSIX locale this is
+
*! equivalent to @expr{%I:%M:%S %p@}.
+
*!
+
*! @item %R
+
*! The time in 24-hour notation (@expr{%H:%M@}). (SU)
+
*! For a version including the seconds, see @expr{%T@} below.
+
*!
+
*! @item %s
+
*! The number of seconds since the Epoch,
+
*! 1970-01-01 00:00:00 +0000 (UTC). (TZ)
+
*!
+
*! @item %S
+
*! The second as a decimal number (range 00 to 60).
+
*! (The range is up to 60 to allow for occasional leap seconds.)
+
*!
+
*! @item %t
+
*! A tab character. (SU)
+
*!
+
*! @item %T
+
*! The time in 24-hour notation (@expr{%H:%M:%S@}). (SU)
+
*!
+
*! @item %u
+
*! The day of the week as a decimal, range 1 to 7, Monday being 1.
+
*! See also @expr{%w@}. (SU)
+
*!
+
*! @item %U
+
*! The week number of the current year as a decimal number,
+
*! range 00 to 53, starting with the first Sunday as the first
+
*! day of week 01. See also @expr{%V@} and @expr{%W@}.
+
*!
+
*! @item %V
+
*! The ISO 8601 week number of the current year as a decimal number,
+
*! range 01 to 53, where week 1 is the first week that has at least
+
*! 4 days in the new year. See also @expr{%U@} and @expr{%W@}.
+
*!
+
*! @item %w
+
*! The day of the week as a decimal, range 0 to 6, Sunday being 0.
+
*! See also @expr{%u@}.
+
*! @enddl
+
*/
+
PIKEFUN string strftime(string(1..255) format)
+
{
+
char *buffer = xalloc( 8192 );
+
buffer[0] = 0;
+
strftime_zone( buffer, 8192, format->str, &THIS->t );
+
push_text( buffer );
+
}
+
+
/*! @decl int(0..60) sec;
+
*! @decl int(0..59) min;
+
*! @decl int(0..59) hour;
+
*! @decl int(1..31) mday;
+
*! @decl int(0..11) mon;
+
*! @decl int year;
+
*!
+
*! The various fields in the structure. Note that setting these
+
*! might cause other fields to be recalculated, as an example,
+
*! adding 1000 to the hour field would advance the 'mday', 'mon'
+
*! and possibly 'year' fields.
+
*!
+
*! When read the fields are always normalized.
+
*!
+
*! Unlike the system struct tm the 'year' field is not year-1900,
+
*! instead it is the actual year.
+
*/
+
PIKEFUN int(0..60) `sec() { FIX_THIS();RETURN THIS->t.tm_sec; }
+
PIKEFUN int(0..59) `min() { FIX_THIS();RETURN THIS->t.tm_min; }
+
PIKEFUN int(0..23) `hour() { FIX_THIS();RETURN THIS->t.tm_hour; }
+
PIKEFUN int(1..31) `mday() { FIX_THIS();RETURN THIS->t.tm_mday; }
+
PIKEFUN int(0..11) `mon() { FIX_THIS();RETURN THIS->t.tm_mon; }
+
+
PIKEFUN int `year() { FIX_THIS();RETURN THIS->t.tm_year+1900; }
+
PIKEFUN int `sec=(int a) { MODIFY(tm_sec=a); }
+
PIKEFUN int `min=(int a) { MODIFY(tm_min=a); }
+
PIKEFUN int `hour=(int a){ MODIFY(tm_hour=a); }
+
PIKEFUN int `mday=(int a){ MODIFY(tm_mday=a); }
+
PIKEFUN int `year=(int a){ MODIFY(tm_year=a-1900); }
+
PIKEFUN int `mon=(int a){ MODIFY(tm_mon=a); }
+
+
/*! @decl int isdst
+
*!
+
*! True if daylight savings are in effect. If this field is -1
+
*! (the default) it (and the timezone info) will be updated
+
*! automatically using the timezone rules.
+
*/
+
PIKEFUN int(-1..1) `isdst() {
+
FIX_THIS();
+
RETURN THIS->t.tm_isdst;
+
}
+
+
/*! @decl int wday
+
*! The day of the week, sunday is 0, saturday is 6.
+
*! This is calculated from the other fields and can not be changed directly.
+
*/
+
PIKEFUN int(0..6) `wday() { FIX_THIS(); RETURN THIS->t.tm_wday; }
+
+
/*! @decl int yday
+
*! The day of the year, from 0 (the first day) to 365
+
*! This is calculated from the other fields and can not be changed directly.
+
*/
+
PIKEFUN int(0..365) `yday() { FIX_THIS(); RETURN THIS->t.tm_yday; }
+
+
/*! @decl int unix_time()
+
*! Return the unix time corresponding to this time_t. If no time
+
*! can be parsed from the structure -1 is returned.
+
*/
+
PIKEFUN int unix_time()
+
{
+
FIX_THIS();
+
RETURN THIS->unix_time;
+
}
+
+
/*! @decl string asctime()
+
*! Return a string representing the time. Mostly useful for debug
+
*! purposes, the exact format is very locale (see
+
*! @[Gettext.setlocale]) and OS dependent.
+
*/
+
PIKEFUN string asctime()
+
{
+
FIX_THIS();
+
{
+
char *tval = asctime_zone( &THIS->t );
+
if( tval )
+
push_text( tval );
+
else
+
push_undefined();
+
}
+
}
+
+
PIKEFUN string _sprintf( int flag, mapping options )
+
{
+
int post_sum = 1;
+
switch( flag )
+
{
+
case 'O':
+
push_static_text("System.TM(");
+
post_sum = 1;
+
/* fallthrough */
+
case 's':
+
f_TM_asctime(0);
+
push_static_text("\n");
+
if( GET_ZONE(&(THIS->t)) )
+
{
+
push_static_text(" ");
+
push_text( GET_ZONE(&(THIS->t)) );
+
f_add( 2 );
+
}
+
else
+
push_static_text("");
+
f_replace( 3 );
+
break;
+
case 'd':
+
f_TM_unix_time(0);
+
break;
+
default:
+
Pike_error("Can not format as %c", flag );
+
}
+
if( post_sum )
+
{
+
push_static_text(")");
+
f_add(3);
+
}
+
+
}
+
+
/*! @decl int|string cast(string to)
+
*!
+
*! Casted to an integer @[unix_time] will be returned.
+
*!
+
*! Casting to a string will call @[asctime].
+
*/
+
PIKEFUN int|string cast( string to )
+
flags ID_PROTECTED;
+
{
+
if( to == literal_int_string )
+
{
+
f_TM_unix_time(0);
+
return;
+
}
+
if( to == literal_string_string )
+
{
+
f_TM_asctime(0);
+
return;
+
}
+
pop_stack();
+
push_undefined();
+
}
+
+
/*! @decl string zone
+
*!
+
*! The timezone of this structure
+
*/
+
PIKEFUN string `zone() {
+
FIX_THIS();
+
if( GET_ZONE(&(THIS->t)) )
+
push_text( GET_ZONE(&(THIS->t)) );
+
else
+
push_undefined();
+
}
+
+
/*! @decl int gmtoff
+
*! The offset from GMT for the time in this tm-struct
+
*/
+
PIKEFUN int `gmtoff() {
+
FIX_THIS();
+
push_int( GET_GMTOFF(&(THIS->t)) );
+
}
+
+
/* Setting the zone does not work, so.. */
+
+
/* PIKEFUN string `zone=(string x) { */
+
/* if( THIS->set_zone ) */
+
/* free_string( THIS->set_zone ); */
+
/* THIS->set_zone = x; */
+
/* MODIFY( tm_zone = x->str ); */
+
/* x->refs++; */
+
/* } */
+
+
/*! @decl int(0..1) localtime( int time )
+
*! Initialize the struct tm to the local time for the specified
+
*! unix time_t.
+
*/
+
PIKEFUN int(0..1) localtime( int _t )
+
{
+
time_t t = _t;
+
struct tm *res = localtime_zone( &t, &THIS->t );
+
if( !res )
+
RETURN 0;
+
+
/* These are supposedly correctly by localtime_zone. */
+
SET_GMTOFF(res, GET_GMTOFF(&(THIS->t)));
+
SET_ZONE(res, GET_ZONE(&(THIS->t)));
+
+
THIS->t = *res;
+
THIS->modified = 1;
+
RETURN 1;
+
}
+
+
+
/*! @decl int(0..1) gmtime( int time )
+
*! Initialize the struct tm to the UTC time for the specified
+
*! unix time_t.
+
*/
+
PIKEFUN int(0..1) gmtime( int _t )
+
{
+
time_t t = _t;
+
struct tm *res = gmtime( &t );
+
+
if( !res )
+
RETURN 0;
+
+
THIS->t = *res;
+
THIS->modified = 1;
+
RETURN 1;
+
}
+
+
/*! @decl void create(int t)
+
*! Create a new @[TM] initialized from a unix time_t.
+
*! The timezone will always be UTC when using this function.
+
*/
+
PIKEFUN void create( int _t )
+
{
+
f_TM_gmtime( 1 );
+
if( Pike_sp[-1].u.integer == 0 )
+
Pike_error("time out of range\n");
+
}
+
+
/*! @decl void create()
+
*! Construct a new TM, all fields will be set to 0.
+
*/
+
PIKEFUN void create( )
+
{
+
memset( &THIS->t, 0, sizeof( struct tm ) );
+
THIS->t.tm_isdst = -1;
+
THIS->unix_time = 0;
+
THIS->modified = 1;
+
}
+
+
/*! @decl void create( int year, int(0..11) mon, int(1..31) mday, @
+
*! int(0..24) hour, int(0..59) min, int(0..59) sec, @
+
*! string|void timezone )
+
*! Construct a new time using the given values.
+
*! Slightly faster than setting them individually.
+
*/
+
PIKEFUN void create( int year, int(0..11) mon, int(1..31) mday,
+
int(0..24) hour, int(0..59) min, int(0..59) sec,
+
string|void timezone )
+
{
+
struct tm *t = &THIS->t;
+
t->tm_isdst = -1;
+
t->tm_year = year - 1900;
+
t->tm_mon = mon;
+
t->tm_mday = mday;
+
t->tm_hour = hour;
+
t->tm_min = min;
+
t->tm_sec = sec;
+
if (THIS->set_zone) {
+
free_string(THIS->set_zone);
+
THIS->set_zone = NULL;
+
}
+
if( !timezone ) /* gmtime. */
+
SET_ZONE(t, "UTC");
+
else
+
{
+
add_ref(timezone);
+
THIS->set_zone = timezone;
+
SET_ZONE(t, timezone->str);
+
}
+
THIS->unix_time = mktime_zone( t );
+
}
+
+
INIT {
+
THIS->set_zone = 0;
+
THIS->modified = 0;
+
}
+
+
EXIT {
+
if( THIS->set_zone )
+
free_string( THIS->set_zone );
+
}
+
}
+
/*! @endclass
+
*/
+
#undef FIX_THIS
+
#ifdef STRUCT_TM_HAS___TM_GMTOFF
+
#undef tm_zone
+
#undef tm_gmtoff
+
#endif
+
+
/*! @endmodule
+
*/
+
/*! @decl array(array(int|string|type)) describe_program(program p) *! @belongs Debug *! *! Debug function for showing the symbol table of a program. *! *! @returns *! Returns an array of arrays with the following information *! for each symbol in @[p]: *! @array *! @elem int modifiers
pike.git/src/builtin.cmod:130:
f_aggregate_mapping(0); apply_low(x->u.object, fun + p->inherits[SUBTYPEOF(*x)].identifier_level, 2); if(TYPEOF(Pike_sp[-1]) == T_STRING) { stack_swap(); pop_stack(); return; } else if (UNSAFE_IS_ZERO(Pike_sp-1)) { pop_n_elems(2);
-
push_
constant_text
(
"
object
"
);
+
ref_
push_
string(literal
_object
_string
);
return; } else { Pike_error("Non-string returned from _sprintf()\n"); } } } 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_ARRAY:
ref_
push_
string(literal
_array
_string
); break;
+
case T_FLOAT:
ref_
push_
string(literal
_float
_string
); break;
+
case T_FUNCTION:
ref_
push_
string(literal
_function
_string
); break;
+
case T_INT:
ref_
push_
string(literal
_int
_string
); break;
+
case T_MAPPING:
ref_
push_
string(literal
_mapping
_string
); break;
+
case T_MULTISET:
ref_
push_
string(literal
_multiset
_string
); break;
+
case T_OBJECT:
ref_
push_
string(literal
_object
_string
); break;
+
case T_PROGRAM:
ref_
push_
string(literal
_program
_string
); break;
+
case T_STRING:
ref_
push_
string(literal
_string
_string
); break;
+
case T_TYPE:
ref_
push_
string(literal
_type
_string
); break;
case T_ZERO: push_constant_text("zero"); break; case T_VOID: push_constant_text("void"); break; /* 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;
-
case T_MAPPING_DATA: push_
constant
_text("mapping_data"); break;
-
case T_PIKE_FRAME: push_
constant
_text("pike_frame"); break;
-
case T_MULTISET_DATA: push_
constant
_text("multiset_data"); break;
-
default: push_
constant
_text("unknown"); break;
+
case T_SVALUE_PTR: push_
static
_text("svalue_ptr"); break;
+
case T_OBJ_INDEX: push_
static
_text("obj_index"); break;
+
case T_MAPPING_DATA: push_
static
_text("mapping_data"); break;
+
case T_PIKE_FRAME: push_
static
_text("pike_frame"); break;
+
case T_MULTISET_DATA: push_
static
_text("multiset_data"); break;
+
default: push_
static
_text("unknown"); break;
} } /*! @decl string int2char(int x) *! @appears String.int2char *! *! Same as sprintf("%c",x); *! *! @seealso *! @[sprintf()] */ PMOD_EXPORT PIKEFUN string int2char(int|object x) efun; optflags OPT_TRY_OPTIMIZE;
-
+
rawtype tFunc(tSetvar(0, tOr(tInt,tObj)), tNStr(tVar(0)));
{ int c; struct program *p; if(TYPEOF(*x) == T_OBJECT && (p = x->u.object->prog)) { ptrdiff_t fun = FIND_LFUN(p->inherits[SUBTYPEOF(*x)].prog, LFUN__SPRINTF); if(fun != -1) { push_int('c'); f_aggregate_mapping(0);
pike.git/src/builtin.cmod:290:
while(len && n) { s->str[--len]="0123456789abcdef"[n&0xf]; n>>=4; } } RETURN end_shared_string(s); }
-
static INLINE int hexchar( int v )
-
{
-
return v<10 ? v+'0' : (v-10)+'a';
-
}
-
+
/*! @decl string string2hex(string data) *! @appears String.string2hex *! *! Convert a string of binary data to a hexadecimal string. *! *! @seealso *! @[hex2string()] */
-
+
+
static const char hexchar[] = {
+
'0','1','2','3','4','5','6','7','8','9',
+
'a','b','c','d','e','f'
+
};
+
+
static const unsigned char hexdecode[256] =
+
{
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
+
/* '0' - '9' */
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+
+
16,16,16,16,16,16,16,
+
+
/* 'A' - 'F' */
+
10, 11, 12, 13, 14, 15,
+
+
16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,
+
+
/* 'a' - 'f' */
+
10, 11, 12, 13, 14, 15,
+
+
16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+
};
+
PMOD_EXPORT
-
PIKEFUN string string2hex(string s)
-
errname String.string2hex;
+
PIKEFUN string
(0..255)
string2hex(string s)
optflags OPT_TRY_OPTIMIZE; { struct pike_string *hex;
-
unsigned char *st = (unsigned char *)s->str;
-
int i;
+
unsigned char *
p,*
st = (unsigned char *)s->str;
+
int i
, l
;
if (s->size_shift) Pike_error("Bad argument 1 to string2hex(), expected 8-bit string.\n"); hex = begin_shared_string(2 * s->len);
-
+
p = (unsigned char *)hex->str;
+
l = s->len;
-
for (i=0; i<
s->len
; i++) {
-
hex->str[i<<1]
= hexchar
(st
[
i]
>>4
)
;
-
hex->str[i<<1|1]
= hexchar
(st
[
i]
&15
)
;
+
for (i=0; i<
l
; i++) {
+
*p++
= hexchar
[*
st>>4
]
;
+
*p++
= hexchar
[*
st&15
]
;
+
st++;
} RETURN end_shared_string(hex); }
-
/*! @decl string hex2string(string hex)
+
/*! @decl string
(8bit)
hex2string(string
(8bit)
hex)
*! @appears String.hex2string *!
-
*! Convert a string of hexadecimal digits to binary data.
+
*! Convert a string of hexadecimal digits to binary
+
*!
data.
Non-hexadecimal characters will be ignored when between
+
*! tuples. Eg. "00 00" is ok, but "0 000" isn't.
*! *! @seealso *! @[string2hex()] */ PMOD_EXPORT
-
PIKEFUN string hex2string(string hex)
-
errname String.hex2string;
+
PIKEFUN string
(0..255)
hex2string(string hex)
optflags OPT_TRY_OPTIMIZE; { struct pike_string *s;
-
int
i
,
o=0
;
-
unsigned char *q = (unsigned char *)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");
+
int
tmp
,
i
;
+
unsigned char *
p, *
q = (unsigned char *)hex->str;
+
int l =
i =
hex->len;
+
if(hex->size_shift) Pike_error("
Wide
strings
are
not
allowed.\n");
-
s = begin_shared_string(l);
-
for
(i
=
0
;
i<
l
;
i++
)
+
s = begin_shared_string(l
>>1
);
+
p
=
(
unsigned char *)s->str
;
+
while(
l
> 1
)
{
-
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++
;
+
tmp
=
hexdecode
[
*q++
]
;
+
if
(
tmp
=
=16
)
{ l
-
-;
i--; continue; }
+
*p = tmp
<<4;
+
tmp
=
hexdecode
[
*q++
]
;
+
if
(
tmp
=
=16
)
Pike_error("Illegal
hex format.\n"
);
+
*p++ |= tmp
;
+
l -= 2;
}
-
+
if( l && hexdecode[*q]!=16 )
+
Pike_error("Illegal hex format.\n");
+
+
if( hex->len>>1 == i>>1 )
RETURN end_shared_string(s);
-
+
else
+
RETURN end_and_resize_shared_string(s, i>>1);
}
-
+
/*! @decl array(int) range(string s)
+
*! @appears String.range
+
*!
+
*! Returns the character range of a string in an array of two
+
*! elements. The first element contains the lower bound and the
+
*! second the upper. The precision is only 8 bits, so for wide
+
*! strings only character blocks are known.
+
*/
+
PIKEFUN array(int) string_range(string s)
+
errname range;
+
optflags OPT_TRY_OPTIMIZE;
+
{
+
int min, max;
+
check_string_range(s, 0, &min, &max);
+
pop_n_elems(args);
+
push_int(min);
+
push_int(max);
+
f_aggregate(2);
+
}
+
/*! @decl array column(array data, mixed index) *! *! Extract a column from a two-dimensional array. *! *! This function is exactly equivalent to: *! @code *! map(@[data], lambda(mixed x,mixed y) { return x[y]; }, @[index]) *! @endcode *! *! Except of course it is a lot shorter and faster.
pike.git/src/builtin.cmod:529:
*! this limit is that the current amount of garbage can only be *! measured in a gc run, and if the gc starts to run very seldom *! due to very little garbage, it might get too slow to react to an *! increase in garbage generation. Set to 0.0 to turn this limit *! off. *! @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.
+
*! @member function(:void) "pre_cb"
+
*! This function is called when the gc starts.
+
*! @member function(:void) "post_cb"
+
*! This function is called when the mark and sweep pass of the gc
+
*! is done.
+
*! @member function(object,int,int:void) "destruct_cb"
+
*! This function is called once for each object that is part of
+
*! a cycle just before the gc will destruct it.
+
*! The arguments are:
+
*! @dl
+
*! @item
+
*! The object to be destructed.
+
*! @item
+
*! The reason for it being destructed. One of:
+
*! @int
+
*! @value Object.DESTRUCT_CLEANUP
+
*! Destructed during exit.
+
*! @value Object.DESTRUCT_GC
+
*! Destructed during normal implicit or explicit @[gc()].
+
*! @endint
+
*! @item
+
*! The number of references it had.
+
*! @enddl
+
*! @member function(int:void) "done_cb"
+
*! This function is called when the gc is done and about to exit.
+
*! The argument is the same value as will be returned by gc().
*! @endmapping *! *! @seealso *! @[gc], @[Debug.gc_status] */ PMOD_EXPORT 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 (6)); params = Pike_sp[-1].u.mapping; }
pike.git/src/builtin.cmod:563:
else { \ GET; \ mapping_string_insert (params, str, &get); \ } \ } while (0) #define HANDLE_FLOAT_FACTOR(NAME, VAR) \ HANDLE_PARAM (NAME, { \ if (TYPEOF(*set) != T_FLOAT || \ set->u.float_number < 0.0 || set->u.float_number > 1.0) \
-
SIMPLE_BAD_ARG_ERROR ("
Pike.
gc_parameters", 1, \
+
SIMPLE_BAD_ARG_ERROR ("gc_parameters", 1,
\
"float between 0.0 and 1.0 for " NAME); \
-
VAR =
DO_NOT_WARN
(
(
double) set->u.float_number
)
; \
+
VAR = (double) set->u.float_number;
\
}, { \
-
SET_SVAL(get, T_FLOAT, 0, float_number,
\
-
DO_NOT_WARN
(
(
FLOAT_TYPE) VAR)
)
;
\
+
SET_SVAL(get, T_FLOAT, 0, float_number, (FLOAT_TYPE) VAR); \
}); HANDLE_PARAM ("enabled", { if (TYPEOF(*set) != T_INT || set->u.integer < -1 || set->u.integer > 1)
-
SIMPLE_BAD_ARG_ERROR ("
Pike.
gc_parameters", 1,
+
SIMPLE_BAD_ARG_ERROR ("gc_parameters", 1,
"integer in the range -1..1 for 'enabled'"); if (gc_enabled != set->u.integer) {
-
if (gc_enabled > 0)
-
gc_
enabled
=
set
->u.integer;
-
else
{
-
gc_
enabled
=
1;
-
if (
alloc_threshold =
=
GC
_
MAX_ALLOC_THRESHOLD)
-
alloc_threshold = GC_
MIN
_ALLOC_THRESHOLD;
+
if (gc_enabled > 0)
{
+
/* Disabling automatic
gc
- save the old alloc
_
threshold
and
set
it to
+
*
the
maximum
value
to
avoid
getting
gc_
evaluator_callback
added.
*/
+
saved_
alloc_threshold =
alloc
_
threshold;
+
alloc_threshold = GC_
MAX
_ALLOC_THRESHOLD;
}
-
+
else if (set->u.integer > 0) {
+
/* Enabling automatic gc - restore the old alloc_threshold. If the
+
* gc interval has gotten longer than it should be then the
+
* multiplier calculation in do_gc should compensate. */
+
alloc_threshold = saved_alloc_threshold;
}
-
+
gc_enabled = set->u.integer;
+
}
}, { SET_SVAL(get, T_INT, NUMBER_NUMBER, 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 ("min_gc_time_ratio", gc_min_time_ratio); HANDLE_FLOAT_FACTOR ("average_slowness", gc_average_slowness);
-
+
HANDLE_PARAM("pre_cb", {
+
assign_svalue(&gc_pre_cb, set);
+
}, {
+
assign_svalue(&get, &gc_pre_cb);
+
});
+
HANDLE_PARAM("post_cb", {
+
assign_svalue(&gc_post_cb, set);
+
}, {
+
assign_svalue(&get, &gc_post_cb);
+
});
+
HANDLE_PARAM("destruct_cb", {
+
assign_svalue(&gc_destruct_cb, set);
+
}, {
+
assign_svalue(&get, &gc_destruct_cb);
+
});
+
HANDLE_PARAM("done_cb", {
+
assign_svalue(&gc_done_cb, set);
+
}, {
+
assign_svalue(&get, &gc_done_cb);
+
});
+
#undef HANDLE_PARAM #undef HANDLE_FLOAT_FACTOR REF_RETURN params; } /*! @decl string ctime(int timestamp) *! *! Convert the output from a previous call to @[time()] into a readable *! string containing the current year, month, day and time.
pike.git/src/builtin.cmod:658:
optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; { if(ind->size != val->size) bad_arg_error("mkmapping", Pike_sp-args, args, 2, "array", Pike_sp+1-args, "mkmapping called on arrays of different sizes (%d != %d)\n", ind->size, val->size); RETURN mkmapping(ind, val); }
-
/*! @decl
void
secure(string str)
+
/*! @decl
string
secure(string str)
*! @belongs String *! *! Marks the string as secure, which will clear the memory area *! before freeing the string.
-
+
*!
+
*! @seealso
+
*! @[Object.secure()]
*/ PIKEFUN string string_secure(string str) optflags OPT_SIDE_EFFECT; rawtype tFunc(tSetvar(0, tStr), tVar(0)); { str->flags |= STRING_CLEAR_ON_EXIT; REF_RETURN str; }
-
+
/*! @decl object secure(object str)
+
*! @belongs Object
+
*!
+
*! Marks the object as secure, which will clear the memory area
+
*! before freeing the object.
+
*!
+
*! @seealso
+
*! @[String.secure()]
+
*/
+
PIKEFUN object object_secure(object obj)
+
optflags OPT_SIDE_EFFECT;
+
rawtype tFunc(tSetvar(0, tObj), tVar(0));
+
{
+
obj->flags |= OBJECT_CLEAR_ON_EXIT;
+
REF_RETURN obj;
+
}
+
/*! @decl int count(string haystack, string needle) *! @belongs String *! *! 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. *! *! @seealso *! @[search()], @[`/()] */ PMOD_EXPORT PIKEFUN int string_count(string haystack, string needle)
-
errname
String.
count;
+
errname count;
optflags OPT_TRY_OPTIMIZE; { ptrdiff_t c = 0; ptrdiff_t i, j; switch (needle->len) { case 0: switch (haystack->len) {
pike.git/src/builtin.cmod:715:
default: for (i=0; i<haystack->len; i++) { j=string_search(haystack,needle,i); if (j==-1) break; i=j+needle->len-1; c++; } break; }
-
RETURN
DO_NOT_WARN
(
(
INT_TYPE)c
)
;
+
RETURN (INT_TYPE)c;
} /*! @decl string trim_whites (string s) *! @belongs String *! *! Trim leading and trailing spaces and tabs from the string @[s]. */ PMOD_EXPORT PIKEFUN string string_trim_whites (string s)
-
errname
String.
trim_whites;
+
errname 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; \
pike.git/src/builtin.cmod:775:
*! @note *! Trailing and leading whitespace around \r and \n characters *! is stripped as well (only useful if they're not in the @[whitespace] set). *! *! @note *! This function is a lot faster with just one argument (i.e. the builtin *! whitespace set has an optimised code path). */ PMOD_EXPORT PIKEFUN string string_normalize_space (string s, string|void whitespace)
-
errname
String.
normalize_space;
+
errname normalize_space;
optflags OPT_TRY_OPTIMIZE; { size_t len = s->len, wlen; const void *src = s->str; unsigned shift = s->size_shift, replspace; const void *ws; void *wstemp = 0; struct string_builder sb; unsigned foundspace = 0;
pike.git/src/builtin.cmod:825:
ws = wstemp; } } else ws = 0; init_string_builder_alloc (&sb, len, bshift); if(bshift == shift) sb.known_shift = bshift; }
-
#define SPACECASE8 \
-
case ' ':case '\t':case '\r':case '\n':case '\v':case '\f': \
-
case 0x85:case 0xa0:
-
#include "whitespace.h"
+
switch (shift) { #define NORMALISE_TIGHT_LOOP(TYPE,CASE) \ { \ const TYPE *start = src, *end = start+len; \ if (!ws) { \ TYPE *dst = (void*)sb.s->str; \ for (; start < end; start++) { \ switch(*start) { \ CASE \
pike.git/src/builtin.cmod:893:
} \ string_builder_putchar(&sb, chr); foundspace=0; \ skip##TYPE:; \ } \ } \ } case 0: NORMALISE_TIGHT_LOOP (p_wchar0,SPACECASE8); break; case 1: NORMALISE_TIGHT_LOOP (p_wchar1,SPACECASE16); break; case 2: NORMALISE_TIGHT_LOOP (p_wchar2,SPACECASE16); break; #undef NORMALISE_TIGHT_LOOP
-
#undef SPACECASE8
-
#undef SPACECASE16
+
} if (wstemp) free(wstemp); if (foundspace) sb.s->len--; RETURN finish_string_builder (&sb); } /*! @decl string trim_all_whites (string s) *! @belongs String *! *! Trim leading and trailing white spaces characters (space, tab, *! newline, carriage return, form feed, vertical tab and all the *! white spaces defined in Unicode) from the string @[s]. */ PMOD_EXPORT PIKEFUN string string_trim_all_whites (string s)
-
errname
String.
trim_all_whites;
+
errname trim_all_whites;
optflags OPT_TRY_OPTIMIZE; { ptrdiff_t start = 0, end = s->len; int chr; switch (s->size_shift) {
-
#define SPACECASE8 \
-
case ' ':case '\t':case '\r':case '\n':case '\v':case '\f': \
-
case 0x85:case 0xa0:
-
#include "whitespace.h"
+
#define DO_IT(TYPE,CASE) \ { \ for (; start < end; start++) { \ chr = ((TYPE *) s->str)[start]; \ switch(chr) { \ CASE \ continue; \ } \ break; \
pike.git/src/builtin.cmod:946:
CASE \ continue; \ } \ break; \ } \ } case 0: DO_IT (p_wchar0,SPACECASE8); break; case 1: DO_IT (p_wchar1,SPACECASE16); break; case 2: DO_IT (p_wchar2,SPACECASE16); break; #undef DO_IT
-
#undef SPACECASE8
-
#undef SPACECASE16
+
} RETURN string_slice (s, start, end + 1 - start); }
-
+
/*! @decl string status(int verbose)
+
*! @belongs String
+
*!
+
*! Get string table statistics.
+
*!
+
*! @returns
+
*! Returns a string with an ASCII table containing
+
*! the current string table statistics.
+
*!
+
*! @note
+
*! Currently returns the empty string (@expr{""@})
+
*! if @[verbose] is zero.
+
*!
+
*! @note
+
*! The formatting and contents of the result
+
*! may vary between different versions of Pike.
+
*/
+
PIKEFUN string string_status(int verbose)
+
errname status;
+
{
+
RETURN add_string_status(verbose);
+
}
+
/*! @decl int implements(program prog, program api) *! @belongs Program *! *! Returns 1 if @[prog] implements @[api]. */ PMOD_EXPORT PIKEFUN int program_implements(program prog, program api)
-
errname
Program.
implements;
+
errname implements;
optflags OPT_TRY_OPTIMIZE; { RETURN implements(prog, api); }
-
/*! @decl int inherits(program child, program parent)
+
/*! @decl int inherits(program
|object
child, program parent)
*! @belongs Program *! *! Returns 1 if @[child] has inherited @[parent]. */ PMOD_EXPORT
-
PIKEFUN int program_inherits(program
parent
, program
child
)
-
errname
Program.
inherits;
+
PIKEFUN int program_inherits(program
|object
child
, program
parent
)
+
errname inherits;
optflags OPT_TRY_OPTIMIZE; {
-
RETURN low_get_storage(
parent
,
child
) != -1;
+
struct program *p = program_from_svalue(child);
+
+
if (!p)
+
SIMPLE_ARG_TYPE_ERROR("inherits", 1, "program|object");
+
RETURN low_get_storage(
p
,
parent
) != -1;
} /*! @decl string defined(program p) *! @belongs Program *! *! Returns a string with filename and linenumber describing where *! the program @[p] was defined. *! *! The returned string is of the format @expr{"filename:linenumber"@}. *! *! If it cannot be determined where the program was defined, @expr{0@} *! (zero) will be returned. */ PMOD_EXPORT PIKEFUN string program_defined(program p)
-
errname
Program.
defined;
+
errname defined;
optflags OPT_TRY_OPTIMIZE; {
-
INT32
line;
+
INT_TYPE
line;
struct pike_string *tmp = low_get_program_line(p, &line); pop_n_elems(args); if (tmp) { push_string(tmp); if(line >= 1) {
-
push_
constant
_text(":");
+
push_
static
_text(":");
push_int(line); f_add(3); } } else push_int(0); } /*! @decl int(8..8)|int(16..16)|int(32..32) width(string s) *! @belongs String *! *! 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
+
*!
@returns
+
*!
Three return values are
currently
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
+
*!
+
*! @note
+
*! It is possible that a future version of Pike may return
+
*! further values. In particular the width @expr{7@} seems
+
*! like it could be useful.
*/ PMOD_EXPORT PIKEFUN int(8 .. 8)|int(16 .. 16)|int(32 .. 32) string_width(string s)
-
errname
String.
width;
+
errname width;
optflags OPT_TRY_OPTIMIZE; { RETURN 8 * (1 << s->size_shift); } /*! @decl mixed m_delete(object|mapping map, mixed index) *! *! If @[map] is an object that implements @[lfun::_m_delete()], *! that function will be called with @[index] as its single argument. *!
pike.git/src/builtin.cmod:1119:
} /*! @decl program __empty_program(int|void line, string|void file) */ PIKEFUN program __empty_program(int|zero|void line, string|void file) efun; optflags OPT_EXTERNAL_DEPEND; { struct program *prog = low_allocate_program(); if (file) ext_store_program_line (prog, line, file);
-
#if 0
-
push_program (prog);
-
safe_pike_fprintf (stderr, "Creating empty program %O (%x)\n",
-
Pike_sp - 1, Pike_sp[-1].u.program);
-
Pike_sp--;
-
#endif
+
RETURN prog; } /* Cut the string at the first NUL. */ static struct pike_string *delambda(struct pike_string *str) { PCHARP pcharp = MKPCHARP_STR(str); ptrdiff_t len = pcharp_strlen(pcharp); if (len == str->len) { /* Common case. */ add_ref(str); return str; } return make_shared_binary_pcharp(pcharp, len); }
-
/*! @decl string function_name(function f)
+
/*! @decl string function_name(function
|program
f)
*!
-
*! Return the name of the function @[f].
+
*! Return the name of the function
or program
@[f].
*! *! If @[f] is a global function defined in the runtime @expr{0@} *! (zero) will be returned. *! *! @seealso *! @[function_object()] */ PMOD_EXPORT PIKEFUN string function_name(program|function func) efun; optflags OPT_TRY_OPTIMIZE; {
-
+
int f = -1;
+
struct program *p = NULL;
+
switch(TYPEOF(*func)) { default: SIMPLE_BAD_ARG_ERROR("function_name", 1, "function|program");
-
return;
/* NOTREACHED */
+
UNREACHABLE(
return
)
;
case PIKE_T_PROGRAM: {
-
struct program *
p=func->u.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--; )
pike.git/src/builtin.cmod:1201:
"Parent program info:\n", func->u.program); dump_program_tables(func->u.program->parent, 0); } #endif } break; } case PIKE_T_FUNCTION:
-
if(SUBTYPEOF(*func) == FUNCTION_BUILTIN) break;
-
if(!func->u.object->prog)
+
if(
(f =
SUBTYPEOF(*func)
)
== FUNCTION_BUILTIN) break;
+
if(!
(p =
func->u.object->prog)
)
bad_arg_error("function_name", Pike_sp-args, args, 1, "function", Pike_sp-args, "Destructed object.\n");
-
if(
func->u.object->prog
== pike_trampoline_program)
+
if(
p
== pike_trampoline_program)
{ struct pike_trampoline *t; t=((struct pike_trampoline *)func->u.object->storage);
-
if(t->frame->current_object->prog)
-
RETURN
delambda(ID_FROM_INT(
t->frame->current_object->prog
,
-
t->func
)->name)
;
+
if(t->frame->current_object->prog)
{
+
p
=
t->frame->current_object->prog
;
+
f
=
t->func;
}
-
+
}
-
RETURN
delambda
(
ID_FROM_INT(func-
>
u.object
->
prog,
-
SUBTYPEOF
(
*func
)
)
->name);
+
#ifdef
PIKE_DEBUG
+
if
(
f
>
= p
->
num_identifier_references)
+
Pike_fatal("Function
without
reference.\n");
+
#endif
+
RETURN
delambda(ID_FROM_INT(p,
f
)->name);
} pop_n_elems(args); push_int(0); } /*! @decl object function_object(function f) *! *! Return the object the function @[f] is in. *! *! If @[f] is a global function defined in the runtime @expr{0@}
pike.git/src/builtin.cmod:1328:
SIMPLE_BAD_ARG_ERROR("function_program", 1, "function"); } pop_n_elems(args); push_int(0); } /*! @decl mixed random(object o) *! If random is called with an object, @[lfun::random] will be *! called in the object.
+
*!
*! @seealso
-
*! @[lfun::_random]
+
*! @[lfun::_random
()
]
*/
-
/*! @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()]
-
*/
-
+
PMOD_EXPORT PIKEFUN mixed random(object o) efun; optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; {
-
apply
(o,
"
_
random
",0);
+
int f = low_find_lfun
(o
->prog
,
LFUN
_
_RANDOM);
+
if (f < 0) {
+
Pike_error(
"
Calling undefined lfun::%s.\n"
,
lfun_names[LFUN__RANDOM]);
+
}
+
apply_low(o, f,
0);
stack_swap(); pop_stack(); } /*! @decl int random(int max)
-
*! @decl float random(float max)
+
*!
-
*! This function returns a random number in the range
0
-
@[max]-
1
.
+
*! This function returns a random number in the range
@expr{0
..
@[max]-
1@}
.
*! *! @seealso *! @[random_seed()] */ PMOD_EXPORT PIKEFUN int random(int i) { if(i <= 0) RETURN 0;
-
#if
defined (AUTO_BIGNUM) &&
SIZEOF_INT_TYPE > 4
-
if(i >> 31)
{
-
unsigned
INT_TYPE a =
my_
rand
(
);
-
unsigned INT_TYPE b = my_rand();
-
RETURN (INT_TYPE)(((a<<32)|b) %
i);
-
}
+
#if SIZEOF_INT_TYPE > 4
+
if(i >> 31)
+
RETURN
my_
rand64
(i);
#endif
-
RETURN
my_rand(
) %
i;
+
RETURN my_rand(
i
);
}
-
+
/*! @decl float random(float max)
+
*!
+
*! This function returns a random number in the range @expr{0 .. @[max]-É›@}.
+
*!
+
*! @seealso
+
*! @[random_seed()]
+
*/
+
PMOD_EXPORT PIKEFUN float random(float f) { if(f<=0.0) RETURN 0.0;
-
#define N 1048576
-
RETURN f * (my_rand()%N/((float)N)) +
-
f * (my_rand()%N/( ((float)N) * ((float)N) ));
+
-
+
RETURN (FLOAT_TYPE)ldexp((double)f * my_rand64(0xffffffffffffffff), -64);
} /*! @decl mixed random(array|multiset x) *! Returns a random element from @[x]. */ PMOD_EXPORT PIKEFUN mixed random(array a) rawtype tFunc(tArr(tSetvar(0,tMix)),tVar(0)); { if(!a->size) SIMPLE_BAD_ARG_ERROR("random", 1, "array with elements in it");
-
push_svalue(a->item + (my_rand(
) %
a->size));
+
push_svalue(a->item + (my_rand(a->size))
)
;
stack_swap(); pop_stack(); } PMOD_EXPORT PIKEFUN mixed random(multiset m) rawtype tFunc(tSet(tSetvar(1,tMix)),tVar(1)); { if(multiset_is_empty (m)) SIMPLE_BAD_ARG_ERROR("random", 1, "multiset with elements in it");
-
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
);
+
push_
multiset_
index
(m
,
multiset_get_nth (m, my_rand(multiset_sizeof (m))));
sub_msnode_ref (m);
-
f_aggregate (2);
-
}
-
else {
-
push_multiset_index (m, multiset_get_nth (m, my_rand() %
-
multiset_sizeof (m)));
-
sub_msnode_ref (m);
-
}
+
stack_swap(); pop_stack(); } /*! @decl array random(mapping m) *! Returns a random index-value pair from the mapping. */ PMOD_EXPORT PIKEFUN array random(mapping m) {
-
struct mapping_data *md=m->data;
-
size_t bucket, count;
+
struct keypair *k;
-
+
int e, count;
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
=
my_rand(
m_sizeof
(
m
) );
-
/*
Count
entries
in
bucket
*/
-
count=0;
-
for(k=md->
hash
[bucket];k;k=k->next)
count++;
+
/*
We
could
optimize
this
by
not
iterating
over
hash
buckets we will
+
not pick a member from. */
-
/* 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 */
+
NEW
_
MAPPING_LOOP
(
m->data
)
+
{
+
if
(count--
<
1
)
+
{
push_svalue(&k->ind); push_svalue(&k->val); f_aggregate(2); stack_swap(); pop_stack();
-
+
return;
}
-
+
}
+
}
#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) #define USE_SETENV #else /* Used to hold refs to the strings that we feed to putenv. Indexed on * variable names, values are the "name=value" strings. * * This is not needed when using {,un}setenv(), since they maintain * their own corresponding table. */ static struct mapping *env_allocs = NULL;
pike.git/src/builtin.cmod:1492:
pop_stack(); push_int (0); } else { char *entry = getenv (var->str); pop_stack(); if (!entry) push_int (0); else {
-
char *eq =
STRCHR
(entry, '=');
+
char *eq =
strchr
(entry, '=');
/* There should always be a '=' in the entry, but you never know.. */ push_string (make_shared_string (eq ? eq + 1 : entry)); } } } else { #ifdef DECLARE_ENVIRON extern char **environ; #endif
pike.git/src/builtin.cmod:1520:
* one in gnu libc). */ for (n = 0; environ[n]; n++) {} m = allocate_mapping (n); #ifndef USE_SETENV if (env_allocs) new_env_allocs = allocate_mapping (m_sizeof (env_allocs)); #endif /* !USE_SETENV */ while (--n >= 0) {
-
char *entry = environ[n], *eq =
STRCHR
(entry, '=');
+
char *entry = environ[n], *eq =
strchr
(entry, '=');
if (eq) { /* gnu libc getenv ignores variables without '='. */ struct pike_string *var = make_shared_binary_string (entry, eq - entry); struct pike_string *val = make_shared_string (eq + 1); mapping_string_insert_string (m, var, val); #ifndef USE_SETENV /* Populate new_env_allocs with the env_allocs entries that * are still in use. */ if (env_allocs) { struct svalue *ea_val = low_mapping_string_lookup (env_allocs, var);
pike.git/src/builtin.cmod:1563:
* updates the real environment. Everyone should use the version in * the master instead so that the cache doesn't get stale. */ PIKEFUN void _putenv (string var, void|string val) { #ifndef USE_SETENV struct pike_string *putenv_str, *env_alloc_var; #endif if (var->size_shift) SIMPLE_ARG_TYPE_ERROR ("putenv", 1, "string(0..255)");
-
if (string_has_null (var) ||
STRCHR
(var->str, '='))
+
if (string_has_null (var) ||
strchr
(var->str, '='))
SIMPLE_ARG_ERROR ("putenv", 1, "Variable name cannot contain '=' or NUL."); if (val) { #ifndef USE_SETENV struct string_builder sb; #endif if (val->size_shift) SIMPLE_ARG_TYPE_ERROR ("putenv", 2, "void|string(0..255)"); if (string_has_null (val))
pike.git/src/builtin.cmod:1671:
PIKEVAR array args; /* These are cleared when filename and lineno have been initialized * from them. */ PIKEVAR program prog flags ID_PROTECTED|ID_PRIVATE; CVAR PIKE_OPCODE_T *pc; /* These two are considered to be uninitialized from prog, pc and * fun as long as lineno == -1. */ CVAR struct pike_string *filename;
-
CVAR
INT32
lineno;
+
CVAR
INT_TYPE
lineno;
INIT { THIS->pc = NULL; THIS->lineno = -1; THIS->filename = NULL; } EXIT gc_trivial;
pike.git/src/builtin.cmod:1719:
} #endif assign_svalue(&THIS->_fun, val); } /*! @decl int(0..1) _is_type(string t) *! This object claims to be an array for backward compatibility. */ PIKEFUN int(0..1) _is_type(string t) {
-
INT_TYPE
res =
(t == findstring("array"));
-
pop_n_elems(args);
-
push_int(res);
+
RETURN
(t == findstring("array"));
} static void fill_in_file_and_line() { struct pike_string *file = NULL; assert (THIS->lineno == -1); if (THIS->pc && THIS->prog) { file = low_get_line(THIS->pc, THIS->prog, &THIS->lineno); THIS->pc = NULL;
pike.git/src/builtin.cmod:1772:
*/ PIKEFUN string _sprintf(int c, mapping|void opts) { pop_n_elems(args); if (c != 'O') { push_undefined (); return; }
-
push_text("backtrace_frame(");
+
push_
static_
text("backtrace_frame(");
if (THIS->lineno == -1) fill_in_file_and_line(); if (THIS->filename) { ref_push_string(THIS->filename);
-
push_text(":");
+
push_
static_
text(":");
push_int(THIS->lineno);
-
push_text(", ");
+
push_
static_
text(", ");
f_add(4); } else {
-
push_text("Unknown file, ");
+
push_
static_
text("Unknown file, ");
} if (TYPEOF(THIS->_fun) == PIKE_T_FUNCTION) { if (THIS->_fun.u.object->prog) { #ifdef PIKE_DEBUG if (THIS->_fun.u.object->prog != THIS->oprog) { struct identifier *id = ID_FROM_INT(THIS->oprog, SUBTYPEOF(THIS->_fun)); /* FIXME: Dump dmalloc info for the object? */ Pike_fatal("Lost track of function pointer! Function name was %s.\n", id->name?id->name->str:"<no name>"); } #endif push_svalue(&THIS->_fun); f_function_name(1);
-
push_text("(), ");
+
push_
static_
text("(), ");
f_add(2); } else { free_svalue(&THIS->_fun); SET_SVAL(THIS->_fun, PIKE_T_INT, NUMBER_DESTRUCTED, integer, 0);
-
push_text("destructed_function(), ");
+
push_
static_
text("destructed_function(), ");
}
-
+
} else if (TYPEOF(THIS->_fun) == PIKE_T_PROGRAM) {
+
/* FIXME: Use the master? */
+
push_static_text("program(), ");
+
} else if (TYPEOF(THIS->_fun) == PIKE_T_STRING) {
+
push_svalue(&THIS->_fun);
+
push_static_text("(), ");
+
f_add(2);
} else {
-
push_text("destructed_function(), ");
+
push_
static_
text("destructed_function(), ");
} if (THIS->args) {
-
push_text("Args: ");
+
push_
static_
text("Args: ");
push_int(THIS->args->size); f_add(2); } else {
-
push_text("No args");
+
push_
static_
text("No args");
}
-
push_text(")");
+
push_
static_
text(")");
f_add(5); } /*! @decl int(3..) _sizeof() */ PIKEFUN int(3..) _sizeof() { if (THIS->args) { push_int(THIS->args->size + 3); } else { push_int(3); } } /*! @decl mixed `[](int index, int|void end_or_none) *! The BacktraceFrame object can be indexed as an array. */ PIKEFUN mixed `[](int index, int|void end_or_none) { INT_TYPE end = index;
-
INT32 numargs =
0
;
+
INT32 numargs =
3
;
INT32 i;
-
if (THIS->args)
{
-
numargs = THIS->args->size;
-
}
+
if (THIS->args)
+
numargs
+
= THIS->args->size;
-
numargs += 3;
-
+
if (!end_or_none) {
-
if (index < 0)
{
+
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)
{
+
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 (TYPEOF(*end_or_none) != PIKE_T_INT) {
-
SIMPLE_BAD_ARG_ERROR("`[]",2,"int|void");
-
}
+
} else
end = end_or_none->u.integer;
-
}
+
pop_n_elems(args); if (end_or_none) { if ((end < 0) || (end < index) || (index >= numargs)) { f_aggregate(0); return; }
-
if (end >= numargs)
{
+
if (end >= numargs)
end = numargs-1; }
-
}
+
for (i = index; i <= end; i++) { switch(i) { case 0: /* Filename */ if (THIS->lineno == -1) fill_in_file_and_line();
-
if (THIS->filename)
{
+
if (THIS->filename)
ref_push_string(THIS->filename);
-
}
else
{
+
else
push_int(0);
-
}
+
break; case 1: /* Linenumber */ if (THIS->lineno == -1) fill_in_file_and_line(); push_int(THIS->lineno); break; case 2: /* Function */ push_svalue(&THIS->_fun); break; default: /* Arguments */ { if ((i > 2) && (THIS->args) && (i-3 < THIS->args->size)) { push_svalue(THIS->args->item + (i - 3)); break; }
-
bad_arg_error("
backtrace_frame->
`[]", Pike_sp-args, args, 1,
+
bad_arg_error("`[]", 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;
+
UNREACHABLE(
break
)
;
} }
-
if (end_or_none)
{
+
if (end_or_none)
f_aggregate(1 + end - index); }
-
}
+
/*! @decl mixed `[]=(int index, mixed value) */ PIKEFUN mixed `[]=(int index, mixed value) {
-
INT32 numargs =
0
;
+
INT32 numargs =
3
;
-
if (THIS->args)
{
-
numargs = THIS->args->size;
-
}
+
if (THIS->args)
+
numargs
+
= THIS->args->size;
-
numargs += 3;
-
-
if ((index < -numargs) || (index >= numargs))
{
+
if ((index < -numargs) || (index >= numargs))
index_error("pike_frame->`[]=", Pike_sp-args, args, NULL, Pike_sp-args, "Index %"PRINTPIKEINT"d is out of array range 0..%d,\n", index, numargs-1);
-
}
else if (index < 0)
{
+
else if (index < 0)
index += numargs;
-
}
+
if (args > 2) { pop_n_elems(args - 2); args = 2; } switch(index) { case 0: /* Filename */ if (THIS->lineno == -1) fill_in_file_and_line(); if (TYPEOF(*value) != PIKE_T_STRING) { if ((TYPEOF(*value) != PIKE_T_INT) || (value->u.integer)) {
-
SIMPLE_BAD_ARG_ERROR("
backtrace_frame->
`[]=", 2,
-
"string|int(0..0)");
+
SIMPLE_BAD_ARG_ERROR("`[]=", 2, "string|int(0..0)");
} if (THIS->filename) { free_string(THIS->filename); THIS->filename = NULL; } } else { if (THIS->filename) { free_string(THIS->filename); THIS->filename = NULL; } copy_shared_string(THIS->filename, value->u.string); } break; case 1: /* Linenumber */ if (THIS->lineno == -1) fill_in_file_and_line();
-
if (TYPEOF(*value) != PIKE_T_INT)
{
-
SIMPLE_BAD_ARG_ERROR("
backtrace_frame->
`[]=", 2, "int(1..)");
-
}
+
if (TYPEOF(*value) != PIKE_T_INT)
+
SIMPLE_BAD_ARG_ERROR("`[]=", 2, "int(1..)");
THIS->lineno = value->u.integer; break; case 2: /* Function */ if (THIS->lineno == -1) fill_in_file_and_line(); assign_svalue(&THIS->_fun, value); break; default: /* Arguments */ assign_svalue(THIS->args->item + index - 3, value); break;
pike.git/src/builtin.cmod:2015:
*! Usually @expr{32@} or @expr{64@}. *! @member int(0..1) "auto_bignum" *! Present if integers larger than the native size are automatically *! converted into bignums. *! @endmapping */ PIKEFUN mapping(string:int|string) get_runtime_info() optflags OPT_TRY_OPTIMIZE; { pop_n_elems(args);
-
push_
constant
_text("bytecode_method");
-
push_
constant_
text(PIKE_BYTECODE_METHOD_NAME);
-
push_
constant
_text("abi");
+
push_
static
_text("bytecode_method");
+
push_text(PIKE_BYTECODE_METHOD_NAME);
+
push_
static
_text("abi");
push_int(sizeof(void *) * 8);
-
push_
constant
_text("native_byteorder");
+
push_
static
_text("native_byteorder");
push_int(PIKE_BYTEORDER);
-
push_
constant
_text("int_size");
+
push_
static
_text("int_size");
push_int(sizeof(INT_TYPE) * 8);
-
push_
constant
_text("float_size");
+
push_
static
_text("float_size");
push_int(sizeof(FLOAT_TYPE) * 8);
-
#ifdef
AUTO_BIGNUM
-
push_
constant
_text("auto_bignum");
+
push_
static
_text("auto_bignum");
push_int(1); f_aggregate_mapping(6*2);
-
#else
-
f_aggregate_mapping(5*2);
-
#endif
+
} /*! @endmodule */ void low_backtrace(struct Pike_interpreter_struct *i) { struct svalue *stack_top = i->stack_pointer; struct pike_frame *f, *of = 0; int size = 0;
pike.git/src/builtin.cmod:2069:
SET_SVAL(res->item[size], PIKE_T_OBJECT, 0, object, o); bf = OBJ2_BACKTRACE_FRAME(o); if ((bf->prog = f->context->prog)) { add_ref(bf->prog); bf->pc = f->pc; }
+
SET_SVAL(bf->_fun, PIKE_T_INT, NUMBER_DESTRUCTED, integer, 0);
+
if (f->current_object && f->current_object->prog) {
-
+
if (f->fun == FUNCTION_BUILTIN) {
+
/* Unusual case. The frame is from call_c_initializers(), gc()
+
* or similar. cf [bug 6156]. /grubba
+
*
+
* Masquerade as the program.
+
*
+
* FIXME: Ought to keep parent-pointers.
+
*/
+
SET_SVAL(bf->_fun, PIKE_T_PROGRAM, 0,
+
program, f->current_object->prog);
+
add_ref(f->current_object->prog);
+
} else {
SET_SVAL(bf->_fun, PIKE_T_FUNCTION, CHECK_IDREF_RANGE(f->fun, f->current_object->prog), object, f->current_object); add_ref(f->current_object);
-
+
function = ID_FROM_INT(f->current_object->prog, f->fun);
#ifdef PIKE_DEBUG add_ref(bf->oprog = bf->_fun.u.object->prog); #endif
-
function = ID_FROM_INT(f->current_object->prog, f->fun);
-
} else {
-
SET_SVAL(bf->_fun, PIKE_T_INT, NUMBER_DESTRUCTED, integer, 0);
+
}
-
+
}
if (f->locals) {
-
INT32 numargs =
DO_NOT_WARN
(
(
INT32) MINIMUM(f->num_args,
-
stack_top - f->locals)
)
;
+
INT32 numargs = (INT32) MINIMUM(f->num_args,
+
stack_top - f->locals);
INT32 varargs = 0; if(of && of->locals) { /* 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)
)
;
+
* reason. I don't know why. /mast
+
*
+
* possibly because f->num_args was uninitialized for c_initializers
+
*
/
arne
+
* */
+
+
numargs = (INT32)MINIMUM(f->num_args,of->locals - f->locals);
} numargs = MAXIMUM(numargs, 0); /* Handle varargs... */ if (function && (function->identifier_flags & IDENTIFIER_VARARGS) && (f->locals + numargs < stack_top) && (TYPEOF(f->locals[numargs]) == T_ARRAY)) { varargs = f->locals[numargs].u.array->size; }
pike.git/src/builtin.cmod:2144:
*! @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
-
*!
(than 7.1)
of Pike to accommodate for deferred backtraces.
+
*! of Pike to accommodate for deferred backtraces.
*! *! 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
pike.git/src/builtin.cmod:2195:
*! .. call add several times in code ... *! *! string result = b->get(); // also clears the buffer *! @endcode */ PIKECLASS Buffer { CVAR struct string_builder str; CVAR int initial;
+
PIKEFUN int _size_object()
+
{
+
if( THIS->str.s )
+
RETURN THIS->str.malloced;
+
RETURN 0;
+
}
+
void f_Buffer_get_copy( INT32 args ); void f_Buffer_get( INT32 args ); void f_Buffer_add( INT32 args );
-
+
/*! @decl void create(int initial_size) *! *! Initializes a new buffer. *! *! If no @[initial_size] is specified, 256 is used. If you *! know approximately how big the buffer will be, you can optimize *! the operation of @[add()] (slightly) by passing the size to this *! function. */ PIKEFUN void create( int|void size ) { struct Buffer_struct *str = THIS;
-
if(
args
)
+
if(
size
)
str->initial = MAXIMUM( size->u.integer, 512 ); else str->initial = 256; } /*! @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. */ 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 */)" );
+
push_
static_
text( "Buffer(%d /* %d */)" );
if( str->str.s ) { push_int(str->str.s->len); push_int(str->str.malloced); } else { push_int( 0 ); push_int( 0 ); }
pike.git/src/builtin.cmod:2270:
} pop_n_elems( args ); push_undefined(); } /*! @decl mixed cast( string type ) *! It is possible to cast a String.Buffer object to *! a @expr{string@} and an @expr{int@}. */ PIKEFUN mixed cast( string type )
+
flags ID_PROTECTED;
{
-
struct pike_string *string_t;
-
struct pike_string *int_t;
-
MAKE_CONST_STRING( string_t, "string" );
-
MAKE_CONST_STRING( int_t, "int" );
-
-
if( type == string_
t
)
+
if( type ==
literal_
string_
string
)
{
-
pop_
n_elems
(
args
);
+
pop_
stack
();
if( Pike_fp->current_object->refs != 1 ) f_Buffer_get_copy( 0 ); else f_Buffer_get( 0 ); return; }
-
if( type == int_
t
)
+
if( type ==
literal_
int_
string
)
{ 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; }
-
Pike
_
error
(
"Cannot
cast
to
%S\n",
type
);
+
+
pop
_
stack
(
);
+
push_undefined(
);
}
-
/*! @decl String.Buffer `+( string what )
+
/*! @decl String.Buffer `+( string
|String.Buffer
what )
*/
-
PIKEFUN object `+( string what )
+
PIKEFUN object `+( string
|Buffer
what )
+
rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER);
{ struct Buffer_struct *str = THIS, *str2; struct object *res = fast_clone_object( Buffer_program ); str2 = OBJ2_BUFFER( res ); str2->initial = str->initial; if( str->str.s ) init_string_builder_copy (&str2->str, &str->str);
-
+
if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
+
res->flags |= OBJECT_CLEAR_ON_EXIT;
apply( res, "add", 1 ); RETURN res; }
-
/*! @decl String.Buffer `+=( string what )
+
/*! @decl String.Buffer `+=( string
|String.Buffer
what )
*/
-
PIKEFUN object `+=( string what )
+
PIKEFUN object `+=( string
|Buffer
what )
+
rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER);
{ f_Buffer_add( 1 ); REF_RETURN Pike_fp->current_object; }
-
/*! @decl int add(string ... data)
+
/*! @decl int add(string
|String.Buffer
... data)
*! *! Adds @[data] to the buffer. *! *! @returns *! Returns the size of the buffer. *!
-
*!
@seealso
-
*! @[
addat()
]
+
*!
@note
+
*!
Pike 7.8 and earlier did not support adding
@[
String.Buffer
]
s
+
*! directly.
*/
-
PIKEFUN int add( string ... arg1 )
+
PIKEFUN int add( string
|Buffer
... arg1 )
+
rawtype tFuncV(tNone, tOr(tString, tObjIs_BUFFER), tIntPos);
{ struct Buffer_struct *str = THIS;
-
int init_from_arg0 = 0, j;
+
-
if (
!str->str.s &&
args) {
+
if (args) {
+
int init_from_arg0 = 0, j;
ptrdiff_t sum = 0; int shift = 0; for (j=0; j < args; j++) {
-
struct pike_string *a = Pike_sp[j-args].u.string;
+
struct pike_string *a
;
+
if (TYPEOF(Pike_sp[j-args])
=
=
PIKE_T_STRING)
+
a =
Pike_sp[j-args].u.string;
+
else if ((TYPEOF(Pike_sp[j-args]) != PIKE_T_OBJECT) ||
+
(Pike_sp[j-args].u.object->prog != Buffer_program))
+
SIMPLE_BAD_ARG_ERROR("add", j+1, "string|String.Buffer");
+
else {
+
a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s;
+
if (!a) continue;
+
}
sum += a->len; shift |= a->size_shift; }
-
if
(sum < str->initial)
-
sum
= str->
initial;
-
else if (sum >
str
->initial)
-
sum <<= 1
;
+
shift
|
= str->str
.known_shift
;
shift = shift & ~(shift >> 1);
-
-
if (shift == Pike_sp[-args].u.string->size_shift &&
-
init_string_builder_with_string (&str->str, Pike_sp[-args].u.string)) {
-
mark_free_svalue (Pike_sp - args);
-
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);
-
+
/* We know it will be a string that really is this wide. */ str->str.known_shift = shift;
-
}
+
-
for( j = init_from_arg0; j<args; j++ )
-
{
-
struct pike_string *a = Pike_sp[j-args].u.string;
-
string_builder_shared_strcat( &str->str, a );
-
}
-
-
if (str->str.s) {
-
RETURN str->str.s->len;
-
} else {
-
RETURN 0;
-
}
-
}
-
-
/*! @decl int addat(int(0..) pos, string ... data)
-
*!
-
*! Adds @[data] to the buffer, starting at position @[pos].
-
*!
-
*! @returns
-
*! Returns the size of the buffer.
-
*!
-
*! @note
-
*! If the buffer isn't of the required size, it is padded
-
*! with NUL-characters.
-
*!
-
*! @seealso
-
*! @[add()]
-
*/
-
PIKEFUN int addat(int(0..) pos, string ... arg1 )
-
{
-
struct Buffer_struct *str = THIS;
-
-
if (pos < 0)
-
SIMPLE_BAD_ARG_ERROR("addat", 1, "int(0..)");
-
-
if (args) {
-
int init_from_arg0 = 0, j;
-
ptrdiff_t sum = 0;
-
int shift = 0;
-
for (j=1; j < args; j++) {
-
struct pike_string *a = Pike_sp[j-args].u.string;
-
sum += a->len;
-
shift |= a->size_shift;
-
}
-
+
if (!str->str.s) {
-
if (
(
sum
+ pos)
<= str->initial)
{
+
if (sum <= str->initial)
sum = str->initial;
-
}
else
{
+
else
sum <<= 1;
-
sum += pos;
-
}
-
shift = shift & ~(shift >> 1);
+
init_string_builder_alloc(&str->str, sum, shift);
-
} else
{
-
sum += pos;
-
shift |= str->str.known_shift;
-
shift = shift & ~(shift >> 1);
-
if (sum > str->str.s->len) {
-
string_build_mkspace(&str->str, sum
- str->str.s->len
, shift);
-
} else if (shift != str->str.known_shift) {
-
string_build_mkspace(&str->str, 0, shift);
-
}
-
}
-
/* We know it will be a string that really is this wide. */
-
str->str.known_shift = shift;
+
} else
+
string_build_mkspace(&str->str, sum, shift);
-
if
(str->str.s->len <
pos
) {
-
/*
Clear
the padding.
*
/
-
MEMSET
(
str
-
>str.s->str
+
(str->str.s->len
<<
str
-
>str
.
s->size_shift),
-
0,
(pos
-
str
-
>str
.
s->len
)
<< str
->str.s
->size_shift
);
+
for(sum
=
str->str.s->len
,
j = 0; j
<
args;
j++
) {
+
struct
pike_string
*
a;
+
if
(
TYPEOF(Pike_sp[j
-
args])
==
PIKE_T_STRING)
+
a = Pike_sp[j
-
args]
.
u.string;
+
else
{
+
a = OBJ2_BUFFER(Pike_sp[j
-
args]
.
u.object
)->str.s
;
+
if (!a
)
continue
;
}
-
-
for(j
=
1; j<args; j++) {
-
struct
pike_string
*a = Pike
_
sp[j-args].u.string;
-
pike_string_
cpy(MKPCHARP_STR_OFF(str->str.s,
pos
), a);
-
pos
+= a->len;
+
pike_string_cpy(MKPCHARP_STR_OFF(str->str.s,
sum
), a);
+
sum
+= a->len;
}
-
if (
str->str.s->len
< pos) {
-
str->str.s->len
=
pos
;
-
/* Ensure NUL-termination */
-
str->str.s->str[str->str.s->len << str->str.s->size_shift] = 0;
+
str->str.s->len =
sum
;
}
-
}
+
-
if
(
str->str.s
)
{
-
RETURN
str->str.s->len
;
-
} else {
-
RETURN
0;
+
RETURN
str->str.s
?
str->str.s->len
:
0;
}
-
}
+
/*! @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); }
-
+
/*! @decl int sprintf(strict_sprintf_format format, sprintf_args ... args)
+
*! Appends the output from @[sprintf] at the end of the string.
+
*! Returns the resulting size of the String.Buffer.
+
*/
+
PIKEFUN int sprintf(mixed ... arguments)
+
rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)),
+
tAttr("sprintf_args", tMix), tStr);
+
+
{
+
// FIXME: Reset length on exception?
+
struct Buffer_struct *str = THIS;
+
if(!str->str.s)
+
init_string_builder_alloc(&str->str, str->initial, 0);
+
low_f_sprintf(args, &str->str);
+
RETURN str->str.s->len;
+
}
+
/*! @decl string get_copy() *! *! Get the data from the buffer. Significantly slower than @[get], *! but does not clear the buffer.
-
+
*!
+
*! @seealso
+
*! @[get()]
*/ PIKEFUN string get_copy() { struct pike_string *str = THIS->str.s;
-
if( str )
+
ptrdiff_t len;
+
if( str
&& (len = str->len
)
> 0 )
{
-
ptrdiff_t len = str->len;
-
if( len > 0 )
-
{
+
char *d = (char *)str->str; switch( str->size_shift ) { case 0:
-
RETURN
make_shared_binary_string0((p_wchar0 *)d,len);
+
str=
make_shared_binary_string0((p_wchar0 *)d,len);
break; case 1:
-
RETURN
make_shared_binary_string1((p_wchar1 *)d,len);
+
str=
make_shared_binary_string1((p_wchar1 *)d,len);
break; case 2:
-
RETURN
make_shared_binary_string2((p_wchar2 *)d,len);
+
str=
make_shared_binary_string2((p_wchar2 *)d,len);
break; }
-
+
if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
+
str->flags |= STRING_CLEAR_ON_EXIT;
+
RETURN str;
}
-
}
+
push_empty_string(); return; } /*! @decl string get() *! *! Get the data from the buffer. *! *! @note *! This will clear the data in the buffer
-
+
*!
+
*! @seealso
+
*! @[get_copy()], @[clear()]
*/ PIKEFUN string get( ) { struct Buffer_struct *str = THIS;
-
+
pop_n_elems(args);
if( str->str.s ) { struct pike_string *s = finish_string_builder( &str->str ); str->str.malloced = 0; str->str.s = NULL;
-
RETURN
s;
+
if(
Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
+
s
->flags |= STRING_CLEAR_ON_EXIT
;
+
push_string(s);
}
-
pop_n_elems(args);
+
else
push_empty_string();
-
return;
+
}
-
+
/*! @decl void clear()
+
*!
+
*! Empty the buffer, and don't care about the old content.
+
*!
+
*! @note
+
*! This function was not available in Pike 7.8 and earlier.
+
*!
+
*! @seealso
+
*! @[get()]
+
*/
+
PIKEFUN void clear()
+
{
+
/* FIXME: Support resetting the initial size? */
+
struct Buffer_struct *str = THIS;
+
if (str->str.s) {
+
/* FIXME: There's also the alternative of using
+
* reset_string_builder() here.
+
*/
+
free_string_builder(&str->str);
+
str->str.s = NULL;
+
}
+
}
+
/*! @decl int _sizeof() *! *! Returns the size of the buffer. */ PIKEFUN int _sizeof() { struct Buffer_struct *str = THIS; RETURN str->str.s ? str->str.s->len : 0; }
pike.git/src/builtin.cmod:2543:
{ struct Buffer_struct *str = THIS; MEMSET( str, 0, sizeof( *str ) ); } EXIT gc_trivial; { struct Buffer_struct *str = THIS; if( str->str.s )
+
{
+
if( Pike_fp->flags & OBJECT_CLEAR_ON_EXIT )
+
guaranteed_memset( str->str.s->str, 0, str->str.s->len );
free_string_builder( &str->str ); }
-
+
}
GC_RECURSE { if (mc_count_bytes (Pike_fp->current_object) && THIS->str.s) mc_counted_bytes += THIS->str.malloced; } } /*! @endclass */
pike.git/src/builtin.cmod:2573:
*! applied. The Replace object is used internally by the Pike *! optimizer and need not be used manually. */ PIKECLASS multi_string_replace { CVAR struct replace_many_context ctx; /* NOTE: from and to are only kept for _encode()'s use. */ PIKEVAR array from flags ID_PROTECTED; PIKEVAR array to flags ID_PROTECTED;
-
/*!
@decl
void
create
(
array
(
string
)
|mapping
(string
:string
)
|void
from
,
@
-
*!
array(string)
|
string|
void
to)
+
PIKEFUN
int
_size_object()
+
{
+
int res = 0, i;
+
if
(
THIS->ctx.v )
+
{
+
struct svalue tmp;
+
SET_SVAL_TYPE
(
tmp, PIKE_T_STRING
)
;
+
for
(
i=0; i<THIS->ctx.num; i++ )
+
{
+
res += sizeof(struct replace_many_tupel);
+
tmp.u.
string
= THIS->ctx.v[i].ind;
+
res += rec_size_svalue( &tmp, NULL
)
;
+
tmp.u.string = THIS->ctx.v[i].val;
+
res += rec_size_svalue( &tmp
,
NULL );
+
}
+
}
+
+
RETURN res;
+
}
+
+
/*! @decl void create()
+
*! @decl void create(mapping(string:string))
+
*! @decl void create(
array(string)
from, array(string)
|string to)
*/ PIKEFUN void create(array(string)|mapping(string:string)|void from_arg, array(string)|string|void to_arg) { if (THIS->from) { free_array(THIS->from); THIS->from = NULL; } if (THIS->to) { free_array(THIS->to); THIS->to = NULL; } if (THIS->ctx.v) free_replace_many_context(&THIS->ctx);
-
if
(
!
args) {
-
push_int(
0
);
+
switch
(args)
+
{
+
case
0
:
return;
-
}
-
if
(from_arg
&&
TYPEOF(*from_arg) =
=
T_MAPPING)
{
-
if
(to_arg)
{
-
Pike_error("
Bad
number of
arguments to create().\n");
-
}
+
+
case 1:
+
if (TYPEOF(*from_arg)
!
= T_MAPPING)
+
Pike_error("
Illegal
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
-
*
-
* It probably has to do with the "if (!args)" above: It should
-
* be possible to create an empty instance. /mast
-
*/
-
if (!from_arg || !to_arg) {
-
Pike_error("Bad number of arguments to create().\n");
-
}
-
pop_n_elems(args-
2
);
-
args = 2;
+
break
;
+
+
case
2
:
if (TYPEOF(*from_arg) != T_ARRAY) {
-
SIMPLE_BAD_ARG_ERROR("
Replace
", 1,
+
SIMPLE_BAD_ARG_ERROR("
replace
", 1,
"array(string)|mapping(string:string)"); } if (TYPEOF(*to_arg) == T_STRING) { push_int(from_arg->u.array->size); stack_swap(); f_allocate(2); } if (TYPEOF(*to_arg) != T_ARRAY) {
-
SIMPLE_BAD_ARG_ERROR("
Replace
", 2, "array(string)|string");
+
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); } if (!THIS->from->size) { /* Enter no-op mode. */
-
pop_n_elems(args);
-
push_int(0);
+
return; } if( (THIS->from->type_field & ~BIT_STRING) && (array_fix_type_field(THIS->from) & ~BIT_STRING) )
-
SIMPLE_BAD_ARG_ERROR("
Replace
", 1,
+
SIMPLE_BAD_ARG_ERROR("
replace
", 1,
"array(string)|mapping(string:string)"); if( (THIS->to->type_field & ~BIT_STRING) && (array_fix_type_field(THIS->to) & ~BIT_STRING) )
-
SIMPLE_BAD_ARG_ERROR("
Replace
", 2, "array(string)|string");
+
SIMPLE_BAD_ARG_ERROR("
replace
", 2, "array(string)|string");
compile_replace_many(&THIS->ctx, THIS->from, THIS->to, 1);
-
-
pop_n_elems(args);
-
push_int(0);
+
} /*! @decl string `()(string str) */ PIKEFUN string `()(string str) { if (!THIS->ctx.v) { /* The result is already on the stack in the correct place... */ return; }
pike.git/src/builtin.cmod:2690:
push_undefined(); } f_aggregate(2); } /*! @decl void _decode(array(array(string)) encoded) */ PIKEFUN void _decode(array(array(string)) encoded) { INT32 i;
-
for (i=0; i < encoded->size; i++)
{
+
for (i=0; i < encoded->size; i++)
push_svalue(encoded->item + i);
-
stack_swap();
-
}
-
pop_stack();
+
f_multi_string_replace_create(i); } INIT { MEMSET(&THIS->ctx, 0, sizeof(struct replace_many_context)); } EXIT
pike.git/src/builtin.cmod:2757:
THIS->del = NULL; } if (THIS->to) { free_string(THIS->to); THIS->to = NULL; } if (!del) return; if (!to) {
-
SIMPLE_BAD_ARG_ERROR("
String.SingleReplace->create
", 2, "string");
+
SIMPLE_BAD_ARG_ERROR("
replace
", 2, "string");
} if (del == to) { /* No-op... */ return; } copy_shared_string(THIS->del, del); copy_shared_string(THIS->to, to);
pike.git/src/builtin.cmod:2816:
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
-
default: Pike_fatal("Illegal shift.\n");
-
#endif
+
} if(del->len == to->len) { ret = begin_wide_shared_string(str->len, shift); } else { INT32 delimiters = 0; s = str->str;
pike.git/src/builtin.cmod:2885:
} /*! @decl void _decode(array(string)|int(0..0) encoded) */ PIKEFUN void _decode(array(string)|int(0..0) encoded_) { INT32 i = 0; if (TYPEOF(*encoded_) == PIKE_T_ARRAY) { struct array *encoded = encoded_->u.array;
-
for (i=0; i < encoded->size; i++)
{
+
for (i=0; i < encoded->size; i++)
push_svalue(encoded->item + i);
-
stack_swap();
+
}
-
}
-
pop_stack();
+
f_single_string_replace_create(i); } } /*! @endclass */ /*! @class Bootstring *!
pike.git/src/builtin.cmod:3002:
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] =
DO_NOT_WARN
(
(
p_wchar0) n
)
;
+
s[p] = (p_wchar0) 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] =
DO_NOT_WARN
(
(
p_wchar1) n
)
;
+
s[p] = (p_wchar1) 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] =
DO_NOT_WARN
(
(
p_wchar2) n
)
;
+
s[p] = (p_wchar2) n;
} break;
-
#ifdef PIKE_DEBUG
-
default:
-
Pike_fatal("Illegal shift size!\n");
-
#endif
+
} i++; } RETURN finish_string_builder( &output ); } /*! @decl string encode(string s) *! *! Encodes a string using Bootstring encoding into a string constisting
pike.git/src/builtin.cmod:3194:
*! last whole second, respectively. (See also @[predef::time()]) *! *! @note *! Please note that these variables will continually update when *! they are requested, there is no need to create new Time() *! objects. */ PIKEFUN int `sec() {
-
extern
struct timeval
current_time
;
+
struct timeval
now
;
if( THIS->hard_update )
-
GETTIMEOFDAY( &
current
_
time
);
+
ACCURATE_
GETTIMEOFDAY( &
now );
+
else
+
INACCURATE
_
GETTIMEOFDAY(
&now
);
-
RETURN
current_time
.tv_sec;
+
RETURN
now
.tv_sec;
} PIKEFUN int `usec() {
-
extern
struct timeval
current_time
;
+
struct timeval
now
;
if( THIS->hard_update )
-
GETTIMEOFDAY( &
current
_
time
);
+
ACCURATE_
GETTIMEOFDAY( &
now );
+
else
+
INACCURATE
_
GETTIMEOFDAY(
&now
);
-
RETURN
current_time
.tv_usec;
+
RETURN
now
.tv_usec;
} /*! @decl int usec_full *! *! The number of microseconds since the epoch. Please note that *! pike needs to have been compiled with bignum support for this *! variable to contain sensible values. */ PIKEFUN int `usec_full() {
-
extern
struct timeval
current_time
;
+
struct timeval
now
;
if( THIS->hard_update )
-
GETTIMEOFDAY( &
current
_
time
);
+
ACCURATE_
GETTIMEOFDAY( &
now );
+
else
+
INACCURATE
_
GETTIMEOFDAY(
&now
);
-
#ifdef
AUTO_BIGNUM
-
push_int(
current_time
.tv_sec );
+
push_int(
now
.tv_sec );
push_int( 1000000 ); f_multiply( 2 );
-
push_int(
current_time
.tv_usec );
+
push_int(
now
.tv_usec );
f_add( 2 ); return;
-
#else
-
RETURN (current_time.tv_sec * 1000000 + current_time.tv_usec);
-
#endif
+
} /*! @decl protected 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. */
pike.git/src/builtin.cmod:3268:
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
;
+
struct timeval
now
;
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)/(FLOAT_TYPE) 1000000.0;
+
ACCURATE_
GETTIMEOFDAY( &
now );
+
else
+
INACCURATE
_
GETTIMEOFDAY(
&now
);
+
res =
now
.tv_sec-THIS->last_time.tv_sec +
+
(
now
.tv_usec-THIS->last_time.tv_usec)/(FLOAT_TYPE) 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
;
+
INACCURATE_GETTIMEOFDAY(&
THIS->last_time
)
;
return; } /*! @decl protected 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 more or less inexact timekeeping. *! The pike maintained time is only updated when a @[Pike.Backend] *! object stops waiting and starts executing code. */ PIKEFUN void create( int|zero|void fast ) flags ID_PROTECTED; {
-
extern struct timeval current_time;
+
THIS->hard_update = !fast; if( THIS->hard_update )
-
GETTIMEOFDAY( &
current
_time );
-
THIS->last_time
= current_time
;
+
ACCURATE_
GETTIMEOFDAY( &
THIS->last
_time );
+
else
+
INACCURATE_GETTIMEOFDAY( &
THIS->last_time
)
;
} } /*! @endclass */ /*! @endmodule */
-
+
/*! @module Builtin
+
*/
+
+
/*! @class automap_marker
+
*!
+
*! This is an internal class used by @[__automap__()].
+
*!
+
*! It may show up during module dumping or in backtraces
+
*! and the like.
+
*!
+
*! It should in normal circumstances never be used directly.
+
*!
+
*! @seealso
+
*! @[__automap__()], @[map()]
+
*/
PIKECLASS automap_marker { PIKEVAR array arg; PIKEVAR int depth;
-
+
/*! @decl void create(array arr, int depth)
+
*!
+
*! @param arr
+
*! Array that @[__automap__()] is to loop over.
+
*!
+
*! @param depth
+
*! Recursion depth of @[arr] where the loop will be.
+
*/
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); if (mode != 'O') { push_undefined (); return; }
-
push_text("%O%*'[*]'n");
+
push_
static_
text("%O%*'[*]'n");
if(THIS->arg) ref_push_array(THIS->arg); else push_int(0); push_int(THIS->depth*3); f_sprintf(3); } }
-
+
/*! @endclass
+
*/
-
+
/*! @endmodule
+
*/
+
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; TYPE_FIELD types;
pike.git/src/builtin.cmod:3378:
args, tmpargs, NULL, "Automap on non-array.\n"); tmp=tmpargs[e].u.array->size; if(tmp < size) size=tmp; } }
-
#ifdef PIKE_DEBUG
+
if(size == 0x7fffffff)
-
Pike_
fatal
("No automap markers found in
low
_
automap\n
");
-
#endif
+
Pike_
error
("No automap markers found in _
_automap__\n
");
push_array(ret=allocate_array(size)); types = 0; for(x=0;x<size;x++) { for(e=0;e<args;e++) { if(TYPEOF(real_args[e]) == T_OBJECT && real_args[e].u.object->prog == automap_marker_program &&
pike.git/src/builtin.cmod:3415:
apply_svalue(fun,args); else low_automap(d+1,depth,fun,real_args,args); stack_pop_to_no_free (ITEM(ret) + x); types |= 1 << TYPEOF(ITEM(ret)[x]); } ret->type_field = types; stack_unlink(args); }
-
+
/*! @decl array __automap__(function fun, mixed ... args)
+
*!
+
*! Automap execution function.
+
*!
+
*! @param fun
+
*! Function to call for each of the mapped arguments.
+
*!
+
*! @param args
+
*! Arguments for @[fun]. Either
+
*! @mixed
+
*! @type Builtin.automap_marker
+
*! Wrapper for an array to loop over. All of the
+
*! arrays will be looped over in parallel.
+
*! @type mixed
+
*! All other arguments will be held constant during
+
*! the automap, and sent as is to @[fun].
+
*! @endmixed
+
*!
+
*! @note
+
*! This function is used by the compiler to implement the
+
*! automap syntax, and should in normal circumstances never
+
*! be used directly.
+
*!
+
*! It may however show up during module dumping and in
+
*! backtraces.
+
*!
+
*! @note
+
*! It is an error not to have any @[Builtin.automap_marker]s
+
*! in @[args].
+
*!
+
*! @seealso
+
*! @[Builtin.automap_marker], @[map()]
+
*/
PIKEFUN array __automap__(mixed fun, mixed ... tmpargs) efun; { int e,depth=-1; check_stack(args); for(e=0;e<args-1;e++) { if(TYPEOF(tmpargs[e]) == T_OBJECT && tmpargs[e].u.object->prog == automap_marker_program)
pike.git/src/builtin.cmod:3439:
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); }
-
/* Linked list stuff.
-
*/
-
#undef INIT_BLOCK
-
#define INIT_BLOCK(NODE) do { \
-
(NODE)->next = (NODE)->prev = NULL; \
-
(NODE)->refs = 1; \
-
SET_SVAL((NODE)->val, T_INT, NUMBER_UNDEFINED, \
-
integer, 0); \
-
} while(0)
-
-
#undef EXIT_BLOCK
-
#define EXIT_BLOCK(NODE) do { \
-
if ((NODE)->prev) { \
-
free_list_node((NODE)->prev); \
-
} \
-
if ((NODE)->next) { \
-
free_list_node((NODE)->next); \
-
} \
-
free_svalue(&(NODE)->val); \
-
} while(0)
-
-
BLOCK_ALLOC_FILL_PAGES(pike_list_node, 4);
-
-
PMOD_EXPORT void free_list_node(struct pike_list_node *node)
-
{
-
if (!sub_ref(node)) {
-
really_free_pike_list_node(node);
-
}
-
}
-
-
PMOD_EXPORT void unlink_list_node(struct pike_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 */
-
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;
-
-
/* 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 pike_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 */
-
}
-
}
-
-
PMOD_EXPORT void prepend_list_node(struct pike_list_node *node,
-
struct pike_list_node *new_node)
-
{
-
#ifdef PIKE_DEBUG
-
if (!node) {
-
Pike_fatal("No node to prepend.\n");
-
}
-
if (!node->prev) {
-
Pike_fatal("Prepending unhooked node.\n");
-
}
-
if (!new_node) {
-
Pike_fatal("Prepending NULL node.\n");
-
}
-
if (new_node->next || new_node->prev) {
-
Pike_fatal("Prepending hooked node.\n");
-
}
-
#endif /* PIKE_DEBUG */
-
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);
-
}
-
-
PMOD_EXPORT void append_list_node(struct pike_list_node *node,
-
struct pike_list_node *new_node)
-
{
-
#ifdef PIKE_DEBUG
-
if (!node) {
-
Pike_fatal("No node to append.\n");
-
}
-
if (!node->next) {
-
Pike_fatal("Appending unhooked node.\n");
-
}
-
if (!new_node) {
-
Pike_fatal("Appending NULL node.\n");
-
}
-
if (new_node->next || new_node->prev) {
-
Pike_fatal("Appending hooked node.\n");
-
}
-
#endif /* PIKE_DEBUG */
-
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);
-
}
-
+
/*! @module Builtin */ /*! @class Setter *! *! Internal class for implementing setters.
-
+
*!
+
*! This class is used by @[_get_setter()].
+
*!
+
*! @seealso
+
*! @[_get_setter()]
*/ PIKECLASS Setter { PIKEVAR object o flags ID_PROTECTED|ID_PRIVATE|ID_LOCAL; CVAR int f;
-
+
+
/*! @decl void `()(mixed val)
+
*!
+
*! Set the variable for the setter to @[val].
+
*!
+
*! This is the function returned by @[_get_setter()].
+
*/
PIKEFUN void `()(mixed val) flags ID_PROTECTED; { if (!THIS->o) { Pike_error("Uninitialized Setter!\n"); } object_low_set_index(THIS->o, THIS->f, Pike_sp-1);
-
pop_n_elems(args);
-
push_int(0);
+
}
-
+
PIKEFUN string _sprintf(int c, mapping|void opts) flags ID_PROTECTED; { struct program *prog; if (!THIS->o) {
-
push_
constant
_text("Setter()");
+
push_
static
_text("Setter()");
} else if ((prog = THIS->o->prog)) {
-
push_
constant
_text("%O->`%s=");
+
push_
static
_text("%O->`%s=");
ref_push_object(THIS->o); ref_push_string(ID_FROM_INT(prog, THIS->f)->name); f_sprintf(3); } else {
-
push_
constant
_text("Setter(destructed object)");
+
push_
static
_text("Setter(destructed object)");
} stack_pop_n_elems_keep_top(args); } }
-
+
/*! @endclass
+
*/
+
PMOD_EXPORT struct object *get_setter(struct object *o, int f) { struct object *res = clone_object(Setter_program, 0); struct Setter_struct *setter = OBJ2_SETTER(res); add_ref(setter->o = o); setter->f = f; return res; }
-
/*! @decl function(mixed_void) _get_setter(object o, string
s
)
+
/*! @decl function(mixed_void) _get_setter(object o, string
varname
)
*!
-
*! Get a setter for the variable named @[
s
] in object @[o].
+
*! Get a setter for the variable named @[
varname
] in object @[o].
*!
-
+
*! @returns
+
*! Returns a @[Setter()->`()()] for the variable if it exists,
+
*! and @expr{UNDEFINED@} otherwise.
+
*!
*! @seealso *! @[object_variablep()] */ PIKEFUN function(mixed:void) _get_setter(object o, string s) { struct program *p; struct inherit *inh; int f; if (!(p = o->prog)) { Pike_error("Indexing a destructed object.\n");
pike.git/src/builtin.cmod:3659:
if ((f >= 0) && IDENTIFIER_IS_VARIABLE(ID_FROM_INT(p, f)->identifier_flags)) { f += inh->identifier_level; push_function(get_setter(o, f), f_Setter_cq__backtick_28_29_fun_num); } else { push_undefined(); } stack_pop_n_elems_keep_top(args); }
-
/*! @endclass
-
*/
-
+
/*! @class Null *! *! This class is used to implement the low-level aspects of @[Val.Null]. *! *! @note *! This class should typically not be used directly. Use *! @[Val.Null] instead. *! *! @note *! This class was previously available as @[Sql.Null]. Any such use
pike.git/src/builtin.cmod:3708:
flags ID_PROTECTED; { RETURN 1; } PIKEFUN string _sprintf(int fmt, mixed ... extras) flags ID_PROTECTED; { pop_n_elems(args); if (fmt == 'O') {
-
push_
constant
_text("Val.null");
+
push_
static
_text("Val.null");
} else { push_undefined(); } } PIKEFUN int __hash() flags ID_PROTECTED; {
-
pop_n_elems(args);
-
push_int(
17
)
;
+
RETURN
17;
} PIKEFUN int `==(mixed other) flags ID_PROTECTED; {
-
if (TYPEOF(*other) != T_OBJECT)
{
-
pop_stack();
-
push_int(
0
)
;
-
return;
-
}
+
if (TYPEOF(*other) != T_OBJECT)
+
RETURN
0;
/* Look for the is_val_null constant directly in the program of * other, without going through its `[]. When this is called in a * codec, other can be a completely arbitrary object which may not * have a `[] that works in that context. */ push_int (0); ref_push_program (other->u.object->prog); push_constant_text("is_val_null"); if (program_index_no_free (Pike_sp - 3, Pike_sp - 2, Pike_sp - 1) && TYPEOF(Pike_sp[-3]) == T_INT && Pike_sp[-3].u.integer) {
pike.git/src/builtin.cmod:3755:
} } /*! @decl string encode_json() *! *! Defined for use with @[Standards.JSON.encode], so that it *! formats NULL as @expr{null@}. */ PIKEFUN string encode_json(...) {
+
pop_n_elems(args);
push_constant_text ("null"); } } /*! @endclass */
-
+
PMOD_EXPORT
+
PIKEFUN int levenshtein_distance(string a, string b)
+
{
+
int i, j, n, *lev_i, *lev_p;
+
+
/* Simple cases: strings are equal or one of them is empty: */
+
if (a == b) RETURN 0;
+
if (a->len == 0) RETURN b->len;
+
if (b->len == 0) RETURN a->len;
+
+
/* Return -1 if any of the strings is wider than 8 bits: */
+
if (a->size_shift || b->size_shift) RETURN -1;
+
+
/* Allocate two rows on the stack: */
+
n = b->len+1;
+
lev_i = alloca(n*sizeof(int));
+
lev_p = alloca(n*sizeof(int));
+
if (!lev_i || !lev_p) RETURN -1;
+
+
/* Initialise the first row */
+
for (j = 0; j < n; j++) lev_i[j] = j;
+
+
for (i = 0; i < a->len; i++)
+
{
+
/* lev_p = row for i, lev_i = row for i+1: */
+
memcpy(lev_p, lev_i, n*sizeof(int));
+
lev_i[0] = i + 1;
+
for (j = 0; j < b->len; j++)
+
{
+
int cost = (a->str[i] == b->str[j]) ? 0 : 1;
+
int test, min_val = lev_i[j]+1;
+
if ((test = lev_p[j+1]+1) < min_val) min_val = test;
+
if ((test = lev_p[j]+cost) < min_val) min_val = test;
+
lev_i[j+1] = min_val;
+
}
+
}
+
RETURN lev_i[b->len];
+
}
+
/*! @endmodule */ /*! @module Serializer */ /*! @class Serializable *! *! The base class for serializable objects. *! *! Inherit this class in classes that need to be serializable. *! *! @seealso *! @[Serializer.serialize()], @[Serializer.deserialize()] */ PIKECLASS Serializable {
-
/* Loop over all variables, and call fun_num in the current object. */
+
/* Loop over all
true
variables, and call fun_num in the current object. */
static void low_serialize(int i, struct svalue *fun, int use_setter, int fun_num) { struct inherit *inh; struct program *p = Pike_fp->current_object->prog; struct svalue *save_sp = Pike_sp; inh = p->inherits + i; p = inh->prog; for (i = 0; i < p->num_identifier_references; i++) { struct reference *ref = PTR_FROM_INT(p, i); struct identifier *id; if ((ref->id_flags & ID_HIDDEN) || ((ref->id_flags & (ID_PRIVATE|ID_INHERITED)) == (ID_PRIVATE|ID_INHERITED))) { continue; } id = ID_FROM_PTR(p, ref);
-
if (!IDENTIFIER_IS_VARIABLE(id->identifier_flags)) {
+
if (!IDENTIFIER_IS_VARIABLE(id->identifier_flags)
||
+
(id->run_time_type == PIKE_T_GET_SET
)
)
{
continue; } push_svalue(fun); if (use_setter) { push_function(get_setter(Pike_fp->current_object, i + inh->identifier_level), f_Setter_cq__backtick_28_29_fun_num); } else { low_object_index_no_free(Pike_sp, Pike_fp->current_object, i + inh->identifier_level);
pike.git/src/builtin.cmod:3861:
*! @[_serialize()], @[_deserialize_variable()] */ PIKEFUN void _serialize_variable(function(mixed, string, type:void) serializer, mixed value, string symbol, type symbol_type) flags ID_PROTECTED; rawtype tFunc(tFunc(tMix tStr tType(tMix), tVoid) tMix tStr tType(tMix), tVoid); { f_call_function(args);
-
pop_stack();
-
push_int(0);
+
} /*! @decl protected void _serialize(object o, @ *! function(mixed, string, type:void) serializer) *! *! Dispatch function for serialization. *! *! @param o *! Object to serialize. Always a context of the current object. *! *! @param serializer *! Function to typically be called once for every variable *! in the inheriting class. *! *! This function calls @[_serialize_variable()] once
-
*! for every variable in the inheriting class.
+
*! for every variable in the inheriting class
, which
+
*! in turn will call @[serializer] with the arguments:
+
*! @dl
+
*! @item Argument 1
+
*! The value of the variable
.
+
*! @item Argument 2
+
*! The name of the variable.
+
*! @item Argument 3
+
*! The declared type of the variable.
+
*! @enddl
*! *! @note *! The symbols will be listed in the order they were defined *! in the class. *! *! @note *! This function is typically called via @[Serializer.serialize()].
-
+
*!
+
*! @seealso
+
*! @[Serializer.serialize()], @[_serialize_variable()],
+
*! @[_deserialize()]
*/ PIKEFUN void _serialize(object o, function(mixed, string, type:void) serializer) flags ID_PROTECTED; rawtype tFunc(tObj tFunc(tMix tStr tType(tMix), tVoid), tVoid); { if (o != Pike_fp->current_object) { SIMPLE_BAD_ARG_ERROR("_serialize", 1, "this"); } low_serialize(SUBTYPEOF(Pike_sp[-args]), serializer, 0, f_Serializable_cq__serialize_variable_fun_num);
-
pop_n_elems(args);
-
push_int(0);
+
} static void *find_program_from_object_type_cb(struct pike_type *t) { struct program *p; if ((t->type != PIKE_T_OBJECT) || !t->cdr) return NULL; p = id_to_program(CDR_TO_INT(t)); if (!p || (p->flags & PROGRAM_NEEDS_PARENT) || (low_find_lfun(p, LFUN__DESERIALIZE) == -1)) return NULL; return p;
pike.git/src/builtin.cmod:3969:
*! Selects one of the object types in case of a complex *! @[symbol_type]. The selected type is NOT deterministic *! in case there are multiple choices that satisfy the above. *! @item *! Is likely to throw errors if @tt{p()@} requires arguments. *! @endul *! *! These issues can all be solved by overloading this function. *! *! @seealso
-
*! @[_deserialize()], @[_serialize_variable()]
+
*! @[_deserialize()], @[_serialize_variable()]
, @[Builtin.Setter]
*/ PIKEFUN void _deserialize_variable(function(function(mixed:void), string, type: void) deserializer, function(mixed:void) setter, string symbol, type symbol_type) flags ID_PROTECTED; rawtype tFunc(tFunc(tFunc(tMix, tVoid) tStr tType(tMix), tVoid) tFunc(tMix, tVoid) tStr tType(tMix), tVoid); {
pike.git/src/builtin.cmod:3993:
struct object *o = clone_object(p, 0); push_object(o); /* Protection against errors and arg to deserialize. */ ref_push_object(o); apply_svalue(setter, 1); pop_stack(); push_svalue(deserializer); f_deserialize(2); return; } f_call_function(args);
-
pop_stack();
-
push_int(0);
+
} /*! @decl protected void _deserialize(object o, @ *! function(function(mixed:void), @ *! string, type: void) deserializer) *! *! Dispatch function for deserialization. *! *! @param o *! Object to serialize. Always a context of the current object. *! *! @param deserializer *! Function to typically be called once for every variable *! in the inheriting class. *! *! This function calls @[_deserialize_variable()] once
-
*! for every variable in the inheriting class.
+
*! for every variable in the inheriting class
, which
+
*! in turn will call @[deserializer] with the arguments:
+
*! @dl
+
*! @item Argument 1
+
*! The setter for the variable
.
+
*! @item Argument 2
+
*! The name of the variable.
+
*! @item Argument 3
+
*! The declared type of the variable.
+
*! @enddl
*! *! @note *! The symbols will be listed in the order they were defined *! in the class. *! *! @note *! This function is typically called via @[Serializer.deserialize()].
-
+
*!
+
*! @seealso
+
*! @[Serializer.deserialize()], @[_deserialize_variable()],
+
*! @[_serialize()], @[Builtin.Setter]
*/ PIKEFUN void _deserialize(object o, function(function(mixed:void), string, type: void) deserializer) flags ID_PROTECTED; rawtype tFunc(tObj tFunc(tFunc(tMix, tVoid) tStr tType(tMix), tVoid), tVoid); { if (o != Pike_fp->current_object) { SIMPLE_BAD_ARG_ERROR("_serialize", 1, "this"); } low_serialize(SUBTYPEOF(Pike_sp[-args]), deserializer, 1, f_Serializable_cq__deserialize_variable_fun_num);
-
pop_n_elems(args);
-
push_int(0);
+
} } /*! @endclass */ /*! @decl void serialize(object o, @ *! function(mixed, string, type:void) serializer) *! *! Call @[lfun::_serialize()] in @[o]. *!
pike.git/src/builtin.cmod:4100:
} apply_low(o, fun + inh->identifier_level, args); } /*! @endmodule */ /*! @module ADT */
+
/* Linked list stuff.
+
*/
+
static struct block_allocator pike_list_node_allocator = BA_INIT_PAGES(sizeof(struct pike_list_node), 4);
+
+
ATTRIBUTE((malloc))
+
static struct pike_list_node * alloc_pike_list_node() {
+
struct pike_list_node * node = ba_alloc(&pike_list_node_allocator);
+
node->next = node->prev = NULL;
+
node->refs = 1;
+
SET_SVAL(node->val, T_INT, NUMBER_UNDEFINED, integer, 0);
+
return node;
+
}
+
+
void count_memory_in_pike_list_nodes(size_t * n, size_t * s) {
+
ba_count_all(&pike_list_node_allocator, n, s);
+
}
+
+
void free_all_pike_list_node_blocks(void) {
+
ba_destroy(&pike_list_node_allocator);
+
}
+
+
PMOD_EXPORT void free_list_node(struct pike_list_node *node)
+
{
+
if (!sub_ref(node)) {
+
if (node->prev) {
+
free_list_node(node->prev);
+
}
+
if (node->next) {
+
free_list_node(node->next);
+
}
+
free_svalue(&node->val);
+
ba_free(&pike_list_node_allocator, node);
+
}
+
}
+
+
PMOD_EXPORT void unlink_list_node(struct pike_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 */
+
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;
+
+
/* 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 pike_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 */
+
}
+
}
+
+
PMOD_EXPORT void prepend_list_node(struct pike_list_node *node,
+
struct pike_list_node *new_node)
+
{
+
#ifdef PIKE_DEBUG
+
if (!node) {
+
Pike_fatal("No node to prepend.\n");
+
}
+
if (!node->prev) {
+
Pike_fatal("Prepending unhooked node.\n");
+
}
+
if (!new_node) {
+
Pike_fatal("Prepending NULL node.\n");
+
}
+
if (new_node->next || new_node->prev) {
+
Pike_fatal("Prepending hooked node.\n");
+
}
+
#endif /* PIKE_DEBUG */
+
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);
+
}
+
+
PMOD_EXPORT void append_list_node(struct pike_list_node *node,
+
struct pike_list_node *new_node)
+
{
+
#ifdef PIKE_DEBUG
+
if (!node) {
+
Pike_fatal("No node to append.\n");
+
}
+
if (!node->next) {
+
Pike_fatal("Appending unhooked node.\n");
+
}
+
if (!new_node) {
+
Pike_fatal("Appending NULL node.\n");
+
}
+
if (new_node->next || new_node->prev) {
+
Pike_fatal("Appending hooked node.\n");
+
}
+
#endif /* PIKE_DEBUG */
+
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);
+
}
+
/*! @class List *! *! Linked list of values. */ PIKECLASS List { CVAR struct pike_list_node *head; CVAR INT32 head_sentinel_refs; CVAR struct pike_list_node *tail; /* Always NULL. */ CVAR INT32 tail_sentinel_refs;
pike.git/src/builtin.cmod:4141:
* o Detach segment (requires new iterator implementation) * o Iterator copy * o _equal() for iterators and lists. * o _values(), _search(), cast() * o _sizeof()?, _indices()?? * o Support for reverse(), filter() and map(). * o Initialization from array. * o Support for Pike.count_memory. */
+
+
PIKEFUN int _size_object()
+
{
+
int q = THIS->num_elems;
+
int res = q * sizeof(struct pike_list_node);
+
struct mapping *m = NULL;
+
struct pike_list_node *n = THIS->head;
+
while( q-- )
+
{
+
res += rec_size_svalue( &n->val, &m );
+
n = n->next;
+
}
+
if( m ) free_mapping( m );
+
RETURN res;
+
}
+
INIT { THIS->tail = NULL; THIS->head = TAIL_SENTINEL(THIS); THIS->tail_pred = HEAD_SENTINEL(THIS); THIS->head_sentinel_refs = THIS->tail_sentinel_refs = 2; THIS->num_elems = 0; } EXIT
pike.git/src/builtin.cmod:4247:
*! *! Describe the list. *! *! @seealso *! @[sprintf()], @[lfun::_sprintf()] */ PIKEFUN string _sprintf(int c, mapping(string:mixed)|void attr) flags ID_PROTECTED; { if (!THIS->num_elems) {
-
push_
constant
_text("ADT.List(/* empty */)");
+
push_
static
_text("ADT.List(/* empty */)");
} else if (c == 'O') { struct pike_list_node *node = THIS->head; if (THIS->num_elems == 1) {
-
push_
constant
_text("ADT.List(/* 1 element */\n");
+
push_
static
_text("ADT.List(/* 1 element */\n");
} else {
-
push_
constant
_text("ADT.List(/* %d elements */\n");
+
push_
static
_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");
+
push_
static
_text(" %O,\n");
} else {
-
push_
constant
_text(" %O\n");
+
push_
static
_text(" %O\n");
} push_svalue(&node->val); f_sprintf(2); node = node->next; }
-
push_
constant
_text(")");
+
push_
static
_text(")");
f_add(THIS->num_elems + 2); } else { if (THIS->num_elems == 1) {
-
push_
constant
_text("ADT.List(/* 1 element */)");
+
push_
static
_text("ADT.List(/* 1 element */)");
} else {
-
push_
constant
_text("ADT.List(/* %d elements */)");
+
push_
static
_text("ADT.List(/* %d elements */)");
push_int(THIS->num_elems); f_sprintf(2); } } stack_pop_n_elems_keep_top(args); } /*! @decl mixed head() *! *! Get the element at the head of the list.
pike.git/src/builtin.cmod:4308:
} /*! @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()]
+
*! @[is_empty()], @[head()], @[pop
_back
()]
*/ PIKEFUN mixed tail() {
-
if (THIS->
tail
->
prev
) {
-
push_svalue(&
THIS
->
tail
->val);
+
struct pike_list_node * node = TAIL_SENTINEL(THIS);
+
if (THIS->
head
->
next
) {
+
push_svalue(&
node
->
prev
->val);
} else { Pike_error("Empty list.\n"); } }
-
+
static inline void pop_node(struct pike_list_node * node) {
+
push_svalue(&node->val);
+
if (node->refs == 2) {
+
unlink_list_node(node);
+
} else {
+
detach_list_node(node);
+
}
+
THIS->num_elems--;
+
}
+
/*! @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()]
+
*! @[is_empty()], @[head()], @[tail()]
, @[pop_back()]
*/ PIKEFUN mixed pop() { if (THIS->head->next) {
-
push
_
svalue(&THIS->head->val);
-
if (THIS->head->refs == 2) {
-
unlink_list_
node(THIS->head);
+
pop
_node(THIS->head);
} else {
-
detach
_
list_node
(
THIS->head
);
+
Pike_error("Empty
list
.\n"
);
}
-
THIS->
num
_
elems
-
-
;
+
}
+
+
/*!
@decl
mixed pop_back()
+
*!
+
*! Pop the element at the tail of the list from the list.
+
*!
+
*! @throws
+
*! Throws an error if the list is empty.
+
*!
+
*! @seealso
+
*! @[is_empty()], @[head()], @[tail()], @[pop()]
+
*/
+
PIKEFUN mixed pop_back()
+
{
+
const struct pike_list_node * node = TAIL_SENTINEL(
THIS
);
+
if (THIS
->
head->next) {
+
pop
_
node(node
-
>prev)
;
} else { Pike_error("Empty list.\n"); } }
-
+
/*! @decl array _values()
+
*!
+
*! Returns an array of elements in the list.
+
*/
+
PIKEFUN array _values()
+
flags ID_PROTECTED;
+
{
+
struct array * a;
+
push_int(THIS->num_elems);
+
f_allocate(1);
+
+
a = Pike_sp[-1].u.array;
+
if (THIS->num_elems) {
+
struct pike_list_node *node = THIS->head;
+
int i;
+
for (i = 0; i < THIS->num_elems; i++) {
+
assign_svalue_no_free(ITEM(a) + i, &node->val);
+
node = node->next;
+
}
+
}
+
}
+
+
/*! @decl array cast(string type)
+
*!
+
*! Cast the lists. @expr{array@} is the only
+
*! supported type.
+
*/
+
PIKEFUN array cast(string type)
+
flags ID_PROTECTED;
+
{
+
pop_stack(); /* type as at least one more reference. */
+
if (type == literal_array_string)
+
apply_current(f_List_cq__values_fun_num, 0);
+
else
+
push_undefined();
+
}
+
+
+
/*! @decl mixed `[](mixed key) */
+
PIKEFUN mixed `[](mixed key)
+
flags ID_PROTECTED;
+
{
+
struct pike_list_node *node;
+
INT_TYPE n;
+
if (TYPEOF(*key) != PIKE_T_INT) SIMPLE_BAD_ARG_ERROR("`[]", 1, "int");
+
+
n = key->u.integer;
+
if (n < 0) n = -(n+1);
+
+
if (n >= THIS->num_elems) Pike_error("out of bounds");
+
+
if (n >= THIS->num_elems >> 1) { /* use shorter direction */
+
n = THIS->num_elems - n - 1;
+
key->u.integer = - key->u.integer - 1;
+
}
+
+
if (key->u.integer < 0) {
+
node = TAIL_SENTINEL(THIS)->prev;
+
while (n--) node = node->prev;
+
} else {
+
node = THIS->head;
+
while (n--) node = node->next;
+
}
+
+
pop_n_elems(args);
+
push_svalue(&node->val);
+
}
+
/*! @decl void append(mixed ... values) *! *! Append @[values] to the end of the list. *! *! @seealso *! @[insert()] */ PIKEFUN void append(mixed ... values) { struct pike_list_node *node = TAIL_SENTINEL(THIS); while (args--) { struct pike_list_node *new_node = alloc_pike_list_node(); new_node->val = *(--Pike_sp); prepend_list_node(node, new_node); free_list_node(node = new_node); THIS->num_elems++; }
-
push_int(0);
+
} /*! @decl void insert(mixed ... values) *! *! Insert @[values] at the front of the list. *! *! @seealso *! @[append()] */ PIKEFUN void insert(mixed ... values) { struct pike_list_node *node = THIS->head; while (args--) { struct pike_list_node *new_node = alloc_pike_list_node(); new_node->val = *(--Pike_sp); prepend_list_node(node, new_node); free_list_node(node = new_node); THIS->num_elems++; }
-
push_int(0);
+
}
-
+
/*! @decl void flush()
+
*!
+
*! Empties the List.
+
*/
+
PIKEFUN void flush() {
+
if (THIS->num_elems) {
+
while (THIS->head->next) {
+
if (THIS->head->refs == 2)
+
unlink_list_node(THIS->head);
+
else
+
detach_list_node(THIS->head);
+
}
+
THIS->num_elems = 0;
+
}
+
}
+
/*! @decl protected 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_PROTECTED; {
-
/*
FIXME:
Reset
the
list?
*/
+
if
(THIS->num_elems)
+
apply_current(f_List_flush_fun_num, 0);
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_PROTECTED;
pike.git/src/builtin.cmod:4419:
/* NOTE: cur may never refer to an unlinked node. * cur may however refer to a detached node, or to sentinels. */ static struct List_struct *List__get_iterator_find_parent() { 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)
;
+
loc.inherit =
Pike
_
fp
->
context
;
find_external_context(&loc, 1); return (struct List_struct *)(loc.o->storage + loc.inherit->storage_offset); } INIT { add_ref(THIS->cur = List__get_iterator_find_parent()->head); THIS->ind = 0; }
pike.git/src/builtin.cmod:4559:
#endif /* PIKE_DEBUG */ /* Detached node. */ gc_recurse_svalues(&THIS->cur->val, 1); gc_recurse_list_node_tree_forward(THIS->cur->next, THIS->cur->prev); gc_recurse_list_node_tree_backward(THIS->cur->next, THIS->cur->prev); } PIKEFUN int(0..1) `!() flags ID_PROTECTED; {
-
pop_n_elems(args);
-
push_int
(!THIS->cur->next || !THIS->cur->prev);
+
RETURN
(!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(); }
pike.git/src/builtin.cmod:4639:
*! @seealso *! @[prev()] */ PIKEFUN int(0..1) next() { struct pike_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;
+
if (next->next)
+
RETURN
1;
}
-
+
RETURN 0;
}
-
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 pike_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;
+
if (prev->prev)
+
RETURN
1;
}
-
+
RETURN 0;
}
-
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) {
pike.git/src/builtin.cmod:4721:
struct pike_list_node *new_node; if (!THIS->cur->prev) { Pike_error("Attempt to insert before the start sentinel.\n"); } new_node = alloc_pike_list_node(); assign_svalue_no_free(&new_node->val, val); prepend_list_node(THIS->cur, new_node); free_list_node(THIS->cur); THIS->cur = new_node; List__get_iterator_find_parent()->num_elems++;
-
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) { struct pike_list_node *new_node; if (!THIS->cur->next) { Pike_error("Attempt to append after the end sentinel.\n"); } new_node = alloc_pike_list_node(); assign_svalue_no_free(&new_node->val, val); append_list_node(THIS->cur, new_node); free_list_node(new_node); List__get_iterator_find_parent()->num_elems++;
-
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()]. *!
pike.git/src/builtin.cmod:4776:
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--; } free_list_node(THIS->cur); THIS->cur = next;
-
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 */ /*! @endmodule */
pike.git/src/builtin.cmod:4864:
} /*! @endclass */ /*! @endmodule */ static struct object *val_module;
-
static void get_val_module()
+
static void get_val_module(
void
)
{ assert (!val_module);
-
push_
constant
_text ("Val");
+
push_
static
_text ("Val");
APPLY_MASTER ("resolv", 1); if (TYPEOF(Pike_sp[-1]) != T_OBJECT) Pike_error ("\"Val\" didn't resolve to a module object.\n"); val_module = (--Pike_sp)->u.object; } /* Always do the lookup in the Val module dynamically to allow the * values to be replaced. */ #define GET_VAL(NAME) \ PMOD_EXPORT struct object *PIKE_CONCAT (get_val_, NAME) (void) \
pike.git/src/builtin.cmod:4900:
GET_VAL (false) GET_VAL (null) /* Kludge needed for the static null objects in the oracle module. It * ought to be fixed to use dynamic lookup of them instead. */ PMOD_EXPORT struct program *get_sql_null_prog(void) { return Null_program; }
+
PIKECLASS __Backtrace_Tester__
+
{
+
INIT {
+
Pike_error("__Backtrace_Tester__\n");
+
}
+
}
+
void init_builtin(void) {
-
init
_
pike
_
list
_
node
_
blocks
();
-
INIT
+
SET
_
SVAL(gc
_
pre
_
cb, PIKE
_
T_INT, NUMBER_NUMBER, integer, 0);
+
SET_SVAL
(
gc_post_cb, PIKE_T_INT, NUMBER_NUMBER, integer, 0
);
+
SET_SVAL(gc_destruct_cb, PIKE_T_INT, NUMBER_NUMBER, integer, 0);
+
SET_SVAL(gc_done_cb, PIKE_T_INT, NUMBER_NUMBER, integer, 0);
+
INIT
;
} void exit_builtin(void) {
-
+
struct svalue zero;
if (val_module) free_object (val_module);
-
EXIT
+
EXIT
;
+
SET_SVAL(zero, PIKE_T_INT, NUMBER_NUMBER, integer, 0);
+
assign_svalue(&gc_pre_cb, &zero);
+
assign_svalue(&gc_post_cb, &zero);
+
assign_svalue(&gc_destruct_cb, &zero);
+
assign_svalue(&gc_done_cb, &zero);
#ifndef DO_PIKE_CLEANUP /* This is performed by exit_builtin_modules() at a later point * in this case, so that the pike_list_node's are valid at cleanup * time, thus avoiding "got invalid pointer" fatals at exit. */
-
free
_
all_
pike_list_node_
blocks(
);
+
ba
_
destroy(&
pike_list_node_
allocator
);
#endif #ifndef USE_SETENV if (env_allocs) free_mapping (env_allocs); #endif }