From 008f906b43b9cf8e0890d21b19b9d67d36550d5f Mon Sep 17 00:00:00 2001 From: "Stephen R. van den Berg" <srb@cuci.nl> Date: Wed, 10 Sep 2014 12:52:05 +0200 Subject: [PATCH] Revert to String.Buffer simplex. Lost functionality needs to be found in IOBuffer. --- CHANGES | 15 - lib/modules/String.pmod/testsuite.in | 648 +-------- src/builtin.cmod | 2018 ++++---------------------- 3 files changed, 304 insertions(+), 2377 deletions(-) diff --git a/CHANGES b/CHANGES index 745e98a369..da064a7597 100644 --- a/CHANGES +++ b/CHANGES @@ -603,25 +603,10 @@ o Image.BMP now supports some more BMP:s. o String.Buffer - String.Buffer can now add the storage from a different String.Buffer - object with the add() method. - It is possible to add sprintf-formatted data to a String.Buffer object by calling the sprintf() method. This function works just as the normal sprintf(), but writes to the buffer instead. - The new method addat() allows for writing into the buffer at any - position. - - It is possible to address (and update) single characters in a Buffer - using []. - It is possible to extract a new Buffer (copy) from an existing Buffer - using the [..] range operator. - It is possible to compare two Buffers or a Buffer and a string - directly using the ==, <, >, >= and <= operators. - A new method cut() allows you to cut and copy, or simply delete and - contract data from an existing Buffer. - o String.range(str) This returns the minimum and maximum character value in the string. diff --git a/lib/modules/String.pmod/testsuite.in b/lib/modules/String.pmod/testsuite.in index 5082b60cfa..4c0abb7d93 100644 --- a/lib/modules/String.pmod/testsuite.in +++ b/lib/modules/String.pmod/testsuite.in @@ -3,25 +3,25 @@ START_MARKER test_eq([[ String.Buffer()->add("xxx") ]], 3) test_any([[ String.Buffer s=String.Buffer(); - s->append("xxx"); - s->append("a", "b"); + s->add("xxx"); + s->add("a", "b"); s->putchar(65); - s->append("B"); + s->add("B"); s->putchar(67); return (string)s; ]], "xxxabABC") test_any([[ String.Buffer s=String.Buffer(2); - s->append("a"); - s->append("b"); - s->append("c"); + s->add("a"); + s->add("b"); + s->add("c"); return (string)s; ]], "abc") test_any([[ String.Buffer s=String.Buffer(2); - s->append("abcdefg"); - s->append(""); - s->append("hij"); + s->add("abcdefg"); + s->add(""); + s->add("hij"); return s->get(); ]], "abcdefghij") test_any([[ @@ -30,23 +30,23 @@ test_any([[ ]], "") test_any([[ String.Buffer s=String.Buffer(); - s->append("xxx"); + s->add("xxx"); string t=s->get(); - s->append("yyy"); + s->add("yyy"); string u=(string)s; - s->append("zzz"); + s->add("zzz"); return t+u+(string)s; ]], "xxxyyyyyyzzz") test_any([[ String.Buffer s=String.Buffer(); - s->append("xxx"); + s->add("xxx"); string t=s->get_copy(); - s->append("yyy"); + s->add("yyy"); return t+(string)s; ]], "xxxxxxyyy") test_any([[ String.Buffer s=String.Buffer(); - s->append("abcde"); + s->add("abcde"); return sizeof(s); ]], 5) test_any([[ @@ -61,34 +61,14 @@ test_any([[ ]], 1) test_any([[ String.Buffer b = String.Buffer(); - b->append("abc"); - b->append("\400\500"); + b->add("abc"); + b->add("\400\500"); b->putchar(256); - b->append("x"); + b->add("x"); return (string)b; ]], "abc\400\500\400x") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("x"); - b->sprintf("%x",1178); - b->append("y"); - return (string)b; -]], "x49ay") -test_any([[ - String.Buffer b = String.Buffer(); - b->sprintf("%x", 10); - b->sprintf("%x", 11); - return (string)b; -]], "ab") -test_any([[ - String.Buffer b = String.Buffer(); - b->sprintf(""); - return (string)b; -]], "") -test_compile_error([[ String.Buffer()->sprintf("%d"); ]]) -test_eval_error([[ function f=String.Buffer()->sprintf; f("%d"); ]]) -test_eval_error([[ String.Buffer()->append("x",([])); ]]) +test_eval_error([[ String.Buffer()->add("x",([])); ]]) test_do([[ String.Buffer b = String.Buffer(); @@ -109,596 +89,6 @@ test_do([[ return (string) b; ]]) -test_any([[ - String.Buffer b = String.Buffer(); - b->append("abc"); - return b[1]; -]], 98) -test_any([[ - String.Buffer b = String.Buffer(); - b->append("abc"); - return b[-1]; -]], 99) -test_runtime_error([[ - String.Buffer b = String.Buffer(); - b->append("abc"); - return b[3]; -]]) -test_any([[ - String.Buffer b = String.Buffer(); - b->append("a\u0100a"); - return b[1]; -]], 256) -test_any([[ - String.Buffer b = String.Buffer(); - b->append("a\U00010000a"); - return b[1]; -]], 65536) - -test_any([[ - String.Buffer b = String.Buffer(); - b->append("aaa"); - return b[1]='b'; -]], 98) -test_any([[ - String.Buffer b = String.Buffer(); - b->append("aaa"); - b[1]='b'; - return (string)b; -]], "aba") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("aaa"); - b[1]=256; - return (string)b; -]], "a\u0100a") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("aaa"); - b[1]=65536; - return (string)b; -]], "a\U00010000a") - -test_any([[ - String.Buffer b = String.Buffer(); - b->append("1234"); - b = b[..1]; - b->append("x"); - return (string)b; -]], "12x") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("1234"); - return (string)b[1..]; -]], "234") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("1234"); - return (string)b[1..2]; -]], "23") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("1234"); - return (string)b[..<1]; -]], "123") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("ab\u0100\u0101\U00010000\U00010001"); - return (string)b[0..1]; -]], "ab") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("ab\u0100\u0101\U00010000\U00010001"); - return (string)b[2..3]; -]], "\u0100\u0101") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("ab\u0100\u0101\U00010000\U00010001"); - return (string)b[4..5]; -]], "\U00010000\U00010001") - -test_any([[ - String.Buffer b = String.Buffer(); - b->append("ab\u0100\u0101\U00010000\U00010001cd"); - b->cut(4,5,1); - return (string)b; -]], "ab\u0100\u0101cd") -test_any([[ - String.Buffer b = String.Buffer(); - b->append("ab\u0100\u0101\U00010000\U00010001cd"); - b->cut(2,5,1); - return (string)b; -]], "abcd") -test_any([[ - String.Buffer b = String.Buffer(),c; - b->append("ab\u0100\u0101\U00010000\U00010001cd"); - c=b->cut(4,5); - return (string)(c + "_" + b); -]], "\U00010000\U00010001_ab\u0100\u0101cd") -test_any([[ - String.Buffer b = String.Buffer(),c; - b->append("ab\u0100\u0101\U00010000\U00010001cd"); - c=b->cut(2,5); - return (string)c + "_" + (string)b; -]], "\u0100\u0101\U00010000\U00010001_abcd") - -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcd");a->append("abcd"); - return b=="abcd" && b==a; -]], 1) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcd");a->append("abcd"); - return b>="abcd" && b>=a; -]], 1) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcd");a->append("abcd"); - return b<="abcd" && b<=a; -]], 1) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abce");a->append("abcd"); - return b>="abcd" && b>=a; -]], 1) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abce");a->append("abcd"); - return b<="abcd" || b<=a; -]], 0) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abce");a->append("abcd"); - return b>"abcd" && b>a; -]], 1) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abce");a->append("abcd"); - return b<"abcd" || b<a; -]], 0) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcb");a->append("abcd"); - return b=="abcd" || b==a; -]], 0) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcb");a->append("abcd"); - return b>="abcd" || b>=a; -]], 0) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcb");a->append("abcd"); - return b<="abcd" && b<=a; -]], 1) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcb");a->append("abcd"); - return b>"abcd" || b>a; -]], 0) -test_any([[ - String.Buffer b = String.Buffer(), a = String.Buffer(); - b->append("abcb");a->append("abcd"); - return b<"abcd" || b<a; -]], 1) - -test_any([[ - String.Buffer a=String.Buffer(),b=String.Buffer(); - a->append("Testing"); - a->appendat(0, "Is that sentence"); - a->putchar(','); - a->append(" the concluding three ", "words"); - a->append(" `were left out'"); - a->appendat(3,"th","is"); - a[1]=a[10]; - b->append("Test me harder"); - b->append(a); - if(b->cut(5,6) != "me") - return 0; - b->cut(0,11,1); - if(a[3..6] != "this" || a->get_copy() !=b) - return "X"+(string)b; - if(a->get()!="In this sentence, the concluding three words `were left out'") - return "X"+a->get(); - return 1; -]],1) - -test_equal([[String.Buffer("hej")->read(1)]], "h") - -dnl sscanf - -test_equal([[String.Buffer("hej")->sscanf("%3c")]], ({6841706})) -test_equal([[String.Buffer("hej")->match("%3c")]], 6841706) - -test_any([[ - int i; - String.Buffer b=String.Buffer("hej hej hej"); - - while( array a = b->sscanf( "%*[ ]%[^ ]") ) - i+= sizeof(a); - return i; -]], 3) - -dnl sizeof() -test_equal( sizeof(String.Buffer("hej")), sizeof("hej")) -test_equal( sizeof(String.Buffer("ej")->append("alpha")), 7) -test_equal( sizeof(String.Buffer()->sprintf("%4H","hej")), 7) - -dnl create(int) -test_any([[ - String.Buffer b = String.Buffer(1024*1024); - if( b->_size_object() != 1024*1024 ) - return -1; - for( int i = 0; i<1024/4*1024; i++ ) - b->append("test"); - if( b->_size_object() != 1024*1024 ) - return -2; - - return 1; -]], 1 ) - -dnl create/add( system.memory ) -test_any([[ - System.Memory a = System.Memory(__FILE__); - System.Memory b = System.Memory(100); - - String.Buffer buf = String.Buffer(a); - - if( buf->_size_object() != 0 ) - return -1; - - buf->append( b ); - - if( buf->_size_object() < sizeof(a)+sizeof(b) ) - return -2; - if( !has_suffix((string)buf,"\0"*100) ) - return -3; - return 1; -]], 1) - -dnl create/add( iobuffer ) -test_any([[ - String.Buffer a = String.Buffer("buffer 1"); - String.Buffer b = String.Buffer("buffer 2"); - - String.Buffer buf = String.Buffer(a); - - buf->append( b ); - - if( buf->_size_object() < sizeof(a)+sizeof(b) ) - return -2; - if( (string)buf != "buffer 1buffer 2") - return -3; - return 1; -]], 1) - -dnl add( char ) -test_any([[ - string a = ("buffer 1"); - - String.Buffer buf = String.Buffer(a); - - if( buf->_size_object() >= sizeof(a) ) - return -1; - - buf->append( 'b','u','f','f','e','r',' ','2' ); - - if( buf->_size_object() < sizeof(a)+8 ) - return -2; - if( (string)buf != "buffer 1buffer 2") - return -3; - return 1; -]], 1) - -dnl add(array( mix )) -test_any([[ - System.Memory tst = System.Memory(2); - tst[0] = ' '; - tst[1] = '2'; - String.Buffer er = String.Buffer(); - mixed z = ({ - 'b','u','f',String.Buffer("f"), - er, - tst - }); - er->append("er"); - - String.Buffer buf = String.Buffer("buffer 1"); - - buf->append( z, 'b','u','f','f',({'e','r',({' '}),'3'}) ); - - if( buf->_size_object() < 24 ) - return -2; - if( (string)buf != "buffer 1buffer 2buffer 3") - return -3; - - return 1; -]], 1) - -dnl multi add( combo, also sort of a speed test ) -dnl will use on average 3Mb RAM -test_any([[ - String.Buffer two = String.Buffer(); - array tst = ({ - System.Memory(1), - two, - '3', - "4", - String.Buffer("5"), - ({ "6", ({'7',"","",""}) }), - }); - - two->append("2"); - - int l = 0; - String.Buffer res = String.Buffer(); - for( int i = 0; i<102*1024; i++ ) - { - array args = allocate( random(50) ); - for( int j=0; j<sizeof( args ); j++ ) - { - mixed e = random( tst ); - if( intp( e ) ) - l +=1; - else - l += sizeof( e ); - args[j] = e; - } - res->append(@args); - if( sizeof( res ) != l ) - return ([i:args, "l":l,"sizeof":sizeof(res)]); - } - - return 1; -]], 1) - -dnl basic zero-copy check - -test_any( [[ - String.Buffer i = String.Buffer("zero copy"); - - i->append(" data goes here"); - if( i->_size_object() == 0 ) return -4; - - i->read(); - if( i->_size_object() != 0 ) return -5; - - i->append("zero copy"); - if( i->_size_object() == 0 ) return -6; - - i->append(" indeed"); /* note: shorter than first string. */ - if( i->_size_object() == 0 ) return -7; - - /* - crete - no move - add - realloc + move - read - no move (buffer now empty) - add - point to str - add - move to malloc arena - add - no move - */ - return 1; -]], 1) - -dnl add_byte() add_int8() - -test_any( [[ - String.Buffer b = String.Buffer(); - - for( int i=0; i<255; i++ ) - b->add_byte( i )->add_int8(~i); - - for( int i=0; i<255; i++ ) - if( b[i*2] != i || b[i*2+1] != (~i&255)) - return -i; - return 1; -]], 1 ) - -dnl add_short() add_int16() - - test_any( [[ - string ref = ""; - String.Buffer b = String.Buffer(); - - for( int i=0; i<255; i++ ) - { - ref += sprintf("%2c%2c", i, (~i)&0xffff ); - b->add_short( i )->add_int16(~i); - if( (string)b != ref ) return ([ - "i":i, - "ref":ref, - "b":(string)b - ]); - } - return 1; - ]], 1 ) - -dnl add_int32() - test_any( [[ - string ref = ""; - String.Buffer b = String.Buffer(); - - for( int i=0; i<255; i++ ) - { - ref += sprintf("%4c%4c", i, (~i)&0xffffffff ); - b->add_int32( i )->add_int32(~i); - if( (string)b != ref ) return ([ - "i":i, - "ref":ref, - "b":(string)b - ]); - } - return 1; - ]], 1 ) - - -dnl add_int( x, bits); - - test_any( [[ - string ref = ""; - String.Buffer b = String.Buffer(); - - for( int j=1; j<255; j++ ) - { - string fmt = "%"+j+"c"; - for( int i=0; i<255; i+=random(10) ) - { - ref += sprintf(fmt, i ); - b->add_int( i, j ); - } - if( (string)b != ref ) return ([ - "width":j, - "ref":ref, - "b":(string)b - ]); - b->clear(); - ref = ""; - } - return 1; - ]], 1 ) - - -dnl add_int( bignum, bits); - -test_any( [[ - string ref = ""; - String.Buffer b = String.Buffer(); - - for( int j=1; j<255; j++ ) - { - int i = random((1<<(j*8))-1); - ref += sprintf("%"+j+"c", i ); - b->add_int( i, j ); - if( (string)b != ref ) return ([ - "i":i, "j":j, - "ref":ref, - "b":(string)b - ]); - } - return 1; - ]], 1 ) - - -dnl add_hstring( str, bytes ) - -test_error( [[ - String.Buffer b = String.Buffer(); - b->add_hstring("testing", 0 ); - ]], 1 ) - -test_error( [[ - String.Buffer b = String.Buffer(); - b->add_hstring(" "*256, 1 ); -]], 1 ) - -test_any( [[ - array(String.Buffer) b = allocate(10,String.Buffer)(); - for( int i = 0; i<1000; i++ ) - { - string chunk = " "*random(255); - - for( int w = 0; w<9; w++ ) - b[w]->add_hstring( chunk, w+1 ); - } - - for( int i = 0; i<1000; i++ ) - { - string chunk = b[0]->read_hstring(1); - for( int w = 1; w<9; w++ ) - { - if( b[w]->read_hstring( w+1 ) != chunk ) - return ({w+1,strlen(chunk)}); - } - } - return 1; -]], 1); - -test_any( [[ - String.Buffer b = String.Buffer(); - b->append( "\0\1" ); - if( b->read_hstring( 2 ) ) - return -1; - if( sizeof(b) != 2 ) - return -2; - b->append("x"); - if( b->read_hstring( 2 ) != "x" ) - return -3; - return 1; -]], 1); - -dnl add_hstring( obj*, bits ) - -test_any( [[ - array(String.Buffer) b = allocate(10,String.Buffer)(); - System.Memory chunk = System.Memory(255); - for( int i = 0; i<1000; i++ ) - { - for( int w = 0; w<9; w++ ) - b[w]->add_hstring( chunk, w+1 ); - } - - for( int i = 0; i<1000; i++ ) - { - string chunk = b[0]->read_hstring(1); - for( int w = 1; w<9; w++ ) - { - if( b[w]->read_hstring( w+1 ) != chunk ) - return ({w+1,strlen(chunk)}); - } - } - return 1; -]], 1); - -dnl add_ints - -test_any( [[ - array q = allocate(100000,random)(8438439834983948938439849834983498349834983498); - String.Buffer i = String.Buffer(); - i->add_ints( q, 20 ); - return 1; -]], 1); - -test_error( [[ - array q = ({8438439834983948938439849834983498349834983498}); - String.Buffer i = String.Buffer(); - i->add_ints( q, 7 ); - return 1; -]], 1); - - -test_any( [[ - array q = ({10,20,30}); - String.Buffer i = String.Buffer(); - i->add_ints( q, 7 ); - return 1; -]], 1); -test_error( [[ - array q = ({10,"20",30}); - String.Buffer i = String.Buffer(); - i->add_ints( q, 7 ); - return 1; -]], 1); - -test_any( [[ - array q = ({10,"20",30}); - String.Buffer i = String.Buffer(); - catch{i->add_ints( q, 7 );}; - if( sizeof(i) ) - return -1; - return 1; -]], 1); - -dnl lock - -dnl cast - -dnl indexing - -dnl set_error_mode - -dnl _sprintf - test_eq([[ String.count( "", "a" ) ]], 0) test_eq([[ String.count( "aaa", "a" ) ]], 3) test_eq([[ String.count( "aaa", "aa" ) ]], 1) diff --git a/src/builtin.cmod b/src/builtin.cmod index 84cf567d77..49a259969b 100644 --- a/src/builtin.cmod +++ b/src/builtin.cmod @@ -26,15 +26,12 @@ #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> @@ -2956,8 +2953,12 @@ PIKEFUN array(mixed) backtrace() /*! @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,409 +2978,282 @@ PIKEFUN array(mixed) backtrace() */ 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 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; - } + CVAR struct string_builder str; + CVAR int initial; - static int buffer_range_error(int howmuch) + PIKEFUN int _size_object() { - if( THIS->error_mode ) - Pike_error("Trying to read %d outside allowed range\n", howmuch); - return 0; + if( THIS->str.s ) + RETURN THIS->str.malloced; + RETURN 0; } - static struct pike_string*buffer_mkspace(ptrdiff_t diff, unsigned shift) + 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; - 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; + if( size ) + str->initial = MAXIMUM( size->u.integer, 512 ); + else + str->initial = 256; } - static void buffer_lock() + /*! @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 ) { - THIS->readonly++; - } + switch( flag ) + { + case 'O': + { + struct pike_string *res; + struct Buffer_struct *str = THIS; + push_text( "Buffer(%d /* %d */)" ); + if( str->str.s ) + { + push_int(str->str.s->len); + push_int(str->str.malloced); + } + else + { + push_int( 0 ); + push_int( 0 ); + } + f_sprintf( 3 ); + dmalloc_touch_svalue(Pike_sp-1); + res = Pike_sp[-1].u.string; + Pike_sp--; + RETURN res; + } - static void buffer_release() - { - THIS->readonly--; + case 's': + { + pop_n_elems( args ); + if( Pike_fp->current_object->refs != 1 ) + f_Buffer_get_copy( 0 ); + else + f_Buffer_get( 0 ); + } + return; + + case 't': + RETURN make_shared_binary_string("Buffer",6); + } + pop_n_elems( args ); + push_undefined(); } - PIKEFUN int _size_object() + /*! @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 Buffer_struct *str = THIS; - unsigned retval = 0; + if( type == literal_string_string ) + { + pop_stack(); + if( Pike_fp->current_object->refs != 1 ) + f_Buffer_get_copy( 0 ); + else + f_Buffer_get( 0 ); + return; + } - if (str->str.s) { - retval = str->str.s->refs; - retval = (str->str.malloced+retval-1)/retval; + 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; } - 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(); + push_undefined(); } - - /*! @decl protected bool range_error( int howmuch ) - *! - *! 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). + + /*! @decl String.Buffer `+( string|String.Buffer what ) */ - 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) + PIKEFUN object `+( string|Buffer what ) + rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER); { - 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; + 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; } - static char* buffer_memcpy(char*dest, unsigned shift, - unsigned args, struct svalue*pargs) + /*! @decl String.Buffer `+=( string|String.Buffer what ) + */ + PIKEFUN object `+=( string|Buffer what ) + rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER); { - 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; + f_Buffer_add( 1 ); + REF_RETURN Pike_fp->current_object; } - /*! @decl Buffer appendat(int(0..) pos, - *! string|int|Buffer|object|array(string|int|Buffer|object) ... data) + /*! @decl int addat(int(0..) pos, string|String.Buffer ... 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. + *! Returns the size of the buffer. + *! + *! @note + *! If the buffer isn't of the required size, it will be padded + *! with NUL-characters. *! *! @note - *! If the starting position @[pos] extends beyond the end of the - *! current buffer content, the gap will be filled with NUL-characters. + *! Pike 7.8 and earlier did not support adding @[String.Buffer]s + *! directly. *! *! @seealso - *! @[append()], @[add()] + *! @[add()] */ - PIKEFUN Buffer appendat(int(0..) pos, - string|int|Buffer|object|array(string|int|Buffer|object) ... data ) + PIKEFUN int addat(int(0..) pos, string ... arg1 ) + rawtype tFuncV(tIntPos, tOr(tString, tObjIs_BUFFER), tIntPos); { 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..)"); + 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; + 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; + } + sum += a->len; + shift |= a->size_shift; + } - args--; - sum = pos + buffer_findsize(args, Pike_sp-args, &shift); + if (!str->str.s) { + if ((sum + pos) <= str->initial) + sum = str->initial; + else + sum <<= 1, sum += pos; + shift = shift & ~(shift >> 1); - 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; + 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; - if (str->len < pos) /* Clear the padding */ - MEMSET(str->buffer + (str->len << shift), 0, (pos - str->len) << 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); + } - { - char*dest = buffer_memcpy(s->str+((pos+str->offset)<<shift), shift, - args, Pike_sp-args); + 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; + } + pike_string_cpy(MKPCHARP_STR_OFF(str->str.s, pos), a); + pos += a->len; + } - if (s->len < (pos = (dest - s->str)>>shift)) - s->len = pos; - if (str->len < (pos -= str->offset)) - str->len = pos; + if (str->str.s->len < pos) + str->str.s->len = pos; } - pop_n_elems( args+1 ); - ref_push_object(Pike_fp->current_object); + RETURN str->str.s ? str->str.s->len : 0; } - /*! @decl Buffer append( - *! string|int|Buffer|object|array(string|int|Buffer|object) ... data ) + /*! @decl int add(string|String.Buffer ... data) *! *! Adds @[data] to the buffer. *! *! @returns - *! Returns the buffer itself. + *! Returns the size of the buffer. + *! + *! @note + *! Pike 7.8 and earlier did not support adding @[String.Buffer]s + *! directly. *! *! @seealso - *! @[appendat()], @[add()] + *! @[addat()] */ - PIKEFUN Buffer append( - string|int|Buffer|object|array(string|int|Buffer|object) ... data ) + PIKEFUN int add( string|Buffer ... arg1 ) + rawtype tFuncV(tNone, tOr(tString, tObjIs_BUFFER), tIntPos); { - push_int(THIS->len); + struct Buffer_struct *str = THIS; + push_int(str->str.s ? str->str.s->len : 0); stack_revroll(++args); - f_Buffer_appendat(args); + f_Buffer_addat(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()] + /*! @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 add( - string|int|Buffer|object|array(string|int|Buffer|object) ... data ) + PIKEFUN int sprintf(mixed ... arguments) + rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)), + tAttr("sprintf_args", tMix), tStr); + { - f_Buffer_append(args); - pop_stack(); - push_int(THIS->len); + // 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, 0, &str->str); + RETURN str->str.s->len; } /*! @decl string get_copy() @@ -3392,63 +3266,29 @@ inc: dest += blen<<shift; */ 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; + struct pike_string *str = THIS->str.s; + ptrdiff_t len; + if( str && (len = str->len) > 0 ) + { + char *d = (char *)str->str; + switch( str->size_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; } + if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT ) + str->flags |= STRING_CLEAR_ON_EXIT; + RETURN str; } - 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; + push_empty_string(); + return; } /*! @decl string get() @@ -3463,1364 +3303,77 @@ inc: dest += blen<<shift; */ PIKEFUN string get( ) { - f_Buffer_read(0); + 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; + if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT ) + s->flags |= STRING_CLEAR_ON_EXIT; + push_string(s); + } + else + push_empty_string(); } - /*! @decl void create(void|int(1..) initial_size) - *! @decl void create(string|Buffer|System.Memory value) + /*! @decl void clear() *! - *! Initializes a new buffer. + *! Empty the buffer, and don't care about the old content. *! - *! 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. + *! @note + *! This function was not available in Pike 7.8 and earlier. + *! + *! @seealso + *! @[get()] */ - PIKEFUN void create( string|object|int|void value ) + PIKEFUN void clear() { + /* FIXME: Support resetting the initial 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); - 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); - } - else if (sm = system_memory(value->u.object)) { - add_ref(str->source = value->u.object); - str->buffer = sm->p; - str->len = sm->size; - } - break; + 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 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. + /*! @decl int _sizeof() + *! + *! Returns the size of the buffer. */ - PIKEFUN string _sprintf( int flag, mapping flags ) - { - pop_n_elems( args ); - switch( flag ) - { - case 'O': - { - struct Buffer_struct *str = THIS; - push_text( "Buffer(%d-%d /* %d */)" ); - push_int(str->len); - push_int(str->offset); - 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(); - } - } - - /*! @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() - { - f_Buffer_read( 0 ); - } - - PIKEFUN void _decode(string x) - { - if( THIS->str.s->len ) - Pike_error("Cannot initialise twice.\n"); - f_Buffer_add( 1 ); - } - - /*! @decl string|int 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 ) - flags ID_PROTECTED; - { - if( type == literal_string_string ) - { - pop_stack(); - if( Pike_fp->current_object->refs != 1 - || THIS->str.s->refs != 1) - f_Buffer_get_copy( 0 ); - else - f_Buffer_read( 0 ); - return; - } - - if( type == literal_int_string ) - { - struct Buffer_struct *str = THIS; - pop_stack(); - if( Pike_fp->current_object->refs != 1 - || THIS->str.s->refs != 1) - f_Buffer_get_copy( 0 ); - else - f_Buffer_read( 0 ); - o_cast_to_int( ); - return; - } - - pop_stack(); - push_undefined(); - } - - /*! @decl int `[](int index) - */ - 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 ) - 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( (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); - } - - /*! @decl Buffer ``+( string ... what ) - */ - PIKEFUN Buffer ``+( string ... what ) - rawtype tFunc(tString, 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); - } - } - - 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 ) - *! - *! 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 ) - *! - *! 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 ) - *! - *! 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 ) - *! - *! Appends a single character at the end of the string. - *! - *! @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 ) - { - struct Buffer_struct *str = THIS; - struct pike_string *snum; - - push_int(256); - apply( i, "digits", 1 ); - - 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 ); - } - - 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 */ - } - - 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); - } - - 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 ); - } - - static void buffer_end_rewind_on_error( ONERROR *x ) - { - UNSET_ONERROR( (*x) ); - } - - /*! @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); - } - 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(); - } - 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); - } - - PIKEFUN Buffer add_short( int(0..65535) i ) { - f_Buffer_add_int16(1); - } - - /*! @decl Buffer add_int32( int(-2147483648..2147483647) i ) - *! @decl Buffer add_long( int(0..4294967295) i ) - *! - *! Appends an int32 at the end of the string. - *! - *! @returns - *! Returns the buffer itself. - *! - *! @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 ) - *! - *! 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()] - */ - 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: - { - struct sysmem *sm; - if (str->u.object->prog == Buffer_program) { - len = OBJ2_BUFFER(str->u.object)->len; - break; - } - 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. - */ - 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 ); - } - - /*! @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. - */ - PIKEFUN Buffer sprintf(mixed ... arguments) - rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)), - tAttr("sprintf_args", tMix), tStr); - - { - 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 ); - 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); - } - - /*! @decl Buffer|void cut(int start, int|void end, void|int(0..1) discard) - *! - *! Cut and delete the range of data from the current buffer. - *! Returns a new buffer with the cut data, unless discard is true. - *! - *! @seealso - *! @[get()], @[get_copy()], @[clear()] - */ - PIKEFUN Buffer|void cut(int index, int|void end_or_none, - void|int(0..1) discard) - { - 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 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) + PIKEFUN int _sizeof() { 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; - } - else - for (buf+=n<<shift; n++; ) { - if (LONGEST_HIGH_BYTE&res) - goto overflow; - res<<=8; buf-=1<<shift; res|=*buf; - } - 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; - 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 ) - *! - *! Identical in functionality to @[read](@[read_int](@[n])) but - *! faster. - *! - *! 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 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()] - */ - PIKEFUN int(-1..) consume( int(0..) n ) - { - 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 ) - { - 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; - } - 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(); - } - break; - } - } - - /*! @decl void trim() - *! - *! Compact buffer, free unused memory. - *! When called on a subbuffer, it clears the subbuffer and releases - *! the parent buffer to be modified again. - *! - *! @note - *! Calling this function excessively might slow things down, - *! since the data often has to be copied. - *! - *! @seealso - *! @[clear()]] - */ - PIKEFUN void trim() - { - 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; - } - } - - /*! @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; + 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( Pike_fp->flags & OBJECT_CLEAR_ON_EXIT ) - guaranteed_memset( s->str, 0, str->str.malloced ); - free_string( s ); + 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 ); } - 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,7 +3544,7 @@ PIKECLASS multi_string_replace INIT { - memset(&THIS->ctx, 0, sizeof(struct replace_many_context)); + MEMSET(&THIS->ctx, 0, sizeof(struct replace_many_context)); } EXIT @@ -7498,5 +6051,4 @@ void exit_builtin(void) #ifndef USE_SETENV if (env_allocs) free_mapping (env_allocs); #endif - if( shm_program ) free_program( shm_program ); } -- GitLab