From 7c1e87ef5c6123111c0c31e3b067d51863cb7204 Mon Sep 17 00:00:00 2001 From: Winlin Date: Sat, 6 Sep 2025 12:39:46 -0400 Subject: [PATCH] AI: Add more utests for kernel module. v7.0.81 (#4478) This PR significantly enhances the kernel module by adding comprehensive unit test coverage and improving interface design for core buffer and load balancer components. - **ISrsDecoder**: New interface for decoding/deserialization operations - **ISrsLbRoundRobin**: Extracted interface from concrete SrsLbRoundRobin class for better abstraction - **Enhanced Documentation**: Added comprehensive inline documentation for ISrsEncoder, ISrsCodec, SrsBuffer, and SrsBitBuffer classes --------- Co-authored-by: OSSRS-AI --- .augment-guidelines | 54 + trunk/doc/CHANGELOG.md | 1 + trunk/src/app/srs_app_edge.cpp | 16 +- trunk/src/app/srs_app_edge.hpp | 16 +- trunk/src/app/srs_app_source.cpp | 5 - trunk/src/app/srs_app_source.hpp | 3 - trunk/src/core/srs_core_version7.hpp | 2 +- trunk/src/kernel/srs_kernel_balance.cpp | 8 + trunk/src/kernel/srs_kernel_balance.hpp | 48 +- trunk/src/kernel/srs_kernel_buffer.cpp | 55 +- trunk/src/kernel/srs_kernel_buffer.hpp | 574 ++++++-- trunk/src/utest/srs_utest_core.cpp | 394 +++++ trunk/src/utest/srs_utest_kernel.cpp | 411 ++++-- trunk/src/utest/srs_utest_kernel2.cpp | 1768 +++++++++++++++++++++++ 14 files changed, 3108 insertions(+), 247 deletions(-) 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); + } +}