@@ -22,33 +22,104 @@ def count(self, limit: int = 0) -> int:
2222 obx_box_count (self ._c_box , limit , ctypes .byref (count ))
2323 return int (count .value )
2424
25- def put (self , object ) -> int :
26- id = object_id = self ._entity .get_object_id (object )
25+ def put (self , * objects ):
26+ """Puts an object (or a list of objects) and returns its ID (or nothing for a list objects)"""
27+
28+ if len (objects ) != 1 :
29+ self ._put_many (objects )
30+ elif isinstance (objects [0 ], list ):
31+ self ._put_many (objects [0 ])
32+ else :
33+ return self ._put_one (objects [0 ])
34+
35+ def _put_one (self , obj ) -> int :
36+ id = object_id = self ._entity .get_object_id (obj )
2737
2838 if not id :
2939 id = obx_box_id_for_put (self ._c_box , 0 )
3040
31- data = self ._entity .marshal (object , id )
41+ data = self ._entity .marshal (obj , id )
3242 obx_box_put (self ._c_box , id , bytes (data ), len (data ), OBXPutMode_PUT )
3343
3444 if id != object_id :
35- self ._entity .set_object_id (object , id )
45+ self ._entity .set_object_id (obj , id )
46+
3647 return id
3748
49+ def _put_many (self , objects ) -> None :
50+ # retrieve IDs from the objects (to distinguish new objects and updates)
51+ new = {}
52+ ids = {}
53+ for k in range (len (objects )):
54+ id = self ._entity .get_object_id (objects [k ])
55+ if not id :
56+ new [k ] = 0
57+ ids [k ] = id
58+
59+ # acquire IDs for the new objects and set them
60+ if len (new ) > 0 :
61+ c_next_id = obx_id ()
62+ obx_box_ids_for_put (self ._c_box , len (new ), ctypes .byref (c_next_id ))
63+ next_id = c_next_id .value
64+ for k in new .keys ():
65+ ids [k ] = next_id
66+ next_id += 1
67+
68+ # allocate a C bytes array structure where we will push the object data
69+ # OBX_bytes_array with .count = len(objects)
70+ c_bytes_array_p = obx_bytes_array_create (len (objects ))
71+
72+ try :
73+ # we need to keep the data around until put_many is executed because obx_bytes_array_set doesn't do a copy
74+ data = {}
75+ for k in range (len (objects )):
76+ data [k ] = bytes (self ._entity .marshal (objects [k ], ids [k ]))
77+ key = ctypes .c_size_t (k )
78+
79+ # OBX_bytes_array.data[k] = data
80+ obx_bytes_array_set (c_bytes_array_p , key , data [k ], len (data [k ]))
81+
82+ c_ids = (obx_id * len (ids ))(* ids .values ())
83+ obx_box_put_many (self ._c_box , c_bytes_array_p , c_ids , OBXPutMode_PUT )
84+
85+ finally :
86+ obx_bytes_array_free (c_bytes_array_p )
87+
88+ # assign new IDs on the object
89+ for k in new .keys ():
90+ self ._entity .set_object_id (objects [k ], ids [k ])
91+
3892 def get (self , id : int ):
3993 c_data = ctypes .c_void_p ()
4094 c_size = ctypes .c_size_t ()
4195 obx_box_get (self ._c_box , id , ctypes .byref (c_data ), ctypes .byref (c_size ))
4296
43- # TODO verify which of the following two approaches is better.
97+ data = c_voidp_as_bytes (c_data , c_size .value )
98+ return self ._entity .unmarshal (data )
4499
45- # slice the data from the pointer
46- # data = ctypes.cast(c_data, ctypes.POINTER(ctypes.c_char))[:c_size.value]
100+ def get_all (self ) -> list :
101+ # OBX_bytes_array*
102+ c_bytes_array_p = obx_box_get_all (self ._c_box )
47103
48- # create a memory view
49- data = memoryview (ctypes .cast (c_data , ctypes .POINTER (ctypes .c_ubyte * c_size .value ))[0 ]).tobytes ()
104+ try :
105+ # OBX_bytes_array
106+ c_bytes_array = c_bytes_array_p .contents
50107
51- return self ._entity .unmarshal (data )
108+ result = list ()
109+ for i in range (c_bytes_array .count ):
110+ # OBX_bytes
111+ c_bytes = c_bytes_array .data [i ]
112+ data = c_voidp_as_bytes (c_bytes .data , c_bytes .size )
113+ result .append (self ._entity .unmarshal (data ))
114+
115+ return result
116+ finally :
117+ obx_bytes_array_free (c_bytes_array_p )
52118
53119 def remove (self , id : int ):
54- obx_box_remove (self ._c_box , id )
120+ obx_box_remove (self ._c_box , id )
121+
122+ def remove_all (self ) -> int :
123+ count = ctypes .c_uint64 ()
124+ obx_box_remove_all (self ._c_box , ctypes .byref (count ))
125+ return int (count .value )
0 commit comments