rizin/librz/include/rz_util/rz_buf.h
billow 7a77b8f7bd
linter: update clang-format installation to version 20 (#5451)
* fix: format with clang-format-20
* fix: fix clang-format linter with rewrite '^#define.*/\*.*\\$'
* fix: update clang-format to version 20 in workflows and documentation
* fix: add SPDX license information to .git-blame-ignore-revs
2025-10-12 08:02:03 +08:00

410 lines
17 KiB
C

#ifndef RZ_BUF_H
#define RZ_BUF_H
#include <rz_util/rz_mem.h>
#include <rz_types.h>
#include <rz_list.h>
#include <rz_util/rz_assert.h>
#ifdef __cplusplus
extern "C" {
#endif
// TODO: choose whether the _at operations should preserve the current seek or not
#define RZ_BUF_SET 0
#define RZ_BUF_CUR 1
#define RZ_BUF_END 2
typedef struct rz_buf_t RzBuffer;
typedef bool (*RzBufferInit)(RzBuffer *b, const void *user);
typedef bool (*RzBufferFini)(RzBuffer *b);
typedef st64 (*RzBufferRead)(RZ_BORROW RzBuffer *b, RZ_OUT ut8 *buf, ut64 len);
typedef st64 (*RzBufferWrite)(RzBuffer *b, const ut8 *buf, ut64 len);
typedef ut64 (*RzBufferGetSize)(RzBuffer *b);
typedef bool (*RzBufferResize)(RzBuffer *b, ut64 newsize);
typedef st64 (*RzBufferSeek)(RzBuffer *b, st64 addr, int whence);
typedef ut8 *(*RzBufferGetWholeBuf)(RzBuffer *b, ut64 *sz);
typedef void (*RzBufferFreeWholeBuf)(RzBuffer *b);
typedef RzList *(*RzBufferNonEmptyList)(RzBuffer *b);
typedef struct rz_buffer_methods_t {
RzBufferInit init;
RzBufferFini fini;
RzBufferRead read; ///< The buffer read() method. It should behave as rz_buf_read() documents.
RzBufferWrite write;
RzBufferGetSize get_size;
RzBufferResize resize;
RzBufferSeek seek;
RzBufferGetWholeBuf get_whole_buf;
RzBufferFreeWholeBuf free_whole_buf;
} RzBufferMethods;
typedef enum {
RZ_BUFFER_INVALID = 0,
RZ_BUFFER_FILE,
RZ_BUFFER_IO_FD,
RZ_BUFFER_IO, ///< A buffer over RzIO.
RZ_BUFFER_BYTES, ///< A buffer over raw bytes.
RZ_BUFFER_MMAP,
RZ_BUFFER_SPARSE,
RZ_BUFFER_REF,
RZ_BUFFER_CUSTOM, ///< A buffer with custom methods.
} RzBufferType;
struct rz_buf_t {
RzBufferType type;
const RzBufferMethods *methods;
void *priv;
ut8 *whole_buf;
bool readonly;
ut8 Oxff_priv;
int refctr;
int fd;
};
typedef struct rz_buf_sparse_chunk_t {
ut64 from; ///< inclusive
ut64 to; ///< inclusive, there can't be chunks with size == 0
ut8 *data; ///< size == to - from + 1
} RzBufferSparseChunk;
typedef enum {
RZ_BUF_SPARSE_WRITE_MODE_SPARSE, ///< all writes are performed in the sparse overlay
RZ_BUF_SPARSE_WRITE_MODE_THROUGH ///< all writes are performed in the underlying base buffer
} RzBufferSparseWriteMode;
/* utils */
/// change cur according to addr and whence (RZ_BUF_SET/RZ_BUF_CUR/RZ_BUF_END)
static inline st64 rz_seek_offset(ut64 cur, ut64 length, st64 addr, int whence) {
switch (whence) {
case RZ_BUF_CUR:
if (ST64_ADD_OVFCHK((st64)cur, addr)) {
return -1;
}
return cur + addr;
case RZ_BUF_SET:
if ((st64)addr < 0) {
return -1;
}
return addr;
case RZ_BUF_END:
if (ST64_ADD_OVFCHK((st64)length, addr)) {
return -1;
}
return length + addr;
default:
rz_warn_if_reached();
return -1;
}
}
/* constructors */
RZ_API RZ_OWN RzBuffer *rz_buf_new_empty(ut64 len);
RZ_API RZ_OWN RzBuffer *rz_buf_new_file(const char *file, int perm, int mode);
RZ_API RZ_OWN RzBuffer *rz_buf_new_mmap(const char *file, int flags, int mode);
RZ_API RZ_OWN RzBuffer *rz_buf_new_slice(RzBuffer *b, ut64 offset, ut64 size);
RZ_API RZ_OWN RzBuffer *rz_buf_new_slurp(const char *file);
RZ_API RZ_OWN RzBuffer *rz_buf_new_sparse(ut8 Oxff);
RZ_API RZ_OWN RzBuffer *rz_buf_new_sparse_overlay(RzBuffer *b, RzBufferSparseWriteMode write_mode);
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_buf(RzBuffer *b);
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_bytes(RZ_NULLABLE RZ_BORROW const ut8 *bytes, ut64 len);
RZ_API RZ_OWN RzBuffer *rz_buf_new_from_bytes(RZ_NULLABLE RZ_OWN ut8 *bytes, ut64 len);
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_io_fd(RZ_NONNULL void /* RzIOBind */ *iob, int fd);
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_io(RZ_NONNULL void /* RzIOBind */ *iob);
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_methods(RZ_NONNULL const RzBufferMethods *methods, void *init_user, RzBufferType type);
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_pointers(const ut8 *bytes, ut64 len, bool steal);
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_string(RZ_NONNULL const char *msg);
/* methods */
RZ_API RZ_OWN char *rz_buf_get_nstring(RZ_NONNULL RzBuffer *b, ut64 addr, size_t size);
RZ_API RZ_OWN char *rz_buf_get_string(RZ_NONNULL RzBuffer *b, ut64 addr);
RZ_API ut64 rz_buf_read_string(RZ_NONNULL RzBuffer *b, RZ_BORROW RZ_NULLABLE char **s);
RZ_API RZ_OWN char *rz_buf_to_string(RZ_NONNULL RzBuffer *b);
RZ_API RzBuffer *rz_buf_ref(RzBuffer *b);
RZ_API bool rz_buf_append_buf(RZ_NONNULL RzBuffer *b, RZ_NONNULL RzBuffer *a);
RZ_API bool rz_buf_append_buf_slice(RZ_NONNULL RzBuffer *b, RZ_NONNULL RzBuffer *a, ut64 offset, ut64 size);
RZ_API bool rz_buf_append_bytes(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 len);
RZ_API bool rz_buf_append_nbytes(RZ_NONNULL RzBuffer *b, ut64 len);
RZ_API bool rz_buf_append_ut16(RZ_NONNULL RzBuffer *b, ut16 n);
RZ_API bool rz_buf_append_ut32(RZ_NONNULL RzBuffer *b, ut32 n);
RZ_API bool rz_buf_append_ut64(RZ_NONNULL RzBuffer *b, ut64 n);
RZ_API bool rz_buf_dump(RZ_NONNULL RzBuffer *buf, RZ_NONNULL const char *file);
RZ_API bool rz_buf_fini(RzBuffer *b);
RZ_API bool rz_buf_prepend_bytes(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 len);
RZ_API bool rz_buf_read8(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut8 *result);
RZ_API bool rz_buf_read8_at(RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut8 *result);
RZ_API bool rz_buf_resize(RZ_NONNULL RzBuffer *b, ut64 newsize);
RZ_API bool rz_buf_set_bytes(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 len);
RZ_API bool rz_buf_write8(RZ_NONNULL RzBuffer *b, ut8 value);
RZ_API bool rz_buf_write8_at(RZ_NONNULL RzBuffer *b, ut64 addr, ut8 value);
RZ_API st64 rz_buf_append_string(RZ_NONNULL RzBuffer *b, RZ_NONNULL const char *str);
RZ_API st64 rz_buf_fread(RZ_NONNULL RzBuffer *b, RZ_NONNULL ut8 *buf, RZ_NONNULL const char *fmt, int n);
RZ_API st64 rz_buf_fread_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL ut8 *buf, RZ_NONNULL const char *fmt, int n);
RZ_API st64 rz_buf_fwrite(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, RZ_NONNULL const char *fmt, int n);
RZ_API st64 rz_buf_fwrite_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, RZ_NONNULL const char *fmt, int n);
RZ_API st64 rz_buf_insert_bytes(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, ut64 len);
RZ_API st64 rz_buf_read(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len);
RZ_API st64 rz_buf_read_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len);
RZ_API st64 rz_buf_seek(RZ_NONNULL RzBuffer *b, st64 addr, int whence);
RZ_API st64 rz_buf_write(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 len);
RZ_API st64 rz_buf_write_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, ut64 len);
RZ_API ut64 rz_buf_size(RZ_NONNULL RzBuffer *b);
RZ_API ut64 rz_buf_tell(RZ_NONNULL RzBuffer *b);
RZ_API void rz_buf_free(RzBuffer *b);
RZ_API void rz_buf_set_overflow_byte(RZ_NONNULL RzBuffer *b, ut8 Oxff);
RZ_API bool rz_buf_is_bytes_buf(const RzBuffer *b);
RZ_DEPRECATE RZ_API RZ_BORROW ut8 *rz_buf_data(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut64 *size);
RZ_API RZ_BORROW const ut8 *rz_buf_get_whole_hot_paths(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut64 *sz);
RZ_API RzBufferType rz_buf_type(RZ_NONNULL const RzBuffer *b);
/**
* \brief Callback to be used with rz_buf_fwd_scan().
*
* \param buf Buffer it can read.
* \param len Length of buffer to read.
* \param user User data.
*
* \return The number of bytes read.
*/
typedef ut64 (*RzBufferFwdScan)(RZ_BORROW RZ_NONNULL const ut8 *buf, ut64 len, RZ_NULLABLE void *user);
RZ_API ut64 rz_buf_fwd_scan(RZ_NONNULL RzBuffer *b, ut64 start, ut64 amount, RZ_NONNULL RzBufferFwdScan fwd_scan, RZ_NULLABLE void *user);
RZ_API st64 rz_buf_uleb128(RZ_NONNULL RzBuffer *buffer, RZ_NONNULL ut64 *value);
RZ_API st64 rz_buf_sleb128(RZ_NONNULL RzBuffer *buffer, RZ_NONNULL st64 *value);
static inline st64 rz_buf_uleb128_at(RzBuffer *b, ut64 addr, ut64 *v) {
rz_buf_seek(b, addr, RZ_BUF_SET);
return rz_buf_uleb128(b, v);
}
static inline st64 rz_buf_sleb128_at(RzBuffer *b, ut64 addr, st64 *v) {
rz_buf_seek(b, addr, RZ_BUF_SET);
return rz_buf_sleb128(b, v);
}
/* generated methods */
#define DEFINE_RZ_BUF_READ_BLE(size) \
static inline bool rz_buf_read_ble##size(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut##size *result, bool big_endian) { \
rz_return_val_if_fail(b && result, false); \
\
ut8 tmp[sizeof(ut##size)]; \
if (rz_buf_read(b, tmp, sizeof(tmp)) != sizeof(tmp)) { \
return false; \
} \
\
*result = rz_read_ble##size(tmp, big_endian); \
return true; \
} \
\
static inline bool rz_buf_read_ble##size##_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut##size *result, bool big_endian) { \
rz_return_val_if_fail(b && result, false); \
\
ut8 tmp[sizeof(ut##size)]; \
if (rz_buf_read_at(b, addr, tmp, sizeof(tmp)) != sizeof(tmp)) { \
return false; \
} \
\
*result = rz_read_ble##size(tmp, big_endian); \
return true; \
}
#define DEFINE_RZ_BUF_WRITE_BLE(size) \
static inline bool rz_buf_write_ble##size(RZ_NONNULL RzBuffer *b, ut##size value, bool big_endian) { \
ut8 tmp[sizeof(ut##size)]; \
rz_write_ble##size(tmp, value, big_endian); \
\
return rz_buf_write(b, tmp, sizeof(tmp)) == sizeof(tmp); \
} \
\
static inline bool rz_buf_write_ble##size##_at(RZ_NONNULL RzBuffer *b, ut64 addr, ut##size value, bool big_endian) { \
ut8 tmp[sizeof(ut##size)]; \
rz_write_ble##size(tmp, value, big_endian); \
\
return rz_buf_write_at(b, addr, tmp, sizeof(tmp)) == sizeof(tmp); \
}
/**
* \brief Read a big endian or little endian (ut16, ut32, ut64) at the specified address or cursor in the buffer.
* \param b ...
* \param addr (optional)
* \param result ...
* \param big_endian ...
* \return Return the status of the operation.
*/
DEFINE_RZ_BUF_READ_BLE(16)
DEFINE_RZ_BUF_READ_BLE(32)
DEFINE_RZ_BUF_READ_BLE(64)
DEFINE_RZ_BUF_READ_BLE(128)
/**
* \brief Write a big endian or little endian ut16 at the specified address or cursor in the buffer.
* \param b ...
* \param addr (optional)
* \param result ...
* \param big_endian ...
* \return Return the status of the operation.
*/
DEFINE_RZ_BUF_WRITE_BLE(16)
DEFINE_RZ_BUF_WRITE_BLE(32)
DEFINE_RZ_BUF_WRITE_BLE(64)
DEFINE_RZ_BUF_WRITE_BLE(128)
#define DEFINE_RZ_BUF_READ_OFFSET_BLE(size) \
static inline bool rz_buf_read_ble##size##_offset(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_INOUT ut64 *offset, RZ_NONNULL RZ_OUT ut##size *result, bool big_endian) { \
rz_return_val_if_fail(b && offset && result, false); \
if (!rz_buf_read_ble##size##_at(b, *offset, result, big_endian)) { \
return false; \
} \
*offset += sizeof(*result); \
return true; \
}
static inline bool rz_buf_read_offset(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_INOUT ut64 *offset, RZ_NONNULL RZ_OUT ut8 *result, size_t size) {
rz_return_val_if_fail(b && offset && result, false);
if (rz_buf_read_at(b, *offset, result, size) != (st64)size) {
return false;
}
*offset += size;
return true;
}
#define DEFINE_RZ_BUF_WRITE_OFFSET_BLE(size) \
static inline bool rz_buf_write_ble##size##_offset(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_INOUT ut64 *offset, ut##size value, bool big_endian) { \
rz_return_val_if_fail(b && offset, false); \
if (!rz_buf_write_ble##size##_at(b, *offset, value, big_endian)) { \
return false; \
} \
*offset += sizeof(value); \
return true; \
}
static inline bool rz_buf_write_offset(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_INOUT ut64 *offset, RZ_NONNULL ut8 *result, size_t size) {
rz_return_val_if_fail(b && offset && result, false);
if (rz_buf_write_at(b, *offset, result, size) != (st64)size) {
return false;
}
*offset += size;
return true;
}
#define rz_buf_read_ble8_at(b, addr, result, endian) ((void)endian, rz_buf_read8_at(b, addr, result))
#define rz_buf_write_ble8_at(b, addr, value, endian) ((void)endian, rz_buf_write8_at(b, addr, value))
/**
* \brief Read a big endian or little endian (ut16, ut32, ut64) at the specified offset in the buffer and shifts the offset.
* \param b ...
* \param offset ...
* \param result ...
* \param big_endian ...
* \return Return the status of the operation.
*/
DEFINE_RZ_BUF_READ_OFFSET_BLE(8)
DEFINE_RZ_BUF_READ_OFFSET_BLE(16)
DEFINE_RZ_BUF_READ_OFFSET_BLE(32)
DEFINE_RZ_BUF_READ_OFFSET_BLE(64)
/**
* \brief Write a big endian or little endian ut16 at the specified address or cursor in the buffer and shifts the offset.
* \param b ...
* \param addr (optional)
* \param result ...
* \param big_endian ...
* \return Return the status of the operation.
*/
DEFINE_RZ_BUF_WRITE_OFFSET_BLE(8)
DEFINE_RZ_BUF_WRITE_OFFSET_BLE(16)
DEFINE_RZ_BUF_WRITE_OFFSET_BLE(32)
DEFINE_RZ_BUF_WRITE_OFFSET_BLE(64)
#define rz_buf_read_le16(b, result) rz_buf_read_ble16(b, result, false)
#define rz_buf_read_le32(b, result) rz_buf_read_ble32(b, result, false)
#define rz_buf_read_le64(b, result) rz_buf_read_ble64(b, result, false)
#define rz_buf_read_le16_at(b, addr, result) rz_buf_read_ble16_at(b, addr, result, false)
#define rz_buf_read_le32_at(b, addr, result) rz_buf_read_ble32_at(b, addr, result, false)
#define rz_buf_read_le64_at(b, addr, result) rz_buf_read_ble64_at(b, addr, result, false)
#define rz_buf_read8_offset(b, offset, result) rz_buf_read_ble8_offset(b, offset, result, false)
#define rz_buf_read_le16_offset(b, offset, result) rz_buf_read_ble16_offset(b, offset, result, false)
#define rz_buf_read_le32_offset(b, offset, result) rz_buf_read_ble32_offset(b, offset, result, false)
#define rz_buf_read_le64_offset(b, offset, result) rz_buf_read_ble64_offset(b, offset, result, false)
#define rz_buf_read_be16(b, result) rz_buf_read_ble16(b, result, true)
#define rz_buf_read_be32(b, result) rz_buf_read_ble32(b, result, true)
#define rz_buf_read_be64(b, result) rz_buf_read_ble64(b, result, true)
#define rz_buf_read_be16_at(b, addr, result) rz_buf_read_ble16_at(b, addr, result, true)
#define rz_buf_read_be32_at(b, addr, result) rz_buf_read_ble32_at(b, addr, result, true)
#define rz_buf_read_be64_at(b, addr, result) rz_buf_read_ble64_at(b, addr, result, true)
#define rz_buf_read_be16_offset(b, offset, result) rz_buf_read_ble16_offset(b, offset, result, true)
#define rz_buf_read_be32_offset(b, offset, result) rz_buf_read_ble32_offset(b, offset, result, true)
#define rz_buf_read_be64_offset(b, offset, result) rz_buf_read_ble64_offset(b, offset, result, true)
#define rz_buf_write_le16(b, value) rz_buf_write_ble16(b, value, false)
#define rz_buf_write_le32(b, value) rz_buf_write_ble32(b, value, false)
#define rz_buf_write_le64(b, value) rz_buf_write_ble64(b, value, false)
#define rz_buf_write_le16_at(b, addr, value) rz_buf_write_ble16_at(b, addr, value, false)
#define rz_buf_write_le32_at(b, addr, value) rz_buf_write_ble32_at(b, addr, value, false)
#define rz_buf_write_le64_at(b, addr, value) rz_buf_write_ble64_at(b, addr, value, false)
#define rz_buf_write8_offset(b, offset, value) rz_buf_write_ble8_offset(b, offset, value, false)
#define rz_buf_write_le16_offset(b, offset, value) rz_buf_write_ble16_offset(b, offset, value, false)
#define rz_buf_write_le32_offset(b, offset, value) rz_buf_write_ble32_offset(b, offset, value, false)
#define rz_buf_write_le64_offset(b, offset, value) rz_buf_write_ble64_offset(b, offset, value, false)
#define rz_buf_write_be16(b, value) rz_buf_write_ble16(b, value, true)
#define rz_buf_write_be32(b, value) rz_buf_write_ble32(b, value, true)
#define rz_buf_write_be64(b, value) rz_buf_write_ble64(b, value, true)
#define rz_buf_write_be16_at(b, addr, value) rz_buf_write_ble16_at(b, addr, value, true)
#define rz_buf_write_be32_at(b, addr, value) rz_buf_write_ble32_at(b, addr, value, true)
#define rz_buf_write_be64_at(b, addr, value) rz_buf_write_ble64_at(b, addr, value, true)
#define rz_buf_write_be16_offset(b, offset, value) rz_buf_write_ble16_offset(b, offset, value, true)
#define rz_buf_write_be32_offset(b, offset, value) rz_buf_write_ble32_offset(b, offset, value, true)
#define rz_buf_write_be64_offset(b, offset, value) rz_buf_write_ble64_offset(b, offset, value, true)
#undef DEFINE_RZ_BUF_READ_BLE
#undef DEFINE_RZ_BUF_WRITE_BLE
#undef DEFINE_RZ_BUF_READ_OFFSET_BLE
#undef DEFINE_RZ_BUF_WRITE_OFFSET_BLE
/**
* \brief Peeks at the next byte in the buffer without modify the buffer position.
*
* It assumes that the buffer contains at least one byte beyond the current position.
* otherwise, the behavior of the function is undefined.
* \param b The buffer
* \return The byte value
*/
static inline ut8 rz_buf_peek(RZ_NONNULL RzBuffer *b) {
ut8 x = 0;
rz_buf_read8_at(b, rz_buf_tell(b), &x);
return x;
}
// sparse-specific
RZ_API const RzBufferSparseChunk *rz_buf_sparse_get_chunks(RzBuffer *b, RZ_NONNULL size_t *count);
RZ_API void rz_buf_sparse_set_write_mode(RzBuffer *b, RzBufferSparseWriteMode mode);
RZ_API bool rz_buf_sparse_populated_in(RzBuffer *b, ut64 from, ut64 to);
RZ_API bool rz_deflatew_buf(RZ_NONNULL RzBuffer *src, RZ_NONNULL RzBuffer *dst, ut64 block_size, ut8 *src_consumed, int wbits);
RZ_API bool rz_deflate_buf(RZ_NONNULL RzBuffer *src, RZ_NONNULL RzBuffer *dst, ut64 block_size, ut8 *src_consumed);
RZ_API bool rz_inflatew_buf(RZ_NONNULL RzBuffer *src, RZ_NONNULL RzBuffer *dst, ut64 block_size, ut8 *src_consumed, int wbits);
RZ_API bool rz_inflate_buf(RZ_NONNULL RzBuffer *src, RZ_NONNULL RzBuffer *dst, ut64 block_size, ut8 *src_consumed);
RZ_API bool rz_lzma_dec_buf(RZ_NONNULL RzBuffer *src, RZ_NONNULL RzBuffer *dst, ut64 block_size, ut8 *src_consumed);
RZ_API bool rz_lzma_enc_buf(RZ_NONNULL RzBuffer *src, RZ_NONNULL RzBuffer *dst, ut64 block_size, ut8 *src_consumed);
#ifdef __cplusplus
}
#endif
#endif // RZ_BUF_H