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

version» Context lines:

pike.git/src/modules/_Stdio/buffer.cmod:117:    Pike_error("Can not modify the buffer right now, "    " there are active subbuffers.\n");    }       static void io_ensure_unlocked(Buffer *io)    {    if( io->locked )    io_was_locked( );    }    +  static void io_trim( Buffer *io ) +  ATTRIBUTE((noinline)); +     static INT_TYPE io_consume( Buffer *io, int num )    {    io->offset += num; -  +  if( UNLIKELY(io->allocated > (io_len(io) * io->max_waste)) ) +  io_trim(io);    return io_len(io);    }       static unsigned char *io_read_pointer(Buffer *io)    {    return io->buffer + io->offset;    }       static int io_is_whitespace( Buffer *io, size_t pos )    {    if( pos > io_len( io ) )    return -1;    switch( io->buffer[io->offset+pos] )    {    SPACECASE8    return 1;    }    return 0;    }    -  +  static void io_trim( Buffer *io ) +  { +  if( io->malloced && (io->offset > 64 || io->len > 64)) +  { +  if( io->offset > 64 && (!io->locked_move && (io->offset > io_len(io)))) +  { +  memmove( io->buffer, io_read_pointer(io), io_len(io) ); +  io->len -= io->offset; +  io->num_move++; +  io->offset = 0; +  } +  if( io->len > 64 && ((io->allocated > (io->len)*(1.0+io->max_waste)))) +  { +  void *new_ptr = xrealloc( io->buffer, io->len ); +  if( !new_ptr ) +  Pike_error(msg_out_of_mem_2, io->len); +  +  io->buffer = new_ptr; +  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 );    }    if( io->source ) free_object( io->source );    if( io->str ) free_string( io->str );    io->source = 0;    io->sub = 0;    io->str = 0;    }    -  static int io_unstash_malloc( Buffer *io ) -  ATTRIBUTE((noclone,noinline)); -  -  static int io_unstash_malloc( Buffer *io ) -  { -  if( LIKELY(!io->stash.ptr) ) -  return 0; -  -  if( io->stash.len >= io_len( io ) ) -  { -  if( io_len(io) ) -  { -  /* do not count this one, since it comes from add and would -  * have been a copy normally. -  */ -  memcpy( io->stash.ptr, io_read_pointer( io ), io_len(io) ); -  } -  io->buffer = io->stash.ptr; -  io->malloced = 1; -  io->len = io_len(io); -  io->offset = 0; -  io->allocated = io->stash.len; -  io_unlink_external_storage(io); -  } -  else -  free( io->stash.ptr ); -  io->stash.ptr = 0; -  io->stash.len = 0; -  return io->malloced; -  } -  -  static void io_stash_malloc( Buffer *io ) -  { -  if( io->malloced ) -  { -  io->stash.ptr = io->buffer; -  io->stash.len = io->allocated; -  io->malloced = 0; -  io->buffer = 0; -  io->offset = 0; -  io->len = 0; -  } -  } -  +     static void io_ensure_malloced( Buffer *io, size_t bytes )    {    if( UNLIKELY(!io->malloced) )    {    /* convert to malloced buffer from a shared one. */ -  if( !io_unstash_malloc(io) ) -  { +     unsigned char *old = io->buffer;       bytes += io->len;       if (bytes < io->len || bytes + 100 < bytes)    Pike_error(msg_out_of_mem_2, bytes + 100);       bytes += 100;       io->buffer = xalloc( bytes );    io->malloced = 1;    io->allocated = bytes;    io->num_malloc++;    memcpy( io->buffer, old, io->len );    io_unlink_external_storage(io);    }    } -  } +        static unsigned char *io_add_space_do_something( Buffer *io, size_t bytes, int force )    ATTRIBUTE((noclone,noinline));    static unsigned char *io_add_space_do_something( Buffer *io, size_t bytes, int force )    {    if( bytes && io->len+bytes < io->len )    Pike_error("Too large buffer, can not have more than %lu bytes",    (size_t)-1);      
pike.git/src/modules/_Stdio/buffer.cmod:246:    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; -  -  * And this one: -  -  || (io_len(io)+bytes < io->allocated -  && io->len+bytes >= io->allocated) -  +     */ -  +     if( LIKELY(!io->locked_move) )    { -  if( UNLIKELY((force && io->offset) || (io->offset > io->len>>1)) ) +  if( UNLIKELY((force && io->offset) || (io->offset > io->len)) )    { -  +  /* 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. +  */    memmove( io->buffer, io_read_pointer(io), io_len(io) );    io->num_move++;    io->len -= io->offset;    io->offset = 0;    }    }       if( UNLIKELY(io->len + bytes > io->allocated) )    { -  size_t new_len; -  void *new_ptr; +  /* Actually grow the buffer. */ +  size_t growth = +  (io->allocated>>1) + +  (io->allocated>>3);/* io->allocated * 0.625 */    - #if SIZEOF_CHARP == 8 -  new_len = round_up64(io->len+bytes); - #else -  new_len = round_up32(io->len+bytes); - #endif -  if (!new_len) new_len = io->len + bytes; +  if( growth < bytes ) +  growth = bytes + (bytes>>1);    -  new_ptr = xrealloc( io->buffer, new_len ); +  if( io->allocated + growth < io->allocated ) +  { +  growth = bytes+1; +  if( io->allocated + growth < io->allocated ) +  Pike_error("Overflow in buffer size calculations\n"); +  } +  void *new_ptr = xrealloc( io->buffer, io->allocated + growth );    if( !new_ptr ) -  Pike_error(msg_out_of_mem_2, new_len ); +  Pike_error(msg_out_of_mem_2, io->allocated+growth );    io->buffer = new_ptr;    io->num_malloc++; -  io->allocated = new_len; +  io->allocated += growth;    }    return io->buffer+io->len;    }       static unsigned char *io_add_space( Buffer *io, size_t bytes, int force )    {    if( io->len == io->offset )    io->offset = io->len = 0;    if( !force && io->malloced && !io->locked && io->len+bytes < io->allocated &&    (!bytes || io->len+bytes > io->len))
pike.git/src/modules/_Stdio/buffer.cmod:963:    int once = 0;       if( _nbytes && (SUBTYPEOF(*_nbytes) == NUMBER_NUMBER) ) {    nbytes = _nbytes->u.integer;    if (!nbytes) RETURN 0;    }       if( _once )    once = _once->u.integer;    -  +     if( (fd = get_storage( f, file_program )) )    {    while( 1 )    {    unsigned char *ptr = io_add_space( io, 4096, 0 );    int res;       res = fd_read( fd->box.fd, ptr, MINIMUM(4096,nbytes) );       if( res == -1 && errno == EINTR )
pike.git/src/modules/_Stdio/buffer.cmod:1215:    ref_push_string( tmp );    push_int( 256 );    push_object( clone_object( get_auto_bignum_program(), 2 ) );    if( tmp->str[0]&0x80 )    o_xor();    free_string( tmp );    }       PIKEFUN int(0..) _size_object( )    { -  RETURN THIS->malloced ? THIS->allocated : THIS->stash.len; +  RETURN THIS->malloced ? THIS->allocated : 0;    }       /*! @decl Buffer add_padding( int(0..) nbytes, int(0..255)|void byte )    *!    *! Add @[nbytes] bytes of padding, if @[byte] is not specified the    *! area will be filled with 0's, otherwise the specified byte will    *! be repeated.    */    PIKEFUN Buffer add_padding( int(0..) nbytes, int|void _byte )    {
pike.git/src/modules/_Stdio/buffer.cmod:1275:    *! @seealso    *! @[sprintf], @[add_int8], @[add_int16], @[add_int32], @[add_int]    *! and    *! @[add_hstring]    */    PIKEFUN Buffer add( object|string|int|array(object|string|int) ... argp)    {    int i;    Buffer *io = THIS;    -  if( args == 1 && !io_len( io ) ) -  io_stash_malloc( io ); -  +     for(i=0; i<args; i++ )    io_append_svalue( io, argp+i );       pop_stack();    ref_push_object(io->this);    }       /*! @decl Buffer add_int8( int(0..255) )    *! Adds a single byte to the buffer.    */
pike.git/src/modules/_Stdio/buffer.cmod:2078:    /*! @decl void clear()    *!    *! Clear the buffer.    */    PIKEFUN void clear( )    {    Buffer *io = THIS;    io->offset = io->len = 0;    }    +  /*! @decl void set_max_waste(float factor) +  *! +  *! Configure how much free space should be allowed, at most, as a +  *! factor of the current buffer size. +  *! +  *! The default is 0.5, leaving at most half the buffer as waste. +  *! +  */ +  PIKEFUN void set_max_waste(float howmuch) +  { +  Buffer *io = THIS; +  io->max_waste = howmuch; +  io_add_space( io, 0, 1 ); +  io_consume( io, 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( )    {    Buffer *io = THIS;    void *new_ptr;       io_add_space( io, 0, 1 ); -  if( io->allocated > io->len+32 ) -  { -  new_ptr = xrealloc( io->buffer, io->len ); -  if (new_ptr) -  Pike_error(msg_out_of_mem_2, io->len); -  io->buffer = new_ptr; -  io->num_malloc++; -  io->allocated = io->len; +  io_trim(io);    } -  } +        /*! @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 )    {    Pike_sp--;
pike.git/src/modules/_Stdio/buffer.cmod:2536:    this->allocated = MAXIMUM(len,1);    this->malloced = 1;    }    else    io_append_svalue( THIS, x );    }       INIT {    Buffer *this = THIS;    memset( this, 0, sizeof(Buffer)); +  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 )    free( this->buffer ); -  free(this->stash.ptr); +     }         /*! @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    *! at when the object was created either when it is released (when it