InStream/OutStream
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
#include <c_BinaryInStream.h>
|
||||
#include "c_Float.h"
|
||||
|
||||
c_err_t c_BinaryInStream_Init(c_BinaryInStream_t* self, c_InStream_t* underlying_io_port) {
|
||||
if (!self || !underlying_io_port) return C_ERR_PARAM;
|
||||
self->base_io = underlying_io_port;
|
||||
self->buffer = 0;
|
||||
self->n = 0;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadBits(c_BinaryInStream_t* self, int bit_count, uint32_t* out_value) {
|
||||
if (!self || !self->base_io || !out_value || bit_count < 1 || bit_count > 32) return C_ERR_PARAM;
|
||||
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < bit_count; ++i) {
|
||||
if (self->n == 0) {
|
||||
unsigned char next_byte = 0;
|
||||
c_size_t read_bytes = 0;
|
||||
c_err_t err = c_InStream_Read(self->base_io, &next_byte, 1, &read_bytes);
|
||||
if (err != C_SUCCESS && err != C_ERR_OUTOFBOUND) return err;
|
||||
if (read_bytes == 0) return C_ERR_OUTOFBOUND;
|
||||
|
||||
self->buffer = next_byte;
|
||||
self->n = 8;
|
||||
}
|
||||
int bit = (self->buffer >> (self->n - 1)) & 1;
|
||||
self->n--;
|
||||
result = (result << 1) | bit;
|
||||
}
|
||||
*out_value = result;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadBit(c_BinaryInStream_t* self, int* out_bit) {
|
||||
if (!out_bit) return C_ERR_PARAM;
|
||||
uint32_t val = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadBits(self, 1, &val);
|
||||
if (err == C_SUCCESS) *out_bit = (int)val;
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadByte(c_BinaryInStream_t* self, int* out_byte) {
|
||||
if (!out_byte) return C_ERR_PARAM;
|
||||
uint32_t val = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadBits(self, 8, &val);
|
||||
if (err == C_SUCCESS) *out_byte = (int)val;
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadChar(c_BinaryInStream_t* self, char* out_char) {
|
||||
if (!out_char) return C_ERR_PARAM;
|
||||
uint32_t val = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadBits(self, 8, &val);
|
||||
if (err == C_SUCCESS) *out_char = (char)val;
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadUInt16(c_BinaryInStream_t* self, uint16_t* out_x) {
|
||||
if (!out_x) return C_ERR_PARAM;
|
||||
uint32_t val = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadBits(self, 16, &val);
|
||||
if (err == C_SUCCESS) *out_x = (uint16_t)val;
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadUInt32(c_BinaryInStream_t* self, uint32_t* out_x) {
|
||||
return c_BinaryInStream_ReadBits(self, 32, out_x);
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadUInt64(c_BinaryInStream_t* self, uint64_t* out_x) {
|
||||
if (!out_x) return C_ERR_PARAM;
|
||||
uint32_t high = 0, low = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadBits(self, 32, &high);
|
||||
if (err != C_SUCCESS) return err;
|
||||
err = c_BinaryInStream_ReadBits(self, 32, &low);
|
||||
if (err != C_SUCCESS) return err;
|
||||
*out_x = ((uint64_t)high << 32) | low;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadFloat(c_BinaryInStream_t* self, float* out_x) {
|
||||
if (!out_x) return C_ERR_PARAM;
|
||||
uint32_t bits = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadBits(self, 32, &bits);
|
||||
if (err == C_SUCCESS) *out_x = c_Float_FromBits(bits);
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadDouble(c_BinaryInStream_t* self, double* out_x) {
|
||||
if (!out_x) return C_ERR_PARAM;
|
||||
uint64_t bits = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadUInt64(self, &bits);
|
||||
if (err == C_SUCCESS) *out_x = c_Double_FromBits(bits);
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryInStream_ReadString(c_BinaryInStream_t* self, char* out_buffer, c_size_t buffer_cap) {
|
||||
if (!self || !out_buffer || buffer_cap == 0) return C_ERR_PARAM;
|
||||
|
||||
c_size_t idx = 0;
|
||||
while (idx < buffer_cap - 1) {
|
||||
char ch = 0;
|
||||
c_err_t err = c_BinaryInStream_ReadChar(self, &ch);
|
||||
if (err != C_SUCCESS) {
|
||||
out_buffer[idx] = '\0';
|
||||
return err;
|
||||
}
|
||||
|
||||
out_buffer[idx++] = ch;
|
||||
if (ch == '\0') return C_SUCCESS; /* Completed extraction sequence safely */
|
||||
}
|
||||
|
||||
out_buffer[idx] = '\0'; /* Force sentinel safety ceiling string termination */
|
||||
return C_ERR_OUTOFBOUND;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef INCLUDED_C_BINARYINSTREAM_H
|
||||
#define INCLUDED_C_BINARYINSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_INSTREAM_H
|
||||
#include <c_InStream.h>
|
||||
#endif /*INCLUDED_C_INSTREAM_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
typedef struct {
|
||||
c_InStream_t* base_io; /* Underlying core polymorphically bound I/O driver ports backend link */
|
||||
int buffer; /* 8-bit cache buffer of bits read in */
|
||||
int n; /* Number of bits currently remaining active/unread in buffer */
|
||||
} c_BinaryInStream_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BinaryInStream_Init(c_BinaryInStream_t* self, c_InStream_t* underlying_io_port);
|
||||
|
||||
c_err_t c_BinaryInStream_ReadBits(c_BinaryInStream_t* self, int bit_count, uint32_t* out_value);
|
||||
c_err_t c_BinaryInStream_ReadBit(c_BinaryInStream_t* self, int* out_bit);
|
||||
c_err_t c_BinaryInStream_ReadByte(c_BinaryInStream_t* self, int* out_byte);
|
||||
c_err_t c_BinaryInStream_ReadChar(c_BinaryInStream_t* self, char* out_char);
|
||||
c_err_t c_BinaryInStream_ReadUInt16(c_BinaryInStream_t* self, uint16_t* out_x);
|
||||
c_err_t c_BinaryInStream_ReadUInt32(c_BinaryInStream_t* self, uint32_t* out_x);
|
||||
c_err_t c_BinaryInStream_ReadUInt64(c_BinaryInStream_t* self, uint64_t* out_x);
|
||||
c_err_t c_BinaryInStream_ReadFloat(c_BinaryInStream_t* self, float* out_x);
|
||||
c_err_t c_BinaryInStream_ReadDouble(c_BinaryInStream_t* self, double* out_x);
|
||||
|
||||
/**
|
||||
* @brief Reads a null-terminated string out from the binary stream.
|
||||
* @param buffer_cap Maximum threshold size limit matching out_buffer allocation bounds to prevent overflows.
|
||||
*/
|
||||
c_err_t c_BinaryInStream_ReadString(c_BinaryInStream_t* self, char* out_buffer, c_size_t buffer_cap);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_BINARYINSTREAM_H*/
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "c_Test.h"
|
||||
#include "c_InStream.h"
|
||||
#include "c_BinaryInStream.h"
|
||||
#include <math.h>
|
||||
#include "c_BufferInputStream.h"
|
||||
|
||||
TEST_CASE(test_polymorphic_binary_in_stream_pipeline) {
|
||||
/*
|
||||
* Mock payload data layout block generated by a valid serialization pass:
|
||||
* - Bit 1: 1
|
||||
* - Bits 2-5: 0x0A (4 bits)
|
||||
* - Byte: 0xFE (8 bits)
|
||||
* - Char: 'C' (8 bits)
|
||||
* - UInt16: 0xABCD (16 bits)
|
||||
* - UInt32: 0x12345678 (32 bits)
|
||||
* - UInt64: 0x1122334455667788ULL
|
||||
* - Float: -2.0f (32 bits)
|
||||
* - Double: 3.1415926535
|
||||
* - String: "DAG\0" (32 bits)
|
||||
* Explicit byte sequence block array footprint:
|
||||
*/
|
||||
unsigned char mock_raw_payload[] = {
|
||||
0xD7, 0xF2, 0x1D, 0x5E, 0x68, 0x91, 0xA2, 0xB3,
|
||||
0xC0, 0x89, 0x11, 0x9A, 0x22, 0xAB, 0x33, 0xBC,
|
||||
0x46, 0x00, 0x00, 0x00, 0x02, 0x00, 0x49, 0x0F,
|
||||
0xDA, 0xA2, 0x08, 0xBA, 0x22, 0x22, 0x0A, 0x38,
|
||||
};
|
||||
c_size_t payload_len = sizeof(mock_raw_payload);
|
||||
|
||||
c_InStream_t* mem_input_stream = NULL;
|
||||
/* 1. Spin up the concrete memory buffer reader subclass instance */
|
||||
c_err_t err = c_BufferInputStream_Create(&mem_input_stream, mock_raw_payload, payload_len, NULL);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* 2. Bind the unpacking adapter pipeline over the polymorphic interface */
|
||||
c_BinaryInStream_t bin_in;
|
||||
err = c_BinaryInStream_Init(&bin_in, mem_input_stream);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* 3. Execute extraction queries sequentially and assert exact matches */
|
||||
int bit_val = 0;
|
||||
err = c_BinaryInStream_ReadBit(&bin_in, &bit_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_INT_EQ(1, bit_val);
|
||||
|
||||
uint32_t bits_val = 0;
|
||||
err = c_BinaryInStream_ReadBits(&bin_in, 4, &bits_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_LL_EQ(0x0A, bits_val);
|
||||
|
||||
int byte_val = 0;
|
||||
err = c_BinaryInStream_ReadByte(&bin_in, &byte_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_INT_EQ(0xFE, byte_val);
|
||||
|
||||
char char_val = 0;
|
||||
err = c_BinaryInStream_ReadChar(&bin_in, &char_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_INT_EQ('C', char_val);
|
||||
|
||||
uint16_t u16_val = 0;
|
||||
err = c_BinaryInStream_ReadUInt16(&bin_in, &u16_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_INT_EQ(0xABCD, u16_val);
|
||||
|
||||
uint32_t u32_val = 0;
|
||||
err = c_BinaryInStream_ReadUInt32(&bin_in, &u32_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_LL_EQ(0x12345678, u32_val);
|
||||
|
||||
uint64_t u64_val = 0;
|
||||
err = c_BinaryInStream_ReadUInt64(&bin_in, &u64_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_INT_EQ(0x1122334455667788ULL, u64_val);
|
||||
|
||||
float f32_val = 0.0f;
|
||||
err = c_BinaryInStream_ReadFloat(&bin_in, &f32_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_DOUBLE_EQ_MSG(-2.0, (double)f32_val, "Polymorphic ReadFloat alignment precision failed");
|
||||
|
||||
double d64_val = 0.0;
|
||||
err = c_BinaryInStream_ReadDouble(&bin_in, &d64_val);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_DOUBLE_EQ_MSG(3.1415926535, d64_val, "Polymorphic ReadDouble alignment precision failed");
|
||||
|
||||
char string_buf[8]={0};
|
||||
err = c_BinaryInStream_ReadString(&bin_in, string_buf, sizeof(string_buf));
|
||||
ASSERT_INT_EQ(C_ERR_OUTOFBOUND, err);
|
||||
ASSERT_INT_EQ(0, strcmp("DAG", string_buf));
|
||||
|
||||
/* 4. Tear down infrastructure safely via the abstraction layer handle */
|
||||
c_InStream_Destroy(mem_input_stream);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
TEST_START(Polymorphic_InStream_Pipeline_Suite);
|
||||
RUN_TEST(test_polymorphic_binary_in_stream_pipeline);
|
||||
TEST_REPORT();
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
#include <c_BinaryOutStream.h>
|
||||
|
||||
#include "c_Float.h"
|
||||
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_err_t c_BinaryOutStream_ClearBuffer(c_BinaryOutStream_t* self) {
|
||||
if (!self || self->n<0 || !self->base_io) return C_ERR_PARAM;
|
||||
if (self->n==0) return C_ERR_OK;
|
||||
unsigned char final_byte = (unsigned char)(self->buffer << (8 - self->n));
|
||||
c_size_t written = 0;
|
||||
c_err_t err= c_OutStream_Write(self->base_io, &final_byte, 1, &written);
|
||||
if (err==C_ERR_OK) {
|
||||
self->buffer = 0;
|
||||
self->n = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
c_err_t c_BinaryOutStream_Init(c_BinaryOutStream_t* self, c_OutStream_t* underlying_io_port) {
|
||||
if (!self || !underlying_io_port) return C_ERR_PARAM;
|
||||
self->base_io = underlying_io_port;
|
||||
self->buffer = 0;
|
||||
self->n = 0;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_Flush(c_BinaryOutStream_t* self) {
|
||||
if (!self || !self->base_io) return C_ERR_PARAM;
|
||||
if (self->n == 0) return C_SUCCESS;
|
||||
unsigned char final_byte = (unsigned char)(self->buffer << (8 - self->n));
|
||||
c_size_t written = 0;
|
||||
c_err_t err = c_OutStream_Write(self->base_io, &final_byte, 1, &written);
|
||||
if (err!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
self->buffer = 0;
|
||||
self->n = 0;
|
||||
return c_OutStream_Flush(self->base_io);
|
||||
}
|
||||
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteBit(c_BinaryOutStream_t* self, int x) {
|
||||
if (!self || (x!=0 && x!=1)) return C_ERR_PARAM;
|
||||
|
||||
self->buffer <<=1;
|
||||
if (x) self->buffer|=1;
|
||||
self->n++;
|
||||
if (self->n==8) {
|
||||
c_BinaryOutStream_ClearBuffer(self);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteByte(c_BinaryOutStream_t* self, int x) {
|
||||
if (!self || (x<0 || x>255)) return C_ERR_PARAM;
|
||||
if (self->n ==0) {
|
||||
c_size_t written = 0;
|
||||
c_err_t err= c_OutStream_Write(self->base_io, &x, 1, &written);
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t err = C_ERR_OK;
|
||||
for (int i=0; i<8; i++) {
|
||||
int bit = ((x >> (8-i-1)) & 1) == 1;
|
||||
if ((err=c_BinaryOutStream_WriteBit(self, bit))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteBits(c_BinaryOutStream_t* self, uint32_t x, int bit_count) {
|
||||
if (!self || !self->base_io || bit_count < 1 || bit_count > 32) return C_ERR_PARAM;
|
||||
if (x >= (1<<bit_count)) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
c_err_t err = C_ERR_OK;
|
||||
for (int i=0; i<bit_count; i++) {
|
||||
int bit = (int)((x >> (bit_count - i - 1)) & 1);
|
||||
if ((err = c_BinaryOutStream_WriteBit(self, bit))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteChar(c_BinaryOutStream_t* self, char x) {
|
||||
if (!self || !self->base_io ) return C_ERR_PARAM;
|
||||
return c_BinaryOutStream_WriteByte(self, x);
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteUInt16(c_BinaryOutStream_t* self, uint16_t x) {
|
||||
if (!self || !self->base_io) return C_ERR_PARAM;
|
||||
c_err_t err = C_ERR_OK;
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 8) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 0) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteUInt32(c_BinaryOutStream_t* self, uint32_t x) {
|
||||
if (!self || !self->base_io) return C_ERR_PARAM;
|
||||
c_err_t err = C_ERR_OK;
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 24) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 16) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 8) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 0) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteUInt64(c_BinaryOutStream_t* self, uint64_t x) {
|
||||
if (!self || !self->base_io) return C_ERR_PARAM;
|
||||
c_err_t err = C_ERR_OK;
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 56) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 48) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 40) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 32) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 24) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 16) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 8) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = c_BinaryOutStream_WriteByte(self, (x >> 0) & 0xff))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteFloat(c_BinaryOutStream_t* self, float x) {
|
||||
uint32_t u32_val = c_Float_ToBits(x);
|
||||
return c_BinaryOutStream_WriteUInt32(self, u32_val);
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteDouble(c_BinaryOutStream_t* self, double x) {
|
||||
uint64_t u64_val = c_Double_ToBits(x);
|
||||
return c_BinaryOutStream_WriteUInt64(self, u64_val);
|
||||
}
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteString(c_BinaryOutStream_t* self, const char* s) {
|
||||
if (!self || !self->base_io || !s) return C_ERR_PARAM;
|
||||
c_size_t s_len = strlen(s);
|
||||
c_err_t err = C_ERR_OK;
|
||||
for (int i=0; i<s_len; i++) {
|
||||
if ((err=c_BinaryOutStream_WriteChar(self, s[i]))!=C_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_C_BINARYOUTSTREAM_H
|
||||
#define INCLUDED_C_BINARYOUTSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_OUTSTREAM_H
|
||||
#include <c_OutStream.h>
|
||||
#endif /*INCLUDED_C_OUTSTREAM_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_OutStream_t* base_io; /* Underlying core polymorphically bound I/O driver ports backend link */
|
||||
int buffer; /* 8-bit buffer of bits to write out */
|
||||
int n; /* Number of bits currently remaining active in buffer */
|
||||
} c_BinaryOutStream_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BinaryOutStream_Init(c_BinaryOutStream_t* self, c_OutStream_t* underlying_io_port);
|
||||
c_err_t c_BinaryOutStream_WriteBits(c_BinaryOutStream_t* self, uint32_t value, int bit_count);
|
||||
c_err_t c_BinaryOutStream_Flush(c_BinaryOutStream_t* self);
|
||||
|
||||
c_err_t c_BinaryOutStream_WriteBit(c_BinaryOutStream_t* self, int x);
|
||||
c_err_t c_BinaryOutStream_WriteByte(c_BinaryOutStream_t* self, int x);
|
||||
c_err_t c_BinaryOutStream_WriteChar(c_BinaryOutStream_t* self, char x);
|
||||
c_err_t c_BinaryOutStream_WriteUInt16(c_BinaryOutStream_t* self, uint16_t x);
|
||||
c_err_t c_BinaryOutStream_WriteUInt32(c_BinaryOutStream_t* self, uint32_t x);
|
||||
c_err_t c_BinaryOutStream_WriteUInt64(c_BinaryOutStream_t* self, uint64_t x);
|
||||
c_err_t c_BinaryOutStream_WriteFloat(c_BinaryOutStream_t* self, float x);
|
||||
c_err_t c_BinaryOutStream_WriteDouble(c_BinaryOutStream_t* self, double x);
|
||||
c_err_t c_BinaryOutStream_WriteString(c_BinaryOutStream_t* self, const char* s);
|
||||
|
||||
#endif /*INCLUDED_C_BINARYOUTSTREAM_H*/
|
||||
@@ -0,0 +1,131 @@
|
||||
#include "c_BinaryOutStream.h"
|
||||
#include "c_BufferOutputStream.h"
|
||||
#include "c_Float.h"
|
||||
#include "c_Test.h"
|
||||
#include "c_Hex.h"
|
||||
|
||||
typedef struct {
|
||||
const unsigned char* data;
|
||||
c_size_t size;
|
||||
c_size_t byte_ptr;
|
||||
int buffer;
|
||||
int n;
|
||||
} c_BinaryInHelper_t;
|
||||
|
||||
static inline void c_BinaryInHelper_Init(c_BinaryInHelper_t* self, const void* data, c_size_t size) {
|
||||
self->data = (const unsigned char*)data;
|
||||
self->size = size;
|
||||
self->byte_ptr = 0;
|
||||
self->buffer = 0;
|
||||
self->n = 0;
|
||||
}
|
||||
|
||||
static inline uint32_t c_BinaryInHelper_ReadBits(c_BinaryInHelper_t* self, int bit_count) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < bit_count; ++i) {
|
||||
if (self->n == 0) {
|
||||
self->buffer = self->data[self->byte_ptr++];
|
||||
self->n = 8;
|
||||
}
|
||||
int bit = (self->buffer >> (self->n - 1)) & 1;
|
||||
self->n--;
|
||||
result = (result << 1) | bit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE(test_binary_out_stream_extended_apis) {
|
||||
c_OutStream_t* mem_stream = NULL;
|
||||
|
||||
/* 1. Spin up an in-memory dynamic serialization stream subclass */
|
||||
c_err_t err = c_BufferOutputStream_Create(&mem_stream, 32, NULL);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* 2. Bind the expanded binary bit-packer onto the abstract handle */
|
||||
c_BinaryOutStream_t bin_out;
|
||||
err = c_BinaryOutStream_Init(&bin_out, mem_stream);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* 3. Serialize a mixture of unaligned bits and primitive types */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteBit(&bin_out, 1)); /* 1 bit */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteBits(&bin_out, 0x0A, 4)); /* 4 bits */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteByte(&bin_out, 0xFE)); /* 8 bits */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteChar(&bin_out, 'C')); /* 8 bits */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteUInt16(&bin_out, 0xABCD)); /* 16 bits */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteUInt32(&bin_out, 0x12345678)); /* 32 bits */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteUInt64(&bin_out, 0x1122334455667788ULL)); /* 64 bits */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteFloat(&bin_out, -2.0f)); /* 32 bits (IEEE 754) */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteDouble(&bin_out, 3.1415926535)); /* 64 bits (IEEE 754) */
|
||||
ASSERT_INT_EQ(C_SUCCESS, c_BinaryOutStream_WriteString(&bin_out, "DAG")); /* 'D','A','G','\0' bytes */
|
||||
|
||||
/* Flush out the remaining bit cache down into the underlying memory stream buffer */
|
||||
err = c_BinaryOutStream_Flush(&bin_out);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* 4. Intercept the written raw payload buffer to verify bit-level precision */
|
||||
const void* raw_data = NULL;
|
||||
c_size_t total_bytes = 0;
|
||||
err = c_BufferOutputStream_GetBuffer(mem_stream, &raw_data, &total_bytes);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
ASSERT_TRUE(total_bytes > 0);
|
||||
|
||||
c_Hex_Dump(raw_data, total_bytes);
|
||||
|
||||
/* 5. Initialize our bit-level reader helper over the raw payload */
|
||||
c_BinaryInHelper_t bin_in;
|
||||
c_BinaryInHelper_Init(&bin_in, raw_data, total_bytes);
|
||||
|
||||
/* Verify unaligned bit configurations */
|
||||
ASSERT_LL_EQ(1, c_BinaryInHelper_ReadBits(&bin_in, 1));
|
||||
ASSERT_LL_EQ(0x0A, c_BinaryInHelper_ReadBits(&bin_in, 4));
|
||||
|
||||
/* Verify byte-aligned primitives */
|
||||
ASSERT_LL_EQ(0xFE, c_BinaryInHelper_ReadBits(&bin_in, 8));
|
||||
ASSERT_LL_EQ('C', c_BinaryInHelper_ReadBits(&bin_in, 8));
|
||||
ASSERT_LL_EQ(0xABCD, c_BinaryInHelper_ReadBits(&bin_in, 16));
|
||||
ASSERT_LL_EQ(0x12345678, c_BinaryInHelper_ReadBits(&bin_in, 32));
|
||||
|
||||
/* Reconstruct 64-bit unsigned integer splits */
|
||||
uint64_t actual_u64 = ((uint64_t)c_BinaryInHelper_ReadBits(&bin_in, 32) << 32) | c_BinaryInHelper_ReadBits(&bin_in, 32);
|
||||
ASSERT_LL_EQ(0x1122334455667788ULL, actual_u64);
|
||||
|
||||
/* Verify IEEE 754 Single-Precision Float using your bit-casting utilities */
|
||||
uint32_t float_bits = c_BinaryInHelper_ReadBits(&bin_in, 32);
|
||||
c_float_t actual_float = c_Float_FromBits(float_bits);
|
||||
ASSERT_DOUBLE_EQ_MSG(-2.0, (double)actual_float, "Float stream serialization precision failed");
|
||||
|
||||
/* Verify IEEE 754 Double-Precision Float */
|
||||
uint64_t double_bits = ((uint64_t)c_BinaryInHelper_ReadBits(&bin_in, 32) << 32) | c_BinaryInHelper_ReadBits(&bin_in, 32);
|
||||
c_double_t actual_double = c_Double_FromBits(double_bits);
|
||||
/* Float comparisons use your Epsilon tolerances helper pattern */
|
||||
ASSERT_TRUE(fabs(actual_double - 3.1415926535) <= 1e-9);
|
||||
|
||||
/* Verify null-terminated String extraction sequence loops */
|
||||
char actual_str[4];
|
||||
actual_str[0] = (char)c_BinaryInHelper_ReadBits(&bin_in, 8);
|
||||
actual_str[1] = (char)c_BinaryInHelper_ReadBits(&bin_in, 8);
|
||||
actual_str[2] = (char)c_BinaryInHelper_ReadBits(&bin_in, 8);
|
||||
actual_str[3] = (char)c_BinaryInHelper_ReadBits(&bin_in, 8); /* Should read the '\0' delimiter */
|
||||
|
||||
ASSERT_INT_EQ(0, strcmp("DAG", actual_str));
|
||||
ASSERT_INT_EQ('\0', actual_str[3]);
|
||||
|
||||
/* 6. Clean up polymorphic dynamic stream via virtual tables */
|
||||
c_OutStream_Destroy(mem_stream);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
/* Fire up the core testing wrapper suite */
|
||||
TEST_START(BinaryOutStream_ExtendedAPIs_Suite);
|
||||
|
||||
/* Run the specified data stream pipeline integration test */
|
||||
RUN_TEST(test_binary_out_stream_extended_apis);
|
||||
|
||||
/* Output summary metrics logs to console */
|
||||
TEST_REPORT();
|
||||
|
||||
/* Unwind back with proper testing suite status codes */
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <c_BufferInputStream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
c_InStream_t base;
|
||||
const char* data;
|
||||
c_size_t size;
|
||||
c_size_t ptr;
|
||||
c_Allocator_t allocator;
|
||||
} c_BufferInImpl_t;
|
||||
|
||||
static c_err_t _BufferIn_Read(c_InStream_t* self, void* buf, c_size_t len, c_size_t* bytes_read) {
|
||||
c_BufferInImpl_t* impl = (c_BufferInImpl_t*)self;
|
||||
if (!buf || len == 0) return C_ERR_PARAM;
|
||||
|
||||
if (impl->ptr >= impl->size) {
|
||||
if (bytes_read) *bytes_read = 0;
|
||||
return C_ERR_OUTOFBOUND;
|
||||
}
|
||||
|
||||
c_size_t available = impl->size - impl->ptr;
|
||||
c_size_t to_copy = (len < available) ? len : available;
|
||||
|
||||
memcpy(buf, impl->data + impl->ptr, to_copy);
|
||||
impl->ptr += to_copy;
|
||||
if (bytes_read) *bytes_read = to_copy;
|
||||
|
||||
return (to_copy == len) ? C_SUCCESS : C_ERR_OUTOFBOUND;
|
||||
}
|
||||
|
||||
static void _BufferIn_Destroy(c_InStream_t* self) {
|
||||
c_BufferInImpl_t* impl = (c_BufferInImpl_t*)self;
|
||||
c_Allocator_t alloc = impl->allocator;
|
||||
c_Allocator_Free(&alloc, impl);
|
||||
}
|
||||
|
||||
static const c_InStreamVtbl_t g_BufferInVtbl = { _BufferIn_Read, _BufferIn_Destroy };
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BufferInputStream_Create(c_InStream_t** out_stream, const void* buffer_ptr, c_size_t buffer_len, c_Allocator_t* allocator) {
|
||||
if (!out_stream || !buffer_ptr || buffer_len == 0) return C_ERR_PARAM;
|
||||
c_Allocator_t alloc = allocator ? *allocator : c_DefaultAllocator;
|
||||
c_BufferInImpl_t* impl = (c_BufferInImpl_t*)c_Allocator_Calloc(&alloc, 1, sizeof(c_BufferInImpl_t));
|
||||
if (!impl) return C_ERR_NOMEM;
|
||||
|
||||
impl->base.vtbl = &g_BufferInVtbl;
|
||||
impl->data = (const char*)buffer_ptr;
|
||||
impl->size = buffer_len;
|
||||
impl->ptr = 0;
|
||||
impl->allocator = alloc;
|
||||
*out_stream = &impl->base;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef INCLUDED_C_BUFFERINPUTSTREAM_H
|
||||
#define INCLUDED_C_BUFFERINPUTSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_INSTREAM_H
|
||||
#include <c_InStream.h>
|
||||
#endif /*INCLUDED_C_INSTREAM_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Instantiates an in-memory buffer Stream reader.
|
||||
* @note This implementation performs a shallow copy of the reference pointer, or handles data depending on arguments.
|
||||
*/
|
||||
c_err_t c_BufferInputStream_Create(c_InStream_t** out_stream, const void* buffer_ptr, c_size_t buffer_len, c_Allocator_t* allocator);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_BUFFERINPUTSTREAM_H*/
|
||||
@@ -0,0 +1,60 @@
|
||||
#include <c_BufferOutputStream.h>
|
||||
|
||||
typedef struct {
|
||||
c_OutStream_t base;
|
||||
char* data;
|
||||
c_size_t capacity;
|
||||
c_size_t size;
|
||||
c_Allocator_t allocator;
|
||||
} c_BufferOutImpl_t;
|
||||
|
||||
static c_err_t _BufferOut_Write(c_OutStream_t* self, const void* buf, c_size_t len, c_size_t* written) {
|
||||
c_BufferOutImpl_t* impl = (c_BufferOutImpl_t*)self;
|
||||
if (impl->size + len > impl->capacity) {
|
||||
c_size_t new_cap = impl->capacity == 0 ? 16 : impl->capacity * 2;
|
||||
while (impl->size + len > new_cap) new_cap *= 2;
|
||||
char* new_data = (char*)c_Allocator_Realloc(&impl->allocator, impl->data, impl->capacity, new_cap);
|
||||
if (!new_data) return C_ERR_NOMEM;
|
||||
impl->data = new_data;
|
||||
impl->capacity = new_cap;
|
||||
}
|
||||
memcpy(impl->data + impl->size, buf, len);
|
||||
impl->size += len;
|
||||
if (written) *written = len;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
static c_err_t _BufferOut_Flush(c_OutStream_t* self) { (void)self; return C_SUCCESS; }
|
||||
|
||||
static void _BufferOut_Destroy(c_OutStream_t* self) {
|
||||
c_BufferOutImpl_t* impl = (c_BufferOutImpl_t*)self;
|
||||
c_Allocator_t alloc = impl->allocator;
|
||||
if (impl->data) c_Allocator_Free(&alloc, impl->data);
|
||||
c_Allocator_Free(&alloc, impl);
|
||||
}
|
||||
|
||||
static const c_OutStreamVtbl_t g_BufferOutVtbl = { _BufferOut_Write, _BufferOut_Flush, _BufferOut_Destroy };
|
||||
|
||||
c_err_t c_BufferOutputStream_Create(c_OutStream_t** out_stream, c_size_t initial_capacity, c_Allocator_t* allocator) {
|
||||
if (!out_stream) return C_ERR_PARAM;
|
||||
c_Allocator_t alloc = allocator ? *allocator : c_DefaultAllocator;
|
||||
c_BufferOutImpl_t* impl = (c_BufferOutImpl_t*)c_Allocator_Calloc(&alloc, 1, sizeof(c_BufferOutImpl_t));
|
||||
if (!impl) return C_ERR_NOMEM;
|
||||
impl->base.vtbl = &g_BufferOutVtbl;
|
||||
impl->allocator = alloc;
|
||||
if (initial_capacity > 0) {
|
||||
impl->data = (char*)c_Allocator_Calloc(&impl->allocator, initial_capacity, sizeof(char));
|
||||
if (!impl->data) { c_Allocator_Free(&alloc, impl); return C_ERR_NOMEM; }
|
||||
impl->capacity = initial_capacity;
|
||||
}
|
||||
*out_stream = &impl->base;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
c_err_t c_BufferOutputStream_GetBuffer(c_OutStream_t* self, const void** out_ptr, c_size_t* out_len) {
|
||||
if (!self || self->vtbl != &g_BufferOutVtbl || !out_ptr || !out_len) return C_ERR_PARAM;
|
||||
c_BufferOutImpl_t* impl = (c_BufferOutImpl_t*)self;
|
||||
*out_ptr = impl->data;
|
||||
*out_len = impl->size;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef INCLUDED_C_BUFFEROUTPUTSTREAM_H
|
||||
#define INCLUDED_C_BUFFEROUTPUTSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_OUTSTREAM_H
|
||||
#include <c_OutStream.h>
|
||||
#endif /*INCLUDED_C_OUTSTREAM_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* @brief Instantiates an in-memory dynamic rolling buffer Stream writer.
|
||||
*/
|
||||
c_err_t c_BufferOutputStream_Create(c_OutStream_t** out_stream, c_size_t initial_capacity, c_Allocator_t* allocator);
|
||||
|
||||
c_err_t c_BufferOutputStream_GetBuffer(c_OutStream_t* self, const void** out_ptr, c_size_t* out_len);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_BUFFEROUTPUTSTREAM_H*/
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <c_FileInputStream.h>
|
||||
|
||||
typedef struct {
|
||||
c_InStream_t base;
|
||||
FILE* fp;
|
||||
c_Allocator_t allocator;
|
||||
} c_FileInImpl_t;
|
||||
|
||||
static c_err_t _FileIn_Read(c_InStream_t* self, void* buf, c_size_t len, c_size_t* bytes_read) {
|
||||
c_FileInImpl_t* impl = (c_FileInImpl_t*)self;
|
||||
if (!impl->fp || !buf || len == 0) return C_ERR_PARAM;
|
||||
|
||||
c_size_t items = fread(buf, 1, (size_t)len, impl->fp);
|
||||
if (bytes_read) *bytes_read = items;
|
||||
|
||||
if (items == 0 && ferror(impl->fp)) return C_ERR_FAIL;
|
||||
return (items == len) ? C_SUCCESS : C_ERR_OUTOFBOUND;
|
||||
}
|
||||
|
||||
static void _FileIn_Destroy(c_InStream_t* self) {
|
||||
c_FileInImpl_t* impl = (c_FileInImpl_t*)self;
|
||||
c_Allocator_t alloc = impl->allocator;
|
||||
c_Allocator_Free(&alloc, impl);
|
||||
}
|
||||
|
||||
static const c_InStreamVtbl_t g_FileInVtbl = { _FileIn_Read, _FileIn_Destroy };
|
||||
|
||||
c_err_t c_FileInputStream_Create(c_InStream_t** out_stream, FILE* file_handle, c_Allocator_t* allocator) {
|
||||
if (!out_stream || !file_handle) return C_ERR_PARAM;
|
||||
c_Allocator_t alloc = allocator ? *allocator : c_DefaultAllocator;
|
||||
c_FileInImpl_t* impl = (c_FileInImpl_t*)c_Allocator_Calloc(&alloc, 1, sizeof(c_FileInImpl_t));
|
||||
if (!impl) return C_ERR_NOMEM;
|
||||
|
||||
impl->base.vtbl = &g_FileInVtbl;
|
||||
impl->fp = file_handle;
|
||||
impl->allocator = alloc;
|
||||
*out_stream = &impl->base;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_C_FILEINPUTSTREAM_H
|
||||
#define INCLUDED_C_FILEINPUTSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_INSTREAM_H
|
||||
#include <c_InStream.h>
|
||||
#endif /*INCLUDED_C_INSTREAM_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_STDIO_H
|
||||
#define INCLUDED_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif /*INCLUDED_STDIO_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Instantiates a File-bound disk Stream reader.
|
||||
*/
|
||||
c_err_t c_FileInputStream_Create(c_InStream_t** out_stream, FILE* file_handle, c_Allocator_t* allocator);
|
||||
|
||||
#endif /*INCLUDED_C_FILEINPUTSTREAM_H*/
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <c_FileOutputStream.h>
|
||||
|
||||
typedef struct {
|
||||
c_OutStream_t base;
|
||||
FILE* fp;
|
||||
c_Allocator_t allocator;
|
||||
} c_FileOutImpl_t;
|
||||
|
||||
static c_err_t _FileOut_Write(c_OutStream_t* self, const void* buf, c_size_t len, c_size_t* written) {
|
||||
c_FileOutImpl_t* impl = (c_FileOutImpl_t*)self;
|
||||
if (!impl->fp || !buf || len == 0) return C_ERR_PARAM;
|
||||
c_size_t items = fwrite(buf, 1, (size_t)len, impl->fp);
|
||||
if (written) *written = items;
|
||||
return (items == len) ? C_SUCCESS : C_ERR_FAIL;
|
||||
}
|
||||
|
||||
static c_err_t _FileOut_Flush(c_OutStream_t* self) {
|
||||
c_FileOutImpl_t* impl = (c_FileOutImpl_t*)self;
|
||||
return (fflush(impl->fp) == 0) ? C_SUCCESS : C_ERR_FAIL;
|
||||
}
|
||||
|
||||
static void _FileOut_Destroy(c_OutStream_t* self) {
|
||||
c_FileOutImpl_t* impl = (c_FileOutImpl_t*)self;
|
||||
c_Allocator_t alloc = impl->allocator;
|
||||
c_Allocator_Free(&alloc, impl);
|
||||
}
|
||||
|
||||
static const c_OutStreamVtbl_t g_FileOutVtbl = { _FileOut_Write, _FileOut_Flush, _FileOut_Destroy };
|
||||
|
||||
c_err_t c_FileOutputStream_Create(c_OutStream_t** out_stream, FILE* file_handle, c_Allocator_t* allocator) {
|
||||
if (!out_stream || !file_handle) return C_ERR_PARAM;
|
||||
c_Allocator_t alloc = allocator ? *allocator : c_DefaultAllocator;
|
||||
c_FileOutImpl_t* impl = (c_FileOutImpl_t*)c_Allocator_Calloc(&alloc, 1, sizeof(c_FileOutImpl_t));
|
||||
if (!impl) return C_ERR_NOMEM;
|
||||
impl->base.vtbl = &g_FileOutVtbl;
|
||||
impl->fp = file_handle;
|
||||
impl->allocator = alloc;
|
||||
*out_stream = &impl->base;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef INCLUDED_C_FILEOUTPUTSTREAM_H
|
||||
#define INCLUDED_C_FILEOUTPUTSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_OUTSTREAM_H
|
||||
#include <c_OutStream.h>
|
||||
#endif /*INCLUDED_C_OUTSTREAM_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
#ifndef INCLUDED_STDIO_H
|
||||
#define INCLUDED_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif /*INCLUDED_STDIO_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* @brief Instantiates a File-bound disk Stream writer.
|
||||
*/
|
||||
c_err_t c_FileOutputStream_Create(c_OutStream_t** out_stream, FILE* file_handle, c_Allocator_t* allocator);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_FILEOUTPUTSTREAM_H*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_Float.h>
|
||||
@@ -0,0 +1,152 @@
|
||||
#ifndef INCLUDED_C_FLOAT_H
|
||||
#define INCLUDED_C_FLOAT_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_MATH_H
|
||||
#define INCLUDED_MATH_H
|
||||
#include <math.h>
|
||||
#endif /*INCLUDED_MATH_H*/
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/* Complete IEEE 754 Standard Width Type Definition Constraints [1] */
|
||||
typedef float c_float_t;
|
||||
typedef double c_double_t;
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* 32-bit Single Precision IEEE 754 Bitfields Layout [1] */
|
||||
|
||||
typedef struct {
|
||||
uint32_t sign : 1; /* Bit 31: Sign [1] */
|
||||
uint32_t exponent : 8; /* Bits 30-23: Biased Exponent [1] */
|
||||
uint32_t mantissa : 23; /* Bits 22-0: Fractional Significand [1] */
|
||||
} c_IEEE754_Float_Layout_t;
|
||||
|
||||
typedef union {
|
||||
c_float_t value;
|
||||
uint32_t bits;
|
||||
c_IEEE754_Float_Layout_t layout;
|
||||
} c_Float_Cast_t;
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* 64-bit Double Precision IEEE 754 Bitfields Layout [1] */
|
||||
|
||||
typedef struct {
|
||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
uint64_t sign : 1; /* Bit 63 [1] */
|
||||
uint64_t exponent : 11; /* Bits 62-52 [1] */
|
||||
uint64_t mantissa : 52; /* Bits 51-0 [1] */
|
||||
#else
|
||||
/* Little Endian Bitfield Ordering Layout Configuration */
|
||||
uint64_t mantissa : 52; /* Bits 0-51 [1] */
|
||||
uint64_t exponent : 11; /* Bits 52-62 [1] */
|
||||
uint64_t sign : 1; /* Bit 63 [1] */
|
||||
#endif
|
||||
} c_IEEE754_Double_Layout_t;
|
||||
|
||||
typedef union {
|
||||
c_double_t value;
|
||||
uint64_t bits;
|
||||
c_IEEE754_Double_Layout_t layout;
|
||||
} c_Double_Cast_t;
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* Core Inline Bit-Level Inspection Queries */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
uint32_t c_Float_ToBits(c_float_t f) {
|
||||
c_Float_Cast_t cast;
|
||||
cast.value = f;
|
||||
return cast.bits;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_float_t c_Float_FromBits(uint32_t bits) {
|
||||
c_Float_Cast_t cast;
|
||||
cast.bits = bits;
|
||||
return cast.value;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
uint64_t c_Double_ToBits(c_double_t d) {
|
||||
c_Double_Cast_t cast;
|
||||
cast.value = d;
|
||||
return cast.bits;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_double_t c_Double_FromBits(uint64_t bits) {
|
||||
c_Double_Cast_t cast;
|
||||
cast.bits = bits;
|
||||
return cast.value;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
bool c_Float_IsNaN(c_float_t f) {
|
||||
c_Float_Cast_t cast;
|
||||
cast.value = f;
|
||||
/* IEEE 754 rule: Exponent is all 1s, Mantissa is non-zero [1] */
|
||||
return (cast.layout.exponent == 0xFF) && (cast.layout.mantissa != 0);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
bool c_Double_IsNaN(c_double_t d) {
|
||||
c_Double_Cast_t cast;
|
||||
cast.value = d;
|
||||
/* IEEE 754 rule: Exponent is all 1s (0x7FF), Mantissa is non-zero [1] */
|
||||
return (cast.layout.exponent == 0x7FF) && (cast.layout.mantissa != 0);
|
||||
}
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* Generic Callback Comparators (Perfect for Heaps & Sort Engines) */
|
||||
|
||||
/**
|
||||
* @brief Standard generic min-heap comparator for single-precision c_float_t elements.
|
||||
* @return < 0 if a < b, 0 if a == b, > 0 if a > b
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE int c_Compare_FloatMin(const void* a, const void* b, void* args) {
|
||||
c_float_t val_a = *(const c_float_t*)a;
|
||||
c_float_t val_b = *(const c_float_t*)b;
|
||||
(void)args;
|
||||
return (val_a > val_b) - (val_a < val_b); /* Highly optimized branchless pattern */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Standard generic min-heap comparator for double-precision c_double_t elements.
|
||||
* @return < 0 if a < b, 0 if a == b, > 0 if a > b
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE int c_Compare_DoubleMin(const void* a, const void* b, void* args) {
|
||||
c_double_t val_a = *(const c_double_t*)a;
|
||||
c_double_t val_b = *(const c_double_t*)b;
|
||||
(void)args;
|
||||
return (val_a > val_b) - (val_a < val_b);
|
||||
}
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* Epsilon Tolerant Mathematical Assertions */
|
||||
|
||||
/**
|
||||
* @brief Checks if two single-precision floats are practically equal under an epsilon boundary.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE bool c_Float_AlmostEquals(c_float_t a, c_float_t b, c_float_t epsilon) {
|
||||
/* If exactly equal (or both are positive/negative infinity), shortcut out */
|
||||
if (a == b) return true;
|
||||
return fabs(a - b) <= epsilon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if two double-precision floats are practically equal under an epsilon boundary.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE bool c_Double_AlmostEquals(c_double_t a, c_double_t b, c_double_t epsilon) {
|
||||
if (a == b) return true;
|
||||
return fabs(a - b) <= epsilon;
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_FLOAT_H*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_InStream.h>
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef INCLUDED_C_INSTREAM_H
|
||||
#define INCLUDED_C_INSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct c_InStream_t c_InStream_t;
|
||||
|
||||
typedef struct {
|
||||
c_err_t (*read)(c_InStream_t* self, void* buffer, c_size_t bytes_to_read, c_size_t* out_bytes_read);
|
||||
void (*destroy)(c_InStream_t* self);
|
||||
} c_InStreamVtbl_t;
|
||||
|
||||
struct c_InStream_t {
|
||||
const c_InStreamVtbl_t* vtbl; /* Object vtable linkage tracking handle */
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/* Base Public Routing Virtual Methods */
|
||||
C_STATIC_FORCE_INLINE c_err_t c_InStream_Read(c_InStream_t* self, void* buffer, c_size_t bytes_to_read, c_size_t* out_bytes_read) {
|
||||
if (!self || !self->vtbl || !self->vtbl->read) return C_ERR_PARAM;
|
||||
return self->vtbl->read(self, buffer, bytes_to_read, out_bytes_read);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE void c_InStream_Destroy(c_InStream_t* self) {
|
||||
if (!self || !self->vtbl || !self->vtbl->destroy) return;
|
||||
self->vtbl->destroy(self);
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_INSTREAM_H*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_OutStream.h>
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef INCLUDED_C_OUTSTREAM_H
|
||||
#define INCLUDED_C_OUTSTREAM_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct c_OutStream_t c_OutStream_t;
|
||||
|
||||
typedef struct {
|
||||
c_err_t (*write)(c_OutStream_t* self, const void* buffer, c_size_t bytes_to_write, c_size_t* out_bytes_written);
|
||||
c_err_t (*flush)(c_OutStream_t* self);
|
||||
void (*destroy)(c_OutStream_t* self);
|
||||
} c_OutStreamVtbl_t;
|
||||
|
||||
struct c_OutStream_t {
|
||||
const c_OutStreamVtbl_t* vtbl; /* Object vtable tracker handle link */
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/* Base Public Routing Virtual Methods */
|
||||
C_STATIC_FORCE_INLINE c_err_t c_OutStream_Write(c_OutStream_t* self, const void* buffer, c_size_t bytes_to_write, c_size_t* out_bytes_written) {
|
||||
if (!self || !self->vtbl || !self->vtbl->write) return C_ERR_PARAM;
|
||||
return self->vtbl->write(self, buffer, bytes_to_write, out_bytes_written);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE c_err_t c_OutStream_Flush(c_OutStream_t* self) {
|
||||
if (!self || !self->vtbl || !self->vtbl->flush) return C_ERR_PARAM;
|
||||
return self->vtbl->flush(self);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE void c_OutStream_Destroy(c_OutStream_t* self) {
|
||||
if (!self || !self->vtbl || !self->vtbl->destroy) return;
|
||||
self->vtbl->destroy(self);
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_OUTSTREAM_H*/
|
||||
Reference in New Issue
Block a user