IOS Streaming Browser 1.0
An IOS streaming browser to stream the display to others or to a projector

GCDAsyncReadPacket Class Reference

Collaboration diagram for GCDAsyncReadPacket:

Public Member Functions

(id) - initWithData:startOffset:maxLength:timeout:readLength:terminator:tag:
(void) - ensureCapacityForAdditionalDataOfLength:
(NSUInteger) - optimalReadLengthWithDefault:shouldPreBuffer:
(NSUInteger) - readLengthForNonTermWithHint:
(NSUInteger) - readLengthForTermWithHint:shouldPreBuffer:
(NSUInteger) - readLengthForTermWithPreBuffer:found:
(NSInteger) - searchForTermAfterPreBuffering:

Data Fields

NSMutableData * buffer
NSUInteger startOffset
NSUInteger bytesDone
NSUInteger maxLength
NSTimeInterval timeout
NSUInteger readLength
NSDataterm
BOOL bufferOwner
NSUInteger originalBufferLength
long tag

Detailed Description

The GCDAsyncReadPacket encompasses the instructions for any given read. The content of a read packet allows the code to determine if we're:

  • reading to a certain length
  • reading to a certain separator
  • or simply reading the first chunk of available data

Definition at line 596 of file GCDAsyncSocket.m.


Member Function Documentation

- (void) ensureCapacityForAdditionalDataOfLength: (NSUInteger)  bytesToRead

Ensure the read buffer has the capacity for additional data param NSUInteger

Increases the length of the buffer (if needed) to ensure a read of the given size will fit. param NSUInteger

Definition at line 782 of file GCDAsyncSocket.m.

                                               :(NSUInteger)bytesToRead
{
    // Gets the read buffer size
        NSUInteger buffSize = [buffer length];
    
    // Determines the amount of the buffer used by adding the buffer
    // offset plus the number of bytes that have been read so far for the read operation
        NSUInteger buffUsed = startOffset + bytesDone;
        
    // Computes the space available on the buffer by subtracting
    // the amount of the buffer used from the total buffer size
        NSUInteger buffSpace = buffSize - buffUsed;
        
    // If the bytes yet to read is greater than the buffer size then
    // increase the size of the buffer by the difference
        if (bytesToRead > buffSpace)
        {
        
        // Determine the size to increase the buffer
                NSUInteger buffInc = bytesToRead - buffSpace;
                
        
        // Increase the size of the read buffer
                [buffer increaseLengthBy:buffInc];
        }
}
- (id) initWithData: (NSMutableData *)  d
startOffset: (NSUInteger)  s
maxLength: (NSUInteger)  m
timeout: (NSTimeInterval)  t
readLength: (NSUInteger)  l
terminator: (NSData *)  e
tag: (long)  i 

param NSMutableData param NSUInteger param NSUInteger param NSTimeInterval param NSUInteger param NSData param long returns id

Initialize the GCDAsyncReadPacket param NSMutableData param NSUInteger param NSUInteger param NSTimeInterval param NSUInteger param NSData param long returns id

Definition at line 731 of file GCDAsyncSocket.m.

                  :(NSMutableData *)d
       startOffset:(NSUInteger)s  // Number of characerts from the start
         maxLength:(NSUInteger)m  // maximum length
           timeout:(NSTimeInterval)t  // timeout for the packet
        readLength:(NSUInteger)l 
        terminator:(NSData *)e 
               tag:(long)i
{
        if((self = [super init]))
        {
                bytesDone = 0; // number of bytes that have been read so far for the read operation
        
                maxLength = m; // set the maximum length of the read packet
                timeout = t;  // set the read timeout
                readLength = l; // set the read length
                term = [e copy];  // set the terminator
                tag = i;
                
                if (d) // if there is mutable data passed-in to initialize the method then copy the buffer, set the offset and buffer length
                {
            // the read buffer
                        buffer = [d retain];
                        startOffset = s; 
                        bufferOwner = NO; // if there is not a buffer owner
                        originalBufferLength = [d length];
                }
                else // if there is not mutable data
                {
                        if (readLength > 0)
            {
                // Initialize the read buffer with a specific length
                                buffer = [[NSMutableData alloc] initWithLength:readLength];
                
                
                        }else{ // If readLength is less than or equal to zero
                                buffer = [[NSMutableData alloc] initWithLength:0];
                        }
            
            
                        startOffset = 0;
                        bufferOwner = YES; // If there is a buffer owner
                        originalBufferLength = 0; 
                }
        }
        return self;
}
- (NSUInteger) optimalReadLengthWithDefault: (NSUInteger)  defaultValue
shouldPreBuffer: (BOOL *)  shouldPreBufferPtr 

The optimal read length with a default value, and whether should prebuffer param NSUInteger param BOOL returns NSUInteger

This method is used when we do NOT know how much data is available to be read from the socket. This method returns the default value unless it exceeds the specified readLength or maxLength.

Furthermore, the shouldPreBuffer decision is based upon the packet type, and whether the returned value would fit in the current buffer without requiring a resize of the buffer. param NSUInteger param BOOL returns NSUInteger

Definition at line 819 of file GCDAsyncSocket.m.

                                          :(NSUInteger)defaultValue shouldPreBuffer:(BOOL *)shouldPreBufferPtr
{
    // Local variable for holding the result
        NSUInteger result;
        
    // If the length of the bytes in the packet is greater than zero
        if (readLength > 0)
        {
                // Read a specific length of data
        
                // Set the result to the lesser of the default value of bytes, or the length of the read packet less the bytes already read
                result = MIN(defaultValue, (readLength - bytesDone));
                
                // There is no need to prebuffer since we know exactly how much data we need to read.
                // Even if the buffer isn't currently big enough to fit this amount of data,
                // it would have to be resized eventually anyway.
                
        // Whether should prebuffer the data
                if (shouldPreBufferPtr){
                        *shouldPreBufferPtr = NO;
        }
        }
        else // if readLength is equal to zero
        {
                // Either reading until we find a specified terminator,
                // or we're simply reading all available data.
                // 
                // In other words, one of:
                // 
                // - readDataToData packet
                // - readDataWithTimeout packet
                
                if (maxLength > 0)
        {
                        result =  MIN(defaultValue, (maxLength - bytesDone));
            
                }else{ // if maximum length is not greater than zero
            
                        result = defaultValue;
                }
        
                // Since we don't know the size of the read in advance,
                // the shouldPreBuffer decision is based upon whether the returned value would fit in the current buffer without requiring a resize of the buffer.
                // 
                // This is because, in all likelyhood, the amount read from the socket will be less than the default value.
                // Thus we should avoid over-allocating the read buffer when we can simply use the pre-buffer instead.
                
        // Whether should pre-buffer
                if (shouldPreBufferPtr)
                {
            // Gets the buffer size
                        NSUInteger buffSize = [buffer length];
            
            // Get the amount of the buffer which has been utilized
                        NSUInteger buffUsed = startOffset + bytesDone;
                        
            // Gets the amount of available space in the bufer
                        NSUInteger buffSpace = buffSize - buffUsed;
                        
            // If the available space in the read buffer is larger than the default size than we don't need to prebuffer
                        if (buffSpace >= result)
            {
                                *shouldPreBufferPtr = NO;
                        }else{ // if the available space in the read buffer is less than the default size than we need to prebuffer the request
                                *shouldPreBufferPtr = YES;
            }
                }
        }
        
    // Returns the optimal read length
        return result;
}
- (NSUInteger) readLengthForNonTermWithHint: (NSUInteger)  bytesAvailable

Reads length from data without a terminator param NSUInteger returns NSUInteger

For read packets without a set terminator, returns the amount of data that can be read without exceeding the readLength or maxLength.

The given parameter indicates the number of bytes estimated to be available on the socket, which is taken into consideration during the calculation.

The given hint MUST be greater than zero. param NSUInteger returns NSUInteger

Definition at line 903 of file GCDAsyncSocket.m.

                                          :(NSUInteger)bytesAvailable
{
    // Test whether there is a terminator
        NSAssert(term == nil, @"This method does not apply to term reads");
        
    // Test if there are bytes available to read
    NSAssert(bytesAvailable > 0, @"Invalid parameter: bytesAvailable");
        
    // If the read packet has length
        if (readLength > 0)
        {
                // Read a specific length of data
                
                return MIN(bytesAvailable, (readLength - bytesDone));
                
                // No need to avoid resizing the buffer.
                // If the user provided their own buffer,
                // and told us to read a certain length of data that exceeds the size of the buffer,
                // then it is clear that our code will resize the buffer during the read operation.
                // 
                // This method does not actually do any resizing.
                // The resizing will happen elsewhere if needed.
        }
        else
        {
                // Read all available data
                
        // Get the number of bytes available to read
                NSUInteger result = bytesAvailable;
                
        // If the maximum length is set
                if (maxLength > 0)
                {
            // Get the lesser of the bytes available, or the maximum length minus the bytesDone reading or writing
                        result = MIN(result, (maxLength - bytesDone));
                }
                
                // No need to avoid resizing the buffer.
                // If the user provided their own buffer,
                // and told us to read all available data without giving us a maxLength,
                // then it is clear that our code might resize the buffer during the read operation.
                // 
                // This method does not actually do any resizing.
                // The resizing will happen elsewhere if needed.
                
                return result;
        }
}
- (NSUInteger) readLengthForTermWithHint: (NSUInteger)  bytesAvailable
shouldPreBuffer: (BOOL *)  shouldPreBufferPtr 

Reads length of data which has a terminator param NSUInteger param BOOL returns NSUInteger

For read packets with a set terminator, returns the amount of data that can be read without exceeding the maxLength.

The given parameter indicates the number of bytes estimated to be available on the socket, which is taken into consideration during the calculation.

To optimize memory allocations, mem copies, and mem moves the shouldPreBuffer boolean value will indicate if the data should be read into a prebuffer first, or if the data can be read directly into the read packet's buffer. param NSUInteger (count of bytes available to read) param BOOL returns NSUInteger

Definition at line 963 of file GCDAsyncSocket.m.

                                       :(NSUInteger)bytesAvailable shouldPreBuffer:(BOOL *)shouldPreBufferPtr
{
    // Test whether the terminator is not nil
        NSAssert(term != nil, @"This method does not apply to non-term reads");
    
    // Test whether there are bytes available to read
        NSAssert(bytesAvailable > 0, @"Invalid parameter: bytesAvailable");
        
        // Gets teh number of bytes available to read
        NSUInteger result = bytesAvailable;
        
    // if the maximum length of the read packet is greater than zero
        if (maxLength > 0)
        {
        
        // Get the lesser of the result or the maximum length less the number of bytes read from the read operation
                result = MIN(result, (maxLength - bytesDone));
        }
        
        // Should the data be read into the read packet's buffer, or into a pre-buffer first?
        // 
        // One would imagine the preferred option is the faster one.
        // So which one is faster?
        // 
        // Reading directly into the packet's buffer requires:
        // 1. Possibly resizing packet buffer (malloc/realloc)
        // 2. Filling buffer (read)
        // 3. Searching for term (memcmp)
        // 4. Possibly copying overflow into prebuffer (malloc/realloc, memcpy)
        // 
        // Reading into prebuffer first:
        // 1. Possibly resizing prebuffer (malloc/realloc)
        // 2. Filling buffer (read)
        // 3. Searching for term (memcmp)
        // 4. Copying underflow into packet buffer (malloc/realloc, memcpy)
        // 5. Removing underflow from prebuffer (memmove)
        // 
        // Comparing the performance of the two we can see that reading
        // data into the prebuffer first is slower due to the extra memove.
        // 
        // However:
        // The implementation of NSMutableData is open source via core foundation's CFMutableData.
        // Decreasing the length of a mutable data object doesn't cause a realloc.
        // In other words, the capacity of a mutable data object can grow, but doesn't shrink.
        // 
        // This means the prebuffer will rarely need a realloc.
        // The packet buffer, on the other hand, may often need a realloc.
        // This is especially true if we are the buffer owner.
        // Furthermore, if we are constantly realloc'ing the packet buffer,
        // and then moving the overflow into the prebuffer,
        // then we're consistently over-allocating memory for each term read.
        // And now we get into a bit of a tradeoff between speed and memory utilization.
        // 
        // The end result is that the two perform very similarly.
        // And we can answer the original question very simply by another means.
        // 
        // If we can read all the data directly into the packet's buffer without resizing it first,
        // then we do so. Otherwise we use the prebuffer.
        
        if (shouldPreBufferPtr)
        {
        // Gets the buffer size
                NSUInteger buffSize = [buffer length];
        
        // Gets the amount of the buffer used by getting the offset and adding the number of bytes that have been read so far for the read operation
                NSUInteger buffUsed = startOffset + bytesDone;
                
        // Check if the buffer size is large enough to hold the result.  If so, then we don't need to prebuffer
                if ((buffSize - buffUsed) >= result)
        {
                        *shouldPreBufferPtr = NO;
                }else{  // If the buffer size is not large enough to hold the result, then pre-buffer
                        *shouldPreBufferPtr = YES;
        }
        }
        
    // Returns the read length
        return result;
}
- (NSUInteger) readLengthForTermWithPreBuffer: (NSData *)  preBuffer
found: (BOOL *)  foundPtr 

Reads length of data which has a terminator but which is larger than the buffer so we need to prebuffer the data param NSData param BOOL return NSUInteger

For read packets with a set terminator,returns the amount of data that can be read from the given preBuffer,without going over a terminator or the maxLength.

It is assumed the terminator has not already been read.

param NSData param BOOL returns NSUInteger

Definition at line 1052 of file GCDAsyncSocket.m.

                                            :(NSData *)preBuffer found:(BOOL *)foundPtr
{
    // Test whether the terminator is not nil
        NSAssert(term != nil, @"This method does not apply to non-term reads");
    
    // Test whether the prebuffer length is greater than zero
        NSAssert([preBuffer length] > 0, @"Invoked with empty pre buffer!");
        
        // We know that the terminator, as a whole, doesn't exist in our own buffer.
        // But it is possible that a portion of it exists in our buffer.
        // So we're going to look for the terminator starting with a portion of our own buffer.
        // 
        // Example:
        // 
        // term length      = 3 bytes
        // bytesDone        = 5 bytes
        // preBuffer length = 5 bytes
        // 
        // If we append the preBuffer to our buffer,
        // it would look like this:
        // 
        // ---------------------
        // |B|B|B|B|B|P|P|P|P|P|
        // ---------------------
        // 
        // So we start our search here:
        // 
        // ---------------------
        // |B|B|B|B|B|P|P|P|P|P|
        // -------^-^-^---------
        // 
        // And move forwards...
        // 
        // ---------------------
        // |B|B|B|B|B|P|P|P|P|P|
        // ---------^-^-^-------
        // 
        // Until we find the terminator or reach the end.
        // 
        // ---------------------
        // |B|B|B|B|B|P|P|P|P|P|
        // ---------------^-^-^-
        
        BOOL found = NO;
        
    // Get the length of the terminator
        NSUInteger termLength = [term length];
    
    // Get the preBuffer length
        NSUInteger preBufferLength = [preBuffer length];
        
    
    // Check if the bytes done plus prebuffer lengh is less than the 
    // termination length.  The bytes done is the number of bytes that have been read so far for the read operation
        if ((bytesDone + preBufferLength) < termLength)
        {
                // Not enough data for a full term sequence yet
                return preBufferLength;
        }
        
    
    // Maximum prebuffer length
        NSUInteger maxPreBufferLength;
    
    // If the maximum prebuffer length is greater than zero
        if (maxLength > 0) {
        
        // Gets the maximum prebuffer length
                maxPreBufferLength = MIN(preBufferLength, (maxLength - bytesDone));
                
                // Note: maxLength >= termLength
        }
    // If the maximum prebuffer length is equal to or less than zero
        else {
        
        // Sets the maximum prebuffer length to the prebuffer length
                maxPreBufferLength = preBufferLength;
        }
        
    // the byte sequence
        Byte seq[termLength];
    
    // Create a constant read only local attribute
        const void *termBuf = [term bytes];
        
    
    // Buffer length
        NSUInteger bufLen = MIN(bytesDone, (termLength - 1));
    
    
        void *buf = [buffer mutableBytes] + startOffset + bytesDone - bufLen;
        
    
    // Prebuffer length
        NSUInteger preLen = termLength - bufLen;
    
    
        void *pre = (void *)[preBuffer bytes];
        
    // Set the loop count for searching through the buffer and prebuffer
        NSUInteger loopCount = bufLen + maxPreBufferLength - termLength + 1; // Plus one. See example above.
        
    
    // Prebuffer length
        NSUInteger result = preBufferLength;
        
    
        NSUInteger i;
    
    // Loop is the length of the buffer and pre-buffer
        for (i = 0; i < loopCount; i++)
        {
        // If there are bytes in the buffer
                if (bufLen > 0)
                {
                        // Combining bytes from buffer and preBuffer
            
            // Copies bufLen bytes from the bufer to the seq
                        memcpy(seq, buf, bufLen);
            
            // Copies preLen bytes from pre to seq plus bufLen
                        memcpy(seq + bufLen, pre, preLen);
                        
            // compare bytes in memory
                        if (memcmp(seq, termBuf, termLength) == 0)
                        {
                                result = preLen;
                                found = YES;
                                break;
                        }
                        
            // Increases the buffer size
                        buf++;
            
            // Decreases the buffer length
                        bufLen--;
            
            // Increases the prebuffer length
                        preLen++;
                }
                else // if buffer length is not greater than zero
                {
                        // Comparing directly from preBuffer
            
                        // compares byte string.  Compares prebuffer with termBuf - both bytes are assumed to be termLength long.  The memcmp function returns zero is the two byte strings are equal
                        if (memcmp(pre, termBuf, termLength) == 0)
                        {
                
                // Sets the prebuffer offset
                                NSUInteger preOffset = pre - [preBuffer bytes]; // pointer arithmetic
                                
                // Sets the result equal to the prebuffer offset plus the termLength (i.e. length of the terminator)
                                result = preOffset + termLength;
                
                // Found the terminator in the prebuffer
                                found = YES;
                
                                break;
                        }
                        
            // Increments the prebuffer
                        pre++;
                }
        }
        
        // There is no need to avoid resizing the buffer in this particular situation.
        
        if (foundPtr) 
    {
        *foundPtr = found;
    }
    
        return result;
}
- (NSInteger) searchForTermAfterPreBuffering: (ssize_t)  numBytes

Search for the terminator after prebuffering the data param ssize_t returns NSInteger

For read packets with a set terminator, scans the packet buffer for the term.

It is assumed the terminator had not been fully read prior to the new bytes.

If the term is found, the number of excess bytes after the term are returned.

If the term is not found, this method will return -1.

Note: A return value of zero means the term was found at the very en.

Prerequisites: The given number of bytes have been added to the end of our buffer. Our bytesDone variable has NOT been changed due to the prebuffered bytes.

param ssize_t returns NSInteger

Definition at line 1250 of file GCDAsyncSocket.m.

                                           :(ssize_t)numBytes
{
    // Test whether the terminator is not nil
        NSAssert(term != nil, @"This method does not apply to non-term reads");
        
        // The implementation of this method is very similar to the above method.
        // See the above method for a discussion of the algorithm used here.
        
        void *buff = [buffer mutableBytes];
    
    
    // The number of bytes that have been read so far for the read operation plus the number of bytes for prebuffering
        NSUInteger buffLength = bytesDone + numBytes;
        
    // Create a constant read only local attribute
    // Gets the size of the terminator
        const void *termBuff = [term bytes];
    
    // Gets the length of the terminator
        NSUInteger termLength = [term length];
        
        // Note: We are dealing with unsigned integers,
        // so make sure the math doesn't go below zero.
        
        NSUInteger i = ((buffLength - numBytes) >= termLength) ? (buffLength - numBytes - termLength + 1) : 0;
        
    // While the terimination length is less than or equal to the buffer length
        while (i + termLength <= buffLength)
        {
        
                void *subBuffer = buff + startOffset + i;
                
        // compare bytes in memory
        // Checks if the subBuffer equals the terminator buffer
                if (memcmp(subBuffer, termBuff, termLength) == 0)
                {
            // if the subBuffer and termBuffer are the same
                        return buffLength - (i + termLength);
                }
                
                i++;
        }
        
        return -1;
}

Field Documentation

- (NSMutableData*) buffer

read buffer

Definition at line 603 of file GCDAsyncSocket.m.

- (BOOL) bufferOwner

whether there is a buffer owner

Definition at line 639 of file GCDAsyncSocket.m.

- (NSUInteger) bytesDone

number of bytes that have been read so far for the read operation

Definition at line 613 of file GCDAsyncSocket.m.

- (NSUInteger) maxLength

maximum length

Definition at line 618 of file GCDAsyncSocket.m.

- (NSUInteger) originalBufferLength

original buffer length

Definition at line 644 of file GCDAsyncSocket.m.

- (NSUInteger) readLength

read length

Definition at line 628 of file GCDAsyncSocket.m.

- (NSUInteger) startOffset

start offset for read buffer

Definition at line 608 of file GCDAsyncSocket.m.

- (long) tag

An application-defined integer or pointer that will be sent as an argument to the -socket:didReadData:withTag: message sent to the delegate.

Definition at line 649 of file GCDAsyncSocket.m.

- (NSData*) term

terminator

Definition at line 634 of file GCDAsyncSocket.m.

- (NSTimeInterval) timeout

the timeout value for reading from a host

Definition at line 623 of file GCDAsyncSocket.m.


The documentation for this class was generated from the following file:
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Properties Defines