#include "global.h" |
#include "object.h" |
#include "interpret.h" |
#include "operators.h" |
#include "bignum.h" |
#include "sscanf.h" |
#include "builtin_functions.h" |
#include "port.h" |
#include "whitespace.h" |
#include "pike_types.h" |
#include "iobuffer.h" |
#include "module_support.h" |
|
#include <arpa/inet.h> |
|
#define DEFAULT_CMOD_STORAGE static |
DECLARATIONS |
|
/*! @class IOBuffer |
*! |
*! A buffer to use as input or buffering when doing I/O. It is |
*! similar to @[String.Buffer], but can only contain 8bit data and is |
*! designed for protocol parsing. It is optimized for reading from |
*! the beginning and adding to the end, and will try to minimize the |
*! amount of data copying that is done. |
*! |
*! It can also directly read from and write to filedescriptors if so |
*! desired. This eliminates at least one memory copy. |
*/ |
PIKECLASS IOBuffer |
{ |
CVAR IOBuffer b; |
|
static void io_set_error_mode( IOBuffer *io, int m ) |
{ |
io->error_mode = m; |
} |
|
static size_t io_len( IOBuffer *io ) |
{ |
return io->len-io->offset; |
} |
|
static void io_unlock( IOBuffer *io ) |
{ |
io->locked--; |
} |
|
static void io_lock( IOBuffer *io ) |
{ |
io->locked++; |
} |
|
static void io_ensure_unlocked(IOBuffer *io) |
{ |
if( io->locked ) |
Pike_error("Can not modify the buffer right now, " |
" there are active subbuffers.\n"); |
} |
|
static INT_TYPE io_consume( IOBuffer *io, int num ) |
{ |
io->offset += num; |
return io_len(io); |
} |
|
static unsigned char *io_read_pointer(IOBuffer *io) |
{ |
return io->buffer + io->offset; |
} |
|
static int io_is_whitespace( IOBuffer *io, size_t pos ) |
{ |
if( pos > io_len( io ) ) |
return -1; |
switch( io->buffer[io->offset+pos] ) |
{ |
SPACECASE8 |
return 1; |
} |
return 0; |
} |
|
static unsigned char *io_add_space( IOBuffer *io, int bytes, int force ) |
{ |
io_ensure_unlocked(io); |
if( !io->malloced ) |
{ |
/* convert to malloced buffer from a shared one. */ |
unsigned char *old = io->buffer; |
io->buffer = xalloc( io->len + bytes + 100 ); |
io->allocated = io->len + bytes + 100; |
memcpy( io->buffer, old, io->len ); |
if( io->sub ) { |
io_unlock( get_storage(io->sub,IOBuffer_program ) ); |
free_object( io->sub ); |
} |
if( io->str ) free_string( io->str ); |
io->sub = 0; |
io->str = 0; |
io->malloced = 1; |
} |
|
if( force || (io->offset > io->len>>1) ) /* more than half used up. */ |
{ |
memmove( io->buffer, io->buffer+io->offset, io->len-io->offset ); |
io->len -= io->offset; |
io->offset = 0; |
} |
if( io->len + bytes > io->allocated ) |
{ |
size_t new_len = io->allocated; |
do |
new_len = ((new_len+32)*2)-32; |
while( new_len < io->len + bytes ); |
io->buffer = xrealloc( io->buffer, new_len ); |
io->allocated = new_len; |
} |
return io->buffer+io->len; |
} |
|
static void io_range_error( IOBuffer *io, int howmuch ) |
{ |
/* throw error if so desired. */ |
if( io->error_mode ) |
Pike_error("Trying to read %d outside allowed range\n", howmuch); |
} |
|
static int io_avail( IOBuffer *io, int len ) |
{ |
if( len < 0 || len + io->offset > io->len ) |
{ |
io_range_error( io, len ); |
return 0; |
} |
return 1; |
} |
|
static size_t io_append( IOBuffer *io, void *p, int bytes ) |
{ |
memcpy( io_add_space( io, bytes, 0 ), p, bytes ); |
io->len += bytes; |
return io_len(io); |
} |
|
static size_t io_read( IOBuffer *io, void *to, size_t len ) |
{ |
if( !io_avail(io,len)) |
return 0; |
memcpy( to, io_read_pointer(io), len ); |
io_consume( io, len ); |
return len; |
} |
|
static struct pike_string *io_read_string( IOBuffer *io, size_t len ) |
{ |
struct pike_string *s; |
if( !io_avail(io,len)) |
return NULL; |
|
if( len > 0x7fffffff ) |
Pike_error("This region is too large to convert to a string.\n"); |
s = begin_shared_string( len ); |
io_read( io, s->str, s->len ); |
return end_shared_string(s); |
} |
|
static struct object *io_read_buffer( IOBuffer *io, size_t len, int do_copy ) |
{ |
struct object *b; |
IOBuffer *to; |
if( !io_avail(io,len)) |
return NULL; |
|
b = low_clone( IOBuffer_program ); |
to = get_storage(b,IOBuffer_program); |
|
io_lock( io ); |
|
to->buffer = io_read_pointer(io); |
to->len = len; |
to->sub = Pike_fp->current_object; |
to->sub->refs++; |
io_consume( io, len ); |
|
if( do_copy ) |
io_add_space( to, 0, 0 );/* copies data. */ |
|
return b; |
} |
|
static int io_read_byte_uc( IOBuffer *io ) |
{ |
return io->buffer[io->offset++]; |
} |
|
#if 0 |
static int io_read_byte( IOBuffer *io ) |
{ |
if( !io_avail(io,1)) |
return -1; |
return io_read_byte_uc( io ); |
} |
#endif |
|
static LONGEST io_read_number( IOBuffer *io, size_t len ) |
{ |
size_t i; |
LONGEST res; |
if( !io_avail(io, len) ) |
return -1; |
if( len > SIZEOF_INT_TYPE ) |
{ |
unsigned int extra = len-SIZEOF_INT_TYPE; |
/* ensure only 0:s */ |
for( i=0; i<extra; i++ ) |
{ |
if( io_read_byte_uc(io) ) |
Pike_error("Integer (%dbit) overflow.\n", SIZEOF_INT_TYPE*8); |
} |
len=SIZEOF_INT_TYPE; |
} |
if( len == SIZEOF_INT_TYPE ) |
{ |
res = io_read_byte_uc(io); |
if( res > 127 ) |
Pike_error("Signed (%dbit) overflow.\n", SIZEOF_INT_TYPE*8); |
len--; |
} |
else |
res = 0; |
for( i=0; i<len; i++ ) |
{ |
res <<= 8; |
res |= io_read_byte_uc(io); |
} |
return res; |
} |
|
static struct object *io_read_bignum( IOBuffer *io, size_t len ) |
{ |
int i; |
struct pike_string *num; |
LONGEST res; |
num = io_read_string( io, len ); |
if( !num ) return NULL; |
push_string( num ); |
push_int( 256 ); |
return clone_object( get_auto_bignum_program(), 2 ); |
} |
|
static size_t io_add_int( IOBuffer *io, INT_TYPE i, size_t bytes ) |
{ |
unsigned char *x = io_add_space(io, bytes, 0); |
io->len += bytes; |
while(bytes--) |
{ |
x[bytes] = i&255; |
i>>=8; |
} |
return io_len( io ); |
} |
|
static size_t io_rewind( IOBuffer *io, INT_TYPE n ) |
{ |
if( n < 0 || (io->offset < (unsigned)n) ) |
{ |
io_range_error(io,-n); |
return -1; |
} |
io->offset -= n; |
return io->offset; |
} |
|
/* pike functions */ |
|
#undef THIS |
#define THIS (&(((struct IOBuffer_struct *)Pike_fp->current_storage)->b)) |
|
PIKEFUN int _size_object( ) |
{ |
RETURN THIS->malloced ? THIS->allocated : 0; |
} |
|
/* PIKEFUN void add( object(String.IOBuffer) x[, int len[,int start]] )*/ |
/* PIKEFUN void add( object(String.Buffer) x[, int len[,int start]] ) */ |
/* PIKEFUN void add( System.Memory x[, int len[,int start]] ) */ |
|
|
/*! @decl void add( string(8bit)|int(8bit) ... data ) |
*! |
*! Add the items in data one at a time. |
*! Integers are assumed to be 8bit numbers (characters) |
*/ |
PIKEFUN int add( string|int ... argp) |
{ |
int i; |
IOBuffer *io = THIS; |
for(i=0; i<args; i++ ) |
{ |
if( TYPEOF(argp[i]) == PIKE_T_STRING ) |
{ |
struct pike_string *s = argp[i].u.string; |
if( s->size_shift ) Pike_error("IOBuffer only handles 8bit data\n"); |
io_append( io, s->str, s->len ); |
} |
else |
{ |
unsigned char a = argp[i].u.integer & 255; |
io_append( io, &a, 1 ); |
} |
} |
RETURN io_len(io); |
} |
|
/*! @decl int add_byte( int(0..255) ) |
*! @decl int add_int8( int(0..255) ) |
*! Adds a single byte to the buffer. |
*/ |
PIKEFUN int add_byte( int(0..255) i ) |
{ |
unsigned char a = i&255; |
RETURN io_append( THIS, &a, 1 ); |
} |
|
PIKEFUN int add_int8( int(0..255) i ) |
{ |
unsigned char a = i&255; |
RETURN io_append( THIS, &a, 1 ); |
} |
|
/*! @decl int add_int16( int(0..65535) ) |
*! @decl int add_short( int(0..65535) ) |
*! |
*! Add a 16-bit network byte order value to the buffer |
*/ |
PIKEFUN int add_int16( int(0..65535) i ) |
{ |
unsigned short a = htons((i&65535)); |
push_int(io_append( THIS, &a, 2 )); |
} |
|
PIKEFUN int add_short( int(0..65535) i ) |
{ |
unsigned short a = htons((i&65535)); |
RETURN io_append( THIS, &a, 2 ); |
} |
|
/*! @decl int add_int32( int i ) |
*! Adds a 32 bit network byte order value to the buffer |
*/ |
PIKEFUN int add_int32( int i ) |
{ |
INT32 a = htonl(i); |
push_int(io_append( THIS, &a, 4 )); |
} |
|
/*! @decl void add_hstring( string(0..255) 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. |
*/ |
PIKEFUN int add_hstring( string str, int size_size ) |
{ |
if( str->size_shift ) |
Pike_error("Only 8bit is supported\n"); |
|
/* We know this is safe for len>=4, since pike strings are at |
* most 0x7ffffff bytes long. |
*/ |
if( size_size < 4 && |
str->len > (1<<(8*(size_size<0?-size_size:size_size)))-1 ) |
Pike_error("Too long string\n"); |
|
io_add_int( THIS, str->len, size_size ); |
push_int(io_append( THIS, str->str, str->len )); |
} |
|
/*! @decl int add_int( int i, int(0..) width ) |
*! |
*! Adds a generic integer to the buffer as an (width*8)bit |
*! network byteorder number. |
*! |
*! @[width] must be less than Int.NATIVE_MAX. |
*! |
*/ |
PIKEFUN int add_int( int i, int width ) |
{ |
RETURN io_add_int( THIS, i, width ); |
} |
|
PIKEFUN int add_int( object o, int width ) |
{ |
char *p; |
int extra, len; |
pop_stack(); |
convert_stack_top_to_bignum(); |
/* we now know for sure it's a bignum. */ |
push_int(256); |
apply( Pike_sp[-2].u.object, "digits", 1 ); |
p = Pike_sp[-1].u.string->str; |
len = Pike_sp[-1].u.string->len; |
if( len > width ) |
Pike_error("Number too large to store in %d bits\n", width*8); |
/* p += len-width;*/ |
|
if( len < width ) |
{ |
INT_TYPE null = 0; |
|
while( len < width ) |
{ |
int a = MIN(width-len,sizeof(INT_TYPE)); |
io_append( THIS, &null, a ); |
width-=a; |
} |
} |
RETURN io_append( THIS, p, width ); |
} |
|
/*! @decl protected int `[](int off) |
*! |
*! Return the character at the specified offset. |
*/ |
PIKEFUN int `[]( int off ) |
flags ID_PROTECTED; |
{ |
IOBuffer *io = THIS; |
pop_stack(); |
if( off < 0 ) |
off = io_len(io)-off; |
|
if( io_avail( io, off ) ) |
push_int( io_read_pointer(io)[off] ); |
else |
push_int(-1); |
} |
|
/*! @decl protected int `[]=(int off, int char) |
*! |
*! Set the character at the specified offset to @[char]. |
*/ |
PIKEFUN int `[]=( int off, int val ) |
flags ID_PROTECTED; |
{ |
IOBuffer *io = THIS; |
io_add_space( io, 0, 0 ); |
|
if( off < 0 ) off = io_len(io)-off; |
|
if( io_avail( io, off ) ) |
{ |
io_read_pointer(io)[off]=(val&0xff); |
} |
else |
{ |
/* hm, well. We could extend the buffer? */ |
push_int(-1); |
} |
} |
|
|
/*! @decl int _sizeof() |
*! |
*! Returns the buffer size, in bytes. |
*! This is how much you can read from the buffer until it runs out of data. |
*/ |
PIKEFUN int _sizeof() |
flags ID_PROTECTED; |
{ |
push_int64(io_len(THIS)); |
} |
|
/*! @decl string cast(string type) |
*! |
*! Convert the buffer to a string. |
*! |
*!@note |
*! This only works for buffers whose length is less than 0x7fffffff. |
*/ |
PIKEFUN string cast(string to) |
{ |
if( to != literal_string_string ) |
{ |
push_undefined(); |
return; |
} |
if( io_len(THIS) > 0x7fffffff ) |
Pike_error("This buffer is too large to convert to a string.\n"); |
push_string(make_shared_binary_string((void*)io_read_pointer(THIS), |
(INT32)io_len(THIS))); |
} |
|
|
/*! @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( IOBuffer 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( IOBuffer 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 ) |
{ |
io_set_error_mode( THIS, m ); |
pop_stack(); |
} |
|
/*! @decl object lock() |
*! |
*! Makes this buffer read only until the returned object is released. |
*! |
*! @note |
*! This currently simply returns a 0-length subbuffer. |
*/ |
PIKEFUN IOBuffer lock() |
{ |
push_object( io_read_buffer( THIS, 0, 0 ) ); |
} |
|
PIKEFUN string _sprintf(int o, mapping ignore) |
flags ID_PROTECTED; |
{ |
pop_n_elems(args); |
if( o == 'O' ) |
{ |
push_text("IOBuffer(%d bytes, read=[..%d] data=[%d..%d] free=[%d..%d] %s%s)"); |
/* io_len [..offset] [offset..len] [..allocated] */ |
push_int(io_len(THIS)); |
push_int(THIS->offset-1); |
push_int(THIS->offset); |
push_int(THIS->len-1); |
push_int(THIS->len); |
push_int(THIS->allocated); |
push_text( (THIS->str ? "string" : THIS->sub ? "subbuffer" : "allocated" ) ); |
if( THIS->locked ) |
push_text(" (read only)"); |
else |
push_text(""); |
f_sprintf(9); |
} |
else |
push_undefined(); |
} |
|
/*! @decl string(8bit) read_hstring( int(0..) n ) |
*! |
*! Identical in functionality to @[read](@[read_number](@[n])) but |
*! faster. |
*! |
*! Read a network byte order number of size n*8 bits, then return the |
*! indicated number of bytes as a string. |
*! |
*! If there is not enough data available return 0. |
*! |
*! Note that pike string can not be longer than 0x7fffffff bytes (~2Gb). |
*/ |
PIKEFUN string(0..255) read_hstring( int(0..) bytes ) |
{ |
LONGEST len; |
struct pike_string *s; |
|
len = io_read_number( THIS, bytes ); |
s = io_read_string( THIS, len ); |
if( s ) |
push_string(s); |
else |
push_int(0); |
} |
|
/*! @decl IOBuffer read_hbuffer( int n ) |
*! @decl IOBuffer read_hbuffer( int n, bool copy ) |
*! |
*! Same as @[read_hstring], but returns the result as an IOBuffer. |
*! |
*! No data is copied unless @[copy] is specified and true, the new |
*! buffer points into the old one. |
*! |
*! @note |
*! As long as the subbuffer exists no data can be added to the |
*! main buffer. |
*! |
*! Usually this is OK, since it often represents something that |
*! should be parsed before the next whatever is extracted from |
*! the buffer, but do take care. |
*! |
*! If you need to unlink the new buffer after it has been |
*! created, call @[trim] in it. |
*/ |
PIKEFUN IOBuffer read_hbuffer( int(0..) bytes, int|void copy ) |
{ |
LONGEST len = io_read_number( THIS, bytes ); |
int do_copy = 0; |
struct object *o; |
if( copy ) do_copy = copy->u.integer; |
pop_n_elems(args); |
|
if( (o = io_read_buffer( THIS, len, do_copy )) ) |
push_object(o); |
else |
push_int(0); |
} |
|
/*! @decl IOBuffer read_buffer( int n ) |
*! @decl IOBuffer read_buffer( int n, bool copy ) |
*! |
*! Same as @[read], but returns the result as an IOBuffer. |
*! |
*! No data is copied unless @[copy] is specified and true, the new buffer |
*! points into the old one. |
*! |
*! @note |
*! As long as the subbuffer exists no data can be added to the main buffer. |
*! |
*! Usually this is OK, since it often represents something that |
*! should be parsed before the next whatever is extracted from |
*! the buffer, but do take care. |
*/ |
PIKEFUN IOBuffer read_buffer( int bytes, int|void copy ) |
{ |
int do_copy = 0; |
struct object *o; |
if( copy ) |
do_copy = copy->u.integer; |
pop_n_elems(args); |
if( (o = io_read_buffer( THIS, bytes, do_copy )) ) |
push_object(o); |
else |
push_int(0); |
} |
|
/*! @decl int sprintf(strict_sprintf_format format, sprintf_args ... args) |
*! |
*! Appends the output from @[sprintf] at the end of the buffer. |
*! |
*! This is somewhat faster than add(sprintf(...)) since no |
*! intermediate string is created. |
*/ |
PIKEFUN int sprintf(mixed ... ignored) |
rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)), |
tAttr("sprintf_args", tMix), tStr); |
{ |
ONERROR _e; |
struct string_builder tmp; |
INT_TYPE sz; |
init_string_builder(&tmp,0); |
SET_ONERROR(_e, free_string_builder, &tmp); |
low_f_sprintf(args, 0, &tmp ); |
if( tmp.s->size_shift ) |
Pike_error("IOBuffer only handles 8bit data\n"); |
sz = io_append( THIS, tmp.s->str, tmp.s->len ); |
pop_n_elems(args); |
CALL_AND_UNSET_ONERROR(_e); |
push_int(sz); |
} |
|
/*! @decl array sscanf(string(8bit) 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 sscanf( string format ) |
{ |
INT32 i; |
ptrdiff_t num_used; |
struct svalue *start = Pike_sp; |
|
i = low_sscanf_pcharp( |
MKPCHARP(io_read_pointer(THIS), 0), io_len(THIS), |
MKPCHARP(format->str,format->size_shift), format->len, |
&num_used, 0); |
|
if( !num_used ) |
{ |
io_range_error(THIS,-1); |
pop_n_elems(Pike_sp-start); |
push_int(0); |
} |
else |
{ |
io_consume( THIS, num_used ); |
f_aggregate(Pike_sp-start); |
} |
} |
|
|
/*! @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 whitespaces 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) |
{ |
int stop, whites = 0; |
static ptrdiff_t(*parse_json_pcharp)(PCHARP,size_t,int,char**); |
char *err = NULL; |
if( require_whitespace ) |
whites = require_whitespace->u.integer; |
|
if( !parse_json_pcharp ) |
parse_json_pcharp = PIKE_MODULE_IMPORT(Standards.JSON, parse_json_pcharp ); |
|
stop = parse_json_pcharp( MKPCHARP(io_read_pointer(THIS),0), |
io_len(THIS), 1|8, &err ); /* json_utf8 */ |
|
if( stop < 0 ) |
{ |
if( -stop == (ptrdiff_t)io_len(THIS) || (err && !strncmp(err,"Unterminated",12))) |
{ |
io_range_error(THIS,-stop+1); |
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 && |
(io_is_whitespace(THIS,stop)<=0 && io_is_whitespace(THIS,stop-1)<=0)) |
{ |
if( stop == (ptrdiff_t)io_len(THIS) ) |
{ |
io_range_error(THIS,stop); |
pop_stack(); |
push_undefined(); |
} |
else |
Pike_error("Missing whitespace between json values at offset %d\n", stop ); |
} |
else |
{ |
if( whites ) |
while( io_is_whitespace( THIS, stop ) ) |
stop++; |
io_consume( THIS, stop ); |
} |
} |
} |
|
|
/*! @decl mixed match(string(8bit) 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 match( string format ) |
{ |
INT32 i; |
ptrdiff_t num_used; |
struct svalue *start = Pike_sp; |
|
i = low_sscanf_pcharp( |
MKPCHARP(io_read_pointer(THIS), 0), io_len(THIS), |
MKPCHARP(format->str,format->size_shift), format->len, |
&num_used, |
0); |
|
if( !num_used ) |
{ |
io_range_error(THIS,-1); |
pop_n_elems(Pike_sp-start); |
push_int(0); |
} |
else |
{ |
io_consume( THIS, num_used ); |
if( Pike_sp-start > 1 ) |
f_add(Pike_sp-start); |
} |
} |
|
|
/*! @decl void clear() |
*! |
*! Clear the buffer. |
*/ |
PIKEFUN void clear( ) |
{ |
IOBuffer *io = THIS; |
io->offset = io->len = 0; |
} |
|
/*! @decl void trim() |
*! |
*! Frees unused memory. |
*! |
*! Note that calling this function excessively will slow things |
*! down, since the data often has to be copied. |
*! |
*! @note |
*! This function could possibly throw an out-of-memory error |
*! if the realloc fails to find a new (smaller) memory area. |
*/ |
PIKEFUN void trim( ) |
{ |
IOBuffer *io = THIS; |
io_add_space( io, 0, 1 ); |
if( io->allocated > io->len+32 ) |
{ |
io->buffer = xrealloc( io->buffer, io->len ); |
io->allocated = io->len; |
} |
} |
|
/*! @decl int(0..)|int(-1..-1) consume( int(0..) n ) |
*! |
*! Discard the first @[n] bytes from the buffer |
*/ |
PIKEFUN int(-1..) consume( int n ) |
{ |
if( !io_avail( THIS, n ) ) |
{ |
io_range_error( THIS, n ); |
push_int(-1); |
} |
else |
push_int64( io_consume( THIS, n ) ); |
} |
|
|
/*! @decl int(0..)|int(-1..-1) unread( int(0..) n ) |
*! |
*! Rewind the buffer @[n] bytes. |
*! |
*! @returns |
*! |
*! This function returns how many more bytes of buffer is |
*! available to rewind, or -1 on error. |
*! |
*! @note |
*! |
*! Unless you add new data to the buffer using any of the add |
*! functions you can always rewind. |
*! |
*! You can call @[undread(0)] to see how much. |
*/ |
PIKEFUN int(-1..) unread( int(0..) bytes ) |
{ |
push_int64( io_rewind( THIS, bytes ) ); |
} |
|
/*! @decl string(8bit) read( int n ) |
*! |
*! Read @[bytes] bytes of data from the buffer. |
*! |
*! If there is not enough data available this returns 0. |
*/ |
PIKEFUN string(0..255) read( int(0..) bytes ) |
{ |
struct pike_string * s = io_read_string(THIS, bytes ); |
pop_stack(); |
if( s ) |
push_string(s); |
else |
push_int(0); |
} |
|
/*! @decl string(8bit) read( ) |
*! |
*! Read all data from the buffer. |
*! |
*! If there is not enough data available this returns 0. |
*! |
*! This is basically equivalent to (string)buffer, but it also |
*! removes the data from the buffer. |
*/ |
PIKEFUN string(0..255) read() |
{ |
struct pike_string * s = io_read_string(THIS, io_len(THIS)); |
if( !s ) |
push_int(0); |
else |
push_string(s); |
} |
|
/*! @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. |
*! |
*! Will return -1 if there is not enough buffer space available |
*! unless error mode is set to throw errors. |
*/ |
PIKEFUN int read_int( int len ) |
{ |
IOBuffer *io = THIS; |
struct object *o; |
|
pop_stack(); |
|
if( len < SIZEOF_INT_TYPE-1 ) /* will for sure fit. */ |
{ |
push_int( io_read_number( io, len ) ); |
return; |
} |
|
if( (o = io_read_bignum(io, len )) ) |
{ |
push_object(o); |
reduce_stack_top_bignum(); |
return; |
} |
push_int(-1); |
} |
|
/*! @decl void create( string(8bit) x ) |
*! |
*! Create a new buffer with the contents of the given |
*! string. |
*! |
*! @note |
*! This will not copy the string data, instead data will |
*! be read from the string until it needs to be modified, so the |
*! buffer creation is fast regardless of the length of the |
*! string. |
*/ |
PIKEFUN void create( string x ) |
flags ID_PROTECTED; |
{ |
IOBuffer *this = THIS; |
if( this->buffer ) |
Pike_error("Can not initialize twice.\n"); |
if( x->size_shift ) |
Pike_error("Only string(0..255) supported.\n"); |
this->buffer = (unsigned char*)x->str; |
this->len = x->len; |
this->malloced = 0; |
this->str = x; |
x->refs++; |
} |
|
/*! @decl void create( int|void len ) |
*! |
*! Create a new buffer of the given size. |
*! The buffer will grow if needed, so the length is only the |
*! initial size. |
*! |
*! The default initial size is 4K |
*/ |
PIKEFUN void create( int len ) |
flags ID_PROTECTED; |
{ |
IOBuffer *this = THIS; |
if( this->buffer ) |
Pike_error("Can not initialize twice.\n"); |
this->buffer = xalloc(len); |
this->allocated = len; |
this->malloced = 1; |
} |
|
PIKEFUN void create( ) |
flags ID_PROTECTED; |
{ |
IOBuffer *this = THIS; |
if( this->buffer ) |
Pike_error("Can not initialize twice.\n"); |
this->buffer = xalloc(4096-32); |
this->allocated = 4096-32; |
this->malloced = 1; |
} |
|
/*! @endclass |
*/ |
|
INIT { |
IOBuffer *this = THIS; |
memset( this, 0, sizeof(IOBuffer)); |
} |
|
EXIT { |
IOBuffer *this = THIS; |
if( this->sub ) |
{ |
free_object( this->sub ); |
io_unlock( get_storage(this->sub,IOBuffer_program ) ); |
} |
if( this->str ) |
free_string( this->str ); |
if( this->malloced ) |
free( this->buffer ); |
} |
} |
void init_stdio_buffer(void) |
{ |
INIT |
} |
|
|
void exit_stdio_buffer(void) |
{ |
EXIT |
} |
|