contrib/python-zstandard/c-ext/compressionreader.c
changeset 42070 675775c33ab6
parent 40121 73fef626dae3
child 46374 e92ca942ddca
equal deleted inserted replaced
42069:668eff08387f 42070:675775c33ab6
   126 static PyObject* reader_tell(ZstdCompressionReader* self) {
   126 static PyObject* reader_tell(ZstdCompressionReader* self) {
   127 	/* TODO should this raise OSError since stream isn't seekable? */
   127 	/* TODO should this raise OSError since stream isn't seekable? */
   128 	return PyLong_FromUnsignedLongLong(self->bytesCompressed);
   128 	return PyLong_FromUnsignedLongLong(self->bytesCompressed);
   129 }
   129 }
   130 
   130 
       
   131 int read_compressor_input(ZstdCompressionReader* self) {
       
   132 	if (self->finishedInput) {
       
   133 		return 0;
       
   134 	}
       
   135 
       
   136 	if (self->input.pos != self->input.size) {
       
   137 		return 0;
       
   138 	}
       
   139 
       
   140 	if (self->reader) {
       
   141 		Py_buffer buffer;
       
   142 
       
   143 		assert(self->readResult == NULL);
       
   144 
       
   145 		self->readResult = PyObject_CallMethod(self->reader, "read",
       
   146 		    "k", self->readSize);
       
   147 
       
   148 		if (NULL == self->readResult) {
       
   149 			return -1;
       
   150 		}
       
   151 
       
   152 		memset(&buffer, 0, sizeof(buffer));
       
   153 
       
   154 		if (0 != PyObject_GetBuffer(self->readResult, &buffer, PyBUF_CONTIG_RO)) {
       
   155 			return -1;
       
   156 		}
       
   157 
       
   158 		/* EOF */
       
   159 		if (0 == buffer.len) {
       
   160 			self->finishedInput = 1;
       
   161 			Py_CLEAR(self->readResult);
       
   162 		}
       
   163 		else {
       
   164 			self->input.src = buffer.buf;
       
   165 			self->input.size = buffer.len;
       
   166 			self->input.pos = 0;
       
   167 		}
       
   168 
       
   169 		PyBuffer_Release(&buffer);
       
   170 	}
       
   171 	else {
       
   172 		assert(self->buffer.buf);
       
   173 
       
   174 		self->input.src = self->buffer.buf;
       
   175 		self->input.size = self->buffer.len;
       
   176 		self->input.pos = 0;
       
   177 	}
       
   178 
       
   179 	return 1;
       
   180 }
       
   181 
       
   182 int compress_input(ZstdCompressionReader* self, ZSTD_outBuffer* output) {
       
   183 	size_t oldPos;
       
   184 	size_t zresult;
       
   185 
       
   186 	/* If we have data left over, consume it. */
       
   187 	if (self->input.pos < self->input.size) {
       
   188 		oldPos = output->pos;
       
   189 
       
   190 		Py_BEGIN_ALLOW_THREADS
       
   191 		zresult = ZSTD_compressStream2(self->compressor->cctx,
       
   192 		    output, &self->input, ZSTD_e_continue);
       
   193 		Py_END_ALLOW_THREADS
       
   194 
       
   195 		self->bytesCompressed += output->pos - oldPos;
       
   196 
       
   197 		/* Input exhausted. Clear out state tracking. */
       
   198 		if (self->input.pos == self->input.size) {
       
   199 			memset(&self->input, 0, sizeof(self->input));
       
   200 			Py_CLEAR(self->readResult);
       
   201 
       
   202 			if (self->buffer.buf) {
       
   203 				self->finishedInput = 1;
       
   204 			}
       
   205 		}
       
   206 
       
   207 		if (ZSTD_isError(zresult)) {
       
   208 			PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult));
       
   209 			return -1;
       
   210 		}
       
   211 	}
       
   212 
       
   213     if (output->pos && output->pos == output->size) {
       
   214         return 1;
       
   215     }
       
   216     else {
       
   217         return 0;
       
   218     }
       
   219 }
       
   220 
   131 static PyObject* reader_read(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) {
   221 static PyObject* reader_read(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) {
   132 	static char* kwlist[] = {
   222 	static char* kwlist[] = {
   133 		"size",
   223 		"size",
   134 		NULL
   224 		NULL
   135 	};
   225 	};
   138 	PyObject* result = NULL;
   228 	PyObject* result = NULL;
   139 	char* resultBuffer;
   229 	char* resultBuffer;
   140 	Py_ssize_t resultSize;
   230 	Py_ssize_t resultSize;
   141 	size_t zresult;
   231 	size_t zresult;
   142 	size_t oldPos;
   232 	size_t oldPos;
       
   233 	int readResult, compressResult;
   143 
   234 
   144 	if (self->closed) {
   235 	if (self->closed) {
   145 		PyErr_SetString(PyExc_ValueError, "stream is closed");
   236 		PyErr_SetString(PyExc_ValueError, "stream is closed");
   146 		return NULL;
   237 		return NULL;
   147 	}
   238 	}
   148 
   239 
   149 	if (self->finishedOutput) {
   240 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n", kwlist, &size)) {
       
   241 		return NULL;
       
   242 	}
       
   243 
       
   244 	if (size < -1) {
       
   245 		PyErr_SetString(PyExc_ValueError, "cannot read negative amounts less than -1");
       
   246 		return NULL;
       
   247 	}
       
   248 
       
   249 	if (size == -1) {
       
   250 		return PyObject_CallMethod((PyObject*)self, "readall", NULL);
       
   251 	}
       
   252 
       
   253 	if (self->finishedOutput || size == 0) {
   150 		return PyBytes_FromStringAndSize("", 0);
   254 		return PyBytes_FromStringAndSize("", 0);
   151 	}
       
   152 
       
   153 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "n", kwlist, &size)) {
       
   154 		return NULL;
       
   155 	}
       
   156 
       
   157 	if (size < 1) {
       
   158 		PyErr_SetString(PyExc_ValueError, "cannot read negative or size 0 amounts");
       
   159 		return NULL;
       
   160 	}
   255 	}
   161 
   256 
   162 	result = PyBytes_FromStringAndSize(NULL, size);
   257 	result = PyBytes_FromStringAndSize(NULL, size);
   163 	if (NULL == result) {
   258 	if (NULL == result) {
   164 		return NULL;
   259 		return NULL;
   170 	self->output.size = resultSize;
   265 	self->output.size = resultSize;
   171 	self->output.pos = 0;
   266 	self->output.pos = 0;
   172 
   267 
   173 readinput:
   268 readinput:
   174 
   269 
   175 	/* If we have data left over, consume it. */
   270     compressResult = compress_input(self, &self->output);
   176 	if (self->input.pos < self->input.size) {
   271 
   177 		oldPos = self->output.pos;
   272 	if (-1 == compressResult) {
   178 
   273 		Py_XDECREF(result);
   179 		Py_BEGIN_ALLOW_THREADS
   274 		return NULL;
   180 		zresult = ZSTD_compress_generic(self->compressor->cctx,
   275 	}
   181 			&self->output, &self->input, ZSTD_e_continue);
   276 	else if (0 == compressResult) {
   182 
   277 		/* There is room in the output. We fall through to below, which will
   183 		Py_END_ALLOW_THREADS
   278 		 * either get more input for us or will attempt to end the stream.
   184 
   279 		 */
   185 		self->bytesCompressed += self->output.pos - oldPos;
   280 	}
   186 
   281 	else if (1 == compressResult) {
   187 		/* Input exhausted. Clear out state tracking. */
   282 		memset(&self->output, 0, sizeof(self->output));
   188 		if (self->input.pos == self->input.size) {
   283 		return result;
   189 			memset(&self->input, 0, sizeof(self->input));
   284 	}
   190 			Py_CLEAR(self->readResult);
   285 	else {
   191 
   286 		assert(0);
   192 			if (self->buffer.buf) {
   287 	}
   193 				self->finishedInput = 1;
   288 
   194 			}
   289 	readResult = read_compressor_input(self);
   195 		}
   290 
   196 
   291 	if (-1 == readResult) {
   197 		if (ZSTD_isError(zresult)) {
   292 		return NULL;
   198 			PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult));
   293 	}
   199 			return NULL;
   294 	else if (0 == readResult) { }
   200 		}
   295 	else if (1 == readResult) { }
   201 
   296 	else {
   202 		if (self->output.pos) {
   297 		assert(0);
   203 			/* If no more room in output, emit it. */
       
   204 			if (self->output.pos == self->output.size) {
       
   205 				memset(&self->output, 0, sizeof(self->output));
       
   206 				return result;
       
   207 			}
       
   208 
       
   209 			/*
       
   210 			 * There is room in the output. We fall through to below, which will either
       
   211 			 * get more input for us or will attempt to end the stream.
       
   212 			 */
       
   213 		}
       
   214 
       
   215 		/* Fall through to gather more input. */
       
   216 	}
       
   217 
       
   218 	if (!self->finishedInput) {
       
   219 		if (self->reader) {
       
   220 			Py_buffer buffer;
       
   221 
       
   222 			assert(self->readResult == NULL);
       
   223 			self->readResult = PyObject_CallMethod(self->reader, "read",
       
   224 				"k", self->readSize);
       
   225 			if (self->readResult == NULL) {
       
   226 				return NULL;
       
   227 			}
       
   228 
       
   229 			memset(&buffer, 0, sizeof(buffer));
       
   230 
       
   231 			if (0 != PyObject_GetBuffer(self->readResult, &buffer, PyBUF_CONTIG_RO)) {
       
   232 				return NULL;
       
   233 			}
       
   234 
       
   235 			/* EOF */
       
   236 			if (0 == buffer.len) {
       
   237 				self->finishedInput = 1;
       
   238 				Py_CLEAR(self->readResult);
       
   239 			}
       
   240 			else {
       
   241 				self->input.src = buffer.buf;
       
   242 				self->input.size = buffer.len;
       
   243 				self->input.pos = 0;
       
   244 			}
       
   245 
       
   246 			PyBuffer_Release(&buffer);
       
   247 		}
       
   248 		else {
       
   249 			assert(self->buffer.buf);
       
   250 
       
   251 			self->input.src = self->buffer.buf;
       
   252 			self->input.size = self->buffer.len;
       
   253 			self->input.pos = 0;
       
   254 		}
       
   255 	}
   298 	}
   256 
   299 
   257 	if (self->input.size) {
   300 	if (self->input.size) {
   258 		goto readinput;
   301 		goto readinput;
   259 	}
   302 	}
   260 
   303 
   261 	/* Else EOF */
   304 	/* Else EOF */
   262 	oldPos = self->output.pos;
   305 	oldPos = self->output.pos;
   263 
   306 
   264 	zresult = ZSTD_compress_generic(self->compressor->cctx, &self->output,
   307 	zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output,
   265 		&self->input, ZSTD_e_end);
   308 		&self->input, ZSTD_e_end);
   266 
   309 
   267 	self->bytesCompressed += self->output.pos - oldPos;
   310 	self->bytesCompressed += self->output.pos - oldPos;
   268 
   311 
   269 	if (ZSTD_isError(zresult)) {
   312 	if (ZSTD_isError(zresult)) {
   270 		PyErr_Format(ZstdError, "error ending compression stream: %s",
   313 		PyErr_Format(ZstdError, "error ending compression stream: %s",
   271 			ZSTD_getErrorName(zresult));
   314 			ZSTD_getErrorName(zresult));
       
   315 		Py_XDECREF(result);
   272 		return NULL;
   316 		return NULL;
   273 	}
   317 	}
   274 
   318 
   275 	assert(self->output.pos);
   319 	assert(self->output.pos);
   276 
   320 
   286 	memset(&self->output, 0, sizeof(self->output));
   330 	memset(&self->output, 0, sizeof(self->output));
   287 
   331 
   288 	return result;
   332 	return result;
   289 }
   333 }
   290 
   334 
       
   335 static PyObject* reader_read1(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) {
       
   336 	static char* kwlist[] = {
       
   337 		"size",
       
   338 		NULL
       
   339 	};
       
   340 
       
   341 	Py_ssize_t size = -1;
       
   342 	PyObject* result = NULL;
       
   343 	char* resultBuffer;
       
   344 	Py_ssize_t resultSize;
       
   345 	ZSTD_outBuffer output;
       
   346 	int compressResult;
       
   347 	size_t oldPos;
       
   348 	size_t zresult;
       
   349 
       
   350 	if (self->closed) {
       
   351 		PyErr_SetString(PyExc_ValueError, "stream is closed");
       
   352 		return NULL;
       
   353 	}
       
   354 
       
   355 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n:read1", kwlist, &size)) {
       
   356 		return NULL;
       
   357 	}
       
   358 
       
   359 	if (size < -1) {
       
   360 		PyErr_SetString(PyExc_ValueError, "cannot read negative amounts less than -1");
       
   361 		return NULL;
       
   362 	}
       
   363 
       
   364 	if (self->finishedOutput || size == 0) {
       
   365 		return PyBytes_FromStringAndSize("", 0);
       
   366 	}
       
   367 
       
   368 	if (size == -1) {
       
   369 		size = ZSTD_CStreamOutSize();
       
   370 	}
       
   371 
       
   372 	result = PyBytes_FromStringAndSize(NULL, size);
       
   373 	if (NULL == result) {
       
   374 		return NULL;
       
   375 	}
       
   376 
       
   377 	PyBytes_AsStringAndSize(result, &resultBuffer, &resultSize);
       
   378 
       
   379 	output.dst = resultBuffer;
       
   380 	output.size = resultSize;
       
   381 	output.pos = 0;
       
   382 
       
   383 	/* read1() is supposed to use at most 1 read() from the underlying stream.
       
   384 	   However, we can't satisfy this requirement with compression because
       
   385 	   not every input will generate output. We /could/ flush the compressor,
       
   386 	   but this may not be desirable. We allow multiple read() from the
       
   387 	   underlying stream. But unlike read(), we return as soon as output data
       
   388 	   is available.
       
   389 	*/
       
   390 
       
   391 	compressResult = compress_input(self, &output);
       
   392 
       
   393 	if (-1 == compressResult) {
       
   394 		Py_XDECREF(result);
       
   395 		return NULL;
       
   396 	}
       
   397 	else if (0 == compressResult || 1 == compressResult) { }
       
   398 	else {
       
   399 		assert(0);
       
   400 	}
       
   401 
       
   402 	if (output.pos) {
       
   403 		goto finally;
       
   404 	}
       
   405 
       
   406 	while (!self->finishedInput) {
       
   407 		int readResult = read_compressor_input(self);
       
   408 
       
   409 		if (-1 == readResult) {
       
   410 			Py_XDECREF(result);
       
   411 			return NULL;
       
   412 		}
       
   413 		else if (0 == readResult || 1 == readResult) { }
       
   414 		else {
       
   415 			assert(0);
       
   416 		}
       
   417 
       
   418 		compressResult = compress_input(self, &output);
       
   419 
       
   420 		if (-1 == compressResult) {
       
   421 			Py_XDECREF(result);
       
   422 			return NULL;
       
   423 		}
       
   424 		else if (0 == compressResult || 1 == compressResult) { }
       
   425 		else {
       
   426 			assert(0);
       
   427 		}
       
   428 
       
   429 		if (output.pos) {
       
   430 			goto finally;
       
   431 		}
       
   432 	}
       
   433 
       
   434 	/* EOF */
       
   435 	oldPos = output.pos;
       
   436 
       
   437 	zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
       
   438         ZSTD_e_end);
       
   439 
       
   440 	self->bytesCompressed += output.pos - oldPos;
       
   441 
       
   442 	if (ZSTD_isError(zresult)) {
       
   443 		PyErr_Format(ZstdError, "error ending compression stream: %s",
       
   444 		    ZSTD_getErrorName(zresult));
       
   445 		Py_XDECREF(result);
       
   446 		return NULL;
       
   447 	}
       
   448 
       
   449 	if (zresult == 0) {
       
   450 		self->finishedOutput = 1;
       
   451 	}
       
   452 
       
   453 finally:
       
   454 	if (result) {
       
   455 		if (safe_pybytes_resize(&result, output.pos)) {
       
   456 			Py_XDECREF(result);
       
   457 			return NULL;
       
   458 		}
       
   459 	}
       
   460 
       
   461 	return result;
       
   462 }
       
   463 
   291 static PyObject* reader_readall(PyObject* self) {
   464 static PyObject* reader_readall(PyObject* self) {
   292 	PyErr_SetNone(PyExc_NotImplementedError);
   465 	PyObject* chunks = NULL;
   293 	return NULL;
   466 	PyObject* empty = NULL;
       
   467 	PyObject* result = NULL;
       
   468 
       
   469 	/* Our strategy is to collect chunks into a list then join all the
       
   470 	 * chunks at the end. We could potentially use e.g. an io.BytesIO. But
       
   471 	 * this feels simple enough to implement and avoids potentially expensive
       
   472 	 * reallocations of large buffers.
       
   473 	 */
       
   474 	chunks = PyList_New(0);
       
   475 	if (NULL == chunks) {
       
   476 		return NULL;
       
   477 	}
       
   478 
       
   479 	while (1) {
       
   480 		PyObject* chunk = PyObject_CallMethod(self, "read", "i", 1048576);
       
   481 		if (NULL == chunk) {
       
   482 			Py_DECREF(chunks);
       
   483 			return NULL;
       
   484 		}
       
   485 
       
   486 		if (!PyBytes_Size(chunk)) {
       
   487 			Py_DECREF(chunk);
       
   488 			break;
       
   489 		}
       
   490 
       
   491 		if (PyList_Append(chunks, chunk)) {
       
   492 			Py_DECREF(chunk);
       
   493 			Py_DECREF(chunks);
       
   494 			return NULL;
       
   495 		}
       
   496 
       
   497 		Py_DECREF(chunk);
       
   498 	}
       
   499 
       
   500 	empty = PyBytes_FromStringAndSize("", 0);
       
   501 	if (NULL == empty) {
       
   502 		Py_DECREF(chunks);
       
   503 		return NULL;
       
   504 	}
       
   505 
       
   506 	result = PyObject_CallMethod(empty, "join", "O", chunks);
       
   507 
       
   508 	Py_DECREF(empty);
       
   509 	Py_DECREF(chunks);
       
   510 
       
   511 	return result;
       
   512 }
       
   513 
       
   514 static PyObject* reader_readinto(ZstdCompressionReader* self, PyObject* args) {
       
   515 	Py_buffer dest;
       
   516 	ZSTD_outBuffer output;
       
   517 	int readResult, compressResult;
       
   518 	PyObject* result = NULL;
       
   519 	size_t zresult;
       
   520 	size_t oldPos;
       
   521 
       
   522 	if (self->closed) {
       
   523 		PyErr_SetString(PyExc_ValueError, "stream is closed");
       
   524 		return NULL;
       
   525 	}
       
   526 
       
   527 	if (self->finishedOutput) {
       
   528 		return PyLong_FromLong(0);
       
   529 	}
       
   530 
       
   531 	if (!PyArg_ParseTuple(args, "w*:readinto", &dest)) {
       
   532 		return NULL;
       
   533 	}
       
   534 
       
   535 	if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) {
       
   536 		PyErr_SetString(PyExc_ValueError,
       
   537 		    "destination buffer should be contiguous and have at most one dimension");
       
   538 		goto finally;
       
   539 	}
       
   540 
       
   541 	output.dst = dest.buf;
       
   542 	output.size = dest.len;
       
   543 	output.pos = 0;
       
   544 
       
   545 	compressResult = compress_input(self, &output);
       
   546 
       
   547 	if (-1 == compressResult) {
       
   548 		goto finally;
       
   549 	}
       
   550 	else if (0 == compressResult) {	}
       
   551 	else if (1 == compressResult) {
       
   552 		result = PyLong_FromSize_t(output.pos);
       
   553 		goto finally;
       
   554 	}
       
   555 	else {
       
   556 		assert(0);
       
   557 	}
       
   558 
       
   559 	while (!self->finishedInput) {
       
   560 		readResult = read_compressor_input(self);
       
   561 
       
   562 		if (-1 == readResult) {
       
   563 			goto finally;
       
   564 		}
       
   565 		else if (0 == readResult || 1 == readResult) {}
       
   566 		else {
       
   567 			assert(0);
       
   568 		}
       
   569 
       
   570 		compressResult = compress_input(self, &output);
       
   571 
       
   572 		if (-1 == compressResult) {
       
   573 			goto finally;
       
   574 		}
       
   575 		else if (0 == compressResult) { }
       
   576 		else if (1 == compressResult) {
       
   577 			result = PyLong_FromSize_t(output.pos);
       
   578 			goto finally;
       
   579 		}
       
   580 		else {
       
   581 			assert(0);
       
   582 		}
       
   583 	}
       
   584 
       
   585 	/* EOF */
       
   586 	oldPos = output.pos;
       
   587 
       
   588 	zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
       
   589 	    ZSTD_e_end);
       
   590 
       
   591 	self->bytesCompressed += self->output.pos - oldPos;
       
   592 
       
   593 	if (ZSTD_isError(zresult)) {
       
   594 		PyErr_Format(ZstdError, "error ending compression stream: %s",
       
   595 		    ZSTD_getErrorName(zresult));
       
   596 		goto finally;
       
   597 	}
       
   598 
       
   599 	assert(output.pos);
       
   600 
       
   601 	if (0 == zresult) {
       
   602 		self->finishedOutput = 1;
       
   603 	}
       
   604 
       
   605 	result = PyLong_FromSize_t(output.pos);
       
   606 
       
   607 finally:
       
   608 	PyBuffer_Release(&dest);
       
   609 
       
   610 	return result;
       
   611 }
       
   612 
       
   613 static PyObject* reader_readinto1(ZstdCompressionReader* self, PyObject* args) {
       
   614 	Py_buffer dest;
       
   615 	PyObject* result = NULL;
       
   616 	ZSTD_outBuffer output;
       
   617 	int compressResult;
       
   618 	size_t oldPos;
       
   619 	size_t zresult;
       
   620 
       
   621 	if (self->closed) {
       
   622 		PyErr_SetString(PyExc_ValueError, "stream is closed");
       
   623 		return NULL;
       
   624 	}
       
   625 
       
   626 	if (self->finishedOutput) {
       
   627 		return PyLong_FromLong(0);
       
   628 	}
       
   629 
       
   630 	if (!PyArg_ParseTuple(args, "w*:readinto1", &dest)) {
       
   631 		return NULL;
       
   632 	}
       
   633 
       
   634 	if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) {
       
   635 		PyErr_SetString(PyExc_ValueError,
       
   636 		    "destination buffer should be contiguous and have at most one dimension");
       
   637 		goto finally;
       
   638 	}
       
   639 
       
   640 	output.dst = dest.buf;
       
   641 	output.size = dest.len;
       
   642 	output.pos = 0;
       
   643 
       
   644 	compressResult = compress_input(self, &output);
       
   645 
       
   646 	if (-1 == compressResult) {
       
   647 		goto finally;
       
   648 	}
       
   649 	else if (0 == compressResult || 1 == compressResult) { }
       
   650 	else {
       
   651 		assert(0);
       
   652 	}
       
   653 
       
   654 	if (output.pos) {
       
   655 		result = PyLong_FromSize_t(output.pos);
       
   656 		goto finally;
       
   657 	}
       
   658 
       
   659 	while (!self->finishedInput) {
       
   660 		int readResult = read_compressor_input(self);
       
   661 
       
   662 		if (-1 == readResult) {
       
   663 			goto finally;
       
   664 		}
       
   665 		else if (0 == readResult || 1 == readResult) { }
       
   666 		else {
       
   667 			assert(0);
       
   668 		}
       
   669 
       
   670 		compressResult = compress_input(self, &output);
       
   671 
       
   672 		if (-1 == compressResult) {
       
   673 			goto finally;
       
   674 		}
       
   675 		else if (0 == compressResult) { }
       
   676 		else if (1 == compressResult) {
       
   677 			result = PyLong_FromSize_t(output.pos);
       
   678 			goto finally;
       
   679 		}
       
   680 		else {
       
   681 			assert(0);
       
   682 		}
       
   683 
       
   684 		/* If we produced output and we're not done with input, emit
       
   685 		 * that output now, as we've hit restrictions of read1().
       
   686 		 */
       
   687 		if (output.pos && !self->finishedInput) {
       
   688 			result = PyLong_FromSize_t(output.pos);
       
   689 			goto finally;
       
   690 		}
       
   691 
       
   692 		/* Otherwise we either have no output or we've exhausted the
       
   693 		 * input. Either we try to get more input or we fall through
       
   694 		 * to EOF below */
       
   695 	}
       
   696 
       
   697 	/* EOF */
       
   698 	oldPos = output.pos;
       
   699 
       
   700 	zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
       
   701 	    ZSTD_e_end);
       
   702 
       
   703 	self->bytesCompressed += self->output.pos - oldPos;
       
   704 
       
   705 	if (ZSTD_isError(zresult)) {
       
   706 		PyErr_Format(ZstdError, "error ending compression stream: %s",
       
   707 		    ZSTD_getErrorName(zresult));
       
   708 		goto finally;
       
   709 	}
       
   710 
       
   711 	assert(output.pos);
       
   712 
       
   713 	if (0 == zresult) {
       
   714 		self->finishedOutput = 1;
       
   715 	}
       
   716 
       
   717 	result = PyLong_FromSize_t(output.pos);
       
   718 
       
   719 finally:
       
   720 	PyBuffer_Release(&dest);
       
   721 
       
   722 	return result;
   294 }
   723 }
   295 
   724 
   296 static PyObject* reader_iter(PyObject* self) {
   725 static PyObject* reader_iter(PyObject* self) {
   297 	set_unsupported_operation();
   726 	set_unsupported_operation();
   298 	return NULL;
   727 	return NULL;
   313 	{ "flush", (PyCFunction)reader_flush, METH_NOARGS, PyDoc_STR("no-ops") },
   742 	{ "flush", (PyCFunction)reader_flush, METH_NOARGS, PyDoc_STR("no-ops") },
   314 	{ "isatty", (PyCFunction)reader_isatty, METH_NOARGS, PyDoc_STR("Returns False") },
   743 	{ "isatty", (PyCFunction)reader_isatty, METH_NOARGS, PyDoc_STR("Returns False") },
   315 	{ "readable", (PyCFunction)reader_readable, METH_NOARGS,
   744 	{ "readable", (PyCFunction)reader_readable, METH_NOARGS,
   316 	PyDoc_STR("Returns True") },
   745 	PyDoc_STR("Returns True") },
   317 	{ "read", (PyCFunction)reader_read, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data") },
   746 	{ "read", (PyCFunction)reader_read, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data") },
       
   747 	{ "read1", (PyCFunction)reader_read1, METH_VARARGS | METH_KEYWORDS, NULL },
   318 	{ "readall", (PyCFunction)reader_readall, METH_NOARGS, PyDoc_STR("Not implemented") },
   748 	{ "readall", (PyCFunction)reader_readall, METH_NOARGS, PyDoc_STR("Not implemented") },
       
   749 	{ "readinto", (PyCFunction)reader_readinto, METH_VARARGS, NULL },
       
   750 	{ "readinto1", (PyCFunction)reader_readinto1, METH_VARARGS, NULL },
   319 	{ "readline", (PyCFunction)reader_readline, METH_VARARGS, PyDoc_STR("Not implemented") },
   751 	{ "readline", (PyCFunction)reader_readline, METH_VARARGS, PyDoc_STR("Not implemented") },
   320 	{ "readlines", (PyCFunction)reader_readlines, METH_VARARGS, PyDoc_STR("Not implemented") },
   752 	{ "readlines", (PyCFunction)reader_readlines, METH_VARARGS, PyDoc_STR("Not implemented") },
   321 	{ "seekable", (PyCFunction)reader_seekable, METH_NOARGS,
   753 	{ "seekable", (PyCFunction)reader_seekable, METH_NOARGS,
   322 	PyDoc_STR("Returns False") },
   754 	PyDoc_STR("Returns False") },
   323 	{ "tell", (PyCFunction)reader_tell, METH_NOARGS,
   755 	{ "tell", (PyCFunction)reader_tell, METH_NOARGS,