Skip to content

Commit de7c94c

Browse files
committed
PYTHON-1785 C extension decoders support the buffer protocol
1 parent 201c3d2 commit de7c94c

File tree

1 file changed

+60
-69
lines changed

1 file changed

+60
-69
lines changed

bson/_cbsonmodule.c

Lines changed: 60 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,55 +2848,63 @@ static PyObject* elements_to_dict(PyObject* self, const char* string,
28482848
return result;
28492849
}
28502850

2851+
static int _get_buffer(PyObject *exporter, Py_buffer *view) {
2852+
if (PyObject_GetBuffer(exporter, view, PyBUF_SIMPLE) == -1) {
2853+
return 0;
2854+
}
2855+
if (!PyBuffer_IsContiguous(view, 'C')) {
2856+
PyErr_SetString(PyExc_ValueError,
2857+
"must be a contiguous buffer");
2858+
goto fail;
2859+
}
2860+
if (!view->buf || view->len < 0) {
2861+
PyErr_SetString(PyExc_ValueError, "invalid buffer");
2862+
goto fail;
2863+
}
2864+
if (view->itemsize != 1) {
2865+
PyErr_SetString(PyExc_ValueError,
2866+
"buffer data must be ascii or utf8");
2867+
goto fail;
2868+
}
2869+
return 1;
2870+
fail:
2871+
PyBuffer_Release(view);
2872+
return 0;
2873+
}
2874+
28512875
static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
28522876
int32_t size;
28532877
Py_ssize_t total_size;
28542878
const char* string;
28552879
PyObject* bson;
28562880
codec_options_t options;
2857-
PyObject* result;
2881+
PyObject* result = NULL;
28582882
PyObject* options_obj;
2883+
Py_buffer view;
28592884

28602885
if (! (PyArg_ParseTuple(args, "OO", &bson, &options_obj) &&
28612886
convert_codec_options(options_obj, &options))) {
2862-
return NULL;
2887+
return result;
28632888
}
28642889

2865-
#if PY_MAJOR_VERSION >= 3
2866-
if (!PyBytes_Check(bson)) {
2867-
PyErr_SetString(PyExc_TypeError, "argument to _bson_to_dict must be a bytes object");
2868-
#else
2869-
if (!PyString_Check(bson)) {
2870-
PyErr_SetString(PyExc_TypeError, "argument to _bson_to_dict must be a string");
2871-
#endif
2890+
if (!_get_buffer(bson, &view)) {
28722891
destroy_codec_options(&options);
2873-
return NULL;
2892+
return result;
28742893
}
2875-
#if PY_MAJOR_VERSION >= 3
2876-
total_size = PyBytes_Size(bson);
2877-
#else
2878-
total_size = PyString_Size(bson);
2879-
#endif
2894+
2895+
total_size = view.len;
2896+
28802897
if (total_size < BSON_MIN_SIZE) {
28812898
PyObject* InvalidBSON = _error("InvalidBSON");
28822899
if (InvalidBSON) {
28832900
PyErr_SetString(InvalidBSON,
28842901
"not enough data for a BSON document");
28852902
Py_DECREF(InvalidBSON);
28862903
}
2887-
destroy_codec_options(&options);
2888-
return NULL;
2904+
goto done;;
28892905
}
28902906

2891-
#if PY_MAJOR_VERSION >= 3
2892-
string = PyBytes_AsString(bson);
2893-
#else
2894-
string = PyString_AsString(bson);
2895-
#endif
2896-
if (!string) {
2897-
destroy_codec_options(&options);
2898-
return NULL;
2899-
}
2907+
string = (char*)view.buf;
29002908

29012909
memcpy(&size, string, 4);
29022910
size = (int32_t)BSON_UINT32_FROM_LE(size);
@@ -2906,8 +2914,7 @@ static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
29062914
PyErr_SetString(InvalidBSON, "invalid message size");
29072915
Py_DECREF(InvalidBSON);
29082916
}
2909-
destroy_codec_options(&options);
2910-
return NULL;
2917+
goto done;
29112918
}
29122919

29132920
if (total_size < size || total_size > BSON_MAX_SIZE) {
@@ -2916,8 +2923,7 @@ static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
29162923
PyErr_SetString(InvalidBSON, "objsize too large");
29172924
Py_DECREF(InvalidBSON);
29182925
}
2919-
destroy_codec_options(&options);
2920-
return NULL;
2926+
goto done;
29212927
}
29222928

29232929
if (size != total_size || string[size - 1]) {
@@ -2926,18 +2932,20 @@ static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
29262932
PyErr_SetString(InvalidBSON, "bad eoo");
29272933
Py_DECREF(InvalidBSON);
29282934
}
2929-
destroy_codec_options(&options);
2930-
return NULL;
2935+
goto done;
29312936
}
29322937

29332938
/* No need to decode fields if using RawBSONDocument */
29342939
if (options.is_raw_bson) {
2935-
return PyObject_CallFunction(
2940+
result = PyObject_CallFunction(
29362941
options.document_class, BYTES_FORMAT_STRING "O", string, size,
29372942
options_obj);
29382943
}
2939-
2940-
result = elements_to_dict(self, string + 4, (unsigned)size - 5, &options);
2944+
else {
2945+
result = elements_to_dict(self, string + 4, (unsigned)size - 5, &options);
2946+
}
2947+
done:
2948+
PyBuffer_Release(&view);
29412949
destroy_codec_options(&options);
29422950
return result;
29432951
}
@@ -2948,9 +2956,10 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
29482956
const char* string;
29492957
PyObject* bson;
29502958
PyObject* dict;
2951-
PyObject* result;
2959+
PyObject* result = NULL;
29522960
codec_options_t options;
29532961
PyObject* options_obj;
2962+
Py_buffer view;
29542963

29552964
if (!PyArg_ParseTuple(args, "O|O", &bson, &options_obj)) {
29562965
return NULL;
@@ -2963,31 +2972,15 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
29632972
return NULL;
29642973
}
29652974

2966-
#if PY_MAJOR_VERSION >= 3
2967-
if (!PyBytes_Check(bson)) {
2968-
PyErr_SetString(PyExc_TypeError, "argument to decode_all must be a bytes object");
2969-
#else
2970-
if (!PyString_Check(bson)) {
2971-
PyErr_SetString(PyExc_TypeError, "argument to decode_all must be a string");
2972-
#endif
2973-
destroy_codec_options(&options);
2974-
return NULL;
2975-
}
2976-
#if PY_MAJOR_VERSION >= 3
2977-
total_size = PyBytes_Size(bson);
2978-
string = PyBytes_AsString(bson);
2979-
#else
2980-
total_size = PyString_Size(bson);
2981-
string = PyString_AsString(bson);
2982-
#endif
2983-
if (!string) {
2975+
if (!_get_buffer(bson, &view)) {
29842976
destroy_codec_options(&options);
29852977
return NULL;
29862978
}
2979+
total_size = view.len;
2980+
string = (char*)view.buf;
29872981

29882982
if (!(result = PyList_New(0))) {
2989-
destroy_codec_options(&options);
2990-
return NULL;
2983+
goto fail;
29912984
}
29922985

29932986
while (total_size > 0) {
@@ -2998,9 +2991,8 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
29982991
"not enough data for a BSON document");
29992992
Py_DECREF(InvalidBSON);
30002993
}
3001-
destroy_codec_options(&options);
30022994
Py_DECREF(result);
3003-
return NULL;
2995+
goto fail;
30042996
}
30052997

30062998
memcpy(&size, string, 4);
@@ -3011,9 +3003,8 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
30113003
PyErr_SetString(InvalidBSON, "invalid message size");
30123004
Py_DECREF(InvalidBSON);
30133005
}
3014-
destroy_codec_options(&options);
30153006
Py_DECREF(result);
3016-
return NULL;
3007+
goto fail;
30173008
}
30183009

30193010
if (total_size < size) {
@@ -3022,9 +3013,8 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
30223013
PyErr_SetString(InvalidBSON, "objsize too large");
30233014
Py_DECREF(InvalidBSON);
30243015
}
3025-
destroy_codec_options(&options);
30263016
Py_DECREF(result);
3027-
return NULL;
3017+
goto fail;
30283018
}
30293019

30303020
if (string[size - 1]) {
@@ -3033,9 +3023,8 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
30333023
PyErr_SetString(InvalidBSON, "bad eoo");
30343024
Py_DECREF(InvalidBSON);
30353025
}
3036-
destroy_codec_options(&options);
30373026
Py_DECREF(result);
3038-
return NULL;
3027+
goto fail;
30393028
}
30403029

30413030
/* No need to decode fields if using RawBSONDocument. */
@@ -3048,20 +3037,22 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
30483037
}
30493038
if (!dict) {
30503039
Py_DECREF(result);
3051-
destroy_codec_options(&options);
3052-
return NULL;
3040+
goto fail;
30533041
}
30543042
if (PyList_Append(result, dict) < 0) {
30553043
Py_DECREF(dict);
30563044
Py_DECREF(result);
3057-
destroy_codec_options(&options);
3058-
return NULL;
3045+
goto fail;
30593046
}
30603047
Py_DECREF(dict);
30613048
string += size;
30623049
total_size -= size;
30633050
}
3064-
3051+
goto done;
3052+
fail:
3053+
result = NULL;
3054+
done:
3055+
PyBuffer_Release(&view);
30653056
destroy_codec_options(&options);
30663057
return result;
30673058
}

0 commit comments

Comments
 (0)