cdef extern from "unistd.h": int read(int fileno, char *buffer, int nbytes) int write(int fileno, char *buffer, int nbytes) cdef extern from "malloc.h": char *malloc(int length) void free(char *buffer) import sys import exceptions cdef class Ring_buffer: # private instance variables cdef int begin cdef int end cdef int buffer_size cdef char *buffer def __init__(self, buffer_size): '''Constructor''' # the first byte we're using self.begin = 0 # one byte past the last byte we're using - like a python slice self.end = 0 # we can actually store this many bytes-1... because otherwise we couldn't distinguish between empty and 100% full self.buffer_size = buffer_size self.buffer = malloc(self.buffer_size) maybe_null = self.buffer assert maybe_null != 0 def __dealloc__(self): '''Deallocate the buffer''' free(self.buffer) cpdef empty(self): '''Return true if the buffer is empty''' if self.begin == self.end: return True else: return False cpdef full(self): '''Return true if the buffer is full''' if self.begin == (self.end + 1) % self.buffer_size: return True else: return False def __len__(self): '''Return the number of bytes in the buffer''' if self.begin == self.end: amount_in_use = 0 elif self.begin < self.end: # begin is to the left of end, that's simple amount_in_use = self.end - self.begin else: # begin is after the end; it's in a wrapped state amount_in_use = self.end + self.buffer_size - self.begin return amount_in_use cpdef would_overflow(self, length): '''Return true if adding a string of length would cause the buffer to overflow''' # compute how much remains in the buffer, then compare to length amount_in_use = len(self) if amount_in_use + length >= self.buffer_size: return True else: return False cpdef would_underflow(self, length): '''Return true if removing a string of length would cause the buffer to underflow''' # compute how much is in the buffer, then compare to length amount_in_use = len(self) sys.stdout.flush() if amount_in_use < length: return True else: return False cdef would_add_wrap(self, length): '''Return true if adding a string of length would wrap from the end of the buffer around to the beginning''' if self.end + length >= self.buffer_size: return True else: return False cdef would_subtract_wrap(self, length): '''Return true if subtracting a string of length would wrap from the end of the buffer around to the beginning''' if self.begin + length >= self.buffer_size: return True else: return False cdef split_to_two_add(self, length): '''Return the lengths of the two pieces at the end and beginning of the buffer; assumes we're going to wrap''' first_begin = self.end first_end = self.buffer_size - 1 first_length = first_end - first_begin second_begin = 0 second_end = self.buffer_size - first_length second_length = second_end - second_begin return first_length, second_length cdef split_to_two_subtract(self, length): '''Return the lengths of the two pieces at the end and beginning of the buffer; assumes we're going to wrap''' first_length = self.buffer_size - self.begin second_length = length - first_length return first_length, second_length cpdef add_from_fileno(self, fileno, length_to_add): '''Add to the buffer length_to_add bytes from fileno - private''' if self.would_overflow(length_to_add): raise exceptions.BufferError, "Buffer overflow" # FIXME: We need to do something about EINTR on these 3 read's! if self.would_add_wrap(length_to_add): # do the read in two pieces first_length, second_length = self.split_to_two_add(length_to_add) length_added = read(fileno, &self.buffer[self.end], first_length) assert length_added == first_length, "length_added != first_length" length_added = read(fileno, self.buffer, second_length) assert length_added == second_length, "length_added != second_length" self.end = second_length else: # do the read in one piece length_added = read(fileno, &self.buffer[self.end], length_to_add) assert length_added == length_to_add, "length_added != length_to_add" self.end += length_to_add cpdef add_from_string(self, string_): '''Add string_ to the buffer''' cdef int string_index cdef int buffer_index length_to_add = len(string_) if self.would_overflow(length_to_add): raise exceptions.BufferError, "Buffer overflow" if self.would_add_wrap(length_to_add): # do the "read" in two pieces first_length, second_length = self.split_to_two_add(length_to_add) for string_index in range(length_to_add): buffer_index = (self.end + string_index) % self.buffer_size self.buffer[buffer_index] = ord(string_[string_index]) self.end = (self.end + length_to_add) % self.buffer_size else: # do the "read" in one piece string_index = 0 for buffer_index in range(self.end, self.end + length_to_add): self.buffer[buffer_index] = ord(string_[string_index]) string_index += 1 self.end += length_to_add cpdef subtract_to_fileno(self, fileno, length_to_subtract=None): '''Remove from the buffer, putting the extracted bytes into fileno''' if length_to_subtract == None: length_to_subtract = len(self) if self.would_underflow(length_to_subtract): raise exceptions.BufferError, "Buffer underflow" if self.would_subtract_wrap(length_to_subtract): # there's wrap - do the write in two pieces first_length, second_length = self.split_to_two_subtract(length_to_subtract) length_subtracted = write(fileno, &self.buffer[self.begin], first_length) assert length_subtracted == first_length length_subtracted = write(fileno, self.buffer, second_length) assert length_subtracted == second_length else: # no wrap - do the write in one piece print 'subtract_to_fileno: length_to_subtract is %d' % length_to_subtract length_subtracted = write(fileno, &self.buffer[self.begin], length_to_subtract) assert length_subtracted == length_to_subtract self.begin += length_to_subtract cpdef peek_to_string(self, length_to_peek=None): '''Peek at length_to_peek bytes in the buffer - don't subtract them''' if length_to_peek == None: length_to_peek = len(self) return self.subtract_to_string(length_to_peek, True) cpdef subtract_to_string(self, length_to_subtract, peek=False): '''Pull length_to_subtract bytes from the buffer''' if length_to_subtract == None: length_to_subtract = len(self) if self.would_underflow(length_to_subtract): raise exceptions.BufferError, "Buffer underflow" if self.would_subtract_wrap(length_to_subtract): # do the "write" in two pieces first_length, second_length = self.split_to_two_subtract(length_to_subtract) list_ = [] for index in xrange(self.begin, self.begin+first_length): list_.append(self.buffer[index]) for index in xrange(second_length): list_.append(self.buffer[index]) list_of_strings = [ chr(byte) for byte in list_ ] result = ''.join(list_of_strings) if not peek: self.begin = (self.begin + length_to_subtract) % self.buffer_size assert len(result) == length_to_subtract return result else: # do the "write" in one piece list_ = [] for index in xrange(self.begin, self.begin+length_to_subtract): list_.append(chr(self.buffer[index])) result = ''.join(list_) assert len(result) == length_to_subtract if not peek: self.begin = (self.begin + length_to_subtract) % self.buffer_size return result def __iter__(self): '''Return ourself - so we can iterate over ourself''' return self # The Cython doc says you must define __next__ and must not define next, but it seems in reality __next__ is ignored and next works. def __next__(self): '''Toy iterator example - will be removed eventually''' retval = self.value self.value *= 2 if retval >= 10e200: raise exceptions.StopIteration else: return retval