Last active
October 19, 2025 19:41
-
-
Save Vizonex/ae69ce64a7ef7ec8683afeffc90ba554 to your computer and use it in GitHub Desktop.
concet for a new writer for aiohttp based written in CPython based off the current aiohttp _http_writer written in cython but with a few more apis for http/2 http/3 frame packing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* This CPython Writer is a mirror of the aiohttp & Yarl source code for implementing | |
| a fast and easy to use writer that could be implemented & Used in Cython and | |
| CPython projects. This was an idea for the transition of the current _http_writer | |
| cython module in aiohttp on over to C When or if we decide to go down that route with | |
| header serlization with Http/1.1 , | |
| This also has new tools that could be used for http/2 Frame writing | |
| so this library also implements tools that the struct module would use but for writing | |
| in special bytes. | |
| This code is under than MIT/Appache 2.0 LICENSE | |
| Author: Vizonex | |
| */ | |
| #ifndef __CWRITER_H__ | |
| #define __CWRITER_H__ | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| #include <Python.h> | |
| #include <stdint.h> /* uint64_t */ | |
| #include <string.h> /* memset */ | |
| /* TODO: A Macro for altering writer's typename if nessary by an end developer | |
| incase this name comes off as being too generic */ | |
| typedef struct writer_s writer_t; | |
| struct writer_s { | |
| char* buf; | |
| Py_ssize_t size; | |
| Py_ssize_t pos; | |
| Py_ssize_t realloc_size; | |
| /* TODO: Store a flag in this object instead of a heap_allocated boolean | |
| * as code-safety */ | |
| int heap_allocated; | |
| }; | |
| static inline void | |
| writer_init(writer_t* writer, char* buf, Py_ssize_t size){ | |
| writer->buf = buf; | |
| writer->size = size; | |
| /* will use this size to increment the heap by */ | |
| writer->realloc_size = size; | |
| writer->pos = 0; | |
| writer->heap_allocated = 0; | |
| } | |
| static inline void | |
| writer_release(writer_t* writer){ | |
| if (writer->heap_allocated){ | |
| PyMem_Free(writer->buf); | |
| } | |
| } | |
| static inline int | |
| writer_add_byte(writer_t* writer, uint8_t ch){ | |
| char* buf; | |
| Py_ssize_t size; | |
| if (writer->pos == writer->size){ | |
| /* Reallocate Memory */ | |
| size = writer->size + writer->realloc_size; | |
| if (!writer->heap_allocated){ | |
| buf = (char*)PyMem_Malloc(size); | |
| if (buf == NULL){ | |
| PyErr_NoMemory(); | |
| return -1; | |
| } | |
| memcpy(buf , writer->buf, writer->size); | |
| /* set flag that were heap allocated */ | |
| writer->heap_allocated = 1; | |
| } else { | |
| buf = (char*)PyMem_Realloc(writer->buf, size); | |
| if (buf == NULL){ | |
| PyErr_NoMemory(); | |
| return -1; | |
| } | |
| } | |
| writer->buf = buf; | |
| writer->size = size; | |
| } | |
| writer->buf[writer->pos] = (char)(ch); | |
| writer->pos++; | |
| return 0; | |
| } | |
| static inline int | |
| writer_add_utf8(writer_t* writer, Py_UCS4 symbol){ | |
| uint64_t utf = (uint64_t)(symbol); | |
| if (utf < 0x80){ | |
| return writer_add_byte(writer, (uint8_t)(utf)); | |
| } | |
| if (utf < 0x800){ | |
| if (writer_add_byte(writer, (uint8_t)(0xc0 | (utf >> 6))) < 0) | |
| return -1; | |
| return writer_add_byte(writer, (uint8_t)(0x80 | (utf & 0x3f))); | |
| } | |
| if (0xD800 <= utf <= 0xDFFF){ | |
| /* Ignore Surrogate Escape Values */ | |
| return 0; | |
| } | |
| if (utf < 0x10000){ | |
| if (writer_add_byte(writer, (uint8_t)(0xe0 | (utf >> 12))) < 0) | |
| return -1; | |
| if (writer_add_byte(writer, (uint8_t)(0x80 | ((utf >> 6) & 0x3f))) < 0) | |
| return -1; | |
| return writer_add_byte(writer, (uint8_t)(0x80 | (utf & 0x3f))); | |
| } | |
| if (utf > 0x10FFFF){ | |
| /* symbol is too large*/ | |
| return 0; | |
| } | |
| if (writer_add_byte(writer, (uint8_t)(0xf0 | (utf >> 18))) < 0) | |
| return -1; | |
| if (writer_add_byte(writer, | |
| (uint8_t)(0x80 | ((utf >> 12) & 0x3f))) < 0) | |
| return -1; | |
| if (writer_add_byte(writer, | |
| (uint8_t)(0x80 | ((utf >> 6) & 0x3f))) < 0) | |
| return -1; | |
| return writer_add_byte(writer, (uint8_t)(0x80 | (utf & 0x3f))); | |
| } | |
| static inline int | |
| writer_add_unicode(writer_t* writer, PyObject* s){ | |
| int ret = -1; | |
| Py_INCREF(s); | |
| Py_UCS4* chars = PyUnicode_4BYTE_DATA(s); | |
| Py_ssize_t size = PyUnicode_GET_LENGTH(s); | |
| for (Py_ssize_t i = 0; i < size; i++){ | |
| if (writer_add_utf8(writer, chars[i]) < 0){ | |
| goto exit; | |
| } | |
| } | |
| ret = 0; | |
| exit: | |
| Py_DECREF(s); | |
| return ret; | |
| } | |
| /* This point onwards is custom and does not come directly from aiohttp or yarl | |
| and is licensed under MIT */ | |
| static int writer_add_buffer(writer_t* writer, PyObject* buffer){ | |
| Py_buffer buf; | |
| int ret = -1; | |
| /* Ensure this buffer is held onto until were out of scope of this function */ | |
| Py_INCREF(buffer); | |
| if (PyObject_GetBuffer(buffer, &buf, PyBUF_SIMPLE) < 0){ | |
| goto exit; | |
| } | |
| char* data = (char*)(buf.buf); | |
| for (Py_ssize_t i = 0; i < buf.len; i++){ | |
| if (writer_add_byte(writer, data[i]) < 0){ | |
| PyBuffer_Release(&buf); | |
| goto exit; | |
| } | |
| } | |
| PyBuffer_Release(&buf); | |
| ret = 0; | |
| exit: | |
| Py_DECREF(buffer); | |
| return ret; | |
| } | |
| /* Packs a uint16 value in little-endian order */ | |
| static int writer_pack_u16(writer_t* writer, const uint16_t v){ | |
| uint16_t x = v; | |
| if (writer_add_byte(writer, (uint8_t)(x & 0xFF)) < 0){ | |
| return -1; | |
| } | |
| x >>= 8; | |
| return writer_add_byte(writer, (uint8_t)(x & 0xFF)); | |
| } | |
| #define CWRITER_PACK_BY(WRITER, BITCOUNT, X) \ | |
| for (size_t i = 0; i < BITCOUNT; i++){\ | |
| if (writer_add_byte(WRITER, (uint8_t)(X & 0xFF)) < 0){\ | |
| return -1; \ | |
| }\ | |
| X <<= 8;\ | |
| } | |
| /* They are the same thing... */ | |
| // static inline int writer_pack_u8(writer_t* writer, const uint8_t v); | |
| #define writer_pack_u8 writer_add_byte | |
| /* Packs a uint32_t as uint24_t in little endian order */ | |
| static inline int | |
| writer_pack_u24(writer_t* writer, const uint32_t v){ | |
| uint32_t x = v; | |
| CWRITER_PACK_BY(writer, 3, x); | |
| return 0; | |
| } | |
| /* Packs a uint32_t in little endian order */ | |
| static inline int | |
| writer_pack_u32(writer_t* writer, const uint32_t v){ | |
| uint32_t x = v; | |
| CWRITER_PACK_BY(writer, 4, x); | |
| return 0; | |
| } | |
| /* Packs a uint64_t in little endian order */ | |
| static inline int | |
| writer_pack_u64(writer_t* writer, const uint64_t v){ | |
| uint32_t x = v; | |
| CWRITER_PACK_BY(writer, 4, x); | |
| return 0; | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| #endif // __CWRITER_H__ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment