File indexing completed on 2025-01-30 10:18:05
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035 #ifndef Py_INTERNAL_BLOCKS_OUTPUT_BUFFER_H
0036 #define Py_INTERNAL_BLOCKS_OUTPUT_BUFFER_H
0037 #ifdef __cplusplus
0038 extern "C" {
0039 #endif
0040
0041 #include "Python.h"
0042
0043 typedef struct {
0044
0045 PyObject *list;
0046
0047 Py_ssize_t allocated;
0048
0049 Py_ssize_t max_length;
0050 } _BlocksOutputBuffer;
0051
0052 static const char unable_allocate_msg[] = "Unable to allocate output buffer.";
0053
0054
0055 #define OUTPUT_BUFFER_MAX_BLOCK_SIZE (256*1024*1024)
0056
0057
0058 #define KB (1024)
0059 #define MB (1024*1024)
0060 static const Py_ssize_t BUFFER_BLOCK_SIZE[] =
0061 { 32*KB, 64*KB, 256*KB, 1*MB, 4*MB, 8*MB, 16*MB, 16*MB,
0062 32*MB, 32*MB, 32*MB, 32*MB, 64*MB, 64*MB, 128*MB, 128*MB,
0063 OUTPUT_BUFFER_MAX_BLOCK_SIZE };
0064 #undef KB
0065 #undef MB
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 static inline Py_ssize_t
0102 _BlocksOutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer,
0103 const Py_ssize_t max_length,
0104 void **next_out)
0105 {
0106 PyObject *b;
0107 Py_ssize_t block_size;
0108
0109
0110 assert(buffer->list == NULL);
0111
0112
0113 if (0 <= max_length && max_length < BUFFER_BLOCK_SIZE[0]) {
0114 block_size = max_length;
0115 } else {
0116 block_size = BUFFER_BLOCK_SIZE[0];
0117 }
0118
0119
0120 b = PyBytes_FromStringAndSize(NULL, block_size);
0121 if (b == NULL) {
0122 return -1;
0123 }
0124
0125
0126 buffer->list = PyList_New(1);
0127 if (buffer->list == NULL) {
0128 Py_DECREF(b);
0129 return -1;
0130 }
0131 PyList_SET_ITEM(buffer->list, 0, b);
0132
0133
0134 buffer->allocated = block_size;
0135 buffer->max_length = max_length;
0136
0137 *next_out = PyBytes_AS_STRING(b);
0138 return block_size;
0139 }
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149 static inline Py_ssize_t
0150 _BlocksOutputBuffer_InitWithSize(_BlocksOutputBuffer *buffer,
0151 const Py_ssize_t init_size,
0152 void **next_out)
0153 {
0154 PyObject *b;
0155
0156
0157 assert(buffer->list == NULL);
0158
0159
0160 b = PyBytes_FromStringAndSize(NULL, init_size);
0161 if (b == NULL) {
0162 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0163 return -1;
0164 }
0165
0166
0167 buffer->list = PyList_New(1);
0168 if (buffer->list == NULL) {
0169 Py_DECREF(b);
0170 return -1;
0171 }
0172 PyList_SET_ITEM(buffer->list, 0, b);
0173
0174
0175 buffer->allocated = init_size;
0176 buffer->max_length = -1;
0177
0178 *next_out = PyBytes_AS_STRING(b);
0179 return init_size;
0180 }
0181
0182
0183
0184
0185
0186
0187 static inline Py_ssize_t
0188 _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
0189 void **next_out,
0190 const Py_ssize_t avail_out)
0191 {
0192 PyObject *b;
0193 const Py_ssize_t list_len = Py_SIZE(buffer->list);
0194 Py_ssize_t block_size;
0195
0196
0197 if (avail_out != 0) {
0198 PyErr_SetString(PyExc_SystemError,
0199 "avail_out is non-zero in _BlocksOutputBuffer_Grow().");
0200 return -1;
0201 }
0202
0203
0204 if (list_len < (Py_ssize_t) Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE)) {
0205 block_size = BUFFER_BLOCK_SIZE[list_len];
0206 } else {
0207 block_size = BUFFER_BLOCK_SIZE[Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE) - 1];
0208 }
0209
0210
0211 if (buffer->max_length >= 0) {
0212
0213 Py_ssize_t rest = buffer->max_length - buffer->allocated;
0214 assert(rest > 0);
0215
0216
0217 if (block_size > rest) {
0218 block_size = rest;
0219 }
0220 }
0221
0222
0223 if (block_size > PY_SSIZE_T_MAX - buffer->allocated) {
0224 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0225 return -1;
0226 }
0227
0228
0229 b = PyBytes_FromStringAndSize(NULL, block_size);
0230 if (b == NULL) {
0231 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0232 return -1;
0233 }
0234 if (PyList_Append(buffer->list, b) < 0) {
0235 Py_DECREF(b);
0236 return -1;
0237 }
0238 Py_DECREF(b);
0239
0240
0241 buffer->allocated += block_size;
0242
0243 *next_out = PyBytes_AS_STRING(b);
0244 return block_size;
0245 }
0246
0247
0248 static inline Py_ssize_t
0249 _BlocksOutputBuffer_GetDataSize(_BlocksOutputBuffer *buffer,
0250 const Py_ssize_t avail_out)
0251 {
0252 return buffer->allocated - avail_out;
0253 }
0254
0255
0256
0257
0258
0259
0260 static inline PyObject *
0261 _BlocksOutputBuffer_Finish(_BlocksOutputBuffer *buffer,
0262 const Py_ssize_t avail_out)
0263 {
0264 PyObject *result, *block;
0265 const Py_ssize_t list_len = Py_SIZE(buffer->list);
0266
0267
0268 if ((list_len == 1 && avail_out == 0) ||
0269 (list_len == 2 && Py_SIZE(PyList_GET_ITEM(buffer->list, 1)) == avail_out))
0270 {
0271 block = PyList_GET_ITEM(buffer->list, 0);
0272 Py_INCREF(block);
0273
0274 Py_CLEAR(buffer->list);
0275 return block;
0276 }
0277
0278
0279 result = PyBytes_FromStringAndSize(NULL, buffer->allocated - avail_out);
0280 if (result == NULL) {
0281 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0282 return NULL;
0283 }
0284
0285
0286 if (list_len > 0) {
0287 char *posi = PyBytes_AS_STRING(result);
0288
0289
0290 Py_ssize_t i = 0;
0291 for (; i < list_len-1; i++) {
0292 block = PyList_GET_ITEM(buffer->list, i);
0293 memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block));
0294 posi += Py_SIZE(block);
0295 }
0296
0297 block = PyList_GET_ITEM(buffer->list, i);
0298 memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block) - avail_out);
0299 } else {
0300 assert(Py_SIZE(result) == 0);
0301 }
0302
0303 Py_CLEAR(buffer->list);
0304 return result;
0305 }
0306
0307
0308 static inline void
0309 _BlocksOutputBuffer_OnError(_BlocksOutputBuffer *buffer)
0310 {
0311 Py_CLEAR(buffer->list);
0312 }
0313
0314 #ifdef __cplusplus
0315 }
0316 #endif
0317 #endif