##// END OF EJS Templates
copies-rust: start recording overwrite as they happens...
copies-rust: start recording overwrite as they happens If a revision has information overwriting data from another revision, the overwriting revision is a descendant of the overwritten one. So we could warm the Oracle cache with such information to avoid potential future `is_ancestors` call. This provide us with a large speedup in the most expensive cases: Repo Case Source-Rev Dest-Rev # of revisions old time new time Difference Factor time per rev --------------------------------------------------------------------------------------------------------------------------------------------------------------- mozilla-try x00000_revs_x00000_added_x0000_copies 1b661134e2ca 1ae03d022d6d : 228985 revs, 41.113063 s, 36.001255 s, -5.111808 s, × 0.8757, 157 µs/rev mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 382065 revs, 27.891612 s, 14.340641 s, -13.550971 s, × 0.5142, 37 µs/rev Full comparison below: Repo Case Source-Rev Dest-Rev # of revisions old time new time Difference Factor time per rev --------------------------------------------------------------------------------------------------------------------------------------------------------------- mercurial x_revs_x_added_0_copies ad6b123de1c7 39cfcef4f463 : 1 revs, 0.000042 s, 0.000042 s, +0.000000 s, × 1.0000, 42 µs/rev mercurial x_revs_x_added_x_copies 2b1c78674230 0c1d10351869 : 6 revs, 0.000114 s, 0.000109 s, -0.000005 s, × 0.9561, 18 µs/rev mercurial x000_revs_x000_added_x_copies 81f8ff2a9bf2 dd3267698d84 : 1032 revs, 0.004934 s, 0.004953 s, +0.000019 s, × 1.0039, 4 µs/rev pypy x_revs_x_added_0_copies aed021ee8ae8 099ed31b181b : 9 revs, 0.000195 s, 0.000237 s, +0.000042 s, × 1.2154, 26 µs/rev pypy x_revs_x000_added_0_copies 4aa4e1f8e19a 359343b9ac0e : 1 revs, 0.000050 s, 0.000050 s, +0.000000 s, × 1.0000, 50 µs/rev pypy x_revs_x_added_x_copies ac52eb7bbbb0 72e022663155 : 7 revs, 0.000113 s, 0.000113 s, +0.000000 s, × 1.0000, 16 µs/rev pypy x_revs_x00_added_x_copies c3b14617fbd7 ace7255d9a26 : 1 revs, 0.6f1f4a s, 0.6f1f4a s, +0.000000 s, × 1.0000, 322 µs/rev pypy x_revs_x000_added_x000_copies df6f7a526b60 a83dc6a2d56f : 6 revs, 0.010788 s, 0.010702 s, -0.000086 s, × 0.9920, 1783 µs/rev pypy x000_revs_xx00_added_0_copies 89a76aede314 2f22446ff07e : 4785 revs, 0.050880 s, 0.050504 s, -0.000376 s, × 0.9926, 10 µs/rev pypy x000_revs_x000_added_x_copies 8a3b5bfd266e 2c68e87c3efe : 6780 revs, 0.081760 s, 0.080159 s, -0.001601 s, × 0.9804, 11 µs/rev pypy x000_revs_x000_added_x000_copies 89a76aede314 7b3dda341c84 : 5441 revs, 0.061382 s, 0.060058 s, -0.001324 s, × 0.9784, 11 µs/rev pypy x0000_revs_x_added_0_copies d1defd0dc478 c9cb1334cc78 : 43645 revs, 0.585802 s, 0.536950 s, -0.048852 s, × 0.9166, 12 µs/rev pypy x0000_revs_xx000_added_0_copies bf2c629d0071 4ffed77c095c : 2 revs, 0.012803 s, 0.012868 s, +0.000065 s, × 1.0051, 6434 µs/rev pypy x0000_revs_xx000_added_x000_copies 08ea3258278e d9fa043f30c0 : 11316 revs, 0.113558 s, 0.112806 s, -0.000752 s, × 0.9934, 9 µs/rev netbeans x_revs_x_added_0_copies fb0955ffcbcd a01e9239f9e7 : 2 revs, 0.000085 s, 0.000084 s, -0.000001 s, × 0.9882, 42 µs/rev netbeans x_revs_x000_added_0_copies 6f360122949f 20eb231cc7d0 : 2 revs, 0.000106 s, 0.000106 s, +0.000000 s, × 1.0000, 53 µs/rev netbeans x_revs_x_added_x_copies 1ada3faf6fb6 5a39d12eecf4 : 3 revs, 0.000175 s, 0.000174 s, -0.000001 s, × 0.9943, 58 µs/rev netbeans x_revs_x00_added_x_copies 35be93ba1e2c 9eec5e90c05f : 9 revs, 0.000721 s, 0.000726 s, +0.000005 s, × 1.0069, 80 µs/rev netbeans x000_revs_xx00_added_0_copies eac3045b4fdd 51d4ae7f1290 : 1421 revs, 0.010127 s, 0.010105 s, -0.000022 s, × 0.9978, 7 µs/rev netbeans x000_revs_x000_added_x_copies e2063d266acd 6081d72689dc : 1533 revs, 0.015616 s, 0.015748 s, +0.000132 s, × 1.0085, 10 µs/rev netbeans x000_revs_x000_added_x000_copies ff453e9fee32 411350406ec2 : 5750 revs, 0.061341 s, 0.060357 s, -0.000984 s, × 0.9840, 10 µs/rev netbeans x0000_revs_xx000_added_x000_copies 588c2d1ced70 1aad62e59ddd : 66949 revs, 0.542214 s, 0.499356 s, -0.042858 s, × 0.9210, 7 µs/rev mozilla-central x_revs_x_added_0_copies 3697f962bb7b 7015fcdd43a2 : 2 revs, 0.000089 s, 0.000092 s, +0.000003 s, × 1.0337, 46 µs/rev mozilla-central x_revs_x000_added_0_copies dd390860c6c9 40d0c5bed75d : 8 revs, 0.000279 s, 0.000279 s, +0.000000 s, × 1.0000, 34 µs/rev mozilla-central x_revs_x_added_x_copies 8d198483ae3b 14207ffc2b2f : 9 revs, 0.000184 s, 0.000186 s, +0.000002 s, × 1.0109, 20 µs/rev mozilla-central x_revs_x00_added_x_copies 98cbc58cc6bc 446a150332c3 : 7 revs, 0.000661 s, 0.000660 s, -0.000001 s, × 0.9985, 94 µs/rev mozilla-central x_revs_x000_added_x000_copies 3c684b4b8f68 0a5e72d1b479 : 3 revs, 0.003377 s, 0.003372 s, -0.000005 s, × 0.9985, 1124 µs/rev mozilla-central x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.070508 s, 0.070294 s, -0.000214 s, × 0.9970, 11715 µs/rev mozilla-central x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.006576 s, 0.006545 s, -0.000031 s, × 0.9953, 4 µs/rev mozilla-central x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.004809 s, 0.004998 s, +0.000189 s, × 1.0393, 121 µs/rev mozilla-central x000_revs_x000_added_x000_copies 7c97034feb78 4407bd0c6330 : 7839 revs, 0.064872 s, 0.063348 s, -0.001524 s, × 0.9765, 8 µs/rev mozilla-central x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.026142 s, 0.026154 s, +0.000012 s, × 1.0005, 42 µs/rev mozilla-central x0000_revs_xx000_added_x000_copies f78c615a656c 96a38b690156 : 30263 revs, 0.203956 s, 0.199063 s, -0.004893 s, × 0.9760, 6 µs/rev mozilla-central x00000_revs_x0000_added_x0000_copies 6832ae71433c 4c222a1d9a00 : 153721 revs, 1.763853 s, 1.277320 s, -0.486533 s, × 0.7242, 8 µs/rev mozilla-central x00000_revs_x00000_added_x000_copies 76caed42cf7c 1daa622bbe42 : 204976 revs, 2.609761 s, 1.698794 s, -0.910967 s, × 0.6509, 8 µs/rev mozilla-try x_revs_x_added_0_copies aaf6dde0deb8 9790f499805a : 2 revs, 0.000847 s, 0.000842 s, -0.000005 s, × 0.9941, 421 µs/rev mozilla-try x_revs_x000_added_0_copies d8d0222927b4 5bb8ce8c7450 : 2 revs, 0.000867 s, 0.000865 s, -0.000002 s, × 0.9977, 432 µs/rev mozilla-try x_revs_x_added_x_copies 092fcca11bdb 936255a0384a : 4 revs, 0.000161 s, 0.000160 s, -0.000001 s, × 0.9938, 40 µs/rev mozilla-try x_revs_x00_added_x_copies b53d2fadbdb5 017afae788ec : 2 revs, 0.001131 s, 0.001122 s, -0.000009 s, × 0.9920, 561 µs/rev mozilla-try x_revs_x000_added_x000_copies 20408ad61ce5 6f0ee96e21ad : 1 revs, 0.033114 s, 0.032743 s, -0.000371 s, × 0.9888, 32743 µs/rev mozilla-try x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 6 revs, 0.071092 s, 0.071529 s, +0.000437 s, × 1.0061, 11921 µs/rev mozilla-try x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 1593 revs, 0.006554 s, 0.006593 s, +0.000039 s, × 1.0060, 4 µs/rev mozilla-try x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 41 revs, 0.005160 s, 0.005311 s, +0.000151 s, × 1.0293, 129 µs/rev mozilla-try x000_revs_x000_added_x000_copies 1346fd0130e4 4c65cbdabc1f : 6657 revs, 0.065063 s, 0.063063 s, -0.002000 s, × 0.9693, 9 µs/rev mozilla-try x0000_revs_x_added_0_copies 63519bfd42ee a36a2a865d92 : 40314 revs, 0.297118 s, 0.312363 s, +0.015245 s, × 1.0513, 7 µs/rev mozilla-try x0000_revs_x_added_x_copies 9fe69ff0762d bcabf2a78927 : 38690 revs, 0.284002 s, 0.283106 s, -0.000896 s, × 0.9968, 7 µs/rev mozilla-try x0000_revs_xx000_added_x_copies 156f6e2674f2 4d0f2c178e66 : 8598 revs, 0.086311 s, 0.083817 s, -0.002494 s, × 0.9711, 9 µs/rev mozilla-try x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 615 revs, 0.026738 s, 0.026516 s, -0.000222 s, × 0.9917, 43 µs/rev mozilla-try x0000_revs_xx000_added_x000_copies 89294cd501d9 7ccb2fc7ccb5 : 97052 revs, 1.514270 s, 1.304865 s, -0.209405 s, × 0.8617, 13 µs/rev mozilla-try x0000_revs_x0000_added_x0000_copies e928c65095ed e951f4ad123a : 52031 revs, 0.735875 s, 0.681088 s, -0.054787 s, × 0.9255, 13 µs/rev mozilla-try x00000_revs_x_added_0_copies 6a320851d377 1ebb79acd503 : 363753 revs, 4.843329 s, 4.454320 s, -0.389009 s, × 0.9197, 12 µs/rev mozilla-try x00000_revs_x00000_added_0_copies dc8a3ca7010e d16fde900c9c : 34414 revs, 0.591752 s, 0.567913 s, -0.023839 s, × 0.9597, 16 µs/rev mozilla-try x00000_revs_x_added_x_copies 5173c4b6f97c 95d83ee7242d : 362229 revs, 4.760563 s, 4.547043 s, -0.213520 s, × 0.9551, 12 µs/rev mozilla-try x00000_revs_x000_added_x_copies 9126823d0e9c ca82787bb23c : 359344 revs, 4.751942 s, 4.378579 s, -0.373363 s, × 0.9214, 12 µs/rev mozilla-try x00000_revs_x0000_added_x0000_copies 8d3fafa80d4b eb884023b810 : 192665 revs, 2.605014 s, 1.703622 s, -0.901392 s, × 0.6540, 8 µs/rev mozilla-try x00000_revs_x00000_added_x0000_copies 1b661134e2ca 1ae03d022d6d : 228985 revs, 41.113063 s, 36.001255 s, -5.111808 s, × 0.8757, 157 µs/rev mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 382065 revs, 27.891612 s, 14.340641 s, -13.550971 s, × 0.5142, 37 µs/rev Differential Revision: https://phab.mercurial-scm.org/D9497

File last commit:

r42237:675775c3 default
r46770:fce2f20a default
Show More
compressionreader.c
818 lines | 17.7 KiB | text/x-c | CLexer
/**
* Copyright (c) 2017-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;
static void set_unsupported_operation(void) {
PyObject* iomod;
PyObject* exc;
iomod = PyImport_ImportModule("io");
if (NULL == iomod) {
return;
}
exc = PyObject_GetAttrString(iomod, "UnsupportedOperation");
if (NULL == exc) {
Py_DECREF(iomod);
return;
}
PyErr_SetNone(exc);
Py_DECREF(exc);
Py_DECREF(iomod);
}
static void reader_dealloc(ZstdCompressionReader* self) {
Py_XDECREF(self->compressor);
Py_XDECREF(self->reader);
if (self->buffer.buf) {
PyBuffer_Release(&self->buffer);
memset(&self->buffer, 0, sizeof(self->buffer));
}
PyObject_Del(self);
}
static ZstdCompressionReader* reader_enter(ZstdCompressionReader* self) {
if (self->entered) {
PyErr_SetString(PyExc_ValueError, "cannot __enter__ multiple times");
return NULL;
}
self->entered = 1;
Py_INCREF(self);
return self;
}
static PyObject* reader_exit(ZstdCompressionReader* self, PyObject* args) {
PyObject* exc_type;
PyObject* exc_value;
PyObject* exc_tb;
if (!PyArg_ParseTuple(args, "OOO:__exit__", &exc_type, &exc_value, &exc_tb)) {
return NULL;
}
self->entered = 0;
self->closed = 1;
/* Release resources associated with source. */
Py_CLEAR(self->reader);
if (self->buffer.buf) {
PyBuffer_Release(&self->buffer);
memset(&self->buffer, 0, sizeof(self->buffer));
}
Py_CLEAR(self->compressor);
Py_RETURN_FALSE;
}
static PyObject* reader_readable(ZstdCompressionReader* self) {
Py_RETURN_TRUE;
}
static PyObject* reader_writable(ZstdCompressionReader* self) {
Py_RETURN_FALSE;
}
static PyObject* reader_seekable(ZstdCompressionReader* self) {
Py_RETURN_FALSE;
}
static PyObject* reader_readline(PyObject* self, PyObject* args) {
set_unsupported_operation();
return NULL;
}
static PyObject* reader_readlines(PyObject* self, PyObject* args) {
set_unsupported_operation();
return NULL;
}
static PyObject* reader_write(PyObject* self, PyObject* args) {
PyErr_SetString(PyExc_OSError, "stream is not writable");
return NULL;
}
static PyObject* reader_writelines(PyObject* self, PyObject* args) {
PyErr_SetString(PyExc_OSError, "stream is not writable");
return NULL;
}
static PyObject* reader_isatty(PyObject* self) {
Py_RETURN_FALSE;
}
static PyObject* reader_flush(PyObject* self) {
Py_RETURN_NONE;
}
static PyObject* reader_close(ZstdCompressionReader* self) {
self->closed = 1;
Py_RETURN_NONE;
}
static PyObject* reader_tell(ZstdCompressionReader* self) {
/* TODO should this raise OSError since stream isn't seekable? */
return PyLong_FromUnsignedLongLong(self->bytesCompressed);
}
int read_compressor_input(ZstdCompressionReader* self) {
if (self->finishedInput) {
return 0;
}
if (self->input.pos != self->input.size) {
return 0;
}
if (self->reader) {
Py_buffer buffer;
assert(self->readResult == NULL);
self->readResult = PyObject_CallMethod(self->reader, "read",
"k", self->readSize);
if (NULL == self->readResult) {
return -1;
}
memset(&buffer, 0, sizeof(buffer));
if (0 != PyObject_GetBuffer(self->readResult, &buffer, PyBUF_CONTIG_RO)) {
return -1;
}
/* EOF */
if (0 == buffer.len) {
self->finishedInput = 1;
Py_CLEAR(self->readResult);
}
else {
self->input.src = buffer.buf;
self->input.size = buffer.len;
self->input.pos = 0;
}
PyBuffer_Release(&buffer);
}
else {
assert(self->buffer.buf);
self->input.src = self->buffer.buf;
self->input.size = self->buffer.len;
self->input.pos = 0;
}
return 1;
}
int compress_input(ZstdCompressionReader* self, ZSTD_outBuffer* output) {
size_t oldPos;
size_t zresult;
/* If we have data left over, consume it. */
if (self->input.pos < self->input.size) {
oldPos = output->pos;
Py_BEGIN_ALLOW_THREADS
zresult = ZSTD_compressStream2(self->compressor->cctx,
output, &self->input, ZSTD_e_continue);
Py_END_ALLOW_THREADS
self->bytesCompressed += output->pos - oldPos;
/* Input exhausted. Clear out state tracking. */
if (self->input.pos == self->input.size) {
memset(&self->input, 0, sizeof(self->input));
Py_CLEAR(self->readResult);
if (self->buffer.buf) {
self->finishedInput = 1;
}
}
if (ZSTD_isError(zresult)) {
PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult));
return -1;
}
}
if (output->pos && output->pos == output->size) {
return 1;
}
else {
return 0;
}
}
static PyObject* reader_read(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) {
static char* kwlist[] = {
"size",
NULL
};
Py_ssize_t size = -1;
PyObject* result = NULL;
char* resultBuffer;
Py_ssize_t resultSize;
size_t zresult;
size_t oldPos;
int readResult, compressResult;
if (self->closed) {
PyErr_SetString(PyExc_ValueError, "stream is closed");
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n", kwlist, &size)) {
return NULL;
}
if (size < -1) {
PyErr_SetString(PyExc_ValueError, "cannot read negative amounts less than -1");
return NULL;
}
if (size == -1) {
return PyObject_CallMethod((PyObject*)self, "readall", NULL);
}
if (self->finishedOutput || size == 0) {
return PyBytes_FromStringAndSize("", 0);
}
result = PyBytes_FromStringAndSize(NULL, size);
if (NULL == result) {
return NULL;
}
PyBytes_AsStringAndSize(result, &resultBuffer, &resultSize);
self->output.dst = resultBuffer;
self->output.size = resultSize;
self->output.pos = 0;
readinput:
compressResult = compress_input(self, &self->output);
if (-1 == compressResult) {
Py_XDECREF(result);
return NULL;
}
else if (0 == compressResult) {
/* There is room in the output. We fall through to below, which will
* either get more input for us or will attempt to end the stream.
*/
}
else if (1 == compressResult) {
memset(&self->output, 0, sizeof(self->output));
return result;
}
else {
assert(0);
}
readResult = read_compressor_input(self);
if (-1 == readResult) {
return NULL;
}
else if (0 == readResult) { }
else if (1 == readResult) { }
else {
assert(0);
}
if (self->input.size) {
goto readinput;
}
/* Else EOF */
oldPos = self->output.pos;
zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output,
&self->input, ZSTD_e_end);
self->bytesCompressed += self->output.pos - oldPos;
if (ZSTD_isError(zresult)) {
PyErr_Format(ZstdError, "error ending compression stream: %s",
ZSTD_getErrorName(zresult));
Py_XDECREF(result);
return NULL;
}
assert(self->output.pos);
if (0 == zresult) {
self->finishedOutput = 1;
}
if (safe_pybytes_resize(&result, self->output.pos)) {
Py_XDECREF(result);
return NULL;
}
memset(&self->output, 0, sizeof(self->output));
return result;
}
static PyObject* reader_read1(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) {
static char* kwlist[] = {
"size",
NULL
};
Py_ssize_t size = -1;
PyObject* result = NULL;
char* resultBuffer;
Py_ssize_t resultSize;
ZSTD_outBuffer output;
int compressResult;
size_t oldPos;
size_t zresult;
if (self->closed) {
PyErr_SetString(PyExc_ValueError, "stream is closed");
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n:read1", kwlist, &size)) {
return NULL;
}
if (size < -1) {
PyErr_SetString(PyExc_ValueError, "cannot read negative amounts less than -1");
return NULL;
}
if (self->finishedOutput || size == 0) {
return PyBytes_FromStringAndSize("", 0);
}
if (size == -1) {
size = ZSTD_CStreamOutSize();
}
result = PyBytes_FromStringAndSize(NULL, size);
if (NULL == result) {
return NULL;
}
PyBytes_AsStringAndSize(result, &resultBuffer, &resultSize);
output.dst = resultBuffer;
output.size = resultSize;
output.pos = 0;
/* read1() is supposed to use at most 1 read() from the underlying stream.
However, we can't satisfy this requirement with compression because
not every input will generate output. We /could/ flush the compressor,
but this may not be desirable. We allow multiple read() from the
underlying stream. But unlike read(), we return as soon as output data
is available.
*/
compressResult = compress_input(self, &output);
if (-1 == compressResult) {
Py_XDECREF(result);
return NULL;
}
else if (0 == compressResult || 1 == compressResult) { }
else {
assert(0);
}
if (output.pos) {
goto finally;
}
while (!self->finishedInput) {
int readResult = read_compressor_input(self);
if (-1 == readResult) {
Py_XDECREF(result);
return NULL;
}
else if (0 == readResult || 1 == readResult) { }
else {
assert(0);
}
compressResult = compress_input(self, &output);
if (-1 == compressResult) {
Py_XDECREF(result);
return NULL;
}
else if (0 == compressResult || 1 == compressResult) { }
else {
assert(0);
}
if (output.pos) {
goto finally;
}
}
/* EOF */
oldPos = output.pos;
zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
ZSTD_e_end);
self->bytesCompressed += output.pos - oldPos;
if (ZSTD_isError(zresult)) {
PyErr_Format(ZstdError, "error ending compression stream: %s",
ZSTD_getErrorName(zresult));
Py_XDECREF(result);
return NULL;
}
if (zresult == 0) {
self->finishedOutput = 1;
}
finally:
if (result) {
if (safe_pybytes_resize(&result, output.pos)) {
Py_XDECREF(result);
return NULL;
}
}
return result;
}
static PyObject* reader_readall(PyObject* self) {
PyObject* chunks = NULL;
PyObject* empty = NULL;
PyObject* result = NULL;
/* Our strategy is to collect chunks into a list then join all the
* chunks at the end. We could potentially use e.g. an io.BytesIO. But
* this feels simple enough to implement and avoids potentially expensive
* reallocations of large buffers.
*/
chunks = PyList_New(0);
if (NULL == chunks) {
return NULL;
}
while (1) {
PyObject* chunk = PyObject_CallMethod(self, "read", "i", 1048576);
if (NULL == chunk) {
Py_DECREF(chunks);
return NULL;
}
if (!PyBytes_Size(chunk)) {
Py_DECREF(chunk);
break;
}
if (PyList_Append(chunks, chunk)) {
Py_DECREF(chunk);
Py_DECREF(chunks);
return NULL;
}
Py_DECREF(chunk);
}
empty = PyBytes_FromStringAndSize("", 0);
if (NULL == empty) {
Py_DECREF(chunks);
return NULL;
}
result = PyObject_CallMethod(empty, "join", "O", chunks);
Py_DECREF(empty);
Py_DECREF(chunks);
return result;
}
static PyObject* reader_readinto(ZstdCompressionReader* self, PyObject* args) {
Py_buffer dest;
ZSTD_outBuffer output;
int readResult, compressResult;
PyObject* result = NULL;
size_t zresult;
size_t oldPos;
if (self->closed) {
PyErr_SetString(PyExc_ValueError, "stream is closed");
return NULL;
}
if (self->finishedOutput) {
return PyLong_FromLong(0);
}
if (!PyArg_ParseTuple(args, "w*:readinto", &dest)) {
return NULL;
}
if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) {
PyErr_SetString(PyExc_ValueError,
"destination buffer should be contiguous and have at most one dimension");
goto finally;
}
output.dst = dest.buf;
output.size = dest.len;
output.pos = 0;
compressResult = compress_input(self, &output);
if (-1 == compressResult) {
goto finally;
}
else if (0 == compressResult) { }
else if (1 == compressResult) {
result = PyLong_FromSize_t(output.pos);
goto finally;
}
else {
assert(0);
}
while (!self->finishedInput) {
readResult = read_compressor_input(self);
if (-1 == readResult) {
goto finally;
}
else if (0 == readResult || 1 == readResult) {}
else {
assert(0);
}
compressResult = compress_input(self, &output);
if (-1 == compressResult) {
goto finally;
}
else if (0 == compressResult) { }
else if (1 == compressResult) {
result = PyLong_FromSize_t(output.pos);
goto finally;
}
else {
assert(0);
}
}
/* EOF */
oldPos = output.pos;
zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
ZSTD_e_end);
self->bytesCompressed += self->output.pos - oldPos;
if (ZSTD_isError(zresult)) {
PyErr_Format(ZstdError, "error ending compression stream: %s",
ZSTD_getErrorName(zresult));
goto finally;
}
assert(output.pos);
if (0 == zresult) {
self->finishedOutput = 1;
}
result = PyLong_FromSize_t(output.pos);
finally:
PyBuffer_Release(&dest);
return result;
}
static PyObject* reader_readinto1(ZstdCompressionReader* self, PyObject* args) {
Py_buffer dest;
PyObject* result = NULL;
ZSTD_outBuffer output;
int compressResult;
size_t oldPos;
size_t zresult;
if (self->closed) {
PyErr_SetString(PyExc_ValueError, "stream is closed");
return NULL;
}
if (self->finishedOutput) {
return PyLong_FromLong(0);
}
if (!PyArg_ParseTuple(args, "w*:readinto1", &dest)) {
return NULL;
}
if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) {
PyErr_SetString(PyExc_ValueError,
"destination buffer should be contiguous and have at most one dimension");
goto finally;
}
output.dst = dest.buf;
output.size = dest.len;
output.pos = 0;
compressResult = compress_input(self, &output);
if (-1 == compressResult) {
goto finally;
}
else if (0 == compressResult || 1 == compressResult) { }
else {
assert(0);
}
if (output.pos) {
result = PyLong_FromSize_t(output.pos);
goto finally;
}
while (!self->finishedInput) {
int readResult = read_compressor_input(self);
if (-1 == readResult) {
goto finally;
}
else if (0 == readResult || 1 == readResult) { }
else {
assert(0);
}
compressResult = compress_input(self, &output);
if (-1 == compressResult) {
goto finally;
}
else if (0 == compressResult) { }
else if (1 == compressResult) {
result = PyLong_FromSize_t(output.pos);
goto finally;
}
else {
assert(0);
}
/* If we produced output and we're not done with input, emit
* that output now, as we've hit restrictions of read1().
*/
if (output.pos && !self->finishedInput) {
result = PyLong_FromSize_t(output.pos);
goto finally;
}
/* Otherwise we either have no output or we've exhausted the
* input. Either we try to get more input or we fall through
* to EOF below */
}
/* EOF */
oldPos = output.pos;
zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
ZSTD_e_end);
self->bytesCompressed += self->output.pos - oldPos;
if (ZSTD_isError(zresult)) {
PyErr_Format(ZstdError, "error ending compression stream: %s",
ZSTD_getErrorName(zresult));
goto finally;
}
assert(output.pos);
if (0 == zresult) {
self->finishedOutput = 1;
}
result = PyLong_FromSize_t(output.pos);
finally:
PyBuffer_Release(&dest);
return result;
}
static PyObject* reader_iter(PyObject* self) {
set_unsupported_operation();
return NULL;
}
static PyObject* reader_iternext(PyObject* self) {
set_unsupported_operation();
return NULL;
}
static PyMethodDef reader_methods[] = {
{ "__enter__", (PyCFunction)reader_enter, METH_NOARGS,
PyDoc_STR("Enter a compression context") },
{ "__exit__", (PyCFunction)reader_exit, METH_VARARGS,
PyDoc_STR("Exit a compression context") },
{ "close", (PyCFunction)reader_close, METH_NOARGS,
PyDoc_STR("Close the stream so it cannot perform any more operations") },
{ "flush", (PyCFunction)reader_flush, METH_NOARGS, PyDoc_STR("no-ops") },
{ "isatty", (PyCFunction)reader_isatty, METH_NOARGS, PyDoc_STR("Returns False") },
{ "readable", (PyCFunction)reader_readable, METH_NOARGS,
PyDoc_STR("Returns True") },
{ "read", (PyCFunction)reader_read, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data") },
{ "read1", (PyCFunction)reader_read1, METH_VARARGS | METH_KEYWORDS, NULL },
{ "readall", (PyCFunction)reader_readall, METH_NOARGS, PyDoc_STR("Not implemented") },
{ "readinto", (PyCFunction)reader_readinto, METH_VARARGS, NULL },
{ "readinto1", (PyCFunction)reader_readinto1, METH_VARARGS, NULL },
{ "readline", (PyCFunction)reader_readline, METH_VARARGS, PyDoc_STR("Not implemented") },
{ "readlines", (PyCFunction)reader_readlines, METH_VARARGS, PyDoc_STR("Not implemented") },
{ "seekable", (PyCFunction)reader_seekable, METH_NOARGS,
PyDoc_STR("Returns False") },
{ "tell", (PyCFunction)reader_tell, METH_NOARGS,
PyDoc_STR("Returns current number of bytes compressed") },
{ "writable", (PyCFunction)reader_writable, METH_NOARGS,
PyDoc_STR("Returns False") },
{ "write", reader_write, METH_VARARGS, PyDoc_STR("Raises OSError") },
{ "writelines", reader_writelines, METH_VARARGS, PyDoc_STR("Not implemented") },
{ NULL, NULL }
};
static PyMemberDef reader_members[] = {
{ "closed", T_BOOL, offsetof(ZstdCompressionReader, closed),
READONLY, "whether stream is closed" },
{ NULL }
};
PyTypeObject ZstdCompressionReaderType = {
PyVarObject_HEAD_INIT(NULL, 0)
"zstd.ZstdCompressionReader", /* tp_name */
sizeof(ZstdCompressionReader), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)reader_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, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
reader_iter, /* tp_iter */
reader_iternext, /* tp_iternext */
reader_methods, /* tp_methods */
reader_members, /* tp_members */
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 compressionreader_module_init(PyObject* mod) {
/* TODO make reader a sub-class of io.RawIOBase */
Py_TYPE(&ZstdCompressionReaderType) = &PyType_Type;
if (PyType_Ready(&ZstdCompressionReaderType) < 0) {
return;
}
}