pike.git / src / modules / _Stdio / buffer.cmod

version» Context lines:

pike.git/src/modules/_Stdio/buffer.cmod:16:   #include "pike_types.h"   #include "pike_threadlib.h"   #include "iobuffer.h"   #include "module_support.h"      #include <arpa/inet.h>      #define DEFAULT_CMOD_STORAGE static   DECLARATIONS    + struct sysmem { +  unsigned char *p; +  size_t size; + };      static struct program *shm_program, *sbuf_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);    if (!shm_program)    return 0;    Pike_sp--;    }    return get_storage( o, shm_program ); - }; + }    - struct sysmem { -  unsigned char *p; -  size_t size; - }; -  +    static struct string_builder *string_buffer(struct object *o)   {    if( !sbuf_program )    {    push_text("String.Buffer");    SAFE_APPLY_MASTER("resolv", 1);    sbuf_program = program_from_svalue(Pike_sp - 1);    if (!sbuf_program)    return 0;    Pike_sp--;    }    return get_storage( o, sbuf_program ); - }; + }       -  + static struct program *buffer_error_program; +    /*! @module Stdio    */ - static struct program *buffer_error_program; +       /*! @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.    *!    *! The class maintains two separate offsets, one for reading and one
pike.git/src/modules/_Stdio/buffer.cmod:134:    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 ) +  static void io_unstash_malloc( IOBuffer *io )    { -  io_ensure_unlocked(io); -  if( !io->malloced ) +  if( LIKELY(io->malloced || !io->stash.ptr) ) +  return; +  +  if( io->stash.len >= io_len( io ) )    { -  +  memcpy( io->stash.ptr, io_read_pointer( io ), io_len(io) ); +  io->buffer = io->stash.ptr; +  io->len = io_len(io); +  io->offset = 0; +  io->allocated = io->stash.len; +  io->malloced = 1; +  } +  else +  free( io->stash.ptr ); +  io->stash.ptr = 0; +  io->stash.len = 0; +  } +  +  static void io_stash_malloc( IOBuffer *io ) +  { +  if( io->malloced ) +  { +  io->stash.ptr = io->buffer; +  io->stash.len = io->allocated; +  io->malloced = 0; +  io->buffer = 0; +  io->offset = 0; +  } +  } +  +  static void io_ensure_malloced( IOBuffer *io, int bytes ) +  { +  if( UNLIKELY(!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; -  +  io->num_malloc++; +  io->num_move++;    memcpy( io->buffer, old, io->len );    if( io->sub ) {    io_unlock( get_storage(io->sub,IOBuffer_program ) );    free_object( io->sub );    }    if( io->source ) free_object( io->source );    if( io->str ) free_string( io->str );    io->source = 0;    io->sub = 0;    io->str = 0;    io->malloced = 1;    } -  +  }    -  if( io->len == io->offset ) -  io->offset = io->len = 0; +  static unsigned char *io_add_space( IOBuffer *io, int bytes, int force ) +  { +  io_ensure_unlocked(io); +  io_unstash_malloc(io); +  io_ensure_malloced(io, bytes);    -  +  /* +  * It is actually not certain that checking this is a good idea. +  * +  * The reason being that if the current buffer size is very small +  * (io_len(io)) and the bytes added is large and there is an +  * offset, it makes sense to move now when we do not have to copy +  * as much. +  * +  * However, if we are not going to add any more data it makes +  * sense to not move. +  * +  * But, since the buffer is intended to be used mainly for +  * bufering of files, it is very likely we will add more. +  * +  +  * In much the same way it might make sense to move the data if +  * wthe data can be added without reallocation in that way, even if +  * less than 50% of the buffer is wasted. +  * +  * However, that might lead to O(n) performance. +  * +  * So, for now this check is disabled: +     if( io->len + bytes < io->allocated )    return io->buffer+io->len;    -  if( (force && io->offset) -  || (io->offset > io->len>>1) -  || (io_len(io)+bytes < io->allocated && -  (io->len+bytes > io->allocated)) ) +  * And this one: +  +  || (io_len(io)+bytes < io->allocated +  && io->len+bytes >= io->allocated) +  +  */ +  +  if( LIKELY(!io->locked_move) )    { -  +  if( io->len == io->offset ) +  io->offset = io->len = 0; +  if( UNLIKELY((force && io->offset) || (io->offset > io->len>>1)) ) +  {    memmove( io->buffer, io_read_pointer(io), io_len(io) ); -  +  io->num_move++;    io->len -= io->offset;    io->offset = 0;    } -  +  }    -  if( io->len + bytes > io->allocated ) +  if( UNLIKELY(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->num_malloc++;    io->allocated = new_len;    }    return io->buffer+io->len;    }          /*! @decl protected bool range_error( int howmuch )    *!    *! This function is called when an attempt is made to read out of bounds.    *!
pike.git/src/modules/_Stdio/buffer.cmod:228:    */    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);    }       static void io_range_error_throw( IOBuffer *io, int howmuch ) +  ATTRIBUTE((noclone,noinline)); +  +  static void io_range_error_throw( IOBuffer *io, int howmuch )    {    if( io->error_mode )    {    if( howmuch > 0 )    {    push_text("Trying to read %d bytes outside allowed range\n");    push_int(howmuch);    f_sprintf(2);    }    else    push_text("Illegal arguments\n");       ref_push_object( io->this );    push_object(clone_object(io->error_mode, 2));    f_throw(1);    }    }    -  static struct pike_string *io_read_string( IOBuffer *io, size_t len ); +  static struct pike_string *io_read_string( IOBuffer *io, size_t len ) +  ATTRIBUTE((noclone,noinline));    static size_t io_rewind( IOBuffer *io, INT_TYPE n );    -  +  static void io_do_rewind_on_error( struct rewind_to *e ) +  { +  e->io->locked_move--; +  e->io->offset = e->rewind_to; +  free( e ); +  } +  +  static void io_rewind_on_error( IOBuffer *io, ONERROR *x ) +  { +  struct rewind_to *rew = xalloc( sizeof( struct rewind_to ) ); + #if defined(PIKE_DEBUG) +  rew->old_locked_move = io->locked_move; + #endif +  io->locked_move++; +  rew->io = io; +  rew->rewind_to = io->offset; +  SET_ONERROR( (*x), io_do_rewind_on_error, rew ); +  } +  +  static void io_unset_rewind_on_error( IOBuffer *io, ONERROR *x ) +  { +  UNSET_ONERROR( (*x) ); +  io->locked_move--; + #if defined(PIKE_DEBUG) +  if( io->locked_move != ((struct rewind*)x)->old_locked_mode ) +  Pike_fatal( "Invalid io_rewind_on_error nesting\n"); + #endif +  free( x->arg ); +  } +  +  +  +  static void io_do_unwrite_on_error( struct rewind_to *e ) +  { +  e->io->len = e->rewind_to; +  free( e ); +  } +  +  static void io_unwrite_on_error( IOBuffer *io, ONERROR *x ) +  { +  struct rewind_to *rew = xalloc( sizeof( struct rewind_to ) ); +  rew->io = io; +  rew->rewind_to = io->len; +  SET_ONERROR( (*x), io_do_unwrite_on_error, rew ); +  } +  +  static void io_unset_unwrite_on_error( IOBuffer *io, ONERROR *x ) +  { +  UNSET_ONERROR( (*x) ); +  free( x->arg ); +  } +     static void io_trigger_output( IOBuffer *io )    {    if( io->output && !io->output_triggered )    {    struct my_file *fd;    if( (fd = get_storage( io->output, file_program )) )    {    /* actual fd? Just register the callback. */    fd->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB);    if(!SAFE_IS_ZERO(&fd->event_cbs[PIKE_FD_WRITE])
pike.git/src/modules/_Stdio/buffer.cmod:285:    pop_stack();    if( l < 0 )    l = 0;    if( bytes > (unsigned)l )    io_rewind( io, bytes-l );    }    }    }    }    -  static int io_range_error( IOBuffer *io, int howmuch ) +  static int io_range_error( IOBuffer *io, ssize_t howmuch ) +  ATTRIBUTE((noclone,noinline)); +  +  static int io_range_error( IOBuffer *io, ssize_t howmuch )    {    int res;    struct svalue *osp = Pike_sp;    -  push_int( howmuch ); +  push_int64( howmuch );    apply_current( f_IOBuffer_range_error_fun_num, 1 );    res = Pike_sp[-1].u.integer;    pop_n_elems( Pike_sp-osp );    if( !res ) io_range_error_throw( io, howmuch );       return res;    }    -  static int io_avail( IOBuffer *io, int len ) +  static int io_avail( IOBuffer *io, ssize_t len )    {    if( len < 0 || len + io->offset > io->len )    {    if( len < 0 )    io_range_error_throw( io, 0 );    else if( io_range_error( io, len+io->len-io->offset ) )    return io_avail(io,len);    return 0;    }    return 1;    }    -  +  static int io_avail_mul( IOBuffer *io, ssize_t len, ssize_t each ) +  { +  /* safely check if len*each is available. */ +  size_t total = io_len(io); +  if( len < 0 || each <= 0 ) +  { +  io_range_error_throw( io, 0 ); +  return 0; +  } +  +  if( (total/(size_t)each) < (size_t)len ) +  { +  if( io_range_error( io, len+io->len-io->offset ) ) +  return io_avail_mul(io,len,each); +  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;    io_trigger_output( io );    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"); -  +  +  if( !io_avail(io,len)) +  return NULL; +     s = begin_shared_string( len );    io_read( io, s->str, 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))
pike.git/src/modules/_Stdio/buffer.cmod:361:       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. */ +  io_ensure_malloced( to, 0 );       return b;    }       static int io_read_byte_uc( IOBuffer *io )    {    return io->buffer[io->offset++];    }    - #if 0 -  static int io_read_byte( IOBuffer *io ) +  static INT_TYPE io_read_number_uc( IOBuffer *io, size_t len )    { -  if( !io_avail(io,1)) -  return -1; -  return io_read_byte_uc( io ); +  size_t i; +  LONGEST res = 0; +  for( i=0; i<len; i++ ) +  { +  res <<= 8; +  res |= io_read_byte_uc(io);    } - #endif +  return res; +  }       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;
pike.git/src/modules/_Stdio/buffer.cmod:406:    }    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 io_read_number_uc( io, len );    } -  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 ) +  static void io_add_bignum( IOBuffer *io, struct object *o, int width )    { -  unsigned char *x = io_add_space(io, bytes, 0); +  char *p; +  INT_TYPE len; +  +  push_int(256); +  apply( o, "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); +  +  if( len < width ) +  { +  INT_TYPE null = 0; +  while( len < width ) +  { +  int a = MIN(width-len,sizeof(INT_TYPE)); +  io_append( io, &null, a ); +  width-=a; +  } +  } +  io_append( io, p, width ); +  pop_stack(); +  } +  +  static void io_add_int_uc( IOBuffer *io, INT_TYPE i, size_t bytes ) +  { +  unsigned char *x = io->buffer+io->offset;    io->len += bytes;    while(bytes--)    {    x[bytes] = i&255;    i>>=8;    } -  +  } +  +  static size_t io_add_int( IOBuffer *io, INT_TYPE i, size_t bytes ) +  { +  unsigned char *x = io_add_space(io, bytes, 0); +  io_add_int_uc( io, i, bytes );    io_trigger_output( io );    return io_len( io );    }       static size_t io_rewind( IOBuffer *io, INT_TYPE n )    {    if( n < 0 || (io->offset < (unsigned)n) )    {    if( n < 0 )    io_range_error_throw( io, 0 );
pike.git/src/modules/_Stdio/buffer.cmod:471:    *((short *)(io->buffer+io->len)) = htons(shrt);    io->len+=2;    }       static void io_append_int_uc( IOBuffer *io, unsigned INT32 i )    {    *((INT32 *)(io->buffer+io->len)) = htonl(i);    io->len+=4;    }    +  +  static size_t io_svalue_len( IOBuffer *UNUSED(io), struct svalue *p ) +  { +  union { +  struct string_builder *b; +  struct sysmem *s; +  IOBuffer *io; +  } src; +  +  if( TYPEOF(*p) == PIKE_T_STRING ) +  { +  if( p->u.string->size_shift ) +  Pike_error("Only string(0..255) supported\n"); +  return p->u.string->len; +  } +  +  if( TYPEOF(*p) == PIKE_T_OBJECT ) +  { +  struct object *x = p->u.object; +  +  if( (src.io = get_storage(x, IOBuffer_program)) ) +  return io_len(src.io); +  +  if( (src.b = string_buffer( x )) ) +  { +  if( src.b->s->size_shift ) +  Pike_error("Only string(0..255) supported\n"); +  else +  return src.b->s->len; +  } +  +  if( (src.s = system_memory( x )) ) +  return src.s->size; +  } +  Pike_error("Illegal argument (not a string or buffer object)\n"); +  } +  +  /* NOTE: Can return negative integers. */    static INT_TYPE get_small_int( struct svalue *s )    {    if( TYPEOF(*s) == PIKE_T_INT )    return s->u.integer;    if( is_bignum_object_in_svalue( s ) )    {    INT64 i64;    if( int64_from_bignum( &i64, s->u.object ) )    return i64; -  Pike_error("Too big bignum\n"); +  Pike_error("Too big bignum, can not fit in indicated width\n");    } -  Pike_error("Non integer in array\n"); +  Pike_error("Non integer argument\n");    }    -  +  static void io_append_svalue( IOBuffer *io, struct svalue *p ) +  { +  switch( TYPEOF(*p) ) +  { +  case PIKE_T_STRING: +  { +  struct pike_string *s = p->u.string; +  if( s->size_shift ) Pike_error("IOBuffer only handles 8bit data\n"); +  if( !io->buffer ) +  { +  io->str = s; +  io->buffer = (unsigned char*)s->str; +  io->len = s->len; +  s->refs++; +  } +  else +  io_append( io, s->str, s->len ); +  } +  break; +  case PIKE_T_ARRAY: +  { +  struct array *argp = p->u.array; +  INT_TYPE i; +  for(i=0; i<argp->size; i++ ) +  io_append_svalue( io, argp->item+i ); +  } +  break; +  case PIKE_T_OBJECT: +  { +  struct object *x = p->u.object; +  union { +  IOBuffer *io; +  struct string_builder *b; +  struct sysmem *s; +  } src;    -  +  if( (src.io = get_storage(x, IOBuffer_program)) ) +  { +  io_append( io, io_read_pointer(src.io), io_len(src.io) ); +  io_consume( src.io, io_len(src.io) ); +  } +  else if( (src.b = string_buffer( x )) ) +  { +  if( src.b->s->size_shift ) +  Pike_error("Only string(0..255) supported\n"); +  io_append( io, src.b->s->str, src.b->s->len ); +  } +  else if( (src.s = system_memory( x )) ) +  { +  if( !io->buffer ) +  { +  io->source = x; +  io->buffer = src.s->p; +  io->len = src.s->size; +  io->source->refs++; +  } +  else +  io_append( io, src.s->p, src.s->size ); +  } +  else +  { +  default: +  Pike_error("Unsupported argument type\n"); +  } +  } +  break; +  case PIKE_T_INT: +  { +  unsigned char a = p->u.integer; +  io_append( io, &a, 1 ); +  } +  } +  }    -  -  /* pike functions */ -  +    #undef THIS   #define THIS (&(((struct IOBuffer_struct *)Pike_fp->current_storage)->b))    -  +  +  /* pike functions */ +     /*! @decl int(0..) input_from( Stdio.Stream f, int|void nbytes )    *!    *! Read data from @[f] into this buffer. If @[nbytes] is not    *! specified, read until there is no more data to read (currently).    *!    *! Returns the amount of data that was read    *!    *! @note    *! Please note that this funcition will read all data from the    *! filedescriptor unless it's set to be non-blocking.
pike.git/src/modules/_Stdio/buffer.cmod:604:    *! Write data from the buffer to the indicated file.    *!    *! Will return the number of bytes that were successfully written.    *!    *! If @[nbytes] is not specified the whole buffer will be written    *! if possible. Otherwise at most @[nbytes] will be written.    */    PIKEFUN int(0..) output_to( object f, int|void _nbytes )    {    IOBuffer *io = THIS; -  size_t sz = io_len( io ); +     size_t written = 0, nbytes = (size_t)-1;    struct my_file *fd;    INT_TYPE *wr = &Pike_sp->u.integer;    -  +  size_t sz = io_len( io ); +     if( !sz )    {    io_range_error(io, sz);    sz = io_len(io);    }    if( _nbytes )    nbytes = _nbytes->u.integer;       if( (fd = get_storage( f, file_program )) )    {
pike.git/src/modules/_Stdio/buffer.cmod:671:    }    }    RETURN written;    }       PIKEFUN int(0..) _size_object( )    {    RETURN THIS->malloced ? THIS->allocated : 0;    }    -  /*! @decl void add( System.Memory|Stdio.IOBuffer|String.Buffer|string(8bit)|int(8bit) ... data ) +  /*! @decl IOBuffer add( AddArgument ... data ) +  *! @code +  *! private typedef @[System.Memory]|@[Stdio.IOBuffer]|@[String.Buffer] BufferObject; +  *! private typedef BufferObject|string(8bit)|int(8bit)|array(AddArgument) AddArgument; +  *! @endcode    *!    *! Add the items in data to the end of the buffer. -  *! Integers are assumed to be 8bit numbers (characters) +     *! -  +  *! The supported argument types are: +  *! +  *! @ul +  *! @item string(0.255) +  *! An eight bit string. +  *! @item int(0..255) +  *! A single byte +  *! @item System.Memory +  *! A chunk of memory. The whole memory area is added. +  *! @item Stdio.IOBuffer +  *! A chunk of memory. The whole memory area is added. +  *! @item String.Buffer +  *! A chunk of memory. The whole memory area is added. +  *! @item array(AddArgument) +  *! Add all elements in the array individually. Each element can be +  *! one of the types listed here. +  *! @endul +  *!    *! @seealso    *! @[sprintf], @[add_int8], @[add_int16], @[add_int32], @[add_int]    *! and    *! @[add_hstring]    */ -  PIKEFUN IOBuffer add( object|string|int ... argp) +  PIKEFUN IOBuffer add( object|string|int|array(object|string|int) ... argp)    {    int i;    IOBuffer *io = THIS; -  +  +  if( args == 1 && !io_len( io ) ) +  io_stash_malloc( io ); +     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 if( TYPEOF(argp[i]) == PIKE_T_OBJECT ) -  { -  struct object *x = argp[i].u.object; -  IOBuffer *src; -  struct string_builder *b; -  struct sysmem *s; +  io_append_svalue( io, argp+i );    -  if( (src = get_storage(x, IOBuffer_program)) ) -  { -  io_append( io, io_read_pointer(src), io_len(src) ); -  io_consume( src, io_len(src) ); -  } -  else if( (b = string_buffer( x )) ) -  { -  if( b->s->size_shift ) -  Pike_error("Only string(0..255) supported\n"); -  io_append( io, b->s->str, b->s->len ); -  } -  else if( (s = system_memory( x )) ) -  { -  io_append( io, s->p, s->size ); -  } -  else -  Pike_error("Unsupported object type\n"); -  } -  else -  { -  unsigned char a = argp[i].u.integer & 255; -  io_append( io, &a, 1 ); -  } -  } +  pop_stack();    ref_push_object(io->this);    }       /*! @decl IOBuffer add_byte( int(0..255) )    *! @decl IOBuffer add_int8( int(0..255) )    *! Adds a single byte to the buffer.    */    PIKEFUN IOBuffer add_byte( int i )    {    unsigned char a = i&255;
pike.git/src/modules/_Stdio/buffer.cmod:775:    *! Adds a 32 bit network byte order value to the buffer    */    PIKEFUN int(0..) add_int32( int i )    {    INT32 a = htonl(i);    io_append( THIS, &a, 4 );    ref_push_object(Pike_fp->current_object);    }       /*! @decl IOBuffer add_hstring( string(0..255) data, int size_size ) +  *! @decl IOBuffer add_hstring( Stdio.IOBuffer data, int size_size ) +  *! @decl IOBuffer add_hstring( System.Memory data, int size_size ) +  *! @decl IOBuffer add_hstring( String.Buffer data, int size_size )    *!    *! Adds length/data for @[data] to the buffer.    *! -  *! This is identical to @[sprintf("%"+size_size+"H",data)] but -  *! significantly faster. +  *! This is identical to @[sprintf("%"+size_size+"H",(string)data)] but +  *! can be significantly faster.    *!    *! @[size_size] must be less than Int.NATIVE_MAX.    */ -  +  +  PIKEFUN IOBuffer add_hstring( object str, int size_size ) +  { +  IOBuffer *io = THIS; +  size_t len = io_svalue_len(io, Pike_sp-1); +  +  if( size_size < (int)sizeof(INT_TYPE) && +  len > (((size_t)1)<<(8*size_size))-1 ) +  Pike_error("Too long string, need larger size field\n"); +  +  io_add_int( io, len, size_size ); +  io_append_svalue( io, Pike_sp-1 ); +  ref_push_object(io->this); +  } +  +     PIKEFUN IOBuffer add_hstring( string str, int size_size )    { -  +  IOBuffer *io = THIS;    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 ); -  io_append( THIS, str->str, str->len ); -  ref_push_object(Pike_fp->current_object); +  if( size_size < 4 && str->len > ((INT_TYPE)1<<(8*size_size))-1 ) +  Pike_error("Too long string, need larger size field\n"); +  io_add_int( io, str->len, size_size ); +  io_append( io, str->str, str->len ); +  ref_push_object(io->this);    }       /*! @decl IOBuffer 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 IOBuffer add_int( int i, int width )    {    io_add_int( THIS, i, width ); -  +  Pike_sp--;    ref_push_object(Pike_fp->current_object);    }       PIKEFUN IOBuffer add_int( object o, int width )    { -  char *p; -  int extra, len; -  pop_stack(); +  pop_stack(); /* width */    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; -  } -  } -  io_append( THIS, p, width ); +  io_add_bignum( THIS, Pike_sp[-1].u.object, width ); +  pop_stack(); /* o. */    ref_push_object(Pike_fp->current_object);    }       /*! @decl IOBuffer 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. +  *! Equivalent to calling @[add_int] for each integer, but faster, +  *! and if an error occurs the buffer will contain no new +  *! data. Either all or none of the integers will be added. +  *! +  *! Errors can occur if one of the elements in @[integers] is not +  *! actually an integer, if sizeof(integers)*len is bigger than can +  *! be represented in a size_t, or if the buffer cannot grow due to +  *! an out of memory condition.    */    PIKEFUN IOBuffer add_ints( array(int) a, int bpi )    {    int i,l = a->size;    struct svalue *it = a->item;    unsigned char *ptr; -  +  ssize_t n; +  ONERROR e;    IOBuffer *io = THIS;    -  if( bpi < 0 ) Pike_error("Illegal int width\n"); -  io_add_space( io, bpi*l, 0 ); +  io_unwrite_on_error(io, &e);    -  +  if( bpi < 0 ) +  Pike_error("Illegal int width\n"); +  + #if SIZEOF_SIZE_T == 4 +  if( DO_INT32_MUL_OVERFLOW( l, bpi, &n ) ) + #else +  if( DO_INT64_MUL_OVERFLOW( l, bpi, &n ) ) + #endif +  Pike_error("Result size exceeds ssize_t size\n"); +  +  io_add_space( io, n, 0 );    switch( bpi )    {    case 1:    for( i=0; i<l; i++ )    io_append_byte_uc( io, get_small_int(it+i) );    break;    case 2:    for( i=0; i<l; i++ )    io_append_short_uc( io, get_small_int(it+i) );    break;
pike.git/src/modules/_Stdio/buffer.cmod:880:    for( i=0; i<l; i++ )    io_append_int_uc( io, get_small_int(it+i) );    break;    case 3:   #if SIZEOF_INT_TYPE > 4    case 5:    case 6:    case 7:   #endif    for( i=0; i<l; i++ ) -  io_add_int( io, get_small_int(it+i), bpi ); +  io_add_int_uc( io, get_small_int(it+i), bpi );    break;       default:    /* bignums. */ -  for( i=0; i<l; i++ ){ -  push_svalue( it+i ); -  push_int( bpi ); -  f_IOBuffer_add_int(2); -  pop_stack(); +  for( i=0; i<l; i++ ) +  { +  if( LIKELY(TYPEOF(it[i]) == PIKE_T_INT) ) +  io_add_int_uc( io, it[i].u.integer, bpi); +  else if( LIKELY(TYPEOF(it[i]) == PIKE_T_OBJECT) ) +  io_add_bignum( io, it[i].u.object, bpi ); +  else +  Pike_error("Illegal argument.\n");    }    } -  +  io_unset_unwrite_on_error( io, &e ); +  io_trigger_output( io );    pop_n_elems(args);    ref_push_object(io->this);    }       /*! @decl protected int `[](int off)    *!    *! Return the character at the specified offset.    */    PIKEFUN int(0..255) `[]( int off )    flags ID_PROTECTED;
pike.git/src/modules/_Stdio/buffer.cmod:922:    }       /*! @decl protected int `[]=(int off, int char)    *!    *! Set the character at the specified offset to @[char].    */    PIKEFUN int(0..255) `[]=( int off, int val )    flags ID_PROTECTED;    {    IOBuffer *io = THIS; -  io_add_space( io, 0, 0 ); +  io_ensure_malloced( io, 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(0..) _sizeof()    flags ID_PROTECTED;    {    push_int64(io_len(THIS));    }
pike.git/src/modules/_Stdio/buffer.cmod:970:    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) -  *! @decl void set_error_mode(program m) +  /*! @decl IOBuffer set_error_mode(int m) +  *! @decl IOBuffer set_error_mode(program 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 throw an error. If @[m] is a program    *! a clone of it will be thrown on error.    *!    *! This is useful when parsing received data, you do not have to    *! verify that each and every read operation suceeds.    *!
pike.git/src/modules/_Stdio/buffer.cmod:1020:    *! ...    *! 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|program m ) +  PIKEFUN IOBuffer set_error_mode( int|program m )    {    if( TYPEOF(*m) == PIKE_T_INT )    io_set_error_mode( THIS, m->u.integer ? buffer_error_program : 0 );    else    {    io_set_error_mode( THIS, program_from_svalue(m));    }    pop_stack(); -  +  ref_push_object(Pike_fp->current_object);    }       /*! @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()
pike.git/src/modules/_Stdio/buffer.cmod:1085:    *! 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 bytes )    {    LONGEST len; +  IOBuffer *io = THIS;    struct pike_string *s; -  +  ONERROR e;    -  len = io_read_number( THIS, bytes ); +  io_rewind_on_error( io, &e ); +  len = io_read_number( io, bytes );       if (len < 0) {    /* io_avail() in io_read_number() failed. */ -  +  CALL_AND_UNSET_ONERROR(e);    push_int(0);    return;    }       /* NB: We assume that io_avail() in io_read_string() doesn't throw. */ -  s = io_read_string( THIS, len ); +  s = io_read_string( io, len );       if( s ) { -  +  io_unset_rewind_on_error( io, &e );    push_string(s);    } else { -  io_rewind( THIS, bytes ); +  CALL_AND_UNSET_ONERROR(e);    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
pike.git/src/modules/_Stdio/buffer.cmod:1129:    *! 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 bytes, int|void copy )    {    LONGEST len;    int do_copy = 0; -  struct object *o; +  IOBuffer *io = THIS; +  ONERROR e;    -  +  io_rewind_on_error( io, &e ); +     if( copy ) do_copy = copy->u.integer;    pop_n_elems(args);    -  if (!io_avail( THIS, bytes )) { -  push_int(0); +  len = io_read_number( io, bytes ); +  if( len >= 0 && io_avail( io, len ) ) +  { +  push_object( io_read_buffer( io, len, do_copy ) ); +  io_unset_rewind_on_error( io, &e );    return;    } -  -  len = io_read_number( THIS, bytes ); -  -  /* NB: We assume that io_avail() in io_read_buffer() doesn't throw. */ -  o = io_read_buffer( THIS, len, do_copy ); -  -  if( o ) { -  push_object(o); -  } else { -  io_rewind( THIS, bytes ); +  CALL_AND_UNSET_ONERROR(e);    push_int(0); -  +  return; +  /* +  io_read_buffer will not throw here, since io_avail is true. +  */    } -  } +        /*! @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
pike.git/src/modules/_Stdio/buffer.cmod:1180:    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) +  /*! @decl IOBuffer 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(0..) sprintf(mixed ... ignored)    rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)),    tAttr("sprintf_args", tMix), tStr);    {
pike.git/src/modules/_Stdio/buffer.cmod:1202:    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); +  ref_push_object(Pike_fp->current_object);    }       /*! @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.
pike.git/src/modules/_Stdio/buffer.cmod:1361:    }    else    {    io_consume( THIS, num_used );    if( Pike_sp-start > 1 )    f_add(Pike_sp-start);    }    }       +  /* Used in the testsuite. +  +  It would be convenient with a 'PIKEVAR int struct.X' or something. +  */ +  PIKEFUN int(0..) `num_malloc() { RETURN THIS->num_malloc; } +  PIKEFUN int(0..) `num_move() { RETURN THIS->num_move; } +  +     /*! @decl void clear()    *!    *! Clear the buffer.    */    PIKEFUN void clear( )    {    IOBuffer *io = THIS;    io->offset = io->len = 0;    }   
pike.git/src/modules/_Stdio/buffer.cmod:1389:    *! 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->num_malloc++;    io->allocated = io->len;    }    }       /*! @decl int(0..)|int(-1..-1) consume( int(0..) n )    *!    *! Discard the first @[n] bytes from the buffer -  +  *! +  *! Returns -1 on error and the amount of space still left otherwise.    */    PIKEFUN int(-1..) consume( int n )    {    if( !io_avail( THIS, n ) )    push_int(-1);    else    push_int64( io_consume( THIS, n ) );    }      
pike.git/src/modules/_Stdio/buffer.cmod:1461:    PIKEFUN string(0..255) read()    {    struct pike_string * s = io_read_string(THIS, io_len(THIS));    if( s )    RETURN s;    push_undefined();    }       /*! @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 +  *! Read a network 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(0..) read_int( int len )    {    IOBuffer *io = THIS;    struct object *o;   
pike.git/src/modules/_Stdio/buffer.cmod:1490:       if( (o = io_read_bignum(io, len )) )    {    push_object(o);    reduce_stack_top_bignum();    return;    }    push_int(-1);    }    +  /*! @decl array(int) read_ints( int n, int width ) +  *! +  *! Read a list of @[n] network byte order unsigned numbers each of +  *! size @[width]*8 bits, then return it. +  *! +  *! Will return 0 if there is not enough buffer space available +  *! unless error mode is set to throw errors. +  */ +  PIKEFUN array(int(0..)) read_ints( int num, int len ) +  { +  IOBuffer *io = THIS; +  INT_TYPE i; +  struct object *o; +  struct array *a; +  +  pop_n_elems(2); +  +  if( !io_avail_mul( io, num, len ) ) +  { +  push_int(0); +  return; +  } +  +  if( len < SIZEOF_INT_TYPE-1 ) /* will for sure fit. */ +  { +  push_array(a = allocate_array(num)); +  for( i=0;i<num;i++ ) +  a->item[i].u.integer = io_read_number_uc( io, len ); +  return; +  } +  +  for( i=0; i<num; i++ ) +  { +  push_object(io_read_bignum( io, len )); +  reduce_stack_top_bignum(); +  } +  f_aggregate(num); +  } +    /*! @decl string _encode()    *! @decl void _decode(string x)    *!    *! Encode and decode Stdio.IOBuffer objects.    *! Only the buffer data is kept, no other state is saved.    */    PIKEFUN string _encode()    { -  struct pike_string * s = io_read_string(THIS, io_len(THIS)); -  push_string(s); +  push_string(io_read_string(THIS, io_len(THIS)));    }       PIKEFUN void _decode(string(0..255) x)    {    IOBuffer *this = THIS;    if( this->buffer )    Pike_error("Can not initialize twice.\n");    if( x->size_shift )    Pike_error("Can not handle non-8bit data.\n");    this->buffer = (unsigned char*)x->str;
pike.git/src/modules/_Stdio/buffer.cmod:1555:    *! so the buffer creation is fast regardless of the length of the    *! string.    *!    *! However, as an example, if the buffer is created with a 100Gb    *! @[System.Memory] mmap:ed file as the @[contents] and you later on    *! try to modify the buffer using one of the @[add] functions (or    *! @[sprintf] and similar) the old contents @b{will@} be copied.    *!    *! You can use @[read_only()] to avoid accidents.    */ -  PIKEFUN void create( string x ) +  PIKEFUN void create( string|object 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++; +  io_append_svalue( THIS, Pike_sp-1 );    }    -  PIKEFUN void create( object x ) -  flags ID_PROTECTED; -  { -  IOBuffer *this = THIS, *io; -  struct sysmem *s; -  struct string_builder *b; -  -  if( this->buffer ) -  Pike_error("Can not initialize twice. Use add to add more data\n"); -  -  this->malloced = 0; -  -  if( (b = string_buffer( x )) ) -  { -  if( b->s->size_shift ) -  Pike_error("Only string(0..255) supported\n"); -  if( x->refs == 1 ) -  { -  this->source = x; -  this->buffer = (void*)b->s->str; -  this->len = b->s->len; -  this->source->refs++; -  } -  else -  { -  this->buffer = (void*)b->s->str; -  this->len = b->s->len; -  io_add_space(this,0,0); -  } -  } -  else if( (s = system_memory( x )) ) -  { -  this->source = x; -  this->buffer = s->p; -  this->len = s->size; -  this->source->refs++; -  } -  else if( (io = get_storage(x,IOBuffer_program)) ) -  { -  this->buffer = io_read_pointer(io); -  this->len = io_len(io); -  this->sub = x; -  io_lock(io); -  } -  else -  Pike_error("Expected String.Buffer or System.Memory as object argument 1\n"); -  } -  +     PIKEFUN void create( int len )    flags ID_PROTECTED;    {    IOBuffer *this = THIS;    if( this->buffer )    Pike_error("Can not initialize twice.\n"); -  +  if( len <= 0 ) +  this->buffer = xalloc(1); +  else    this->buffer = xalloc(len); -  this->allocated = len; +  this->allocated = MAX(len,1);    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(256-32);
pike.git/src/modules/_Stdio/buffer.cmod:1666:    if( this->output )    free_object(this->output);    if( this->source )    free_object( this->source );    if( this->str )    free_string( this->str );    if( this->error_mode )    free_program( this->error_mode );    if( this->malloced )    free( this->buffer ); +  if( this->stash.ptr ) +  free(this->stash.ptr);    }   }      void init_stdio_buffer(void)   {    INIT    start_new_program();    low_inherit(generic_error_program,0,0,0,0,0);    add_integer_constant( "buffer_error", 1, 0 );    buffer_error_program = end_program();