123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- from cpython.bytes cimport PyBytes_FromStringAndSize
- from cpython.exc cimport PyErr_NoMemory
- from cpython.mem cimport PyMem_Free, PyMem_Malloc, PyMem_Realloc
- from cpython.object cimport PyObject_Str
- from libc.stdint cimport uint8_t, uint64_t
- from libc.string cimport memcpy
- from multidict import istr
- DEF BUF_SIZE = 16 * 1024 # 16KiB
- cdef char BUFFER[BUF_SIZE]
- cdef object _istr = istr
- # ----------------- writer ---------------------------
- cdef struct Writer:
- char *buf
- Py_ssize_t size
- Py_ssize_t pos
- cdef inline void _init_writer(Writer* writer):
- writer.buf = &BUFFER[0]
- writer.size = BUF_SIZE
- writer.pos = 0
- cdef inline void _release_writer(Writer* writer):
- if writer.buf != BUFFER:
- PyMem_Free(writer.buf)
- cdef inline int _write_byte(Writer* writer, uint8_t ch):
- cdef char * buf
- cdef Py_ssize_t size
- if writer.pos == writer.size:
- # reallocate
- size = writer.size + BUF_SIZE
- if writer.buf == BUFFER:
- buf = <char*>PyMem_Malloc(size)
- if buf == NULL:
- PyErr_NoMemory()
- return -1
- memcpy(buf, writer.buf, writer.size)
- 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 += 1
- return 0
- cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol):
- cdef uint64_t utf = <uint64_t> symbol
- if utf < 0x80:
- return _write_byte(writer, <uint8_t>utf)
- elif utf < 0x800:
- if _write_byte(writer, <uint8_t>(0xc0 | (utf >> 6))) < 0:
- return -1
- return _write_byte(writer, <uint8_t>(0x80 | (utf & 0x3f)))
- elif 0xD800 <= utf <= 0xDFFF:
- # surogate pair, ignored
- return 0
- elif utf < 0x10000:
- if _write_byte(writer, <uint8_t>(0xe0 | (utf >> 12))) < 0:
- return -1
- if _write_byte(writer, <uint8_t>(0x80 | ((utf >> 6) & 0x3f))) < 0:
- return -1
- return _write_byte(writer, <uint8_t>(0x80 | (utf & 0x3f)))
- elif utf > 0x10FFFF:
- # symbol is too large
- return 0
- else:
- if _write_byte(writer, <uint8_t>(0xf0 | (utf >> 18))) < 0:
- return -1
- if _write_byte(writer,
- <uint8_t>(0x80 | ((utf >> 12) & 0x3f))) < 0:
- return -1
- if _write_byte(writer,
- <uint8_t>(0x80 | ((utf >> 6) & 0x3f))) < 0:
- return -1
- return _write_byte(writer, <uint8_t>(0x80 | (utf & 0x3f)))
- cdef inline int _write_str(Writer* writer, str s):
- cdef Py_UCS4 ch
- for ch in s:
- if _write_utf8(writer, ch) < 0:
- return -1
- # --------------- _serialize_headers ----------------------
- cdef str to_str(object s):
- typ = type(s)
- if typ is str:
- return <str>s
- elif typ is _istr:
- return PyObject_Str(s)
- elif not isinstance(s, str):
- raise TypeError("Cannot serialize non-str key {!r}".format(s))
- else:
- return str(s)
- def _serialize_headers(str status_line, headers):
- cdef Writer writer
- cdef object key
- cdef object val
- cdef bytes ret
- _init_writer(&writer)
- try:
- if _write_str(&writer, status_line) < 0:
- raise
- if _write_byte(&writer, b'\r') < 0:
- raise
- if _write_byte(&writer, b'\n') < 0:
- raise
- for key, val in headers.items():
- if _write_str(&writer, to_str(key)) < 0:
- raise
- if _write_byte(&writer, b':') < 0:
- raise
- if _write_byte(&writer, b' ') < 0:
- raise
- if _write_str(&writer, to_str(val)) < 0:
- raise
- if _write_byte(&writer, b'\r') < 0:
- raise
- if _write_byte(&writer, b'\n') < 0:
- raise
- if _write_byte(&writer, b'\r') < 0:
- raise
- if _write_byte(&writer, b'\n') < 0:
- raise
- return PyBytes_FromStringAndSize(writer.buf, writer.pos)
- finally:
- _release_writer(&writer)
|