xref: /haiku/src/kits/network/libnetapi/NetBuffer.cpp (revision e8e6c3e8add38406e0cc4e7110032a253a80f054)
1 /*=--------------------------------------------------------------------------=*
2  * NetBuffer.cpp -- Implementation of the BNetBuffer class.
3  *
4  * Written by S.T. Mansfield (thephantom@mac.com)
5  *
6  * Remarks:
7  *   * This is basically a fancy-schmancy FIFO stack manager.  Thusly, it is
8  *     considered an error if data is not popped off a particular instance's
9  *     stack in the order it was pushed on.
10  *   * We could possibly implement this class as a thin wrapper around the
11  *     BMessage class, but I'm not sure what kind of payload the latter class
12  *     brings to the party, so we'll do our own stack management.  We also
13  *     cannot be sure that BMessage behaves as described above down the road.
14  *   * Never use hot wax to soothe enraged lobsters.
15  *=--------------------------------------------------------------------------=*
16  * Copyright (c) 2002, The OpenBeOS project.
17  *
18  * Permission is hereby granted, free of charge, to any person obtaining a
19  * copy of this software and associated documentation files (the "Software"),
20  * to deal in the Software without restriction, including without limitation
21  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
22  * and/or sell copies of the Software, and to permit persons to whom the
23  * Software is furnished to do so, subject to the following conditions:
24  *
25  * The above copyright notice and this permission notice shall be included in
26  * all copies or substantial portions of the Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34  * DEALINGS IN THE SOFTWARE.
35  *=--------------------------------------------------------------------------=*
36  */
37 
38 
39 #include <string.h>
40 #include <ByteOrder.h>
41 #include <Message.h>
42 
43 #include <NetBuffer.h>
44 
45 /*
46  * Parametric ctor initialization list defaults.
47  */
48 #define CTOR_INIT_LIST \
49     BArchivable( ), \
50     m_init( B_NO_INIT ), \
51     fData( NULL ), \
52     fDataSize( 0 ), \
53     fStackSize( 0 ), \
54     fCapacity( 0 )
55 
56 
57 /*
58  * Markers that surround a record in an instance's stack.
59  */
60 #define DATA_START_MARKER   0x01c0ffee
61 #define DATA_END_MARKER     0xbeefeeee
62 
63 
64 /* BNetBuffer
65  *=--------------------------------------------------------------------------=*
66  * Purpose:
67  *     Class ctor.
68  *
69  * Input parameter:
70  *     size         : Initial size of buffer (default is zero).
71  *
72  * Remarks:
73  *     This bad boy doubles up as the defalt ctor.
74  */
75 BNetBuffer::BNetBuffer( size_t size )
76            : CTOR_INIT_LIST
77 {
78     m_init = ( resize( size ) == B_OK ) ? B_OK : B_NO_INIT;
79 }
80 
81 
82 /* BNetBuffer
83  *=--------------------------------------------------------------------------=*
84  * Purpose:
85  *     Class copy ctor.
86  *
87  * Input parameter:
88  *     refparam     : Instance to copy.
89  */
90 BNetBuffer::BNetBuffer( const BNetBuffer& refparam )
91             : CTOR_INIT_LIST
92 {
93     m_init = ( clone( refparam ) == B_OK ) ? B_OK : B_NO_INIT;
94 }
95 
96 
97 /* BNetBuffer
98  *=--------------------------------------------------------------------------=*
99  * Purpose:
100  *     Ctor to instantiate from serialized data (BMessage).
101  *
102  * Input parameter:
103  *     archive      : Serialized object to instantiate from.
104  */
105 BNetBuffer::BNetBuffer( BMessage* archive )
106            : CTOR_INIT_LIST
107 {
108     const unsigned char* msgDataPtr;
109     ssize_t msgNBytes;
110 
111     int32  dataSize;
112     int32  stackSize;
113     int32  capacity;
114 
115     if ( archive->FindInt32( "bnbuff_datasize", &dataSize ) != B_OK )
116     {
117         return;
118     }
119 
120     if ( archive->FindInt32( "bnbuff_stacksize", &stackSize ) != B_OK )
121     {
122         return;
123     }
124 
125     if ( archive->FindInt32( "bnbuff_capacity", &capacity ) != B_OK )
126     {
127         return;
128     }
129 
130     if ( archive->FindData( "bnbuff_stack", B_RAW_TYPE,
131                             ( const void** )&msgDataPtr, &msgNBytes ) != B_OK )
132     {
133         return;
134     }
135 
136     if ( capacity )
137     {
138         if ( resize( capacity, true ) != B_OK )
139         {
140             return;
141         }
142 
143         memcpy( fData, msgDataPtr, stackSize );
144     }
145 
146     fDataSize = dataSize;
147     fStackSize = stackSize;
148     fCapacity = capacity;
149 
150     m_init = B_OK;
151 }
152 
153 
154 /* ~BNetBuffer
155  *=--------------------------------------------------------------------------=*
156  * Purpose:
157  *     Class dtor.
158  */
159 BNetBuffer::~BNetBuffer( void )
160 {
161     if ( fData != NULL )
162     {
163         delete fData;
164         fData = NULL;
165     }
166 
167     fDataSize = fStackSize = fCapacity = 0;
168 
169     m_init = B_NO_INIT;
170 }
171 
172 
173 /* operator=
174  *=--------------------------------------------------------------------------=*
175  * Purpose:
176  *     Class' assignment operator.
177  *
178  * Input parameter:
179  *     refparam     : Instance to assign from.
180  */
181 BNetBuffer& BNetBuffer::operator=( const BNetBuffer& refparam )
182 {
183     m_init = clone( refparam );
184 
185     return *this;
186 }
187 
188 
189 /* InitCheck
190  *=--------------------------------------------------------------------------=*
191  * Purpose:
192  *     Determine whether or not this instance is properly initialized.
193  *
194  * Returns:
195  *     B_OK if this instance is initialized, B_ERROR if not.
196  */
197 status_t BNetBuffer::InitCheck( void )
198 {
199     return ( m_init == B_OK ) ? B_OK : B_ERROR;
200 }
201 
202 
203 /* Archive
204  *=--------------------------------------------------------------------------=*
205  * Purpose:
206  *     Serialize this instance into the passed BMessage parameter.
207  *
208  * Input parameter:
209  *     deep         : [ignored] default==true.
210  *
211  * Output parameter:
212  *     into         : BMessage object to serialize into.
213  *
214  * Returns:
215  *     B_OK/BERROR on success/failure.  Returns B_NO_INIT if instance not
216  *     properly initialized.
217  */
218 status_t BNetBuffer::Archive( BMessage* into, bool deep ) const
219 {
220     if ( m_init != B_OK )
221     {
222         return B_NO_INIT;
223     }
224 
225     if ( into->AddInt32( "bnbuff_datasize", fDataSize ) )
226     {
227         return B_ERROR;
228     }
229 
230     if ( into->AddInt32( "bnbuff_stacksize", fStackSize ) )
231     {
232         return B_ERROR;
233     }
234 
235     if ( into->AddInt32( "bnbuff_capacity", fCapacity ) )
236     {
237         return B_ERROR;
238     }
239 
240     if ( into->AddData( "bnbuff_stack", B_RAW_TYPE, fData, fStackSize ) != B_OK )
241     //STM: Should we store the *whole* stack instead?......^^^^^^^^^^fCapacity
242     {
243         return B_ERROR;
244     }
245 
246     return B_OK;
247 }
248 
249 
250 /* Instantiate
251  *=--------------------------------------------------------------------------=*
252  * Purpose:
253  *     Un-serialize and instantiate from the passed BMessage parameter.
254  *
255  * Input parameter:
256  *     archive      : Archived BMessage object for (de)serialization.
257  *
258  * Returns:
259  *     NULL if a BNetBuffer instance can not be initialized, otherwise
260  *     a new BNetBuffer object instantiated from the BMessage parameter.
261  */
262 BArchivable* BNetBuffer::Instantiate( BMessage* archive )
263 {
264     if ( !validate_instantiation( archive, "BNetBuffer" ) )
265     {
266         return NULL;
267     }
268 
269     BNetBuffer* bnb = new BNetBuffer( archive );
270     if ( bnb == NULL )
271     {
272         return NULL;
273     }
274 
275     if ( bnb->InitCheck( ) != B_OK )
276     {
277         delete bnb;
278         return NULL;
279     }
280 
281     return bnb;
282 }
283 
284 
285 /* AppendXXX
286  *=--------------------------------------------------------------------------=*
287  * Purpose:
288  *     Append some data to this buffer (think PUSHing onto a FIFO stack).
289  *
290  * Returns:
291  *     B_OK/B_ERROR for success/failure.  B_NO_INIT if instance not properly
292  *     initialized or stuck in some unknown state during construction.
293  *
294  * Remarks:
295  *   * Where appropriate numeric data such as (u)int16, et al, are converted
296  *     to network byte order during storage, no need to do this in advance.
297  */
298 status_t BNetBuffer::AppendInt8( int8 Data )
299 {
300     return dpush( B_INT8_TYPE, sizeof( int8 ), &Data );
301 }
302 
303 status_t BNetBuffer::AppendUint8( uint8 Data )
304 {
305     return dpush( B_UINT8_TYPE, sizeof( uint8 ), &Data );
306 }
307 
308 status_t BNetBuffer::AppendInt16( int16 Data )
309 {
310     int16 locData = B_HOST_TO_BENDIAN_INT16( Data );
311     return dpush( B_INT16_TYPE, sizeof( int16 ), &locData );
312 }
313 
314 status_t BNetBuffer::AppendUint16( uint16 Data )
315 {
316     uint16 locData = B_HOST_TO_BENDIAN_INT16( Data );
317     return dpush( B_UINT16_TYPE, sizeof( uint16 ), &locData );
318 }
319 
320 status_t BNetBuffer::AppendInt32( int32 Data )
321 {
322     int32 locData = B_HOST_TO_BENDIAN_INT32( Data );
323     return dpush( B_INT32_TYPE, sizeof( int32 ), &locData );
324 }
325 
326 status_t BNetBuffer::AppendUint32( uint32 Data )
327 {
328     uint32 locData = B_HOST_TO_BENDIAN_INT32( Data );
329     return dpush( B_UINT32_TYPE, sizeof( uint32 ), &locData );
330 }
331 
332 status_t BNetBuffer::AppendInt64( int64 Data )
333 {
334     int64 locData = B_HOST_TO_BENDIAN_INT64( Data );
335     return dpush( B_INT64_TYPE, sizeof( int64 ), &locData );
336 }
337 
338 status_t BNetBuffer::AppendUint64( uint64 Data )
339 {
340     uint64 locData = B_HOST_TO_BENDIAN_INT64( Data );
341     return dpush( B_UINT64_TYPE, sizeof( uint64 ), &locData );
342 }
343 
344 status_t BNetBuffer::AppendFloat( float Data )
345 {
346     return dpush( B_FLOAT_TYPE, sizeof( float ), &Data );
347 }
348 
349 status_t BNetBuffer::AppendDouble( double Data )
350 {
351     return dpush( B_DOUBLE_TYPE, sizeof( double ), &Data );
352 }
353 
354 status_t BNetBuffer::AppendString( const char* Data )
355 {
356     return dpush( B_STRING_TYPE, strlen( Data ), Data );
357 }
358 
359 status_t BNetBuffer::AppendData( const void* Data, size_t Length )
360 {
361     return dpush( B_RAW_TYPE, Length, Data );
362 }
363 
364 status_t BNetBuffer::AppendMessage( const BMessage& Msg )
365 {
366     status_t result;
367     ssize_t msgLen;
368     char* msgData;
369 
370     msgLen = Msg.FlattenedSize( );
371     if ( msgLen == 0 )
372     {
373         //STM: It's possible, but SHOULD we store an empty BMessage?
374         //STM: Is an empty BMessage instance even legit in Be/OBOS?
375         return B_ERROR;
376     }
377 
378     if ( ( msgData = new char[msgLen] ) == NULL )
379     {
380         return B_ERROR; //STM: B_NO_MEM???
381     }
382 
383     // Don't get tripped on this, just trying to err on the side of caution.
384     result = B_ERROR;
385 
386     if ( Msg.Flatten( msgData, msgLen ) == B_OK )
387     {
388         result = dpush( B_MESSAGE_TYPE, msgLen, msgData );
389     }
390 
391     delete msgData;
392     return result;
393 }
394 
395 
396 /* RemoveXXXX
397  *=--------------------------------------------------------------------------=*
398  * Purpose:
399  *     Remove some data from this buffer, think POPping from a FIFO stack.
400  *
401  * Returns:
402  *     B_OK/B_ERROR for success/failure.  B_NO_INIT if instance not properly
403  *     initialized or stuck in some unknown state during construction.
404  *
405  * Remarks:
406  *     Where appropriate numeric data such as (u)int16, et al, are converted
407  *     from network byte order.  No need to do this ipso post facto.
408  */
409 status_t BNetBuffer::RemoveInt8( int8& Data )
410 {
411     return dpop( B_INT8_TYPE, sizeof( int8 ), &Data );
412 }
413 
414 status_t BNetBuffer::RemoveUint8( uint8& Data )
415 {
416     return dpop( B_UINT8_TYPE, sizeof( uint8 ), &Data );
417 }
418 
419 status_t BNetBuffer::RemoveInt16( int16& Data )
420 {
421     // dpop will convert this bad boy from network to host byte order.
422     return dpop( B_INT16_TYPE, sizeof( int16 ), &Data );
423 }
424 
425 status_t BNetBuffer::RemoveUint16( uint16& Data )
426 {
427     // dpop will convert this beast from network to host byte order.
428     return dpop( B_UINT16_TYPE, sizeof( uint16 ), &Data );
429 }
430 
431 status_t BNetBuffer::RemoveInt32( int32& Data )
432 {
433     // dpop will convert this thang from network to host byte order.
434     return dpop( B_INT32_TYPE, sizeof( int32 ), &Data );
435 }
436 
437 status_t BNetBuffer::RemoveUint32( uint32& Data )
438 {
439     // dpop will convert this variable from network to host byte order.
440     return dpop( B_UINT32_TYPE, sizeof( uint32 ), &Data );
441 }
442 
443 status_t BNetBuffer::RemoveInt64( int64& Data )
444 {
445     // dpop will convert this bag-o-bits from network to host byte order.
446     return dpop( B_INT64_TYPE, sizeof( int64 ), &Data );
447 }
448 
449 status_t BNetBuffer::RemoveUint64( uint64& Data )
450 {
451     // dpop will convert this construct from network to host byte order.
452     return dpop( B_UINT64_TYPE, sizeof( uint64 ), &Data );
453 }
454 
455 status_t BNetBuffer::RemoveFloat( float& Data )
456 {
457     return dpop( B_FLOAT_TYPE, sizeof( float ), &Data );
458 }
459 
460 status_t BNetBuffer::RemoveDouble( double& Data )
461 {
462     return dpop( B_DOUBLE_TYPE, sizeof( double ), &Data );
463 }
464 
465 status_t BNetBuffer::RemoveString( char* Data, size_t Length )
466 {
467     return dpop( B_STRING_TYPE, Length, Data );
468 }
469 
470 status_t BNetBuffer::RemoveData( void* Data, size_t Length )
471 {
472     return dpop( B_RAW_TYPE, Length, Data );
473 }
474 
475 status_t BNetBuffer::RemoveMessage( BMessage& message )
476 {
477     status_t result;
478     int32 v;
479     unsigned char* stackPtr = fData;
480     char* msgData;
481 
482     /*
483      * We have to cheat a little bit here and peek ahead of dpop so we can
484      * get at the size parameter.  Perform basic checks along the way so we
485      * can make sure that we really get to the size parameter and not some
486      * bogus value.  Redundant, but necessary.
487      */
488     v = *( int32 * )stackPtr;
489     if ( v != DATA_START_MARKER )
490     {
491         return B_ERROR;
492     }
493 
494     stackPtr += sizeof( int32 );
495     v = *( int32 * )stackPtr;
496     if ( v != B_MESSAGE_TYPE )
497     {
498         return B_ERROR;
499     }
500 
501     // Snarf the data size.
502     stackPtr += sizeof( int32 );
503     v = *( int32 * )stackPtr;
504 
505     if ( ( msgData = new char[v] ) == NULL )
506     {
507         return B_ERROR; //STM: B_NO_MEM???
508     }
509 
510     if ( dpop( B_MESSAGE_TYPE, v, msgData ) != B_OK )
511     {
512         delete msgData;
513         return B_ERROR;
514     }
515 
516     result = message.Unflatten( msgData );
517     delete msgData;
518     return result;
519 }
520 
521 
522 /* Data
523  *=--------------------------------------------------------------------------=*
524  * Purpose:
525  *     Class accessor.
526  *
527  * Returns:
528  *   Pointer to class' data suitable for those functions and methods that
529  *   expect some kind of "generic" pointer.
530  *
531  * Remarks:
532  *   * This is WEAK and WRONG, as it allows non-members to directly
533  *     manipulate an instance's member data without the usual checks in
534  *     place.  At the very least the return value should be const.  Ideally,
535  *     this method should not exist. <author steps off soapbox>
536  */
537 unsigned char* BNetBuffer::Data( void ) const
538 {
539     // Have I mentioned that this is a Really Bad Thing to do?
540     return fData;
541     // In case I missed it: returning a non-const pointer to our member data
542     // is not a good thing.  Caveat emptor.
543 }
544 
545 
546 /* Size
547  *=--------------------------------------------------------------------------=*
548  * Purpose:
549  *     Class accessor.
550  *
551  * Returns:
552  *     How many bytes of data are contained.  This *does not* include the
553  *     overhead of the stack control variables (delineators and size).
554  */
555 size_t BNetBuffer::Size( void ) const
556 {
557     return fDataSize;
558 }
559 
560 
561 /* BytesRemaining
562  *=--------------------------------------------------------------------------=*
563  * Purpose:
564  *     Class accessor ...erm... sort of.
565  *
566  * Returns:
567  *     How much capacity this instance has left to store data.
568  *
569  * Remarks:
570  *     This method is essentially meaningless as the class' storage capacity
571  *     will grow (or shrink) as necessary (that's an obfuscated way to say
572  *     "dynamically re-allocated" boys and girls).  Do not rely on this
573  *     particular method in normal use as it refers to a moving target.
574  */
575 size_t BNetBuffer::BytesRemaining( void ) const
576 {
577     return ( fCapacity - fStackSize );
578 }
579 
580 
581 /* clone
582  *=--------------------------------------------------------------------------=*
583  * Purpose:
584  *     Private copy helper method.
585  *
586  * Input parameter:
587  *     RefParam: Instance to clone.
588  *
589  * Returns:
590  *     status_t indicating success or point of failure (see resize()).
591  *     B_NO_INIT if RefParam not properly initialized.
592  */
593 status_t BNetBuffer::clone( const BNetBuffer& RefParam )
594 {
595     if ( !RefParam.m_init )
596     {
597         return B_NO_INIT;
598     }
599 
600     if ( resize( RefParam.fCapacity, true ) != B_OK )
601     {
602         return B_ERROR;
603     }
604 
605     fDataSize = RefParam.fDataSize;
606     fStackSize = RefParam.fStackSize;
607     memcpy( fData, RefParam.fData, fStackSize );
608 
609     return B_OK;
610 }
611 
612 
613 /* dpop
614  *=--------------------------------------------------------------------------=*
615  * Purpose:
616  *     Pop some data from our stack.
617  *
618  * Input parameters:
619  *     Type         : Expected data type for this record.
620  *     Length       : Expected data size for this record.
621  *
622  * Output parameter:
623  *     Data         : Recepticle for popped data.  You are responsible for
624  *                    insuring that this is large enough to hold your
625  *                    intended data.  If this parameter is NULL, you'll get
626  *                    back a B_ERROR.  If the space allocated that this
627  *                    parameter points to is not sufficient to contain the
628  *                    intended data, you'll likely over-run something else
629  *                    in user-space and piss off a few folks in the process.
630  *
631  * Returns:
632  *     B_OK/B_ERROR on success/failure.
633  *     B_NO_INIT if this instance not properly initialized.
634  */
635 status_t BNetBuffer::dpop( int32 Type, int32 Length, void* Data )
636 {
637     if ( m_init != B_OK )
638     {
639         return B_NO_INIT;
640     }
641 
642     if ( Data == NULL )
643     {
644         return B_ERROR;
645     }
646 
647     unsigned char* stackPtr = fData;
648     unsigned char* userDataPtr;
649     int32 int32size = sizeof( int32 ); // <-- Performance: called a lot here.
650     int32 recordSize = ( ( int32size * 4 ) + Length );
651     int32 tmp;
652 
653     // Validate the start marker.
654     tmp = *( int32 * )stackPtr;
655     if ( tmp != (int32) DATA_START_MARKER )
656     {
657         return B_ERROR;
658     }
659 
660     // Validate the data type.
661     stackPtr += int32size;
662     tmp =  *( int32 * )stackPtr;
663     if ( tmp != Type )
664     {
665         return B_ERROR;
666     }
667 
668     // Validate the data size.
669     stackPtr += int32size;
670     tmp =  *( int32 * )stackPtr;
671     if ( tmp != Length )
672     {
673         return B_ERROR;
674     }
675 
676     // Stash a pointer to the contained user data, used when we really "pop."
677     stackPtr += int32size;
678     userDataPtr = stackPtr;
679 
680     // Validate the end marker.
681     stackPtr += Length;
682     tmp = *( int32 * )stackPtr;
683     if ( tmp != (int32) DATA_END_MARKER )
684     {
685         return B_ERROR;
686     }
687 
688     // Point to the next entry in the stack.
689     stackPtr += int32size;
690 
691     // Extract the contained data.
692     memcpy( Data, userDataPtr, Length );
693 
694     // Convert from network byte order if necessary.
695     switch ( Type )
696     {
697         case B_INT16_TYPE:
698         case B_UINT16_TYPE:
699             *( uint16 * )Data = B_BENDIAN_TO_HOST_INT16( *( uint16 * )Data );
700             break;
701 
702         case B_INT32_TYPE:
703         case B_UINT32_TYPE:
704             *( uint32 * )Data = B_BENDIAN_TO_HOST_INT32( *( uint32 * )Data );
705             break;
706 
707         case B_INT64_TYPE:
708         case B_UINT64_TYPE:
709             *( uint64 * )Data = B_BENDIAN_TO_HOST_INT64( *( uint64 * )Data );
710             break;
711 
712         default:
713             break;
714     }
715 
716     // Condense the stack.
717     fStackSize -= recordSize;
718     memmove( fData, stackPtr, fStackSize );
719 
720     // Zero out the unused area of the stack where we memmove'd from.
721     memset( &fData[fStackSize], 0, ( fCapacity - fStackSize ) );
722 
723     // Update the extents.
724     fCapacity += recordSize;
725     fDataSize -= Length;
726 
727     return B_OK;
728 }
729 
730 
731 /* dpush
732  *=--------------------------------------------------------------------------=*
733  * Purpose:
734  *     Push some data onto our buffer.
735  *
736  * Input parameters:
737  *     Type         : Data type for this record.
738  *     Length       : Data length for this record.
739  *     Data         : Pointer to data to store.  NO-OP (B_ERROR) if NULL.
740  *
741  * Returns:
742  *     B_OK/B_ERROR on success/failure.
743  *     B_NO_INIT if this instance not properly initialized.
744  *
745  * Remarks:
746  *   * No need to worry about host-to-network byte ordering conversion --
747  *     since this is a /private/ helper said conversion, where applicable,
748  *     takes place in our caller (see AddIntXX if you haven't already passed
749  *     them on your way down to this humble helper).
750  */
751 status_t BNetBuffer::dpush( int32 Type, int32 Length, const void* Data )
752 {
753     if ( m_init != B_OK )
754     {
755         return B_NO_INIT;
756     }
757 
758     if ( Data == NULL )
759     {
760         return B_ERROR;
761     }
762 
763     int32 int32size = sizeof( int32 ); // <--Performance: called a lot here.
764     int32 recordSize = ( ( int32size * 4 ) + Length );
765 
766     if ( resize( fCapacity + recordSize ) != B_OK )
767     {
768         return B_ERROR;
769     }
770 
771     unsigned char* stackPtr = &fData[fStackSize];
772     *( int32 * )stackPtr = DATA_START_MARKER;
773 
774     stackPtr += int32size;
775     *( int32 * )stackPtr = Type;
776 
777     stackPtr += int32size;
778     *( int32 * )stackPtr = Length;
779 
780     stackPtr += int32size;
781     memcpy( stackPtr, Data, Length );
782 
783     stackPtr += Length;
784     *( int32 * )stackPtr = DATA_END_MARKER;
785 
786     // Groovy!  All stored, dot our t's and cross our i's.
787     fDataSize += Length;
788     fStackSize += recordSize;
789 
790     return B_OK;
791 }
792 
793 
794 /* resize
795  *=--------------------------------------------------------------------------=*
796  * Purpose:
797  *     Grow the internal data buffer to accommodate the requested size.
798  *
799  * //STM: What about shrinking the buffer to conserve resources?
800  *
801  * Input parameter:
802  *     NewSize      : New size of data buffer.  Duh.
803  *     RegenStack   : If true, do not try to preserve the existing stack.
804  *                    Default is false (preserve existing data).
805  *
806  * Returns:
807  *     B_OK: Success.
808  *     B_NO_MEMORY: if we cannot allocate memory for the data buffer.
809  *
810  * Remarks:
811  *   * In keeping with the "contract programming" development model: this is
812  *     a "safe copy" method in that the class' existing member data will not
813  *     be disturbed in the event of failure.  When our caller gets back a
814  *     success status (B_OK), said caller is guaranteed that all is well.
815  *     Laterally, in the event this method returns failure, the caller is
816  *     guaranteed that the class' existing member data is left untouched,
817  *     thereby preserving the existing instance's integrity.
818  *
819  *   * Buffer size is adjusted on a 16-byte granularity to keep from beating
820  *     the bleep out of the memory pool (can you say "fragmentation" boys and
821  *     girls?).  To tweak the granularity, change the GRANULARITY token at the
822  *     beginning of this method.  For best results, GRANULARITY should be a
823  *     power of two, thankyouverymuch.
824  *
825  *   * 'RegenStack' and its intented behavior is a bit of a double-negative.
826  *     May have to change this to eliminate said double negative if it proves
827  *     to be too confusing.
828  */
829 status_t BNetBuffer::resize( int32 NewSize, bool RegenStack )
830 {
831 #define GRANULARITY 16
832 
833     unsigned char* newData;
834     int32 newCapacity;
835 
836     newCapacity = ( ( ( NewSize / GRANULARITY ) + 1 ) * GRANULARITY );
837 
838     // Don't waste cycles or thrash the memory pool if we don't need to...
839     if ( newCapacity <= fCapacity )
840     {
841         return B_OK;
842     }
843 
844 /*  STM: If we decide to shrink as well as grow, then replace above with:
845     if ( newCapacity == fCapacity )
846     {
847         return B_OK;
848     }
849 */
850 
851     newData = new unsigned char[newCapacity];
852     if ( newData == NULL )
853     {
854         return B_NO_MEMORY;
855     }
856 
857     // Paranoid?  Pffffsh.  You bet.  :-)
858     memset( newData, 0, newCapacity );
859 
860     if ( fData != NULL )
861     {
862         if ( !RegenStack )
863         {
864             memcpy( newData, fData, fStackSize );
865         }
866 
867         delete fData;
868     }
869 
870     fData = newData;
871     fCapacity = newCapacity;
872 
873     return B_OK;
874 
875 #undef GRANULARITY
876 }
877 
878 
879 /* Reserved methods, for future evolution */
880 
881 void BNetBuffer::_ReservedBNetBufferFBCCruft1()
882 {
883 }
884 
885 void BNetBuffer::_ReservedBNetBufferFBCCruft2()
886 {
887 }
888 
889 void BNetBuffer::_ReservedBNetBufferFBCCruft3()
890 {
891 }
892 
893 void BNetBuffer::_ReservedBNetBufferFBCCruft4()
894 {
895 }
896 
897 void BNetBuffer::_ReservedBNetBufferFBCCruft5()
898 {
899 }
900 
901 void BNetBuffer::_ReservedBNetBufferFBCCruft6()
902 {
903 }
904 
905 
906 /*=------------------------------------------------------------------- End -=*/
907