Branch: Tag:

2014-10-05

2014-10-05 11:59:41 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Stdio.Buffer: output_to() et al now support functions.

This reduces the need for proxy objects in higher level code
using Stdio.Buffer internally.

Also adds support for subtyped objects and multiple inheritance
of Stdio.Fd et al.

73:       CVAR Buffer b;    +  EXTRA +  { +  PIKE_MAP_VARIABLE("__output", OFFSETOF(Buffer_struct, b.output), +  tMix, PIKE_T_MIXED, ID_PRIVATE|ID_HIDDEN|ID_PROTECTED); +  } +     static void io_set_error_mode( Buffer *io, struct program *m )    {    if( m ) add_ref(m);
409:    free( x->arg );    }    -  static ptrdiff_t io_call_write( Buffer *io, struct object *o, ptrdiff_t nbytes ) +  static ptrdiff_t io_call_write( Buffer *io, struct svalue *fun, +  ptrdiff_t nbytes )    ATTRIBUTE((noclone,noinline));       static void io_set_events( Buffer *io, struct my_file *fd, int extra, int set )
422:    set_fd_callback_events(&fd->box, fd->box.events|(1<<set), 0);    }    -  static ptrdiff_t io_call_write( Buffer *io, struct object *o, ptrdiff_t bytes ) +  static ptrdiff_t io_call_write( Buffer *io, struct svalue *fun, +  ptrdiff_t bytes )    {    if( bytes > 0 )    {
433:    {    io->output_triggered = 1;    push_string( s ); -  apply( o, "write", 1 ); +  apply_svalue( fun, 1 ); +  if (UNLIKELY(TYPEOF(Pike_sp[-1]) != PIKE_T_INT)) { +  Pike_error("Invalid return value from write callback.\n"); +  }    l = Pike_sp[-1].u.integer;    pop_stack(); -  if( l < 0 ) +  if( l < 0 )    {    io_rewind( io, bytes );    return -1;
454:       static void io_actually_trigger_output( Buffer *io )    { -  struct my_file *fd; -  if( (fd = get_storage( io->output, file_program )) ) -  { +  struct program *prog; +  struct reference *ref; +  struct inherit *inh; +  +  if (UNLIKELY(!(prog = io->output.u.object->prog))) { +  /* Destructed object. */ +  free_svalue(&io->output); +  SET_SVAL(io->output, PIKE_T_INT, NUMBER_NUMBER, integer, 0); +  return 0; +  } +  ref = PTR_FROM_INT(prog, SUBTYPEOF(io->output)); +  inh = INHERIT_FROM_PTR(prog, ref); +  if ((inh->prog == file_program) && +  (ref->identifier_offset == fd_write_identifier_offset)) { +  /* Stdio.Fd::write */ +  struct my_file *fd = +  get_inherit_storage( io->output.u.object, ref->inherit_offset );    io_set_events( io, fd, PIKE_BIT_FD_WRITE_OOB, PIKE_FD_WRITE );    io->output_triggered = 1;    }    else -  io_call_write( io, io->output, MINIMUM( io_len(io), 100 ) ); +  io_call_write( io, &io->output, MINIMUM( io_len(io), 100 ) );    }       static void io_trigger_output( Buffer *io )    { -  if( UNLIKELY(io->output) && UNLIKELY(!io->output_triggered) ) +  if( UNLIKELY(io->output.u.object) && UNLIKELY(!io->output_triggered) )    io_actually_trigger_output(io);    }   
937:    RETURN bread;    }    -  /*! @decl int __fd_set_output( object f ) +  /*! @decl int __fd_set_output( object|function(string:int) write_callback )    *!    *! This tells the buffer to trigger the write callback for the    *! specified filedescriptor when data is added to the buffer.
945:    *! This is used internally by Stdio.File to handle nonblocking    *! buffered mode, and is not really intended to be used directly.    *! -  *! If f is 0 the state is cleared. +  *! If @[write_callback] is @expr{0@} (zero) the state is cleared.    */ -  PIKEFUN void __fd_set_output( int(0..0)|object f ) +  PIKEFUN void __fd_set_output( int(0..0)|object|function f )    {    Buffer *io = THIS; -  if( io->output ) free_object(io->output); -  io->output_triggered = 0; -  if( TYPEOF(*f) != PIKE_T_OBJECT ) -  io->output = 0; -  else -  { -  io->output = f->u.object; -  add_ref(io->output); +  if( TYPEOF(*f) == PIKE_T_OBJECT ) { +  struct program *p = f->u.object->prog; +  if (p) { +  struct inherit *inh = p->inherits + SUBTYPEOF(*f); +  int write_fun_num; +  p = inh->prog; +  if ((write_fun_num = find_identifier("write", p)) == -1) { +  Pike_error("Function \"write\" not available in object.\n");    } -  +  SET_SVAL_TYPE_SUBTYPE(*f, PIKE_T_FUNCTION, write_fun_num);    } -  +  } else if (TYPEOF(*f) != PIKE_T_FUNCTION) { +  push_int(0); +  f = Pike_sp-1; +  } +  assign_svalue(&io->output, f); +  io->output_triggered = 0; +  }    -  /*! @decl int(-1..) output_to( Stdio.Stream f, int(0..)|void nbytes ) +  /*! @decl int(-1..) output_to( Stdio.Stream|function(string:int) fun, @ +  *! int(0..)|void nbytes )    *!    *! Write data from the buffer to the indicated file.    *! -  +  *! @param fun +  *! Write function. Either one of: +  *! @mixed +  *! @type Stdio.Stream +  *! A file object in which the function @expr{write()@} will +  *! be called. +  *! @type function(string:int) +  *! A function which will be called with a @expr{string(8bit)@} +  *! to write and is expected to return an @expr{int@} indicating +  *! the number of bytes successfully written or @expr{-1@} on +  *! failure. +  *! @endmixed +  *!    *! @param nbytes -  *! If @[nbytes] is not specified the whole buffer will be written -  *! if possible. Otherwise at most @[nbytes] will be written. +  *! If @[nbytes] is not specified the whole buffer will be written +  *! if possible. Otherwise at most @[nbytes] will be written.    *!    *! @returns    *! Will return the number of bytes that have been written successfully.    *! -  *! If no bytes have been written successfully and @expr{f->write()@} failed +  *! If no bytes have been written successfully and @expr{fun()@} failed    *! with an error, @expr{-1@} will be returned.    */ -  PIKEFUN int(-1..) output_to( object f, int|void nbytes ) +  PIKEFUN int(-1..) output_to( object|function(string:int) f, int|void nbytes )    {    Buffer *io = THIS;    ptrdiff_t written = 0; -  struct my_file *fd; +     ptrdiff_t sz = io_len( io ); -  int write_fun_num; -  struct inherit *inh; +  int write_fun_num = -1;       if( !sz )    {
992:    if( nbytes )    sz = MINIMUM(nbytes->u.integer, sz);    -  if ((write_fun_num = find_identifier("write", f->prog)) == -1) { -  Pike_error("Cannot call unknown function \"write\".\n"); +  if( TYPEOF(*f) == PIKE_T_OBJECT ) { +  struct program *p = f->u.object->prog; +  if (LIKELY(p)) { +  struct inherit *inh = p->inherits + SUBTYPEOF(*f); +  p = inh->prog; +  if ((write_fun_num = find_identifier("write", p)) == -1) { +  Pike_error("Function \"write\" not available in object.\n");    } -  +  SET_SVAL_TYPE_SUBTYPE(*f, PIKE_T_FUNCTION, write_fun_num); +  } else { +  SIMPLE_BAD_ARG_ERROR("output_to", 1, "object|function"); +  } +  } else if (UNLIKELY(TYPEOF(*f) != PIKE_T_FUNCTION)) { +  SIMPLE_BAD_ARG_ERROR("output_to", 1, "object|function"); +  } else { +  write_fun_num = SUBTYPEOF(*f); +  }    -  inh = INHERIT_FROM_INT(f->prog, write_fun_num); +  if (write_fun_num != FUNCTION_BUILTIN) { +  struct program *prog = f->u.object->prog; +  struct reference *ref = PTR_FROM_INT(prog, write_fun_num); +  struct inherit *inh = INHERIT_FROM_PTR(prog, ref);    -  if( inh->prog == file_program ) -  { -  fd = get_inherit_storage( f, inh - f->prog->inherits ); -  +  if( (inh->prog == file_program) && +  (ref->identifier_offset == fd_write_identifier_offset) ) { +  struct my_file *fd = +  get_inherit_storage( f->u.object, ref->inherit_offset );    while( sz > written )    {    ptrdiff_t rd = MINIMUM(sz-written,4096);
1019:    written += res;    io_set_events( io, fd, PIKE_BIT_FD_WRITE_OOB, PIKE_FD_WRITE);    } +  RETURN written;    } -  else -  { -  /* some other object. Just call write */ +  } +  +  /* Some other object or function. Just call it. */    while( sz > written )    {    size_t rd = MINIMUM(sz-written,4096);
1036:    if( wr < 4096 )    break;    } -  } +     RETURN written;    }   
2373:    EXIT {    Buffer *this = THIS;    io_unlink_external_storage( this ); -  if( this->output ) -  free_object(this->output); +     if( this->error_mode )    free_program( this->error_mode );    if( this->malloced )
2453:    PIKEFUN void create()    flags ID_PRIVATE;    { +  /* FIXME: The following zeroing isn't safe! */    THIS->obj = 0;    THIS->io = 0;    Pike_error("Not supported\n");