diff --git a/.augment-guidelines b/.augment-guidelines
index f3c2fce1d..5f9fd49e5 100644
--- a/.augment-guidelines
+++ b/.augment-guidelines
@@ -162,6 +162,60 @@ code_patterns:
- pattern: "srs_freepa"
description: "Custom macro for freeing arrays - use only when smart pointers are not suitable"
+ naming_conventions:
+ - pattern: "field_naming"
+ description: "MANDATORY - All class and struct fields must end with underscore (_)"
+ usage: |
+ WRONG: Fields without underscore
+ class SrsBuffer {
+ private:
+ char *p;
+ int size;
+ public:
+ bool enabled;
+ };
+
+ CORRECT: Fields with underscore
+ class SrsBuffer {
+ private:
+ char *p_;
+ int size_;
+ public:
+ bool enabled_;
+ };
+ scope: "Applies to ALL fields (private, protected, public) in both classes and structs"
+ exceptions: "Only applies to SRS-defined classes/structs - do NOT change 3rd party code like llhttp"
+ rationale: "Consistent naming convention across SRS codebase for better code readability and maintenance"
+
+ commenting_style:
+ - pattern: "C++ style comments"
+ description: "MANDATORY - Use C++ style comments (//) instead of C style comments (/* */)"
+ usage: |
+ WRONG: C style comments
+ /* This is a comment */
+ /**
+ * Multi-line comment
+ * with multiple lines
+ */
+
+ CORRECT: C++ style comments
+ // This is a comment
+ // Multi-line comment
+ // with multiple lines
+ rationale: "Consistent with SRS codebase style and better for single-line documentation"
+
+ - pattern: "No thread-safety comments"
+ description: "Do NOT describe thread-safety in comments since SRS is a single-threaded application"
+ usage: |
+ WRONG: Mentioning thread-safety
+ // This implementation is thread-safe for single-threaded usage but may
+ // require external synchronization in multi-threaded environments.
+
+ CORRECT: Focus on functionality without thread-safety mentions
+ // This implementation maintains state between calls for consistent
+ // round-robin behavior across multiple selections.
+ rationale: "SRS uses single-threaded coroutine-based architecture, so thread-safety discussions are irrelevant and confusing"
+
error_handling:
- pattern: "srs_error_t"
description: "Custom error handling system - MANDATORY for all functions that return srs_error_t"
diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md
index fadeaca91..e73f301ad 100644
--- a/trunk/doc/CHANGELOG.md
+++ b/trunk/doc/CHANGELOG.md
@@ -7,6 +7,7 @@ The changelog for SRS.
## SRS 7.0 Changelog
+* v7.0, 2025-09-06, Merge [#4478](https://github.com/ossrs/srs/pull/4478): AI: Add more utests for kernel module. v7.0.81 (#4478)
* v7.0, 2025-09-06, Merge [#4475](https://github.com/ossrs/srs/pull/4475): AI: Support anonymous coroutine with code block. v7.0.80 (#4475)
* v7.0, 2025-09-05, Merge [#4474](https://github.com/ossrs/srs/pull/4474): WebRTC: Fix race condition in RTC nack timer callbacks. v7.0.79 (#4474)
* v7.0, 2025-09-04, Merge [#4467](https://github.com/ossrs/srs/pull/4467): WebRTC: Fix NACK recovered packets not being added to receive queue. v7.0.78 (#4467)
diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp
index 4ae494523..fd0686b0a 100644
--- a/trunk/src/app/srs_app_edge.cpp
+++ b/trunk/src/app/srs_app_edge.cpp
@@ -62,7 +62,7 @@ SrsEdgeRtmpUpstream::~SrsEdgeRtmpUpstream()
close();
}
-srs_error_t SrsEdgeRtmpUpstream::connect(ISrsRequest *r, SrsLbRoundRobin *lb)
+srs_error_t SrsEdgeRtmpUpstream::connect(ISrsRequest *r, ISrsLbRoundRobin *lb)
{
srs_error_t err = srs_success;
@@ -173,7 +173,7 @@ SrsEdgeFlvUpstream::~SrsEdgeFlvUpstream()
close();
}
-srs_error_t SrsEdgeFlvUpstream::connect(ISrsRequest *r, SrsLbRoundRobin *lb)
+srs_error_t SrsEdgeFlvUpstream::connect(ISrsRequest *r, ISrsLbRoundRobin *lb)
{
// Because we might modify the r, which cause retry fail, so we must copy it.
ISrsRequest *cp = r->copy();
@@ -185,7 +185,7 @@ srs_error_t SrsEdgeFlvUpstream::connect(ISrsRequest *r, SrsLbRoundRobin *lb)
return do_connect(cp, lb, 0);
}
-srs_error_t SrsEdgeFlvUpstream::do_connect(ISrsRequest *r, SrsLbRoundRobin *lb, int redirect_depth)
+srs_error_t SrsEdgeFlvUpstream::do_connect(ISrsRequest *r, ISrsLbRoundRobin *lb, int redirect_depth)
{
srs_error_t err = srs_success;
@@ -458,11 +458,6 @@ void SrsEdgeIngester::stop()
}
}
-string SrsEdgeIngester::get_curr_origin()
-{
- return lb->selected();
-}
-
#ifdef SRS_APM
ISrsApmSpan *SrsEdgeIngester::span()
{
@@ -999,11 +994,6 @@ void SrsPlayEdge::on_all_client_stop()
}
}
-string SrsPlayEdge::get_curr_origin()
-{
- return ingester->get_curr_origin();
-}
-
srs_error_t SrsPlayEdge::on_ingest_play()
{
srs_error_t err = srs_success;
diff --git a/trunk/src/app/srs_app_edge.hpp b/trunk/src/app/srs_app_edge.hpp
index 82da129f1..241192a2b 100644
--- a/trunk/src/app/srs_app_edge.hpp
+++ b/trunk/src/app/srs_app_edge.hpp
@@ -25,7 +25,7 @@ class SrsRtmpCommonMessage;
class SrsMessageQueue;
class ISrsProtocolReadWriter;
class SrsKbps;
-class SrsLbRoundRobin;
+class ISrsLbRoundRobin;
class SrsTcpClient;
class SrsSimpleRtmpClient;
class SrsRtmpCommand;
@@ -64,7 +64,7 @@ public:
virtual ~SrsEdgeUpstream();
public:
- virtual srs_error_t connect(ISrsRequest *r, SrsLbRoundRobin *lb) = 0;
+ virtual srs_error_t connect(ISrsRequest *r, ISrsLbRoundRobin *lb) = 0;
virtual srs_error_t recv_message(SrsRtmpCommonMessage **pmsg) = 0;
virtual srs_error_t decode_message(SrsRtmpCommonMessage *msg, SrsRtmpCommand **ppacket) = 0;
virtual void close() = 0;
@@ -94,7 +94,7 @@ public:
virtual ~SrsEdgeRtmpUpstream();
public:
- virtual srs_error_t connect(ISrsRequest *r, SrsLbRoundRobin *lb);
+ virtual srs_error_t connect(ISrsRequest *r, ISrsLbRoundRobin *lb);
virtual srs_error_t recv_message(SrsRtmpCommonMessage **pmsg);
virtual srs_error_t decode_message(SrsRtmpCommonMessage *msg, SrsRtmpCommand **ppacket);
virtual void close();
@@ -128,10 +128,10 @@ public:
virtual ~SrsEdgeFlvUpstream();
public:
- virtual srs_error_t connect(ISrsRequest *r, SrsLbRoundRobin *lb);
+ virtual srs_error_t connect(ISrsRequest *r, ISrsLbRoundRobin *lb);
private:
- virtual srs_error_t do_connect(ISrsRequest *r, SrsLbRoundRobin *lb, int redirect_depth);
+ virtual srs_error_t do_connect(ISrsRequest *r, ISrsLbRoundRobin *lb, int redirect_depth);
public:
virtual srs_error_t recv_message(SrsRtmpCommonMessage **pmsg);
@@ -155,7 +155,7 @@ private:
SrsPlayEdge *edge;
ISrsRequest *req;
SrsCoroutine *trd;
- SrsLbRoundRobin *lb;
+ ISrsLbRoundRobin *lb;
SrsEdgeUpstream *upstream;
public:
@@ -166,7 +166,6 @@ public:
virtual srs_error_t initialize(SrsSharedPtr s, SrsPlayEdge *e, ISrsRequest *r);
virtual srs_error_t start();
virtual void stop();
- virtual std::string get_curr_origin();
// Interface ISrsReusableThread2Handler
public:
@@ -192,7 +191,7 @@ private:
ISrsRequest *req;
SrsCoroutine *trd;
SrsSimpleRtmpClient *sdk;
- SrsLbRoundRobin *lb;
+ ISrsLbRoundRobin *lb;
// we must ensure one thread one fd principle,
// that is, a fd must be write/read by the one thread.
// The publish service thread will proxy(msg), and the edge forward thread
@@ -243,7 +242,6 @@ public:
virtual srs_error_t on_client_play();
// When all client stopped play, disconnect to origin.
virtual void on_all_client_stop();
- virtual std::string get_curr_origin();
public:
// When ingester start to play stream.
diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp
index c143d73a8..473f03831 100644
--- a/trunk/src/app/srs_app_source.cpp
+++ b/trunk/src/app/srs_app_source.cpp
@@ -2521,8 +2521,3 @@ void SrsLiveSource::on_edge_proxy_unpublish()
{
publish_edge->on_proxy_unpublish();
}
-
-string SrsLiveSource::get_curr_origin()
-{
- return play_edge->get_curr_origin();
-}
diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp
index 6a288f071..922518b1a 100644
--- a/trunk/src/app/srs_app_source.hpp
+++ b/trunk/src/app/srs_app_source.hpp
@@ -638,9 +638,6 @@ public:
virtual srs_error_t on_edge_proxy_publish(SrsRtmpCommonMessage *msg);
// For edge, proxy stop publish
virtual void on_edge_proxy_unpublish();
-
-public:
- virtual std::string get_curr_origin();
};
#endif
diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp
index d434e9bad..096fe561e 100644
--- a/trunk/src/core/srs_core_version7.hpp
+++ b/trunk/src/core/srs_core_version7.hpp
@@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
-#define VERSION_REVISION 80
+#define VERSION_REVISION 81
#endif
\ No newline at end of file
diff --git a/trunk/src/kernel/srs_kernel_balance.cpp b/trunk/src/kernel/srs_kernel_balance.cpp
index 0ad7cad86..698bd5fc1 100644
--- a/trunk/src/kernel/srs_kernel_balance.cpp
+++ b/trunk/src/kernel/srs_kernel_balance.cpp
@@ -10,6 +10,14 @@
using namespace std;
+ISrsLbRoundRobin::ISrsLbRoundRobin()
+{
+}
+
+ISrsLbRoundRobin::~ISrsLbRoundRobin()
+{
+}
+
SrsLbRoundRobin::SrsLbRoundRobin()
{
index = -1;
diff --git a/trunk/src/kernel/srs_kernel_balance.hpp b/trunk/src/kernel/srs_kernel_balance.hpp
index a02b755d8..33687dc12 100644
--- a/trunk/src/kernel/srs_kernel_balance.hpp
+++ b/trunk/src/kernel/srs_kernel_balance.hpp
@@ -12,18 +12,47 @@
#include
#include
-/**
- * the round-robin load balance algorithm,
- * used for edge pull and other multiple server feature.
- */
-class SrsLbRoundRobin
+// Interface for round-robin load balance algorithm.
+//
+// This interface defines the contract for load balancing algorithms that distribute
+// requests across multiple servers in a round-robin fashion. It's primarily used
+// for edge pull scenarios and other features that require distributing load across
+// multiple backend servers.
+//
+// The round-robin algorithm ensures fair distribution by cycling through available
+// servers in sequence, giving each server an equal opportunity to handle requests.
+//
+class ISrsLbRoundRobin
+{
+public:
+ ISrsLbRoundRobin();
+ virtual ~ISrsLbRoundRobin();
+
+public:
+ // Select one server from the provided list of servers using the load balancing algorithm.
+ //
+ // @param servers A vector of server addresses/URLs to choose from. Must not be empty.
+ // @return The selected server address/URL as a string.
+ //
+ // @remark The implementation should handle the load balancing logic and maintain
+ // any necessary state to ensure proper distribution across servers.
+ // @remark Callers must ensure the servers vector is not empty before calling this method.
+ //
+ virtual std::string select(const std::vector &servers) = 0;
+};
+
+// Implementation of round-robin load balance algorithm.
+//
+// This class provides a concrete implementation of the ISrsLbRoundRobin interface
+// that distributes requests across multiple servers using a simple round-robin
+// algorithm. It maintains internal state to track the current position in the
+// server list and ensures fair distribution by cycling through servers sequentially.
+//
+class SrsLbRoundRobin : public ISrsLbRoundRobin
{
private:
- // current selected index.
int index;
- // total scheduled count.
uint32_t count;
- // current selected server.
std::string elem;
public:
@@ -31,8 +60,11 @@ public:
virtual ~SrsLbRoundRobin();
public:
+ // Get the current server index.
virtual uint32_t current();
+ // Get the currently selected server.
virtual std::string selected();
+ // Select the next server using round-robin algorithm.
virtual std::string select(const std::vector &servers);
};
diff --git a/trunk/src/kernel/srs_kernel_buffer.cpp b/trunk/src/kernel/srs_kernel_buffer.cpp
index 5a4baa72d..755e1a71c 100644
--- a/trunk/src/kernel/srs_kernel_buffer.cpp
+++ b/trunk/src/kernel/srs_kernel_buffer.cpp
@@ -20,6 +20,14 @@ ISrsEncoder::~ISrsEncoder()
{
}
+ISrsDecoder::ISrsDecoder()
+{
+}
+
+ISrsDecoder::~ISrsDecoder()
+{
+}
+
ISrsCodec::ISrsCodec()
{
}
@@ -468,14 +476,23 @@ srs_error_t SrsBitBuffer::read_bits_ue(uint32_t &v)
return srs_error_new(ERROR_HEVC_NALU_UEV, "empty stream");
}
+ // Unsigned Exp-Golomb decoding algorithm from ITU-T H.265 specification
// ue(v) in 9.2 Parsing process for Exp-Golomb codes
// ITU-T-H.265-2021.pdf, page 221.
- // Syntax elements coded as ue(v), me(v), or se(v) are Exp-Golomb-coded.
- // leadingZeroBits = -1;
- // for( b = 0; !b; leadingZeroBits++ )
- // b = read_bits( 1 )
- // The variable codeNum is then assigned as follows:
- // codeNum = (2< leadingZeroBits=0, suffix=none -> value=0
+ // "010" -> leadingZeroBits=1, suffix=0 -> value=1
+ // "011" -> leadingZeroBits=1, suffix=1 -> value=2
+ // "00100" -> leadingZeroBits=2, suffix=00 -> value=3
+
+ // Step 1: Count leading zero bits
int leadingZeroBits = -1;
for (int8_t b = 0; !b && !empty(); leadingZeroBits++) {
b = read_bit();
@@ -485,7 +502,10 @@ srs_error_t SrsBitBuffer::read_bits_ue(uint32_t &v)
return srs_error_new(ERROR_HEVC_NALU_UEV, "%dbits overflow 31bits", leadingZeroBits);
}
+ // Step 2: Calculate base value: (2^leadingZeroBits) - 1
v = (1 << leadingZeroBits) - 1;
+
+ // Step 3: Read suffix bits and add to base value
for (int i = 0; i < (int)leadingZeroBits; i++) {
if (empty()) {
return srs_error_new(ERROR_HEVC_NALU_UEV, "no bytes for leadingZeroBits=%d", leadingZeroBits);
@@ -506,15 +526,30 @@ srs_error_t SrsBitBuffer::read_bits_se(int32_t &v)
return srs_error_new(ERROR_HEVC_NALU_SEV, "empty stream");
}
- // ue(v) in 9.2.1 General Parsing process for Exp-Golomb codes
- // ITU-T-H.265-2021.pdf, page 221.
+ // Signed Exp-Golomb decoding algorithm from ITU-T H.265 specification
+ // se(v) in 9.2.2 Mapping process for signed Exp-Golomb codes
+ // ITU-T-H.265-2021.pdf, page 222.
+ //
+ // Algorithm:
+ // 1. First decode as unsigned Exp-Golomb to get codeNum
+ // 2. Map to signed value using alternating positive/negative pattern:
+ // - If codeNum is odd: se_value = (codeNum + 1) / 2
+ // - If codeNum is even: se_value = -(codeNum / 2)
+ //
+ // Mapping table:
+ // codeNum: 0 1 2 3 4 5 6 7 8 ...
+ // se(v): 0 1 -1 2 -2 3 -3 4 -4 ...
+ //
+ // This encoding efficiently represents signed integers with smaller
+ // absolute values using fewer bits.
+
+ // Step 1: Decode unsigned Exp-Golomb value
uint32_t val = 0;
if ((err = read_bits_ue(val)) != srs_success) {
return srs_error_wrap(err, "read uev");
}
- // se(v) in 9.2.2 Mapping process for signed Exp-Golomb codes
- // ITU-T-H.265-2021.pdf, page 222.
+ // Step 2: Map unsigned code to signed value
if (val & 0x01) {
v = (val + 1) / 2;
} else {
diff --git a/trunk/src/kernel/srs_kernel_buffer.hpp b/trunk/src/kernel/srs_kernel_buffer.hpp
index 05e6059f5..27d160f9a 100644
--- a/trunk/src/kernel/srs_kernel_buffer.hpp
+++ b/trunk/src/kernel/srs_kernel_buffer.hpp
@@ -14,7 +14,28 @@
class SrsBuffer;
-// Encoder.
+// Abstract interface for encoding objects to binary format.
+//
+// ISrsEncoder defines the contract for classes that can serialize/encode objects
+// into binary data within an SrsBuffer. This interface is commonly implemented by
+// protocol handlers, message formatters, and data serializers throughout SRS.
+//
+// Key responsibilities:
+// - Calculate the exact number of bytes needed for encoding
+// - Serialize object data into a provided buffer
+// - Handle encoding errors gracefully
+//
+// Usage pattern:
+// ISrsEncoder* encoder = create_some_encoder();
+// uint64_t required_size = encoder->nb_bytes();
+//
+// char buffer_data[required_size];
+// SrsBuffer buffer(buffer_data, required_size);
+//
+// srs_error_t err = encoder->encode(&buffer);
+// if (err != srs_success) {
+// // Handle encoding error
+// }
class ISrsEncoder
{
public:
@@ -22,215 +43,552 @@ public:
virtual ~ISrsEncoder();
public:
- /**
- * get the number of bytes to code to.
- */
+ // Calculate the number of bytes required to encode this object.
+ // @return The exact number of bytes needed for successful encoding.
+ // @remark This should return a consistent value for the same object state.
+ // @remark Implementations must ensure the returned size is accurate to prevent buffer overruns.
virtual uint64_t nb_bytes() = 0;
- /**
- * encode object to bytes in SrsBuffer.
- */
+
+ // Encode this object into the provided buffer.
+ // @param buf The target buffer to write encoded data into. Must not be NULL.
+ // @return srs_success on successful encoding, error code on failure.
+ // @remark Caller must ensure buf has at least nb_bytes() space available.
+ // @remark The buffer's position will be advanced by the number of bytes written.
+ // @remark On error, the buffer state is undefined and should not be used.
virtual srs_error_t encode(SrsBuffer *buf) = 0;
};
-/**
- * the srs codec, to code and decode object with bytes:
- * code: to encode/serialize object to bytes in buffer,
- * decode: to decode/deserialize object from bytes in buffer.
- * we use SrsBuffer as bytes helper utility,
- * for example, to code:
- * ISrsCodec* obj = ...
- * char* bytes = new char[obj->size()];
- *
- * SrsBuffer* buf = new SrsBuffer();
- * buf->initialize(bytes, obj->size())
- *
- * obj->encode(buf);
- * for example, to decode:
- * int nb_bytes = ...
- * char* bytes = ...
- *
- * SrsBuffer* buf = new Srsbuffer();
- * buf->initialize(bytes, nb_bytes);
- *
- * ISrsCodec* obj = ...
- * obj->decode(buf);
- * @remark protocol or amf0 or json should implements this interface.
- */
-// TODO: FIXME: protocol, amf0, json should implements it.
-class ISrsCodec : public ISrsEncoder
+// Abstract interface for decoding/deserializing objects from binary format.
+//
+// ISrsDecoder defines the contract for classes that can deserialize objects
+// from binary data within an SrsBuffer. This interface complements ISrsEncoder
+// and is commonly implemented by protocol parsers, message decoders, and data
+// deserializers throughout SRS.
+//
+// Key responsibilities:
+// - Deserialize object data from a provided buffer
+// - Advance buffer position by the number of bytes consumed
+// - Handle decoding errors gracefully
+// - Maintain object state consistency during decoding
+//
+// Usage pattern:
+// ISrsDecoder* decoder = create_some_decoder();
+// SrsBuffer buffer(received_data, data_size);
+//
+// srs_error_t err = decoder->decode(&buffer);
+// if (err != srs_success) {
+// // Handle decoding error
+// }
+// // Use decoded object...
+class ISrsDecoder
+{
+public:
+ ISrsDecoder();
+ virtual ~ISrsDecoder();
+
+public:
+ // Decode/deserialize object from the provided buffer.
+ // @param buf The source buffer containing binary data to decode. Must not be NULL.
+ // @return srs_success on successful decoding, error code on failure.
+ // @remark The buffer's position will be advanced by the number of bytes consumed.
+ // @remark On error, both the object state and buffer position are undefined.
+ // @remark Caller should ensure the buffer contains sufficient data for decoding.
+ // @remark Implementation should validate input data and handle malformed content gracefully.
+ virtual srs_error_t decode(SrsBuffer *buf) = 0;
+};
+
+// Abstract interface for bidirectional encoding/decoding of objects to/from binary format.
+//
+// ISrsCodec combines ISrsEncoder and ISrsDecoder to provide both serialization
+// (encoding) and deserialization (decoding) capabilities. This interface is the
+// foundation for protocol handlers, message parsers, and data format converters
+// in SRS that need to both read and write binary data.
+//
+// Key capabilities:
+// - Serialize objects to binary format (inherited from ISrsEncoder)
+// - Deserialize objects from binary format (inherited from ISrsDecoder)
+// - Calculate exact space requirements for encoding
+// - Handle encoding/decoding errors gracefully
+// - Ensure symmetric encode/decode operations
+//
+// Common use cases:
+// - Protocol message handlers (RTMP, HTTP, WebRTC, etc.)
+// - Media format parsers (AMF0, JSON, FLV, etc.)
+// - Configuration and metadata serializers
+// - Bidirectional data converters and transformers
+//
+// Complete workflow example:
+// // Encoding workflow
+// ISrsCodec* codec = create_some_codec();
+// uint64_t size = codec->nb_bytes();
+//
+// char buffer_data[size];
+// SrsBuffer encode_buffer(buffer_data, size);
+//
+// srs_error_t err = codec->encode(&encode_buffer);
+// // Transmit or store encoded data...
+//
+// // Decoding workflow
+// ISrsCodec* decoder = create_same_codec_type();
+// SrsBuffer decode_buffer(received_data, data_size);
+//
+// err = decoder->decode(&decode_buffer);
+// // Use decoded object (should match original)...
+//
+// @remark Protocol handlers, AMF0, and JSON parsers should implement this interface.
+// @remark Implementations must ensure encode/decode operations are symmetric.
+// @remark The same codec type should be able to decode what it encoded.
+// TODO: FIXME: protocol, amf0, json should implement this interface.
+class ISrsCodec : public ISrsEncoder, public ISrsDecoder
{
public:
ISrsCodec();
virtual ~ISrsCodec();
-
-public:
- // Get the number of bytes to code to.
- virtual uint64_t nb_bytes() = 0;
- // Encode object to bytes in SrsBuffer.
- virtual srs_error_t encode(SrsBuffer *buf) = 0;
-
-public:
- // Decode object from bytes in SrsBuffer.
- virtual srs_error_t decode(SrsBuffer *buf) = 0;
};
-/**
- * bytes utility, used to:
- * convert basic types to bytes,
- * build basic types from bytes.
- * @remark the buffer never mange the bytes, user must manage it.
- */
+// Byte-level buffer for reading and writing binary data with automatic position tracking.
+//
+// SrsBuffer provides a convenient interface for reading and writing basic data types
+// (integers, strings, byte arrays) to/from a binary buffer. It maintains an internal
+// position pointer that automatically advances as data is read or written, making it
+// ideal for parsing and constructing binary protocols and file formats.
+//
+// Key features:
+// - Automatic position tracking with read/write operations
+// - Support for both big-endian (network byte order) and little-endian data
+// - Type-safe reading/writing of 1, 2, 3, 4, and 8-byte integers
+// - String and raw byte array operations
+// - Buffer bounds checking and validation
+// - Position manipulation (skip, reset) for flexible parsing
+//
+// Usage example:
+// char data[1024];
+// SrsBuffer buffer(data, sizeof(data));
+//
+// // Writing data
+// buffer.write_4bytes(0x12345678); // Write 32-bit integer
+// buffer.write_string("hello"); // Write string
+//
+// // Reading data (reset position first)
+// buffer.skip(-buffer.pos()); // Reset to beginning
+// int32_t value = buffer.read_4bytes(); // Read 32-bit integer
+// std::string str = buffer.read_string(5); // Read 5-byte string
+//
+// Memory management:
+// - SrsBuffer does NOT take ownership of the provided buffer
+// - Caller is responsible for allocating and freeing the buffer memory
+// - Buffer must remain valid for the lifetime of the SrsBuffer instance
+//
+// @remark The buffer never manages the bytes memory, user must manage it.
class SrsBuffer
{
private:
- // current position at bytes.
+ // Current read/write position within the buffer
char *p;
- // the bytes data for buffer to read or write.
+ // Pointer to the start of the buffer data (not owned by this class)
char *bytes;
- // the total number of bytes.
+ // Total size of the buffer in bytes
int nb_bytes;
public:
- // Create buffer with data b and size nn.
- // @remark User must free the data b.
+ // Create a buffer wrapper around existing memory.
+ // @param b Pointer to the buffer data. Must not be NULL and must remain valid
+ // for the lifetime of this SrsBuffer instance.
+ // @param nn Size of the buffer in bytes. Must be non-negative.
+ // @remark User must manage the memory lifecycle of buffer b.
SrsBuffer(char *b, int nn);
- ~SrsBuffer();
+
+ // Destructor. Does not free the underlying buffer memory.
+ virtual ~SrsBuffer();
public:
- // Copy the object, keep position of buffer.
+ // Create a copy of this buffer with the same position and data reference.
+ // @return A new SrsBuffer instance pointing to the same data with same position.
+ // @remark The returned buffer shares the same underlying data pointer.
+ // @remark Caller is responsible for deleting the returned buffer.
SrsBuffer *copy();
- // Get the data and head of buffer.
- // current-bytes = head() = data() + pos()
+
+ // Get pointer to the start of the buffer data.
+ // @return Pointer to the beginning of the buffer (same as constructor parameter).
char *data();
+
+ // Get pointer to the current position in the buffer.
+ // @return Pointer to current read/write position (data() + pos()).
char *head();
- // Get the total size of buffer.
- // left-bytes = size() - pos()
+
+ // Get the total size of the buffer.
+ // @return Total buffer size in bytes.
int size();
+
+ // Set the total size of the buffer.
+ // @param v New size in bytes. Should not exceed the original buffer size.
+ // @remark Use with caution - does not reallocate memory.
void set_size(int v);
- // Get the current buffer position.
+
+ // Get the current position within the buffer.
+ // @return Current offset from the start of the buffer in bytes.
int pos();
- // Left bytes in buffer, total size() minus the current pos().
+
+ // Get the number of bytes remaining from current position to end of buffer.
+ // @return Number of unread/unwritten bytes (size() - pos()).
int left();
- // Whether buffer is empty.
+
+ // Check if the buffer has no remaining bytes.
+ // @return true if pos() >= size(), false otherwise.
bool empty();
- // Whether buffer is able to supply required size of bytes.
- // @remark User should check buffer by require then do read/write.
- // @remark Assert the required_size is not negative.
+
+ // Check if the buffer has enough remaining bytes for a read/write operation.
+ // @param required_size Number of bytes needed for the operation.
+ // @return true if left() >= required_size, false otherwise.
+ // @remark Always call this before read/write operations to avoid buffer overruns.
+ // @remark Asserts that required_size is non-negative.
bool require(int required_size);
public:
- // Skip some size.
- // @param size can be any value. positive to forward; negative to backward.
- // @remark to skip(pos()) to reset buffer.
- // @remark assert initialized, the data() not NULL.
+ // Move the current position forward or backward within the buffer.
+ // @param size Number of bytes to skip. Positive values move forward,
+ // negative values move backward.
+ // @remark Use skip(-pos()) to reset buffer position to the beginning.
+ // @remark Asserts that the buffer is initialized (data() is not NULL).
+ // @remark Does not perform bounds checking - caller must ensure valid position.
void skip(int size);
public:
- // Read 1bytes char from buffer.
+ // Read a single byte from the buffer and advance position.
+ // @return The byte value as a signed 8-bit integer.
+ // @remark Caller must ensure require(1) before calling.
int8_t read_1bytes();
- // Read 2bytes int from buffer.
+
+ // Read a 2-byte integer from the buffer in big-endian (network) byte order.
+ // @return The 16-bit integer value.
+ // @remark Caller must ensure require(2) before calling.
int16_t read_2bytes();
+
+ // Read a 2-byte integer from the buffer in little-endian byte order.
+ // @return The 16-bit integer value.
+ // @remark Caller must ensure require(2) before calling.
int16_t read_le2bytes();
- // Read 3bytes int from buffer.
+
+ // Read a 3-byte integer from the buffer in big-endian byte order.
+ // @return The 24-bit value as a 32-bit integer (high byte is 0).
+ // @remark Caller must ensure require(3) before calling.
int32_t read_3bytes();
+
+ // Read a 3-byte integer from the buffer in little-endian byte order.
+ // @return The 24-bit value as a 32-bit integer (high byte is 0).
+ // @remark Caller must ensure require(3) before calling.
int32_t read_le3bytes();
- // Read 4bytes int from buffer.
+
+ // Read a 4-byte integer from the buffer in big-endian (network) byte order.
+ // @return The 32-bit integer value.
+ // @remark Caller must ensure require(4) before calling.
int32_t read_4bytes();
+
+ // Read a 4-byte integer from the buffer in little-endian byte order.
+ // @return The 32-bit integer value.
+ // @remark Caller must ensure require(4) before calling.
int32_t read_le4bytes();
- // Read 8bytes int from buffer.
+
+ // Read an 8-byte integer from the buffer in big-endian (network) byte order.
+ // @return The 64-bit integer value.
+ // @remark Caller must ensure require(8) before calling.
int64_t read_8bytes();
+
+ // Read an 8-byte integer from the buffer in little-endian byte order.
+ // @return The 64-bit integer value.
+ // @remark Caller must ensure require(8) before calling.
int64_t read_le8bytes();
- // Read string from buffer, length specifies by param len.
+
+ // Read a string of specified length from the buffer.
+ // @param len Number of bytes to read as string data.
+ // @return String containing the read bytes.
+ // @remark Caller must ensure require(len) before calling.
+ // @remark Does not assume null-termination - reads exactly len bytes.
std::string read_string(int len);
- // Read bytes from buffer, length specifies by param len.
+
+ // Read raw bytes from the buffer into a provided array.
+ // @param data Destination buffer to copy bytes into. Must not be NULL.
+ // @param size Number of bytes to read and copy.
+ // @remark Caller must ensure require(size) before calling.
+ // @remark Caller must ensure data buffer has at least size bytes available.
void read_bytes(char *data, int size);
public:
- // Write 1bytes char to buffer.
+ // Write a single byte to the buffer and advance position.
+ // @param value The byte value to write.
+ // @remark Caller must ensure require(1) before calling.
void write_1bytes(int8_t value);
- // Write 2bytes int to buffer.
+
+ // Write a 2-byte integer to the buffer in big-endian (network) byte order.
+ // @param value The 16-bit integer value to write.
+ // @remark Caller must ensure require(2) before calling.
void write_2bytes(int16_t value);
+
+ // Write a 2-byte integer to the buffer in little-endian byte order.
+ // @param value The 16-bit integer value to write.
+ // @remark Caller must ensure require(2) before calling.
void write_le2bytes(int16_t value);
- // Write 4bytes int to buffer.
+
+ // Write a 4-byte integer to the buffer in big-endian (network) byte order.
+ // @param value The 32-bit integer value to write.
+ // @remark Caller must ensure require(4) before calling.
void write_4bytes(int32_t value);
+
+ // Write a 4-byte integer to the buffer in little-endian byte order.
+ // @param value The 32-bit integer value to write.
+ // @remark Caller must ensure require(4) before calling.
void write_le4bytes(int32_t value);
- // Write 3bytes int to buffer.
+
+ // Write a 3-byte integer to the buffer in big-endian byte order.
+ // @param value The 24-bit value to write (high byte ignored).
+ // @remark Caller must ensure require(3) before calling.
void write_3bytes(int32_t value);
+
+ // Write a 3-byte integer to the buffer in little-endian byte order.
+ // @param value The 24-bit value to write (high byte ignored).
+ // @remark Caller must ensure require(3) before calling.
void write_le3bytes(int32_t value);
- // Write 8bytes int to buffer.
+
+ // Write an 8-byte integer to the buffer in big-endian (network) byte order.
+ // @param value The 64-bit integer value to write.
+ // @remark Caller must ensure require(8) before calling.
void write_8bytes(int64_t value);
+
+ // Write an 8-byte integer to the buffer in little-endian byte order.
+ // @param value The 64-bit integer value to write.
+ // @remark Caller must ensure require(8) before calling.
void write_le8bytes(int64_t value);
- // Write string to buffer
+
+ // Write a string to the buffer as raw bytes.
+ // @param value The string to write. All bytes of the string are written.
+ // @remark Caller must ensure require(value.length()) before calling.
+ // @remark Does not write null terminator - only the string content.
void write_string(std::string value);
- // Write bytes to buffer
+
+ // Write raw bytes from an array to the buffer.
+ // @param data Source buffer containing bytes to write. Must not be NULL.
+ // @param size Number of bytes to write from the source buffer.
+ // @remark Caller must ensure require(size) before calling.
+ // @remark Caller must ensure data buffer contains at least size bytes.
void write_bytes(char *data, int size);
};
-/**
- * the bit buffer, base on SrsBuffer,
- * for exmaple, the h.264 avc buffer is bit buffer.
- */
+// Bit-level buffer reader for parsing binary data at bit granularity.
+//
+// SrsBitBuffer provides bit-level access to binary data, commonly used for parsing
+// video codec bitstreams like H.264/AVC, H.265/HEVC, and other formats that require
+// bit-precise parsing. It wraps an SrsBuffer and maintains internal state to track
+// partial byte consumption.
+//
+// Key features:
+// - Bit-level reading with automatic byte boundary handling
+// - Optimized fast paths for byte-aligned operations (8, 16, 32-bit reads)
+// - Support for Exp-Golomb encoding used in H.264/H.265 standards
+// - Efficient buffering with minimal memory overhead
+//
+// Usage example:
+// char data[] = {0x12, 0x34, 0x56, 0x78};
+// SrsBuffer buffer(data, 4);
+// SrsBitBuffer bb(&buffer);
+//
+// int bit = bb.read_bit(); // Read single bit
+// int nibble = bb.read_bits(4); // Read 4 bits
+// int byte_val = bb.read_8bits(); // Read 8 bits (optimized)
+//
+// uint32_t ue_val;
+// bb.read_bits_ue(ue_val); // Read unsigned Exp-Golomb
+//
+// @remark The underlying SrsBuffer must remain valid for the lifetime of SrsBitBuffer.
+// @remark This class does not take ownership of the SrsBuffer pointer.
class SrsBitBuffer
{
private:
+ // Current byte being processed (cached from stream)
int8_t cb;
+ // Number of unread bits remaining in current byte (0-8)
uint8_t cb_left;
+ // Underlying byte buffer (not owned by this class)
SrsBuffer *stream;
public:
+ // Construct a bit buffer from an existing byte buffer.
+ // @param b The underlying SrsBuffer to read from. Must not be NULL and must
+ // remain valid for the lifetime of this SrsBitBuffer instance.
SrsBitBuffer(SrsBuffer *b);
- ~SrsBitBuffer();
+
+ // Destructor. Does not free the underlying SrsBuffer.
+ virtual ~SrsBitBuffer();
public:
+ // Check if the bit buffer is empty (no more bits to read).
+ // @return true if no more bits are available, false otherwise.
bool empty();
+
+ // Read a single bit from the buffer.
+ // @return The bit value (0 or 1).
+ // @remark Caller must ensure !empty() before calling.
int8_t read_bit();
+
+ // Check if the specified number of bits are available for reading.
+ // @param n Number of bits to check for availability. Must be non-negative.
+ // @return true if n bits are available, false otherwise.
bool require_bits(int n);
+
+ // Get the number of bits remaining in the buffer.
+ // @return Total number of unread bits available.
int left_bits();
+
+ // Skip the specified number of bits without reading their values.
+ // @param n Number of bits to skip. Must be <= left_bits().
void skip_bits(int n);
+
+ // Read the specified number of bits as an integer value.
+ // @param n Number of bits to read (1-32). Must be <= left_bits().
+ // @return The bits interpreted as an unsigned integer, MSB first.
int32_t read_bits(int n);
+
+ // Read 8 bits as a byte value.
+ // Uses optimized fast path when byte-aligned, falls back to read_bits(8) otherwise.
+ // @return The 8-bit value.
+ // @remark Caller must ensure at least 8 bits are available.
int8_t read_8bits();
+
+ // Read 16 bits as a short value.
+ // Uses optimized fast path when byte-aligned, falls back to read_bits(16) otherwise.
+ // @return The 16-bit value in network byte order.
+ // @remark Caller must ensure at least 16 bits are available.
int16_t read_16bits();
+
+ // Read 32 bits as an integer value.
+ // Uses optimized fast path when byte-aligned, falls back to read_bits(32) otherwise.
+ // @return The 32-bit value in network byte order.
+ // @remark Caller must ensure at least 32 bits are available.
int32_t read_32bits();
+
+ // Read an unsigned Exp-Golomb encoded value.
+ //
+ // Implements the ue(v) parsing algorithm from ITU-T H.265 specification:
+ // - Count leading zero bits (leadingZeroBits)
+ // - Read 1 bit (must be 1)
+ // - Read leadingZeroBits more bits
+ // - Calculate: codeNum = (2^leadingZeroBits) - 1 + read_bits(leadingZeroBits)
+ //
+ // Common in H.264/H.265 for encoding non-negative integers with variable length.
+ // Smaller values use fewer bits.
+ //
+ // @param v Output parameter to store the decoded unsigned value.
+ // @return srs_success on success, error code on failure (empty buffer, overflow).
srs_error_t read_bits_ue(uint32_t &v);
+
+ // Read a signed Exp-Golomb encoded value.
+ //
+ // Implements the se(v) parsing algorithm from ITU-T H.265 specification:
+ // - First reads ue(v) to get unsigned code
+ // - Maps to signed value: if (code & 1) result = (code + 1) / 2; else result = -(code / 2)
+ //
+ // This encoding alternates between positive and negative values:
+ // se(0)=0, se(1)=1, se(-1)=-1, se(2)=2, se(-2)=-2, etc.
+ //
+ // @param v Output parameter to store the decoded signed value.
+ // @return srs_success on success, error code on failure (propagated from read_bits_ue).
srs_error_t read_bits_se(int32_t &v);
};
-// Memory block for shared payload data.
-// This class encapsulates a memory buffer with size information,
-// designed to be used with SrsSharedPtr for efficient memory sharing.
+// Memory block for managing shared payload data with automatic lifecycle management.
+//
+// SrsMemoryBlock encapsulates a dynamically allocated memory buffer with size tracking,
+// specifically designed for use with SrsSharedPtr to enable efficient memory sharing
+// across multiple components without unnecessary copying. This is commonly used for
+// message payloads, media data, and other binary content in SRS.
+//
+// Key features:
+// - Automatic memory lifecycle management (allocation and deallocation)
+// - Support for both copying and taking ownership of existing buffers
+// - Size tracking for safe buffer operations
+// - Designed for shared ownership via SrsSharedPtr
+// - Suitable for chunked data that may arrive in multiple parts
+//
+// Usage patterns:
+//
+// Creating new memory block:
+// SrsSharedPtr block(new SrsMemoryBlock());
+// block->create(1024); // Allocate 1KB
+// memcpy(block->payload(), data, data_size);
+//
+// Copying existing data:
+// SrsSharedPtr block(new SrsMemoryBlock());
+// block->create(source_data, source_size); // Copies the data
+//
+// Taking ownership of existing buffer:
+// char* buffer = malloc(size); // Caller allocates
+// SrsSharedPtr block(new SrsMemoryBlock());
+// block->attach(buffer, size); // Takes ownership, caller must not free
+//
+// Shared usage:
+// SrsSharedPtr block1 = original_block;
+// SrsSharedPtr block2 = original_block;
+// // Both share the same underlying memory
+//
+// @remark Not all payload data can be decoded to structured packets - some data
+// (like video/audio packets) may remain as raw bytes.
+// @remark The size may be less than the allocated buffer size for chunked data.
class SrsMemoryBlock
{
private:
- // The current message parsed size,
- // size <= allocated buffer size
- // For the payload maybe sent in multiple chunks.
+ // Current size of valid data in the buffer.
+ // This may be less than the allocated buffer size for chunked data
+ // that arrives in multiple parts.
int size_;
- // The payload of message, the SrsMemoryBlock manages the memory lifecycle.
- // @remark, not all message payload can be decoded to packet. for example,
- // video/audio packet use raw bytes, no video/audio packet.
+
+ // Pointer to the allocated memory buffer.
+ // SrsMemoryBlock owns this memory and is responsible for its lifecycle.
+ // The buffer contains the actual payload data.
char *payload_;
public:
+ // Construct an empty memory block.
+ // Call create() or attach() to initialize with actual memory.
SrsMemoryBlock();
+
+ // Destructor automatically frees the managed memory.
+ // Safe to call even if no memory was allocated.
virtual ~SrsMemoryBlock();
public:
+ // Get direct access to the payload buffer.
+ // @return Pointer to the memory buffer, or NULL if not initialized.
+ // @remark Caller should not free this pointer - it's managed by SrsMemoryBlock.
+ // @remark The returned pointer is valid until the SrsMemoryBlock is destroyed.
char *payload() { return payload_; }
+
+ // Get the current size of valid data in the buffer.
+ // @return Number of bytes of valid data, may be 0 if not initialized.
+ // @remark This may be less than the allocated buffer size for chunked data.
int size() { return size_; }
public:
- // Create memory block with specified size.
- // @param size, the size of memory to allocate. Must be non-negative.
+ // Allocate a new memory buffer of the specified size.
+ // @param size Number of bytes to allocate. Must be non-negative.
+ // @remark Any existing buffer is freed before allocating the new one.
+ // @remark The allocated memory is uninitialized - caller should fill it as needed.
+ // @remark If size is 0, creates a valid but empty memory block.
virtual void create(int size);
- // Create memory block from existing buffer.
- // @param data, the buffer to copy from.
- // @param size, the size of buffer. Must be non-negative.
- // @remark, this method will copy the data.
+
+ // Create a memory buffer by copying data from an existing buffer.
+ // @param data Source buffer to copy from. Must not be NULL if size > 0.
+ // @param size Number of bytes to copy. Must be non-negative.
+ // @remark Any existing buffer is freed before creating the new one.
+ // @remark This method creates an independent copy - changes to source won't affect this block.
+ // @remark If size is 0, creates a valid but empty memory block.
virtual void create(char *data, int size);
- // Attach existing buffer to memory block.
- // @param data, the buffer to attach, memory block takes ownership.
- // @param size, the size of buffer. Must be non-negative.
- // @remark, this method takes ownership of data, caller should not free it.
+
+ // Take ownership of an existing buffer without copying.
+ // @param data Buffer to take ownership of. Must be allocated with malloc/new[].
+ // @param size Size of the buffer in bytes. Must be non-negative.
+ // @remark Any existing buffer is freed before attaching the new one.
+ // @remark This method takes ownership - caller must NOT free the data pointer.
+ // @remark The provided buffer will be freed with delete[] when this object is destroyed.
+ // @remark If data is NULL and size is 0, creates a valid but empty memory block.
virtual void attach(char *data, int size);
};
diff --git a/trunk/src/utest/srs_utest_core.cpp b/trunk/src/utest/srs_utest_core.cpp
index 11f370add..d6e9910eb 100644
--- a/trunk/src/utest/srs_utest_core.cpp
+++ b/trunk/src/utest/srs_utest_core.cpp
@@ -19,6 +19,68 @@ VOID TEST(CoreAutoFreeTest, Free)
EXPECT_TRUE(data == NULL);
}
+VOID TEST(CoreAutoFreeTest, FreepPointer)
+{
+ int *ptr = new int(42);
+ EXPECT_TRUE(ptr != NULL);
+ EXPECT_EQ(42, *ptr);
+
+ srs_freep(ptr);
+ EXPECT_TRUE(ptr == NULL);
+}
+
+VOID TEST(CoreAutoFreeTest, FreepObject)
+{
+ MyNormalObject *obj = new MyNormalObject(100);
+ EXPECT_TRUE(obj != NULL);
+ EXPECT_EQ(100, obj->id());
+
+ srs_freep(obj);
+ EXPECT_TRUE(obj == NULL);
+}
+
+VOID TEST(CoreAutoFreeTest, FreepNullPointer)
+{
+ int *ptr = NULL;
+ srs_freep(ptr); // Should not crash
+ EXPECT_TRUE(ptr == NULL);
+}
+
+VOID TEST(CoreAutoFreeTest, FreepaArray)
+{
+ int *arr = new int[10];
+ for (int i = 0; i < 10; i++) {
+ arr[i] = i * 2;
+ }
+ EXPECT_TRUE(arr != NULL);
+ EXPECT_EQ(0, arr[0]);
+ EXPECT_EQ(18, arr[9]);
+
+ srs_freepa(arr);
+ EXPECT_TRUE(arr == NULL);
+}
+
+VOID TEST(CoreAutoFreeTest, FreepaNullArray)
+{
+ int *arr = NULL;
+ srs_freepa(arr); // Should not crash
+ EXPECT_TRUE(arr == NULL);
+}
+
+VOID TEST(CoreAutoFreeTest, FreepaCharArray)
+{
+ char *chars = new char[256];
+ for (int i = 0; i < 10; i++) {
+ chars[i] = 'a' + i;
+ }
+ chars[10] = '\0';
+ EXPECT_TRUE(chars != NULL);
+ EXPECT_STREQ("abcdefghij", chars);
+
+ srs_freepa(chars);
+ EXPECT_TRUE(chars == NULL);
+}
+
VOID TEST(CoreMacroseTest, Check)
{
#ifndef SRS_BUILD_TS
@@ -75,11 +137,15 @@ VOID TEST(CoreLogger, CheckVsnprintf)
EXPECT_EQ(4, snprintf(buf, sizeof(buf), "Hell"));
EXPECT_STREQ("Hell", buf);
+ // Test intentional truncation - suppress warning as this is expected behavior
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-truncation"
EXPECT_EQ(5, snprintf(buf, sizeof(buf), "Hello"));
EXPECT_STREQ("Hell", buf);
EXPECT_EQ(10, snprintf(buf, sizeof(buf), "HelloWorld"));
EXPECT_STREQ("Hell", buf);
+#pragma clang diagnostic pop
}
}
@@ -342,6 +408,95 @@ VOID TEST(CoreSmartPtr, SharedPtrMove)
}
}
+VOID TEST(CoreSmartPtr, SharedPtrResetMethod)
+{
+ if (true) {
+ SrsSharedPtr p(new int(100));
+ EXPECT_TRUE(p);
+ EXPECT_EQ(100, *p);
+
+ p.reset();
+ EXPECT_FALSE(p);
+ EXPECT_TRUE(p.get() == NULL);
+ }
+
+ if (true) {
+ SrsSharedPtr p(new MyNormalObject(200));
+ EXPECT_TRUE(p);
+ EXPECT_EQ(200, p->id());
+
+ p.reset();
+ EXPECT_FALSE(p);
+ EXPECT_TRUE(p.get() == NULL);
+ }
+}
+
+VOID TEST(CoreSmartPtr, SharedPtrSelfAssignment)
+{
+ if (true) {
+ SrsSharedPtr p(new int(100));
+ int *original_ptr = p.get();
+
+ // Test self assignment - suppress warning as this is intentional
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-assign-overloaded"
+ p = p; // Self assignment
+#pragma clang diagnostic pop
+ EXPECT_EQ(original_ptr, p.get());
+ EXPECT_EQ(100, *p);
+ }
+}
+
+VOID TEST(CoreSmartPtr, SharedPtrMultipleReferences)
+{
+ int *ptr = new int(100);
+ SrsUniquePtr ptr_uptr(ptr);
+ EXPECT_EQ(100, *ptr);
+
+ if (true) {
+ SrsSharedPtr p1(new MockWrapper(ptr));
+ EXPECT_EQ(101, *ptr);
+
+ SrsSharedPtr p2 = p1;
+ EXPECT_EQ(101, *ptr);
+
+ SrsSharedPtr p3(p1);
+ EXPECT_EQ(101, *ptr);
+
+ SrsSharedPtr p4(NULL);
+ p4 = p1;
+ EXPECT_EQ(101, *ptr);
+
+ // All should point to the same object
+ EXPECT_EQ(p1.get(), p2.get());
+ EXPECT_EQ(p1.get(), p3.get());
+ EXPECT_EQ(p1.get(), p4.get());
+ }
+ EXPECT_EQ(100, *ptr); // All references gone, object destroyed
+}
+
+VOID TEST(CoreSmartPtr, SharedPtrBoolOperator)
+{
+ if (true) {
+ SrsSharedPtr p(new int(42));
+ EXPECT_TRUE(p);
+ EXPECT_TRUE(static_cast(p));
+ }
+
+ if (true) {
+ SrsSharedPtr p(NULL);
+ EXPECT_FALSE(p);
+ EXPECT_FALSE(static_cast(p));
+ }
+
+ if (true) {
+ SrsSharedPtr p(new int(42));
+ EXPECT_TRUE(p);
+ p.reset();
+ EXPECT_FALSE(p);
+ }
+}
+
class MockIntResource : public ISrsResource
{
public:
@@ -450,6 +605,87 @@ VOID TEST(CoreSmartPtr, SharedResourceMove)
}
}
+VOID TEST(CoreSmartPtr, SharedResourceNullPointer)
+{
+ if (true) {
+ SrsSharedResource p(NULL);
+ EXPECT_FALSE(p);
+ EXPECT_TRUE(p.get() == NULL);
+ }
+}
+
+VOID TEST(CoreSmartPtr, SharedResourceSelfAssignment)
+{
+ if (true) {
+ SrsSharedResource p(new MockIntResource(100));
+ MockIntResource *original_ptr = p.get();
+
+ // Test self assignment - suppress warning as this is intentional
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-assign-overloaded"
+ p = p; // Self assignment
+#pragma clang diagnostic pop
+ EXPECT_EQ(original_ptr, p.get());
+ EXPECT_EQ(100, p->value_);
+ }
+}
+
+VOID TEST(CoreSmartPtr, SharedResourceBoolOperator)
+{
+ if (true) {
+ SrsSharedResource p(new MockIntResource(42));
+ EXPECT_TRUE(p);
+ EXPECT_TRUE(static_cast(p));
+ }
+
+ if (true) {
+ SrsSharedResource p(NULL);
+ EXPECT_FALSE(p);
+ EXPECT_FALSE(static_cast(p));
+ }
+}
+
+VOID TEST(CoreSmartPtr, SharedResourceISrsResourceInterface)
+{
+ if (true) {
+ SrsSharedResource *p = new SrsSharedResource(new MockIntResource(100));
+
+ // Test ISrsResource interface
+ const SrsContextId &id = p->get_id();
+ EXPECT_TRUE(id.empty());
+
+ std::string desc = p->desc();
+ EXPECT_TRUE(desc.empty());
+
+ // Test access to wrapped object
+ EXPECT_EQ(100, (*p)->value_);
+ EXPECT_EQ(100, p->get()->value_);
+
+ srs_freep(p);
+ }
+}
+
+VOID TEST(CoreSmartPtr, SharedResourceMultipleReferences)
+{
+ if (true) {
+ SrsSharedResource p1(new MockIntResource(200));
+ SrsSharedResource p2 = p1;
+ SrsSharedResource p3(p1);
+
+ // All should point to the same object
+ EXPECT_EQ(p1.get(), p2.get());
+ EXPECT_EQ(p1.get(), p3.get());
+ EXPECT_EQ(200, p1->value_);
+ EXPECT_EQ(200, p2->value_);
+ EXPECT_EQ(200, p3->value_);
+
+ // Modify through one reference, should be visible through all
+ p1->value_ = 300;
+ EXPECT_EQ(300, p2->value_);
+ EXPECT_EQ(300, p3->value_);
+ }
+}
+
VOID TEST(CoreSmartPtr, UniquePtrNormal)
{
if (true) {
@@ -631,3 +867,161 @@ VOID TEST(CoreSmartPtr, UniquePtrDeleterMalloc)
}
EXPECT_TRUE(p.bytes_ == NULL);
}
+
+VOID TEST(CoreSmartPtr, UniquePtrNullPointer)
+{
+ if (true) {
+ SrsUniquePtr ptr(NULL);
+ EXPECT_TRUE(ptr.get() == NULL);
+ }
+
+ if (true) {
+ SrsUniquePtr ptr(NULL);
+ EXPECT_TRUE(ptr.get() == NULL);
+ }
+}
+
+VOID TEST(CoreSmartPtr, UniquePtrGetMethod)
+{
+ if (true) {
+ int *raw_ptr = new int(42);
+ SrsUniquePtr ptr(raw_ptr);
+ EXPECT_EQ(raw_ptr, ptr.get());
+ EXPECT_EQ(42, *ptr.get());
+ }
+
+ if (true) {
+ MyNormalObject *raw_obj = new MyNormalObject(100);
+ SrsUniquePtr ptr(raw_obj);
+ EXPECT_EQ(raw_obj, ptr.get());
+ EXPECT_EQ(100, ptr.get()->id());
+ }
+}
+
+VOID TEST(CoreSmartPtr, UniquePtrArrowOperator)
+{
+ if (true) {
+ MyNormalObject *raw_obj = new MyNormalObject(200);
+ SrsUniquePtr ptr(raw_obj);
+ EXPECT_EQ(200, ptr->id());
+ }
+}
+
+VOID TEST(CoreSmartPtr, UniquePtrArrayIndexOperator)
+{
+ if (true) {
+ int *arr = new int[5];
+ for (int i = 0; i < 5; i++) {
+ arr[i] = i * 10;
+ }
+
+ SrsUniquePtr ptr(arr);
+ EXPECT_EQ(0, ptr[0]);
+ EXPECT_EQ(10, ptr[1]);
+ EXPECT_EQ(40, ptr[4]);
+ }
+}
+
+VOID TEST(CoreSmartPtr, UniquePtrArrayConstIndexOperator)
+{
+ if (true) {
+ int *arr = new int[3];
+ arr[0] = 100;
+ arr[1] = 200;
+ arr[2] = 300;
+
+ const SrsUniquePtr ptr(arr);
+ EXPECT_EQ(100, ptr[0]);
+ EXPECT_EQ(200, ptr[1]);
+ EXPECT_EQ(300, ptr[2]);
+ }
+}
+
+VOID TEST(CoreSmartPtr, SmartPointerEdgeCases)
+{
+ // Test SrsUniquePtr with custom deleter and NULL pointer
+ if (true) {
+ SrsUniquePtr ptr(NULL, mock_free_chars);
+ EXPECT_TRUE(ptr.get() == NULL);
+ // Should not crash when destroyed with NULL pointer
+ }
+
+ // Test SrsSharedPtr with NULL pointer in copy constructor
+ if (true) {
+ SrsSharedPtr p1(NULL);
+ SrsSharedPtr p2(p1);
+ EXPECT_FALSE(p1);
+ EXPECT_FALSE(p2);
+ EXPECT_EQ(p1.get(), p2.get());
+ }
+
+ // Test SrsSharedPtr with NULL pointer in assignment
+ if (true) {
+ SrsSharedPtr p1(NULL);
+ SrsSharedPtr p2(new int(42));
+ EXPECT_TRUE(p2);
+
+ p2 = p1;
+ EXPECT_FALSE(p1);
+ EXPECT_FALSE(p2);
+ }
+
+ // Test SrsSharedResource with NULL pointer in copy constructor
+ if (true) {
+ SrsSharedResource p1(NULL);
+ SrsSharedResource p2(p1);
+ EXPECT_FALSE(p1);
+ EXPECT_FALSE(p2);
+ EXPECT_EQ(p1.get(), p2.get());
+ }
+}
+
+VOID TEST(CoreSmartPtr, SmartPointerMemoryManagement)
+{
+ // Test that SrsUniquePtr properly manages memory
+ int *counter = new int(0);
+ SrsUniquePtr counter_uptr(counter);
+
+ if (true) {
+ SrsUniquePtr ptr(new MockWrapper(counter));
+ EXPECT_EQ(1, *counter);
+ }
+ EXPECT_EQ(0, *counter); // MockWrapper destructor should have decremented
+
+ // Test that SrsSharedPtr properly manages reference counting
+ if (true) {
+ SrsSharedPtr p1(new MockWrapper(counter));
+ EXPECT_EQ(1, *counter);
+
+ if (true) {
+ SrsSharedPtr p2 = p1;
+ EXPECT_EQ(1, *counter); // Same object, counter unchanged
+
+ SrsSharedPtr p3(new MockWrapper(counter));
+ EXPECT_EQ(2, *counter); // New object created
+ }
+ EXPECT_EQ(1, *counter); // p3 destroyed, one object remains
+ }
+ EXPECT_EQ(0, *counter); // All objects destroyed
+}
+
+VOID TEST(CoreSmartPtr, SmartPointerOperatorOverloads)
+{
+ // Test SrsSharedPtr dereference operator
+ if (true) {
+ SrsSharedPtr ptr(new int(42));
+ EXPECT_EQ(42, *ptr);
+
+ *ptr = 100;
+ EXPECT_EQ(100, *ptr);
+ }
+
+ // Test SrsSharedResource dereference operator
+ if (true) {
+ SrsSharedResource ptr(new MockIntResource(200));
+ EXPECT_EQ(200, (*ptr).value_);
+
+ (*ptr).value_ = 300;
+ EXPECT_EQ(300, (*ptr).value_);
+ }
+}
diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp
index 67b5939b0..5a81d6c43 100644
--- a/trunk/src/utest/srs_utest_kernel.cpp
+++ b/trunk/src/utest/srs_utest_kernel.cpp
@@ -3321,6 +3321,327 @@ VOID TEST(KernelLBRRTest, CoverAll)
}
}
+VOID TEST(KernelLBRRTest, InterfaceTest)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("s0");
+ servers.push_back("s1");
+ servers.push_back("s2");
+
+ SrsUniquePtr lb(new SrsLbRoundRobin());
+
+ // Test round-robin behavior through interface
+ EXPECT_TRUE("s0" == lb->select(servers));
+ EXPECT_TRUE("s1" == lb->select(servers));
+ EXPECT_TRUE("s2" == lb->select(servers));
+ EXPECT_TRUE("s0" == lb->select(servers)); // Should wrap around
+ }
+}
+
+VOID TEST(KernelLBRRTest, SingleServer)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("single-server");
+
+ SrsLbRoundRobin lb;
+
+ // Test with single server - should always return the same server
+ EXPECT_TRUE("single-server" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+ EXPECT_TRUE("single-server" == lb.selected());
+
+ EXPECT_TRUE("single-server" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+ EXPECT_TRUE("single-server" == lb.selected());
+
+ EXPECT_TRUE("single-server" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+ EXPECT_TRUE("single-server" == lb.selected());
+ }
+}
+
+VOID TEST(KernelLBRRTest, TwoServers)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("server-a");
+ servers.push_back("server-b");
+
+ SrsLbRoundRobin lb;
+
+ // Test alternating between two servers
+ EXPECT_TRUE("server-a" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+ EXPECT_TRUE("server-a" == lb.selected());
+
+ EXPECT_TRUE("server-b" == lb.select(servers));
+ EXPECT_EQ(1, (int)lb.current());
+ EXPECT_TRUE("server-b" == lb.selected());
+
+ EXPECT_TRUE("server-a" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+ EXPECT_TRUE("server-a" == lb.selected());
+
+ EXPECT_TRUE("server-b" == lb.select(servers));
+ EXPECT_EQ(1, (int)lb.current());
+ EXPECT_TRUE("server-b" == lb.selected());
+ }
+}
+
+VOID TEST(KernelLBRRTest, LargeServerList)
+{
+ if (true) {
+ vector servers;
+ // Create a list of 100 servers
+ for (int i = 0; i < 100; i++) {
+ char server_name[32];
+ snprintf(server_name, sizeof(server_name), "server-%d", i);
+ servers.push_back(string(server_name));
+ }
+
+ SrsLbRoundRobin lb;
+
+ // Test that we cycle through all servers correctly
+ for (int round = 0; round < 3; round++) {
+ for (int i = 0; i < 100; i++) {
+ char expected_name[32];
+ snprintf(expected_name, sizeof(expected_name), "server-%d", i);
+
+ string selected = lb.select(servers);
+ EXPECT_TRUE(expected_name == selected);
+ EXPECT_EQ(i, (int)lb.current());
+ EXPECT_TRUE(expected_name == lb.selected());
+ }
+ }
+ }
+}
+
+VOID TEST(KernelLBRRTest, LongRunningTest)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("s0");
+ servers.push_back("s1");
+ servers.push_back("s2");
+
+ SrsLbRoundRobin lb;
+
+ // Test long running behavior to ensure counter doesn't cause issues
+ // This simulates a long-running server that has been selecting for a while
+ const int large_iterations = 100000;
+ for (int i = 0; i < large_iterations; i++) {
+ string selected = lb.select(servers);
+ int expected_index = i % 3;
+ char expected_name[8];
+ snprintf(expected_name, sizeof(expected_name), "s%d", expected_index);
+
+ EXPECT_TRUE(expected_name == selected);
+ EXPECT_EQ(expected_index, (int)lb.current());
+
+ // Only check every 10000 iterations to avoid too much output
+ if (i % 10000 == 0) {
+ EXPECT_TRUE(expected_name == lb.selected());
+ }
+ }
+ }
+}
+
+VOID TEST(KernelLBRRTest, DifferentServerFormats)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("192.168.1.1:1935");
+ servers.push_back("rtmp://example.com/live");
+ servers.push_back("server.domain.com:8080");
+ servers.push_back("localhost");
+ servers.push_back("10.0.0.1:443");
+
+ SrsLbRoundRobin lb;
+
+ // Test with different server name formats
+ EXPECT_TRUE("192.168.1.1:1935" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+
+ EXPECT_TRUE("rtmp://example.com/live" == lb.select(servers));
+ EXPECT_EQ(1, (int)lb.current());
+
+ EXPECT_TRUE("server.domain.com:8080" == lb.select(servers));
+ EXPECT_EQ(2, (int)lb.current());
+
+ EXPECT_TRUE("localhost" == lb.select(servers));
+ EXPECT_EQ(3, (int)lb.current());
+
+ EXPECT_TRUE("10.0.0.1:443" == lb.select(servers));
+ EXPECT_EQ(4, (int)lb.current());
+
+ // Should wrap around
+ EXPECT_TRUE("192.168.1.1:1935" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+ }
+}
+
+VOID TEST(KernelLBRRTest, MultipleInstances)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("s0");
+ servers.push_back("s1");
+ servers.push_back("s2");
+
+ SrsLbRoundRobin lb1;
+ SrsLbRoundRobin lb2;
+
+ // Test that multiple instances work independently
+ EXPECT_TRUE("s0" == lb1.select(servers));
+ EXPECT_TRUE("s0" == lb2.select(servers));
+
+ EXPECT_TRUE("s1" == lb1.select(servers));
+ EXPECT_TRUE("s1" == lb2.select(servers));
+
+ // Advance lb1 further
+ EXPECT_TRUE("s2" == lb1.select(servers));
+ EXPECT_TRUE("s0" == lb1.select(servers));
+
+ // lb2 should still be at s2
+ EXPECT_TRUE("s2" == lb2.select(servers));
+
+ EXPECT_EQ(0, (int)lb1.current());
+ EXPECT_EQ(2, (int)lb2.current());
+ }
+}
+
+VOID TEST(KernelLBRRTest, StressTest)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("s0");
+ servers.push_back("s1");
+ servers.push_back("s2");
+ servers.push_back("s3");
+ servers.push_back("s4");
+
+ SrsLbRoundRobin lb;
+
+ // Perform many selections to test stability
+ const int iterations = 10000;
+ for (int i = 0; i < iterations; i++) {
+ string selected = lb.select(servers);
+ int expected_index = i % 5;
+ char expected_name[8];
+ snprintf(expected_name, sizeof(expected_name), "s%d", expected_index);
+
+ EXPECT_TRUE(expected_name == selected);
+ EXPECT_EQ(expected_index, (int)lb.current());
+ EXPECT_TRUE(expected_name == lb.selected());
+ }
+ }
+}
+
+VOID TEST(KernelLBRRTest, InterfacePolymorphism)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("server-a");
+ servers.push_back("server-b");
+ servers.push_back("server-c");
+
+ // Test polymorphism - using interface pointer to concrete implementation
+ SrsUniquePtr lb_interface(new SrsLbRoundRobin());
+ SrsLbRoundRobin lb_concrete;
+
+ // Both should behave identically
+ for (int i = 0; i < 6; i++) {
+ string interface_result = lb_interface->select(servers);
+ string concrete_result = lb_concrete.select(servers);
+
+ EXPECT_TRUE(interface_result == concrete_result);
+
+ int expected_index = i % 3;
+ char expected_name[16];
+ snprintf(expected_name, sizeof(expected_name), "server-%c", 'a' + expected_index);
+
+ EXPECT_TRUE(expected_name == interface_result);
+ EXPECT_TRUE(expected_name == concrete_result);
+ }
+ }
+}
+
+VOID TEST(KernelLBRRTest, SpecialServerNames)
+{
+ if (true) {
+ vector servers;
+ servers.push_back(""); // Empty string
+ servers.push_back("server with spaces");
+ servers.push_back("server-with-dashes");
+ servers.push_back("server_with_underscores");
+ servers.push_back("server.with.dots");
+ servers.push_back("server:with:colons");
+ servers.push_back("192.168.1.1");
+ servers.push_back("::1"); // IPv6 localhost
+
+ SrsLbRoundRobin lb;
+
+ // Test with special characters and formats
+ EXPECT_TRUE("" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+
+ EXPECT_TRUE("server with spaces" == lb.select(servers));
+ EXPECT_EQ(1, (int)lb.current());
+
+ EXPECT_TRUE("server-with-dashes" == lb.select(servers));
+ EXPECT_EQ(2, (int)lb.current());
+
+ EXPECT_TRUE("server_with_underscores" == lb.select(servers));
+ EXPECT_EQ(3, (int)lb.current());
+
+ EXPECT_TRUE("server.with.dots" == lb.select(servers));
+ EXPECT_EQ(4, (int)lb.current());
+
+ EXPECT_TRUE("server:with:colons" == lb.select(servers));
+ EXPECT_EQ(5, (int)lb.current());
+
+ EXPECT_TRUE("192.168.1.1" == lb.select(servers));
+ EXPECT_EQ(6, (int)lb.current());
+
+ EXPECT_TRUE("::1" == lb.select(servers));
+ EXPECT_EQ(7, (int)lb.current());
+
+ // Should wrap around to empty string
+ EXPECT_TRUE("" == lb.select(servers));
+ EXPECT_EQ(0, (int)lb.current());
+ }
+}
+
+VOID TEST(KernelLBRRTest, ConsistentState)
+{
+ if (true) {
+ vector servers;
+ servers.push_back("s0");
+ servers.push_back("s1");
+ servers.push_back("s2");
+
+ SrsLbRoundRobin lb;
+
+ // Test that current() and selected() remain consistent after each select()
+ for (int i = 0; i < 10; i++) {
+ string selected = lb.select(servers);
+ uint32_t current_index = lb.current();
+ string current_selected = lb.selected();
+
+ // Verify consistency
+ EXPECT_TRUE(selected == current_selected);
+ EXPECT_EQ(i % 3, (int)current_index);
+
+ char expected_name[8];
+ snprintf(expected_name, sizeof(expected_name), "s%d", i % 3);
+ EXPECT_TRUE(expected_name == selected);
+ EXPECT_TRUE(expected_name == current_selected);
+ }
+ }
+}
+
VOID TEST(KernelCodecTest, CoverAll)
{
if (true) {
@@ -6932,93 +7253,3 @@ VOID TEST(KernelUtilityTest, Base64Decode)
EXPECT_STRNE("admin:admin", plaintext.c_str());
}
}
-
-VOID TEST(KernelMemoryBlockTest, MemoryBlockBasic)
-{
-
- // Test basic construction and destruction
- if (true) {
- SrsMemoryBlock block;
- EXPECT_EQ(0, block.size());
- EXPECT_EQ(NULL, block.payload());
- }
-
- // Test create with size
- if (true) {
- SrsMemoryBlock block;
- block.create(1024);
- EXPECT_EQ(1024, block.size());
- EXPECT_NE((char *)NULL, block.payload());
- }
-
- // Test create with data
- if (true) {
- SrsMemoryBlock block;
- char test_data[] = "Hello, World!";
- int test_size = strlen(test_data);
-
- block.create(test_data, test_size);
- EXPECT_EQ(test_size, block.size());
- EXPECT_NE((char *)NULL, block.payload());
- EXPECT_EQ(0, memcmp(block.payload(), test_data, test_size));
- }
-
- // Test attach
- if (true) {
- SrsMemoryBlock block;
- char *test_data = new char[100];
- memset(test_data, 0x42, 100);
-
- block.attach(test_data, 100);
- EXPECT_EQ(100, block.size());
- EXPECT_EQ(test_data, block.payload());
-
- // Memory will be freed by block destructor
- }
-}
-
-VOID TEST(KernelMemoryBlockTest, SharedMemoryBlock)
-{
-
- // Test basic shared memory block usage
- if (true) {
- SrsSharedPtr shared_block(new SrsMemoryBlock());
- shared_block->create(1024);
-
- EXPECT_EQ(1024, shared_block->size());
- EXPECT_NE((char *)NULL, shared_block->payload());
-
- // Test sharing
- SrsSharedPtr shared_copy = shared_block;
- EXPECT_EQ(shared_block->payload(), shared_copy->payload());
- EXPECT_EQ(shared_block->size(), shared_copy->size());
- }
-
- // Test multiple references
- if (true) {
- SrsSharedPtr original(new SrsMemoryBlock());
- char test_data[] = "Shared memory test data";
- original->create(test_data, strlen(test_data));
-
- // Create multiple references
- SrsSharedPtr copy1 = original;
- SrsSharedPtr copy2 = original;
- SrsSharedPtr copy3 = copy1;
-
- // All should point to the same memory
- EXPECT_EQ(original->payload(), copy1->payload());
- EXPECT_EQ(original->payload(), copy2->payload());
- EXPECT_EQ(original->payload(), copy3->payload());
-
- // All should have the same size
- EXPECT_EQ(original->size(), copy1->size());
- EXPECT_EQ(original->size(), copy2->size());
- EXPECT_EQ(original->size(), copy3->size());
-
- // Verify data integrity
- EXPECT_EQ(0, memcmp(original->payload(), test_data, strlen(test_data)));
- EXPECT_EQ(0, memcmp(copy1->payload(), test_data, strlen(test_data)));
- EXPECT_EQ(0, memcmp(copy2->payload(), test_data, strlen(test_data)));
- EXPECT_EQ(0, memcmp(copy3->payload(), test_data, strlen(test_data)));
- }
-}
diff --git a/trunk/src/utest/srs_utest_kernel2.cpp b/trunk/src/utest/srs_utest_kernel2.cpp
index 8074fccf1..de725e46b 100644
--- a/trunk/src/utest/srs_utest_kernel2.cpp
+++ b/trunk/src/utest/srs_utest_kernel2.cpp
@@ -747,3 +747,1771 @@ VOID TEST(KernelCodecTest, HEVCDuplicatedCode)
EXPECT_NE(ERROR_HEVC_NALU_UEV, ERROR_STREAM_CASTER_HEVC_VPS);
EXPECT_NE(ERROR_HEVC_NALU_SEV, ERROR_STREAM_CASTER_HEVC_SPS);
}
+
+VOID TEST(KernelMemoryBlockTest, MemoryBlockBasic)
+{
+
+ // Test basic construction and destruction
+ if (true) {
+ SrsMemoryBlock block;
+ EXPECT_EQ(0, block.size());
+ EXPECT_EQ(NULL, block.payload());
+ }
+
+ // Test create with size
+ if (true) {
+ SrsMemoryBlock block;
+ block.create(1024);
+ EXPECT_EQ(1024, block.size());
+ EXPECT_NE((char *)NULL, block.payload());
+ }
+
+ // Test create with data
+ if (true) {
+ SrsMemoryBlock block;
+ char test_data[] = "Hello, World!";
+ int test_size = strlen(test_data);
+
+ block.create(test_data, test_size);
+ EXPECT_EQ(test_size, block.size());
+ EXPECT_NE((char *)NULL, block.payload());
+ EXPECT_EQ(0, memcmp(block.payload(), test_data, test_size));
+ }
+
+ // Test attach
+ if (true) {
+ SrsMemoryBlock block;
+ char *test_data = new char[100];
+ memset(test_data, 0x42, 100);
+
+ block.attach(test_data, 100);
+ EXPECT_EQ(100, block.size());
+ EXPECT_EQ(test_data, block.payload());
+
+ // Memory will be freed by block destructor
+ }
+}
+
+VOID TEST(KernelMemoryBlockTest, SharedMemoryBlock)
+{
+
+ // Test basic shared memory block usage
+ if (true) {
+ SrsSharedPtr shared_block(new SrsMemoryBlock());
+ shared_block->create(1024);
+
+ EXPECT_EQ(1024, shared_block->size());
+ EXPECT_NE((char *)NULL, shared_block->payload());
+
+ // Test sharing
+ SrsSharedPtr shared_copy = shared_block;
+ EXPECT_EQ(shared_block->payload(), shared_copy->payload());
+ EXPECT_EQ(shared_block->size(), shared_copy->size());
+ }
+
+ // Test multiple references
+ if (true) {
+ SrsSharedPtr original(new SrsMemoryBlock());
+ char test_data[] = "Shared memory test data";
+ original->create(test_data, strlen(test_data));
+
+ // Create multiple references
+ SrsSharedPtr copy1 = original;
+ SrsSharedPtr copy2 = original;
+ SrsSharedPtr copy3 = copy1;
+
+ // All should point to the same memory
+ EXPECT_EQ(original->payload(), copy1->payload());
+ EXPECT_EQ(original->payload(), copy2->payload());
+ EXPECT_EQ(original->payload(), copy3->payload());
+
+ // All should have the same size
+ EXPECT_EQ(original->size(), copy1->size());
+ EXPECT_EQ(original->size(), copy2->size());
+ EXPECT_EQ(original->size(), copy3->size());
+
+ // Verify data integrity
+ EXPECT_EQ(0, memcmp(original->payload(), test_data, strlen(test_data)));
+ EXPECT_EQ(0, memcmp(copy1->payload(), test_data, strlen(test_data)));
+ EXPECT_EQ(0, memcmp(copy2->payload(), test_data, strlen(test_data)));
+ EXPECT_EQ(0, memcmp(copy3->payload(), test_data, strlen(test_data)));
+ }
+}
+
+VOID TEST(KernelBufferTest, SrsBufferConstructorAndBasics)
+{
+ // Test constructor with valid data
+ char data[10] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
+ SrsBuffer buf(data, 10);
+
+ // Test basic properties
+ EXPECT_EQ(10, buf.size());
+ EXPECT_EQ(0, buf.pos());
+ EXPECT_EQ(10, buf.left());
+ EXPECT_FALSE(buf.empty());
+ EXPECT_EQ(data, buf.data());
+ EXPECT_EQ(data, buf.head());
+
+ // Test constructor with NULL data
+ SrsBuffer null_buf(NULL, 0);
+ EXPECT_EQ(0, null_buf.size());
+ EXPECT_EQ(0, null_buf.pos());
+ EXPECT_EQ(0, null_buf.left());
+ EXPECT_TRUE(null_buf.empty());
+ EXPECT_EQ(NULL, null_buf.data());
+ EXPECT_EQ(NULL, null_buf.head());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferCopy)
+{
+ char data[5] = {0x10, 0x20, 0x30, 0x40, 0x50};
+ SrsBuffer buf(data, 5);
+
+ // Move position forward
+ buf.read_1bytes();
+ buf.read_1bytes();
+ EXPECT_EQ(2, buf.pos());
+
+ // Test copy preserves position
+ SrsBuffer *copy = buf.copy();
+ EXPECT_EQ(5, copy->size());
+ EXPECT_EQ(2, copy->pos());
+ EXPECT_EQ(3, copy->left());
+ EXPECT_EQ(data, copy->data());
+ EXPECT_EQ(data + 2, copy->head());
+
+ // Verify copy is independent
+ copy->read_1bytes();
+ EXPECT_EQ(3, copy->pos());
+ EXPECT_EQ(2, buf.pos()); // Original unchanged
+
+ srs_freep(copy);
+}
+
+VOID TEST(KernelBufferTest, SrsBufferSizeAndSetSize)
+{
+ char data[10];
+ SrsBuffer buf(data, 10);
+
+ EXPECT_EQ(10, buf.size());
+
+ // Test set_size
+ buf.set_size(5);
+ EXPECT_EQ(5, buf.size());
+ EXPECT_EQ(5, buf.left());
+
+ // Test set_size with larger value
+ buf.set_size(15);
+ EXPECT_EQ(15, buf.size());
+ EXPECT_EQ(15, buf.left());
+
+ // Test set_size with zero
+ buf.set_size(0);
+ EXPECT_EQ(0, buf.size());
+ EXPECT_EQ(0, buf.left());
+ EXPECT_TRUE(buf.empty());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferPositionAndHead)
+{
+ char data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (char)0x88};
+ SrsBuffer buf(data, 8);
+
+ // Initial state
+ EXPECT_EQ(0, buf.pos());
+ EXPECT_EQ(data, buf.head());
+
+ // After reading some bytes
+ buf.read_1bytes();
+ EXPECT_EQ(1, buf.pos());
+ EXPECT_EQ(data + 1, buf.head());
+
+ buf.read_2bytes();
+ EXPECT_EQ(3, buf.pos());
+ EXPECT_EQ(data + 3, buf.head());
+
+ // Skip to end
+ buf.skip(5);
+ EXPECT_EQ(8, buf.pos());
+ EXPECT_EQ(data + 8, buf.head());
+ EXPECT_TRUE(buf.empty());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferLeftAndEmpty)
+{
+ char data[6];
+ SrsBuffer buf(data, 6);
+
+ // Initial state
+ EXPECT_EQ(6, buf.left());
+ EXPECT_FALSE(buf.empty());
+
+ // After reading
+ buf.read_1bytes();
+ EXPECT_EQ(5, buf.left());
+ EXPECT_FALSE(buf.empty());
+
+ buf.read_4bytes();
+ EXPECT_EQ(1, buf.left());
+ EXPECT_FALSE(buf.empty());
+
+ buf.read_1bytes();
+ EXPECT_EQ(0, buf.left());
+ EXPECT_TRUE(buf.empty());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferRequire)
+{
+ char data[10];
+ SrsBuffer buf(data, 10);
+
+ // Test require with available bytes
+ EXPECT_TRUE(buf.require(1));
+ EXPECT_TRUE(buf.require(10));
+ EXPECT_FALSE(buf.require(11));
+
+ // After consuming some bytes
+ buf.read_4bytes();
+ EXPECT_TRUE(buf.require(1));
+ EXPECT_TRUE(buf.require(6));
+ EXPECT_FALSE(buf.require(7));
+
+ // At the end
+ buf.read_4bytes();
+ buf.read_2bytes();
+ EXPECT_FALSE(buf.require(1));
+ EXPECT_TRUE(buf.require(0));
+
+ // Test require with zero and negative values
+ buf.skip(-10); // Reset to beginning
+ EXPECT_TRUE(buf.require(0));
+ // Note: According to header comment, require should assert for negative values
+ // but we won't test that as it would crash the test
+}
+
+VOID TEST(KernelBufferTest, SrsBufferSkipOperations)
+{
+ char data[10] = {0};
+ SrsBuffer buf(data, 10);
+
+ // Test basic properties first
+ EXPECT_EQ(0, buf.pos());
+ EXPECT_EQ(10, buf.size());
+ EXPECT_EQ(data, buf.data());
+
+ // Test simple forward skip
+ buf.skip(1);
+ EXPECT_EQ(1, buf.pos());
+ EXPECT_EQ(9, buf.left());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferRead1Bytes)
+{
+ char data[4] = {0x12, 0x34, 0x56, 0x78};
+ SrsBuffer buf(data, 4);
+
+ // Test reading 1-byte values
+ EXPECT_EQ(0x12, buf.read_1bytes());
+ EXPECT_EQ(1, buf.pos());
+
+ EXPECT_EQ(0x34, buf.read_1bytes());
+ EXPECT_EQ(2, buf.pos());
+
+ EXPECT_EQ(0x56, buf.read_1bytes());
+ EXPECT_EQ(3, buf.pos());
+
+ EXPECT_EQ(0x78, buf.read_1bytes());
+ EXPECT_EQ(4, buf.pos());
+ EXPECT_TRUE(buf.empty());
+
+ // Test with signed values
+ char signed_data[2] = {(char)0xFF, (char)0x80};
+ SrsBuffer signed_buf(signed_data, 2);
+
+ EXPECT_EQ(-1, signed_buf.read_1bytes());
+ EXPECT_EQ(-128, signed_buf.read_1bytes());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferRead2Bytes)
+{
+ char data[8] = {0x12, 0x34, 0x56, 0x78, (char)0x9A, (char)0xBC, (char)0xDE, (char)0xF0};
+ SrsBuffer buf(data, 8);
+
+ // Test big-endian 2-byte reads
+ EXPECT_EQ(0x1234, buf.read_2bytes());
+ EXPECT_EQ(2, buf.pos());
+
+ EXPECT_EQ(0x5678, buf.read_2bytes());
+ EXPECT_EQ(4, buf.pos());
+
+ // Test little-endian 2-byte reads
+ buf.skip(-4); // Reset to beginning
+ EXPECT_EQ(0x3412, buf.read_le2bytes());
+ EXPECT_EQ(2, buf.pos());
+
+ EXPECT_EQ(0x7856, buf.read_le2bytes());
+ EXPECT_EQ(4, buf.pos());
+
+ // Continue with remaining bytes
+ EXPECT_EQ((int16_t)0x9ABC, buf.read_2bytes());
+ EXPECT_EQ((int16_t)0xDEF0, buf.read_2bytes());
+ EXPECT_TRUE(buf.empty());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferRead3Bytes)
+{
+ char data[9] = {0x12, 0x34, 0x56, 0x78, (char)0x9A, (char)0xBC, (char)0xDE, (char)0xF0, 0x11};
+ SrsBuffer buf(data, 9);
+
+ // Test big-endian 3-byte reads
+ EXPECT_EQ(0x123456, buf.read_3bytes());
+ EXPECT_EQ(3, buf.pos());
+
+ EXPECT_EQ(0x789ABC, buf.read_3bytes());
+ EXPECT_EQ(6, buf.pos());
+
+ // Test little-endian 3-byte reads
+ buf.skip(-6); // Reset to beginning
+ EXPECT_EQ(0x563412, buf.read_le3bytes());
+ EXPECT_EQ(3, buf.pos());
+
+ EXPECT_EQ(0xBC9A78, buf.read_le3bytes());
+ EXPECT_EQ(6, buf.pos());
+
+ // Read remaining bytes
+ EXPECT_EQ(0xDEF011, buf.read_3bytes());
+ EXPECT_TRUE(buf.empty());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferRead4Bytes)
+{
+ char data[12] = {0x12, 0x34, 0x56, 0x78, (char)0x9A, (char)0xBC, (char)0xDE, (char)0xF0,
+ 0x11, 0x22, 0x33, 0x44};
+ SrsBuffer buf(data, 12);
+
+ // Test big-endian 4-byte reads
+ EXPECT_EQ(0x12345678, buf.read_4bytes());
+ EXPECT_EQ(4, buf.pos());
+
+ EXPECT_EQ((int32_t)0x9ABCDEF0, buf.read_4bytes());
+ EXPECT_EQ(8, buf.pos());
+
+ // Test little-endian 4-byte reads
+ buf.skip(-8); // Reset to beginning
+ EXPECT_EQ(0x78563412, buf.read_le4bytes());
+ EXPECT_EQ(4, buf.pos());
+
+ EXPECT_EQ((int32_t)0xF0DEBC9A, buf.read_le4bytes());
+ EXPECT_EQ(8, buf.pos());
+
+ // Read remaining bytes
+ EXPECT_EQ(0x11223344, buf.read_4bytes());
+ EXPECT_TRUE(buf.empty());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferRead8Bytes)
+{
+ char data[16] = {0x12, 0x34, 0x56, 0x78, (char)0x9A, (char)0xBC, (char)0xDE, (char)0xF0,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (char)0x88};
+ SrsBuffer buf(data, 16);
+
+ // Test big-endian 8-byte reads
+ EXPECT_EQ(0x123456789ABCDEF0LL, buf.read_8bytes());
+ EXPECT_EQ(8, buf.pos());
+
+ // Test little-endian 8-byte reads
+ buf.skip(-8); // Reset to beginning
+ EXPECT_EQ(0xF0DEBC9A78563412LL, buf.read_le8bytes());
+ EXPECT_EQ(8, buf.pos());
+
+ // Read remaining bytes
+ EXPECT_EQ(0x1122334455667788LL, buf.read_8bytes());
+ EXPECT_TRUE(buf.empty());
+
+ // Test with maximum values
+ char max_data[8] = {(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
+ (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF};
+ SrsBuffer max_buf(max_data, 8);
+ EXPECT_EQ(-1LL, max_buf.read_8bytes());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferReadString)
+{
+ char data[] = "Hello, World! This is a test string.";
+ SrsBuffer buf(data, strlen(data));
+
+ // Test reading strings of various lengths
+ std::string str1 = buf.read_string(5);
+ EXPECT_STREQ("Hello", str1.c_str());
+ EXPECT_EQ(5, buf.pos());
+
+ std::string str2 = buf.read_string(2);
+ EXPECT_STREQ(", ", str2.c_str());
+ EXPECT_EQ(7, buf.pos());
+
+ std::string str3 = buf.read_string(6);
+ EXPECT_STREQ("World!", str3.c_str());
+ EXPECT_EQ(13, buf.pos());
+
+ // Test reading zero-length string
+ std::string empty_str = buf.read_string(0);
+ EXPECT_STREQ("", empty_str.c_str());
+ EXPECT_EQ(13, buf.pos()); // Position unchanged
+
+ // Test reading remaining string
+ int remaining = buf.left();
+ std::string remaining_str = buf.read_string(remaining);
+ EXPECT_STREQ(" This is a test string.", remaining_str.c_str());
+ EXPECT_TRUE(buf.empty());
+
+ // Test with binary data containing null bytes
+ char binary_data[6] = {0x41, 0x00, 0x42, 0x00, 0x43, 0x44};
+ SrsBuffer binary_buf(binary_data, 6);
+ std::string binary_str = binary_buf.read_string(6);
+ EXPECT_EQ(6, binary_str.length());
+ EXPECT_EQ(0x41, binary_str[0]);
+ EXPECT_EQ(0x00, binary_str[1]);
+ EXPECT_EQ(0x42, binary_str[2]);
+ EXPECT_EQ(0x00, binary_str[3]);
+ EXPECT_EQ(0x43, binary_str[4]);
+ EXPECT_EQ(0x44, binary_str[5]);
+}
+
+VOID TEST(KernelBufferTest, SrsBufferReadBytes)
+{
+ char data[10] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, (char)0x80, (char)0x90, (char)0xA0};
+ SrsBuffer buf(data, 10);
+
+ // Test reading bytes into buffer
+ char output[4];
+ buf.read_bytes(output, 4);
+ EXPECT_EQ(0x10, output[0]);
+ EXPECT_EQ(0x20, output[1]);
+ EXPECT_EQ(0x30, output[2]);
+ EXPECT_EQ(0x40, output[3]);
+ EXPECT_EQ(4, buf.pos());
+
+ // Test reading more bytes
+ char output2[3];
+ buf.read_bytes(output2, 3);
+ EXPECT_EQ(0x50, output2[0]);
+ EXPECT_EQ(0x60, output2[1]);
+ EXPECT_EQ(0x70, output2[2]);
+ EXPECT_EQ(7, buf.pos());
+
+ // Test reading zero bytes
+ char output3[1] = {(char)0xFF};
+ buf.read_bytes(output3, 0);
+ EXPECT_EQ((char)0xFF, output3[0]); // Should be unchanged
+ EXPECT_EQ(7, buf.pos()); // Position unchanged
+
+ // Test reading remaining bytes
+ char output4[3];
+ buf.read_bytes(output4, 3);
+ EXPECT_EQ((char)0x80, output4[0]);
+ EXPECT_EQ((char)0x90, output4[1]);
+ EXPECT_EQ((char)0xA0, output4[2]);
+ EXPECT_TRUE(buf.empty());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferWrite1Bytes)
+{
+ char data[8];
+ SrsBuffer buf(data, 8);
+
+ // Test writing 1-byte values
+ buf.write_1bytes(0x12);
+ EXPECT_EQ(1, buf.pos());
+ EXPECT_EQ(0x12, data[0]);
+
+ buf.write_1bytes(0x34);
+ EXPECT_EQ(2, buf.pos());
+ EXPECT_EQ(0x34, data[1]);
+
+ // Test writing signed values
+ buf.write_1bytes(-1);
+ EXPECT_EQ(3, buf.pos());
+ EXPECT_EQ((char)0xFF, data[2]);
+
+ buf.write_1bytes(-128);
+ EXPECT_EQ(4, buf.pos());
+ EXPECT_EQ((char)0x80, data[3]);
+
+ // Test writing maximum positive value
+ buf.write_1bytes(127);
+ EXPECT_EQ(5, buf.pos());
+ EXPECT_EQ(0x7F, data[4]);
+
+ // Verify we can read back what we wrote
+ buf.skip(-5); // Reset to beginning
+ EXPECT_EQ(0x12, buf.read_1bytes());
+ EXPECT_EQ(0x34, buf.read_1bytes());
+ EXPECT_EQ(-1, buf.read_1bytes());
+ EXPECT_EQ(-128, buf.read_1bytes());
+ EXPECT_EQ(127, buf.read_1bytes());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferWrite2Bytes)
+{
+ char data[16];
+ SrsBuffer buf(data, 16);
+
+ // Test big-endian 2-byte writes
+ buf.write_2bytes(0x1234);
+ EXPECT_EQ(2, buf.pos());
+ EXPECT_EQ(0x12, data[0]);
+ EXPECT_EQ(0x34, data[1]);
+
+ buf.write_2bytes(0x5678);
+ EXPECT_EQ(4, buf.pos());
+ EXPECT_EQ(0x56, data[2]);
+ EXPECT_EQ(0x78, data[3]);
+
+ // Test little-endian 2-byte writes
+ buf.write_le2bytes(0x9ABC);
+ EXPECT_EQ(6, buf.pos());
+ EXPECT_EQ((char)0xBC, data[4]);
+ EXPECT_EQ((char)0x9A, data[5]);
+
+ buf.write_le2bytes(0xDEF0);
+ EXPECT_EQ(8, buf.pos());
+ EXPECT_EQ((char)0xF0, data[6]);
+ EXPECT_EQ((char)0xDE, data[7]);
+
+ // Test with negative values
+ buf.write_2bytes(-1);
+ EXPECT_EQ(10, buf.pos());
+ EXPECT_EQ((char)0xFF, data[8]);
+ EXPECT_EQ((char)0xFF, data[9]);
+
+ // Verify we can read back what we wrote
+ buf.skip(-10); // Reset to beginning
+ EXPECT_EQ(0x1234, buf.read_2bytes());
+ EXPECT_EQ(0x5678, buf.read_2bytes());
+ EXPECT_EQ((int16_t)0x9ABC, buf.read_le2bytes());
+ EXPECT_EQ((int16_t)0xDEF0, buf.read_le2bytes());
+ EXPECT_EQ(-1, buf.read_2bytes());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferWrite3Bytes)
+{
+ char data[18];
+ SrsBuffer buf(data, 18);
+
+ // Test big-endian 3-byte writes
+ buf.write_3bytes(0x123456);
+ EXPECT_EQ(3, buf.pos());
+ EXPECT_EQ(0x12, data[0]);
+ EXPECT_EQ(0x34, data[1]);
+ EXPECT_EQ(0x56, data[2]);
+
+ buf.write_3bytes(0x789ABC);
+ EXPECT_EQ(6, buf.pos());
+ EXPECT_EQ(0x78, data[3]);
+ EXPECT_EQ((char)0x9A, data[4]);
+ EXPECT_EQ((char)0xBC, data[5]);
+
+ // Test little-endian 3-byte writes
+ buf.write_le3bytes(0xDEF012);
+ EXPECT_EQ(9, buf.pos());
+ EXPECT_EQ(0x12, data[6]);
+ EXPECT_EQ((char)0xF0, data[7]);
+ EXPECT_EQ((char)0xDE, data[8]);
+
+ buf.write_le3bytes(0x345678);
+ EXPECT_EQ(12, buf.pos());
+ EXPECT_EQ(0x78, data[9]);
+ EXPECT_EQ(0x56, data[10]);
+ EXPECT_EQ(0x34, data[11]);
+
+ // Test with maximum 3-byte value
+ buf.write_3bytes(0xFFFFFF);
+ EXPECT_EQ(15, buf.pos());
+ EXPECT_EQ((char)0xFF, data[12]);
+ EXPECT_EQ((char)0xFF, data[13]);
+ EXPECT_EQ((char)0xFF, data[14]);
+
+ // Verify we can read back what we wrote
+ buf.skip(-15); // Reset to beginning
+ EXPECT_EQ(0x123456, buf.read_3bytes());
+ EXPECT_EQ(0x789ABC, buf.read_3bytes());
+ EXPECT_EQ(0xDEF012, buf.read_le3bytes());
+ EXPECT_EQ(0x345678, buf.read_le3bytes());
+ EXPECT_EQ(0xFFFFFF, buf.read_3bytes());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferWrite4Bytes)
+{
+ char data[24];
+ SrsBuffer buf(data, 24);
+
+ // Test big-endian 4-byte writes
+ buf.write_4bytes(0x12345678);
+ EXPECT_EQ(4, buf.pos());
+ EXPECT_EQ(0x12, data[0]);
+ EXPECT_EQ(0x34, data[1]);
+ EXPECT_EQ(0x56, data[2]);
+ EXPECT_EQ(0x78, data[3]);
+
+ buf.write_4bytes((int32_t)0x9ABCDEF0);
+ EXPECT_EQ(8, buf.pos());
+ EXPECT_EQ((char)0x9A, data[4]);
+ EXPECT_EQ((char)0xBC, data[5]);
+ EXPECT_EQ((char)0xDE, data[6]);
+ EXPECT_EQ((char)0xF0, data[7]);
+
+ // Test little-endian 4-byte writes
+ buf.write_le4bytes(0x11223344);
+ EXPECT_EQ(12, buf.pos());
+ EXPECT_EQ(0x44, data[8]);
+ EXPECT_EQ(0x33, data[9]);
+ EXPECT_EQ(0x22, data[10]);
+ EXPECT_EQ(0x11, data[11]);
+
+ buf.write_le4bytes(0x55667788);
+ EXPECT_EQ(16, buf.pos());
+ EXPECT_EQ((char)0x88, data[12]);
+ EXPECT_EQ(0x77, data[13]);
+ EXPECT_EQ(0x66, data[14]);
+ EXPECT_EQ(0x55, data[15]);
+
+ // Test with negative values
+ buf.write_4bytes(-1);
+ EXPECT_EQ(20, buf.pos());
+ EXPECT_EQ((char)0xFF, data[16]);
+ EXPECT_EQ((char)0xFF, data[17]);
+ EXPECT_EQ((char)0xFF, data[18]);
+ EXPECT_EQ((char)0xFF, data[19]);
+
+ // Verify we can read back what we wrote
+ buf.skip(-20); // Reset to beginning
+ EXPECT_EQ(0x12345678, buf.read_4bytes());
+ EXPECT_EQ((int32_t)0x9ABCDEF0, buf.read_4bytes());
+ EXPECT_EQ(0x11223344, buf.read_le4bytes());
+ EXPECT_EQ(0x55667788, buf.read_le4bytes());
+ EXPECT_EQ(-1, buf.read_4bytes());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferWrite8Bytes)
+{
+ char data[32];
+ SrsBuffer buf(data, 32);
+
+ // Test big-endian 8-byte writes
+ buf.write_8bytes(0x123456789ABCDEF0LL);
+ EXPECT_EQ(8, buf.pos());
+ EXPECT_EQ(0x12, data[0]);
+ EXPECT_EQ(0x34, data[1]);
+ EXPECT_EQ(0x56, data[2]);
+ EXPECT_EQ(0x78, data[3]);
+ EXPECT_EQ((char)0x9A, data[4]);
+ EXPECT_EQ((char)0xBC, data[5]);
+ EXPECT_EQ((char)0xDE, data[6]);
+ EXPECT_EQ((char)0xF0, data[7]);
+
+ buf.write_8bytes(0x1122334455667788LL);
+ EXPECT_EQ(16, buf.pos());
+ EXPECT_EQ(0x11, data[8]);
+ EXPECT_EQ(0x22, data[9]);
+ EXPECT_EQ(0x33, data[10]);
+ EXPECT_EQ(0x44, data[11]);
+ EXPECT_EQ(0x55, data[12]);
+ EXPECT_EQ(0x66, data[13]);
+ EXPECT_EQ(0x77, data[14]);
+ EXPECT_EQ((char)0x88, data[15]);
+
+ // Test little-endian 8-byte writes
+ buf.write_le8bytes(0xAABBCCDDEEFF0011LL);
+ EXPECT_EQ(24, buf.pos());
+ EXPECT_EQ(0x11, data[16]);
+ EXPECT_EQ(0x00, data[17]);
+ EXPECT_EQ((char)0xFF, data[18]);
+ EXPECT_EQ((char)0xEE, data[19]);
+ EXPECT_EQ((char)0xDD, data[20]);
+ EXPECT_EQ((char)0xCC, data[21]);
+ EXPECT_EQ((char)0xBB, data[22]);
+ EXPECT_EQ((char)0xAA, data[23]);
+
+ // Test with negative values
+ buf.write_8bytes(-1LL);
+ EXPECT_EQ(32, buf.pos());
+ for (int i = 24; i < 32; i++) {
+ EXPECT_EQ((char)0xFF, data[i]);
+ }
+
+ // Verify we can read back what we wrote
+ buf.skip(-32); // Reset to beginning
+ EXPECT_EQ(0x123456789ABCDEF0LL, buf.read_8bytes());
+ EXPECT_EQ(0x1122334455667788LL, buf.read_8bytes());
+ EXPECT_EQ((int64_t)0xAABBCCDDEEFF0011LL, buf.read_le8bytes());
+ EXPECT_EQ(-1LL, buf.read_8bytes());
+}
+
+VOID TEST(KernelBufferTest, SrsBufferWriteString)
+{
+ char data[64];
+ SrsBuffer buf(data, 64);
+
+ // Test writing simple string
+ std::string str1 = "Hello";
+ buf.write_string(str1);
+ EXPECT_EQ(5, buf.pos());
+ EXPECT_EQ('H', data[0]);
+ EXPECT_EQ('e', data[1]);
+ EXPECT_EQ('l', data[2]);
+ EXPECT_EQ('l', data[3]);
+ EXPECT_EQ('o', data[4]);
+
+ // Test writing string with special characters
+ std::string str2 = ", World!";
+ buf.write_string(str2);
+ EXPECT_EQ(13, buf.pos());
+ EXPECT_EQ(',', data[5]);
+ EXPECT_EQ(' ', data[6]);
+ EXPECT_EQ('W', data[7]);
+ EXPECT_EQ('!', data[12]);
+
+ // Test writing empty string
+ std::string empty_str = "";
+ buf.write_string(empty_str);
+ EXPECT_EQ(13, buf.pos()); // Position unchanged
+
+ // Test writing string with null bytes
+ std::string binary_str;
+ binary_str.push_back(0x41);
+ binary_str.push_back(0x00);
+ binary_str.push_back(0x42);
+ binary_str.push_back(0x00);
+ binary_str.push_back(0x43);
+ buf.write_string(binary_str);
+ EXPECT_EQ(18, buf.pos());
+ EXPECT_EQ(0x41, data[13]);
+ EXPECT_EQ(0x00, data[14]);
+ EXPECT_EQ(0x42, data[15]);
+ EXPECT_EQ(0x00, data[16]);
+ EXPECT_EQ(0x43, data[17]);
+
+ // Verify we can read back what we wrote
+ buf.skip(-18); // Reset to beginning
+ std::string read_str1 = buf.read_string(5);
+ EXPECT_STREQ("Hello", read_str1.c_str());
+ std::string read_str2 = buf.read_string(8);
+ EXPECT_STREQ(", World!", read_str2.c_str());
+ std::string read_binary = buf.read_string(5);
+ EXPECT_EQ(5, read_binary.length());
+ EXPECT_EQ(0x41, read_binary[0]);
+ EXPECT_EQ(0x00, read_binary[1]);
+ EXPECT_EQ(0x42, read_binary[2]);
+ EXPECT_EQ(0x00, read_binary[3]);
+ EXPECT_EQ(0x43, read_binary[4]);
+}
+
+VOID TEST(KernelBufferTest, SrsBufferWriteBytes)
+{
+ char data[32];
+ SrsBuffer buf(data, 32);
+
+ // Test writing byte array
+ char input1[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
+ buf.write_bytes(input1, 6);
+ EXPECT_EQ(6, buf.pos());
+ for (int i = 0; i < 6; i++) {
+ EXPECT_EQ(input1[i], data[i]);
+ }
+
+ // Test writing another byte array
+ char input2[4] = {0x70, (char)0x80, (char)0x90, (char)0xA0};
+ buf.write_bytes(input2, 4);
+ EXPECT_EQ(10, buf.pos());
+ for (int i = 0; i < 4; i++) {
+ EXPECT_EQ(input2[i], data[6 + i]);
+ }
+
+ // Test writing zero bytes
+ char input3[1] = {(char)0xFF};
+ buf.write_bytes(input3, 0);
+ EXPECT_EQ(10, buf.pos()); // Position unchanged
+
+ // Test writing bytes with null values
+ char input4[5] = {(char)0xB0, 0x00, (char)0xC0, 0x00, (char)0xD0};
+ buf.write_bytes(input4, 5);
+ EXPECT_EQ(15, buf.pos());
+ for (int i = 0; i < 5; i++) {
+ EXPECT_EQ(input4[i], data[10 + i]);
+ }
+
+ // Verify we can read back what we wrote
+ buf.skip(-15); // Reset to beginning
+ char output1[6];
+ buf.read_bytes(output1, 6);
+ for (int i = 0; i < 6; i++) {
+ EXPECT_EQ(input1[i], output1[i]);
+ }
+
+ char output2[4];
+ buf.read_bytes(output2, 4);
+ for (int i = 0; i < 4; i++) {
+ EXPECT_EQ(input2[i], output2[i]);
+ }
+
+ char output4[5];
+ buf.read_bytes(output4, 5);
+ for (int i = 0; i < 5; i++) {
+ EXPECT_EQ(input4[i], output4[i]);
+ }
+}
+
+VOID TEST(KernelBufferTest, SrsBufferEdgeCases)
+{
+ // Test with very small buffer
+ char small_data[1];
+ SrsBuffer small_buf(small_data, 1);
+
+ small_buf.write_1bytes(0x42);
+ EXPECT_EQ(1, small_buf.pos());
+ EXPECT_TRUE(small_buf.empty());
+
+ small_buf.skip(-1);
+ EXPECT_EQ(0x42, small_buf.read_1bytes());
+ EXPECT_TRUE(small_buf.empty());
+
+ // Test position tracking with mixed operations
+ char mixed_data[16];
+ SrsBuffer mixed_buf(mixed_data, 16);
+
+ mixed_buf.write_4bytes(0x12345678);
+ EXPECT_EQ(4, mixed_buf.pos());
+
+ mixed_buf.skip(-2);
+ EXPECT_EQ(2, mixed_buf.pos());
+ EXPECT_EQ(0x5678, mixed_buf.read_2bytes());
+ EXPECT_EQ(4, mixed_buf.pos());
+
+ mixed_buf.write_2bytes((int16_t)0x9ABC);
+ EXPECT_EQ(6, mixed_buf.pos());
+
+ // Verify data integrity
+ mixed_buf.skip(-6);
+ EXPECT_EQ(0x12345678, mixed_buf.read_4bytes());
+ EXPECT_EQ((int16_t)0x9ABC, mixed_buf.read_2bytes());
+
+ // Test boundary conditions
+ char boundary_data[8];
+ SrsBuffer boundary_buf(boundary_data, 8);
+
+ // Fill buffer completely
+ boundary_buf.write_8bytes(0x123456789ABCDEF0LL);
+ EXPECT_EQ(8, boundary_buf.pos());
+ EXPECT_TRUE(boundary_buf.empty());
+ EXPECT_EQ(0, boundary_buf.left());
+
+ // Reset and verify
+ boundary_buf.skip(-8);
+ EXPECT_EQ(0, boundary_buf.pos());
+ EXPECT_FALSE(boundary_buf.empty());
+ EXPECT_EQ(8, boundary_buf.left());
+ EXPECT_EQ(0x123456789ABCDEF0LL, boundary_buf.read_8bytes());
+}
+
+VOID TEST(KernelBitBufferTest, Constructor)
+{
+ char data[4] = {0x12, 0x34, 0x56, 0x78};
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ // Test initial state
+ EXPECT_FALSE(bb.empty());
+ EXPECT_EQ(32, bb.left_bits());
+}
+
+VOID TEST(KernelBitBufferTest, EmptyBuffer)
+{
+ SrsBuffer buffer(NULL, 0);
+ SrsBitBuffer bb(&buffer);
+
+ EXPECT_TRUE(bb.empty());
+ EXPECT_EQ(0, bb.left_bits());
+}
+
+VOID TEST(KernelBitBufferTest, ReadBit)
+{
+ // Test reading individual bits from 0x80 (10000000)
+ char data[1] = {(char)0x80};
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ EXPECT_EQ(1, bb.read_bit()); // First bit is 1
+ EXPECT_EQ(0, bb.read_bit()); // Second bit is 0
+ EXPECT_EQ(0, bb.read_bit()); // Third bit is 0
+ EXPECT_EQ(0, bb.read_bit()); // Fourth bit is 0
+ EXPECT_EQ(0, bb.read_bit()); // Fifth bit is 0
+ EXPECT_EQ(0, bb.read_bit()); // Sixth bit is 0
+ EXPECT_EQ(0, bb.read_bit()); // Seventh bit is 0
+ EXPECT_EQ(0, bb.read_bit()); // Eighth bit is 0
+
+ EXPECT_TRUE(bb.empty());
+ EXPECT_EQ(0, bb.left_bits());
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitMultipleBytes)
+{
+ // Test reading bits across byte boundaries: 0xFF00 (11111111 00000000)
+ char data[2] = {(char)0xFF, 0x00};
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ // Read first 8 bits (all 1s)
+ for (int i = 0; i < 8; i++) {
+ EXPECT_EQ(1, bb.read_bit());
+ }
+
+ // Read next 8 bits (all 0s)
+ for (int i = 0; i < 8; i++) {
+ EXPECT_EQ(0, bb.read_bit());
+ }
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, RequireBits)
+{
+ char data[4] = {0x12, 0x34, 0x56, 0x78};
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ EXPECT_TRUE(bb.require_bits(1));
+ EXPECT_TRUE(bb.require_bits(32));
+ EXPECT_FALSE(bb.require_bits(33));
+ EXPECT_FALSE(bb.require_bits(-1));
+
+ // After reading some bits
+ bb.read_bits(16);
+ EXPECT_TRUE(bb.require_bits(16));
+ EXPECT_FALSE(bb.require_bits(17));
+}
+
+VOID TEST(KernelBitBufferTest, SkipBits)
+{
+ char data[2] = {0x12, 0x34};
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ EXPECT_EQ(16, bb.left_bits());
+ bb.skip_bits(4);
+ EXPECT_EQ(12, bb.left_bits());
+
+ bb.skip_bits(8);
+ EXPECT_EQ(4, bb.left_bits());
+
+ bb.skip_bits(4);
+ EXPECT_EQ(0, bb.left_bits());
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, ReadBits)
+{
+ // Test reading 0x12 (00010010) bit by bit and in groups
+ char data[1] = {0x12};
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ // Read first 4 bits: 0001 = 1
+ int32_t value = bb.read_bits(4);
+ EXPECT_EQ(1, value);
+
+ // Read next 4 bits: 0010 = 2
+ value = bb.read_bits(4);
+ EXPECT_EQ(2, value);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsLargeValue)
+{
+ // Test reading 0x12345678
+ char data[4] = {0x12, 0x34, 0x56, 0x78};
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = bb.read_bits(32);
+ EXPECT_EQ(0x12345678, value);
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsAcrossByteBoundary)
+{
+ // Test reading bits that span across byte boundaries
+ // 0x12 = 00010010, 0x34 = 00110100
+ char data[2] = {0x12, 0x34};
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ // Read 12 bits: 000100100011 = 0x123
+ int32_t value = bb.read_bits(12);
+ EXPECT_EQ(0x123, value);
+
+ // Read remaining 4 bits: 0100 = 4
+ value = bb.read_bits(4);
+ EXPECT_EQ(4, value);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, Read8Bits)
+{
+ char data[2] = {0x12, 0x34};
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ // Test fast path (byte-aligned)
+ int8_t value = bb.read_8bits();
+ EXPECT_EQ(0x12, value);
+
+ value = bb.read_8bits();
+ EXPECT_EQ(0x34, value);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, Read8BitsNonAligned)
+{
+ char data[2] = {0x12, 0x34};
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ // Read 4 bits first to make it non-aligned
+ bb.read_bits(4);
+
+ // Now read 8 bits (should use slow path)
+ int8_t value = bb.read_8bits();
+ EXPECT_EQ(0x23, value); // 0010 0011 from remaining bits
+
+ // Read remaining 4 bits
+ int32_t remaining = bb.read_bits(4);
+ EXPECT_EQ(4, remaining);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, Read16Bits)
+{
+ char data[4] = {0x12, 0x34, 0x56, 0x78};
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ // Test fast path (byte-aligned)
+ int16_t value = bb.read_16bits();
+ EXPECT_EQ(0x1234, value);
+
+ value = bb.read_16bits();
+ EXPECT_EQ(0x5678, value);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, Read16BitsNonAligned)
+{
+ char data[3] = {0x12, 0x34, 0x56};
+ SrsBuffer buffer(data, 3);
+ SrsBitBuffer bb(&buffer);
+
+ // Read 4 bits first to make it non-aligned
+ bb.read_bits(4);
+
+ // Now read 16 bits (should use slow path)
+ int16_t value = bb.read_16bits();
+ EXPECT_EQ(0x2345, value); // From remaining bits
+
+ // Read remaining 4 bits
+ int32_t remaining = bb.read_bits(4);
+ EXPECT_EQ(6, remaining);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, Read32Bits)
+{
+ char data[8] = {0x12, 0x34, 0x56, 0x78, (char)0x9A, (char)0xBC, (char)0xDE, (char)0xF0};
+ SrsBuffer buffer(data, 8);
+ SrsBitBuffer bb(&buffer);
+
+ // Test fast path (byte-aligned)
+ int32_t value = bb.read_32bits();
+ EXPECT_EQ(0x12345678, value);
+
+ value = bb.read_32bits();
+ EXPECT_EQ((int32_t)0x9ABCDEF0, value);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, Read32BitsNonAligned)
+{
+ char data[5] = {0x12, 0x34, 0x56, 0x78, (char)0x9A};
+ SrsBuffer buffer(data, 5);
+ SrsBitBuffer bb(&buffer);
+
+ // Read 4 bits first to make it non-aligned
+ bb.read_bits(4);
+
+ // Now read 32 bits (should use slow path)
+ int32_t value = bb.read_32bits();
+ EXPECT_EQ(0x23456789, value); // From remaining bits
+
+ // Read remaining 4 bits
+ int32_t remaining = bb.read_bits(4);
+ EXPECT_EQ(0xA, remaining);
+
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsUE_BasicValues)
+{
+ srs_error_t err;
+
+ // Test ue(v) = 0: encoded as "1" (binary)
+ if (true) {
+ char data[1] = {(char)0x80}; // 10000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(0, (int)value);
+ }
+
+ // Test ue(v) = 1: encoded as "010" (binary)
+ if (true) {
+ char data[1] = {0x40}; // 01000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(1, (int)value);
+ }
+
+ // Test ue(v) = 2: encoded as "011" (binary)
+ if (true) {
+ char data[1] = {0x60}; // 01100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(2, (int)value);
+ }
+
+ // Test ue(v) = 3: encoded as "00100" (binary)
+ if (true) {
+ char data[1] = {0x20}; // 00100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(3, (int)value);
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsUE_LargerValues)
+{
+ srs_error_t err;
+
+ // Test ue(v) = 6: encoded as "00111" (binary) - 0x38 = 00111000
+ if (true) {
+ char data[1] = {0x38}; // 00111000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(6, (int)value);
+ }
+
+ // Test ue(v) = 7: encoded as "00100" (binary) - 0x10 = 00010000
+ if (true) {
+ char data[1] = {0x10}; // 00010000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(7, (int)value);
+ }
+
+ // Test larger value that spans multiple bytes
+ if (true) {
+ char data[2] = {0x01, 0x12}; // 00000001 00010010
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ // From existing tests: 0x01, 0x12 should give 128 - 1 + 9 = 136
+ EXPECT_EQ(136, (int)value);
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsUE_ErrorCases)
+{
+ srs_error_t err;
+
+ // Test empty buffer
+ if (true) {
+ SrsBuffer buffer(NULL, 0);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_FAILED(bb.read_bits_ue(value));
+ }
+
+ // Test overflow case (too many leading zeros)
+ if (true) {
+ char data[4] = {0x00, 0x00, 0x00, 0x01}; // 31+ leading zeros
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_FAILED(bb.read_bits_ue(value));
+ }
+
+ // Test insufficient data for leadingZeroBits
+ if (true) {
+ char data[1] = {0x01}; // 00000001 - needs more data
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_FAILED(bb.read_bits_ue(value));
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsSE_BasicValues)
+{
+ srs_error_t err;
+
+ // Test se(v) = 0: same as ue(v) = 0, encoded as "1"
+ if (true) {
+ char data[1] = {(char)0x80}; // 10000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(0, value);
+ }
+
+ // Test se(v) = 1: ue(v) = 1, encoded as "010"
+ if (true) {
+ char data[1] = {0x40}; // 01000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(1, value);
+ }
+
+ // Test se(v) = -1: ue(v) = 2, encoded as "011"
+ if (true) {
+ char data[1] = {0x60}; // 01100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(-1, value);
+ }
+
+ // Test se(v) = 2: ue(v) = 3, encoded as "00100"
+ if (true) {
+ char data[1] = {0x20}; // 00100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(2, value);
+ }
+
+ // Test se(v) = -2: ue(v) = 4, encoded as "00101"
+ if (true) {
+ char data[1] = {0x28}; // 00101000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(-2, value);
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsSE_ErrorCases)
+{
+ srs_error_t err;
+
+ // Test empty buffer
+ if (true) {
+ SrsBuffer buffer(NULL, 0);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_FAILED(bb.read_bits_se(value));
+ }
+
+ // Test error propagation from read_bits_ue
+ if (true) {
+ char data[4] = {0x00, 0x00, 0x00, 0x01}; // Will cause overflow in ue
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_FAILED(bb.read_bits_se(value));
+ }
+}
+
+VOID TEST(KernelBitBufferTest, EdgeCases)
+{
+ // Test reading from single bit buffer
+ if (true) {
+ char data[1] = {(char)0x80}; // 10000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ EXPECT_EQ(1, bb.read_bit());
+ EXPECT_EQ(7, bb.left_bits());
+ EXPECT_FALSE(bb.empty());
+
+ // Read remaining bits
+ for (int i = 0; i < 7; i++) {
+ EXPECT_EQ(0, bb.read_bit());
+ }
+
+ EXPECT_TRUE(bb.empty());
+ EXPECT_EQ(0, bb.left_bits());
+ }
+
+ // Test mixed operations
+ if (true) {
+ char data[4] = {0x12, 0x34, 0x56, 0x78};
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ // Read some bits
+ int32_t bits = bb.read_bits(4);
+ EXPECT_EQ(1, bits); // First 4 bits of 0x12
+
+ // Skip some bits
+ bb.skip_bits(4);
+
+ // Read a byte (should use slow path since not aligned)
+ int8_t byte_val = bb.read_8bits();
+ EXPECT_EQ(0x34, byte_val);
+
+ // Read remaining bits
+ int32_t remaining = bb.read_bits(16);
+ EXPECT_EQ(0x5678, remaining);
+
+ EXPECT_TRUE(bb.empty());
+ }
+}
+
+VOID TEST(KernelBitBufferTest, LeftBitsCalculation)
+{
+ char data[3] = {0x12, 0x34, 0x56};
+ SrsBuffer buffer(data, 3);
+ SrsBitBuffer bb(&buffer);
+
+ EXPECT_EQ(24, bb.left_bits());
+
+ // Read some bits and verify left_bits calculation
+ bb.read_bits(5);
+ EXPECT_EQ(19, bb.left_bits());
+
+ bb.read_bits(8);
+ EXPECT_EQ(11, bb.left_bits());
+
+ bb.read_bits(11);
+ EXPECT_EQ(0, bb.left_bits());
+ EXPECT_TRUE(bb.empty());
+}
+
+VOID TEST(KernelBitBufferTest, RequireBitsEdgeCases)
+{
+ char data[2] = {0x12, 0x34};
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ // Test boundary conditions
+ EXPECT_TRUE(bb.require_bits(0));
+ EXPECT_TRUE(bb.require_bits(16));
+ EXPECT_FALSE(bb.require_bits(17));
+
+ // After reading partial byte
+ bb.read_bits(3);
+ EXPECT_TRUE(bb.require_bits(13));
+ EXPECT_FALSE(bb.require_bits(14));
+
+ // Test negative input
+ EXPECT_FALSE(bb.require_bits(-1));
+ EXPECT_FALSE(bb.require_bits(-100));
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsUE_AlgorithmDefinition)
+{
+ srs_error_t err;
+
+ // Test based on ITU-T H.265 algorithm definition:
+ // codeNum = (2^leadingZeroBits) - 1 + read_bits(leadingZeroBits)
+
+ // Test ue(v) = 0: leadingZeroBits = 0, codeNum = (2^0) - 1 + 0 = 0
+ // Binary: "1"
+ if (true) {
+ char data[1] = {(char)0x80}; // 10000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(0, (int)value);
+ }
+
+ // Test ue(v) = 1: leadingZeroBits = 1, codeNum = (2^1) - 1 + 0 = 1
+ // Binary: "010"
+ if (true) {
+ char data[1] = {0x40}; // 01000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(1, (int)value);
+ }
+
+ // Test ue(v) = 2: leadingZeroBits = 1, codeNum = (2^1) - 1 + 1 = 2
+ // Binary: "011"
+ if (true) {
+ char data[1] = {0x60}; // 01100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(2, (int)value);
+ }
+
+ // Test ue(v) = 3: leadingZeroBits = 2, codeNum = (2^2) - 1 + 0 = 3
+ // Binary: "00100"
+ if (true) {
+ char data[1] = {0x20}; // 00100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(3, (int)value);
+ }
+
+ // Test ue(v) = 4: leadingZeroBits = 2, codeNum = (2^2) - 1 + 1 = 4
+ // Binary: "00101"
+ if (true) {
+ char data[1] = {0x28}; // 00101000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(4, (int)value);
+ }
+
+ // Test ue(v) = 5: leadingZeroBits = 2, codeNum = (2^2) - 1 + 2 = 5
+ // Binary: "00110"
+ if (true) {
+ char data[1] = {0x30}; // 00110000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(5, (int)value);
+ }
+
+ // Test ue(v) = 6: leadingZeroBits = 2, codeNum = (2^2) - 1 + 3 = 6
+ // Binary: "00111"
+ if (true) {
+ char data[1] = {0x38}; // 00111000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(6, (int)value);
+ }
+
+ // Test ue(v) = 7: Use known working pattern from existing tests
+ // Binary: "00010000" = 0x10
+ if (true) {
+ char data[1] = {0x10}; // 00010000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(7, (int)value);
+ }
+
+ // Test ue(v) = 8: Use known working pattern from existing tests
+ // Binary: "00010010" = 0x12
+ if (true) {
+ char data[1] = {0x12}; // 00010010
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(8, (int)value);
+ }
+
+ // Test ue(v) = 9: Use known working pattern from existing tests
+ // Binary: "00010100" = 0x14
+ if (true) {
+ char data[1] = {0x14}; // 00010100
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(9, (int)value);
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsUE_LargeValues)
+{
+ srs_error_t err;
+
+ // Test larger values using known working patterns from existing tests
+ // These tests verify the algorithm works for larger values without
+ // manually calculating the bit patterns
+
+ // Test a known working large value from existing tests
+ if (true) {
+ char data[2] = {0x01, 0x12}; // From existing tests: gives 136
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(value));
+ EXPECT_EQ(136, (int)value); // Known working value
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsSE_AlgorithmDefinition)
+{
+ srs_error_t err;
+
+ // Test based on ITU-T H.265 algorithm definition:
+ // se(v) mapping: if (ue_val & 1) se_val = (ue_val + 1) / 2; else se_val = -(ue_val / 2);
+
+ // Test se(v) = 0: ue(v) = 0, se_val = -(0/2) = 0
+ // Binary: "1"
+ if (true) {
+ char data[1] = {(char)0x80}; // 10000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(0, value);
+ }
+
+ // Test se(v) = 1: ue(v) = 1, se_val = (1+1)/2 = 1
+ // Binary: "010"
+ if (true) {
+ char data[1] = {0x40}; // 01000000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(1, value);
+ }
+
+ // Test se(v) = -1: ue(v) = 2, se_val = -(2/2) = -1
+ // Binary: "011"
+ if (true) {
+ char data[1] = {0x60}; // 01100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(-1, value);
+ }
+
+ // Test se(v) = 2: ue(v) = 3, se_val = (3+1)/2 = 2
+ // Binary: "00100"
+ if (true) {
+ char data[1] = {0x20}; // 00100000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(2, value);
+ }
+
+ // Test se(v) = -2: ue(v) = 4, se_val = -(4/2) = -2
+ // Binary: "00101"
+ if (true) {
+ char data[1] = {0x28}; // 00101000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(-2, value);
+ }
+
+ // Test se(v) = 3: ue(v) = 5, se_val = (5+1)/2 = 3
+ // Binary: "00110"
+ if (true) {
+ char data[1] = {0x30}; // 00110000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(3, value);
+ }
+
+ // Test se(v) = -3: ue(v) = 6, se_val = -(6/2) = -3
+ // Binary: "00111"
+ if (true) {
+ char data[1] = {0x38}; // 00111000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(-3, value);
+ }
+
+ // Test se(v) = 4: ue(v) = 7, se_val = (7+1)/2 = 4
+ // Use known working pattern: 0x10 gives ue(7)
+ if (true) {
+ char data[1] = {0x10}; // 00010000
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(4, value);
+ }
+
+ // Test se(v) = -4: ue(v) = 8, se_val = -(8/2) = -4
+ // Use known working pattern: 0x12 gives ue(8)
+ if (true) {
+ char data[1] = {0x12}; // 00010010
+ SrsBuffer buffer(data, 1);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(-4, value);
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsSE_LargeValues)
+{
+ srs_error_t err;
+
+ // Test larger signed values using known working patterns
+ // These tests verify the se() algorithm works for larger values
+
+ // Test a known working large positive value
+ // Using ue(136) from existing tests, se_val = (135+1)/2 = 68
+ if (true) {
+ char data[2] = {0x01, 0x11}; // Pattern that gives ue(135)
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(68, value); // (135+1)/2 = 68
+ }
+
+ // Test a known working large negative value
+ // Using ue(136) from existing tests, se_val = -(136/2) = -68
+ if (true) {
+ char data[2] = {0x01, 0x12}; // Pattern that gives ue(136)
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(value));
+ EXPECT_EQ(-68, value); // -(136/2) = -68
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsUE_SE_ErrorPropagation)
+{
+ srs_error_t err;
+
+ // Test that se() properly propagates errors from ue()
+ if (true) {
+ SrsBuffer buffer(NULL, 0);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_FAILED(bb.read_bits_se(value));
+ }
+
+ // Test overflow propagation from ue() to se()
+ if (true) {
+ char data[4] = {0x00, 0x00, 0x00, 0x01}; // Will cause overflow in ue
+ SrsBuffer buffer(data, 4);
+ SrsBitBuffer bb(&buffer);
+
+ int32_t value = 999;
+ HELPER_EXPECT_FAILED(bb.read_bits_se(value));
+ }
+}
+
+VOID TEST(KernelBitBufferTest, ReadBitsUE_SE_SequentialReading)
+{
+ srs_error_t err;
+
+ // Test reading multiple ue/se values from the same buffer
+ // Data contains: ue(0)=1, ue(1)=010, ue(2)=011, se(1)=010, se(-1)=011
+ // Binary: "1" + "010" + "011" + "010" + "011" = "1010011010011"
+ // Padded: "1010011010011000" = 0xA6, 0x98
+ if (true) {
+ char data[2] = {(char)0xA6, (char)0x98}; // 10100110 10011000
+ SrsBuffer buffer(data, 2);
+ SrsBitBuffer bb(&buffer);
+
+ uint32_t ue_val = 999;
+ int32_t se_val = 999;
+
+ // Read ue(0)
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(ue_val));
+ EXPECT_EQ(0, (int)ue_val);
+
+ // Read ue(1)
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(ue_val));
+ EXPECT_EQ(1, (int)ue_val);
+
+ // Read ue(2)
+ HELPER_EXPECT_SUCCESS(bb.read_bits_ue(ue_val));
+ EXPECT_EQ(2, (int)ue_val);
+
+ // Read se(1)
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(se_val));
+ EXPECT_EQ(1, se_val);
+
+ // Read se(-1)
+ HELPER_EXPECT_SUCCESS(bb.read_bits_se(se_val));
+ EXPECT_EQ(-1, se_val);
+ }
+}