File indexing completed on 2025-11-19 09:50:45
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 #ifndef Py_BUILD_CORE
0044 # error "this header requires Py_BUILD_CORE define"
0045 #endif
0046
0047 typedef struct {
0048
0049 PyObject *list;
0050
0051 Py_ssize_t allocated;
0052
0053 Py_ssize_t max_length;
0054 } _BlocksOutputBuffer;
0055
0056 static const char unable_allocate_msg[] = "Unable to allocate output buffer.";
0057
0058
0059 #define OUTPUT_BUFFER_MAX_BLOCK_SIZE (256*1024*1024)
0060
0061
0062 #define KB (1024)
0063 #define MB (1024*1024)
0064 static const Py_ssize_t BUFFER_BLOCK_SIZE[] =
0065 { 32*KB, 64*KB, 256*KB, 1*MB, 4*MB, 8*MB, 16*MB, 16*MB,
0066 32*MB, 32*MB, 32*MB, 32*MB, 64*MB, 64*MB, 128*MB, 128*MB,
0067 OUTPUT_BUFFER_MAX_BLOCK_SIZE };
0068 #undef KB
0069 #undef MB
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
0102
0103
0104
0105 static inline Py_ssize_t
0106 _BlocksOutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer,
0107 const Py_ssize_t max_length,
0108 void **next_out)
0109 {
0110 PyObject *b;
0111 Py_ssize_t block_size;
0112
0113
0114 assert(buffer->list == NULL);
0115
0116
0117 if (0 <= max_length && max_length < BUFFER_BLOCK_SIZE[0]) {
0118 block_size = max_length;
0119 } else {
0120 block_size = BUFFER_BLOCK_SIZE[0];
0121 }
0122
0123
0124 b = PyBytes_FromStringAndSize(NULL, block_size);
0125 if (b == NULL) {
0126 return -1;
0127 }
0128
0129
0130 buffer->list = PyList_New(1);
0131 if (buffer->list == NULL) {
0132 Py_DECREF(b);
0133 return -1;
0134 }
0135 PyList_SET_ITEM(buffer->list, 0, b);
0136
0137
0138 buffer->allocated = block_size;
0139 buffer->max_length = max_length;
0140
0141 *next_out = PyBytes_AS_STRING(b);
0142 return block_size;
0143 }
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153 static inline Py_ssize_t
0154 _BlocksOutputBuffer_InitWithSize(_BlocksOutputBuffer *buffer,
0155 const Py_ssize_t init_size,
0156 void **next_out)
0157 {
0158 PyObject *b;
0159
0160
0161 assert(buffer->list == NULL);
0162
0163
0164 b = PyBytes_FromStringAndSize(NULL, init_size);
0165 if (b == NULL) {
0166 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0167 return -1;
0168 }
0169
0170
0171 buffer->list = PyList_New(1);
0172 if (buffer->list == NULL) {
0173 Py_DECREF(b);
0174 return -1;
0175 }
0176 PyList_SET_ITEM(buffer->list, 0, b);
0177
0178
0179 buffer->allocated = init_size;
0180 buffer->max_length = -1;
0181
0182 *next_out = PyBytes_AS_STRING(b);
0183 return init_size;
0184 }
0185
0186
0187
0188
0189
0190
0191 static inline Py_ssize_t
0192 _BlocksOutputBuffer_Grow(_BlocksOutputBuffer *buffer,
0193 void **next_out,
0194 const Py_ssize_t avail_out)
0195 {
0196 PyObject *b;
0197 const Py_ssize_t list_len = Py_SIZE(buffer->list);
0198 Py_ssize_t block_size;
0199
0200
0201 if (avail_out != 0) {
0202 PyErr_SetString(PyExc_SystemError,
0203 "avail_out is non-zero in _BlocksOutputBuffer_Grow().");
0204 return -1;
0205 }
0206
0207
0208 if (list_len < (Py_ssize_t) Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE)) {
0209 block_size = BUFFER_BLOCK_SIZE[list_len];
0210 } else {
0211 block_size = BUFFER_BLOCK_SIZE[Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE) - 1];
0212 }
0213
0214
0215 if (buffer->max_length >= 0) {
0216
0217 Py_ssize_t rest = buffer->max_length - buffer->allocated;
0218 assert(rest > 0);
0219
0220
0221 if (block_size > rest) {
0222 block_size = rest;
0223 }
0224 }
0225
0226
0227 if (block_size > PY_SSIZE_T_MAX - buffer->allocated) {
0228 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0229 return -1;
0230 }
0231
0232
0233 b = PyBytes_FromStringAndSize(NULL, block_size);
0234 if (b == NULL) {
0235 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0236 return -1;
0237 }
0238 if (PyList_Append(buffer->list, b) < 0) {
0239 Py_DECREF(b);
0240 return -1;
0241 }
0242 Py_DECREF(b);
0243
0244
0245 buffer->allocated += block_size;
0246
0247 *next_out = PyBytes_AS_STRING(b);
0248 return block_size;
0249 }
0250
0251
0252 static inline Py_ssize_t
0253 _BlocksOutputBuffer_GetDataSize(_BlocksOutputBuffer *buffer,
0254 const Py_ssize_t avail_out)
0255 {
0256 return buffer->allocated - avail_out;
0257 }
0258
0259
0260
0261
0262
0263
0264 static inline PyObject *
0265 _BlocksOutputBuffer_Finish(_BlocksOutputBuffer *buffer,
0266 const Py_ssize_t avail_out)
0267 {
0268 PyObject *result, *block;
0269 const Py_ssize_t list_len = Py_SIZE(buffer->list);
0270
0271
0272 if ((list_len == 1 && avail_out == 0) ||
0273 (list_len == 2 && Py_SIZE(PyList_GET_ITEM(buffer->list, 1)) == avail_out))
0274 {
0275 block = PyList_GET_ITEM(buffer->list, 0);
0276 Py_INCREF(block);
0277
0278 Py_CLEAR(buffer->list);
0279 return block;
0280 }
0281
0282
0283 result = PyBytes_FromStringAndSize(NULL, buffer->allocated - avail_out);
0284 if (result == NULL) {
0285 PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
0286 return NULL;
0287 }
0288
0289
0290 if (list_len > 0) {
0291 char *posi = PyBytes_AS_STRING(result);
0292
0293
0294 Py_ssize_t i = 0;
0295 for (; i < list_len-1; i++) {
0296 block = PyList_GET_ITEM(buffer->list, i);
0297 memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block));
0298 posi += Py_SIZE(block);
0299 }
0300
0301 block = PyList_GET_ITEM(buffer->list, i);
0302 memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block) - avail_out);
0303 } else {
0304 assert(Py_SIZE(result) == 0);
0305 }
0306
0307 Py_CLEAR(buffer->list);
0308 return result;
0309 }
0310
0311
0312 static inline void
0313 _BlocksOutputBuffer_OnError(_BlocksOutputBuffer *buffer)
0314 {
0315 Py_CLEAR(buffer->list);
0316 }
0317
0318 #ifdef __cplusplus
0319 }
0320 #endif
0321 #endif