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

version» Context lines:

pike.git/src/modules/_Stdio/buffer.cmod:157:    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( force || (io->offset > io->len>>1) ) /* more than half used up. */ +  if( io->len == io->offset ) +  io->offset = io->len = 0; +  +  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)) )    { -  memmove( io->buffer, io->buffer+io->offset, io->len-io->offset ); +  memmove( io->buffer, io_read_pointer(io), io_len(io) );    io->len -= io->offset;    io->offset = 0;    } -  +     if( io->len + bytes > io->allocated )    {    size_t new_len = io->allocated;    do    new_len = ((new_len+32)*2)-32;    while( new_len < io->len + bytes );    io->buffer = xrealloc( io->buffer, new_len );    io->allocated = new_len;    }    return io->buffer+io->len;    }    -  static void io_range_error( IOBuffer *io, int howmuch ) +  +  /*! @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 returnns 0. +  *! +  *! Override this function to change the behaviour +  *! +  *! The argument @[howmuch] indicates how much data is needed: +  *! +  *! @dl +  *! @dt int(1..) howmuch +  *! Need @[howmuch] bytes more +  *! @dt int(0..0) howmuch +  *! The amount of data needed is not certain. +  *! This most often happens when @[sscanf] or @[read_json] is used +  *! @dt int(..-1) howmuch=... +  *! Tried to @[unread] X bytes. There is usually no way to satisfy +  *! the requested range. +  *! +  *! The only supported way is to extract the data from the buffer, +  *! add the requested amount of "go backbuffer", add the data +  *! back, and forward -@[howmuch] bytes. +  *! @enddt +  *! +  *! @returns +  *! +  *! @[true] if the operation should be retried, @[false] otherwise. +  *! +  *! Do not return true unless you have added data to the buffer, +  *! doing so could result in an infinite loop (since no data is +  *! added, the range_error will be called again immediately). +  */ +  PIKEFUN int(0..1) range_error( int howmuch ) +  flags ID_PROTECTED;    { -  /* throw error if so desired. */ +  /* 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 ) +  {    if( io->error_mode )    { -  +  if( howmuch ) +  {    push_text("Trying to read %d outside allowed range\n");    push_int(howmuch);    f_sprintf(2); -  push_object(clone_object(io->error_mode, 1)); +  } +  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 *range_error_s; +  static int io_range_error( IOBuffer *io, int howmuch ) +  { +  int res; +  struct svalue *osp = Pike_sp; +  +  if(!range_error_s) +  range_error_s = make_shared_string( "range_error" ); +  +  push_int( howmuch ); +  apply_low(io->this, +  really_low_find_shared_string_identifier +  (range_error_s, io->this->prog, SEE_PROTECTED), +  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 )    {    if( len < 0 || len + io->offset > io->len )    { -  io_range_error( 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 size_t io_append( IOBuffer *io, void *p, int bytes )    {    memcpy( io_add_space( io, bytes, 0 ), p, bytes );    io->len += bytes;    return io_len(io);
pike.git/src/modules/_Stdio/buffer.cmod:223:       static struct pike_string *io_read_string( IOBuffer *io, size_t len )    {    struct pike_string *s;    if( !io_avail(io,len))    return NULL;       if( len > 0x7fffffff )    Pike_error("This region is too large to convert to a string.\n");    s = begin_shared_string( len ); -  io_read( io, s->str, s->len ); +  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))    return NULL;   
pike.git/src/modules/_Stdio/buffer.cmod:327:    x[bytes] = i&255;    i>>=8;    }    return io_len( io );    }       static size_t io_rewind( IOBuffer *io, INT_TYPE n )    {    if( n < 0 || (io->offset < (unsigned)n) )    { -  io_range_error(io,-n); +  if( n < 0 ) +  io_range_error_throw( io, 0 ); +  else if( io_range_error(io,-(long)(n-io->offset)) ) +  return io_rewind( io, n );    return -1;    }    io->offset -= n;    return io->offset;    }       /* pike functions */      #undef THIS   #define THIS (&(((struct IOBuffer_struct *)Pike_fp->current_storage)->b))    -  /*! @decl int input_from( Stdio.Stream f ) +  /*! @decl int(0..) input_from( Stdio.Stream f )    *!    *! Read data from @[f] into this buffer. -  +  *! +  *! 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. +  *! +  *! It is designed to only be used in non-blocking IO mode.    */    PIKEFUN int(0..) input_from( object f )    {    IOBuffer *io = THIS;    size_t sz = io_len( io );    size_t bread = 0;    struct my_file *fd;       if( (fd = get_storage( f, file_program )) )    {    while( 1 )    {    unsigned char *ptr = io_add_space( io, 4096, 0 );    int res;    -  -  io_lock(io); -  { -  THREADS_ALLOW(); +     res = fd_read( fd->box.fd, ptr, 4096 ); -  THREADS_DISALLOW(); -  } -  io_unlock(io); +        if( res == -1 && errno == EINTR )    continue;       if( res <= 0 )    break;       io->len += res;    bread += res;    if( res != 4096 )
pike.git/src/modules/_Stdio/buffer.cmod:397:    break;    if( Pike_sp[-1].u.string->size_shift )    Pike_error("Can not handle non-8bit data\n");    io_append( io, Pike_sp[-1].u.string->str, Pike_sp[-1].u.string->len );    pop_stack();    }    }    RETURN bread;    }    -  /*! @decl int output_to( Stdio.Strem f ) +  /*! @decl int output_to( Stdio.Stream f )    *!    *! Write data from the buffer to the indicated file.    *!    *! Will return the number of bytes that were successfully written.    */    PIKEFUN int(0..) output_to( object f )    {    IOBuffer *io = THIS;    size_t sz = io_len( io );    size_t written = 0;    struct my_file *fd;    INT_TYPE *wr = &Pike_sp->u.integer;       if( (fd = get_storage( f, file_program )) )    {    /* lock this object. */    io_lock(io);    -  THREADS_ALLOW(); +  // THREADS_ALLOW();    while( sz > written )    {    size_t rd = MIN(sz-written,4096);    unsigned char *ptr = io_read_pointer( io );    int res;       res = fd_write( fd->box.fd, ptr, rd );    if( res == -1 && errno == EINTR )    continue;    if( res <= 0 )    break;    io_consume( io, res );    written += res;    } -  THREADS_DISALLOW(); -  io_unlock(io); +  // THREADS_DISALLOW(); +  // io_unlock(io);    }    else    {    /* some other object. Just call write */    while( sz > written )    {    size_t rd = MIN(sz-written,4096);    struct pike_string *s = io_read_string( io, rd );    if( !s )    break;
pike.git/src/modules/_Stdio/buffer.cmod:476:    /*! @decl void add( System.Memory|Stdio.IOBuffer|String.Buffer|string(8bit)|int(8bit) ... data )    *!    *! Add the items in data to the end of the buffer.    *! Integers are assumed to be 8bit numbers (characters)    *!    *! @see also    *! @[sprintf], @[add_int8], @[add_int16], @[add_int32], @[add_int]    *! and    *! @[add_hstring]    */ -  PIKEFUN int(0..) add( object|string|int ... argp) +  PIKEFUN IOBuffer add( object|string|int ... argp)    {    int i;    IOBuffer *io = THIS;    for(i=0; i<args; i++ )    {    if( TYPEOF(argp[i]) == PIKE_T_STRING )    {    struct pike_string *s = argp[i].u.string;    if( s->size_shift ) Pike_error("IOBuffer only handles 8bit data\n");    io_append( io, s->str, s->len );
pike.git/src/modules/_Stdio/buffer.cmod:519:    }    else    Pike_error("Unsupported object type\n");    }    else    {    unsigned char a = argp[i].u.integer & 255;    io_append( io, &a, 1 );    }    } -  RETURN io_len(io); +  ref_push_object(io->this);    }       /*! @decl int add_byte( int(0..255) )    *! @decl int add_int8( int(0..255) )    *! Adds a single byte to the buffer.    */ -  PIKEFUN int(0..) add_byte( int i ) +  PIKEFUN IOBuffer add_byte( int i )    {    unsigned char a = i&255; -  RETURN io_append( THIS, &a, 1 ); +  io_append( THIS, &a, 1 ); +  ref_push_object(Pike_fp->current_object);    }    -  PIKEFUN int(0..) add_int8( int i ) +  PIKEFUN IOBuffer add_int8( int i )    {    unsigned char a = i&255; -  RETURN io_append( THIS, &a, 1 ); +  io_append( THIS, &a, 1 ); +  ref_push_object(Pike_fp->current_object);    }       /*! @decl int add_int16( int(0..65535) )    *! @decl int add_short( int(0..65535) )    *!    *! Add a 16-bit network byte order value to the buffer    */ -  PIKEFUN int(0..) add_int16( int i ) +  PIKEFUN IOBuffer add_int16( int i )    {    unsigned short a = htons((i&65535)); -  push_int(io_append( THIS, &a, 2 )); +  io_append( THIS, &a, 2 ); +  ref_push_object(Pike_fp->current_object);    }    -  PIKEFUN int(0..) add_short( int i ) +  PIKEFUN IOBuffer add_short( int i )    {    unsigned short a = htons((i&65535)); -  RETURN io_append( THIS, &a, 2 ); +  io_append( THIS, &a, 2 ); +  ref_push_object(Pike_fp->current_object);    }       /*! @decl int add_int32( int i )    *! Adds a 32 bit network byte order value to the buffer    */    PIKEFUN int(0..) add_int32( int i )    {    INT32 a = htonl(i); -  push_int(io_append( THIS, &a, 4 )); +  io_append( THIS, &a, 4 ); +  ref_push_object(Pike_fp->current_object);    }       /*! @decl void add_hstring( string(0..255) data, int size_size )    *!    *! Adds length/data for @[data] to the buffer.    *!    *! This is identical to @[sprintf("%"+size_size+"H",data)] but    *! significantly faster.    *!    *! @[size_size] must be less than Int.NATIVE_MAX.    */ -  PIKEFUN int(0..) add_hstring( string str, int size_size ) +  PIKEFUN IOBuffer add_hstring( string str, int size_size )    {    if( str->size_shift )    Pike_error("Only 8bit is supported\n");       /* We know this is safe for len>=4, since pike strings are at    * most 0x7ffffff bytes long.    */    if( size_size < 4 &&    str->len > (1<<(8*(size_size<0?-size_size:size_size)))-1 )    Pike_error("Too long string\n");       io_add_int( THIS, str->len, size_size ); -  push_int(io_append( THIS, str->str, str->len )); +  io_append( THIS, str->str, str->len ); +  ref_push_object(Pike_fp->current_object);    }    -  /*! @decl int add_int( int i, int(0..) width ) +  /*! @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 int(0..) add_int( int i, int width ) +  PIKEFUN IOBuffer add_int( int i, int width )    { -  RETURN io_add_int( THIS, i, width ); +  io_add_int( THIS, i, width ); +  ref_push_object(Pike_fp->current_object);    }    -  PIKEFUN int(0..) add_int( object o, int width ) +  PIKEFUN IOBuffer add_int( object o, int width )    {    char *p;    int extra, len;    pop_stack();    convert_stack_top_to_bignum();    /* we now know for sure it's a bignum. */    push_int(256);    apply( Pike_sp[-2].u.object, "digits", 1 );    p = Pike_sp[-1].u.string->str;    len = Pike_sp[-1].u.string->len;
pike.git/src/modules/_Stdio/buffer.cmod:628:    {    INT_TYPE null = 0;       while( len < width )    {    int a = MIN(width-len,sizeof(INT_TYPE));    io_append( THIS, &null, a );    width-=a;    }    } -  RETURN io_append( THIS, p, width ); +  io_append( THIS, p, width ); +  ref_push_object(Pike_fp->current_object);    }       /*! @decl protected int `[](int off)    *!    *! Return the character at the specified offset.    */    PIKEFUN int(0..255) `[]( int off )    flags ID_PROTECTED;    {    IOBuffer *io = THIS;
pike.git/src/modules/_Stdio/buffer.cmod:784:    {    push_object( io_read_buffer( THIS, 0, 0 ) );    }       PIKEFUN string(0..255) _sprintf(int o, mapping ignore)    flags ID_PROTECTED;    {    pop_n_elems(args);    if( o == 'O' )    { -  push_text("IOBuffer(%d bytes, read=[..%d] data=[%d..%d] free=[%d..%d] %s%s)"); +  push_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_text( (THIS->str ? "string" : THIS->malloced ? "allocated" : "subbuffer" ) );    if( THIS->locked )    push_text(" (read only)");    else    push_text(""); -  f_sprintf(9); +  f_sprintf(10);    }    else    push_undefined();    }       /*! @decl string(8bit) read_hstring( int(0..) n )    *!    *! Identical in functionality to @[read](@[read_number](@[n])) but    *! faster.    *!
pike.git/src/modules/_Stdio/buffer.cmod:928:    *!    *! The non-matching data will be left in the buffer.    *!    *! See @[array_sscanf] for more information.    */    PIKEFUN array sscanf( string format )    {    INT32 i;    ptrdiff_t num_used;    struct svalue *start = Pike_sp; -  +  retry:    i = low_sscanf_pcharp(    MKPCHARP(io_read_pointer(THIS), 0), io_len(THIS),    MKPCHARP(format->str,format->size_shift), format->len,    &num_used, 0);       if( !num_used )    { -  io_range_error(THIS,-1); +  if( io_range_error(THIS,0) ) +  goto retry;    pop_n_elems(Pike_sp-start);    push_int(0);    }    else    {    io_consume( THIS, num_used );    f_aggregate(Pike_sp-start);    }    }   
pike.git/src/modules/_Stdio/buffer.cmod:978:    PIKEFUN mixed read_json(int|void require_whitespace)    {    int stop, whites = 0;    static ptrdiff_t(*parse_json_pcharp)(PCHARP,size_t,int,char**);    char *err = NULL;    if( require_whitespace )    whites = require_whitespace->u.integer;       if( !parse_json_pcharp )    parse_json_pcharp = PIKE_MODULE_IMPORT(Standards.JSON, parse_json_pcharp ); -  +  retry:    stop = parse_json_pcharp( MKPCHARP(io_read_pointer(THIS),0),    io_len(THIS), 1|8, &err ); /* json_utf8 */       if( stop < 0 )    {    if( -stop == (ptrdiff_t)io_len(THIS) || (err && !strncmp(err,"Unterminated",12)))    { -  io_range_error(THIS,-stop+1); +  if( io_range_error(THIS,0) ) +  goto retry;    push_undefined();    }    else    {    /* FIXME: Use real json error? */    if( err )    Pike_error("Syntax error in json at offset %d: %s\n", -stop, err );    else    Pike_error("Syntax error in json at offset %d\n", -stop );    }    }    else    {    if( whites &&    (io_is_whitespace(THIS,stop)<=0 && io_is_whitespace(THIS,stop-1)<=0))    {    if( stop == (ptrdiff_t)io_len(THIS) )    { -  io_range_error(THIS,stop); +  if( io_range_error(THIS,0) ) +  goto retry;    pop_stack();    push_undefined();    }    else    Pike_error("Missing whitespace between json values at offset %d\n", stop );    }    else    {    if( whites )    while( io_is_whitespace( THIS, stop ) )
pike.git/src/modules/_Stdio/buffer.cmod:1044:    *! @code    *! // get the next whitespace separated word from the buffer.    *! buffer->match("%*[ \t\r\n]%*[^ \t\r\n]");    *! @endcode    */    PIKEFUN string(0..255)|int|float|array match( string format )    {    INT32 i;    ptrdiff_t num_used;    struct svalue *start = Pike_sp; -  +  retry:    i = low_sscanf_pcharp(    MKPCHARP(io_read_pointer(THIS), 0), io_len(THIS),    MKPCHARP(format->str,format->size_shift), format->len,    &num_used,    0);       if( !num_used )    { -  io_range_error(THIS,-1); +  if( io_range_error(THIS,0) ) +  goto retry;    pop_n_elems(Pike_sp-start);    push_int(0);    }    else    {    io_consume( THIS, num_used );    if( Pike_sp-start > 1 )    f_add(Pike_sp-start);    }    }
pike.git/src/modules/_Stdio/buffer.cmod:1105:    }    }       /*! @decl int(0..)|int(-1..-1) consume( int(0..) n )    *!    *! Discard the first @[n] bytes from the buffer    */    PIKEFUN int(-1..) consume( int n )    {    if( !io_avail( THIS, n ) ) -  { -  io_range_error( THIS, n ); +     push_int(-1); -  } +     else    push_int64( io_consume( THIS, n ) );    }          /*! @decl int(0..)|int(-1..-1) unread( int(0..) n )    *!    *! Rewind the buffer @[n] bytes.    *!    *! @returns
pike.git/src/modules/_Stdio/buffer.cmod:1143:    }       /*! @decl string(8bit) read( int n )    *!    *! Read @[bytes] bytes of data from the buffer.    *!    *! If there is not enough data available this returns 0.    */    PIKEFUN string(0..255) read( int bytes )    { -  struct pike_string * s = io_read_string(THIS, bytes ); -  pop_stack(); +  struct pike_string *s; +  s = io_read_string(THIS, bytes );    if( s ) -  push_string(s); -  else -  push_int(0); +  RETURN s; +  push_undefined();    }       /*! @decl string(8bit) read( )    *!    *! Read all data from the buffer.    *!    *! If there is not enough data available this returns 0.    *!    *! This is basically equivalent to (string)buffer, but it also    *! removes the data from the buffer.    */    PIKEFUN string(0..255) read()    {    struct pike_string * s = io_read_string(THIS, io_len(THIS)); -  if( !s ) -  push_int(0); -  else -  push_string(s); +  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    *! return it.    *!    *! Will return -1 if there is not enough buffer space available    *! unless error mode is set to throw errors.
pike.git/src/modules/_Stdio/buffer.cmod:1242:    PIKEFUN void read_only()    {    io_lock( THIS );    }       /*! @decl void create( int|void len )    *! @decl void create( string(8bit) contents )    *! @decl void create( System.Memory|String.Buffer contents )    *!    *! If passed an integer or no argument, create a buffer of that -  *! size, or if no argument is given, 4Kb. +  *! size, or if no argument is given, 226 bytes.    *!    *! If @[contents] are specified a new buffer with the contents of    *! the given string/System.Memory or String.Buffer will be created.    *!    *! @note    *! In the @[String.Buffer] case the data has to be copied unless    *! there is only one reference to the String.Buffer object, since    *! modifications of the String.Buffer would cause the IOBuffer to    *! point into invalid memory.    *!
pike.git/src/modules/_Stdio/buffer.cmod:1345:    this->allocated = len;    this->malloced = 1;    }       PIKEFUN void create( )    flags ID_PROTECTED;    {    IOBuffer *this = THIS;    if( this->buffer )    Pike_error("Can not initialize twice.\n"); -  this->buffer = xalloc(4096-32); -  this->allocated = 4096-32; +  this->buffer = xalloc(256-32); +  this->allocated = 256-32;    this->malloced = 1;    }      /*! @endclass    */       INIT {    IOBuffer *this = THIS;    memset( this, 0, sizeof(IOBuffer)); -  +  this->this = Pike_fp->current_object;    }       EXIT {    IOBuffer *this = THIS;    if( this->sub )    {    free_object( this->sub );    io_unlock( get_storage(this->sub,IOBuffer_program ) );    }    if( this->source )
pike.git/src/modules/_Stdio/buffer.cmod:1391:    add_integer_constant( "buffer_error", 1, 0 );    buffer_error_program = end_program();   }         void exit_stdio_buffer(void)   {    if( shm_program ) free_program( shm_program );    if( sbuf_program ) free_program( sbuf_program );    free_program( buffer_error_program ); +  if( range_error_s ) free_string(range_error_s);    EXIT   }   /*! @endmodule    */