def test_resize(self): data = np.zeros(10) B = DataBuffer(data=data) data = np.zeros(20) B.set_data(data) assert B.nbytes == data.nbytes assert B._need_resize == True
def test_set_data_base(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data, store=True, copy=False) B.set_data(data) assert len(B._pending_data) == 1
def test_invalid_view_after_set_data(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data) Z = B[5:] B.set_data(np.zeros(15,dtype=dtype)) assert Z._valid == False
def __init__(self, send_seq, receive_seq, send_window, receive_window): self.snd_wnd = send_window self.snd_nxt = send_seq.clone() self.snd_una = send_seq.clone() self.rcv_nxt = receive_seq.clone() self.rcv_wnd = receive_window self.snd_wl1 = receive_seq.clone() self.snd_wl2 = send_seq.clone() self.in_buffer = DataBuffer(start_index=receive_seq.clone()) self.out_buffer = DataBuffer(start_index=send_seq.clone()) self.lock = threading.RLock()
def test_getitem_field(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data) Z = B["position"] assert Z.nbytes == 10 * 3 * np.dtype(np.float32).itemsize assert Z.offset == 0 assert Z.size == 10 assert Z.itemsize == 3 * np.dtype(np.float32).itemsize assert Z.stride == (3+2+4) * np.dtype(np.float32).itemsize assert Z.dtype == (np.float32, 3) Z = B["texcoord"] assert Z.nbytes == 10 * 2 * np.dtype(np.float32).itemsize assert Z.offset == 3 * np.dtype(np.float32).itemsize assert Z.size == 10 assert Z.itemsize == 2 * np.dtype(np.float32).itemsize assert Z.stride == (3+2+4) * np.dtype(np.float32).itemsize assert Z.dtype == (np.float32, 2) Z = B["color"] assert Z.nbytes == 10 * 4 * np.dtype(np.float32).itemsize assert Z.offset == (2+3) * np.dtype(np.float32).itemsize assert Z.size == 10 assert Z.itemsize == 4 * np.dtype(np.float32).itemsize assert Z.stride == (3+2+4) * np.dtype(np.float32).itemsize assert Z.dtype == (np.float32, 4)
def test_setitem_broadcast(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data, store=True, copy=False) B['position'] = 1,2,3 assert np.allclose( data['position'].ravel(), np.resize([1,2,3],30))
def test_setitem_field_no_storage(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data, store=False, copy=False) with self.assertRaises(ValueError): B['position'] = 1,2,3
def test_every_two_item_no_storage(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data, store=False) with self.assertRaises(ValueError): B[::2] = data[::2]
def test_set_data_base(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data, store=True, copy=False) # set_data on field is not allowed because set_data # can result in a buffer resize with self.assertRaises(ValueError): B['position'].set_data(data)
def test_setitem_ellipsis(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data1 = np.zeros(10,dtype=dtype) data2 = np.empty(10,dtype=dtype) B = DataBuffer(data1, store=True, copy=False) B[::2] = data2[::2] assert np.allclose(data1['position'][::2],data2['position'][::2]) assert np.allclose(data1['texcoord'][::2],data2['texcoord'][::2]) assert np.allclose(data1['color'][::2],data2['color'][::2])
def test_setitem_half(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data1 = np.zeros(10,dtype=dtype) data2 = np.empty(10,dtype=dtype) B = DataBuffer(data1, store=True, copy=False) B[:5] = data2[:5] assert np.allclose(data1['position'][:5],data2['position'][:5]) assert np.allclose(data1['texcoord'][:5],data2['texcoord'][:5]) assert np.allclose(data1['color'][:5],data2['color'][:5]) assert len(B._pending_data) == 2
def test_structured_init(self): # Check structured type dtype = np.dtype( [('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data) assert B.nbytes == data.nbytes assert B.offset == 0 assert B.size == 10 assert B.itemsize == data.itemsize assert B.stride == data.itemsize assert B.dtype == data.dtype
def test_default_init(self): # Check default storage and copy flags data = np.ones(100) B = DataBuffer(data) assert B._store == True assert B._copy == False assert B.nbytes == data.nbytes assert B.offset == 0 assert B.size == 100 assert B.itemsize == data.itemsize assert B.stride == data.itemsize assert B.dtype == data.dtype assert B._resizeable == True
def test_getitem_index(self): dtype = np.dtype( [ ('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4) ] ) data = np.zeros(10,dtype=dtype) B = DataBuffer(data) Z = B[0] assert Z.nbytes == 1 * (3+2+4) * np.dtype(np.float32).itemsize assert Z.offset == 0 assert Z.size == 1 assert Z.itemsize == (3+2+4) * np.dtype(np.float32).itemsize assert Z.stride == (3+2+4) * np.dtype(np.float32).itemsize assert Z.dtype == B.dtype
class PTCControlBlock(object): def __init__(self, send_seq, receive_seq, send_window, receive_window): self.snd_wnd = send_window self.snd_nxt = send_seq.clone() self.snd_una = send_seq.clone() self.rcv_nxt = receive_seq.clone() self.rcv_wnd = receive_window self.snd_wl1 = receive_seq.clone() self.snd_wl2 = send_seq.clone() self.in_buffer = DataBuffer(start_index=receive_seq.clone()) self.out_buffer = DataBuffer(start_index=send_seq.clone()) self.lock = threading.RLock() def get_snd_nxt(self): return self.snd_nxt def get_snd_una(self): return self.snd_una def get_snd_wnd(self): return self.snd_wnd def get_snd_wl1(self): return self.snd_wl1 def get_snd_wl2(self): return self.snd_wl2 def get_rcv_nxt(self): return self.rcv_nxt def get_rcv_wnd(self): return self.rcv_wnd def increment_snd_nxt(self): with self: self.snd_nxt += 1 def increment_snd_una(self): with self: self.snd_una += 1 def increment_rcv_nxt(self): with self: self.rcv_nxt += 1 def process_incoming(self, packet, ignore_payload=False): self.process_ack(packet) if not ignore_payload: self.process_payload(packet) def process_payload(self, packet): if self.payload_is_accepted(packet): seq_lo, seq_hi = packet.get_seq_interval() payload = packet.get_payload() lower = max(self.rcv_nxt, seq_lo) upper = min(self.rcv_nxt + self.rcv_wnd, seq_hi) # Honrar RCV_WND descartando aquellos bytes que están por debajo o # por encima de ella. effective_payload = payload[lower-seq_lo:upper-seq_lo] self.in_buffer.add_chunk(lower, effective_payload) if lower == self.rcv_nxt: # Deberíamos avanzar RCV_NXT dado que el límite inferior del # chunk recién agregado coincide con su valor anterior. El # buffer hace seguimiento de este valor a medida que se # insertan y borran datos de él. self.rcv_nxt = self.in_buffer.get_last_index() # Decrementar la ventana hasta que se eliminen datos del # buffer. self.rcv_wnd = self.rcv_wnd - len(effective_payload) def process_ack(self, packet): ack_number = packet.get_ack_number() if self.ack_is_accepted(ack_number): self.snd_una = ack_number if self.should_update_window(ack_number): self.update_window(packet) def ack_is_accepted(self, ack_number): # Aceptar sólo si SND_UNA < ACK <= SND_NXT return SequenceNumber.a_lt_b_leq_c(self.snd_una, ack_number, self.snd_nxt) def payload_is_accepted(self, packet): seq_lo, seq_hi = packet.get_seq_interval() first_byte, last_byte = seq_lo, seq_hi-1 first_ok = SequenceNumber.a_leq_b_leq_c(self.rcv_nxt, first_byte, self.rcv_nxt+self.rcv_wnd) last_ok = SequenceNumber.a_leq_b_leq_c(self.rcv_nxt, last_byte, self.rcv_nxt+self.rcv_wnd) return last_byte >= first_byte and (first_ok or last_ok) def should_update_window(self, ack_number): # TODO: agregar tests para esto. # RFC 1122, p.94 (corrección al RFC 793). return SequenceNumber.a_leq_b_leq_c(self.snd_una, ack_number, self.snd_nxt) def update_window(self, packet): seq_number = packet.get_seq_number() ack_number = packet.get_ack_number() if self.snd_wl1 < seq_number or \ (self.snd_wl1 == seq_number and self.snd_wl2 <= ack_number): self.snd_wnd = packet.get_window_size() self.snd_wl1 = seq_number self.snd_wl2 = ack_number def usable_window_size(self): upper_limit = self.snd_una + self.snd_wnd # Si el límite superior de la ventana está por debajo de SND_NXT, # tenemos que devolver 0. if SequenceNumber.a_leq_b_leq_c(self.snd_una, self.snd_nxt, upper_limit): return upper_limit - self.snd_nxt else: # TODO: agregar test! return 0 def has_data_to_send(self): return not self.out_buffer.empty() def to_out_buffer(self, data): self.out_buffer.put(data) def from_in_buffer(self, size): data = self.in_buffer.get(size) # La ventana debería crecer ahora pues se consumieron datos del buffer. with self: self.rcv_wnd += len(data) return data def extract_from_out_buffer(self, size): usable_window = self.usable_window_size() size = min(size, usable_window) data = self.out_buffer.get(size) self.snd_nxt += len(data) return data def flush_buffers(self): self.in_buffer.flush() self.out_buffer.flush() def __enter__(self, *args, **kwargs): return self.lock.__enter__(*args, **kwargs) def __exit__(self, *args, **kwargs): return self.lock.__exit__(*args, **kwargs)
def test_resize_no_resizeable(self): data = np.zeros(10) B = DataBuffer(data=data, resizeable=False) data = np.zeros(20) with self.assertRaises(ValueError): B.set_data(data)
def test_no_resize_ellipsis(self): data = np.zeros(10) B = DataBuffer(data=data) data = np.zeros(30) with self.assertRaises(ValueError): B[...] = data
def test_storage(self): data = np.ones(100) B = DataBuffer(data, store=True, copy=False) assert B.data.base is data
def test_storage_copy(self): data = np.ones(100) B = DataBuffer(data, store=True, copy=True) assert B.data is not None assert B.data is not data
def test_storage_copy(self): data = np.ones(100) B = DataBuffer(data, store=False) assert B.data is None
def test_empty_init(self): with self.assertRaises(ValueError): B = DataBuffer()
def test_non_contiguous_storage(self): # Ask to have CPU storage and to use data as storage # Not possible since data[::2] is not contiguous data = np.ones(100) B = DataBuffer(data[::2], store=True, copy=False) assert B._copy == True
class PTCControlBlock(object): def __init__(self, send_seq, receive_seq, send_window, receive_window): self.snd_wnd = send_window self.snd_nxt = send_seq.clone() self.snd_una = send_seq.clone() self.rcv_nxt = receive_seq.clone() self.rcv_wnd = receive_window self.snd_wl1 = receive_seq.clone() self.max_rcv_wnd = receive_window self.snd_wl2 = send_seq.clone() self.in_buffer = DataBuffer(start_index=receive_seq.clone()) self.out_buffer = DataBuffer(start_index=send_seq.clone()) self.lock = threading.RLock() self.logger = logging.getLogger('PTCControlBlock') def get_snd_nxt(self): return self.snd_nxt def get_snd_una(self): return self.snd_una def get_snd_wnd(self): return self.snd_wnd def get_snd_wl1(self): return self.snd_wl1 def get_snd_wl2(self): return self.snd_wl2 def get_rcv_nxt(self): return self.rcv_nxt def get_rcv_wnd(self): return self.rcv_wnd def increment_snd_nxt(self): with self: self.snd_nxt += 1 def increment_snd_una(self): with self: self.snd_una += 1 def increment_rcv_nxt(self): with self: self.rcv_nxt += 1 def process_incoming(self, packet, ignore_payload=False): self.process_ack(packet) if not ignore_payload: self.process_payload(packet) def process_payload(self, packet): if self.payload_is_accepted(packet): seq_lo, seq_hi = packet.get_seq_interval() payload = packet.get_payload() lower = max(self.rcv_nxt, seq_lo) upper = min(self.rcv_nxt + self.rcv_wnd, seq_hi) # Honrar RCV_WND descartando aquellos bytes que están por debajo o # por encima de ella. effective_payload = payload[lower-seq_lo:upper-seq_lo] self.in_buffer.add_chunk(lower, effective_payload) if lower == self.rcv_nxt: # Deberíamos avanzar RCV_NXT dado que el límite inferior del # chunk recién agregado coincide con su valor anterior. El # buffer hace seguimiento de este valor a medida que se # insertan y borran datos de él. self.rcv_nxt = self.in_buffer.get_last_index() # Decrementar la ventana hasta que se eliminen datos del # buffer. self.rcv_wnd = self.rcv_wnd - len(effective_payload) self.logger.debug('Ventana reducida: rcv_wnd=%d' % self.rcv_wnd) def process_ack(self, packet): ack_number = packet.get_ack_number() if self.ack_is_accepted(ack_number): self.snd_una = ack_number if self.should_update_window(ack_number): self.update_window(packet) def ack_is_accepted(self, ack_number): # Aceptar sólo si SND_UNA < ACK <= SND_NXT return SequenceNumber.a_lt_b_leq_c(self.snd_una, ack_number, self.snd_nxt) def payload_is_accepted(self, packet): seq_lo, seq_hi = packet.get_seq_interval() first_byte, last_byte = seq_lo, seq_hi-1 first_ok = SequenceNumber.a_leq_b_leq_c(self.rcv_nxt, first_byte, self.rcv_nxt+self.rcv_wnd) last_ok = SequenceNumber.a_leq_b_leq_c(self.rcv_nxt, last_byte, self.rcv_nxt+self.rcv_wnd) return last_byte >= first_byte and (first_ok or last_ok) def should_update_window(self, ack_number): # TODO: agregar tests para esto. # RFC 1122, p.94 (corrección al RFC 793). return SequenceNumber.a_leq_b_leq_c(self.snd_una, ack_number, self.snd_nxt) def update_window(self, packet): seq_number = packet.get_seq_number() ack_number = packet.get_ack_number() if self.snd_wl1 < seq_number or \ (self.snd_wl1 == seq_number and self.snd_wl2 <= ack_number): self.snd_wnd = packet.get_window_size() self.snd_wl1 = seq_number self.snd_wl2 = ack_number def usable_window_size(self): # Original # return self.snd_una + self.snd_wnd - self.snd_nxt # Modificado usable_window = self.snd_una + self.snd_wnd - self.snd_nxt if usable_window >= 0: return usable_window else: return 0 def has_data_to_send(self): return not self.out_buffer.empty() def to_out_buffer(self, data): self.out_buffer.put(data) def from_in_buffer(self, size): data = self.in_buffer.get(size) # La ventana debería crecer ahora pues se consumieron datos del buffer. with self: # Original # self.rcv_wnd += len(data) # Modificado self.rcv_wnd = min(self.rcv_wnd + len(data), self.max_rcv_wnd) self.logger.debug('Ventana aumentada: rcv_wnd=%d' % self.rcv_wnd) return data def extract_from_out_buffer(self, size): usable_window = self.usable_window_size() size = min(size, usable_window) data = self.out_buffer.get(size) self.snd_nxt += len(data) return data def flush_buffers(self): self.in_buffer.flush() self.out_buffer.flush() def __enter__(self, *args, **kwargs): return self.lock.__enter__(*args, **kwargs) def __exit__(self, *args, **kwargs): return self.lock.__exit__(*args, **kwargs)
class PTCControlBlock(object): def __init__(self, send_seq, receive_seq, send_window, receive_window): self.snd_wnd = send_window self.snd_nxt = send_seq.clone() self.snd_una = send_seq.clone() self.rcv_nxt = receive_seq.clone() self.rcv_wnd = receive_window self.snd_wl1 = receive_seq.clone() self.snd_wl2 = send_seq.clone() self.in_buffer = DataBuffer(start_index=receive_seq.clone()) self.out_buffer = DataBuffer(start_index=send_seq.clone()) self.lock = threading.RLock() def get_snd_nxt(self): return self.snd_nxt def get_snd_una(self): return self.snd_una def get_snd_wnd(self): return self.snd_wnd def get_snd_wl1(self): return self.snd_wl1 def get_snd_wl2(self): return self.snd_wl2 def get_rcv_nxt(self): return self.rcv_nxt def get_rcv_wnd(self): return self.rcv_wnd def increment_snd_nxt(self): with self: self.snd_nxt += 1 def increment_snd_una(self): with self: self.snd_una += 1 def increment_rcv_nxt(self): with self: self.rcv_nxt += 1 def process_incoming(self, packet, ignore_payload=False): self.process_ack(packet) if not ignore_payload: self.process_payload(packet) def process_payload(self, packet): if self.payload_is_accepted(packet): seq_lo, seq_hi = packet.get_seq_interval() payload = packet.get_payload() lower = max(self.rcv_nxt, seq_lo) upper = min(self.rcv_nxt + self.rcv_wnd, seq_hi) # Honor RCV_WND by dropping those bytes that go below it # or beyond it. effective_payload = payload[lower-seq_lo:upper-seq_lo] self.in_buffer.add_chunk(lower, effective_payload) if lower == self.rcv_nxt: # We should advance rcv_nxt since the lower end of the chunk # just added matches its old value. The buffer tracks this # value as data is inserted and removed. self.rcv_nxt = self.in_buffer.get_last_index() # Decrease window until data is removed from the buffer. self.rcv_wnd = self.rcv_wnd - len(effective_payload) def process_ack(self, packet): ack_number = packet.get_ack_number() if self.ack_is_accepted(ack_number): self.snd_una = ack_number if self.should_update_window(ack_number): self.update_window(packet) def ack_is_accepted(self, ack_number): # Accept only if SND_UNA < ACK <= SND_NXT return SequenceNumber.a_lt_b_leq_c(self.snd_una, ack_number, self.snd_nxt) def payload_is_accepted(self, packet): seq_lo, seq_hi = packet.get_seq_interval() first_byte, last_byte = seq_lo, seq_hi-1 first_ok = SequenceNumber.a_leq_b_leq_c(self.rcv_nxt, first_byte, self.rcv_nxt+self.rcv_wnd) last_ok = SequenceNumber.a_leq_b_leq_c(self.rcv_nxt, last_byte, self.rcv_nxt+self.rcv_wnd) return last_byte >= first_byte and (first_ok or last_ok) def should_update_window(self, ack_number): # TODO: add tests for this. # RFC 1122, p.94 (correction to RFC 793). return SequenceNumber.a_leq_b_leq_c(self.snd_una, ack_number, self.snd_nxt) def update_window(self, packet): seq_number = packet.get_seq_number() ack_number = packet.get_ack_number() if self.snd_wl1 < seq_number or \ (self.snd_wl1 == seq_number and self.snd_wl2 <= ack_number): self.snd_wnd = packet.get_window_size() self.snd_wl1 = seq_number self.snd_wl2 = ack_number def usable_window_size(self): upper_limit = self.snd_una + self.snd_wnd # If the upper window limit is below SND_NXT, we must return 0. if SequenceNumber.a_leq_b_leq_c(self.snd_una, self.snd_nxt, upper_limit): return upper_limit - self.snd_nxt else: # TODO: add test! return 0 def has_data_to_send(self): return not self.out_buffer.empty() def to_out_buffer(self, data): self.out_buffer.put(data) def from_in_buffer(self, size): data = self.in_buffer.get(size) # Window should grow now, since data has been consumed. with self: self.rcv_wnd += len(data) return data def extract_from_out_buffer(self, size): usable_window = self.usable_window_size() size = min(size, usable_window) data = self.out_buffer.get(size) self.snd_nxt += len(data) return data def flush_buffers(self): self.in_buffer.flush() self.out_buffer.flush() def __enter__(self, *args, **kwargs): return self.lock.__enter__(*args, **kwargs) def __exit__(self, *args, **kwargs): return self.lock.__exit__(*args, **kwargs)