@@ -252,6 +252,15 @@ class ShareableList:
252252 packing format for any storable value must require no more than 8
253253 characters to describe its format."""
254254
255+ # The shared memory area is organized as follows:
256+ # - 8 bytes: number of items (N) as a 64-bit integer
257+ # - (N + 1) * 8 bytes: offsets of each element from the start of the
258+ # data area
259+ # - K bytes: the data area storing item values (with encoding and size
260+ # depending on their respective types)
261+ # - N * 8 bytes: `struct` format string for each element
262+ # - N bytes: index into _back_transforms_mapping for each element
263+ # (for reconstructing the corresponding Python value)
255264 _types_mapping = {
256265 int : "q" ,
257266 float : "d" ,
@@ -283,7 +292,8 @@ def _extract_recreation_code(value):
283292 return 3 # NoneType
284293
285294 def __init__ (self , sequence = None , * , name = None ):
286- if sequence is not None :
295+ if name is None or sequence is not None :
296+ sequence = sequence or ()
287297 _formats = [
288298 self ._types_mapping [type (item )]
289299 if not isinstance (item , (str , bytes ))
@@ -295,10 +305,13 @@ def __init__(self, sequence=None, *, name=None):
295305 self ._list_len = len (_formats )
296306 assert sum (len (fmt ) <= 8 for fmt in _formats ) == self ._list_len
297307 offset = 0
298- self ._allocated_bytes = []
308+ # The offsets of each list element into the shared memory's
309+ # data area (0 meaning the start of the data area, not the start
310+ # of the shared memory area).
311+ self ._allocated_offsets = [0 ]
299312 for fmt in _formats :
300313 offset += self ._alignment if fmt [- 1 ] != "s" else int (fmt [:- 1 ])
301- self ._allocated_bytes .append (offset )
314+ self ._allocated_offsets .append (offset )
302315 _recreation_codes = [
303316 self ._extract_recreation_code (item ) for item in sequence
304317 ]
@@ -309,13 +322,9 @@ def __init__(self, sequence=None, *, name=None):
309322 self ._format_back_transform_codes
310323 )
311324
325+ self .shm = SharedMemory (name , create = True , size = requested_size )
312326 else :
313- requested_size = 8 # Some platforms require > 0.
314-
315- if name is not None and sequence is None :
316327 self .shm = SharedMemory (name )
317- else :
318- self .shm = SharedMemory (name , create = True , size = requested_size )
319328
320329 if sequence is not None :
321330 _enc = _encoding
@@ -324,7 +333,7 @@ def __init__(self, sequence=None, *, name=None):
324333 self .shm .buf ,
325334 0 ,
326335 self ._list_len ,
327- * (self ._allocated_bytes )
336+ * (self ._allocated_offsets )
328337 )
329338 struct .pack_into (
330339 "" .join (_formats ),
@@ -347,7 +356,7 @@ def __init__(self, sequence=None, *, name=None):
347356
348357 else :
349358 self ._list_len = len (self ) # Obtains size from offset 0 in buffer.
350- self ._allocated_bytes = list (
359+ self ._allocated_offsets = list (
351360 struct .unpack_from (
352361 self ._format_size_metainfo ,
353362 self .shm .buf ,
@@ -374,7 +383,6 @@ def _get_packing_format(self, position):
374383 def _get_back_transform (self , position ):
375384 "Gets the back transformation function for a single value."
376385
377- position = position if position >= 0 else position + self ._list_len
378386 if (position >= self ._list_len ) or (self ._list_len < 0 ):
379387 raise IndexError ("Requested position out of range." )
380388
@@ -391,7 +399,6 @@ def _set_packing_format_and_transform(self, position, fmt_as_str, value):
391399 """Sets the packing format and back transformation code for a
392400 single value in the list at the specified position."""
393401
394- position = position if position >= 0 else position + self ._list_len
395402 if (position >= self ._list_len ) or (self ._list_len < 0 ):
396403 raise IndexError ("Requested position out of range." )
397404
@@ -411,12 +418,9 @@ def _set_packing_format_and_transform(self, position, fmt_as_str, value):
411418 )
412419
413420 def __getitem__ (self , position ):
421+ position = position if position >= 0 else position + self ._list_len
414422 try :
415- if position == 0 :
416- allocated_predecessor = 0
417- else :
418- allocated_predecessor = self ._allocated_bytes [position - 1 ]
419- offset = self ._offset_data_start + allocated_predecessor
423+ offset = self ._offset_data_start + self ._allocated_offsets [position ]
420424 (v ,) = struct .unpack_from (
421425 self ._get_packing_format (position ),
422426 self .shm .buf ,
@@ -431,28 +435,26 @@ def __getitem__(self, position):
431435 return v
432436
433437 def __setitem__ (self , position , value ):
438+ position = position if position >= 0 else position + self ._list_len
434439 try :
435- if position == 0 :
436- allocated_predecessor = 0
437- else :
438- allocated_predecessor = self ._allocated_bytes [position - 1 ]
439- offset = self ._offset_data_start + allocated_predecessor
440+ item_offset = self ._allocated_offsets [position ]
441+ offset = self ._offset_data_start + item_offset
440442 current_format = self ._get_packing_format (position )
441443 except IndexError :
442444 raise IndexError ("assignment index out of range" )
443445
444446 if not isinstance (value , (str , bytes )):
445447 new_format = self ._types_mapping [type (value )]
446448 else :
447- allocated_position = self ._allocated_bytes [position ] - allocated_predecessor
449+ allocated_length = self ._allocated_offsets [position + 1 ] - item_offset
448450
449- if len (value ) > allocated_position :
451+ if len (value ) > allocated_length :
450452 raise ValueError ("exceeds available storage for existing str" )
451453 if current_format [- 1 ] == "s" :
452454 new_format = current_format
453455 else :
454456 new_format = self ._types_mapping [str ] % (
455- allocated_position ,
457+ allocated_length ,
456458 )
457459
458460 self ._set_packing_format_and_transform (
@@ -474,33 +476,35 @@ def __repr__(self):
474476
475477 @property
476478 def format (self ):
477- "The struct packing format used by all currently stored values ."
479+ "The struct packing format used by all currently stored items ."
478480 return "" .join (
479481 self ._get_packing_format (i ) for i in range (self ._list_len )
480482 )
481483
482484 @property
483485 def _format_size_metainfo (self ):
484- "The struct packing format used for metainfo on storage sizes ."
485- return f" { self ._list_len } q"
486+ "The struct packing format used for the items' storage offsets ."
487+ return "q" * ( self ._list_len + 1 )
486488
487489 @property
488490 def _format_packing_metainfo (self ):
489- "The struct packing format used for the values ' packing formats."
491+ "The struct packing format used for the items ' packing formats."
490492 return "8s" * self ._list_len
491493
492494 @property
493495 def _format_back_transform_codes (self ):
494- "The struct packing format used for the values ' back transforms."
496+ "The struct packing format used for the items ' back transforms."
495497 return "b" * self ._list_len
496498
497499 @property
498500 def _offset_data_start (self ):
499- return (self ._list_len + 1 ) * 8 # 8 bytes per "q"
501+ # - 8 bytes for the list length
502+ # - (N + 1) * 8 bytes for the element offsets
503+ return (self ._list_len + 2 ) * 8
500504
501505 @property
502506 def _offset_packing_formats (self ):
503- return self ._offset_data_start + self ._allocated_bytes [- 1 ]
507+ return self ._offset_data_start + self ._allocated_offsets [- 1 ]
504508
505509 @property
506510 def _offset_back_transform_codes (self ):
0 commit comments