diff --git a/CMakeLists.txt b/CMakeLists.txt index 05281a7..c6ef79e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ add_subdirectory(src/myx/base) add_subdirectory(src/myx/filesystem) add_subdirectory(src/myx/qt) add_subdirectory(src/myx/math) -# add_subdirectory(src/myx/redis) +add_subdirectory(src/myx/redis) # Примеры if(MYXLIB_BUILD_EXAMPLES OR MYXLIB_BUILD_EXAMPLES_HO) diff --git a/src/myx/redis/CMakeLists.txt b/src/myx/redis/CMakeLists.txt index 42eacee..4e8527d 100644 --- a/src/myx/redis/CMakeLists.txt +++ b/src/myx/redis/CMakeLists.txt @@ -5,7 +5,6 @@ set(TRGT redis) # Список файлов исходных текстов set(TRGT_cpp ${CMAKE_CURRENT_SOURCE_DIR}/client.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/config.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lexer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/parser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/request.cpp) @@ -13,13 +12,18 @@ set(TRGT_cpp # Список заголовочных файлов (используется для установки) set(TRGT_moc_hpp ${CMAKE_CURRENT_SOURCE_DIR}/client.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/config.hpp ${CMAKE_CURRENT_SOURCE_DIR}/lexer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/parser.hpp ${CMAKE_CURRENT_SOURCE_DIR}/reply.hpp ${CMAKE_CURRENT_SOURCE_DIR}/request.hpp) -set(TRGT_headers ${TRGT_hpp}) +set(TRGT_hpp + ${CMAKE_CURRENT_SOURCE_DIR}/client-inl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/lexer-inl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/parser-inl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/request-inl.hpp) + +set(TRGT_headers ${TRGT_moc_hpp} ${TRGT_hpp}) # cmake-format: on add_library(${TRGT}-header-only INTERFACE) diff --git a/src/myx/redis/client-inl.hpp b/src/myx/redis/client-inl.hpp new file mode 100644 index 0000000..0c32fc0 --- /dev/null +++ b/src/myx/redis/client-inl.hpp @@ -0,0 +1,82 @@ +#ifndef MYX_REDIS_CLIENT_INL_HPP_ +#define MYX_REDIS_CLIENT_INL_HPP_ + +#pragma once + +#ifndef MYXLIB_HEADER_ONLY +#include +#endif + +#include + +namespace myx { + +namespace redis { + +ClientPrivate::ClientPrivate( Client* client ) : + lexer ( &socket ), + parser( &lexer ) +{ + connect( &socket, &QTcpSocket::connected, client, &Client::connected ); + connect( &socket, &QTcpSocket::disconnected, client, &Client::disconnected ); + connect( &parser, &Parser::reply, this, &ClientPrivate::sendReply ); +} + + +void ClientPrivate::sendReply( const Reply& reply ) +{ + Q_EMIT queue.dequeue()->reply( reply ); +} + + +Client::Client( QObject* parent ) : + QObject( parent ), + d ( new ClientPrivate( this ) ) +{ +} + + +void Client::connectToHost( const QString& hostName, quint16 port ) +{ + d->socket.connectToHost( hostName, port ); +} + + +void Client::disconnectFromHost() +{ + d->socket.disconnectFromHost(); +} + + +bool Client::isConnected() const +{ + return( d->socket.state() == QAbstractSocket::ConnectedState ); +} + + +Request* Client::sendCommand( const QByteArray& command ) +{ + d->socket.write( command + "\r\n" ); + + auto* request = new Request( this ); + d->queue.enqueue( request ); + return( request ); +} + + +bool Client::waitForConnected( int msecs ) +{ + return( d->socket.waitForConnected( msecs ) ); +} + + +bool Client::waitForDisconnected( int msecs ) +{ + return( d->socket.waitForDisconnected( msecs ) ); +} + +} // namespace redis + +} // namespace myx + +#endif // ifndef MYX_REDIS_CLIENT_INL_HPP_ diff --git a/src/myx/redis/client.cpp b/src/myx/redis/client.cpp index eff63e4..e825f87 100644 --- a/src/myx/redis/client.cpp +++ b/src/myx/redis/client.cpp @@ -1,72 +1,5 @@ -#include -#include +#ifndef MYXLIB_BUILD_LIBRARIES +#error Define MYXLIB_BUILD_LIBRARIES to compile this file. +#endif -namespace myx { - -namespace redis { - -ClientPrivate::ClientPrivate( Client* client ) : - lexer ( &socket ), - parser( &lexer ) -{ - connect( &socket, &QTcpSocket::connected, client, &Client::connected ); - connect( &socket, &QTcpSocket::disconnected, client, &Client::disconnected ); - connect( &parser, &Parser::reply, this, &ClientPrivate::sendReply ); -} - - -void ClientPrivate::sendReply( const Reply& reply ) -{ - Q_EMIT queue.dequeue()->reply( reply ); -} - - -Client::Client( QObject* parent ) : - QObject( parent ), - d ( new ClientPrivate( this ) ) -{ -} - - -void Client::connectToHost( const QString& hostName, quint16 port ) -{ - d->socket.connectToHost( hostName, port ); -} - - -void Client::disconnectFromHost() -{ - d->socket.disconnectFromHost(); -} - - -bool Client::isConnected() const -{ - return( d->socket.state() == QAbstractSocket::ConnectedState ); -} - - -Request* Client::sendCommand( const QByteArray& command ) -{ - d->socket.write( command + "\r\n" ); - - auto* request = new Request( this ); - d->queue.enqueue( request ); - return( request ); -} - - -bool Client::waitForConnected( int msecs ) -{ - return( d->socket.waitForConnected( msecs ) ); -} - - -bool Client::waitForDisconnected( int msecs ) -{ - return( d->socket.waitForDisconnected( msecs ) ); -} - -} // namespace redis - -} // namespace myx +#include diff --git a/src/myx/redis/client.hpp b/src/myx/redis/client.hpp index 58fb4de..e154707 100644 --- a/src/myx/redis/client.hpp +++ b/src/myx/redis/client.hpp @@ -1,22 +1,24 @@ #ifndef MYX_REDIS_CLIENT_HPP_ #define MYX_REDIS_CLIENT_HPP_ +#pragma once + +#include +#include + #include #include -#include -#include - namespace myx { namespace redis { -class MYX_REDIS_EXPORT ClientPrivate; +QT_FORWARD_DECLARE_CLASS( ClientPrivate ) /** * @brief Provides access to a Redis server */ -class MYX_REDIS_EXPORT Client : public QObject +class Client : public QObject { Q_OBJECT @@ -104,10 +106,15 @@ public: private: const QScopedPointer< ClientPrivate > d; -}; // class MYX_REDIS_EXPORT +}; // class Client } // namespace redis } // namespace myx +#ifdef MYXLIB_HEADER_ONLY +#include "client-inl.hpp" +#endif + + #endif // MYX_REDIS_CLIENT_HPP_ diff --git a/src/myx/redis/config.cpp b/src/myx/redis/config.cpp deleted file mode 100644 index 6a7852b..0000000 --- a/src/myx/redis/config.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "config.hpp" diff --git a/src/myx/redis/config.hpp b/src/myx/redis/config.hpp deleted file mode 100644 index d4001cc..0000000 --- a/src/myx/redis/config.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MYX_REDIS_CONFIG_HPP_ -#define MYX_REDIS_CONFIG_HPP_ - -#include - -// #if defined( qredis_EXPORTS ) - #define MYX_REDIS_EXPORT Q_DECL_EXPORT -// #else -// #define MYX_REDIS_EXPORT Q_DECL_IMPORT -// #endif - -// #include - -#endif // MYX_REDIS_CONFIG_HPP_ diff --git a/src/myx/redis/config_flags.hpp.in b/src/myx/redis/config_flags.hpp.in deleted file mode 100644 index 2a1bcff..0000000 --- a/src/myx/redis/config_flags.hpp.in +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef @CMLIB_PROJECT_NAME_CANONICAL@_CONFIG_FLAGS_HPP_ -#define @CMLIB_PROJECT_NAME_CANONICAL@_CONFIG_FLAGS_HPP_ - -// #cmakedefine - -#endif /* @CMLIB_PROJECT_NAME_CANONICAL@_CONFIG_FLAGS_HPP_ */ diff --git a/src/myx/redis/lexer-inl.hpp b/src/myx/redis/lexer-inl.hpp new file mode 100644 index 0000000..62ddd52 --- /dev/null +++ b/src/myx/redis/lexer-inl.hpp @@ -0,0 +1,130 @@ +#ifndef MYX_REDIS_LEXER_INL_HPP_ +#define MYX_REDIS_LEXER_INL_HPP_ + +#pragma once + +#ifndef MYXLIB_HEADER_ONLY +#include +#endif + +namespace myx { + +namespace redis { + +Lexer::Lexer( QIODevice* device, QObject* parent ) : + QObject ( parent ), + m_device( device ), + m_state ( DoingNothing ), + m_crlf ( 0 ), + m_length( 0 ) +{ + connect( device, &QIODevice::readyRead, this, &Lexer::readData ); +} + + +void Lexer::readData() +{ + m_buffer.append( m_device->readAll() ); + + while ( true ) + { + if ( ( m_state == DoingNothing ) && !readCharacter() ) + { + break; + } + + switch ( m_state ) + { + case ReadingLength: + case ReadingUnsafeString: + if ( !readUnsafeString() ) { return; } + break; + case ReadingSafeString: + if ( !readSafeString() ) { return; } + break; + case DoingNothing: + break; + } + + if ( m_state != ReadingSafeString ) + { + m_state = DoingNothing; + } + } +} // Lexer::readData + + +bool Lexer::readCharacter() +{ + if ( m_buffer.isEmpty() ) + { + return( false ); + } + + char c = m_buffer.at( 0 ); + m_buffer.remove( 0, 1 ); + + switch ( c ) + { + case '+': + case '-': + case ':': + case '*': + m_state = ReadingUnsafeString; break; + case '$': + m_state = ReadingLength; break; + } + + Q_EMIT character( c ); + return( true ); +} // Lexer::readCharacter + + +bool Lexer::readUnsafeString() +{ + m_crlf = m_buffer.indexOf( "\r\n", m_crlf ); + if ( m_crlf == -1 ) + { + m_crlf = m_buffer.size(); + return( false ); + } + + QString s = m_buffer.mid( 0, m_crlf ); + m_buffer.remove( 0, m_crlf + 2 ); + + if ( m_state == ReadingLength ) + { + m_length = s.toInt(); + m_state = ReadingSafeString; + } + else + { + Q_EMIT unsafeString( s ); + } + + m_crlf = 0; + return( true ); +} // Lexer::readUnsafeString + + +bool Lexer::readSafeString() +{ + if ( m_buffer.size() - m_length < 2 ) + { + return( false ); + } + + QByteArray d = m_buffer.mid( 0, m_length ); + m_buffer.remove( 0, m_length + 2 ); + + Q_EMIT safeString( d ); + + m_state = DoingNothing; + return( true ); +} + +} // namespace redis + +} // namespace myx + +#endif // ifndef MYX_REDIS_LEXER_INL_HPP_ diff --git a/src/myx/redis/lexer.cpp b/src/myx/redis/lexer.cpp index e338bf1..7e545e5 100644 --- a/src/myx/redis/lexer.cpp +++ b/src/myx/redis/lexer.cpp @@ -1,121 +1,5 @@ -#include "lexer.hpp" +#ifndef MYXLIB_BUILD_LIBRARIES +#error Define MYXLIB_BUILD_LIBRARIES to compile this file. +#endif -namespace myx { - -namespace redis { - -Lexer::Lexer( QIODevice* device, QObject* parent ) : - QObject ( parent ), - m_device( device ), - m_state ( DoingNothing ), - m_crlf ( 0 ), - m_length( 0 ) -{ - connect( device, &QIODevice::readyRead, this, &Lexer::readData ); -} - - -void Lexer::readData() -{ - m_buffer.append( m_device->readAll() ); - - while ( true ) - { - if ( ( m_state == DoingNothing ) && !readCharacter() ) - { - break; - } - - switch ( m_state ) - { - case ReadingLength: - case ReadingUnsafeString: - if ( !readUnsafeString() ) { return; } - break; - case ReadingSafeString: - if ( !readSafeString() ) { return; } - break; - case DoingNothing: - break; - } - - if ( m_state != ReadingSafeString ) - { - m_state = DoingNothing; - } - } -} // Lexer::readData - - -bool Lexer::readCharacter() -{ - if ( m_buffer.isEmpty() ) - { - return( false ); - } - - char c = m_buffer.at( 0 ); - m_buffer.remove( 0, 1 ); - - switch ( c ) - { - case '+': - case '-': - case ':': - case '*': - m_state = ReadingUnsafeString; break; - case '$': - m_state = ReadingLength; break; - } - - Q_EMIT character( c ); - return( true ); -} // Lexer::readCharacter - - -bool Lexer::readUnsafeString() -{ - m_crlf = m_buffer.indexOf( "\r\n", m_crlf ); - if ( m_crlf == -1 ) - { - m_crlf = m_buffer.size(); - return( false ); - } - - QString s = m_buffer.mid( 0, m_crlf ); - m_buffer.remove( 0, m_crlf + 2 ); - - if ( m_state == ReadingLength ) - { - m_length = s.toInt(); - m_state = ReadingSafeString; - } - else - { - Q_EMIT unsafeString( s ); - } - - m_crlf = 0; - return( true ); -} // Lexer::readUnsafeString - - -bool Lexer::readSafeString() -{ - if ( m_buffer.size() - m_length < 2 ) - { - return( false ); - } - - QByteArray d = m_buffer.mid( 0, m_length ); - m_buffer.remove( 0, m_length + 2 ); - - Q_EMIT safeString( d ); - - m_state = DoingNothing; - return( true ); -} - -} // namespace redis - -} // namespace myx +#include diff --git a/src/myx/redis/lexer.hpp b/src/myx/redis/lexer.hpp index d041ca1..4109feb 100644 --- a/src/myx/redis/lexer.hpp +++ b/src/myx/redis/lexer.hpp @@ -1,8 +1,11 @@ #ifndef MYX_REDIS_LEXER_HPP_ #define MYX_REDIS_LEXER_HPP_ +#pragma once + +#include + #include -#include namespace myx { @@ -51,4 +54,9 @@ private: } // namespace myx +#ifdef MYXLIB_HEADER_ONLY +#include "lexer-inl.hpp" +#endif + + #endif // MYX_REDIS_LEXER_HPP_ diff --git a/src/myx/redis/parser-inl.hpp b/src/myx/redis/parser-inl.hpp new file mode 100644 index 0000000..804db59 --- /dev/null +++ b/src/myx/redis/parser-inl.hpp @@ -0,0 +1,91 @@ +#ifndef MYX_REDIS_PARSER_INL_HPP_ +#define MYX_REDIS_PARSER_INL_HPP_ + +#pragma once + +#ifndef MYXLIB_HEADER_ONLY +#include +#endif + +namespace myx { + +namespace redis { + +Parser::Parser( Lexer* lexer, QObject* parent ) : + QObject( parent ) +{ + connect( lexer, &Lexer::character, this, &Parser::readCharacter ); + connect( lexer, &Lexer::unsafeString, this, &Parser::readUnsafeString ); + connect( lexer, &Lexer::safeString, this, &Parser::readSafeString ); +} + + +void Parser::readCharacter( const char c ) +{ + switch ( c ) + { + case '+': + stack.append( Task( Reply::Status ) ); break; + case '-': + stack.append( Task( Reply::Error ) ); break; + case ':': + stack.append( Task( Reply::Integer ) ); break; + case '$': + stack.append( Task( Reply::Bulk ) ); break; + case '*': + stack.append( Task( Reply::MultiBulk ) ); break; + default: + break; + } +} + + +void Parser::readUnsafeString( const QString& value ) +{ + if ( tos().reply.type() == Reply::MultiBulk ) + { + tos().count = value.toInt(); + } + else + { + tos().reply.value() = value; + } + + descend(); +} + + +void Parser::readSafeString( const QByteArray& value ) +{ + tos().reply.value() = value; + descend(); +} + + +void Parser::descend() +{ + while ( true ) + { + if ( ( tos().reply.type() == Reply::MultiBulk ) && + ( tos().reply.value().toList().count() < tos().count ) ) + { + return; + } + + if ( stack.count() == 1 ) + { + auto r = stack.takeLast().reply; + Q_EMIT reply( r ); + return; + } + + auto r = stack.takeLast().reply; + tos().reply.value().toList().append( QVariant::fromValue( r ) ); + } +} + +} // namespace redis + +} // namespace myx + +#endif // ifndef MYX_REDIS_PARSER_INL_HPP_ diff --git a/src/myx/redis/parser.cpp b/src/myx/redis/parser.cpp index a3a881d..2480e99 100644 --- a/src/myx/redis/parser.cpp +++ b/src/myx/redis/parser.cpp @@ -1,82 +1,5 @@ -#include "parser.hpp" +#ifndef MYXLIB_BUILD_LIBRARIES +#error Define MYXLIB_BUILD_LIBRARIES to compile this file. +#endif -namespace myx { - -namespace redis { - -Parser::Parser( Lexer* lexer, QObject* parent ) : - QObject( parent ) -{ - connect( lexer, &Lexer::character, this, &Parser::readCharacter ); - connect( lexer, &Lexer::unsafeString, this, &Parser::readUnsafeString ); - connect( lexer, &Lexer::safeString, this, &Parser::readSafeString ); -} - - -void Parser::readCharacter( const char c ) -{ - switch ( c ) - { - case '+': - stack.append( Task( Reply::Status ) ); break; - case '-': - stack.append( Task( Reply::Error ) ); break; - case ':': - stack.append( Task( Reply::Integer ) ); break; - case '$': - stack.append( Task( Reply::Bulk ) ); break; - case '*': - stack.append( Task( Reply::MultiBulk ) ); break; - default: - break; - } -} - - -void Parser::readUnsafeString( const QString& value ) -{ - if ( tos().reply.type() == Reply::MultiBulk ) - { - tos().count = value.toInt(); - } - else - { - tos().reply.value() = value; - } - - descend(); -} - - -void Parser::readSafeString( const QByteArray& value ) -{ - tos().reply.value() = value; - descend(); -} - - -void Parser::descend() -{ - while ( true ) - { - if ( ( tos().reply.type() == Reply::MultiBulk ) && - ( tos().reply.value().toList().count() < tos().count ) ) - { - return; - } - - if ( stack.count() == 1 ) - { - auto r = stack.takeLast().reply; - Q_EMIT reply( r ); - return; - } - - auto r = stack.takeLast().reply; - tos().reply.value().toList().append( QVariant::fromValue( r ) ); - } -} - -} // namespace redis - -} // namespace myx +#include diff --git a/src/myx/redis/parser.hpp b/src/myx/redis/parser.hpp index 6891453..a1156fc 100644 --- a/src/myx/redis/parser.hpp +++ b/src/myx/redis/parser.hpp @@ -1,14 +1,16 @@ #ifndef MYX_REDIS_PARSER_HPP_ #define MYX_REDIS_PARSER_HPP_ +#pragma once + +#include +#include +#include + #include -#include #include #include -#include -#include - namespace myx { namespace redis { @@ -54,4 +56,8 @@ public: } // namespace myx +#ifdef MYXLIB_HEADER_ONLY +#include "parser-inl.hpp" +#endif + #endif // MYX_REDIS_PARSER_HPP_ diff --git a/src/myx/redis/reply.hpp b/src/myx/redis/reply.hpp index 847bbd8..a62e59a 100644 --- a/src/myx/redis/reply.hpp +++ b/src/myx/redis/reply.hpp @@ -1,9 +1,11 @@ #ifndef MYX_REDIS_REPLY_HPP_ #define MYX_REDIS_REPLY_HPP_ -#include +#pragma once -#include +#include + +#include namespace myx { @@ -12,7 +14,7 @@ namespace redis { /** * @brief Represents a Redis reply */ -class MYX_REDIS_EXPORT Reply +class Reply { public: diff --git a/src/myx/redis/request-inl.hpp b/src/myx/redis/request-inl.hpp new file mode 100644 index 0000000..a06b966 --- /dev/null +++ b/src/myx/redis/request-inl.hpp @@ -0,0 +1,53 @@ +#ifndef MYX_REDIS_REQUEST_INL_HPP_ +#define MYX_REDIS_REQUEST_INL_HPP_ + +#pragma once + +#include + +#ifndef MYXLIB_HEADER_ONLY +#include +#endif +#include + +#include + +namespace myx { + +namespace redis { + +void RequestPrivate::quitEventLoop() +{ + loop.exit( 1 ); +} + + +Request::Request( QObject* parent ) : + QObject( parent ), + d ( new RequestPrivate ) +{ + connect( this, &Request::reply, this, &Request::deleteLater ); +} + + +bool Request::waitForReply( int msecs ) +{ + QTimer timer; + timer.setInterval( msecs ); + timer.setSingleShot( true ); + + connect( &timer, &QTimer::timeout, &d->loop, &QEventLoop::quit ); + connect( this, &Request::reply, d.data(), &RequestPrivate::quitEventLoop ); + + /* + * If the timer fires, the return value will be 0. + * Otherwise, quitEventLoop() will terminate the loop with 1. + */ + return( ( d->loop.exec( QEventLoop::ExcludeUserInputEvents ) != 0 ) ); +} + +} // namespace redis + +} // namespace myx + +#endif // ifndef MYX_REDIS_REQUEST_INL_HPP_ diff --git a/src/myx/redis/request.cpp b/src/myx/redis/request.cpp index 9cb037f..63f05c1 100644 --- a/src/myx/redis/request.cpp +++ b/src/myx/redis/request.cpp @@ -1,42 +1,5 @@ -#include +#ifndef MYXLIB_BUILD_LIBRARIES +#error Define MYXLIB_BUILD_LIBRARIES to compile this file. +#endif -#include -#include - -namespace myx { - -namespace redis { - -void RequestPrivate::quitEventLoop() -{ - loop.exit( 1 ); -} - - -Request::Request( QObject* parent ) : - QObject( parent ), - d ( new RequestPrivate ) -{ - connect( this, &Request::reply, this, &Request::deleteLater ); -} - - -bool Request::waitForReply( int msecs ) -{ - QTimer timer; - timer.setInterval( msecs ); - timer.setSingleShot( true ); - - connect( &timer, &QTimer::timeout, &d->loop, &QEventLoop::quit ); - connect( this, &Request::reply, d.data(), &RequestPrivate::quitEventLoop ); - - /* - * If the timer fires, the return value will be 0. - * Otherwise, quitEventLoop() will terminate the loop with 1. - */ - return( ( d->loop.exec( QEventLoop::ExcludeUserInputEvents ) != 0 ) ); -} - -} // namespace redis - -} // namespace myx +#include diff --git a/src/myx/redis/request.hpp b/src/myx/redis/request.hpp index cbd1bfe..5db6709 100644 --- a/src/myx/redis/request.hpp +++ b/src/myx/redis/request.hpp @@ -1,22 +1,24 @@ #ifndef MYX_REDIS_REQUEST_HPP_ #define MYX_REDIS_REQUEST_HPP_ +#pragma once + +#include +#include + #include #include -#include -#include - namespace myx { namespace redis { -class MYX_REDIS_EXPORT RequestPrivate; +QT_FORWARD_DECLARE_CLASS( RequestPrivate ); /** * @brief Represents a Redis command and its response */ -class MYX_REDIS_EXPORT Request : public QObject +class Request : public QObject { Q_OBJECT @@ -49,10 +51,14 @@ public: private: const QScopedPointer< RequestPrivate > d; -}; // class MYX_REDIS_EXPORT +}; // class Request } // namespace redis } // namespace myx +#ifdef MYXLIB_HEADER_ONLY +#include "request-inl.hpp" +#endif + #endif // MYX_REDIS_REQUEST_HPP_