Bläddra i källkod

Implement buffer protocol for RPNIFS class

Yann Weber 7 månader sedan
förälder
incheckning
d25529c5c6
4 ändrade filer med 145 tillägg och 62 borttagningar
  1. 80
    59
      python_if.c
  2. 18
    0
      python_if.h
  3. 14
    3
      python_ifs.c
  4. 33
    0
      tests/tests_rpn_ifs.py

+ 80
- 59
python_if.c Visa fil

@@ -889,71 +889,22 @@ int rpnif_getbuffer(PyObject *self, Py_buffer *view, int flags)
889 889
 	PyRPNIterExpr_t *expr_self;
890 890
 	expr_self = (PyRPNIterExpr_t*)self;
891 891
 
892
-	return PyObject_GetBuffer(expr_self->mmap, view, flags);
893
-
894
-	rpn_if_default_data_t *data = (rpn_if_default_data_t*)expr_self->rif->params->data;
895
-
896
-	view->buf = expr_self->rif->mem;
897
-	view->obj = self;
898
-	view->len = expr_self->rif->params->mem_sz * expr_self->rif->params->value_sz;
899
-	view->readonly = 0;
900
-	//view->itemsize = expr_self->rif->params->value_sz;
901
-	view->itemsize = sizeof(rpn_value_t);
902
-	if(flags & PyBUF_FORMAT)
903
-	{
904
-		view->format = "L";
905
-	}
906
-	else
892
+	if(expr_self->mmap)
907 893
 	{
908
-		view->format = NULL;
894
+		return PyObject_GetBuffer(expr_self->mmap, view, flags);
909 895
 	}
910
-	view->ndim = 1;
911
-	view->shape = NULL;
912
-	if(flags & PyBUF_ND)
913
-	{
914
-		view->ndim = expr_self->ndim;
915
-
916
-		// !! Error if value_sz < sizeof(rpn_value_t) !!
917
-		short nval = view->itemsize / sizeof(rpn_value_t);
918
-		if(nval > 1)
919
-		{
920
-			view->ndim++;
921
-		}
922
-		view->shape = malloc(sizeof(Py_ssize_t) * view->ndim);
923
-		if(!view->shape)
924
-		{
925
-			goto buff_error;
926
-		}
927
-		for(size_t i=0; i<expr_self->ndim; i++)
928
-		{
929
-			int idx = i;
930
-			if(data->pos_flag == RPN_IF_POSITION_XDIM)
931
-			{
932
-				idx++;
933
-			}
934
-			view->shape[i] = data->size_lim[idx];
935
-		}
936
-		if(nval>1)
937
-		{
938
-			view->shape[expr_self->ndim] = nval;
939
-		}
940
-	}
941
-	view->strides = NULL;
942
-	view->suboffsets = NULL;
943
-
944
-	Py_INCREF(self);
945
-	return 0;
946
-
947
-buff_error:
948
-	PyErr_Format(PyExc_BufferError,
949
-			"Unable to provide buffer : %s", strerror(errno));
950
-	view->obj = NULL;
951
-	return -1;
896
+	return _rpnif_getbuffer_nopymap(self, expr_self->rif->params,
897
+			expr_self->_mmap, view, flags);
952 898
 }
953 899
 
954 900
 void rpnif_releasebuffer(PyObject *self, Py_buffer *view)
955 901
 {
956
-	free(view->shape);
902
+	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
903
+	if(expr_self->mmap)
904
+	{
905
+		return PyBuffer_Release(view);
906
+	}
907
+	return _rpnif_releasebuffer_nopymap(self, view);
957 908
 }
958 909
 
959 910
 PyObject* rpnif_str(PyObject *self)
@@ -1676,6 +1627,76 @@ err:
1676 1627
 }
1677 1628
 
1678 1629
 
1630
+int _rpnif_getbuffer_nopymap(PyObject *self, const rpn_if_param_t *params,
1631
+		void *buf, Py_buffer *view, int flags)
1632
+{
1633
+	rpn_if_default_data_t *data = (rpn_if_default_data_t*)params->data;
1634
+
1635
+	view->buf = buf;
1636
+	view->obj = self;
1637
+	view->len = params->mem_sz * params->value_sz;
1638
+	view->readonly = 0;
1639
+	view->itemsize = sizeof(rpn_value_t);
1640
+	view->format = (flags & PyBUF_FORMAT)?"L":NULL;
1641
+	view->ndim = 1;
1642
+	view->shape = NULL;
1643
+	if(flags & PyBUF_ND)
1644
+	{
1645
+		short nval;
1646
+		switch(data->res_flag)
1647
+		{
1648
+			case RPN_IF_RES_BOOL:
1649
+			case RPN_IF_RES_CONST:
1650
+			case RPN_IF_RES_COUNT:
1651
+				nval =  1;
1652
+				break;
1653
+			case RPN_IF_RES_RGBA:
1654
+			case RPN_IF_RES_CONST_RGBA:
1655
+				nval = 4;
1656
+				break;
1657
+			case RPN_IF_RES_RGB:
1658
+				nval = 3;
1659
+				break;
1660
+			default:
1661
+				PyErr_SetString(PyExc_BufferError,
1662
+						"Unsupported result flag");
1663
+				return -1;
1664
+		}
1665
+		view->ndim += (nval>1)?1:0;
1666
+		if(!(view->shape = malloc(sizeof(Py_ssize_t) * view->ndim)))
1667
+		{
1668
+			PyErr_Format(PyExc_BufferError,
1669
+					"Buffer's shape allocation error : %s",
1670
+					strerror(errno));
1671
+			return -1;
1672
+		}
1673
+		for(size_t i=0; i<data->ndim; i++)
1674
+		{
1675
+			const int idx = i + \
1676
+				(data->pos_flag == RPN_IF_POSITION_XDIM)?1:0;
1677
+			view->shape[i] = data->size_lim[idx];
1678
+		}
1679
+		if(nval>1)
1680
+		{
1681
+			view->shape[data->ndim] = nval;
1682
+		}
1683
+		
1684
+	}
1685
+	view->strides = NULL;
1686
+	view->suboffsets = NULL;
1687
+	Py_INCREF(self);
1688
+	return 0;
1689
+}
1690
+
1691
+
1692
+void _rpnif_releasebuffer_nopymap(PyObject *self, Py_buffer *view)
1693
+{
1694
+	if(view->shape) { free(view->shape); }
1695
+	view->buf = NULL;
1696
+	Py_DECREF(self);
1697
+}
1698
+
1699
+
1679 1700
 int _rpnif_init_expr_tuple(PyRPNIterExpr_t *expr_self)
1680 1701
 {
1681 1702
 	expr_self->expr = PyTuple_New(expr_self->rif->params->rpn_sz);

+ 18
- 0
python_if.h Visa fil

@@ -153,6 +153,7 @@ void rpnif_del(PyObject *self);
153 153
  * @param view A memoryview to fill depending on flags
154 154
  * @param flags Indicates the request type
155 155
  * @return -1 on error
156
+ * @note Used by RPNIterExpr and RPNIFS (proper way to implement it is base class)
156 157
  * See python documentation at 
157 158
  * https://docs.python.org/3/c-api/buffer.html#buffer-request-types
158 159
  * @ingroup pymod_pyrpn_RPNExprIter
@@ -332,6 +333,23 @@ PyObject *_rpnif_to_pos(rpn_if_default_data_t *rif_data, PyObject** argv, Py_ssi
332 333
 /**@brief Implementation of bot RPNIterExpr.to_pos and RPNIFS.from_pos */
333 334
 PyObject *_rpnif_from_pos(rpn_if_default_data_t *rif_data, PyObject* _pos);
334 335
 
336
+
337
+/**@brief Buffer protocol request handler method when no python mmap exist
338
+ *        (deserialized instance)
339
+ * @param self RPNIterExpr instance
340
+ * @param view A memoryview to fill depending on flags
341
+ * @param flags Indicates the request type
342
+ * @return -1 on error
343
+ * @note Used by RPNIterExpr and RPNIFS (proper way to implement it is base class)
344
+ * See python documentation at 
345
+ * https://docs.python.org/3/c-api/buffer.html#buffer-request-types
346
+ * @ingroup pymod_pyrpn_RPNExprIter
347
+ */
348
+int _rpnif_getbuffer_nopymap(PyObject *self, const rpn_if_param_t *params,
349
+		void *buf, Py_buffer *view, int flags);
350
+void _rpnif_releasebuffer_nopymap(PyObject *self, Py_buffer *view);
351
+
352
+/**@todo doc */
335 353
 int _rpnif_init_expr_tuple(PyRPNIterExpr_t *expr_self);
336 354
 
337 355
 #endif

+ 14
- 3
python_ifs.c Visa fil

@@ -456,13 +456,24 @@ int rpnifs_expr_ass_item(PyObject *self, Py_ssize_t idx, PyObject *value)
456 456
 
457 457
 int rpnifs_getbuffer(PyObject *self, Py_buffer *view, int flags)
458 458
 {
459
-	PyErr_SetString(PyExc_NotImplementedError, "TODO");
460
-	return -1;
459
+	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
460
+
461
+	if(ifs_self->mmap)
462
+	{
463
+		return PyObject_GetBuffer(ifs_self->mmap, view, flags);
464
+	}
465
+	return _rpnif_getbuffer_nopymap(self, &ifs_self->ifs->params,
466
+			ifs_self->_mmap, view, flags);
461 467
 }
462 468
 
463 469
 void rpnifs_releasebuffer(PyObject *self, Py_buffer *view)
464 470
 {
465
-	PyErr_SetString(PyExc_NotImplementedError, "TODO");
471
+	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
472
+	if(ifs_self->mmap)
473
+	{
474
+		return PyBuffer_Release(view);
475
+	}
476
+	return _rpnif_releasebuffer_nopymap(self, view);
466 477
 }
467 478
 
468 479
 int _rpnifs_update_if_tuple(PyRPNIFS_t *ifs_self)

+ 33
- 0
tests/tests_rpn_ifs.py Visa fil

@@ -25,6 +25,16 @@ import sys
25 25
 
26 26
 import unittest
27 27
 
28
+try:
29
+    import numpy as np
30
+except (ImportError, NameError) as e:
31
+    np = None
32
+    warnings.warn("Numpy not found, some tests skipped", warnings.ImportWarning)
33
+
34
+def skipIfNoNumpy():
35
+    if np is not None:
36
+        return lambda func: func
37
+    return unittest.skip("Numpy not found")
28 38
 
29 39
 try:
30 40
     import pyrpn
@@ -124,6 +134,29 @@ class TestRPNIFS(unittest.TestCase):
124 134
                 percent = steps / (dist * 100)
125 135
                 self.assertLess(percent, 1)
126 136
 
137
+    @skipIfNoNumpy()
138
+    def test_buffer_protocol(self):
139
+        """ Testing buffer protocol on ifs's mmaps """
140
+        sz = [512,512]
141
+        ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY,
142
+                           pyrpn.const.RESULT_COUNT,
143
+                           sz)
144
+        ifs.weights([1,2])
145
+        for rif in ifs:
146
+            for expr in rif:
147
+                expr.mutate(n_mutations=20)
148
+
149
+        buf = np.frombuffer(ifs, dtype=np.uint64)
150
+        buf2 = np.frombuffer(ifs[0], dtype=np.uint64)
151
+
152
+        for _ in range(512):
153
+            pos = random.randint(0, len(buf))
154
+            buf[pos] = random.randint(0, 0xFFFFFFFF)
155
+            pos = random.randint(0, len(buf))
156
+            buf2[pos] = random.randint(0, 0xFFFFFFFF)
157
+
158
+        self.assertTrue((buf == buf2).all())
159
+
127 160
 class TestRPNIFSCoordinates(unittest.TestCase):
128 161
     """ Testing methods for coordinates <-> position convertions """
129 162
 

Loading…
Avbryt
Spara