mirror of
https://github.com/python/cpython.git
synced 2025-12-31 20:43:36 +00:00
638 lines
20 KiB
C
638 lines
20 KiB
C
/******************************************************************************
|
|
* Python Remote Debugging Module - Binary I/O Header
|
|
*
|
|
* This header provides declarations for high-performance binary file I/O
|
|
* for profiling data with optional zstd streaming compression.
|
|
******************************************************************************/
|
|
|
|
#ifndef Py_BINARY_IO_H
|
|
#define Py_BINARY_IO_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include "Python.h"
|
|
#include "pycore_hashtable.h"
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
/* ============================================================================
|
|
* BINARY FORMAT CONSTANTS
|
|
* ============================================================================ */
|
|
|
|
#define BINARY_FORMAT_MAGIC 0x54414348 /* "TACH" (Tachyon) in native byte order */
|
|
#define BINARY_FORMAT_MAGIC_SWAPPED 0x48434154 /* Byte-swapped magic for endianness detection */
|
|
#define BINARY_FORMAT_VERSION 1
|
|
|
|
/* Conditional byte-swap macros for cross-endian file reading.
|
|
* Uses Python's optimized byte-swap functions from pycore_bitutils.h */
|
|
#define SWAP16_IF(swap, x) ((swap) ? _Py_bswap16(x) : (x))
|
|
#define SWAP32_IF(swap, x) ((swap) ? _Py_bswap32(x) : (x))
|
|
#define SWAP64_IF(swap, x) ((swap) ? _Py_bswap64(x) : (x))
|
|
|
|
/* Header field offsets and sizes */
|
|
#define HDR_OFF_MAGIC 0
|
|
#define HDR_SIZE_MAGIC 4
|
|
#define HDR_OFF_VERSION (HDR_OFF_MAGIC + HDR_SIZE_MAGIC)
|
|
#define HDR_SIZE_VERSION 4
|
|
#define HDR_OFF_PY_VERSION (HDR_OFF_VERSION + HDR_SIZE_VERSION)
|
|
#define HDR_SIZE_PY_VERSION 4 /* 3 bytes: major, minor, micro + 1 reserved */
|
|
#define HDR_OFF_PY_MAJOR HDR_OFF_PY_VERSION
|
|
#define HDR_OFF_PY_MINOR (HDR_OFF_PY_VERSION + 1)
|
|
#define HDR_OFF_PY_MICRO (HDR_OFF_PY_VERSION + 2)
|
|
#define HDR_OFF_START_TIME (HDR_OFF_PY_VERSION + HDR_SIZE_PY_VERSION)
|
|
#define HDR_SIZE_START_TIME 8
|
|
#define HDR_OFF_INTERVAL (HDR_OFF_START_TIME + HDR_SIZE_START_TIME)
|
|
#define HDR_SIZE_INTERVAL 8
|
|
#define HDR_OFF_SAMPLES (HDR_OFF_INTERVAL + HDR_SIZE_INTERVAL)
|
|
#define HDR_SIZE_SAMPLES 4
|
|
#define HDR_OFF_THREADS (HDR_OFF_SAMPLES + HDR_SIZE_SAMPLES)
|
|
#define HDR_SIZE_THREADS 4
|
|
#define HDR_OFF_STR_TABLE (HDR_OFF_THREADS + HDR_SIZE_THREADS)
|
|
#define HDR_SIZE_STR_TABLE 8
|
|
#define HDR_OFF_FRAME_TABLE (HDR_OFF_STR_TABLE + HDR_SIZE_STR_TABLE)
|
|
#define HDR_SIZE_FRAME_TABLE 8
|
|
#define HDR_OFF_COMPRESSION (HDR_OFF_FRAME_TABLE + HDR_SIZE_FRAME_TABLE)
|
|
#define HDR_SIZE_COMPRESSION 4
|
|
#define FILE_HEADER_SIZE (HDR_OFF_COMPRESSION + HDR_SIZE_COMPRESSION)
|
|
#define FILE_HEADER_PLACEHOLDER_SIZE 64
|
|
|
|
static_assert(FILE_HEADER_SIZE <= FILE_HEADER_PLACEHOLDER_SIZE,
|
|
"FILE_HEADER_SIZE exceeds FILE_HEADER_PLACEHOLDER_SIZE");
|
|
|
|
/* Buffer sizes: 512KB balances syscall amortization against memory use,
|
|
* and aligns well with filesystem block sizes and zstd dictionary windows */
|
|
#define WRITE_BUFFER_SIZE (512 * 1024)
|
|
#define COMPRESSED_BUFFER_SIZE (512 * 1024)
|
|
|
|
/* Compression types */
|
|
#define COMPRESSION_NONE 0
|
|
#define COMPRESSION_ZSTD 1
|
|
|
|
/* Stack encoding types for delta compression */
|
|
#define STACK_REPEAT 0x00 /* RLE: identical to previous, with count */
|
|
#define STACK_FULL 0x01 /* Full stack (first sample or no match) */
|
|
#define STACK_SUFFIX 0x02 /* Shares N frames from bottom */
|
|
#define STACK_POP_PUSH 0x03 /* Remove M frames, add N frames */
|
|
|
|
/* Maximum stack depth we'll buffer for delta encoding */
|
|
#define MAX_STACK_DEPTH 256
|
|
|
|
/* Initial capacity for RLE pending buffer */
|
|
#define INITIAL_RLE_CAPACITY 64
|
|
|
|
/* Initial capacities for dynamic arrays - sized to reduce reallocations */
|
|
#define INITIAL_STRING_CAPACITY 4096
|
|
#define INITIAL_FRAME_CAPACITY 4096
|
|
#define INITIAL_THREAD_CAPACITY 256
|
|
|
|
/* ============================================================================
|
|
* STATISTICS STRUCTURES
|
|
* ============================================================================ */
|
|
|
|
/* Writer statistics - tracks encoding efficiency */
|
|
typedef struct {
|
|
uint64_t repeat_records; /* Number of RLE repeat records written */
|
|
uint64_t repeat_samples; /* Total samples encoded via RLE */
|
|
uint64_t full_records; /* Number of full stack records */
|
|
uint64_t suffix_records; /* Number of suffix match records */
|
|
uint64_t pop_push_records; /* Number of pop-push records */
|
|
uint64_t total_frames_written;/* Total frame indices written */
|
|
uint64_t frames_saved; /* Frames avoided due to delta encoding */
|
|
uint64_t bytes_written; /* Total bytes written (before compression) */
|
|
} BinaryWriterStats;
|
|
|
|
/* Reader statistics - tracks reconstruction performance */
|
|
typedef struct {
|
|
uint64_t repeat_records; /* RLE records decoded */
|
|
uint64_t repeat_samples; /* Samples decoded from RLE */
|
|
uint64_t full_records; /* Full stack records decoded */
|
|
uint64_t suffix_records; /* Suffix match records decoded */
|
|
uint64_t pop_push_records; /* Pop-push records decoded */
|
|
uint64_t total_samples; /* Total samples reconstructed */
|
|
uint64_t stack_reconstructions; /* Number of stack array reconstructions */
|
|
} BinaryReaderStats;
|
|
|
|
/* ============================================================================
|
|
* PLATFORM ABSTRACTION
|
|
* ============================================================================ */
|
|
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#define USE_MMAP 1
|
|
#else
|
|
#define USE_MMAP 0
|
|
#endif
|
|
|
|
/* 64-bit file position support for files larger than 2GB.
|
|
* On POSIX: use ftello/fseeko with off_t (already 64-bit on 64-bit systems)
|
|
* On Windows: use _ftelli64/_fseeki64 with __int64 */
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
#include <io.h>
|
|
typedef __int64 file_offset_t;
|
|
#define FTELL64(fp) _ftelli64(fp)
|
|
#define FSEEK64(fp, offset, whence) _fseeki64(fp, offset, whence)
|
|
#else
|
|
/* POSIX - off_t is 64-bit on 64-bit systems, ftello/fseeko handle large files */
|
|
typedef off_t file_offset_t;
|
|
#define FTELL64(fp) ftello(fp)
|
|
#define FSEEK64(fp, offset, whence) fseeko(fp, offset, whence)
|
|
#endif
|
|
|
|
/* Forward declare zstd types if available */
|
|
#ifdef HAVE_ZSTD
|
|
#include <zstd.h>
|
|
#endif
|
|
|
|
/* Branch prediction hints - same as Objects/obmalloc.c */
|
|
#if (defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 2))) && defined(__OPTIMIZE__)
|
|
# define UNLIKELY(value) __builtin_expect((value), 0)
|
|
# define LIKELY(value) __builtin_expect((value), 1)
|
|
#else
|
|
# define UNLIKELY(value) (value)
|
|
# define LIKELY(value) (value)
|
|
#endif
|
|
|
|
/* ============================================================================
|
|
* BINARY WRITER STRUCTURES
|
|
* ============================================================================ */
|
|
|
|
/* zstd compression state (only used if HAVE_ZSTD defined) */
|
|
typedef struct {
|
|
#ifdef HAVE_ZSTD
|
|
ZSTD_CCtx *cctx; /* Modern API: CCtx and CStream are the same since v1.3.0 */
|
|
#else
|
|
void *cctx; /* Placeholder */
|
|
#endif
|
|
uint8_t *compressed_buffer;
|
|
size_t compressed_buffer_size;
|
|
} ZstdCompressor;
|
|
|
|
/* Frame entry - combines all frame data for better cache locality */
|
|
typedef struct {
|
|
uint32_t filename_idx;
|
|
uint32_t funcname_idx;
|
|
int32_t lineno;
|
|
} FrameEntry;
|
|
|
|
/* Frame key for hash table lookup */
|
|
typedef struct {
|
|
uint32_t filename_idx;
|
|
uint32_t funcname_idx;
|
|
int32_t lineno;
|
|
} FrameKey;
|
|
|
|
/* Pending RLE sample - buffered for run-length encoding */
|
|
typedef struct {
|
|
uint64_t timestamp_delta;
|
|
uint8_t status;
|
|
} PendingRLESample;
|
|
|
|
/* Thread entry - tracks per-thread state for delta encoding */
|
|
typedef struct {
|
|
uint64_t thread_id;
|
|
uint64_t prev_timestamp;
|
|
uint32_t interpreter_id;
|
|
|
|
/* Previous stack for delta encoding (frame indices, innermost first) */
|
|
uint32_t *prev_stack;
|
|
size_t prev_stack_depth;
|
|
size_t prev_stack_capacity;
|
|
|
|
/* RLE pending buffer - samples waiting to be written as a repeat group */
|
|
PendingRLESample *pending_rle;
|
|
size_t pending_rle_count;
|
|
size_t pending_rle_capacity;
|
|
int has_pending_rle; /* Flag: do we have buffered repeats? */
|
|
} ThreadEntry;
|
|
|
|
/* Main binary writer structure */
|
|
typedef struct {
|
|
FILE *fp;
|
|
char *filename;
|
|
|
|
/* Write buffer for batched I/O */
|
|
uint8_t *write_buffer;
|
|
size_t buffer_pos;
|
|
size_t buffer_size;
|
|
|
|
/* Compression */
|
|
int compression_type;
|
|
ZstdCompressor zstd;
|
|
|
|
/* Metadata */
|
|
uint64_t start_time_us;
|
|
uint64_t sample_interval_us;
|
|
uint32_t total_samples;
|
|
|
|
/* String hash table: PyObject* -> uint32_t index */
|
|
_Py_hashtable_t *string_hash;
|
|
/* String storage: array of UTF-8 encoded strings */
|
|
char **strings;
|
|
size_t *string_lengths;
|
|
size_t string_count;
|
|
size_t string_capacity;
|
|
|
|
/* Frame hash table: FrameKey* -> uint32_t index */
|
|
_Py_hashtable_t *frame_hash;
|
|
/* Frame storage: combined struct for better cache locality */
|
|
FrameEntry *frame_entries;
|
|
size_t frame_count;
|
|
size_t frame_capacity;
|
|
|
|
/* Thread timestamp tracking for delta encoding - combined for cache locality */
|
|
ThreadEntry *thread_entries;
|
|
size_t thread_count;
|
|
size_t thread_capacity;
|
|
|
|
/* Statistics */
|
|
BinaryWriterStats stats;
|
|
} BinaryWriter;
|
|
|
|
/* ============================================================================
|
|
* BINARY READER STRUCTURES
|
|
* ============================================================================ */
|
|
|
|
/* Per-thread state for stack reconstruction during replay */
|
|
typedef struct {
|
|
uint64_t thread_id;
|
|
uint32_t interpreter_id;
|
|
uint64_t prev_timestamp;
|
|
|
|
/* Reconstructed stack buffer (frame indices, innermost first) */
|
|
uint32_t *current_stack;
|
|
size_t current_stack_depth;
|
|
size_t current_stack_capacity;
|
|
} ReaderThreadState;
|
|
|
|
/* Main binary reader structure */
|
|
typedef struct {
|
|
char *filename;
|
|
|
|
#if USE_MMAP
|
|
int fd;
|
|
uint8_t *mapped_data;
|
|
size_t mapped_size;
|
|
#else
|
|
FILE *fp;
|
|
uint8_t *file_data;
|
|
size_t file_size;
|
|
#endif
|
|
|
|
/* Decompression state */
|
|
int compression_type;
|
|
/* Note: ZSTD_DCtx is not stored - created/freed during decompression */
|
|
uint8_t *decompressed_data;
|
|
size_t decompressed_size;
|
|
|
|
/* Header metadata */
|
|
uint8_t py_major;
|
|
uint8_t py_minor;
|
|
uint8_t py_micro;
|
|
int needs_swap; /* Non-zero if file was written on different-endian system */
|
|
uint64_t start_time_us;
|
|
uint64_t sample_interval_us;
|
|
uint32_t sample_count;
|
|
uint32_t thread_count;
|
|
uint64_t string_table_offset;
|
|
uint64_t frame_table_offset;
|
|
|
|
/* Parsed string table: array of Python string objects */
|
|
PyObject **strings;
|
|
uint32_t strings_count;
|
|
|
|
/* Parsed frame table: packed as [filename_idx, funcname_idx, lineno] */
|
|
uint32_t *frame_data;
|
|
uint32_t frames_count;
|
|
|
|
/* Sample data region */
|
|
uint8_t *sample_data;
|
|
size_t sample_data_size;
|
|
|
|
/* Per-thread state for stack reconstruction (used during replay) */
|
|
ReaderThreadState *thread_states;
|
|
size_t thread_state_count;
|
|
size_t thread_state_capacity;
|
|
|
|
/* Statistics */
|
|
BinaryReaderStats stats;
|
|
} BinaryReader;
|
|
|
|
/* ============================================================================
|
|
* VARINT ENCODING/DECODING (INLINE FOR PERFORMANCE)
|
|
* ============================================================================ */
|
|
|
|
/* Encode unsigned 64-bit varint (LEB128). Returns bytes written. */
|
|
static inline size_t
|
|
encode_varint_u64(uint8_t *buf, uint64_t value)
|
|
{
|
|
/* Fast path for single-byte values (0-127) - very common case */
|
|
if (value < 0x80) {
|
|
buf[0] = (uint8_t)value;
|
|
return 1;
|
|
}
|
|
|
|
size_t i = 0;
|
|
while (value >= 0x80) {
|
|
buf[i++] = (uint8_t)((value & 0x7F) | 0x80);
|
|
value >>= 7;
|
|
}
|
|
buf[i++] = (uint8_t)(value & 0x7F);
|
|
return i;
|
|
}
|
|
|
|
/* Encode unsigned 32-bit varint. Returns bytes written. */
|
|
static inline size_t
|
|
encode_varint_u32(uint8_t *buf, uint32_t value)
|
|
{
|
|
return encode_varint_u64(buf, value);
|
|
}
|
|
|
|
/* Encode signed 32-bit varint (zigzag encoding). Returns bytes written. */
|
|
static inline size_t
|
|
encode_varint_i32(uint8_t *buf, int32_t value)
|
|
{
|
|
/* Zigzag encode: map signed to unsigned */
|
|
uint32_t zigzag = ((uint32_t)value << 1) ^ (uint32_t)(value >> 31);
|
|
return encode_varint_u32(buf, zigzag);
|
|
}
|
|
|
|
/* Decode unsigned 64-bit varint (LEB128). Updates offset only on success.
|
|
* On error (overflow or incomplete), offset is NOT updated, allowing callers
|
|
* to detect errors via (offset == prev_offset) check. Sets PyErr on error. */
|
|
static inline uint64_t
|
|
decode_varint_u64(const uint8_t *data, size_t *offset, size_t max_size)
|
|
{
|
|
size_t pos = *offset;
|
|
uint64_t result = 0;
|
|
int shift = 0;
|
|
|
|
/* Fast path for single-byte varints (0-127) - most common case */
|
|
if (LIKELY(pos < max_size && (data[pos] & 0x80) == 0)) {
|
|
*offset = pos + 1;
|
|
return data[pos];
|
|
}
|
|
|
|
while (pos < max_size) {
|
|
uint8_t byte = data[pos++];
|
|
result |= (uint64_t)(byte & 0x7F) << shift;
|
|
if ((byte & 0x80) == 0) {
|
|
*offset = pos;
|
|
return result;
|
|
}
|
|
shift += 7;
|
|
if (UNLIKELY(shift >= 64)) {
|
|
PyErr_SetString(PyExc_ValueError, "Invalid or incomplete varint in binary data");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
PyErr_SetString(PyExc_ValueError, "Invalid or incomplete varint in binary data");
|
|
return 0;
|
|
}
|
|
|
|
/* Decode unsigned 32-bit varint. If value exceeds UINT32_MAX, treats as error. */
|
|
static inline uint32_t
|
|
decode_varint_u32(const uint8_t *data, size_t *offset, size_t max_size)
|
|
{
|
|
size_t saved_offset = *offset;
|
|
uint64_t value = decode_varint_u64(data, offset, max_size);
|
|
if (PyErr_Occurred()) {
|
|
return 0;
|
|
}
|
|
if (UNLIKELY(value > UINT32_MAX)) {
|
|
*offset = saved_offset;
|
|
PyErr_SetString(PyExc_ValueError, "Invalid or incomplete varint in binary data");
|
|
return 0;
|
|
}
|
|
return (uint32_t)value;
|
|
}
|
|
|
|
/* Decode signed 32-bit varint (zigzag encoding). */
|
|
static inline int32_t
|
|
decode_varint_i32(const uint8_t *data, size_t *offset, size_t max_size)
|
|
{
|
|
uint32_t zigzag = decode_varint_u32(data, offset, max_size);
|
|
if (PyErr_Occurred()) {
|
|
return 0;
|
|
}
|
|
return (int32_t)((zigzag >> 1) ^ -(int32_t)(zigzag & 1));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* SHARED UTILITY FUNCTIONS
|
|
* ============================================================================ */
|
|
|
|
/* Generic array growth - returns new pointer or NULL (sets PyErr_NoMemory)
|
|
* Includes overflow checking for capacity doubling and allocation size. */
|
|
static inline void *
|
|
grow_array(void *ptr, size_t *capacity, size_t elem_size)
|
|
{
|
|
size_t old_cap = *capacity;
|
|
|
|
/* Check for overflow when doubling capacity */
|
|
if (old_cap > SIZE_MAX / 2) {
|
|
PyErr_SetString(PyExc_OverflowError, "Array capacity overflow");
|
|
return NULL;
|
|
}
|
|
size_t new_cap = old_cap * 2;
|
|
|
|
/* Check for overflow when calculating allocation size */
|
|
if (new_cap > SIZE_MAX / elem_size) {
|
|
PyErr_SetString(PyExc_OverflowError, "Array allocation size overflow");
|
|
return NULL;
|
|
}
|
|
|
|
void *new_ptr = PyMem_Realloc(ptr, new_cap * elem_size);
|
|
if (new_ptr) {
|
|
*capacity = new_cap;
|
|
} else {
|
|
PyErr_NoMemory();
|
|
}
|
|
return new_ptr;
|
|
}
|
|
|
|
static inline int
|
|
grow_array_inplace(void **ptr_addr, size_t count, size_t *capacity, size_t elem_size)
|
|
{
|
|
if (count < *capacity) {
|
|
return 0;
|
|
}
|
|
void *tmp = grow_array(*ptr_addr, capacity, elem_size);
|
|
if (tmp == NULL) {
|
|
return -1;
|
|
}
|
|
*ptr_addr = tmp;
|
|
return 0;
|
|
}
|
|
|
|
#define GROW_ARRAY(ptr, count, cap, type) \
|
|
grow_array_inplace((void**)&(ptr), (count), &(cap), sizeof(type))
|
|
|
|
/* ============================================================================
|
|
* BINARY WRITER API
|
|
* ============================================================================ */
|
|
|
|
/*
|
|
* Create a new binary writer.
|
|
*
|
|
* Arguments:
|
|
* filename: Path to output file
|
|
* sample_interval_us: Sampling interval in microseconds
|
|
* compression_type: COMPRESSION_NONE or COMPRESSION_ZSTD
|
|
* start_time_us: Start timestamp in microseconds (from time.monotonic() * 1e6)
|
|
*
|
|
* Returns:
|
|
* New BinaryWriter* on success, NULL on failure (PyErr set)
|
|
*/
|
|
BinaryWriter *binary_writer_create(
|
|
const char *filename,
|
|
uint64_t sample_interval_us,
|
|
int compression_type,
|
|
uint64_t start_time_us
|
|
);
|
|
|
|
/*
|
|
* Write a sample to the binary file.
|
|
*
|
|
* Arguments:
|
|
* writer: Writer from binary_writer_create
|
|
* stack_frames: List of InterpreterInfo struct sequences
|
|
* timestamp_us: Current timestamp in microseconds (from time.monotonic() * 1e6)
|
|
*
|
|
* Returns:
|
|
* 0 on success, -1 on failure (PyErr set)
|
|
*/
|
|
int binary_writer_write_sample(
|
|
BinaryWriter *writer,
|
|
PyObject *stack_frames,
|
|
uint64_t timestamp_us
|
|
);
|
|
|
|
/*
|
|
* Finalize and close the binary file.
|
|
* Writes string/frame tables, footer, and updates header.
|
|
*
|
|
* Arguments:
|
|
* writer: Writer to finalize
|
|
*
|
|
* Returns:
|
|
* 0 on success, -1 on failure (PyErr set)
|
|
*/
|
|
int binary_writer_finalize(BinaryWriter *writer);
|
|
|
|
/*
|
|
* Destroy a binary writer and free all resources.
|
|
* Safe to call even if writer is partially initialized.
|
|
*
|
|
* Arguments:
|
|
* writer: Writer to destroy (may be NULL)
|
|
*/
|
|
void binary_writer_destroy(BinaryWriter *writer);
|
|
|
|
/* ============================================================================
|
|
* BINARY READER API
|
|
* ============================================================================ */
|
|
|
|
/*
|
|
* Open a binary file for reading.
|
|
*
|
|
* Arguments:
|
|
* filename: Path to input file
|
|
*
|
|
* Returns:
|
|
* New BinaryReader* on success, NULL on failure (PyErr set)
|
|
*/
|
|
BinaryReader *binary_reader_open(const char *filename);
|
|
|
|
/*
|
|
* Replay samples from binary file through a collector.
|
|
*
|
|
* Arguments:
|
|
* reader: Reader from binary_reader_open
|
|
* collector: Python collector with collect() method
|
|
* progress_callback: Optional callable(current, total) or NULL
|
|
*
|
|
* Returns:
|
|
* Number of samples replayed on success, -1 on failure (PyErr set)
|
|
*/
|
|
Py_ssize_t binary_reader_replay(
|
|
BinaryReader *reader,
|
|
PyObject *collector,
|
|
PyObject *progress_callback
|
|
);
|
|
|
|
/*
|
|
* Get metadata about the binary file.
|
|
*
|
|
* Arguments:
|
|
* reader: Reader from binary_reader_open
|
|
*
|
|
* Returns:
|
|
* Dict with file metadata on success, NULL on failure (PyErr set)
|
|
*/
|
|
PyObject *binary_reader_get_info(BinaryReader *reader);
|
|
|
|
/*
|
|
* Close a binary reader and free all resources.
|
|
*
|
|
* Arguments:
|
|
* reader: Reader to close (may be NULL)
|
|
*/
|
|
void binary_reader_close(BinaryReader *reader);
|
|
|
|
/* ============================================================================
|
|
* STATISTICS FUNCTIONS
|
|
* ============================================================================ */
|
|
|
|
/*
|
|
* Get writer statistics as a Python dict.
|
|
*
|
|
* Arguments:
|
|
* writer: Writer to get stats from
|
|
*
|
|
* Returns:
|
|
* Dict with statistics on success, NULL on failure (PyErr set)
|
|
*/
|
|
PyObject *binary_writer_get_stats(BinaryWriter *writer);
|
|
|
|
/*
|
|
* Get reader statistics as a Python dict.
|
|
*
|
|
* Arguments:
|
|
* reader: Reader to get stats from
|
|
*
|
|
* Returns:
|
|
* Dict with statistics on success, NULL on failure (PyErr set)
|
|
*/
|
|
PyObject *binary_reader_get_stats(BinaryReader *reader);
|
|
|
|
/* ============================================================================
|
|
* UTILITY FUNCTIONS
|
|
* ============================================================================ */
|
|
|
|
/*
|
|
* Check if zstd compression is available.
|
|
*
|
|
* Returns:
|
|
* 1 if zstd available, 0 otherwise
|
|
*/
|
|
int binary_io_zstd_available(void);
|
|
|
|
/*
|
|
* Get the best available compression type.
|
|
*
|
|
* Returns:
|
|
* COMPRESSION_ZSTD if available, COMPRESSION_NONE otherwise
|
|
*/
|
|
int binary_io_get_best_compression(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* Py_BINARY_IO_H */
|