Branch: Tag:

2014-09-08

2014-09-08 12:30:17 by Per Hedbor <ph@opera.com>

Some IOBuffer work

o Made all (I think) functions atomic. Either they work or they do
nothing (except throw an error or return 0).

o One minor optimization to avoid copies when strings/system.memory
object etc are added to an empty buffer. Unless you add more data
the buffer is just moved to point to the object that was just added.
The malloced buffer is still kept around, however.

o Added a very very basic testsuite for IOBuffer. More to come.

23:   #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)
37:    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 )
56:    Pike_sp--;    }    return get_storage( o, sbuf_program ); - }; + }       -  + static struct program *buffer_error_program; +    /*! @module Stdio    */ - static struct program *buffer_error_program; +       /*! @class IOBuffer    *!
141:    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 ) );
162:    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;
235:    }       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 )    {
253:    }    }    -  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 )
292:    }    }    -  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 );
306:    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 )    {
319:    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 );
339:    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);
368:    io_consume( io, len );       if( do_copy ) -  io_add_space( to, 0, 0 );/* copies data. */ +  io_ensure_malloced( to, 0 );       return b;    }
378:    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 )    {
413:    }    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 )    {
433:    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 );    }
478:    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 )
487:    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
611:    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);
678:    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);    }   
782:    }       /*! @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 )
818:    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:
887:    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);    }
929:    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;   
944:    }    }    -  +     /*! @decl int _sizeof()    *!    *! Returns the buffer size, in bytes.
977:    }       -  /*! @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].    *!
1027:    *! }    *! @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 );
1036:    io_set_error_mode( THIS, program_from_svalue(m));    }    pop_stack(); +  ref_push_object(Pike_fp->current_object);    }       /*! @decl object lock()
1092:    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);    }    }
1136:    {    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 )
1187:    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.    *!
1209:    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)
1368:    }       +  /* 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.
1396:    if( io->allocated > io->len+32 )    {    io->buffer = xrealloc( io->buffer, io->len ); +  io->num_malloc++;    io->allocated = io->len;    }    }
1403:    /*! @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 )    {
1468:       /*! @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
1497:    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)    *!
1505:    */    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)
1562:    *!    *! 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;    }   
1673:    free_program( this->error_mode );    if( this->malloced )    free( this->buffer ); +  if( this->stash.ptr ) +  free(this->stash.ptr);    }   }