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

version» Context lines:

pike.git/src/modules/_Stdio/buffer.cmod:113:    }       static void io_lock( Buffer *io )    {    io->locked++;    }       static void io_was_locked( )    ATTRIBUTE((noclone,noinline));    -  static void io_was_locked( ) -  { -  Pike_error("Cannot modify the buffer right now, " -  " there are active subbuffers.\n"); -  } -  -  static void io_ensure_unlocked(Buffer *io) -  { -  if( io->locked ) -  io_was_locked( ); -  } -  +     PMOD_EXPORT void io_trim( Buffer *io )    ATTRIBUTE((noinline));       static void io_trim_waste( Buffer *io )    {    if( UNLIKELY(io->allocated > (io_len(io) * (1.0+io->max_waste))) )    io_trim(io);    }       static int io_is_whitespace( Buffer *io, size_t pos )
pike.git/src/modules/_Stdio/buffer.cmod:159:    {    memmove( io->buffer, io_read_pointer(io), io_len(io) );    io->len -= io->offset;    io->num_move++;    io->offset = 0;    }    }       PMOD_EXPORT void io_trim( Buffer *io )    { -  if( io->malloced && (io->offset > 64 || io->len > 64) && !io->locked) +  if( io->allocated && (io->offset > 64 || io->len > 64) && !io->locked)    {    if( io->offset > 64 && io->offset > io_len(io) )    io_discard_bufstart(io);       if( io->len > 64 && ((io->allocated > (io->len)*(1.0+io->max_waste))))    {    io->buffer = xrealloc( io->buffer, io->len );    io->num_malloc++;    io->allocated = io->len;    }    }    }       static void io_unlink_external_storage( Buffer *io )    ATTRIBUTE((noclone,noinline));       static void io_unlink_external_storage( Buffer *io )    { -  if( io->sub ) { -  io_unlock( get_storage(io->sub,Buffer_program ) ); -  free_object( io->sub ); +  switch (io->sourcetype) { +  case PIKE_T_STRING: +  free_string(io->source.str); +  break; +  case PIKE_T_MIXED: { +  Buffer *parent = io->source.parent; +  io_unlock(parent); +  free_object(parent->this); +  break;    } -  if( io->source ) free_object( io->source ); -  if( io->str ) free_string( io->str ); -  io->source = 0; -  io->sub = 0; -  io->str = 0; +  case PIKE_T_OBJECT: +  free_object(io->source.obj); +  break;    } -  +  io->sourcetype = 0; +  }    -  PMOD_EXPORT void io_ensure_malloced( Buffer *io, size_t bytes ) +  PMOD_EXPORT void io_ensure_malloced( Buffer *io, size_t headroom )    { -  if( UNLIKELY(!io->malloced) ) +  if( UNLIKELY(!io->allocated) )    {    /* convert to malloced buffer from a shared one. */ -  unsigned char *old = io->buffer; +  unsigned char *old = io_read_pointer(io);    -  bytes += io->len; +  headroom += io->len - io->offset;    -  if (bytes < io->len || bytes + 100 < bytes) -  Pike_error(msg_out_of_mem_2, bytes + 100); +  if (headroom < io->len - io->offset) +  Pike_error(msg_out_of_mem_2, headroom);    -  bytes += 100; -  -  io->buffer = xalloc( bytes ); -  io->malloced = 1; -  io->allocated = bytes; +  io->buffer = xalloc( headroom ); +  io->allocated = headroom; +  io->len -= io->offset; +  io->offset = 0;    io->num_malloc++;    memcpy( io->buffer, old, io->len );    io_unlink_external_storage(io);    }    }    -  +  static struct object * +  io_read_buffer( Buffer *io, size_t len, int do_copy, size_t headroom ) +  ATTRIBUTE((noclone,noinline)); +     PMOD_EXPORT unsigned char *io_add_space_do_something( Buffer *io, size_t bytes, int force )    ATTRIBUTE((noclone,noinline));    PMOD_EXPORT unsigned char *io_add_space_do_something( Buffer *io, size_t bytes, int force )    { -  if( bytes && io->len+bytes < io->len ) +  size_t growth = +  (io->allocated>>1) + +  (io->allocated>>3);/* io->allocated * 0.625 */ +  +  if( growth < 100 ) +  growth = 100; /* minimum growth in bytes */ +  if( growth < bytes ) +  growth = bytes + (bytes>>1); +  +  if( io->allocated + growth < io->allocated ) +  { +  growth = bytes; +  if( io->allocated + growth < io->allocated )    Pike_error("Buffer too large, can not have more than %lu bytes.",    (size_t)-1); -  +  }    -  io_ensure_malloced(io, bytes); +  io_ensure_malloced(io, growth);       /*    * 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.    *    */
pike.git/src/modules/_Stdio/buffer.cmod:245:    /* more than 50% of the buffer is available before the read pointer,    * and we can discard that data. Move the data to the beginning, making    * room for more data.    */    io_discard_bufstart(io);    }       if( UNLIKELY(io->len + bytes > io->allocated) )    {    /* Actually grow the buffer. */ -  size_t growth = -  (io->allocated>>1) + -  (io->allocated>>3);/* io->allocated * 0.625 */ +     -  io_ensure_unlocked(io); -  -  if( growth < bytes ) -  growth = bytes + (bytes>>1); -  -  if( io->allocated + growth < io->allocated ) -  { -  growth = bytes+1; -  if( io->allocated + growth < io->allocated ) -  Pike_error("Overflow in buffer size calculations.\n"); -  } +  if (UNLIKELY(io->locked)) { +  struct object *oldobj = io->this; +  struct object *newobj +  = io_read_buffer(io, io->len - io->offset, 1, growth); +  Buffer *newbuffer = get_storage(newobj, Buffer_program); +  unsigned char*cbuf = newbuffer->buffer; +  int locks = io->locked - (io->max_waste < 0); +  oldobj->refs -= locks; /* Swap refs too */ +  newobj->refs += locks; +  /* Orphaned members in the copied (parked) buffer: +  * locked +  * allocated +  * offset +  * len +  */ +  io->locked = 0; +  newbuffer->buffer = io->buffer; +  io->buffer = cbuf; +  io->len = newbuffer->len; +  io->offset = 0; +  io->allocated = newbuffer->allocated; +  io->this = newobj; +  newbuffer->this = oldobj; +  io->max_waste = newbuffer->max_waste; +  } else {    io->buffer = xrealloc( io->buffer, io->allocated + growth );    io->num_malloc++;    io->allocated += growth;    } -  +  }    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.    *!    *! The default implementation simply returns @expr{0@} (zero).    *!    *! Override this function to change the behavior.
pike.git/src/modules/_Stdio/buffer.cmod:547:    return make_shared_binary_string(NULL,0);       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( Buffer *io, size_t len, int do_copy ) +  static struct object * +  io_read_buffer( Buffer *io, size_t len, int do_copy, size_t headroom )    {    struct object *b;    Buffer *to; -  if( !io_avail(io,len)) +  +  if (UNLIKELY(!io_avail(io, len)))    return NULL;       b = fast_clone_object( Buffer_program ); -  to = get_storage(b,Buffer_program); +  to = get_storage(b, Buffer_program);    -  io_lock( io ); -  +     to->buffer = io_read_pointer(io);    to->len = len; -  to->sub = Pike_fp->current_object; -  add_ref(to->sub); +     io_consume( io, len );    -  if( do_copy ) -  io_ensure_malloced( to, 0 ); +  if (UNLIKELY(do_copy)) +  io_ensure_malloced( to, headroom ); +  else { +  Buffer *parent = io; +  switch (to->sourcetype = io->sourcetype) { +  case PIKE_T_STRING: +  add_ref(to->source.str = io->source.str); +  break; +  case PIKE_T_MIXED: +  parent = io->source.parent; +  default: +  to->sourcetype = PIKE_T_MIXED; +  io_lock(to->source.parent = parent); +  add_ref(parent->this); +  break; +  case PIKE_T_OBJECT: +  add_ref(to->source.obj = io->source.obj); +  break; +  } +  }       return b;    }       static int io_read_byte_uc( Buffer *io )    {    return io->buffer[io->offset++];    }       static INT_TYPE io_read_number_uc( Buffer *io, size_t len )
pike.git/src/modules/_Stdio/buffer.cmod:826:    switch( TYPEOF(*p) )    {    case PIKE_T_STRING:    {    struct pike_string *s = p->u.string;    if( !s->len ) return;    if( s->size_shift ) Pike_error("Buffer only handles 8bit data.\n");    if( !io->buffer )    {   #ifdef PIKE_DEBUG -  if (io->str) Pike_fatal("Buffer with string but NULL buffer.\n"); +  if (io->sourcetype) Pike_fatal("Buffer with string and sourcetype not NULL.\n");   #endif -  io->str = s; +  io->sourcetype = PIKE_T_STRING; +  add_ref(io->source.str = s);    io->buffer = (unsigned char*)s->str;    io->len = s->len; -  add_ref(s); +     io_trigger_output( io );    }    else    io_append( io, s->str, s->len );    }    break;    case PIKE_T_ARRAY:    {    struct array *argp = p->u.array;    INT_TYPE i;
pike.git/src/modules/_Stdio/buffer.cmod:865:    size_t len;    void *ptr;    struct sysmem *s;    enum memobj_type t = get_memory_object_memory( p->u.object, &ptr, &len, NULL );       if( !io->buffer && t==MEMOBJ_SYSTEM_MEMORY )    {    io->buffer = ptr;    io->len = len;    -  io->source = p->u.object; -  add_ref(io->source); + #ifdef PIKE_DEBUG +  if (io->sourcetype) Pike_fatal("Buffer with object and sourcetype not NULL.\n"); + #endif +  io->sourcetype = PIKE_T_OBJECT; +  add_ref(io->source.obj = p->u.object);    return;    }    if( t != MEMOBJ_NONE )    io_append( io, ptr, len );    else    Pike_error("Unsupported argument type.\n");    }    break;    case PIKE_T_INT:    {
pike.git/src/modules/_Stdio/buffer.cmod:1152:    push_int( 256 );    push_object( clone_object( bignum_program, 2 ) );    if( tmp->str[0]&0x80 )    o_xor();    free_string( tmp );    }       PIKEFUN int(0..) _size_object( )    {    Buffer *io = THIS; -  if (io->malloced) +  if (io->allocated)    {    push_ulongest(THIS->allocated);    }    else    {    push_int(0);    }    }       /*! @decl Buffer add_padding( int(0..) nbytes, int(0..255)|void byte )
pike.git/src/modules/_Stdio/buffer.cmod:1682:       /*! @decl object lock()    *!    *! Makes this buffer read only until the returned object is released.    *!    *! @note    *! This currently simply returns a 0-length subbuffer.    */    PIKEFUN Buffer lock()    { -  push_object( io_read_buffer( THIS, 0, 0 ) ); +  push_object( io_read_buffer( THIS, 0, 0, 0 ) );    }       PIKEFUN string(8bit) _sprintf(int o, mapping|void UNUSED)    flags ID_PROTECTED;    {    size_t bytes;    pop_n_elems(args-1);    Pike_sp--;    switch( o )    {
pike.git/src/modules/_Stdio/buffer.cmod:1705:    push_static_text("%O(%d bytes, read=[..%d] data=[%d..%d] free=[%d..%d] %s%s)");    ref_push_program(Pike_fp->current_object->prog);    /* 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_static_text( (THIS->str ? "string" : THIS->malloced ? "allocated" : "subbuffer" ) ); +  switch (THIS->sourcetype) { +  case PIKE_T_STRING: +  push_static_text("string"); +  break; +  case PIKE_T_OBJECT: +  push_static_text("object"); +  break; +  case PIKE_T_MIXED: +  push_static_text("subbuffer"); +  break; +  default: +  push_static_text("allocated"); +  break; +  }    if( THIS->locked )    push_static_text(" (read only)");    else    push_static_text("");    f_sprintf(10);    }    break;       case 's':    bytes = io_len(THIS);
pike.git/src/modules/_Stdio/buffer.cmod:2058:    ONERROR e;       io_rewind_on_error( io, &e );       if( copy ) do_copy = copy->u.integer;    Pike_sp-=args;       len = io_read_number( io, bytes, 1 );    if( len >= 0 && io_avail( io, len ) )    { -  push_object( io_read_buffer( io, len, do_copy ) ); +  push_object( io_read_buffer( io, len, do_copy, 0 ) );    io_unset_rewind_on_error( io, &e );    return;    }    CALL_AND_UNSET_ONERROR(e);    push_int(0);    }       /*! @decl Buffer read_buffer( int n )    *! @decl Buffer read_buffer( int n, bool copy )    *!    *! Same as @[read], but returns the result as an Buffer.    *!    *! 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. +  *! As long as the subbuffer exists the main buffer is locked in memory.    *!    *! 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 Buffer read_buffer( int bytes, int|void copy )    {    int do_copy = 0;    struct object *o;    if( copy )    do_copy = copy->u.integer;    Pike_sp-=args; -  if( (o = io_read_buffer( THIS, bytes, do_copy )) ) +  if( (o = io_read_buffer( THIS, bytes, do_copy, 0 )) )    push_object(o);    else    push_int(0);    }       /*! @decl Buffer 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
pike.git/src/modules/_Stdio/buffer.cmod:2673:    }       PIKEFUN void _decode(string(8bit) x)    {    Buffer *this = THIS;    if( this->buffer )    Pike_error("Cannot initialise twice.\n");    if( x->size_shift )    Pike_error("Cannot handle non-8bit data.\n");    this->buffer = (unsigned char*)x->str; +  this->sourcetype = PIKE_T_STRING;    this->len = x->len; -  this->malloced = 0; -  this->str = x; +  this->source.str = x;    add_ref(x);    }       /*! @decl void read_only()    *! -  *! Make the buffer permanently read only. +  *! This makes the existing data in the buffer permanently read only. +  *!    *! @note    *! You can use lock() to do this temporarily.    */    PIKEFUN void read_only()    { -  io_lock( THIS ); +  Buffer *io = THIS; +  if (!(io->max_waste < 0)) { +  io->max_waste = -1.0; /* Marker to distinguish read-only buffers */ +  io_lock(io);    } -  +  }          static struct object* io_create_rewind_key( Buffer *io, int how );       /*! @decl RewindKey rewind_on_error()    *! @decl RewindKey rewind_key()    *!    *! These functions are very similar. The @[rewind_on_error] variant    *! will create an object that, when it goes out of scope without    *! having been destructed explicitly, will cause the buffer to
pike.git/src/modules/_Stdio/buffer.cmod:2799:    PIKEFUN void create( int|void|string(8bit)|object|array x )    flags ID_PROTECTED;    {    Buffer *this = THIS;    if( this->buffer )    Pike_error("Cannot initialise twice.\n");    if( !x )    {    this->buffer = xalloc(256-32);    this->allocated = 256-32; -  this->malloced = 1; +     }    else if( TYPEOF(*x) == PIKE_T_INT )    {    INT_TYPE len = x->u.integer;    if( len <= 0 )    this->buffer = xalloc(1);    else    this->buffer = xalloc(len);    this->allocated = MAXIMUM(len,1); -  this->malloced = 1; +     pop_stack();    }    else    {    io_append_svalue( THIS, x );    pop_stack();    }    }       INIT {    Buffer *this = THIS;    this->max_waste = 0.615;    this->this = Pike_fp->current_object;    }       EXIT {    Buffer *this = THIS;    io_unlink_external_storage( this );    if( this->error_mode )    free_program( this->error_mode ); -  if( this->malloced ) +  if( this->allocated )    free( this->buffer );    }         /*! @class RewindKey    *!    *! The return value of @[Buffer.rewind_on_error()] and    *! @[Buffer.rewind_key()]    *!    *! This object will cause the buffer to unwind to the position it was