zPython的标准绑定

zstandard的Python项目详细描述


此项目提供用于与 Zstandard压缩库。空调分机 提供CFFI接口。

该项目的主要目标是为 通过pythonic接口在不牺牲 表演。这意味着暴露了大部分特征和灵活性。 在不牺牲python提供的可用性或安全性的情况下。

这个项目的规范库位于由 作者。为了方便起见,该存储库经常与 https://github.com/indygreg/python-zstandard

ci-statusWindows build status

要求

此扩展设计为与Python2.7、3.4、3.5、3.6和3.7一起运行 在通用平台(Linux、Windows和OS X)上。在pypy(pypy2和pypy3)上,我们支持6.0.0及更高版本。 x86和x86_64在windows上经过了很好的测试。只有x86_64在linux和macos上经过了良好的测试。

安装

此包上载到位于https://pypi.python.org/pypi/zstandard的pypi。 因此,要安装此软件包:

$ pip install zstandard

一些平台上提供了二进制轮。如果你需要的话 从源发行版安装,您只需要一个工作的C 编译器和python开发头/库。在许多Linux上 发行版,您可以安装python-devpython-devel 包以提供这些依赖项。

软件包也上传到anaconda cloud https://anaconda.org/indygreg/zstandard。有关如何安装的信息,请参见该url 这个包有conda

性能

zstandard是一种高度可调的压缩算法。在其默认设置中 (压缩级别3),压缩和解压缩速度将更快,并且 在大多数数据集上比zlib有更好的压缩比。调谐时 对于速度,它接近LZ4的速度和比率。当调整为压缩时 比率,接近lzma比率和压缩速度,但减压 速度快得多。有关更多信息,请参阅zStandard官方文档。

zstandard和这个库支持多线程压缩。有一个 使用多线程压缩大输入的机制。

这个库的性能通常与zStandard非常相似 C API可以交付。这个库中的开销是由于一般的python开销造成的 而且不能通过anyzstandard python绑定轻松避免。这个图书馆 公开多个api以执行压缩和解压缩,以便调用方 可以选择适合他们需要的api。与压缩对比 python标准库中的模块(如zlib),它只提供有限的 执行操作的机制。API灵活性意味着消费者可以 选择使用有助于零拷贝或最小化python对象的api 创建和垃圾收集开销。

此库能够实现远超过1 GB/s的单线程吞吐量。 精确的数字,自己测量。源代码存储库有一个bench.py 可用于测量事物的脚本。

API

要与zstandard接口,只需导入zstandard模块:

import zstandard

将模块命名为 简洁性:

import zstandard as zstd

此模块尝试导入和使用C扩展名或CFFI 实施。在支持c扩展的python平台上(比如 cpython),如果无法导入c扩展,则会引发一个重要错误。 在已知不支持c扩展的python平台(如pypy)上,它只支持 尝试导入cffi实现并引发importerror,如果 做不到。在其他平台上,它首先尝试导入c扩展 如果失败了,就退回给cffi,如果cffi失败了,就会引起严重的恐慌。

若要更改模块导入行为,请a PYTHON_ZSTANDARD_IMPORT_POLICY 可以设置环境变量。接受以下值:

默认值
上述行为。
CFFI U回退
始终尝试导入c扩展,然后返回到cffi,如果 失败。
cext
仅尝试导入C扩展名。
cffi
仅尝试导入CFFI实现。

此外,zstandard模块导出一个backend属性 包含正在使用的后端的字符串名称。它将是一个 (分别用于c extensioncffi)。

zstandard模块公开的类型、函数和属性 记录在下面的章节中。

注意

本节中的文档引用了各种zstd 概念和功能。源存储库包含 docs/concepts.rst文件更详细地解释了这些。

zstd压缩机

ZstdCompressor类提供了一个用于执行 压缩操作。每个实例本质上都是 ZSTD_CCtx来自C API。

每个实例都与控制压缩的参数相关联 行为。这些参数来自以下命名参数(全部可选):

水平
整数压缩级别。有效值介于1和22之间。
dict_数据
要使用的压缩字典。

注意:当使用字典数据时,compress()被称为multiple 次,从整数派生的ZstdCompressionParameters。 压缩level和第一个压缩数据的大小将被重用 所有后续操作。如果源数据是 大小差别很大。

压缩参数
aZstdCompressionParameters实例定义压缩设置。
写入校验和
是否应使用压缩数据写入4字节校验和。 默认为false。如果为true,则解压缩程序可以验证 数据与原始输入数据匹配。
写入内容大小
是否将未压缩数据的大小写入 压缩数据的头。默认为true。数据只会是 如果压缩器知道输入数据的大小,则写入。这是 流媒体压缩通常不是这样。
写入dict id
是否将字典id写入压缩数据。 默认为true。只有当字典 正在使用中。
线程
启用并设置用于多线程压缩的线程数 操作。默认为0,这意味着使用单线程压缩。 负值将解析为系统中逻辑CPU的数量。 有关多线程压缩的更多信息,请阅读下面的内容。仅此论点 控制对单个 数据。产生多个线程以处理多个 数据有自己的threads参数。

compression_paramslevelwrite_checksum互斥, write_content_sizewrite_dict_idthreads

除非另有说明,否则假设没有两个方法ZstdCompressor 可以同时从多个python线程调用实例。在其他方面 换句话说,除非另有说明,否则假设实例不是线程安全的。

实用方法

frame_progression()返回一个包含字节数的3元组 由当前压缩操作摄取、消耗和产生的。

memory_size()获取底层zstd的内存利用率 压缩上下文(字节):

cctx = zstd.ZstdCompressor()
memory = cctx.memory_size()

简单的API

compress(data)压缩并返回数据作为一次性操作。:

cctx = zstd.ZstdCompressor()
compressed = cctx.compress(b'data to compress')

参数data可以是实现缓冲区协议的任何对象。

流读取器API

stream_reader(source)可用于获取符合 io.RawIOBase接口,用于将压缩输出读取为流:

with open(path, 'rb') as fh:
    cctx = zstd.ZstdCompressor()
    reader = cctx.stream_reader(fh)
    while True:
        chunk = reader.read(16384)
        if not chunk:
            break

        # Do something with compressed chunk.

实例也可以用作上下文管理器:

with open(path, 'rb') as fh:
    with cctx.stream_reader(fh) as reader:
        while True:
            chunk = reader.read(16384)
            if not chunk:
                break

            # Do something with compressed chunk.

当上下文管理器退出或^ {TT34 }被调用时,THE流关闭, 底层资源被释放,未来针对压缩的操作 流将失败。

stream_reader()source参数可以是具有 read(size)方法或实现缓冲区协议的任何对象。

stream_reader()接受指定输入大小的size参数 溪流是。这是用来调整压缩参数的 根据源大小定制。

with open(path, 'rb') as fh:
    cctx = zstd.ZstdCompressor()
    with cctx.stream_reader(fh, size=os.stat(path).st_size) as reader:
        ...

如果source是流,则可以指定read()请求的大小 应该通过read_size参数到达该流。它默认为 zstandard.COMPRESSION_RECOMMENDED_INPUT_SIZE。:

with open(path, 'rb') as fh:
    cctx = zstd.ZstdCompressor()
    # Will perform fh.read(8192) when obtaining data to feed into the
    # compressor.
    with cctx.stream_reader(fh, read_size=8192) as reader:
        ...

stream_reader()返回的流既不可写也不可查找 (即使底层源代码是可查看的)。readline()readlines()未实现,因为它们对 压缩数据。tell()返回压缩字节数 到目前为止。

流式输入API

stream_writer(fh)允许您将{em1}$stream数据放入压缩器。

返回的实例实现了io.RawIOBase接口。只有方法 涉及写作的东西会有用的。

stream_writer()的参数必须有一个write(data)方法。作为 压缩数据可用,write()将使用压缩 数据作为它的论据。许多常见的python类型实现write(),包括 打开文件句柄和io.BytesIO

write(data)方法用于将数据馈送到压缩器中。

可以调用flush([flush_mode=FLUSH_BLOCK])方法来逐出 数据保持在压缩机的内部状态,进入输出对象。这个 可能导致对输出对象的0个或多个write()调用。这种方法 接受可选的flush_mode参数来控制刷新行为。 它的值可以是任何FLUSH_*常量。

write()flush()都返回写入 对象的write()。在许多情况下,小的投入积累不够 导致写入和write()的数据将返回0

调用close()将流标记为已关闭和后续I/O 操作将提高ValueError(根据 io.RawIOBase)。close()还将调用基础上的close()。 流,如果存在这样的方法。

通常用法如下:

cctx = zstd.ZstdCompressor(level=10)
compressor = cctx.stream_writer(fh)

compressor.write(b'chunk 0\n')
compressor.write(b'chunk 1\n')
compressor.flush()
# Receiver will be able to decode ``chunk 0\nchunk 1\n`` at this point.
# Receiver is also expecting more data in the zstd *frame*.

compressor.write(b'chunk 2\n')
compressor.flush(zstd.FLUSH_FRAME)
# Receiver will be able to decode ``chunk 0\nchunk 1\nchunk 2``.
# Receiver is expecting no more data, as the zstd frame is closed.
# Any future calls to ``write()`` at this point will construct a new
# zstd frame.

实例可以用作上下文管理器。退出上下文管理器是 相当于调用close(),相当于调用 flush(zstd.FLUSH_FRAME)

cctx = zstd.ZstdCompressor(level=10)
with cctx.stream_writer(fh) as compressor:
    compressor.write(b'chunk 0')
    compressor.write(b'chunk 1')
    ...

重要

如果不调用flush(FLUSH_FRAME),则发出的数据不构成 完整的zstdframe和此数据的使用者可能会抱怨格式错误 输入。建议使用实例作为上下文管理器,以确保 frames已正确完成。

如果输入到这个流压缩程序的数据的大小是已知的, 您可以在压缩开始之前声明它:

cctx = zstd.ZstdCompressor()
with cctx.stream_writer(fh, size=data_len) as compressor:
    compressor.write(chunk0)
    compressor.write(chunk1)
    ...

声明源数据的大小允许压缩参数 调整一下。如果使用write_content_size,也会导致 写入输出数据的帧头的内容大小。

可以指定到目的地的write()块的大小:

cctx = zstd.ZstdCompressor()
with cctx.stream_writer(fh, write_size=32768) as compressor:
    ...

要查看流式压缩程序使用了多少内存:

cctx = zstd.ZstdCompressor()
with cctx.stream_writer(fh) as compressor:
    ...
    byte_size = compressor.memory_size()

到目前为止写入的字节总数通过tell()

cctx = zstd.ZstdCompressor()
with cctx.stream_writer(fh) as compressor:
    ...
    total_written = compressor.tell()

stream_writer()接受一个write_return_read布尔参数来控制 write()的返回值。当False(默认值)时,write()返回 字节数是write()``en to the underlying object. When ``Truewrite()返回从输入中读取的字节数 随后写信给压缩机。True正确的行为 对于由io.RawIOBase接口指定的write(),将成为 将来版本中的默认值。

流式输出API

read_to_iter(reader)提供了从 压缩程序作为数据块的迭代器。

cctx = zstd.ZstdCompressor()
for chunk in cctx.read_to_iter(fh):
     # Do something with emitted data.

read_to_iter()接受obj具有read(size)方法或 符合缓冲协议。

未压缩的数据通过调用^{tt37}从源获取$ 或者直接从对象中获取数据片段(在 正在使用缓冲区协议)。返回的迭代器由块组成 压缩数据。

如果通过read()从源读取,则read()将被调用,直到 它引发或返回空字节(b'')。它完全适用于 要传递的字节数少于read(size)所请求的字节数的源。

stream_writer()read_to_iter()也接受size参数 声明输入流的大小:

cctx = zstd.ZstdCompressor()
for chunk in cctx.read_to_iter(fh, size=some_int):
    pass

您还可以控制源和 输出块的理想大小:

cctx = zstd.ZstdCompressor()
for chunk in cctx.read_to_iter(fh, read_size=16384, write_size=8192):
    pass

stream_writer()不同,read_to_iter()不提供直接控制 超过送入压缩机的大块。相反,块大小将 无论从中读取的对象传递什么。这些通常是 大小一致。

流复制api

copy_stream(ifh, ofh)可用于在 压缩。:

cctx = zstd.ZstdCompressor()
cctx.copy_stream(ifh, ofh)

例如,假设要压缩文件:

cctx = zstd.ZstdCompressor()
with open(input_path, 'rb') as ifh, open(output_path, 'wb') as ofh:
    cctx.copy_stream(ifh, ofh)

还可以声明源流的大小:

cctx = zstd.ZstdCompressor()
cctx.copy_stream(ifh, ofh, size=len_of_input)

您还可以指定read()和^{tt52}的块有多大$ 从流到流:

cctx = zstd.ZstdCompressor()
cctx.copy_stream(ifh, ofh, read_size=32768, write_size=16384)

流复制器返回读取和写入的2元组字节:

cctx = zstd.ZstdCompressor()
read_count, write_count = cctx.copy_stream(ifh, ofh)

压缩机API

compressobj()返回一个公开compress(data)flush()方法。每个返回压缩数据或空字节。

compressobj()的目的是提供与api兼容的接口 使用zlib.compressobjbz2.BZ2Compressor等,这允许调用者 使用相同的api时交换不同的压缩程序对象。

flush()接受指示如何结束流的可选参数。 zstd.COMPRESSOBJ_FLUSH_FINISH(默认值)结束压缩流。 执行此类刷新后,compress()flush()可以 不再被呼叫。必须调用这种类型的flush,才能结束 压缩上下文。如果不调用,返回的数据可能不完整。

flush()zstd.COMPRESSOBJ_FLUSH_BLOCK参数将刷新 zstd块。这种类型的刷新可以执行多次。下一个 调用compress()将开始一个新的zstd块。

下面是该api的使用方法:

cctx = zstd.ZstdCompressor()
cobj = cctx.compressobj()
data = cobj.compress(b'raw input 0')
data = cobj.compress(b'raw input 1')
data = cobj.flush()

或刷新块:

cctx.zstd.ZstdCompressor()
cobj = cctx.compressobj()
data = cobj.compress(b'chunk in first block')
data = cobj.flush(zstd.COMPRESSOBJ_FLUSH_BLOCK)
data = cobj.compress(b'chunk in second block')
data = cobj.flush()

为了获得最佳性能结果,请将输入块保持在256kb以下。这样可以避免 对大型输出对象的额外分配。

可以声明将输入的数据的输入大小 压缩机:

cctx = zstd.ZstdCompressor()
cobj = cctx.compressobj(size=6)
data = cobj.compress(b'foobar')
data = cobj.flush()

分块器API

chunker(size=None, chunk_size=COMPRESSION_RECOMMENDED_OUTPUT_SIZE)返回 一种对象,可用于迭代地将数据块馈送到压缩器中 并产生大小一致的输出块。

chunker()返回的对象公开以下方法:

compress(data)
将新的输入数据输入压缩机。
flush()
刷新压缩机中当前的所有数据。
finish()
表示输入数据结束。在此之后不能压缩新数据 方法被调用。

compress()flush()finish()都返回 bytes保存压缩数据的实例。迭代器可能为空。呼叫者 在执行 对对象的另一个操作。

compress()发出的所有块的长度都是chunk_size

flush()finish()可能返回小于 chunk_size

下面是API的使用方法:

cctx = zstd.ZstdCompressor()
chunker = cctx.chunker(chunk_size=32768)

with open(path, 'rb') as fh:
    while True:
        in_chunk = fh.read(32768)
        if not in_chunk:
            break

        for out_chunk in chunker.compress(in_chunk):
            # Do something with output chunk of size 32768.

    for out_chunk in chunker.finish():
        # Do something with output chunks that finalize the zstd frame.

chunker()api通常是compressobj()更好的替代品。

compressobj()将在可用时发出输出数据。这将导致 stream不同大小的输出块。输出块的一致性 带chunker()的大小是mor适用于许多用途,如发送 将数据压缩到套接字。

compressobj()还可以执行额外的内存重新分配,以便 动态调整输出块的大小。自chunker()输出以来 块都是相同大小的(除了刷新或最终块),有 更少的内存分配开销。

批量压缩API

(实验性的。cffi绑定中尚不支持。)

multi_compress_to_buffer(data, [threads=0])执行多个 作为单个操作的输入。

要压缩的数据可以作为BufferWithSegmentsCollection传递,a BufferWithSegments,或包含类字节对象的列表。每个元素 将使用配置的参数单独压缩容器 在ZstdCompressor实例上。

参数threads控制要用于压缩的线程数。这个 默认值是0,这意味着使用单个线程。负值使用 计算机中的逻辑CPU数。

函数返回BufferWithSegmentsCollection。此类型表示 n个离散内存分配,每个分配包含一个或多个压缩帧。

输出数据被写入共享内存缓冲区。这意味着不像 常规python对象,对集合中any对象的引用 使共享缓冲区保持活动状态,从而使支持它的内存保持活动状态。这个可以有 对进程内存使用的不良影响。

这个函数的api和行为是实验性的,可能会改变。 已知缺陷包括:

  • 如果要求使用多个线程,它总是会生成这么多线程, 即使输入太小,无法使用它们。它会自动降低 额外线程只会增加开销时的线程计数。
  • 缓冲区分配策略是固定的。有空间让它充满活力, 甚至可能允许每个输入都有一个输出缓冲区,从而促进了变化 返回一个列表而不受共享内存的不利影响 缓冲器。

zstd减压器

ZstdDecompressor类提供用于执行 减压。它实际上是ZSTD_DCtx类型的包装 C API。

每个实例都与控制解压缩的参数相关联。这些 来自以下命名参数(全部可选):

dict_数据
要使用的压缩字典。
最大窗口大小
为中的解压缩操作设置窗口大小的上限 千字节。此设置可用于防止大内存分配 对于使用大压缩窗口的输入。
格式
设置解码器的数据格式。默认情况下,这是 zstd.FORMAT_ZSTD1。它可以设置为zstd.FORMAT_ZSTD1_MAGICLESSto 允许解码没有4字节幻数头的帧。不是所有的减压 API支持此模式。

这个类的接口与ZstdCompressor(按设计)非常相似。

除非另有说明,否则假设不存在^{tt143}的两个方法$ 可以同时从多个python线程调用实例。在其他方面 换句话说,除非另有说明,否则假设实例不是线程安全的。

实用方法

memory_size()获取底层zstd解压上下文的大小, 以字节为单位。:

dctx = zstd.ZstdDecompressor()
size = dctx.memory_size()

简单的API

decompress(data)可用于解压缩整个压缩zstd 一次操作一帧。

dctx = zstd.ZstdDecompressor()
decompressed = dctx.decompress(data)

默认情况下,decompress(data)将只对使用内容写入的数据起作用 在其头中编码的大小(这是 ZstdCompressor().compress(),但对于流式压缩可能不是这样)。如果 显示没有嵌入内容大小的压缩数据,zstd.ZstdError将 被抚养成人。

如果压缩数据中没有嵌入内容大小, 可以通过指定^{tt154}来尝试解压缩$ 参数.:

dctx = zstd.ZstdDecompressor()
uncompressed = dctx.decompress(data, max_output_size=1048576)

理想情况下,max_output_size将与解压缩的输出相同 大小。

如果max_output_size太小为了保存解压后的数据, zstd.ZstdError将被筹集。

如果max_output_size大于解压缩的数据,则 输出缓冲区的大小将调整为仅使用所需的空间。

请注意,请求的max_output_size的分配将是 每次调用方法时执行。设置为非常大的值可能 会给内存分配器带来很多工作,并可能导致 ^{tt160}如果分配失败,将引发$。

重要

如果解压缩数据的确切大小未知(未显式传入 而不是存储在zstandard框架中),出于性能原因 鼓励使用流式api。

流读取器API

stream_reader(source)可用于获取符合 io.RawIOBase接口,用于将解压缩输出读取为流:

with open(path, 'rb') as fh:
    dctx = zstd.ZstdDecompressor()
    reader = dctx.stream_reader(fh)
    while True:
        chunk = reader.read(16384)
         if not chunk:
             break

         # Do something with decompressed chunk.

流还可以用作上下文管理器:

with open(path, 'rb') as fh:
    dctx = zstd.ZstdDecompressor()
    with dctx.stream_reader(fh) as reader:
        ...

当用作上下文管理器时,流将关闭,并且 当上下文管理器退出时释放资源。未来的行动 这条河会断流的。

stream_reader()source参数可以是具有 read(size)方法或实现缓冲区协议的任何对象。

如果source是流,则可以指定read()请求的大小 应该通过read_size参数到达该流。它默认为 zstandard.DECOMPRESSION_RECOMMENDED_INPUT_SIZE。:

with open(path, 'rb') as fh:
    dctx = zstd.ZstdDecompressor()
    # Will perform fh.read(8192) when obtaining data for the decompressor.
    with dctx.stream_reader(fh, read_size=8192) as reader:
        ...

stream_reader()返回的流不可写。

stream_reader()返回的流是部分可查看的。 绝对和相对位置(SEEK_SETSEEK_CUR)向前 允许当前位置的。当前读取后的偏移量 不允许相对于流结尾的位置和偏移,并且 如果尝试,将引发ValueError

tell()返回到目前为止读取的解压缩字节数。

并非所有的I/O方法都已实现。值得注意的是缺少对 readline()readlines()和逐行迭代支持。这是 因为流操作的是二进制数据,而不是文本数据。如果你想的话 将解压缩的输出转换为文本,可以链接io.TextIOWrapper 到流:

with open(path, 'rb') as fh:
    dctx = zstd.ZstdDecompressor()
    stream_reader = dctx.stream_reader(fh)
    text_stream = io.TextIOWrapper(stream_reader, encoding='utf-8')

    for line in text_stream:
        ...

stream_reader()read_across_frames参数控制 遇到zstdframe结尾时的读取操作行为。 当False(默认值)结束时,读取将在 遇到zstdframe。当True时,读取可能 返回跨越多个zstdframes的数据。

流式输入API

stream_writer(fh)允许您将{em1}$stream数据放入解压缩程序。

返回的实例实现了io.RawIOBase接口。只有方法 涉及写作的东西会有用的。

stream_writer()的参数通常是一个还实现 io.RawIOBase。但是任何带有write(data)方法的对象都可以工作。许多 常见的python类型符合这个接口,包括打开的文件句柄 和io.BytesIO

行为类似于ZstdCompressor.stream_writer():压缩数据 通过调用write(data)发送到解压缩程序并解压缩 输出通过调用其^{tt51}写入底层流$ 方法:

dctx = zstd.ZstdDecompressor()
decompressor = dctx.stream_writer(fh)

decompressor.write(compressed_data)
...

调用write()将返回写入输出的字节数 反对。不是所有的输入都会导致写入字节,所以返回值 可能是0的。

stream_writer()压缩器一样,实例可以用作上下文 经理。然而,上下文管理器没有添加额外的特殊行为,并提供 被利用几乎没有好处。

调用close()将流标记为已关闭和后续的I/O操作 将提高ValueError(根据io.RawIOBase的记录行为)。 close()还将调用基础流上的close(),如果 方法存在。

可以指定到目的地的write()块的大小:

dctx = zstd.ZstdDecompressor()
with dctx.stream_writer(fh, write_size=16384) as decompressor:
    pass

您可以看到解压缩程序使用了多少内存:

dctx = zstd.ZstdDecompressor()
with dctx.stream_writer(fh) as decompressor:
    byte_size = decompressor.memory_size()

stream_writer()接受一个write_return_read布尔参数来控制 write()的返回值。当False(默认值)``,write() 返回write()``en to the underlying stream. When ``True的字节数,write()返回从输入读取的字节数。 True是由 io.RawIOBase接口,并将在将来的版本中成为默认值。

流式输出API

read_to_iter(fh)提供了一种机制,可以将解压缩的数据从 压缩源作为数据块的迭代器。

dctx = zstd.ZstdDecompressor()
for chunk in dctx.read_to_iter(fh):
    # Do something with original data.

read_to_iter()接受具有read(size)方法的对象,该方法将 返回压缩字节或符合缓冲区协议的对象 可以将其数据公开为字节的连续范围。

read_to_iter()返回一个迭代器,其元素是 解压缩的数据。

可以指定从源请求的read()的大小:

dctx = zstd.ZstdDecompressor()
for chunk in dctx.read_to_iter(fh, read_size=16384):
    pass

也可以跳过输入数据中的前导字节:

dctx = zstd.ZstdDecompressor()
for chunk in dctx.read_to_iter(fh, skip_bytes=1):
    pass

提示

如果源数据包含额外的 header数据。传统上,您需要创建一个切片或 memoryview要解压缩的数据。这将创造 头顶上。将偏移量传递到这个api中更有效。

类似于ZstdCompressor.read_to_iter(),迭代器的使用者 控制何时解压缩数据。如果迭代器没有被使用, 减压暂停。

当向read_to_iter()传递符合缓冲区协议的对象时, 这种行为可能类似于简单的解压 使用API。但是,当解压大小未知时,此api可以工作。 此外,如果输入大量的数据,解压器就会成批工作 而不是执行单个操作。

流复制api

copy_stream(ifh, ofh)可用于在 正在执行解压缩。:

dctx = zstd.ZstdDecompressor()
dctx.copy_stream(ifh, ofh)

例如,将一个文件解压缩到另一个文件:

dctx = zstd.ZstdDecompressor()
with open(input_path, 'rb') as ifh, open(output_path, 'wb') as ofh:
    dctx.copy_stream(ifh, ofh)

从流到流的块大小为read()write()。 可以指定:

dctx = zstd.ZstdDecompressor()
dctx.copy_stream(ifh, ofh, read_size=8192, write_size=16384)

减压器API

decompressobj()返回一个公开^{tt150}的对象$ 方法。压缩的数据块被输入到decompress(data)和 返回未压缩的输出(或空字节)。后续输出 需要连接调用才能重新组合完整的解压缩字节 顺序。

decompressobj()的目的是提供与api兼容的接口 使用zlib.decompressobjbz2.BZ2Decompressor。这允许呼叫者 在使用相同的api时交换不同的解压缩程序对象。

每个对象都是一次性的:一旦输入帧被解码,decompress() 不能再叫了。

下面是该api的使用方法:

dctx = zstd.ZstdDecompressor()
dobj = dctx.decompressobj()
data = dobj.decompress(compressed_chunk_0)
data = dobj.decompress(compressed_chunk_1)

默认情况下,对decompress()的调用以大小块的形式写入输出数据 DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE。这些块被连接起来 在被传回给来电者之前。可以定义 这些临时块通过将write_size传递给decompressobj()

dctx = zstd.ZstdDecompressor()
dobj = dctx.decompressobj(write_size=1048576)

注意

因为对decompress()的调用可能需要执行多个 内存(重新)分配,此流解压缩API不是 与其他api一样高效。

为了与标准库api兼容,实例公开了 flush([length=None])方法。这个方法没有操作,没有意义 副作用,可以随时打电话。

批量解压缩API

(实验性的。cffi绑定中尚不支持。)

multi_decompress_to_buffer()执行多个 将帧作为单个操作并返回BufferWithSegmentsCollection 包含所有输入的解压缩数据。

压缩帧可以作为BufferWithSegments传递给函数, 一个BufferWithSegmentsCollection,或包含 符合缓冲协议。为了获得最佳性能,请通过 BufferWithSegmentsCollection或aBufferWithSegments,作为 将对该类型执行最小的输入验证。如果卡利天然气来源 python(与c相反),构造其中一个实例可以添加 开销取消列表验证的性能开销 输入。

dctx = zstd.ZstdDecompressor()
results = dctx.multi_decompress_to_buffer([b'...', b'...'])

每个帧的解压缩大小必须是可发现的。它可以是 嵌入到zstd框架(write_content_size=True参数中 ZstdCompressor)或通过decompressed_sizes参数传入。

decompressed_sizes参数是符合缓冲区的对象 在机器的 定义每个帧的解压缩大小的本机格式。如果这个论点 通过后,它可以避免扫描每个帧的解压缩大小。 在某些情况下,此帧扫描会增加明显的开销。

frames = [...]
sizes = struct.pack('=QQQQ', len0, len1, len2, len3)

dctx = zstd.ZstdDecompressor()
results = dctx.multi_decompress_to_buffer(frames, decompressed_sizes=sizes)

参数threads控制要执行的线程数 减压手术。默认值(0)或值1表示 用一根线。负值使用 机器。

注意

可以通过以下方法将mmap.mmap()实例传递到此函数 用BufferWithSegments实例包装它(它将定义 内存映射区域内的帧偏移)。

这个函数在逻辑上等价于执行dctx.decompress() 在每个输入帧上并返回结果。

此函数用于快速执行多个帧的解压缩 尽可能少的开销。因为减压是 作为单个操作执行,因为解压缩的输出存储在 单个缓冲区、额外内存分配、python对象和python函数 避免打电话。这非常适合打电话的人预先知道 它们需要访问多个帧的数据,例如delta chains是 被利用。

目前,实现总是在请求时生成多个线程, 即使工作量很小。在未来,它会更聪明 当 要做的工作很小。

前缀字典链解压

decompress_content_dict_chain(frames)对 使用链式prefix字典压缩生成的zstd帧。如此 通过压缩离散输入生成帧列表,其中 非初始输入用prefix字典压缩,字典由 上一个输入的内容。

例如,假设您有以下输入:

inputs = [b'input 1', b'input 2', b'input 3']

zstd帧链包括:

  1. b'input 1'在独立/离散模式下压缩
  2. b'input 2'使用b'input 1'作为前缀压缩字典
  3. b'input 3'使用b'input 2'作为前缀字典进行压缩

每个zstd帧必须写入内容大小。

以下python代码可用于生成前缀字典链

def make_chain(inputs):
    frames = []

    # First frame is compressed in standalone/discrete mode.
    zctx = zstd.ZstdCompressor()
    frames.append(zctx.compress(inputs[0]))

    # Subsequent frames use the previous fulltext as a prefix dictionary
    for i, raw in enumerate(inputs[1:]):
        dict_data = zstd.ZstdCompressionDict(
            inputs[i], dict_type=zstd.DICT_TYPE_RAWCONTENT)
        zctx = zstd.ZstdCompressor(dict_data=dict_data)
        frames.append(zctx.compress(raw))

    return frames

decompress_content_dict_chain()返回上一个的未压缩数据 输入链中的元素。

注意

可以实现前缀字典链解压 在其他api之上。但是,这个功能可能更快- 特别是对于长输入链-因为它避免了实例化的开销 以及在c和python之间传递中间对象。

多线程压缩

ZstdCompressor接受控制数字的threads参数 用于压缩的线程。其工作方式是将输入拆分 将每个段送入工作池进行压缩。一次 段被压缩,它被刷新/附加到输出。

注意

这些线程是在c层创建的,不是python线程。所以他们 在gil之外工作。因此,CPU可以使多个核心饱和 来自python。

多段的段大小-线程压缩是从窗口大小中选择的 压缩机。这是从 ZstdCompressionParameters实例。默认情况下,段大小为1+MB 范围。

如果请求多线程压缩,并且输入小于 配置的段大小,将只使用一个压缩线程。如果 输入小于段大小乘以线程池大小,或 如果数据不能足够快地传送到压缩机,则不是所有要求的 压缩机螺纹可能同时激活。

与非多线程压缩相比,多线程压缩具有 更高的每次操作开销。这包括额外的内存操作, 线程创建、锁获取等。

由于使用n压缩的多线程压缩的性质 states,多线程压缩的输出可能会更大 而不是非多线程压缩。差别通常很小。但是 有一个CPU/墙时间与大小的权衡,可能需要调查。

多线程压缩的输出不需要任何特殊处理 在减压侧。到解压器,用 线程压缩器看起来与多线程生成的数据相同 压缩机,不需要任何特殊处理或额外资源 要求。

词典的创建和管理

压缩字典用ZstdCompressionDict类型表示。

实例可以由字节构造:

dict_data = zstd.ZstdCompressionDict(data)

可以从any数据构造字典。如果数据没有 以magic头开始,它将被视为prefix字典。 prefix字典允许压缩操作引用原始数据 在字典里。

可以强制使用prefix字典,也可以要求 字典标题:

dict_data = zstd.ZstdCompressionDict(data,
dict_type=zstd.DICT_TYPE_RAWCONTENT)
dict_data = zstd.ZstdCompressionDict(data,
dict_type=zstd.DICT_TYPE_FULLDICT)

通过调用len()

dict_data = zstd.train_dictionary(size, samples)
dict_size = len(dict_data)  # will not be larger than ``size``

一旦有了字典,就可以将它传递给执行 压缩和解压缩:

dict_data = zstd.train_dictionary(131072, samples)

cctx = zstd.ZstdCompressor(dict_data=dict_data)
for source_data in input_data:
    compressed = cctx.compress(source_data)
    # Do something with compressed data.

dctx = zstd.ZstdDecompressor(dict_data=dict_data)
for compressed_data in input_data:
    buffer = io.BytesIO()
    with dctx.stream_writer(buffer) as decompressor:
        decompressor.write(compressed_data)
    # Do something with raw data in ``buffer``.

字典具有唯一的整数ID。您可以通过:

dict_id = zstd.dictionary_id(dict_data)

您可以获取dict中的原始数据(对于持久化和构造 aZstdCompressionDict稍后)通过as_bytes()

dict_data = zstd.train_dictionary(size, samples)
raw_data = dict_data.as_bytes()

默认情况下,当ZstdCompressionDict附加到 ZstdCompressor,每个ZstdCompressor执行准备 供使用的词典。如果只有一个压缩操作 或者ZstdCompressor正被重用用于多个操作。 但是如果字典中使用了多个ZstdCompressor实例, 这会增加开销。

可以precompute字典,这样就可以很容易地使用它 通过多个ZstdCompressor实例:

d = zstd.ZstdCompressionDict(data)

# Precompute for compression level 3.
d.precompute_compress(level=3)

# Precompute with specific compression parameters.
params = zstd.ZstdCompressionParameters(...)
d.precompute_compress(compression_params=params)

注意

当预计算字典时,压缩参数用于 预计算字典覆盖一些压缩参数 指定为ZstdCompressor.__init__

培训词典

除非使用prefix字典,否则字典数据由training生成。 现有数据:

dict_data = zstd.train_dictionary(size, samples)

这将获取目标字典大小和字节实例列表,并创建和 返回ZstdCompressionDict

字典训练机制称为cover。更多的细节是 本文提供了相对lempel-ziv的有效构造 词典(作者:廖、佩特里、莫法特、威尔斯)。

cover算法使用参数k` and ``d。这些是 段大小dmer大小。返回的词典 此函数创建的实例具有kd属性 包含这些参数的值。如果a ZstdCompressionDict 由原始字节数据(co只有内容的字典)。 kd属性将是0

覆盖算法的段和dmer大小参数可以是 手动指定或train_dictionary()可以尝试多个值 选择最好的一个,其中best表示最小的压缩数据大小。 稍后的模式称为optimization模式。

如果没有kdstepsthreadslevelnotifications, 或者dict_id(基本上是来自底层^{tt289}的任何内容$ 结构)已定义,optimization模式与默认参数一起使用 价值观。

如果定义了stepsthreads,则优化模式启用 对这些参数有明确的控制。指定threads=0threads=1可用于参与优化模式,如果有其他参数 未定义。

否则,非optimization模式将与指定的参数一起使用。

此函数接受以下参数:

听写大小
要生成的字典的目标大小(字节)。
样本
保存字典训练样本的字节列表。
k
用于覆盖定义段大小的算法的参数。合理的范围 是[162048+]。
d
用于覆盖定义dmer大小的算法的参数。合理的范围是 [6,16]。d必须小于或等于k
指令ID
生成字典的整数字典ID。默认值为0,它使用 随机值。
步骤
尝试参数时要执行的步骤数k值 变化。
线程
尝试参数变化时要使用的线程数。默认值为0, 意思是用一根线。负值可以指定为 尽可能多地使用检测到的逻辑CPU。
水平
尝试参数变化时的整数目标压缩级别。
通知
控制将信息消息写入stderr0(在 默认)表示不写任何内容。1写入错误。2写入 进展信息。3写入更多详细信息。并且4写入所有信息。

显式压缩参数

zstandard提供了一个高级压缩级别compression level映射到较低级别 压缩参数。对于许多消费者来说,这个数字级别是 压缩设置你需要触摸。

但是对于高级用例,可能需要调整这些较低级别 设置。

ZstdCompressionParameters类型表示这些低级压缩 设置。

这种类型的实例可以由无数的关键字参数构造 (定义如下)对每个可调的 压缩设置。

从更高的层次,可以构造一个ZstdCompressionParameters实例 给定所需的压缩级别、目标输入和字典大小 使用ZstdCompressionParameters.from_level()。例如:

# Derive compression settings for compression level 7.
params = zstd.ZstdCompressionParameters.from_level(7)

# With an input size of 1MB
params = zstd.ZstdCompressionParameters.from_level(7, source_size=1048576)

使用from_level(),还可以重写单个压缩 参数或定义不自动派生的其他设置。 例如:

params = zstd.ZstdCompressionParameters.from_level(4, window_log=10)
params = zstd.ZstdCompressionParameters.from_level(5, threads=4)

或者您可以直接定义低级压缩设置:

params = zstd.ZstdCompressionParameters(window_log=12, enable_ldm=True)

一旦获得ZstdCompressionParameters实例,就可以使用它来 配置压缩机:

cctx = zstd.ZstdCompressor(compression_params=params)

ZstdCompressionParameters的命名参数和属性如下 如下:

  • 格式
  • 压缩级别
  • 窗口日志
  • 散列日志
  • 链日志
  • 搜索日志
  • 最小匹配
  • 目标长度
  • 策略
  • 压缩策略(已弃用:与strategy相同)
  • 写入内容大小
  • 写入校验和
  • 写入dict id
  • 作业大小
  • 重叠对数
  • 重叠大小日志(已弃用:与overlap_log相同)
  • “最大力”窗口
  • 启用LDM
  • ldm_hash_日志
  • ldm_min_匹配
  • ldm_bucket_size_log
  • ldm_hash_rate_日志
  • ldm_hash_every_log(已弃用:与ldm_hash_rate_log相同)
  • 线程

其中一些是非常低级的设置。向官员咨询可能会有帮助 z他们行为的标准文档。查找ZSTD_p_*常量 在zstd.hhttps://github.com/facebook/zstd/blob/dev/lib/zstd.h)中。

框架检查

zstd压缩发出的数据被封装在frame中。这个框架 以4字节magic number头开头,后跟2到14字节描述 更详细的框架。有关更多信息,请参见 https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md

zstd.get_frame_parameters(data)从字节解析zstdframe头 实例并返回描述帧的FrameParameters对象。

根据框架中出现的字段及其值, 帧参数的长度变化。如果传递的字节不足 为了完全解析帧参数,引发了ZstdError。确保 可以分析帧参数,至少传入18个字节。

FrameParameters实例具有以下属性:

内容大小
原始未压缩内容的整数大小。如果 原始内容大小未写入帧(由 write_content_size参数到ZstdCompressor,或者如果输入 内容大小为0
窗口大小
压缩数据中最大后参考距离的整数大小. < /dD>
指令ID
用于压缩的字典id的整数。0如果没有字典 使用了id,或者字典id是0
有校验和
bool,指示是否在末尾存储4字节内容校验和 帧的。

zstd.frame_header_size(data)返回zstandard框架的大小 头球。

zstd.frame_content_size(data)返回从中分析的内容大小 帧头。-1表示内容大小未知。0表示 空框内容大小通常是正确的。但是,它可能不会 要准确。

其他功能

估计解压缩上下文大小()

估计解压缩程序实例的内存大小需求。

常数

以下模块常量/属性已公开:

zstd_版本
此模块属性公开zStandard版本的3元组。例如 (1, 0, 0)
最大压缩级别
压缩函数接受的最大整数压缩级别
压缩建议的输入大小
为压缩机功能提供的建议块大小
压缩建议的输出大小
压缩输出的建议块大小
建议的解压缩输入大小
建议输入decompresor函数的块大小
建议的解压缩输出大小
解压缩输出的建议块大小
帧头
包含zstandard帧头的字节
幻数
帧头为整数
冲洗块
表示刷新zstd块的刷新行为。减压器会 能够解码到目前为止输入压缩机的所有数据。
刷新帧
表示结束zstd帧的刷新行为。任何新的数据 压缩机将启动一个新框架。
内容大小未知
内容大小未知时内容大小的值。
contentSize\u错误
无法确定内容大小时内容大小的值。
最小窗口日志
压缩参数的最小值
窗口日志最大值 < d>压缩参数的最大值< /dd>
链日志最小值
压缩参数的最小值
链日志最大值 压缩参数的最大值<
哈希日志最小值
压缩参数的最小值
哈希日志最大值
压缩参数的最大值< /dd>
搜索日志最小值
压缩参数的最小值
搜索日志最大值 < d>压缩参数的最大值< /dd>
最小匹配值
压缩参数的最小值
minmatch_max
< d>压缩参数的最大值< /dd>
搜索长度最小值 压缩参数的最小值

不推荐:使用MINMATCH_MIN

搜索长度最大值 >p>压缩参数的最大值

不推荐:使用MINMATCH_MAX

目标长度最小值
压缩参数的最小值
快速策略 压缩策略
战略成本 压缩策略
策略贪婪 压缩策略
策略懒惰
压缩策略
策略2 压缩策略
战略 压缩策略
战略 压缩策略
战略 压缩策略
战略 压缩策略
格式
z标准帧格式
格式化_zstd1_magicless
z标准帧格式,不带幻数头

性能考虑因素

ZstdCompressorZstdDecompressor类型将状态保持为 持久压缩或解压缩context。重用ZstdCompressor 或者ZstdDecompressor多个操作的实例比 为每个实例实例化一个新的ZstdCompressorZstdDecompressor。 操作。随着数据量的减小,这些差异被放大了。为了 例如,100000的context重用和不重用之间的区别 100字节的输入将是重要的(可能比重用上下文快10倍以上) 而10 100000000字节的输入在速度上会更相似(因为 压缩所花费的时间与创建新的contexts所花费的时间相形见绌。

缓冲类型

api公开了一些用于与内存缓冲区交互的自定义类型。 这些类型的主要目标是促进高效的多对象 操作。

其基本思想是让单个内存分配提供支持 多个逻辑对象的存储。这有两个主要优点:更少 分配和最佳内存访问模式。这样可以避免分配 为每个逻辑对象提供一个python对象,并进一步确保 对象的数据在内存中可以是连续的(读:快)。

带段缓冲区

类型BufferWithSegments表示包含n的内存缓冲区 已知长度(段)的离散项。基本上是固定尺寸的 内存地址和一个由2个元组组成的数组(offset, length)64位 定义每个字节偏移量和长度的无符号本机尾数整数 缓冲区内的段。

实例的行为类似于容器。

len()返回实例中的段数。

o[index]__getitem__获得表示 备份缓冲区中的单个段。返回对象引用的 (不是拷贝)内存。这意味着迭代所有对象不会复制 缓冲区中的数据。

.size属性包含备份的总大小(字节) 缓冲区。

实例符合缓冲区协议。所以对备份字节的引用 可以通过memoryview(o)获得。备份字节的copy也可以 通过.tobytes()获得。

.segments属性公开了的(offset, length)数组 缓冲区内的段。它是一个BufferSegments类型。

缓冲段

类型BufferSegment表示BufferWithSegments中的段。 它本质上是对BufferWithSegments中n个字节的引用。

len()返回段的长度(字节)。

.offset包含父段中此段的字节偏移量BufferWithSegments实例。

对象符合缓冲区协议。.tobytes()可以调用 获取带有备份字节副本的bytes实例。

缓冲段

此类型表示定义段的(offset, length)整数数组 在BufferWithSegments内。

数组成员是使用主机/本机位顺序的64位无符号整数。

实例符合缓冲区协议。

带分段采集的缓冲区

类型BufferWithSegmentsCollection表示虚拟跨越视图 多个BufferWithSegments实例的。

实例由1个或多个BufferWithSegments实例构造。这个 结果对象的行为类似于其成员是 每个BufferWithSegments内的段。

len()返回所有^{tt138}中的段数$ 实例。

o[index]__getitem__(index)返回位于 这个偏移量就好像所有的BufferWithSegments实例都是一个 实体。

如果对象由2BufferWithSegments个实例组成,则 第一个有2个段,第二个有3个段,然后b[0] 以及第一个对象中的b[1]访问段和b[2]b[3], 和b[4]从第二个访问段。

选择API

有多个api用于执行压缩和解压缩。这是 因为不同的应用程序有不同的需求,而库希望 在尽可能多的用例中促进最佳使用。

从高层来看,api分为one-shotstreaming:要么 正在一次操作所有数据,或者您对其进行逐段操作。

对于输入或输出为 大小是已知的。(大小可以来自缓冲区长度、文件大小或 存储在zstd frame header)中,one shotapi的一个限制是 输入和输出必须同时装入内存。比如说4 GB的输入, 这通常是不可行的。

one shotapi也将所有工作作为一个单独的操作来执行。所以,如果你 如果给它大量输入,函数可能需要很长时间才能返回。

流式api没有简单api的限制。但是 你为这种灵活性付出的代价是它们比A更复杂。 单一函数调用。

流式api使调用者能够控制压缩和解压缩 允许它们直接控制输入端或输出端的行为 手术的结果。

使用streaming inputcompressordecompressorapi,调用者 完全控制压缩或解压缩流的输入。 他们可以直接选择何时操作新数据。

使用streaming ouutapi,调用者可以完全控制输出 压缩或解压缩流的。它可以选择何时接收 新数据。

当使用streamingapi操作类文件或流对象时, 当请求I/O时,考虑对象中发生了什么是很重要的。 当数据从 底层流(比如与文件系统或网络交互)。这个 可能会增加相当多的开销。

螺纹安全性

ZstdCompressorZstdDecompressor实例没有保证 关于线程安全。不要对相同的ZstdCompressorZstdDecompressor同时来自不同线程的实例。它是 可以将不同的线程调用到单个实例中,而不是在 同时。

有些操作需要多个函数调用才能完成。例如流媒体 操作。不能使用单个ZstdCompressorZstdDecompressor。 用于同时活动的操作。你不能开始流媒体 当另一个流操作已处于活动状态时的操作。

C扩展版本在对zstd c进行非平凡调用时的gil 应用程序编程接口。非常重要的调用是压缩和解压。琐碎的 调用类似于解析帧参数。吉尔被释放的地方 被视为实现细节,可以在任何版本中更改。

接受类似字节的对象的api不会强制底层对象 是只读的。但是,假设传递的对象是 函数调用的持续时间。可以传递可变对象 (像bytearray)到ZstdCompressor.compress(),拥有gil 释放,并从另一个线程中变异对象。这样的种族状况 是python zstandard的使用者中的一个bug。大多数python数据类型是 不可改变,所以除非你在做一些花哨的事情,否则你不需要 担心这个。

zstandard的experimentalapi的注释

此模块使用的许多zstandard api被标记为experimental 在zstandard项目中。

目前尚不清楚zstandard的c api将如何随着时间的推移而发展,特别是 关于这个实验性的功能。我们会尽力保持 python api级别的向后兼容性。但是,我们不能 为我们无法控制的事情保证。

因为zstandard源代码的一个副本 由于我们针对它编译,所以 此模块的版本应始终保持不变。所以如果你 固定项目中使用的此模块的版本(这是一个python 最佳实践),你应该被保护起来,以免将来发生不必要的变化。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
从文本文件中读取时显示java符号“ï»”   java在有很多生产商的情况下如何改进Disruptor?   不同线程的java不同堆栈   用Java模拟oraclespool   jsp java访问自定义web中的错误信息。xml错误页   给出奇怪结果的java集成堆栈   java在jsp中显示值列表   java会话。保存更新具有错误ID的实体   在树数据结构中添加节点时的java递归   java在Spring Data Mongodb中使用$$ROOT检索整个文档   java我应该把图像放在罐子里还是不放在罐子里?(Inno设置)   java将bat文件放入jar文件中   Java:如何在节点上执行XPath查询   控制台应用程序如何在Mac上从Java输出重音字符?