summaryrefslogtreecommitdiff
path: root/ace/IOStream.cpp
blob: 0603f926060fdd3a5941db89a266b9fbd1c1a7bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
// $Id$

#if !defined (ACE_IOSTREAM_C)
#define ACE_IOSTREAM_C

#define ACE_BUILD_DLL

#include "ace/IOStream.h"

ACE_RCSID(ace, IOStream, "$Id$")

#if !defined (ACE_LACKS_ACE_IOSTREAM)

///////////////////////////////////////////////////////////////////////////

/* Here's a simple example of how iostream's non-virtual operators can
   get you in a mess:

  class myiostream : public iostream
  {
  public:
          myiostream& operator>> (String & s)
          {
                  ...
          }
  };

  ...

  int i;
  String s;
  myiostream foo (...);

  foo >> s;
  // OK
  // invokes myiostream::operator>> (String&) returning myiostream&

  foo >> i;
  // OK
  // invokes iostream::operator>> (int&) returning iostream&

  foo >> i >> s;
  // BAD
  // invokes iostream::operator>> (int&) then iostream::operator>> (String&)
  //
  // What has happened is that the first >> is invoked on the base class and returns
  // a reference to iostream.  The second >> has no idea of the ACE_IOStream and
  // gets invoked on iostream.  Probably NOT what you wanted!


  // In order to make all of this work the way you want, you have to do this:

  class myiostream : public iostream
  {
  public:
          myiostream& operator>> (int & i)
          {
                  return ((myiostream&)iostream::operator>> (i));
          }

          myiostream& operator>> (String & s)
          {
                  ...
          }
  };

  ...

  int i;
  String s;
  myiostream foo (...);

  foo >> s;
  // OK
  // invokes myiostream::operator>> (String&) returning myiostream&

  foo >> i;
  // OK
  // invokes myiostream::operator>> (int&) returning myiostream&


  foo >> i >> s;
  // OK
  // Because you provided operator>> (int&) in class myiostream, that
  // function will be invoked by the first >>.  Since it returns
  // a myiostream&, the second >> will be invoked as desired.  */

ACE_HANDLE
ACE_Streambuf::get_handle (void)
{
  return 0;
}

ACE_Time_Value *
ACE_Streambuf::recv_timeout (ACE_Time_Value *tv)
{
  ACE_Time_Value * rval = recv_timeout_;
  if (tv)
    {
      recv_timeout_value_ = *tv;
      recv_timeout_ = &recv_timeout_value_;
    }
  else
    recv_timeout_ = NULL;

  return rval;
}

int
ACE_Streambuf::underflow (void)
{
  // If input mode is not set, any attempt to read from the stream is
  // a failure.

  if (ACE_BIT_DISABLED (mode_, ios::in))
    return EOF;

  // If base () is empty then this is the first time any get/put
  // operation has been attempted on the stream.

  if (!this->base ())
    {
      // Set base () to use our private read buffer.  The arguments are:
      //        beginning of the buffer (base ())
      //        one-beyond the end of the buffer (ebase ())
      //        should base () be deleted on destruction
      //
      // We have to say "no" to the third parameter because we want to
      // explicitly handle deletion of the TWO buffers at destruction.

      setb (this->eback_saved_,
            this->eback_saved_ + streambuf_size_, 0);

      // Remember that we are now in getMode.  This will help us if
      // we're called prior to a mode change as well as helping us
      // when the mode does change.
      this->cur_mode_ = this->get_mode_;
      // Using the new values for base (), initialize the get area.
      // This simply sets eback (), gptr () and egptr () described
      // earlier.
      setg (base (), base (), base ());

      // Set the put buffer such that puts will be disabled.  Any
      // attempt to put data will now cause overflow to be invoked.
      setp (0, 0);
    }
  else  // base () has been initialized already...
    {
      // If we are in put_mode_ now, then it is time to switch to get_mode_
      //
      // 1. get rid of any pending output
      // 2. rearrange base () to use our half of the buffer
      // 3. reset the mode
      //
      if (this->cur_mode_ == this->put_mode_)
        {
          // Dump any pending output to the peer.  This is not really
          // necessary because of the dual-buffer arrangement we've
          // set up but intuitively it makes sense to send the pending
          // data before we request data since the peer will probably
          // need what we're sending before it can respond.
          if (out_waiting () && syncout () == EOF)
            return EOF;

          if( ! pbase() )
            {
              delete [] pbase_saved_;
              (void) reset_put_buffer();
            }
          else
            {
              // We're about to disable put mode but before we do
              // that, we want to preserve it's state.
              this->pbase_saved_ = pbase ();
              this->pptr_saved_  = pptr ();
              this->epptr_saved_ = epptr ();
            }

          // Disable put mode as described in the constructor.
          setp (0, 0);

          // Like the case where base () is false, we now point base
          // () to use our private get buffer.
          setb (this->eback_saved_,
                this->eback_saved_ + streambuf_size_,
                0);

          // And restore the previous state of the get pointers.

          setg (this->eback_saved_, this->gptr_saved_,
                this->egptr_saved_);

          // Finally, set our mode so that we don't get back into this
          // if () and so that overflow can operate correctly.
          cur_mode_ = get_mode_;
        }

      // There could be data in the input buffer if we switched to put
      // mode before reading everything.  In that case, we take this
      // opportunity to feed it back to the iostream.
      if (in_avail ())
        // Remember that we return an int so that we can give back
        // EOF.  The explicit cast prevents us from returning a signed
        // char when we're not returning EOF.
        return (u_char) *gptr ();
    }

  // We really shouldn't be here unless there is a lack of data in the
  // read buffer.  So... go get some more data from the peer.

  int result = fillbuf ();

  // Fillbuf will give us EOF if there was an error with the peer.  In
  // that case, we can do no more input.

  if (EOF == result)
    {
      // Disable ourselves and return failure to the iostream.  That
      // should result in a call to have oursleves closed.
      setg (0, 0, 0);
      return EOF;
    }

  // Return the next available character in the input buffer.  Again,
  // we protect against sign extension.

  return (u_char) *gptr ();
}

// Much of this is similar to underflow.  I'll just hit the highlights
// rather than repeating a lot of what you've already seen.

int
ACE_Streambuf::overflow (int c)
{
  // Check to see if output is allowed at all.
  if (! (mode_ & ios::out))
    return EOF;

  if (!base ())
    {
      // Set base () to use put's private buffer.
      //
      setb (this->pbase_saved_,
            this->pbase_saved_ + streambuf_size_, 0);

      // Set the mode for optimization.
      this->cur_mode_ = this->put_mode_;
      // Set the put area using the new base () values.
      setp (base (), ebuf ());

      // Disable the get area.
      setg (0, 0, 0);
    }
  else  // We're already reading or writing
    {
      // If we're coming out of get mode...
      if (this->cur_mode_ == this->get_mode_)
        {
          // --> JCEJ 6/6/98
          if (! eback())
            {
              /* Something has happened to cause the streambuf
                 to get rid of our get area.
                 We could probably do this a bit cleaner but
                 this method is sure to cleanup the bits and
                 pieces.
              */
              delete [] eback_saved_;
              (void) reset_get_buffer();
            }
          else
            {
              // Save the current get mode values
              this->eback_saved_ = eback ();
              this->gptr_saved_  = gptr ();
              this->egptr_saved_ = egptr ();
            }
          // <-- JCEJ 6/6/98

          // then disable the get buffer
          setg (0, 0, 0);

          // Reconfigure base () and restore the put pointers.
          setb (pbase_saved_, pbase_saved_ + streambuf_size_, 0);
          setp (base (), ebuf ());

          // Save the new mode.
          this->cur_mode_ = this->put_mode_;
        }

      // If there is output to be flushed, do so now.  We shouldn't
      // get here unless this is the case...

      if (out_waiting () && EOF == syncout ())
        return EOF;
    }

  // If we're not putting EOF, then we have to deal with the character
  // that is being put.  Perhaps we should do something special with EOF???

  if (c != EOF)
    {
      // We've already written any data that may have been in the
      // buffer, so we're guaranteed to have room in the buffer for
      // this new information.  So... we add it to the buffer and
      // adjust our 'next' pointer acordingly.
      *pptr () = (char) c;
      pbump (1);
    }

  return 0;
}

// syncin

int
ACE_Streambuf::syncin (void)
{
  // As discussed, there really isn't any way to sync input from a
  // socket-like device.  We specifially override this base-class
  // function so that it won't do anything evil to us.
  return 0;
}

// syncout

int
ACE_Streambuf::syncout (void)
{
  // Unlike syncin, syncout is a doable thing.  All we have to do is
  // write whatever is in the output buffer to the peer.  flushbuf ()
  // is how we do it.

  if (flushbuf () == EOF)
    return EOF;
  else
    return 0;
}

int
ACE_Streambuf::sync (void)
{
  // sync () is fairly traditional in that it syncs both input and
  // output.  We could have omitted the call to syncin () but someday,
  // we may want it to do something.

  syncin ();

  // Don't bother syncing the output unless there is data to be
  // sent...

  if (out_waiting ())
    return syncout ();
  else
    return 0;
}

// flushbuf

int
ACE_Streambuf::flushbuf (void)
{
  // pptr () is one character beyond the last character put into the
  // buffer.  pbase () points to the beginning of the put buffer.
  // Unless pptr () is greater than pbase () there is nothing to be
  // sent to the peer.

  if (pptr () <= pbase ())
    return 0;

  // 4/12/97 -- JCEJ
  // Kludge!!!
  // If the remote side shuts down the connection, an attempt to send
  // () to the remote will result in the message 'Broken Pipe' I think
  // this is an OS message, I've tracked it down to the ACE_OS::write
  // () function.  That's the last one to be called before the
  // message.  I can only test this on Linux though, so I don't know
  // how other systems will react.
  //
  // To get around this gracefully, I do a PEEK recv () with an
  // immediate (nearly) timeout.  recv () is much more graceful on
  // it's failure.  If we get -1 from recv () not due to timeout then
  // we know we're SOL.
  //
  // Q:  Is 'errno' threadsafe?  Should the section below be a
  //     critical section?
  //
  // char tbuf[1];
  // ACE_Time_Value to (0,1);
  // if (this->recv (tbuf, 1, MSG_PEEK, &to) == -1)
  // {
  //    if (errno != ETIME)
  //    {
  //            perror ("OOPS preparing to send to peer");
  //            return EOF;
  //    }
  // }
  //
  // The correct way to handle this is for the application to trap
  // (and ignore?) SIGPIPE.  Thanks to Amos Shapira for reminding me
  // of this.

  // Starting at the beginning of the buffer, send as much data as
  // there is waiting.  send guarantees that all of the data will be
  // sent or an error will be returned.

  if (this->send (pbase (), pptr () - pbase ()) == -1)
    return EOF;

  // Now that we've sent everything in the output buffer, we reset the
  // buffer pointers to appear empty.
  setp (base (), ebuf ());

  return 0;
}

int
ACE_Streambuf::get_one_byte (void)
{
  this->timeout_ = 0;

  // The recv function will return immediately if there is no data
  // waiting.  So, we use recv_n to wait for exactly one byte to come
  // from the peer.  Later, we can use recv to see if there is
  // anything else in the buffer. (Ok, we could use flags to tell it
  // to block but I like this better.)

  if (this->recv_n (base (), 1, MSG_PEEK, this->recv_timeout_) != 1)
    {
      if (errno == ETIME)
        this->timeout_ = 1;
      return EOF;
    }
  else
    return 1;
}

// This will be called when the read (get) buffer has been exhausted
// (ie -- gptr == egptr).

int
ACE_Streambuf::fillbuf (void)
{
  // Invoke recv_n to get exactly one byte from the remote.  This will
  // block until something shows up.

  if (get_one_byte () == EOF)
    return EOF;

  // Now, get whatever else may be in the buffer.  This will return if
  // there is nothing in the buffer.

  int bc = this->recv (base (), blen (), this->recv_timeout_);

  // recv will give us -1 if there was a problem.  If there was
  // nothing waiting to be read, it will give us 0.  That isn't an
  // error.

  if (bc < 0)
    {
      if (errno == ETIME)
        this->timeout_ = 1;
      return EOF;
    }

  // Move the get pointer to reflect the number of bytes we just read.

  setg (base (), base (), base () + bc);

  // Return the byte-read-count including the one from <get_one_byte>.
  return bc;
}

ACE_Streambuf::ACE_Streambuf (u_int streambuf_size, int io_mode)
  : eback_saved_ (0),  // to avoid Purify UMR
    pbase_saved_ (0),  // to avoid Purify UMR
    get_mode_ (1),
    put_mode_ (2),
    mode_ (io_mode),
    streambuf_size_ (streambuf_size),
    recv_timeout_ (NULL)
{
 (void)reset_get_buffer ();
 (void)reset_put_buffer ();
}

u_int
ACE_Streambuf::streambuf_size (void)
{
  return streambuf_size_;
}

// Return the number of bytes not yet gotten. eback + get_waiting =
// gptr.

u_int
ACE_Streambuf::get_waiting (void)
{
  return this->gptr_saved_ - this->eback_saved_;
}

// Return the number of bytes in the get area (includes some already
// gotten); eback + get_avail = egptr.

u_int
ACE_Streambuf::get_avail (void)
{
  return this->egptr_saved_ - this->eback_saved_;
}

// Return the number of bytes to be 'put' onto the stream media.
// pbase + put_avail = pptr.

u_int
ACE_Streambuf::put_avail (void)
{
  return this->pptr_saved_ - this->pbase_saved_;
}

// Typical usage:
//
//      u_int  newGptr  = otherStream->get_waiting ();
//      u_int  newEgptr = otherStream->get_avail ();
//      char * newBuf   = otherStream->reset_get_buffer ();
//      char * oldgetbuf = myStream->reset_get_buffer (newBuf, otherStream->streambuf_size (), newGptr, newEgptr);
//
//      'myStream' now has the get buffer of 'otherStream' and can use it in any way.
//      'otherStream' now has a new, empty get buffer.

char *
ACE_Streambuf::reset_get_buffer (char *newBuffer,
                                 u_int _streambuf_size,
                                 u_int _gptr,
                                 u_int _egptr)
{
  char * rval = this->eback_saved_;

  // The get area is where the iostream will get data from.  This is
  // our read buffer.  There are three pointers which describe the
  // read buffer:
  //
  //    eback () - The beginning of the buffer.  Also the furthest
  //              point at which putbacks can be done.  Hence the name.
  //
  //    gptr ()  - Where the next character is to be got from.
  //
  //    egptr () - One position beyond the last get-able character.
  //
  // So that we can switch quicky from read to write mode without
  // any data copying, we keep copies of these three pointers in
  // the variables below.  Initially, they all point to the beginning
  // of our read-dedicated buffer.
  //
  if (newBuffer)
    {
      if (streambuf_size_ != _streambuf_size)
        return NULL;
      this->eback_saved_ = newBuffer;
    }
  else
    ACE_NEW_RETURN (this->eback_saved_, char[streambuf_size_], 0);

  this->gptr_saved_ = this->eback_saved_ + _gptr;
  this->egptr_saved_ = this->eback_saved_ + _egptr;

  // Disable the get area initially.  This will cause underflow to be
  // invoked on the first get operation.
  setg (0, 0, 0);

  reset_base ();

  return rval;
}

// Typical usage:
//
//      u_int  newPptr = otherStream->put_avail ();
//      char * newBuf  = otherStream->reset_put_buffer ();
//      char * oldputbuf = otherStream->reset_put_buffer (newBuf, otherStream->streambuf_size (), newPptr);

char *
ACE_Streambuf::reset_put_buffer (char *newBuffer,
                                 u_int _streambuf_size,
                                 u_int _pptr)
{
  char *rval = this->pbase_saved_;

  // The put area is where the iostream will put data that needs to be
  // sent to the peer.  This becomes our write buffer.  The three
  // pointers which maintain this area are:
  //
  //    pbase () - The beginning of the put area.
  //
  //    pptr ()  - Where the next character is to be put.
  //
  //    epptr () - One beyond the last valid position for putting.
  //
  // Again to switch quickly between modes, we keep copies of
  // these three pointers.
  //
  if (newBuffer)
    {
      if (streambuf_size_ != _streambuf_size)
        return NULL;
      this->pbase_saved_ = newBuffer;
    }
  else
    ACE_NEW_RETURN (this->pbase_saved_, char[streambuf_size_], 0);

  this->pptr_saved_ = this->pbase_saved_ + _pptr;
  this->epptr_saved_ = this->pbase_saved_ + streambuf_size_;

  // Disable the put area.  Overflow will be called by the first call
  // to any put operator.
  setp (0, 0);

  reset_base ();

  return rval;
}

void
ACE_Streambuf::reset_base (void)
{
  // Until we experience the first get or put operation, we do not
  // know what our current IO mode is.
  this->cur_mode_ = 0;

  // The common area used for reading and writting is called "base".
  // We initialize it this way so that the first get/put operation
  // will have to "allocate" base.  This allocation will set base to
  // the appropriate specific buffer and set the mode to the correct
  // value.
  setb (0, 0);
}

// If the default allocation strategey were used the common buffer
// would be deleted when the object destructs.  Since we are providing
// separate read/write buffers, it is up to us to manage their memory.

ACE_Streambuf::~ACE_Streambuf (void)
{
  delete [] this->eback_saved_;
  delete [] this->pbase_saved_;
}

u_char ACE_Streambuf::timeout (void)
{
  u_char rval = this->timeout_;
  this->timeout_ = 0;
  return rval;
}

#endif /* !ACE_LACKS_ACE_IOSTREAM */
#endif /* ACE_IOSTREAM_C */