pike.git/
src/
builtin.cmod
Branch:
Tag:
Non-build tags
All tags
No tags
2014-09-11
2014-09-11 07:56:31 by Stephen R. van den Berg <srb@cuci.nl>
008f906b43b9cf8e0890d21b19b9d67d36550d5f (
2027
lines) (+
290
/-
1737
)
[
Show
|
Annotate
]
Branch:
8.0
Revert to String.Buffer simplex.
Lost functionality needs to be found in IOBuffer.
26:
#include "operators.h" #include "builtin_functions.h" #include "fsort.h"
+
#include "port.h"
#include "gc.h" #include "block_allocator.h" #include "pikecode.h" #include "opcodes.h" #include "whitespace.h"
-
#include "fdlib.h"
+
-
#include "modules/_Stdio/file_machine.h"
-
#include "modules/_Stdio/file.h"
-
+
#include <ctype.h> #include <errno.h> #include <math.h>
2956:
/*! @class Buffer *! A buffer, used for building strings. It's
-
*! conceptually similar to a string, but
speed-optimised
for
growing
-
*! strings.
+
*! conceptually similar to a string, but
you
can
only @[add]
+
*! strings
to it, and you can only @[get] the value from it once
.
*!
-
+
*! There is a reason for those seemingly rather odd limitations,
+
*! it makes it possible to do some optimizations that really speed
+
*! things up.
+
*!
*! You do not need to use this class unless you add very many *! strings together, or very large strings. *!
2977:
*/ PIKECLASS Buffer {
-
CVAR struct string_builder str;
/* Can be empty */
-
CVAR
char*
buffer; /* Current start of the buffer */
-
CVAR size_t offset; /* Characters consumed but still in the buffer */
-
CVAR size_t len; /* Number of characters available to be read */
-
CVAR unsigned shift; /* Current size_shift of the buffer */
-
CVAR unsigned readonly; /* If the buffer is marked readonly */
-
CVAR size_t
initial;
/* Initial reserved (unfilled) buffersize */
+
CVAR struct string_builder str;
+
CVAR
int
initial;
-
CVAR struct object *source;
-
/* CVAR program *error_mode; */
-
CVAR unsigned error_mode;
-
-
static INLINE unsigned min_magnitude(const unsigned c)
-
{
-
return c<256 ? 0 : c<65536 ? 1 : 2;
-
}
-
-
static int buffer_range_error(int howmuch)
-
{
-
if( THIS->error_mode )
-
Pike_error("Trying to read %d outside allowed range\n", howmuch);
-
return 0;
-
}
-
-
static struct pike_string*buffer_mkspace(ptrdiff_t diff, unsigned shift)
-
{
-
struct Buffer_struct *str = THIS;
-
struct pike_string *s = str->str.s;
-
-
if (str->readonly)
-
Pike_error("Attempt to modify readonly buffer.\n");
-
-
if (!s || s->refs!=1) {
-
shift |= str->shift;
-
shift = shift & ~(shift >> 1);
-
init_string_builder_alloc(&str->str, str->len+diff, shift);
-
generic_memcpy(MKPCHARP(str->str.s->str, shift),
-
MKPCHARP(str->buffer, str->shift), str->len);
-
if (s)
-
sub_ref(s);
-
str->offset = 0;
-
goto fixptr;
-
} else {
-
if (!(s->flags & STRING_NOT_SHARED))
-
unlink_pike_string (s);
-
string_build_mkspace(&str->str, diff, shift);
-
fixptr:
-
s = str->str.s;
-
str->buffer = s->str + (str->offset << (str->shift = s->size_shift));
-
}
-
-
if( str->source ) {
-
free_object( str->source );
-
str->source = 0;
-
}
-
-
return s;
-
}
-
-
static void buffer_lock()
-
{
-
THIS->readonly++;
-
}
-
-
static void buffer_release()
-
{
-
THIS->readonly--;
-
}
-
+
PIKEFUN int _size_object() {
-
struct
Buffer_struct
*str = THIS;
-
unsigned retval = 0;
-
-
if
(
str
->str.s
)
{
-
retval
=
str
->str.
s->refs
;
-
retval
= (str->str.malloced+retval-1)/retval
;
+
if(
THIS->str.s
)
+
RETURN THIS
->str.
malloced
;
+
RETURN
0
;
}
-
push_int( retval );
-
}
+
-
/*! @decl
void
set
_
error_mode(int m)
-
*!
-
*! Set the error mode of this buffer to @[m].
-
*!
-
*! If true operations that would normally return 0
-
*! (like trying to read too much) will instead thrown an error.
-
*!
-
*! This is useful when parsing received data, you do not have to
-
*! verify that each and every read operation suceeds.
-
*!
-
*! However, the non-error mode is more useful when checking to see
-
*! if a packet/segment/whatever has arrived.
-
*!
-
*! The thrown error object will have the constant buffer_error set
-
*! to a non-false value.
-
*!
-
*! @example
-
*! @code
-
*! void read_callback(int i, string new_data)
-
*! {
-
*! inbuffer->add( new_data );
-
*!
-
*! while( String.
Buffer
packet = inbuffer->read
_
hbuffer(2) )
-
*! {
-
*! packet->set
_
error_mode
(
Buffer.THROW_ERROR);
-
*! if( mixed e = catch( handle_packet( packet
)
) )
-
*! if( e->buffer_error )
-
*! protocol_error()
;
// illegal data in packet
-
*! else
-
*! throw(e); // the other code did something bad
-
*! }
-
*! }
-
*!
-
*!
-
*!
void
handle
_
packet( String.
Buffer
pack )
-
*! {
-
*! switch( pack->read
_
int8
(
)
)
-
*! {
-
*! ...
-
*! case HEADER_FRAME:
-
*! int num_headers = pack->read_int32(
);
-
*! for( int i = 0; i<num_headers; i++ )
-
*! headers[pack->read_hstring(2)] = pack->read_hstring(2);
-
*! ...
-
*! }
-
*! }
-
*! @endcode
-
*/
-
PIKEFUN
void
set
_
error
_
mode
(
int
m)
-
{
-
/* FIXME: buffer_error and Buffer.THROW_ERROR still need to
-
be implemented */
-
THIS->error_mode = m;
-
pop_stack(
);
-
}
+
void
f
_Buffer_
get
_
copy
(
INT32
args
);
+
void
f
_Buffer_
get
(
INT32
args
);
+
void
f
_
Buffer
_
add
(
INT32
args
);
-
/*! @decl
protected
bool range_error
(
int
howmuch
)
+
/*! @decl
void
create
(int
initial_size
)
*!
-
*! This function is called when an attempt is made to read out of bounds.
-
*! The default implementation simply returnns 0.
-
*!
-
*! Override this function to change the behaviour
-
*!
-
*! The argument @[howmuch] indicates how much data is needed:
-
*!
-
*! @dl
-
*! @item int(1..) howmuch
-
*! Need @[howmuch] bytes more
-
*! @item int(0..0) howmuch
-
*! The amount of data needed is not certain.
-
*! This most often happens when @[sscanf] or @[read_json] is used
-
*! @item int(..-1) howmuch=...
-
*! Tried to @[unread] X bytes. There is usually no way to satisfy
-
*! the requested range.
-
*!
-
*! Fill the @[unread()] buffer by passing data to it, then
-
*! @[consume()] it again.
-
*! @enddl
-
*!
-
*! @returns
-
*!
-
*! @[true] if the operation should be retried, @[false] otherwise.
-
*!
-
*! Do not return true unless you have added data to the buffer,
-
*! doing so could result in an infinite loop (since no data is
-
*! added, the range_error will be called again immediately).
-
*/
-
PIKEFUN int(0..1) range_error( int howmuch )
-
flags ID_PROTECTED;
-
{
-
/* Default: throw error if so desired, otherwise return 0. */
-
pop_n_elems(args);
-
push_int(0);
-
}
-
-
struct sysmem {
-
unsigned char *p;
-
size_t size;
-
};
-
-
static struct program *shm_program;
-
static struct sysmem *system_memory(struct object *o)
-
{
-
if( !shm_program ) {
-
push_text("System.Memory");
-
SAFE_APPLY_MASTER("resolv", 1);
-
shm_program = program_from_svalue(Pike_sp - 1);
-
Pike_sp--;
-
if (!shm_program)
-
return 0;
-
}
-
return o->prog == shm_program ? get_storage( o, shm_program ) : 0;
-
};
-
-
static size_t buffer_findsize(unsigned args, struct svalue*pargs,
-
unsigned *pshift)
-
{
-
unsigned j,shift = 0;
-
size_t sum = 0;
-
-
for( j=args ; j-- ; ++pargs)
-
switch(TYPEOF(*pargs)) {
-
case PIKE_T_STRING:
-
{
-
struct pike_string *a = pargs->u.string;
-
sum += a->len;
-
shift |= a->size_shift;
-
break;
-
}
-
case PIKE_T_INT:
-
sum++;
-
shift |= min_magnitude(pargs->u.integer);
-
break;
-
case PIKE_T_ARRAY:
-
{
-
struct array *arp = pargs->u.array;
-
DECLARE_CYCLIC();
-
-
if (BEGIN_CYCLIC(Pike_fp->current_object, arp))
-
Pike_error("Attempt to append a cyclic array to a buffer.\n");
-
-
sum += buffer_findsize(arp->size, arp->item, &shift);
-
-
END_CYCLIC();
-
break;
-
}
-
case PIKE_T_OBJECT:
-
{
-
struct sysmem *sm;
-
if(pargs->u.object->prog == Buffer_program) {
-
struct Buffer_struct *bstr = OBJ2_BUFFER(pargs->u.object);
-
sum += bstr->len;
-
shift |= bstr->shift;
-
break;
-
} else if (sm = system_memory(pargs->u.object)) {
-
sum += sm->size;
-
break;
-
}
-
}
-
default:
-
SIMPLE_BAD_ARG_ERROR("append", args-j,
-
"string|int|String.Buffer|System.Memory"
-
"|array(string|int|String.Buffer|System.Memory)");
-
}
-
*pshift |= shift;
-
return sum;
-
}
-
-
static char* buffer_memcpy(char*dest, unsigned shift,
-
unsigned args, struct svalue*pargs)
-
{
-
unsigned j;
-
-
for( j=args ; j-- ; ++pargs) {
-
PCHARP b;
-
size_t blen;
-
-
switch(TYPEOF(*pargs)) {
-
case PIKE_T_STRING:
-
{
-
struct pike_string *bs;
-
b = MKPCHARP_STR(bs = pargs->u.string);
-
blen = bs->len;
-
break;
-
}
-
case PIKE_T_INT:
-
{
-
unsigned ch = pargs->u.integer;
-
switch( shift ) {
-
case 0: *(p_wchar0*)dest = ch; break;
-
case 1: *(p_wchar1*)dest = ch; break;
-
default: *(p_wchar2*)dest = ch; break;
-
}
-
blen = 1;
-
goto inc;
-
}
-
case PIKE_T_ARRAY:
-
{
-
struct array *arp = pargs->u.array;
-
dest = buffer_memcpy(dest, shift, arp->size, arp->item);
-
continue;
-
}
-
default:
-
{
-
struct sysmem *sm;
-
if(pargs->u.object->prog == Buffer_program) {
-
struct Buffer_struct *bstr = OBJ2_BUFFER(pargs->u.object);
-
b = MKPCHARP(bstr->buffer, bstr->shift);
-
blen = bstr->len;
-
break;
-
} else if (sm = system_memory(pargs->u.object)) {
-
b = MKPCHARP(sm->p, 0);
-
blen = sm->size;
-
break;
-
}
-
}
-
}
-
generic_memcpy(MKPCHARP(dest, shift), b, blen);
-
inc: dest += blen<<shift;
-
}
-
return dest;
-
}
-
-
/*! @decl Buffer appendat(int(0..) pos,
-
*! string|int|Buffer|object|array(string|int|Buffer|object) ... data)
-
*!
-
*! Adds @[data] to the buffer, starting at position @[pos].
-
*! It overwrites existing content at that offset, never truncates the
-
*! buffer, possibly extends the buffer if the new content does not fit.
-
*!
-
*! @returns
-
*! Returns the buffer itself.
-
*!
-
*! @note
-
*! If the starting position @[pos] extends beyond the end of the
-
*! current buffer content, the gap will be filled with NUL-characters.
-
*!
-
*! @seealso
-
*! @[append()], @[add()]
-
*/
-
PIKEFUN Buffer appendat(int(0..) pos,
-
string|int|Buffer|object|array(string|int|Buffer|object) ... data )
-
{
-
struct Buffer_struct *str = THIS;
-
struct svalue*pargs;
-
struct pike_string *s;
-
ptrdiff_t sum;
-
int j;
-
unsigned shift = 0;
-
-
if (pos < 0)
-
SIMPLE_BAD_ARG_ERROR("appendat", 1, "int(0..)");
-
-
args--;
-
sum = pos + buffer_findsize(args, Pike_sp-args, &shift);
-
-
shift |= str->shift;
-
shift = shift & ~(shift >> 1);
-
j = sum - str->len;
-
if (j>0) {
-
if (str->initial > j && str->initial > str->str.malloced)
-
j = str->initial;
-
s = buffer_mkspace(j, shift);
-
}
-
else
-
s = buffer_mkspace(0, shift);
-
/* We know it will be a string that really is this wide. */
-
str->str.known_shift = shift;
-
-
if (str->len < pos) /* Clear the padding */
-
MEMSET(str->buffer + (str->len << shift), 0, (pos - str->len) << shift);
-
-
{
-
char*dest = buffer_memcpy(s->str+((pos+str->offset)<<shift), shift,
-
args, Pike_sp-args);
-
-
if (s->len < (pos = (dest - s->str)>>shift))
-
s->len = pos;
-
if (str->len < (pos -= str->offset))
-
str->len = pos;
-
}
-
-
pop_n_elems( args+1 );
-
ref_push_object(Pike_fp->current_object);
-
}
-
-
/*! @decl Buffer append(
-
*! string|int|Buffer|object|array(string|int|Buffer|object) ... data )
-
*!
-
*! Adds @[data] to the buffer.
-
*!
-
*! @returns
-
*! Returns the buffer itself.
-
*!
-
*! @seealso
-
*! @[appendat()], @[add()]
-
*/
-
PIKEFUN Buffer append(
-
string|int|Buffer|object|array(string|int|Buffer|object) ... data )
-
{
-
push_int(THIS->len);
-
stack_revroll(++args);
-
f_Buffer_appendat(args);
-
}
-
-
/*! @decl int add(
-
*! string|int|Buffer|object|array(string|int|Buffer|object) ... data )
-
*!
-
*! Adds @[data] to the buffer.
-
*!
-
*! @returns
-
*! Returns the size of the buffer.
-
*!
-
*! @seealso
-
*! @[appendat()]
-
*/
-
PIKEFUN int add(
-
string|int|Buffer|object|array(string|int|Buffer|object) ... data )
-
{
-
f_Buffer_append(args);
-
pop_stack();
-
push_int(THIS->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 Buffer_struct *str = THIS;
-
-
if(str->len) {
-
struct pike_string *s;
-
s = make_shared_binary_pcharp(MKPCHARP(str->buffer, str->shift),
-
str->len);
-
if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
-
s->flags |= STRING_CLEAR_ON_EXIT;
-
push_string(s);
-
} else
-
push_empty_string();
-
}
-
-
/*! @decl string|void read(void|int n)
-
*!
-
*! Read and remove @[n] characters of data from the beginning
-
*! of the buffer.
-
*! If there are less than @[n] characters available, nothing
-
*! will be removed and the function will return 0.
-
*!
-
*! When called without arguments, this is equivalent to @[get()].
-
*!
-
*! @seealso
-
*! @[get()], @[clear()]
-
*/
-
PIKEFUN string|void read( void|int(0..) n )
-
{
-
struct Buffer_struct *str = THIS;
-
struct pike_string *s;
-
if (args) {
-
int want = n->u.integer;
-
pop_stack();
-
while (want<0 || want>str->len)
-
if (!buffer_range_error( want )) {
-
push_undefined();
-
return;
-
}
-
if (want != str->len) {
-
push_string(make_shared_binary_pcharp(
-
MKPCHARP(str->buffer, str->shift), want));
-
str->buffer += want<<=str->shift;
-
str->offset += want;
-
str->len -= want;
-
return;
-
}
-
}
-
buffer_mkspace(0, 0);
-
if (str->offset) {
-
memmove( str->str.s->str, str->buffer, str->len<<str->shift);
-
str->str.s->len = str->len;
-
}
-
s = finish_string_builder( &str->str );
-
if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
-
s->flags |= STRING_CLEAR_ON_EXIT;
-
push_string(s);
-
str->str.s = 0;
-
str->len = 0;
-
}
-
-
/*! @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( )
-
{
-
f_Buffer_read(0);
-
}
-
-
/*! @decl void create(void|int(1..) initial_size)
-
*! @decl void create(string|Buffer|System.Memory value)
-
*!
+
*! Initializes a new buffer. *! *! If no @[initial_size] is specified, 256 is used. If you
3476:
*! the operation of @[add()] (slightly) by passing the size to this *! function. */
-
PIKEFUN void create(
string|object|
int|void
value
)
+
PIKEFUN void create( int|void
size
)
{ struct Buffer_struct *str = THIS;
-
struct pike_string *s;
-
struct sysmem *sm;
-
-
if(
str->buffer
)
-
Pike_error("Can not initialise twice.\n");
-
str->initial =
256;
-
if
(
!args)
-
goto definit;
-
switch(TYPEOF(*value)) {
-
default:
-
Pike_error("Unsupported object type\n");
-
case PIKE_T_INT:
-
str
->
initial = MAX(value->
u.integer,
1
);
-
definit:
-
init_string_builder_alloc(&str->str, str->initial, 0);
-
str->buffer = str->str.s->str;
-
break;
-
case PIKE_T_STRING:
-
s = value->u.string;
-
if (s->refs == 1)
-
/* Unlink the string and use it as buffer directly. */
-
unlink_pike_string (s);
+
if(
size
)
+
str->initial =
MAXIMUM
(
size
->u.integer,
512
);
else
-
add_ref(s);
-
str->
str.s
=
s
;
-
str->len = str->str.malloced = s->len;
-
str->shift = str->str.known_shift = s->size_shift;
-
str->buffer = s->str;
-
break;
-
case PIKE_T_OBJECT:
-
if (value->u.object->prog == Buffer_program) {
-
struct Buffer_struct *str2 = OBJ2_BUFFER(value->u.object);
-
str->buffer = str2->buffer;
-
str->offset = str2->offset;
-
str->len = str2->len;
-
str->shift = str2->shift;
-
if (str->source = str2->source)
-
add_ref(str->source);
-
str->str = str2->str;
-
if (str->str.s)
-
add_ref(str->str.s);
+
str->
initial
=
256
;
}
-
else if (sm = system_memory(value->u.object)) {
-
add_ref(str->source = value->u.object);
-
str->buffer = sm->p;
-
str->len = sm->size;
-
}
-
break;
-
}
-
}
+
/*! @decl string _sprintf( int flag, mapping flags ) *! It is possible to @[sprintf] a String.Buffer object
-
*! as @tt{%s@} just as if it
were
a string.
+
*! as @tt{%s@} just as if it
was
a string.
*/ PIKEFUN string _sprintf( int flag, mapping flags ) {
-
pop_n_elems( args );
+
switch( flag ) { case 'O': {
-
+
struct pike_string *res;
struct Buffer_struct *str = THIS;
-
push_text( "Buffer(%d
-%d
/* %d */)" );
-
push_int
(str->
len
)
;
-
push_int(str->
offset
);
+
push_text( "Buffer(%d /* %d */)" );
+
if
(
str->
str.s
)
+
{
+
push_int(str->
str.s->len
);
push_int(str->str.malloced);
-
f_sprintf( 4 );
-
break;
+
}
-
case 's':
-
if( Pike_fp->current_object->refs != 1
-
|| THIS->str.s->refs != 1)
-
f_Buffer_get_copy( 0 );
+
else
-
f_Buffer_read( 0 );
-
break;
-
-
case 't':
-
push_
string
(
make_shared_binary_string("Buffer",6));
-
break
;
-
-
default:
-
push_
undefined
();
+
{
+
push_
int
(
0
)
;
+
push_
int
(
0
);
}
-
+
f_sprintf( 3 );
+
dmalloc_touch_svalue(Pike_sp-1);
+
res = Pike_sp[-1].u.string;
+
Pike_sp--;
+
RETURN res;
}
-
/*!
@decl
string
_encode()
-
*! @decl void _decode(string x)
-
*!
-
*! Encode and decode @[String.Buffer] objects.
-
*! Only the buffer data is kept, no other state is saved.
-
*/
-
PIKEFUN string _encode()
+
case
's':
{
-
f_Buffer_
read
( 0 );
+
pop_n_elems( args );
+
if( Pike_fp->current_object->refs != 1 )
+
f_Buffer_
get_copy
( 0 );
+
else
+
f_Buffer_get( 0 );
}
-
+
return;
-
PIKEFUN
void
_decode(string
x)
-
{
-
if(
THIS->str.s->len
)
-
Pike
_
error
("
Cannot initialise twice.\n
");
-
f_Buffer_add( 1 );
+
case
't':
+
RETURN
make
_
shared_binary_string
("
Buffer
"
,6
);
}
-
+
pop_n_elems( args );
+
push_undefined();
+
}
-
/*! @decl
string|int
cast( string type )
+
/*! @decl
mixed
cast( string type )
*! It is possible to cast a String.Buffer object to *! a @expr{string@} and an @expr{int@}. */
-
PIKEFUN
string|int
cast( string type )
+
PIKEFUN
mixed
cast( string type )
flags ID_PROTECTED; { if( type == literal_string_string ) { pop_stack();
-
if( Pike_fp->current_object->refs != 1
-
|| THIS->str.s->refs != 1
)
+
if( Pike_fp->current_object->refs != 1 )
f_Buffer_get_copy( 0 ); else
-
f_Buffer_
read
( 0 );
+
f_Buffer_
get
( 0 );
return; }
3606:
{ struct Buffer_struct *str = THIS; pop_stack();
-
if( Pike_fp->current_object->refs != 1
-
|| THIS->str.s->refs != 1
)
+
if( Pike_fp->current_object->refs != 1 )
f_Buffer_get_copy( 0 ); else
-
f_Buffer_
read
( 0 );
+
f_Buffer_
get
( 0 );
o_cast_to_int( ); return; }
3619:
push_undefined(); }
-
/*! @decl
int
`[]
(
int
index
)
+
/*! @decl
String.Buffer
`+
(
string|String.Buffer what
)
*/
-
PIKEFUN
int `[](int index)
-
{
-
struct Buffer_struct *str = THIS;
-
unsigned len = str->len;
-
-
if (index<0)
-
index += len;
-
if (index<0 || index>=len)
-
index_error("Buffer->`[]", Pike_sp, args, NULL, Pike_sp,
-
"Index %"PRINTPIKEINT"d is out of array range 0..%d,\n",
-
index, len-1);
-
RETURN generic_extract(str->buffer, str->shift, index);
-
}
-
-
/*! @decl Buffer `[..](int start, int start_type, int end, int end_type)
-
*/
-
PIKEFUN Buffer `[..](int start, int start_type, int end, int end_type)
-
{
-
struct Buffer_struct *str = THIS;
-
size_t len = str->len;
-
unsigned shift = str->shift;
-
struct pike_string *s;
-
struct
object
*res;
-
struct Buffer_struct *str2;
-
-
pop_n_elems(args);
-
if (start_type == INDEX_FROM_END)
-
start = len-1-start;
-
if (end_type != INDEX_FROM_BEG)
-
end = len-1-end;
-
res = fast_clone_object( Buffer_program );
-
str2 = OBJ2_BUFFER( res );
-
if (start < 0)
-
start = 0;
-
if (end >= len)
-
end = len-1;
-
-
if (str->str.s) {
-
add_ref(str->str.s);
-
str2->str = str->str;
-
}
-
str2->offset = str->offset+start;
-
str2->len = end-start+1;
-
str2->buffer = str->buffer+(start<<shift);
-
str2->shift = str->shift;
-
str2->initial = str->initial;
-
if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
-
res->flags |= OBJECT_CLEAR_ON_EXIT;
-
push_object(res);
-
}
-
-
/*! @decl int `[]=(int index, int ch)
-
*/
-
PIKEFUN int `[]=(int index, int ch)
-
{
-
struct Buffer_struct *str = THIS;
-
struct pike_string *s;
-
size_t len = str->len;
-
-
pop_n_elems(args);
-
-
if (index<0)
-
index += len;
-
if (index<0 || index>=len)
-
index_error("Buffer->`[]=", Pike_sp, args, NULL, Pike_sp,
-
"Index %"PRINTPIKEINT"d is out of array range 0..%d,\n",
-
index, len-1);
-
len = 0;
-
if ((unsigned)ch >= 256)
-
switch(str->shift) {
-
case 0:
-
if ((unsigned)ch < 65536)
-
len = 1;
-
else {
-
case 1:
-
if((unsigned)ch >= 65536)
-
len = 2;
-
else
-
break;
-
}
-
str->str.known_shift = len;
-
}
-
s = buffer_mkspace(0, len);
-
low_set_index(s, index+str->offset, ch);
-
push_int(ch);
-
}
-
-
/*! @decl Buffer
`+
=
( string|Buffer
...
what )
-
*/
-
PIKEFUN Buffer `+=( string|Buffer ... what )
+
PIKEFUN object `+( string|Buffer what )
rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER); {
-
f_Buffer_add( args );
-
pop_stack();
-
ref_push_object(Pike_fp->current_object);
-
}
-
-
/*! @decl Buffer `+( string|Buffer ... what )
-
*/
-
PIKEFUN Buffer `+( string|Buffer ... what )
-
rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER);
-
{
+
struct Buffer_struct *str = THIS, *str2;
-
struct pike_string *s = str->str.s;
-
-
if( Pike_fp->current_object->refs != 1
-
|| s->refs != 1) {
-
struct Buffer_struct *str2;
-
unsigned shift = str->shift;
-
size_t len = str->len;
+
struct object *res = fast_clone_object( Buffer_program ); str2 = OBJ2_BUFFER( res ); str2->initial = str->initial;
-
str2
->
shift
= shift;
-
init_string_builder_
alloc
(&str2->str,
MAX(len, str2->initial), shift);
-
str2->
str
.s
->
len = str2->len = len;
-
memcpy(str2->buffer = str2->
str
.s->str, str->buffer, len<<shift
);
+
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",
args
);
-
pop_stack();
-
ref_push_object(
res
)
;
-
} else
-
f_Buffer_cq__backtick_add_eq(args);
+
apply( res, "add",
1
);
+
RETURN
res;
}
-
/*! @decl Buffer
``+
( string
.
..
what )
+
/*! @decl
String.
Buffer
`+=
( string
|String
.
Buffer
what )
*/
-
PIKEFUN
Buffer
``+
( string
...
what )
-
rawtype tFunc(tString, tObjIs_BUFFER);
+
PIKEFUN
object
`+=
( string
|Buffer
what )
+
rawtype tFunc(
tOr(
tString, tObjIs_BUFFER)
, tObjIs_BUFFER)
;
{
-
struct Buffer
_
struct *str = THIS, *str2;
-
-
if( Pike_fp->current_object->refs != 1
-
|| str->str.s->refs != 1) {
-
struct
Buffer_
struct *str2;
-
struct object *res = fast_clone_object( Buffer_program );
-
str2 = OBJ2_BUFFER( res );
-
str2->initial = str->initial;
-
if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
-
res->flags |= OBJECT_CLEAR_ON_EXIT;
-
ref_push_object(Pike_fp->current_object);
-
apply( res, "
add
", args+1 );
-
} else {
-
char*dest;
-
struct svalue*pargs = Pike_sp-args;
-
int j,shift = 0;
-
size_t sum = 0, len;
-
-
pargs = Pike_sp;
-
j = args;
-
do {
-
struct pike_string *a = --pargs->u.string;
-
sum += a->len;
-
shift |= a->size_shift;
-
} while
(
--j);
-
-
shift |= str->shift;
-
shift = shift & ~(shift >>
1
);
-
buffer_mkspace(sum - str->str.s->len + (len = str->len
)
, shift)
;
-
memmove(str->str.s->str+(sum<<shift), str->buffer, len<<shift);
-
-
str->buffer = dest = str->str.s->str;
-
str->offset = 0;
-
str->str.s->len = str->len = len + sum;
-
-
do {
-
struct pike
_
string
*bs = pargs++->u.string;
-
size_t blen = bs->len;
-
generic_memcpy(MKPCHARP(dest, shift), MKPCHARP_STR( bs ), blen);
-
dest += blen<<shift;
-
} while(--args);
-
-
ref_push_object(
Pike_fp->current_object
)
;
+
f
_Buffer_add( 1 );
+
REF
_
RETURN
Pike_fp->current_object;
}
-
}
+
-
static int buffer_cmp(struct svalue
*
other,
unsigned
stricttype)
-
{
-
struct Buffer_struct *str = THIS;
-
struct pike_string *s2;
-
PCHARP b;
-
ptrdiff_t blen;
-
-
switch
(
TYPEOF
(
*other)) {
-
case PIKE_T_STRING:
-
b = MKPCHARP_STR(s2 = other->u
.
string);
-
blen = s2->len;
-
goto runcmp;
-
case PIKE_T_OBJECT:
-
if (other->u
.
object->prog == Buffer_program
)
{
-
struct Buffer_struct *str2 = OBJ2_BUFFER(other->u.object);
-
b = MKPCHARP(str2->buffer
,
str2->shift);
-
blen = str2->len;
-
runcmp: return my_quick_pcharpcmp(
-
MKPCHARP(str->buffer, str->shift), str->len, b, blen);
-
}
-
}
-
if(stricttype)
-
Pike_error("Unsupported types in comparison
.
\n");
-
return 1;
-
}
-
-
/*! @decl int(0
..
1)
`==( mixed other
)
+
/
*
!
@decl
int
addat
(
int
(
0..
)
pos,
string
|String
.Buffer ...
data
)
*!
-
*!
Is
able
to
compare
directly with strings and other Buffers.
-
*/
-
PIKEFUN int(0..1) `==( mixed other )
-
rawtype tFunc(tMixed, tInt01);
-
{
-
RETURN !
buffer
_cmp(other
,
0);
-
}
-
-
/*! @decl int(0
.
.1) `<( mixed other )
+
*!
Adds
@[data]
to
the
buffer,
starting
at
position
@[pos]
.
*!
-
*!
Is able to compare directly with strings and other Buffers.
-
*
/
-
PIKEFUN
int(0..1)
`<(
mixed
other
)
-
rawtype tFunc(tMixed, tInt01);
-
{
-
RETURN
buffer
_cmp(other, 1)<0;
-
}
-
-
/*! @decl int(0
.
.1) `>( mixed other )
+
*!
@returns
+
*
!
Returns
the
size
of
the
buffer.
*!
-
*!
Is able to compare directly with strings and other Buffers.
-
*
/
-
PIKEFUN
int(0..1)
`>(
mixed other )
-
rawtype tFunc(tMixed, tInt01);
-
{
-
RETURN
buffer
_cmp(other,
1)>0;
-
}
-
-
static void buffer_putchar(int ch
,
unsigned
mag)
{
-
struct Buffer_struct *str = THIS;
-
void
*
dest;
-
unsigned shift;
-
pop_stack();
-
buffer_mkspace(1, mag);
-
shift = str->shift;
-
dest = str->buffer+(str->len++<<shift);
-
switch( shift ) {
-
case 0: *(p_wchar0*)dest = ch; break;
-
case 1: *(p_wchar1*)dest = ch; break;
-
default: *(p_wchar2*)dest = ch; break;
-
}
-
str->str.s->len = str->offset+str->len;
-
ref_push_object(Pike_fp->current_object);
-
}
-
-
/*
!
@decl
Buffer
putchar(
int char )
-
*! @decl Buffer add_byte( int(0..255) i )
-
*! @decl Buffer add_int8( int(
-
128
.
.127) i )
+
*!
@note
+
*
!
If
the
buffer
isn't
of
the
required
size
,
it
will
be
padded
+
*!
with
NUL
-
characters
.
*!
-
*!
Appends
a
single
character
at
the
end
of
the
string
.
+
*!
@note
+
*!
Pike
7.8
and
earlier did not support adding @[String
.
Buffer]s
+
*! directly.
*!
-
*! @returns
-
*! Returns the buffer itself.
-
*!
+
*! @seealso *! @[add()] */
-
PIKEFUN
Buffer putchar(
int
ch) {
-
buffer_putchar
(
ch, min_magnitude(ch));
-
}
-
-
PIKEFUN
int
add_byte
(
int(
0..
255
)
i ) {
-
buffer_putchar(i&255
,
0);
-
}
-
-
PIKEFUN int add_int8( int(-128
..
127) i ) {
-
buffer_putchar(i&255, 0);
-
}
-
-
/*! @decl Buffer add_int( int i, int(0
.
.)
width
)
-
*!
-
*! Appends a generic integer to the buffer as an
(
width*8)bit
-
*! network byteorder number.
-
*!
-
*! @returns
-
*! Returns the buffer itself.
-
*!
-
*! @seealso
-
*! @[add_int32()]
-
*/
-
PIKEFUN Buffer add_int( int i
,
int width ) {
-
struct Buffer_struct *str = THIS;
-
struct pike_string *s;
-
ptrdiff_t len;
-
-
s = buffer_mkspace
(
width
,
0);
-
len = str->offset+str->len;
-
if(width>=0)
-
for(str->len += width, s->len = len+=width; width--; i>>=8)
-
low
_
set_index(s, --len, i&0xff
)
;
-
else
-
for(str->len -= width
,
s->len = len-width; width++; i>>=8
)
-
low_set_index(s, len++, i&0xff)
;
-
-
pop_n_elems(args);
-
ref_push_object(Pike_fp->current_object);
-
}
-
-
static void buffer_add_bignum( struct object *i, int width )
+
PIKEFUN int
addat
(int(0..)
pos
,
string
...
arg1
)
+
rawtype
tFuncV
(
tIntPos
,
tOr
(
tString
,
tObjIs
_
BUFFER
),
tIntPos
);
{ struct Buffer_struct *str = THIS;
-
struct pike_string *snum;
+
-
push_int
(
256
)
;
-
apply(
i,
"
digits
", 1 );
+
if
(
pos < 0
)
+
SIMPLE_BAD_ARG_ERROR(
"
addat
", 1
,
"int(0..
)
")
;
-
snum
= Pike_sp[-
1
].u.string;
-
-
/*
FIXME:
little
endian
*/
-
-
if(
snum->len
>
width
)
-
Pike_
error("Number
too
large
to
store
in
%d
bits\n
",
width*8
);
-
-
push_int
(str->len
+
width-snum->len);
-
stack
_
swap
();
-
f_Buffer_appendat(
2
);
+
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;
+
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(
"
addat"
,
j+1, "string|String.Buffer"
);
+
else {
+
a = OBJ2
_
BUFFER
(
Pike_sp[j-args].u.object
)
->str.s
;
+
if (!a
)
continue
;
}
-
-
PIKEFUN
Buffer
add_int(
object
i,
int
width
)
{
-
pop_stack()
;
/* width */
-
convert_stack_top_to_bignum();
-
buffer_add_bignum
(
Pike_sp[
-
1].u.object, width);
-
stack
_
pop_keep_top()
;
/* i */
+
sum
+=
a->len
;
+
shift
|=
a
-
>size
_
shift
;
}
-
static
void
buffer_do_rewind_on_error(
void*arg
)
-
{
-
struct
Buffer_struct
*str
=
THIS;
-
str->
str.s->len
= str->
offset+(str->len
=
arg-(void*)str->buffer)
;
-
}
+
if (!str->str.s
) {
+
if
((sum
+
pos)
<=
str->
initial)
+
sum
= str->
initial;
+
else
+
sum <<
=
1, sum += pos;
+
shift = shift & ~
(
shift
>
> 1
);
-
static
void
buffer
_
begin
_
rewind
_
on_error
(
ONERROR
*x
)
-
{
-
struct
Buffer_struct
*str
=
THIS
;
-
SET
_
ONERROR
( (
*x
)
,
buffer
_
do
_
rewind_on_error
, str->
buffer+str
->len );
+
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;
-
static
void
buffer_end_rewind_on_error(
ONERROR
*x
)
-
{
-
UNSET_ONERROR
( (
*x
) );
+
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)
;
}
-
/*!
@decl
Buffer
add_ints(
array
(
int)
integers, int(0..255) len )
-
*!
-
*! Add the integers in the specified array, @[len] bytes per int.
-
*! Equivalent to add_int( integers[*], len ) but faster.
-
*/
-
PIKEFUN Buffer add_ints( array(int) a, int bpi )
-
{
-
struct Buffer_struct *str
=
THIS
;
-
size_t i,l = a->size;
-
struct svalue *it = a->item;
-
-
if( bpi
<
0 )
-
Pike_error("Invalid int width\n")
;
/* FIXME: little endian? */
-
-
if (bpi <= SIZEOF_INT_TYPE
) {
-
struct pike_string *
s = buffer_mkspace(bpi*l, 0)
;
-
size_t
len
= str->offset+str->len - bpi;
-
for (i=l; i--; it++) {
-
INT_TYPE si;
-
unsigned j;
-
-
if
(
TYPEOF(
*it
) == PIKE_T_
INT
)
-
si
=
it
-
>
u.
integer
;
-
else
if( is_bignum_object_in_svalue( it ) )
{
-
INT64
i64;
-
if( int64
_
from_bignum
(
&i64, it
-
>
u.object
)
)
-
si = i64
;
-
else
-
Pike_error
(
"Too big bignum\n"
)
;
/* FIXME: support these anyway? */
-
} else
-
Pike_error("Non integer in array\n")
;
-
-
for(len+=(j=bpi)<<1; j--; si>>=8)
-
low_set_index(s, --len, si&0xff);
+
for
(
j
=
1
;
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
;
}
-
str->len
=
(s->len = len + bpi) - str->offset;
-
} else { /* bignums. */
-
ONERROR e;
-
buffer
_
begin
_
rewind
_
on
_
error
(
&e );
-
for (i=l; i
-
-;)
{
-
push_svalue( it++
)
;
-
push_int( bpi
);
-
f_Buffer_add_int(2);
-
pop_stack()
;
+
pike
_
string
_
cpy(MKPCHARP
_
STR
_
OFF
(
str
-
>str.s,
pos
)
,
a
);
+
pos
+=
a->len
;
}
-
buffer_end_rewind_on_error( &e );
-
}
-
pop_n_elems(args);
-
ref_push_object(Pike_fp->current_object);
-
}
+
-
/*!
@decl
Buffer
add_int16(
int
(-
32768
.
.32767)
i
)
-
*!
@decl
Buffer
add_short(
int(0..65535)
i )
-
*!
-
*! Appends an int16 at the end of the string.
-
*!
-
*! @returns
-
*! Returns the buffer itself.
-
*!
-
*! @seealso
-
*! @[add()],@[add_byte()],@[add_int8()]
-
*/
-
PIKEFUN Buffer add_int16( int(
-
32768
.
.32767)
i
) {
-
push_int(2)
;
-
f_Buffer_add_int(2);
+
if
(
str
-
>str
.
s->len
<
pos
)
+
str->str
.
s
-
>len
=
pos
;
}
-
PIKEFUN
Buffer
add_short(
int(0
.
.65535)
i
)
{
-
f_Buffer_add_int16(1)
;
+
RETURN
str->str
.
s
?
str->str.s->len
:
0
;
}
-
/*! @decl
Buffer
add
_int32
(
int(-2147483648
.
.2147483647) i )
-
*! @decl
Buffer
add_long( int(0
..
4294967295)
i
)
+
/*! @decl
int
add(
string|String
.Buffer ..
.
data
)
*!
-
*!
Appends
an
int32
at
the
end of
the
string
.
+
*!
Adds
@[data]
to
the
buffer
.
*! *! @returns
-
*! Returns the
buffer
itself
.
+
*! Returns the
size of the
buffer.
*!
-
*!
@seealso
-
*!
@[add()],@[add_byte()],@[add_int8()]
-
*/
-
PIKEFUN Buffer add_int32( int(-2147483648
.
.2147483647)
i
)
{
-
push_int(4);
-
f_Buffer_add_int(2);
-
}
-
-
PIKEFUN Buffer add_long( int(0
.
.4294967295) i ) {
-
f_
Buffer
_add_int32(1);
-
}
-
-
/
*!
@decl
Buffer
add_hstring( string|Buffer|System
.
Memory data, int size_size )
+
*!
@note
+
*!
Pike
7
.
8
and
earlier
did
not
support
adding
@[String
.Buffer
]s
+
*!
directly
.
*!
-
*! Adds length/data for @[data] to the buffer.
-
*!
-
*! This is identical to @[sprintf("%"+size_size+"H",data)] but
-
*! significantly faster.
-
*!
-
*! @[size_size] must be less than Int.NATIVE_MAX.
-
*!
-
*! @returns
-
*! Returns the buffer itself.
-
*!
+
*! @seealso
-
*! @[
add
()]
,@[sprintf()]
+
*! @[
addat
()]
*/
-
PIKEFUN
Buffer
add
_hstring
( string|Buffer
|object
str,
int
size_size
)
{
-
size_t
len;
-
-
switch
(
TYPEOF(*str))
{
-
case PIKE
_
T_STRING:
-
len = str->u.string->len
;
-
break;
-
case PIKE_T_OBJECT:
+
PIKEFUN
int
add( string|Buffer
...
arg1
)
+
rawtype
tFuncV(tNone,
tOr
(
tString,
tObjIs
_
BUFFER),
tIntPos)
;
{
-
struct
sysmem
*
sm
;
-
if (
str->
u
.
object
->
prog
==
Buffer_program
)
{
-
len = OBJ2
_
BUFFER
(
str->u.object
)
->len
;
-
break
;
+
struct
Buffer_struct
*str
=
THIS
;
+
push_int(str->str.s
?
str->
str
.
s
->
len
:
0
)
;
+
stack
_
revroll
(
++args
);
+
f_Buffer_addat(args)
;
}
-
if (sm = system_memory(str->u.object)) {
-
len = sm->size;
-
break;
-
}
-
}
-
default:
-
Pike_error
(
"Bad
argument 1 to add_hstring(
)
,"
-
" expected string|Buffer|System.Memory.\n");
-
}
-
/
*
We
know
this
is
safe for size_size>=4, since pike strings are
at
-
* most 0x7ffffff bytes long
.
+
+
/*!
@decl
void
putchar
(
int
c
)
+
*
!
Appends
the
character
@[c]
at
the
end
of
the
string
.
*/
-
if
(
size_size < 4 &&
-
len > (1<<(8*(size_size<0?-size_size:size_size
)
))-1
)
-
Pike
_
error("String
too
long\n");
-
pop_stack()
;
-
push_int
(
len
)
;
-
push_int(size_size);
-
f
_
Buffer
_
add
_
int
(
2
);
-
pop
_
stack();
-
f
_
Buffer_append
(
1
);
+
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
buffer
itself
.
+
*! Returns the
resulting
size of the String
.
Buffer.
*/
-
PIKEFUN
Buffer
sprintf(mixed ... arguments)
+
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;
-
struct pike_string *s;
-
size_t oldlen;
-
ONERROR e;
-
buffer_mkspace
(
0, 0);
-
oldlen =
str->str.s
->len;
-
buffer
_
begin
_
rewind
_
on_error
(
&
e
);
+
if
(
!
str->str.s
)
+
init
_
string
_
builder
_
alloc
(&
str->str,
str->initial, 0
);
low_f_sprintf(args, 0, &str->str);
-
buffer_end_rewind_on_error(
&e );
-
pop_n_elems(args);
-
s =
str->str.s
;
-
str
->
buffer = s->str + (str->offset<<(str->shift = s->size_shift));
-
str->
len
+= s->len-oldlen
;
-
ref_push_object(Pike_fp->current_object);
+
RETURN
str->str.s->len;
}
-
/*! @decl
Buffer|void
cut
(
int start, int|void end, void|int(0..1
)
discard)
+
/*! @decl
string
get_copy
()
*!
-
*!
Cut
and delete
the
range of
data from the
current
buffer.
-
*!
Returns
a
new
buffer
with
the
cut data, unless discard is true
.
+
*!
Get
the data from the buffer.
Significantly slower than @[get],
+
*!
but
does
not
clear
the
buffer
.
*! *! @seealso
-
*! @[get()]
, @[get_copy()], @[clear()]
+
*! @[get()]
*/
-
PIKEFUN
Buffer|void
cut(int index, int|void end
_
or_none,
-
void|int
(
0..1
)
discard)
+
PIKEFUN
string
get
_
copy
()
{
-
struct
Buffer
_
struct
*str = THIS
, *str2;
-
struct object *res;
-
unsigned shift;
-
size_t len;
-
INT_TYPE end, vdiscard;
-
-
len = str
->
len; shift =
str
->str
.s
->size_shift
;
-
end = args==1 ? len-1 : end
_
or_none->u.integer;
-
vdiscard = args==3 ? discard->u.integer : 0;
-
pop_n_elems(args);
-
-
if (index < 0)
-
index = 0;
-
if (++end >
len
)
-
end = len
;
-
-
str->len -= len = end-index;
-
if
(
index)
-
index +=
str
->offset,
end+=str->offset;
-
if (!vdiscard) {
-
res = fast_clone_object( Buffer_program );
-
str2 = OBJ2_BUFFER( res );
-
str2->shift = str->shift;
-
str2->len = len;
-
if (index) {
-
init_string_builder_alloc(
&
str2->str, str2->initial = len, 0);
-
string_builder_append(
&
str2->str,
-
MKPCHARP_OFF
(
str->buffer, str->shift, index),
len
);
-
str2->buffer
=
str2->
str
.s
->
str;
-
} else {
-
str2->offset = str->offset;
-
str2->initial = str->initial;
-
str2->buffer = str->buffer;
-
if (str->str.s
)
{
-
add_ref(str-
>
str.s);
-
str2->str = str->str;
-
}
-
}
-
}
-
if(index) {
-
struct pike_string *s = buffer_mkspace(
0
,
0
)
;
-
memmove(s->str+(index<<shift), s->str+(end<<shift), (s->len-end)<<shift);
-
s->len -= len;
-
} else
-
str->offset += end, str->buffer += end<<shift;
-
-
if(!vdiscard) {
-
if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
-
res->flags |= OBJECT_CLEAR_ON_EXIT;
-
push_object(res);
-
}
-
else
-
push_undefined();
-
}
-
-
/*! @decl Buffer read_buffer(int n)
-
*!
-
*! Same as @[read()], but returns the result as a @[Buffer].
-
*!
-
*! @seealso
-
*! @[read()], @[get()], @[trim()]
-
*/
-
PIKEFUN Buffer|void read_buffer(int n)
+
struct
pike
_
string
*str = THIS->str.s;
+
ptrdiff
_
t
len;
+
if( str && (len = str->
len
) > 0 )
{
-
struct
Buffer_struct
*str
= THIS,
*
str2;
-
struct pike_string *s
=
str->str.s;
-
size_t len = str->len;
-
struct object *res;
-
-
while
(
n<0
|| n>len
)
-
if (!buffer_range_error( n )) {
-
pop_n_elems(args);
-
push_undefined();
-
return;
-
}
-
pop_n_elems(args);
-
res = fast_clone_object( Buffer_program );
-
str2 = OBJ2_BUFFER( res );
-
if (s)
-
add_ref(s);
-
str2->
str
= str
->str;
-
str2->buffer
=
str->buffer;
-
str2->offset =
str->
offset;
-
str2->shift = str->shift;
-
str2->len = n;
-
if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
-
res->flags |= OBJECT_CLEAR_ON_EXIT;
-
push_object(res);
-
}
-
-
#define LONGEST_HIGH_BYTE (~(MAX_ULONGEST>>8))
-
-
static LONGEST buffer_read_number(
size_
t
n
)
+
char
*
d
=
(char
*)str->str;
+
switch(
str->size_
shift
)
{
-
struct
Buffer_struct
*str
=
THIS;
-
unsigned
shift
=
str
->shift
;
-
unsigned
char*buf
=
str->buffer
;
-
LONGEST
res
=
0;
-
-
if
(n>
=
0
)
-
while
(n--)
{
-
if
(LONGEST_HIGH_BYTE&res)
-
goto
overflow
;
-
res<<=8;
res|=*buf;
buf+=1<<shift
;
+
case 0:
+
str
=
make_shared_binary_string0((p_wchar0
*)d,len)
;
+
break
;
+
case 1:
+
str=make_shared_binary_string1
(
(p_wchar1 *
)
d,len);
+
break;
+
default:
+
str=make_shared_binary_string2((p_wchar2
*)d,len)
;
+
break
;
}
-
else
-
for
(
buf+=n<<shift;
n++;
)
{
-
if (LONGEST
_
HIGH
_
BYTE&res
)
-
goto
overflow
;
-
res<<=8
;
buf-=1<<shift; res|=*buf;
+
if
(
Pike_fp->current_object->flags
&
OBJECT
_
CLEAR
_
ON_EXIT
)
+
str->flags
|= STRING_CLEAR_ON_EXIT
;
+
RETURN
str
;
}
-
if (res<0)
-
overflow:
-
Pike_error("Integer (%dbit) overflow.\n", SIZEOF_LONGEST*8);
-
return res;
-
}
-
-
static int buffer_rc_number(size_t n)
-
{
-
struct Buffer_struct *str = THIS;
-
-
pop_stack();
-
for(;;) {
-
LONGEST slen;
-
ptrdiff_t len = str->len;
-
-
if ((len -= n)<0)
-
goto rangeerror;
-
slen = buffer_read_number(n);
-
if ((len -= slen)<0) {
-
rangeerror:
-
if( buffer_range_error( -len ) )
-
continue;
-
push_
undefined();
-
return 0;
-
}
-
str->len -= n;
-
str->offset += n;
-
str->buffer += n<<str->shift;
-
push
_
int(slen);
-
return 1;
-
}
-
}
-
-
static int buffer_presscanf(const struct pike_
string
*format)
-
{
-
struct Buffer_struct *str = THIS;
-
ptrdiff_t num_used;
-
-
goto jin;
-
do {
-
if
(
!buffer_range_error(0
)
)
-
return 0
;
-
jin: low_sscanf_pcharp( MKPCHARP(str->buffer, str->shift), str->len,
-
MKPCHARP_STR(format), format->len, &num_used, 0);
-
} while (!num_used);
-
-
str->len -= num_used;
-
str->offset += num_used;
-
str->buffer += num_used<<str->shift;
-
return 1;
-
}
-
-
/*! @decl int read_int( int n )
-
*!
-
*! Read a network (if n is positive) or little endian (if n is
-
*! negative) byte order unsigned number of size n*8 bits, then
-
*! return it.
-
*!
-
*! @returns
-
*! Will return -1 if there is not enough buffer space available
-
*! unless error mode is set to throw errors.
-
*/
-
PIKEFUN int(0..)|int(-1..-1) read_int( int n )
-
{
-
struct Buffer_struct *str = THIS;
-
-
while (str->len < n)
-
if (!buffer_range_error( (int)str->len-n )) {
-
Pike_sp[-1].u.integer = -1;
+
push_
empty
_string();
return; }
-
if( n < SIZEOF_INT_TYPE ) {
/*
will for sure fit. */
-
pop_stack();
-
push_int(buffer_read_number(n));
-
str->len -= n;
-
str->offset += n;
-
} else {
-
f_Buffer_read( 1 );
-
push_int(256);
-
push_object(clone_object( get_auto_bignum_program(), 2 ));
-
reduce_stack_top_bignum();
-
}
-
}
-
-
/*
! @decl string
read_hstring
(
int(0..
)
n )
+
/*! @decl string
get
()
*!
-
*!
Identical
in
functionality
to
@[read](@[read_int](@[n]))
but
-
*! faster
.
+
*!
Get
the
data
from
the
buffer
.
*!
-
*!
Read a network byte order number of size n*8 bits, then return the
-
*!
indicated
number
of
bytes
as
a
string.
-
*! @returns
-
*! If there is not enough
data
available
return
0.
+
*!
@note
+
*!
This
will
clear
the
data
in
the
buffer
*!
-
*! Note that a pike string can not be longer than 0x7fffffff bytes (~2Gb).
-
*/
-
PIKEFUN string|void read_hstring( int n )
-
{
-
if(buffer_rc_number(n))
-
f_Buffer_read( 1 );
-
}
-
-
/*! @decl Buffer read_hbuffer( int n )
-
*!
-
*! Same as @[read_hstring], but returns the result as a Buffer.
-
*!
-
*/
-
PIKEFUN Buffer|void read_hbuffer( int n )
-
{
-
if(buffer_rc_number(n))
-
f_Buffer_read_buffer( 1 );
-
}
-
-
/*! @decl array|void sscanf(string format)
-
*!
-
*! Reads data from the beginning of the buffer to match the
-
*! specifed format, then return an array with the matches.
-
*!
-
*! The non-matching data will be left in the buffer.
-
*!
-
*! See @[array_sscanf] for more information.
-
*/
-
PIKEFUN array|void sscanf( string format )
-
{
-
struct svalue *start = Pike_sp;
-
unsigned matched, results;
-
-
matched = buffer_presscanf(format);
-
results = Pike_sp - start;
-
-
if(matched)
-
f_aggregate(results);
-
else {
-
pop_n_elems(results);
-
push_undefined();
-
}
-
}
-
-
/*! @decl mixed|void match(string format)
-
*!
-
*! Reads data from the beginning of the buffer to match the
-
*! specifed format, then return the match.
-
*!
-
*! The non-matching data will be left in the buffer.
-
*!
-
*! This function is very similar to @[sscanf], but the
-
*! result is the sum of the matches. Most useful to match
-
*! a single value.
-
*!
-
*! @example
-
*! @code
-
*! // get the next whitespace separated word from the buffer.
-
*! buffer->match("%*[ \t\r\n]%*[^ \t\r\n]");
-
*! @endcode
-
*/
-
PIKEFUN string|int|float|array|void match( string format )
-
{
-
struct svalue *start = Pike_sp;
-
unsigned matched, results;
-
-
matched = buffer_presscanf(format);
-
results = Pike_sp - start;
-
-
if(matched)
-
f_add(results);
-
else {
-
pop_n_elems(results);
-
push_undefined();
-
}
-
}
-
-
/*! @decl int(0..)|int(-1..-1) consume(int(0..) n)
-
*!
-
*! Discard the next @[n] bytes from the beginning of the buffer.
-
*!
-
*! @returns
-
*! Returns the remaining size of the buffer, or -1 if you attempted
-
*! to consume more than was available.
-
*!
+
*! @seealso
-
*! @[
cut
()], @[
unread
()]
+
*! @[
get_copy
()], @[
clear
()]
*/
-
PIKEFUN
int(-1..)
consume
(
int(0..
)
n )
+
PIKEFUN
string
get
( )
{ struct Buffer_struct *str = THIS;
-
int left;
-
-
while (
n
<0 ||
(
left = str->len-n
)
< 0)
-
if
(
!buffer_range_error(
-left )) {
-
left = -1;
-
goto ret;
-
}
-
-
str->
len -= n;
-
str
->offset += n;
-
str->buffer += n<<str->shift;
-
ret:
-
Pike_sp[-1]
.
u.integer
= left;
-
}
-
-
/*! @decl int(0..
)
|int(-1..-1) unread(int(0..)|string|Buffer n)
-
*!
-
*! Rewind the buffer @[n] bytes. If passed a string of Buffer
-
*! argument, it will push back that string at the front of the buffer.
-
*!
-
*! @returns
-
*! Returns how many characters of buffer is available to rewind,
-
*! or -1 if you attempted to rewind more than is available.
-
*!
-
*! @seealso
-
*! @[consume()]
-
*/
-
PIKEFUN int(-1..) unread( int(0..)|string|Buffer n )
+
pop_
n
_elems
(
args
)
;
+
if( str->str.
s
)
{
-
struct
Buffer_struct
*str = THIS;
-
int ret = Pike_sp[-1].u.integer;
-
struct pike_string *
s2;
-
PCHARP b
;
-
ptrdiff_t
blen;
-
-
switch (TYPEOF(Pike_sp[
-
1]))
{
-
case PIKE_T_INT:
-
ret
=
Pike_sp[-1].u.integer
;
-
while
(
ret<0
|| ret
>
str
->
offset)
-
if (!buffer
_
range
_
error(
ret-str->offset
)
) {
-
ret
=
-1
;
-
goto rangx1
;
+
struct pike_string *
s
=
finish_string_builder(
&str->str
)
;
+
str
-
>str.malloced
=
0;
+
str->str.s
=
NULL
;
+
if
(
Pike_fp-
>
current_object
->
flags
&
OBJECT
_
CLEAR
_
ON_EXIT
)
+
s->flags
|
=
STRING_CLEAR_ON_EXIT
;
+
push_string(s)
;
}
-
str->buffer -= ret<<str->shift;
-
str->len += ret;
-
ret = str->offset -= ret;
-
rangx1: break;
-
case PIKE_T_STRING:
-
s2 = Pike_sp[-1].u.string;
-
b = MKPCHARP_STR(s2);
-
blen = s2->len;
-
goto pastein;
-
case PIKE_T_OBJECT:
-
if (Pike_sp[-1].u.object->prog == Buffer_program)
-
{ struct Buffer_struct *str2 = OBJ2_BUFFER(Pike_sp[-1].u.object);
-
ptrdiff_t dif;
-
-
b = MKPCHARP(str2->buffer, str2->shift);
-
blen = str2->len;
-
pastein: if ((dif = (ptrdiff_t)str->offset-blen) < 0) {
-
struct pike_string *s = buffer_mkspace(-dif, b.shift);
-
unsigned shift = s->size_shift;
-
memmove(s->str+(blen<<shift), str->buffer, str->len<<shift);
-
str->buffer = s->str;
-
s->len = str->len += blen;
-
ret = str->offset = 0;
-
}
-
else {
-
buffer_mkspace(0, 0);
-
str->buffer -= blen<<str->shift;
-
str->len += blen;
-
ret = str->offset = dif;
-
}
-
generic_memcpy(MKPCHARP(str->buffer, str->shift), b, blen);
-
break;
-
}
-
default:
-
SIMPLE_BAD_ARG_ERROR("unread", 1, "int(0..)|string|String.Buffer");
-
}
-
pop_stack();
-
push_int(ret);
-
}
-
-
static int buffer_is_whitespace(struct Buffer_struct *str, size_t pos)
-
{
-
switch( generic_extract(str->buffer, str->shift, pos) ) {
-
SPACECASE8
-
return 1;
-
}
-
return 0;
-
}
-
-
/*! @decl mixed read_json(int|void require_whitespace_separator)
-
*!
-
*! Read a single JSON expression from the buffer and return it.
-
*!
-
*! If @[require_whitespace_separator] is true there must be a whitespace
-
*! after each json value (as an example, newline or space).
-
*!
-
*! The JSON is assumed to be utf-8 encoded.
-
*!
-
*! @returns
-
*! UNDEFINED if no data is available to read.
-
*! The read value otherwise.
-
*!
-
*! @note
-
*! Unless strwhitespaces are required this function only really work correctly
-
*! with objects, arrays and strings.
-
*!
-
*! There is really no way to see where one value starts and the other ends
-
*! for most other cases
-
*/
-
PIKEFUN mixed read_json(int|void require_whitespace)
-
{
-
struct Buffer_struct *str = THIS;
-
unsigned whites = 0;
-
static ptrdiff_t(*parse_json_pcharp)(PCHARP,size_t,int,char**);
-
if( require_whitespace ) {
-
whites = require_whitespace->u.integer;
-
pop_stack();
-
}
-
-
if( !parse_json_pcharp )
-
parse_json_pcharp
-
= PIKE_MODULE_IMPORT(Standards.JSON, parse_json_pcharp );
-
for(;;) {
-
char *err = NULL;
-
ptrdiff_t stop = parse_json_pcharp( MKPCHARP(str->buffer,str->shift),
-
str->len, 1|8, &err ); /* json_utf8 */
-
if( stop < 0 )
-
if( -stop == str->len || (err && !strncmp(err,"Unterminated",12))) {
-
if (buffer_range_error( 0 ))
-
continue;
-
push_undefined();
-
} else /* FIXME: Use real json error? */
-
if( err )
-
Pike_error("Syntax error in json at offset %d: %s\n", -stop, err );
+
else
-
Pike_error("Syntax error in json at offset %d\n", -stop );
-
else if( whites &&
-
(stop == str->len || !buffer_is_whitespace(str,stop))
-
&& !buffer_is_whitespace(str,stop-1))
-
if( stop == str->len ) {
-
if (buffer_range_error( 0 ))
-
continue;
-
pop_stack();
-
push_
undefined();
-
} else
-
Pike
_
error
(
"Missing whitespace between json values at offset %d\n",
-
stop
);
-
else {
-
if( whites )
-
while( buffer_is_whitespace( str, stop ) )
-
stop++;
-
push_int( stop );
-
f_Buffer_consume( 1 );
-
pop_stack();
+
push_
empty
_
string
();
}
-
break;
-
}
-
}
+
-
/*! @decl void
trim
()
+
/*! @decl void
clear
()
*!
-
*!
Compact
buffer,
free unused memory.
-
*! When called on a subbuffer, it clears the subbuffer
and
releases
-
*!
the
parent
buffer to be modified again
.
+
*!
Empty
the
buffer, and
don't
care
about
the
old
content
.
*! *! @note
-
*!
Calling
this
function
excessively
might
slow
things
down,
-
*! since the data often has to be copied
.
+
*!
This
function
was
not
available
in
Pike
7.8
and
earlier
.
*! *! @seealso
-
*! @[
clear
()]
]
+
*! @[
get
()]
*/
-
PIKEFUN void
trim
()
+
PIKEFUN void
clear
()
{
-
+
/* FIXME: Support resetting the initial size? */
struct Buffer_struct *str = THIS;
-
struct
pike_string *s =
str->str.s
;
-
unsigned
offset
=
str->offset,
shift =
s
->size_shift;
-
-
if
(str->readonly || !s || s->refs!=1)
-
buffer_mkspace(0,
0);
-
else
{
-
if
(
str->offset
)
-
memmove(s->str, str->buffer, str->len<<str->shift);
-
if (s = realloc
_
unlinked_
string(
s,
str->
len
)
)
-
str->str.s =
s, str->str.malloced = str->len
;
-
str->buffer = s->str;
-
str->offset = 0;
+
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 void clear()
-
*!
-
*! Empty the buffer, and discard the old content.
-
*!
-
*! @seealso
-
*! @[get()], @[cut()], @[trim()]
-
*/
-
PIKEFUN void clear()
-
{
-
struct Buffer_struct *str = THIS;
-
str->len = str->offset = 0;
-
reset_string_builder(&str->str);
-
}
-
+
/*! @decl int _sizeof() *! *! Returns the size of the buffer. */ PIKEFUN int _sizeof() {
-
RETURN
THIS->len;
+
struct Buffer_struct *str = THIS;
+
RETURN
str
->
str.s ? str->str.s->
len
: 0
;
} INIT { struct Buffer_struct *str = THIS;
-
memset
( str, 0, sizeof( *str ) );
+
MEMSET
( str, 0, sizeof( *str ) );
} EXIT gc_trivial; { struct Buffer_struct *str = THIS;
-
struct pike_string *s;
-
if
(
s
=
str->str.s) {
+
if( str->str.s
)
+
{
if( Pike_fp->flags & OBJECT_CLEAR_ON_EXIT )
-
guaranteed_memset( s->str, 0, str->str.
malloced
);
-
free_string(
s
);
+
guaranteed_memset(
str->str.
s->str, 0, str->str.
s->len
);
+
free_string
_builder
(
&str->str
);
}
-
if( str->source )
-
free_object( str->source );
+
} GC_RECURSE {
-
if (mc_count_bytes (Pike_fp->current_object))
+
if (mc_count_bytes (Pike_fp->current_object)
&& THIS->str.s
)
mc_counted_bytes += THIS->str.malloced; }
-
-
#define BUFFER_BLOCK_SIZE 4096
-
-
/*! @decl int input_from( Stdio.Stream f, void|int n )
-
*!
-
*! Read data from @[f] into this buffer.
-
*/
-
PIKEFUN int(0..) input_from( object f, void|int _n )
-
{
-
struct Buffer_struct *str = THIS;
-
size_t bread = 0, n = (size_t)-1;
-
struct my_file *fd;
-
-
if( _n )
-
n = _n->u.integer;
-
-
buffer_mkspace(0, 0);
-
if( (fd = get_storage( f, file_program )) ) {
-
struct pike_string *s;
-
void *tbuf = 0;
-
-
if (str->shift) {
-
tbuf = malloc(MIN(n,BUFFER_BLOCK_SIZE));
-
if (!tbuf)
-
Pike_error("Out of memory allocating %d buffer space.\n",
-
BUFFER_BLOCK_SIZE);
+
}
-
do {
-
int res;
-
void *ptr;
-
size_t getnow = n-bread;
-
if (getnow < BUFFER_BLOCK_SIZE)
-
getnow = BUFFER_BLOCK_SIZE;
-
s = buffer_mkspace(getnow, 0);
-
ptr = tbuf? tbuf: s->str+s->len; /* shift is 0 when no tbuf */
+
-
buffer_lock();
-
{
-
THREADS_ALLOW();
-
res = fd_read( fd->box.fd, ptr, getnow );
-
THREADS_DISALLOW();
-
}
-
buffer_release();
-
-
if( res == -1 && errno == EINTR )
-
continue;
-
-
if( res <= 0 )
-
break;
-
-
if (tbuf)
-
generic_memcpy(MKPCHARP_STR_OFF(s, s->len), MKPCHARP(tbuf,0), res);
-
s->len += res;
-
bread += res;
-
if( res != getnow )
-
break;
-
} while (n>bread);
-
-
if (tbuf)
-
free(tbuf);
-
str->buffer = s->str + (str->offset<<str->shift);
-
str->len += bread;
-
} else /* some other object. Just call read */
-
for(;;) {
-
push_int( BUFFER_BLOCK_SIZE );
-
safe_apply( f, "read", 1 );
-
if( TYPEOF(Pike_sp[-1]) != PIKE_T_STRING
-
|| Pike_sp[-1].u.string->len == 0 )
-
break;
-
bread += Pike_sp[-1].u.string->len;
-
f_Buffer_add( 1 );
-
pop_stack();
-
}
-
RETURN bread;
-
}
-
-
/*! @decl int output_to( Stdio.Stream f, void|int n )
-
*!
-
*! Write data from the buffer to the indicated file.
-
*!
-
*! Will return the number of bytes that were successfully written.
-
*/
-
PIKEFUN int(0..) output_to( object f, void|int _n )
-
{
-
struct Buffer_struct *str = THIS;
-
size_t sz = str->len;
-
size_t written = 0;
-
struct my_file *fd;
-
INT_TYPE *wr = &Pike_sp->u.integer;
-
-
if( _n )
-
sz = MIN(_n->u.integer, sz);
-
-
if( !str->shift && (fd = get_storage( f, file_program )) )
-
{
-
buffer_lock();
-
-
THREADS_ALLOW();
-
while( sz > written ) {
-
int res = fd_write( fd->box.fd, str->buffer, sz-written );
-
if( res == -1 && errno == EINTR )
-
continue;
-
if( res <= 0 )
-
break;
-
str->buffer += res;
-
written += res;
-
}
-
THREADS_DISALLOW();
-
str->len -= written;
-
str->offset += written;
-
buffer_release();
-
} else {
-
/* some other object. Just call write */
-
while( sz > written ) {
-
size_t rd = MIN(sz-written,BUFFER_BLOCK_SIZE);
-
push_int(rd);
-
f_Buffer_read( 1 );
-
if( TYPEOF(Pike_sp[-1]) != PIKE_T_STRING
-
|| Pike_sp[-1].u.string->len == 0 )
-
break;
-
/* when this throws we need to rewind the buffer correctly. */
-
safe_apply(f, "write", 1);
-
if( *wr <= 0 )
-
goto rewind;
-
written += *wr;
-
if( 0 < (rd-=*wr) ) {
-
rewind: str->buffer -= rd<<str->shift;
-
str->offset -= rd;
-
str->len += rd;
-
break;
-
}
-
pop_stack();
-
}
-
}
-
RETURN written;
-
}
-
}
-
+
/*! @endclass */
4991:
INIT {
-
memset
(&THIS->ctx, 0, sizeof(struct replace_many_context));
+
MEMSET
(&THIS->ctx, 0, sizeof(struct replace_many_context));
} EXIT
7498:
#ifndef USE_SETENV if (env_allocs) free_mapping (env_allocs); #endif
-
if( shm_program ) free_program( shm_program );
+
}