compressionwriter.c
372 lines
| 10.6 KiB
| text/x-c
|
CLexer
Gregory Szorc
|
r30435 | /** | ||
* Copyright (c) 2016-present, Gregory Szorc | ||||
* All rights reserved. | ||||
* | ||||
* This software may be modified and distributed under the terms | ||||
* of the BSD license. See the LICENSE file for details. | ||||
*/ | ||||
#include "python-zstandard.h" | ||||
extern PyObject* ZstdError; | ||||
PyDoc_STRVAR(ZstdCompresssionWriter__doc__, | ||||
"""A context manager used for writing compressed output to a writer.\n" | ||||
); | ||||
static void ZstdCompressionWriter_dealloc(ZstdCompressionWriter* self) { | ||||
Py_XDECREF(self->compressor); | ||||
Py_XDECREF(self->writer); | ||||
Gregory Szorc
|
r42237 | PyMem_Free(self->output.dst); | ||
self->output.dst = NULL; | ||||
Gregory Szorc
|
r30435 | PyObject_Del(self); | ||
} | ||||
static PyObject* ZstdCompressionWriter_enter(ZstdCompressionWriter* self) { | ||||
Gregory Szorc
|
r42237 | if (self->closed) { | ||
PyErr_SetString(PyExc_ValueError, "stream is closed"); | ||||
return NULL; | ||||
} | ||||
Gregory Szorc
|
r37513 | |||
Gregory Szorc
|
r30435 | if (self->entered) { | ||
PyErr_SetString(ZstdError, "cannot __enter__ multiple times"); | ||||
return NULL; | ||||
} | ||||
self->entered = 1; | ||||
Py_INCREF(self); | ||||
return (PyObject*)self; | ||||
} | ||||
static PyObject* ZstdCompressionWriter_exit(ZstdCompressionWriter* self, PyObject* args) { | ||||
PyObject* exc_type; | ||||
PyObject* exc_value; | ||||
PyObject* exc_tb; | ||||
Gregory Szorc
|
r30895 | if (!PyArg_ParseTuple(args, "OOO:__exit__", &exc_type, &exc_value, &exc_tb)) { | ||
Gregory Szorc
|
r30435 | return NULL; | ||
} | ||||
self->entered = 0; | ||||
Gregory Szorc
|
r37513 | if (exc_type == Py_None && exc_value == Py_None && exc_tb == Py_None) { | ||
Gregory Szorc
|
r42237 | PyObject* result = PyObject_CallMethod((PyObject*)self, "close", NULL); | ||
Gregory Szorc
|
r30435 | |||
Gregory Szorc
|
r42237 | if (NULL == result) { | ||
return NULL; | ||||
Gregory Szorc
|
r30435 | } | ||
} | ||||
Py_RETURN_FALSE; | ||||
} | ||||
static PyObject* ZstdCompressionWriter_memory_size(ZstdCompressionWriter* self) { | ||||
Gregory Szorc
|
r37513 | return PyLong_FromSize_t(ZSTD_sizeof_CCtx(self->compressor->cctx)); | ||
Gregory Szorc
|
r30435 | } | ||
Gregory Szorc
|
r37513 | static PyObject* ZstdCompressionWriter_write(ZstdCompressionWriter* self, PyObject* args, PyObject* kwargs) { | ||
static char* kwlist[] = { | ||||
"data", | ||||
NULL | ||||
}; | ||||
PyObject* result = NULL; | ||||
Py_buffer source; | ||||
Gregory Szorc
|
r30435 | size_t zresult; | ||
ZSTD_inBuffer input; | ||||
PyObject* res; | ||||
Gregory Szorc
|
r30895 | Py_ssize_t totalWrite = 0; | ||
Gregory Szorc
|
r30435 | |||
#if PY_MAJOR_VERSION >= 3 | ||||
Gregory Szorc
|
r37513 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:write", | ||
Gregory Szorc
|
r30435 | #else | ||
Gregory Szorc
|
r37513 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*:write", | ||
Gregory Szorc
|
r30435 | #endif | ||
Gregory Szorc
|
r37513 | kwlist, &source)) { | ||
Gregory Szorc
|
r30435 | return NULL; | ||
} | ||||
Gregory Szorc
|
r37513 | if (!PyBuffer_IsContiguous(&source, 'C') || source.ndim > 1) { | ||
PyErr_SetString(PyExc_ValueError, | ||||
"data buffer should be contiguous and have at most one dimension"); | ||||
goto finally; | ||||
Gregory Szorc
|
r30435 | } | ||
Gregory Szorc
|
r42237 | if (self->closed) { | ||
PyErr_SetString(PyExc_ValueError, "stream is closed"); | ||||
return NULL; | ||||
Gregory Szorc
|
r30435 | } | ||
Gregory Szorc
|
r42237 | |||
self->output.pos = 0; | ||||
Gregory Szorc
|
r30435 | |||
Gregory Szorc
|
r37513 | input.src = source.buf; | ||
input.size = source.len; | ||||
Gregory Szorc
|
r30435 | input.pos = 0; | ||
Gregory Szorc
|
r42237 | while (input.pos < (size_t)source.len) { | ||
Gregory Szorc
|
r30435 | Py_BEGIN_ALLOW_THREADS | ||
Gregory Szorc
|
r42237 | zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output, &input, ZSTD_e_continue); | ||
Gregory Szorc
|
r30435 | Py_END_ALLOW_THREADS | ||
if (ZSTD_isError(zresult)) { | ||||
PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); | ||||
Gregory Szorc
|
r37513 | goto finally; | ||
Gregory Szorc
|
r30435 | } | ||
/* Copy data from output buffer to writer. */ | ||||
Gregory Szorc
|
r42237 | if (self->output.pos) { | ||
Gregory Szorc
|
r30435 | #if PY_MAJOR_VERSION >= 3 | ||
res = PyObject_CallMethod(self->writer, "write", "y#", | ||||
#else | ||||
res = PyObject_CallMethod(self->writer, "write", "s#", | ||||
#endif | ||||
Gregory Szorc
|
r42237 | self->output.dst, self->output.pos); | ||
Gregory Szorc
|
r30435 | Py_XDECREF(res); | ||
Gregory Szorc
|
r42237 | totalWrite += self->output.pos; | ||
self->bytesCompressed += self->output.pos; | ||||
Gregory Szorc
|
r30435 | } | ||
Gregory Szorc
|
r42237 | self->output.pos = 0; | ||
Gregory Szorc
|
r30435 | } | ||
Gregory Szorc
|
r42237 | if (self->writeReturnRead) { | ||
result = PyLong_FromSize_t(input.pos); | ||||
} | ||||
else { | ||||
result = PyLong_FromSsize_t(totalWrite); | ||||
} | ||||
Gregory Szorc
|
r37513 | |||
finally: | ||||
PyBuffer_Release(&source); | ||||
return result; | ||||
Gregory Szorc
|
r30822 | } | ||
Gregory Szorc
|
r42237 | static PyObject* ZstdCompressionWriter_flush(ZstdCompressionWriter* self, PyObject* args, PyObject* kwargs) { | ||
static char* kwlist[] = { | ||||
"flush_mode", | ||||
NULL | ||||
}; | ||||
Gregory Szorc
|
r30822 | size_t zresult; | ||
Gregory Szorc
|
r37513 | ZSTD_inBuffer input; | ||
Gregory Szorc
|
r30822 | PyObject* res; | ||
Gregory Szorc
|
r30895 | Py_ssize_t totalWrite = 0; | ||
Gregory Szorc
|
r42237 | unsigned flush_mode = 0; | ||
ZSTD_EndDirective flush; | ||||
Gregory Szorc
|
r30822 | |||
Gregory Szorc
|
r42237 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|I:flush", | ||
kwlist, &flush_mode)) { | ||||
Gregory Szorc
|
r30822 | return NULL; | ||
Gregory Szorc
|
r30435 | } | ||
Gregory Szorc
|
r42237 | switch (flush_mode) { | ||
case 0: | ||||
flush = ZSTD_e_flush; | ||||
break; | ||||
case 1: | ||||
flush = ZSTD_e_end; | ||||
break; | ||||
default: | ||||
PyErr_Format(PyExc_ValueError, "unknown flush_mode: %d", flush_mode); | ||||
return NULL; | ||||
} | ||||
if (self->closed) { | ||||
PyErr_SetString(PyExc_ValueError, "stream is closed"); | ||||
return NULL; | ||||
} | ||||
self->output.pos = 0; | ||||
Gregory Szorc
|
r37513 | input.src = NULL; | ||
input.size = 0; | ||||
input.pos = 0; | ||||
Gregory Szorc
|
r30822 | while (1) { | ||
Py_BEGIN_ALLOW_THREADS | ||||
Gregory Szorc
|
r42237 | zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output, &input, flush); | ||
Gregory Szorc
|
r30822 | Py_END_ALLOW_THREADS | ||
if (ZSTD_isError(zresult)) { | ||||
PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); | ||||
return NULL; | ||||
} | ||||
/* Copy data from output buffer to writer. */ | ||||
Gregory Szorc
|
r42237 | if (self->output.pos) { | ||
Gregory Szorc
|
r30822 | #if PY_MAJOR_VERSION >= 3 | ||
res = PyObject_CallMethod(self->writer, "write", "y#", | ||||
#else | ||||
res = PyObject_CallMethod(self->writer, "write", "s#", | ||||
#endif | ||||
Gregory Szorc
|
r42237 | self->output.dst, self->output.pos); | ||
Gregory Szorc
|
r30822 | Py_XDECREF(res); | ||
Gregory Szorc
|
r42237 | totalWrite += self->output.pos; | ||
self->bytesCompressed += self->output.pos; | ||||
Gregory Szorc
|
r30822 | } | ||
Gregory Szorc
|
r40157 | |||
Gregory Szorc
|
r42237 | self->output.pos = 0; | ||
Gregory Szorc
|
r40157 | |||
if (!zresult) { | ||||
break; | ||||
} | ||||
Gregory Szorc
|
r30822 | } | ||
Gregory Szorc
|
r42237 | return PyLong_FromSsize_t(totalWrite); | ||
} | ||||
static PyObject* ZstdCompressionWriter_close(ZstdCompressionWriter* self) { | ||||
PyObject* result; | ||||
if (self->closed) { | ||||
Py_RETURN_NONE; | ||||
} | ||||
result = PyObject_CallMethod((PyObject*)self, "flush", "I", 1); | ||||
self->closed = 1; | ||||
if (NULL == result) { | ||||
return NULL; | ||||
} | ||||
Gregory Szorc
|
r30822 | |||
Gregory Szorc
|
r42237 | /* Call close on underlying stream as well. */ | ||
if (PyObject_HasAttrString(self->writer, "close")) { | ||||
return PyObject_CallMethod(self->writer, "close", NULL); | ||||
} | ||||
Py_RETURN_NONE; | ||||
} | ||||
static PyObject* ZstdCompressionWriter_fileno(ZstdCompressionWriter* self) { | ||||
if (PyObject_HasAttrString(self->writer, "fileno")) { | ||||
return PyObject_CallMethod(self->writer, "fileno", NULL); | ||||
} | ||||
else { | ||||
PyErr_SetString(PyExc_OSError, "fileno not available on underlying writer"); | ||||
return NULL; | ||||
} | ||||
Gregory Szorc
|
r30822 | } | ||
Gregory Szorc
|
r37513 | static PyObject* ZstdCompressionWriter_tell(ZstdCompressionWriter* self) { | ||
return PyLong_FromUnsignedLongLong(self->bytesCompressed); | ||||
} | ||||
Gregory Szorc
|
r42237 | static PyObject* ZstdCompressionWriter_writelines(PyObject* self, PyObject* args) { | ||
PyErr_SetNone(PyExc_NotImplementedError); | ||||
return NULL; | ||||
} | ||||
static PyObject* ZstdCompressionWriter_false(PyObject* self, PyObject* args) { | ||||
Py_RETURN_FALSE; | ||||
} | ||||
static PyObject* ZstdCompressionWriter_true(PyObject* self, PyObject* args) { | ||||
Py_RETURN_TRUE; | ||||
} | ||||
static PyObject* ZstdCompressionWriter_unsupported(PyObject* self, PyObject* args, PyObject* kwargs) { | ||||
PyObject* iomod; | ||||
PyObject* exc; | ||||
iomod = PyImport_ImportModule("io"); | ||||
if (NULL == iomod) { | ||||
return NULL; | ||||
} | ||||
exc = PyObject_GetAttrString(iomod, "UnsupportedOperation"); | ||||
if (NULL == exc) { | ||||
Py_DECREF(iomod); | ||||
return NULL; | ||||
} | ||||
PyErr_SetNone(exc); | ||||
Py_DECREF(exc); | ||||
Py_DECREF(iomod); | ||||
return NULL; | ||||
} | ||||
Gregory Szorc
|
r30435 | static PyMethodDef ZstdCompressionWriter_methods[] = { | ||
{ "__enter__", (PyCFunction)ZstdCompressionWriter_enter, METH_NOARGS, | ||||
PyDoc_STR("Enter a compression context.") }, | ||||
{ "__exit__", (PyCFunction)ZstdCompressionWriter_exit, METH_VARARGS, | ||||
PyDoc_STR("Exit a compression context.") }, | ||||
Gregory Szorc
|
r42237 | { "close", (PyCFunction)ZstdCompressionWriter_close, METH_NOARGS, NULL }, | ||
{ "fileno", (PyCFunction)ZstdCompressionWriter_fileno, METH_NOARGS, NULL }, | ||||
{ "isatty", (PyCFunction)ZstdCompressionWriter_false, METH_NOARGS, NULL }, | ||||
{ "readable", (PyCFunction)ZstdCompressionWriter_false, METH_NOARGS, NULL }, | ||||
{ "readline", (PyCFunction)ZstdCompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL }, | ||||
{ "readlines", (PyCFunction)ZstdCompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL }, | ||||
{ "seek", (PyCFunction)ZstdCompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL }, | ||||
{ "seekable", ZstdCompressionWriter_false, METH_NOARGS, NULL }, | ||||
{ "truncate", (PyCFunction)ZstdCompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL }, | ||||
{ "writable", ZstdCompressionWriter_true, METH_NOARGS, NULL }, | ||||
{ "writelines", ZstdCompressionWriter_writelines, METH_VARARGS, NULL }, | ||||
{ "read", (PyCFunction)ZstdCompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL }, | ||||
{ "readall", (PyCFunction)ZstdCompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL }, | ||||
{ "readinto", (PyCFunction)ZstdCompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL }, | ||||
Gregory Szorc
|
r30435 | { "memory_size", (PyCFunction)ZstdCompressionWriter_memory_size, METH_NOARGS, | ||
PyDoc_STR("Obtain the memory size of the underlying compressor") }, | ||||
Gregory Szorc
|
r37513 | { "write", (PyCFunction)ZstdCompressionWriter_write, METH_VARARGS | METH_KEYWORDS, | ||
Gregory Szorc
|
r30435 | PyDoc_STR("Compress data") }, | ||
Gregory Szorc
|
r42237 | { "flush", (PyCFunction)ZstdCompressionWriter_flush, METH_VARARGS | METH_KEYWORDS, | ||
Gregory Szorc
|
r30822 | PyDoc_STR("Flush data and finish a zstd frame") }, | ||
Gregory Szorc
|
r37513 | { "tell", (PyCFunction)ZstdCompressionWriter_tell, METH_NOARGS, | ||
PyDoc_STR("Returns current number of bytes compressed") }, | ||||
Gregory Szorc
|
r30435 | { NULL, NULL } | ||
}; | ||||
Gregory Szorc
|
r42237 | static PyMemberDef ZstdCompressionWriter_members[] = { | ||
{ "closed", T_BOOL, offsetof(ZstdCompressionWriter, closed), READONLY, NULL }, | ||||
{ NULL } | ||||
}; | ||||
Gregory Szorc
|
r30435 | PyTypeObject ZstdCompressionWriterType = { | ||
PyVarObject_HEAD_INIT(NULL, 0) | ||||
"zstd.ZstdCompressionWriter", /* tp_name */ | ||||
sizeof(ZstdCompressionWriter), /* tp_basicsize */ | ||||
0, /* tp_itemsize */ | ||||
(destructor)ZstdCompressionWriter_dealloc, /* tp_dealloc */ | ||||
0, /* tp_print */ | ||||
0, /* tp_getattr */ | ||||
0, /* tp_setattr */ | ||||
0, /* tp_compare */ | ||||
0, /* tp_repr */ | ||||
0, /* tp_as_number */ | ||||
0, /* tp_as_sequence */ | ||||
0, /* tp_as_mapping */ | ||||
0, /* tp_hash */ | ||||
0, /* tp_call */ | ||||
0, /* tp_str */ | ||||
0, /* tp_getattro */ | ||||
0, /* tp_setattro */ | ||||
0, /* tp_as_buffer */ | ||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ | ||||
ZstdCompresssionWriter__doc__, /* tp_doc */ | ||||
0, /* tp_traverse */ | ||||
0, /* tp_clear */ | ||||
0, /* tp_richcompare */ | ||||
0, /* tp_weaklistoffset */ | ||||
0, /* tp_iter */ | ||||
0, /* tp_iternext */ | ||||
ZstdCompressionWriter_methods, /* tp_methods */ | ||||
Gregory Szorc
|
r42237 | ZstdCompressionWriter_members, /* tp_members */ | ||
Gregory Szorc
|
r30435 | 0, /* tp_getset */ | ||
0, /* tp_base */ | ||||
0, /* tp_dict */ | ||||
0, /* tp_descr_get */ | ||||
0, /* tp_descr_set */ | ||||
0, /* tp_dictoffset */ | ||||
0, /* tp_init */ | ||||
0, /* tp_alloc */ | ||||
PyType_GenericNew, /* tp_new */ | ||||
}; | ||||
void compressionwriter_module_init(PyObject* mod) { | ||||
Victor Stinner
|
r47090 | Py_SET_TYPE(&ZstdCompressionWriterType, &PyType_Type); | ||
Gregory Szorc
|
r30435 | if (PyType_Ready(&ZstdCompressionWriterType) < 0) { | ||
return; | ||||
} | ||||
} | ||||