import io

class BytesFIFO(object):
    """
    A FIFO that can store a fixed number of bytes.
    """
    def __init__(self, init_size):
        """ Create a FIFO of ``init_size`` bytes. """
        self._buffer = io.BytesIO(b"\x00"*init_size)
        self._size = init_size
        self._filled = 0
        self._read_ptr = 0
        self._write_ptr = 0

    def read(self, size=-1):
        """
        Read at most ``size`` bytes from the FIFO.
        
        If less than ``size`` bytes are available, or ``size`` is negative,
        return all remaining bytes.
        """
        if size < 0:
            size = self._filled
        
        # Go to read pointer
        self._buffer.seek(self._read_ptr)

        # Figure out how many bytes we can really read
        size = min(size, self._filled)
        contig = self._size - self._read_ptr
        contig_read = min(contig, size)

        ret =  self._buffer.read(contig_read)
        self._read_ptr += contig_read
        if contig_read < size:
            leftover_size = size - contig_read
            self._buffer.seek(0)
            ret += self._buffer.read(leftover_size)
            self._read_ptr = leftover_size

        self._filled -= size
        
        return ret

    def write(self, data):
        """
        Write as many bytes of ``data`` as are free in the FIFO.
        
        If less than ``len(data)`` bytes are free, write as many as can be written.
        Returns the number of bytes written.
        """
        free = self.free()
        write_size = min(len(data), free)

        if write_size:
            contig = self._size - self._write_ptr
            contig_write = min(contig, write_size)
            # TODO: avoid 0 write
            # TODO: avoid copy
            # TODO: test performance of above
            self._buffer.seek(self._write_ptr)
            self._buffer.write(data[:contig_write])
            self._write_ptr += contig_write

            if contig < write_size:
                self._buffer.seek(0)
                self._buffer.write(data[contig_write:write_size])
                #self._buffer.write(buffer(data, contig_write, write_size - contig_write))
                self._write_ptr = write_size - contig_write

        self._filled += write_size

        return write_size

    def flush(self):
        """ Flush all data from the FIFO. """
        self._filled = 0
        self._read_ptr = 0
        self._write_ptr = 0
        
    def empty(self):
        """ Return ```True``` if FIFO is empty. """
        return self._filled == 0

    def full(self):
        """ Return ``True`` if FIFO is full. """
        return self._filled == self._size

    def free(self):
        """ Return the number of bytes that can be written to the FIFO. """
        return self._size - self._filled

    def capacity(self):
        """ Return the total space allocated for this FIFO. """
        return self._size

    def __len__(self):
        """ Return the amount of data filled in FIFO """
        return self._filled

    def __nonzero__(self):
        """ Return ```True``` if the FIFO is not empty. """
        return self._filled > 0

    def resize(self, new_size):
        """
        Resize FIFO to contain ``new_size`` bytes. If FIFO currently has
        more than ``new_size`` bytes filled, :exc:`ValueError` is raised.
        If ``new_size`` is less than 1, :exc:`ValueError` is raised.

        If ``new_size`` is smaller than the current size, the internal
        buffer is not contracted (yet).
        """
        if new_size < 1:
            raise ValueError("Cannot resize to zero or less bytes.")

        if new_size < self._filled:
            raise ValueError("Cannot contract FIFO to less than {} bytes, "
                             "or data will be lost.".format(self._filled))

        # original data is non-contiguous. we need to copy old data,
        # re-write to the beginning of the buffer, and re-sync
        # the read and write pointers.
        if self._read_ptr >= self._write_ptr:
            old_data = self.read(self._filled)
            self._buffer.seek(0)
            self._buffer.write(old_data)
            self._filled = len(old_data)
            self._read_ptr = 0
            self._write_ptr = self._filled
        
        self._size = new_size

import io

class BytesFIFO(object):
    """
    A FIFO that can store a fixed number of bytes.
    """
    def __init__(self, init_size):
        """ Create a FIFO of ``init_size`` bytes. """
        self._buffer = io.BytesIO(b"\x00"*init_size)
        self._size = init_size
        self._filled = 0
        self._read_ptr = 0
        self._write_ptr = 0

    def read(self, size=-1):
        """
        Read at most ``size`` bytes from the FIFO.
        
        If less than ``size`` bytes are available, or ``size`` is negative,
        return all remaining bytes.
        """
        if size < 0:
            size = self._filled
        
        # Go to read pointer
        self._buffer.seek(self._read_ptr)

        # Figure out how many bytes we can really read
        size = min(size, self._filled)
        contig = self._size - self._read_ptr
        contig_read = min(contig, size)

        ret =  self._buffer.read(contig_read)
        self._read_ptr += contig_read
        if contig_read < size:
            leftover_size = size - contig_read
            self._buffer.seek(0)
            ret += self._buffer.read(leftover_size)
            self._read_ptr = leftover_size

        self._filled -= size
        
        return ret

    def write(self, data):
        """
        Write as many bytes of ``data`` as are free in the FIFO.
        
        If less than ``len(data)`` bytes are free, write as many as can be written.
        Returns the number of bytes written.
        """
        free = self.free()
        write_size = min(len(data), free)

        if write_size:
            contig = self._size - self._write_ptr
            contig_write = min(contig, write_size)
            # TODO: avoid 0 write
            # TODO: avoid copy
            # TODO: test performance of above
            self._buffer.seek(self._write_ptr)
            self._buffer.write(data[:contig_write])
            self._write_ptr += contig_write

            if contig < write_size:
                self._buffer.seek(0)
                self._buffer.write(data[contig_write:write_size])
                #self._buffer.write(buffer(data, contig_write, write_size - contig_write))
                self._write_ptr = write_size - contig_write

        self._filled += write_size

        return write_size

    def flush(self):
        """ Flush all data from the FIFO. """
        self._filled = 0
        self._read_ptr = 0
        self._write_ptr = 0
        
    def empty(self):
        """ Return ```True``` if FIFO is empty. """
        return self._filled == 0

    def full(self):
        """ Return ``True`` if FIFO is full. """
        return self._filled == self._size

    def free(self):
        """ Return the number of bytes that can be written to the FIFO. """
        return self._size - self._filled

    def capacity(self):
        """ Return the total space allocated for this FIFO. """
        return self._size

    def __len__(self):
        """ Return the amount of data filled in FIFO """
        return self._filled

    def __nonzero__(self):
        """ Return ```True``` if the FIFO is not empty. """
        return self._filled > 0

    def resize(self, new_size):
        """
        Resize FIFO to contain ``new_size`` bytes. If FIFO currently has
        more than ``new_size`` bytes filled, :exc:`ValueError` is raised.
        If ``new_size`` is less than 1, :exc:`ValueError` is raised.

        If ``new_size`` is smaller than the current size, the internal
        buffer is not contracted (yet).
        """
        if new_size < 1:
            raise ValueError("Cannot resize to zero or less bytes.")

        if new_size < self._filled:
            raise ValueError("Cannot contract FIFO to less than {} bytes, "
                             "or data will be lost.".format(self._filled))

        # original data is non-contiguous. we need to copy old data,
        # re-write to the beginning of the buffer, and re-sync
        # the read and write pointers.
        if self._read_ptr >= self._write_ptr:
            old_data = self.read(self._filled)
            self._buffer.seek(0)
            self._buffer.write(old_data)
            self._filled = len(old_data)
            self._read_ptr = 0
            self._write_ptr = self._filled
        
        self._size = new_size

A FIFO that can store a fixed number of bytes. Create a FIFO of ``init_size`` bytes. Read at most ``size`` bytes from the FIFO.                  If less than ``size`` bytes are available, or ``size`` is negative,         return all remaining bytes. # Go to read pointer # Figure out how many bytes we can really read Write as many bytes of ``data`` as are free in the FIFO.                  If less than ``len(data)`` bytes are free, write as many as can be written.         Returns the number of bytes written. # TODO: avoid 0 write # TODO: avoid copy # TODO: test performance of above #self._buffer.write(buffer(data, contig_write, write_size - contig_write)) Flush all data from the FIFO. Return ```True``` if FIFO is empty. Return ``True`` if FIFO is full. Return the number of bytes that can be written to the FIFO. Return the total space allocated for this FIFO. Return the amount of data filled in FIFO Return ```True``` if the FIFO is not empty. Resize FIFO to contain ``new_size`` bytes. If FIFO currently has         more than ``new_size`` bytes filled, :exc:`ValueError` is raised.         If ``new_size`` is less than 1, :exc:`ValueError` is raised.          If ``new_size`` is smaller than the current size, the internal         buffer is not contracted (yet). # original data is non-contiguous. we need to copy old data, # re-write to the beginning of the buffer, and re-sync # the read and write pointers.