1 /*
2  * Copyright 2009-2015 MongoDB, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /* Include Python.h so we can set Python's error indicator. */
18 #define PY_SSIZE_T_CLEAN
19 #include "Python.h"
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "buffer.h"
25 
26 #define INITIAL_BUFFER_SIZE 256
27 
28 struct buffer {
29     char* buffer;
30     int size;
31     int position;
32 };
33 
34 /* Set Python's error indicator to MemoryError.
35  * Called after allocation failures. */
set_memory_error(void)36 static void set_memory_error(void) {
37     PyErr_NoMemory();
38 }
39 
40 /* Allocate and return a new buffer.
41  * Return NULL and sets MemoryError on allocation failure. */
buffer_new(void)42 buffer_t buffer_new(void) {
43     buffer_t buffer;
44     buffer = (buffer_t)malloc(sizeof(struct buffer));
45     if (buffer == NULL) {
46         set_memory_error();
47         return NULL;
48     }
49 
50     buffer->size = INITIAL_BUFFER_SIZE;
51     buffer->position = 0;
52     buffer->buffer = (char*)malloc(sizeof(char) * INITIAL_BUFFER_SIZE);
53     if (buffer->buffer == NULL) {
54         free(buffer);
55         set_memory_error();
56         return NULL;
57     }
58 
59     return buffer;
60 }
61 
62 /* Free the memory allocated for `buffer`.
63  * Return non-zero on failure. */
buffer_free(buffer_t buffer)64 int buffer_free(buffer_t buffer) {
65     if (buffer == NULL) {
66         return 1;
67     }
68     /* Buffer will be NULL when buffer_grow fails. */
69     if (buffer->buffer != NULL) {
70         free(buffer->buffer);
71     }
72     free(buffer);
73     return 0;
74 }
75 
76 /* Grow `buffer` to at least `min_length`.
77  * Return non-zero and sets MemoryError on allocation failure. */
buffer_grow(buffer_t buffer,int min_length)78 static int buffer_grow(buffer_t buffer, int min_length) {
79     int old_size = 0;
80     int size = buffer->size;
81     char* old_buffer = buffer->buffer;
82     if (size >= min_length) {
83         return 0;
84     }
85     while (size < min_length) {
86         old_size = size;
87         size *= 2;
88         if (size <= old_size) {
89            /* Size did not increase. Could be an overflow
90             * or size < 1. Just go with min_length. */
91            size = min_length;
92         }
93     }
94     buffer->buffer = (char*)realloc(buffer->buffer, sizeof(char) * size);
95     if (buffer->buffer == NULL) {
96         free(old_buffer);
97         set_memory_error();
98         return 1;
99     }
100     buffer->size = size;
101     return 0;
102 }
103 
104 /* Assure that `buffer` has at least `size` free bytes (and grow if needed).
105  * Return non-zero and sets MemoryError on allocation failure.
106  * Return non-zero and sets ValueError if `size` would exceed 2GiB. */
buffer_assure_space(buffer_t buffer,int size)107 static int buffer_assure_space(buffer_t buffer, int size) {
108     int new_size = buffer->position + size;
109     /* Check for overflow. */
110     if (new_size < buffer->position) {
111         PyErr_SetString(PyExc_ValueError,
112                         "Document would overflow BSON size limit");
113         return 1;
114     }
115 
116     if (new_size <= buffer->size) {
117         return 0;
118     }
119     return buffer_grow(buffer, new_size);
120 }
121 
122 /* Save `size` bytes from the current position in `buffer` (and grow if needed).
123  * Return offset for writing, or -1 on failure.
124  * Sets MemoryError or ValueError on failure. */
buffer_save_space(buffer_t buffer,int size)125 buffer_position buffer_save_space(buffer_t buffer, int size) {
126     int position = buffer->position;
127     if (buffer_assure_space(buffer, size) != 0) {
128         return -1;
129     }
130     buffer->position += size;
131     return position;
132 }
133 
134 /* Write `size` bytes from `data` to `buffer` (and grow if needed).
135  * Return non-zero on failure.
136  * Sets MemoryError or ValueError on failure. */
buffer_write(buffer_t buffer,const char * data,int size)137 int buffer_write(buffer_t buffer, const char* data, int size) {
138     if (buffer_assure_space(buffer, size) != 0) {
139         return 1;
140     }
141 
142     memcpy(buffer->buffer + buffer->position, data, size);
143     buffer->position += size;
144     return 0;
145 }
146 
buffer_get_position(buffer_t buffer)147 int buffer_get_position(buffer_t buffer) {
148     return buffer->position;
149 }
150 
buffer_get_buffer(buffer_t buffer)151 char* buffer_get_buffer(buffer_t buffer) {
152     return buffer->buffer;
153 }
154 
buffer_update_position(buffer_t buffer,buffer_position new_position)155 void buffer_update_position(buffer_t buffer, buffer_position new_position) {
156     buffer->position = new_position;
157 }
158