@@ -1615,7 +1615,7 @@ static PyObject *_dbref_hook(PyObject* self, PyObject* value) {
1615
1615
1616
1616
static PyObject * get_value (PyObject * self , PyObject * name , const char * buffer ,
1617
1617
unsigned * position , unsigned char type ,
1618
- unsigned max , const codec_options_t * options ) {
1618
+ unsigned max , const codec_options_t * options , int raw_array ) {
1619
1619
struct module_state * state = GETSTATE (self );
1620
1620
PyObject * value = NULL ;
1621
1621
switch (type ) {
@@ -1712,11 +1712,20 @@ static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer,
1712
1712
if (size < BSON_MIN_SIZE || max < size ) {
1713
1713
goto invalid ;
1714
1714
}
1715
+
1715
1716
end = * position + size - 1 ;
1716
1717
/* Check for bad eoo */
1717
1718
if (buffer [end ]) {
1718
1719
goto invalid ;
1719
1720
}
1721
+
1722
+ if (raw_array != 0 ) {
1723
+ // Treat it as a binary buffer.
1724
+ value = PyBytes_FromStringAndSize (buffer + * position , size );
1725
+ * position += size ;
1726
+ break ;
1727
+ }
1728
+
1720
1729
* position += 4 ;
1721
1730
1722
1731
value = PyList_New (0 );
@@ -1740,7 +1749,7 @@ static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer,
1740
1749
goto invalid ;
1741
1750
}
1742
1751
to_append = get_value (self , name , buffer , position , bson_type ,
1743
- max - (unsigned )key_size , options );
1752
+ max - (unsigned )key_size , options , raw_array );
1744
1753
Py_LeaveRecursiveCall ();
1745
1754
if (!to_append ) {
1746
1755
Py_DECREF (value );
@@ -2464,6 +2473,7 @@ static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer,
2464
2473
static int _element_to_dict (PyObject * self , const char * string ,
2465
2474
unsigned position , unsigned max ,
2466
2475
const codec_options_t * options ,
2476
+ int raw_array ,
2467
2477
PyObject * * name , PyObject * * value ) {
2468
2478
unsigned char type = (unsigned char )string [position ++ ];
2469
2479
size_t name_length = strlen (string + position );
@@ -2504,7 +2514,7 @@ static int _element_to_dict(PyObject* self, const char* string,
2504
2514
}
2505
2515
position += (unsigned )name_length + 1 ;
2506
2516
* value = get_value (self , * name , string , & position , type ,
2507
- max - position , options );
2517
+ max - position , options , raw_array );
2508
2518
if (!* value ) {
2509
2519
Py_DECREF (* name );
2510
2520
return -1 ;
@@ -2520,12 +2530,13 @@ static PyObject* _cbson_element_to_dict(PyObject* self, PyObject* args) {
2520
2530
unsigned position ;
2521
2531
unsigned max ;
2522
2532
int new_position ;
2533
+ int raw_array = 0 ;
2523
2534
PyObject * name ;
2524
2535
PyObject * value ;
2525
2536
PyObject * result_tuple ;
2526
2537
2527
- if (!PyArg_ParseTuple (args , "OIIO&" , & bson , & position , & max ,
2528
- convert_codec_options , & options )) {
2538
+ if (!PyArg_ParseTuple (args , "OIIO&p " , & bson , & position , & max ,
2539
+ convert_codec_options , & options , & raw_array )) {
2529
2540
return NULL ;
2530
2541
}
2531
2542
@@ -2535,8 +2546,7 @@ static PyObject* _cbson_element_to_dict(PyObject* self, PyObject* args) {
2535
2546
}
2536
2547
string = PyBytes_AS_STRING (bson );
2537
2548
2538
- new_position = _element_to_dict (self , string , position , max , & options ,
2539
- & name , & value );
2549
+ new_position = _element_to_dict (self , string , position , max , & options , raw_array , & name , & value );
2540
2550
if (new_position < 0 ) {
2541
2551
return NULL ;
2542
2552
}
@@ -2560,13 +2570,14 @@ static PyObject* _elements_to_dict(PyObject* self, const char* string,
2560
2570
if (!dict ) {
2561
2571
return NULL ;
2562
2572
}
2573
+ int raw_array = 0 ;
2563
2574
while (position < max ) {
2564
2575
PyObject * name = NULL ;
2565
2576
PyObject * value = NULL ;
2566
2577
int new_position ;
2567
2578
2568
2579
new_position = _element_to_dict (
2569
- self , string , position , max , options , & name , & value );
2580
+ self , string , position , max , options , raw_array , & name , & value );
2570
2581
if (new_position < 0 ) {
2571
2582
Py_DECREF (dict );
2572
2583
return NULL ;
@@ -2649,7 +2660,6 @@ static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
2649
2660
}
2650
2661
2651
2662
string = (char * )view .buf ;
2652
-
2653
2663
memcpy (& size , string , 4 );
2654
2664
size = (int32_t )BSON_UINT32_FROM_LE (size );
2655
2665
if (size < BSON_MIN_SIZE ) {
@@ -2797,6 +2807,124 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
2797
2807
return result ;
2798
2808
}
2799
2809
2810
+
2811
+ static PyObject * _cbson_array_of_documents_to_buffer (PyObject * self , PyObject * args ) {
2812
+ uint32_t size ;
2813
+ uint32_t value_length ;
2814
+ uint32_t position = 0 ;
2815
+ buffer_t buffer ;
2816
+ const char * string ;
2817
+ PyObject * arr ;
2818
+ PyObject * result = NULL ;
2819
+ Py_buffer view = {0 };
2820
+
2821
+ if (!PyArg_ParseTuple (args , "O" , & arr )) {
2822
+ return NULL ;
2823
+ }
2824
+
2825
+ if (!_get_buffer (arr , & view )) {
2826
+ return NULL ;
2827
+ }
2828
+
2829
+ buffer = pymongo_buffer_new ();
2830
+ if (!buffer ) {
2831
+ PyBuffer_Release (& view );
2832
+ return NULL ;
2833
+ }
2834
+
2835
+ string = (char * )view .buf ;
2836
+
2837
+ if (view .len < BSON_MIN_SIZE ) {
2838
+ PyObject * InvalidBSON = _error ("InvalidBSON" );
2839
+ if (InvalidBSON ) {
2840
+ PyErr_SetString (InvalidBSON ,
2841
+ "not enough data for a BSON document" );
2842
+ Py_DECREF (InvalidBSON );
2843
+ }
2844
+ goto done ;
2845
+ }
2846
+
2847
+ memcpy (& size , string , 4 );
2848
+ size = BSON_UINT32_FROM_LE (size );
2849
+ /* save space for length */
2850
+ if (pymongo_buffer_save_space (buffer , size ) == -1 ) {
2851
+ goto fail ;
2852
+ }
2853
+ pymongo_buffer_update_position (buffer , 0 );
2854
+
2855
+ position += 4 ;
2856
+ while (position < size - 1 ) {
2857
+ // Verify the value is an object.
2858
+ unsigned char type = (unsigned char )string [position ];
2859
+ if (type != 3 ) {
2860
+ PyObject * InvalidBSON = _error ("InvalidBSON" );
2861
+ if (InvalidBSON ) {
2862
+ PyErr_SetString (InvalidBSON , "array element was not an object" );
2863
+ Py_DECREF (InvalidBSON );
2864
+ }
2865
+ goto fail ;
2866
+ }
2867
+
2868
+ // Just skip the keys.
2869
+ position = position + strlen (string + position ) + 1 ;
2870
+
2871
+ if (position >= size || (size - position ) < BSON_MIN_SIZE ) {
2872
+ PyObject * InvalidBSON = _error ("InvalidBSON" );
2873
+ if (InvalidBSON ) {
2874
+ PyErr_SetString (InvalidBSON , "invalid array content" );
2875
+ Py_DECREF (InvalidBSON );
2876
+ }
2877
+ goto fail ;
2878
+ }
2879
+
2880
+ memcpy (& value_length , string + position , 4 );
2881
+ value_length = BSON_UINT32_FROM_LE (value_length );
2882
+ if (value_length < BSON_MIN_SIZE ) {
2883
+ PyObject * InvalidBSON = _error ("InvalidBSON" );
2884
+ if (InvalidBSON ) {
2885
+ PyErr_SetString (InvalidBSON , "invalid message size" );
2886
+ Py_DECREF (InvalidBSON );
2887
+ }
2888
+ goto fail ;
2889
+ }
2890
+
2891
+ if (view .len < size ) {
2892
+ PyObject * InvalidBSON = _error ("InvalidBSON" );
2893
+ if (InvalidBSON ) {
2894
+ PyErr_SetString (InvalidBSON , "objsize too large" );
2895
+ Py_DECREF (InvalidBSON );
2896
+ }
2897
+ goto fail ;
2898
+ }
2899
+
2900
+ if (string [size - 1 ]) {
2901
+ PyObject * InvalidBSON = _error ("InvalidBSON" );
2902
+ if (InvalidBSON ) {
2903
+ PyErr_SetString (InvalidBSON , "bad eoo" );
2904
+ Py_DECREF (InvalidBSON );
2905
+ }
2906
+ goto fail ;
2907
+ }
2908
+
2909
+ if (pymongo_buffer_write (buffer , string + position , value_length ) == 1 ) {
2910
+ goto fail ;
2911
+ }
2912
+ position += value_length ;
2913
+ }
2914
+
2915
+ /* objectify buffer */
2916
+ result = Py_BuildValue ("y#" , pymongo_buffer_get_buffer (buffer ),
2917
+ (Py_ssize_t )pymongo_buffer_get_position (buffer ));
2918
+ goto done ;
2919
+ fail :
2920
+ result = NULL ;
2921
+ done :
2922
+ PyBuffer_Release (& view );
2923
+ pymongo_buffer_free (buffer );
2924
+ return result ;
2925
+ }
2926
+
2927
+
2800
2928
static PyMethodDef _CBSONMethods [] = {
2801
2929
{"_dict_to_bson" , _cbson_dict_to_bson , METH_VARARGS ,
2802
2930
"convert a dictionary to a string containing its BSON representation." },
@@ -2806,6 +2934,7 @@ static PyMethodDef _CBSONMethods[] = {
2806
2934
"convert binary data to a sequence of documents." },
2807
2935
{"_element_to_dict" , _cbson_element_to_dict , METH_VARARGS ,
2808
2936
"Decode a single key, value pair." },
2937
+ {"_array_of_documents_to_buffer" , _cbson_array_of_documents_to_buffer , METH_VARARGS , "Convert raw array of documents to a stream of BSON documents" },
2809
2938
{NULL , NULL , 0 , NULL }
2810
2939
};
2811
2940
0 commit comments