Read the given length of bytes, or return None if we can't provide 
        [all of] them yet. When the given length is available but ends in the 
        middle of a buffer, we'll split the buffer. We do this to make it 
        simpler to continue from that point next time (it's always simpler to 
        start at the beginning of a buffer), as well as simpler to remove the
        found bytes later, if need be.

def __passive_read(self, length, start_buffer_index=None):
        """Read the given length of bytes, or return None if we can't provide 
        [all of] them yet. When the given length is available but ends in the 
        middle of a buffer, we'll split the buffer. We do this to make it 
        simpler to continue from that point next time (it's always simpler to 
        start at the beginning of a buffer), as well as simpler to remove the
        found bytes later, if need be.
        """

        if length > self.__length:
            return None
    
        with self.__class__.__locker:
            collected = []
            need_bytes = length
            i = start_buffer_index if start_buffer_index is not None \
                                   else self.__read_buffer_index

            updates = []
            while need_bytes > 0:
                len_current_buffer = len(self.__buffers[i])
                
                if need_bytes >= len_current_buffer:
                    # We need at least as many bytes as are in the current 
                    # buffer. Consume them all.
                
                    collected.append(self.__buffers[i][:])
                    updates.append((i, [], len_current_buffer))
                    need_bytes -= len_current_buffer
                else:
                    # We need less bytes than are in the current buffer. Slice
                    # the current buffer in half, even if the data isn't going
                    # anywhere [yet].
                
                    first_half = self.__buffers[i][:need_bytes]
                    second_half = self.__buffers[i][need_bytes:]

                    self.__buffers[i] = first_half
                    self.__buffers.insert(i + 1, second_half)
                    
                    # We only mark the buffer that came from the first half as
                    # having an update (the second half of the buffer wasn't 
                    # touched).
                    collected.append(first_half)
                    updates.append((i, [], need_bytes))
                    need_bytes = 0

                i += 1

        sequence = ''.join(collected)

        return (sequence, i, updates)