From 86c9511f2fae983ad980ae73b1fdb37691a2b7b4 Mon Sep 17 00:00:00 2001 From: Przemyslaw Czerpak Date: Wed, 6 Jan 2010 06:27:38 +0000 Subject: [PATCH] 2010-01-06 07:27 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/src/rtl/gtwin/gtwin.c + added support for HB_GTI_ISUNICODE * harbour/include/hbapifs.h * added missing 'extern' in some declarations * harbour/include/hbzlib.ch + added HB_ZLIB_STRATEGY_* constants + added HB_ZLIB_COMPRESSION_DISABLE * harbour/include/Makefile + harbour/include/hbznet.h * harbour/include/hbextern.ch * harbour/src/rtl/Makefile * harbour/src/rtl/hbinet.c + harbour/src/rtl/hbznet.c + added support for ZLIB compression in stream sockets. + added .prg function: HB_INETCOMPRESS( , [], [] ) which enables ZLIB compression for given HB_INET*() socket. is a socket created by one of HB_INET*() functions is compression factor between 1 (fastest) and 9 (best) (see HB_ZLIB_COMPRESSION_*) 0 (none) disable compression on output data but decompression is still working. is used to tune compression algorithm, see HB_ZLIB_STRATEGY_* The compression must be enabled on both connection sides, i.e. on the server side: conn := hb_inetAccept( sock ) hb_inetCompress( conn ) and on the client side: sock := hb_inetConnect( cServer, nPort ) hb_inetCompress( sock ) in the same moment but it's not necessary to enable it at the beginning of connection. It can be done later, i.e. when both sides agree to enable connection using some custom protocol. The compression has effect only on stream connections, i.e. TCP and it's ignored in datagram connections like UDP. This function can be executed more then once changing the compression parameters but it causes that all data in readahead decompression buffer is discarded. When called with HB_ZLIB_COMPRESSION_DISABLE as then support for stream compression is removed and sockets works again in raw mode. The compression level and strategy do not have to be the same on both connection sides. Each side can chose the best settings for data it's going to send. This code was written in a way which allows to easy implement alternative compression methods or other extensions like encryption in existing HB_INET*() sockets also by non core code. The public C functions declared in hbznet.h allows to use this extension with raw harbour sockets two. --- harbour/ChangeLog | 55 +++++++ harbour/include/Makefile | 1 + harbour/include/hbapifs.h | 44 +++--- harbour/include/hbextern.ch | 1 + harbour/include/hbzlib.ch | 21 ++- harbour/include/hbznet.h | 89 ++++++++++++ harbour/src/rtl/Makefile | 1 + harbour/src/rtl/gtwin/gtwin.c | 8 ++ harbour/src/rtl/hbinet.c | 77 +++++++++- harbour/src/rtl/hbznet.c | 261 ++++++++++++++++++++++++++++++++++ 10 files changed, 523 insertions(+), 35 deletions(-) create mode 100644 harbour/include/hbznet.h create mode 100644 harbour/src/rtl/hbznet.c diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 0f314c88b0..bc84166874 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -17,6 +17,61 @@ past entries belonging to author(s): Viktor Szakats. */ +2010-01-06 07:27 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/src/rtl/gtwin/gtwin.c + + added support for HB_GTI_ISUNICODE + + * harbour/include/hbapifs.h + * added missing 'extern' in some declarations + + * harbour/include/hbzlib.ch + + added HB_ZLIB_STRATEGY_* constants + + added HB_ZLIB_COMPRESSION_DISABLE + + * harbour/include/Makefile + + harbour/include/hbznet.h + * harbour/include/hbextern.ch + * harbour/src/rtl/Makefile + * harbour/src/rtl/hbinet.c + + harbour/src/rtl/hbznet.c + + added support for ZLIB compression in stream sockets. + + added .prg function: + HB_INETCOMPRESS( , [], [] ) + which enables ZLIB compression for given HB_INET*() socket. + is a socket created by one of HB_INET*() functions + is compression factor between 1 (fastest) and + 9 (best) (see HB_ZLIB_COMPRESSION_*) + 0 (none) disable compression on output data + but decompression is still working. + is used to tune compression algorithm, + see HB_ZLIB_STRATEGY_* + The compression must be enabled on both connection sides, i.e. + on the server side: + conn := hb_inetAccept( sock ) + hb_inetCompress( conn ) + and on the client side: + sock := hb_inetConnect( cServer, nPort ) + hb_inetCompress( sock ) + in the same moment but it's not necessary to enable it at the + beginning of connection. It can be done later, i.e. when both + sides agree to enable connection using some custom protocol. + The compression has effect only on stream connections, i.e. + TCP and it's ignored in datagram connections like UDP. + This function can be executed more then once changing the compression + parameters but it causes that all data in readahead decompression + buffer is discarded. When called with HB_ZLIB_COMPRESSION_DISABLE + as then support for stream compression is removed + and sockets works again in raw mode. + The compression level and strategy do not have to be the same on both + connection sides. Each side can chose the best settings for data it's + going to send. + + This code was written in a way which allows to easy implement + alternative compression methods or other extensions like encryption + in existing HB_INET*() sockets also by non core code. The public C + functions declared in hbznet.h allows to use this extension with raw + harbour sockets two. + 2010-01-05 19:01 UTC-0800 Pritpal Bedi (pritpal@vouchcac.com) * contrib/hbxbp/tests/demoxbp.prg ! Changed to HBQT_SET_RELEASE_METHOD( HBQT_RELEASE_WITH_DELETE ) diff --git a/harbour/include/Makefile b/harbour/include/Makefile index 3a65d7e958..9dbd7fbbfb 100644 --- a/harbour/include/Makefile +++ b/harbour/include/Makefile @@ -69,6 +69,7 @@ C_HEADERS := \ hbwince.h \ hbwinuni.h \ hbzlib.h \ + hbznet.h \ hb_io.h \ PRG_HEADERS := \ diff --git a/harbour/include/hbapifs.h b/harbour/include/hbapifs.h index 8f63801720..e7dbadc6e4 100644 --- a/harbour/include/hbapifs.h +++ b/harbour/include/hbapifs.h @@ -341,32 +341,32 @@ extern HB_EXPORT const char * hb_fsNameConv( const char * szFileName, char ** ps } HB_FILE_FUNCS; - HB_EXPORT BOOL hb_fileRegister( const HB_FILE_FUNCS * pFuncs ); + extern HB_EXPORT BOOL hb_fileRegister( const HB_FILE_FUNCS * pFuncs ); #else typedef void * PHB_FILE; #endif -HB_EXPORT BOOL hb_fileExists( const char * pFilename, char * pRetPath ); -HB_EXPORT BOOL hb_fileDelete( const char * pFilename ); -HB_EXPORT BOOL hb_fileRename( const char * pFilename, const char * pszNewName ); -HB_EXPORT PHB_FILE hb_fileExtOpen( const char * pszFilename, const char * pDefExt, - USHORT uiExFlags, const char * pPaths, - PHB_ITEM pError ); -HB_EXPORT PHB_FILE hb_fileCreateTemp( const char * pszDir, const char * pszPrefix, - ULONG ulAttr, char * pszName ); -HB_EXPORT PHB_FILE hb_fileCreateTempEx( char * pszName, - const char * pszDir, - const char * pszPrefix, - const char * pszExt, - ULONG ulAttr ); -HB_EXPORT void hb_fileClose( PHB_FILE pFile ); -HB_EXPORT BOOL hb_fileLock( PHB_FILE pFile, HB_FOFFSET ulStart, HB_FOFFSET ulLen, int iType ); -HB_EXPORT ULONG hb_fileReadAt( PHB_FILE pFile, void * buffer, ULONG ulSize, HB_FOFFSET llOffset ); -HB_EXPORT ULONG hb_fileWriteAt( PHB_FILE pFile, const void * buffer, ULONG ulSize, HB_FOFFSET llOffset ); -HB_EXPORT BOOL hb_fileTruncAt( PHB_FILE pFile, HB_FOFFSET llOffset ); -HB_EXPORT HB_FOFFSET hb_fileSize( PHB_FILE pFile ); -HB_EXPORT void hb_fileCommit( PHB_FILE pFile ); -HB_EXPORT HB_FHANDLE hb_fileHandle( PHB_FILE pFile ); +extern HB_EXPORT BOOL hb_fileExists( const char * pFilename, char * pRetPath ); +extern HB_EXPORT BOOL hb_fileDelete( const char * pFilename ); +extern HB_EXPORT BOOL hb_fileRename( const char * pFilename, const char * pszNewName ); +extern HB_EXPORT PHB_FILE hb_fileExtOpen( const char * pszFilename, const char * pDefExt, + USHORT uiExFlags, const char * pPaths, + PHB_ITEM pError ); +extern HB_EXPORT PHB_FILE hb_fileCreateTemp( const char * pszDir, const char * pszPrefix, + ULONG ulAttr, char * pszName ); +extern HB_EXPORT PHB_FILE hb_fileCreateTempEx( char * pszName, + const char * pszDir, + const char * pszPrefix, + const char * pszExt, + ULONG ulAttr ); +extern HB_EXPORT void hb_fileClose( PHB_FILE pFile ); +extern HB_EXPORT BOOL hb_fileLock( PHB_FILE pFile, HB_FOFFSET ulStart, HB_FOFFSET ulLen, int iType ); +extern HB_EXPORT ULONG hb_fileReadAt( PHB_FILE pFile, void * buffer, ULONG ulSize, HB_FOFFSET llOffset ); +extern HB_EXPORT ULONG hb_fileWriteAt( PHB_FILE pFile, const void * buffer, ULONG ulSize, HB_FOFFSET llOffset ); +extern HB_EXPORT BOOL hb_fileTruncAt( PHB_FILE pFile, HB_FOFFSET llOffset ); +extern HB_EXPORT HB_FOFFSET hb_fileSize( PHB_FILE pFile ); +extern HB_EXPORT void hb_fileCommit( PHB_FILE pFile ); +extern HB_EXPORT HB_FHANDLE hb_fileHandle( PHB_FILE pFile ); /* wrapper to fopen() which calls hb_fsNameConv() */ extern FILE * hb_fopen( const char *path, const char *mode ); diff --git a/harbour/include/hbextern.ch b/harbour/include/hbextern.ch index 56019d5906..97f9a54d83 100644 --- a/harbour/include/hbextern.ch +++ b/harbour/include/hbextern.ch @@ -1230,6 +1230,7 @@ EXTERNAL HB_INETGETSNDBUFSIZE EXTERNAL HB_INETGETRCVBUFSIZE EXTERNAL HB_INETSETSNDBUFSIZE EXTERNAL HB_INETSETRCVBUFSIZE +EXTERNAL HB_INETCOMPRESS EXTERNAL HB_ZLIBVERSION EXTERNAL HB_ZCOMPRESS diff --git a/harbour/include/hbzlib.ch b/harbour/include/hbzlib.ch index 9495b30a54..6b245102d5 100644 --- a/harbour/include/hbzlib.ch +++ b/harbour/include/hbzlib.ch @@ -53,12 +53,19 @@ #ifndef HB_ZLIB_CH_ #define HB_ZLIB_CH_ -#define HB_ZLIB_METHOD_STORE 0 -#define HB_ZLIB_METHOD_DEFLATED 8 +#define HB_ZLIB_METHOD_STORE 0 +#define HB_ZLIB_METHOD_DEFLATED 8 -#define HB_ZLIB_COMPRESSION_NONE 0 -#define HB_ZLIB_COMPRESSION_SPEED 1 -#define HB_ZLIB_COMPRESSION_SIZE 9 -#define HB_ZLIB_COMPRESSION_DEFAULT (-1) +#define HB_ZLIB_COMPRESSION_NONE 0 +#define HB_ZLIB_COMPRESSION_SPEED 1 +#define HB_ZLIB_COMPRESSION_SIZE 9 +#define HB_ZLIB_COMPRESSION_DEFAULT (-1) +#define HB_ZLIB_COMPRESSION_DISABLE (-2) -#endif +#define HB_ZLIB_STRATEGY_DEFAULT 0 +#define HB_ZLIB_STRATEGY_FILTERED 1 +#define HB_ZLIB_STRATEGY_HUFFMAN_ONLY 2 +#define HB_ZLIB_STRATEGY_RLE 3 +#define HB_ZLIB_STRATEGY_FIXED 4 + +#endif /* HB_ZLIB_CH_ */ diff --git a/harbour/include/hbznet.h b/harbour/include/hbznet.h new file mode 100644 index 0000000000..4c039d26f9 --- /dev/null +++ b/harbour/include/hbznet.h @@ -0,0 +1,89 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * ZLIB compression for Harbour stream sockets + * + * Copyright 2010 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#ifndef HB_ZNET_H_ +#define HB_ZNET_H_ + +#include "hbapi.h" + +HB_EXTERN_BEGIN + +#if defined( _HB_ZNET_INTERNAL_ ) + struct _HB_ZNETSTREAM; + typedef struct _HB_ZNETSTREAM * PHB_ZNETSTREAM; +#else + typedef void * PHB_ZNETSTREAM; +#endif + +typedef long ( * HB_INET_SFUNC ) ( PHB_ZNETSTREAM, HB_SOCKET, const void *, long, HB_LONG ); +typedef long ( * HB_INET_RFUNC ) ( PHB_ZNETSTREAM, HB_SOCKET, void *, long, HB_LONG ); +typedef long ( * HB_INET_FFUNC ) ( PHB_ZNETSTREAM, HB_SOCKET, HB_LONG ); +typedef void ( * HB_INET_CFUNC ) ( PHB_ZNETSTREAM ); + +extern HB_EXPORT int hb_znetError( PHB_ZNETSTREAM pStream ); + +extern HB_EXPORT PHB_ZNETSTREAM hb_znetOpen( int level, int strategy ); +extern HB_EXPORT void hb_znetClose( PHB_ZNETSTREAM pStream ); +extern HB_EXPORT int hb_znetError( PHB_ZNETSTREAM pStream ); +extern HB_EXPORT long hb_znetRead( PHB_ZNETSTREAM pStream, HB_SOCKET sd, void * buffer, long len, HB_LONG timeout ); +extern HB_EXPORT long hb_znetFlush( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_LONG timeout ); +extern HB_EXPORT long hb_znetWrite( PHB_ZNETSTREAM pStream, HB_SOCKET sd, const void * buffer, long len, HB_LONG timeout ); + +extern HB_EXPORT BOOL hb_znetInetInitialize( PHB_ITEM, PHB_ZNETSTREAM, + HB_INET_RFUNC, + HB_INET_SFUNC, + HB_INET_FFUNC, + HB_INET_CFUNC ); + +HB_EXTERN_END + +#endif /* HB_ZNET_H_ */ diff --git a/harbour/src/rtl/Makefile b/harbour/src/rtl/Makefile index c2936da5cd..765bebfc0d 100644 --- a/harbour/src/rtl/Makefile +++ b/harbour/src/rtl/Makefile @@ -97,6 +97,7 @@ C_SOURCES := \ hbtoken.c \ hbzlib.c \ hbzlibgz.c \ + hbznet.c \ idle.c \ idlex.c \ inkey.c \ diff --git a/harbour/src/rtl/gtwin/gtwin.c b/harbour/src/rtl/gtwin/gtwin.c index 78ac66b3a0..3ec9aab5ec 100644 --- a/harbour/src/rtl/gtwin/gtwin.c +++ b/harbour/src/rtl/gtwin/gtwin.c @@ -1628,6 +1628,14 @@ static BOOL hb_gt_win_Info( PHB_GT pGT, int iType, PHB_GT_INFO pInfo ) pInfo->pResult = hb_itemPutL( pInfo->pResult, TRUE ); break; + case HB_GTI_ISUNICODE: +#if defined( UNICODE ) + pInfo->pResult = hb_itemPutL( pInfo->pResult, TRUE ); +#else + pInfo->pResult = hb_itemPutL( pInfo->pResult, FALSE ); +#endif + break; + case HB_GTI_CODEPAGE: { UINT uiCodePage = GetConsoleCP(); diff --git a/harbour/src/rtl/hbinet.c b/harbour/src/rtl/hbinet.c index 143fe21fa6..6478f62f05 100644 --- a/harbour/src/rtl/hbinet.c +++ b/harbour/src/rtl/hbinet.c @@ -66,6 +66,7 @@ #include "hbapierr.h" #include "hbvm.h" #include "hbthread.h" +#include "hbznet.h" #define HB_INET_ERR_OK 0 #define HB_INET_ERR_TIMEOUT ( -1 ) @@ -87,6 +88,11 @@ typedef struct int iTimeout; int iTimeLimit; PHB_ITEM pPeriodicBlock; + PHB_ZNETSTREAM stream; + HB_INET_RFUNC recvFunc; + HB_INET_SFUNC sendFunc; + HB_INET_FFUNC flushFunc; + HB_INET_CFUNC cleanFunc; } HB_SOCKET_STRUCT, * PHB_SOCKET_STRUCT; #define HB_INET_BUFFER_LEN 256 @@ -148,6 +154,18 @@ static int hb_inetCloseSocket( PHB_SOCKET_STRUCT socket ) return ret; } +static void hb_inetCloseStream( PHB_SOCKET_STRUCT socket ) +{ + if( socket->cleanFunc ) + socket->cleanFunc( socket->stream ); + + socket->recvFunc = NULL; + socket->sendFunc = NULL; + socket->flushFunc = NULL; + socket->cleanFunc = NULL; + socket->stream = NULL; +} + static HB_GARBAGE_FUNC( hb_inetSocketFinalize ) { PHB_SOCKET_STRUCT socket = ( PHB_SOCKET_STRUCT ) Cargo; @@ -173,6 +191,7 @@ static HB_GARBAGE_FUNC( hb_inetSocketFinalize ) hb_xfree( socket->buffer ); socket->buffer = NULL; } + hb_inetCloseStream( socket ); } static HB_GARBAGE_FUNC( hb_inetSocketMark ) @@ -207,6 +226,30 @@ static void hb_inetAutoInit( void ) } } +BOOL hb_znetInetInitialize( PHB_ITEM pItem, PHB_ZNETSTREAM pStream, + HB_INET_RFUNC recvFunc, + HB_INET_SFUNC sendFunc, + HB_INET_FFUNC flushFunc, + HB_INET_CFUNC cleanFunc ) +{ + PHB_SOCKET_STRUCT socket = ( PHB_SOCKET_STRUCT ) hb_itemGetPtrGC( pItem, &s_gcInetFuncs ); + + if( socket ) + { + hb_inetCloseStream( socket ); + + socket->recvFunc = recvFunc; + socket->sendFunc = sendFunc; + socket->flushFunc = flushFunc; + socket->cleanFunc = cleanFunc; + socket->stream = pStream; + return TRUE; + } + + hb_inetErrRT(); + return FALSE; +} + HB_FUNC( HB_INETINIT ) { int ret; @@ -568,8 +611,13 @@ static long s_inetRecv( PHB_SOCKET_STRUCT socket, char * buffer, long size, BOOL if( socket->buffer == NULL ) socket->buffer = ( char * ) hb_xgrab( socket->readahead ); socket->posbuffer = 0; - rec = hb_socketRecv( socket->sd, socket->buffer, socket->readahead, - 0, socket->iTimeout ); + if( socket->recvFunc ) + rec = socket->recvFunc( socket->stream, socket->sd, + socket->buffer, socket->readahead, + socket->iTimeout ); + else + rec = hb_socketRecv( socket->sd, socket->buffer, socket->readahead, + 0, socket->iTimeout ); socket->inbuffer = HB_MAX( 0, rec ); } else @@ -583,13 +631,25 @@ static long s_inetRecv( PHB_SOCKET_STRUCT socket, char * buffer, long size, BOOL socket->inbuffer -= rec; if( size > rec && !readahead ) { - size = hb_socketRecv( socket->sd, buffer + rec, size - rec, 0, 0 ); + if( socket->recvFunc ) + rec = socket->recvFunc( socket->stream, socket->sd, + buffer + rec, size - rec, + socket->iTimeout ); + else + size = hb_socketRecv( socket->sd, buffer + rec, size - rec, 0, 0 ); + if( size > 0 ) rec += size; } } else if( !readahead ) - rec = hb_socketRecv( socket->sd, buffer, size, 0, socket->iTimeout ); + { + if( socket->recvFunc ) + rec = socket->recvFunc( socket->stream, socket->sd, + buffer, size, socket->iTimeout ); + else + rec = hb_socketRecv( socket->sd, buffer, size, 0, socket->iTimeout ); + } return rec; } @@ -926,8 +986,13 @@ static void s_inetSendInternal( BOOL lAll ) iSent = iLen = 0; while( iSent < iSend ) { - iLen = hb_socketSend( socket->sd, buffer + iSent, iSend - iSent, 0, - socket->iTimeout ); + if( socket->sendFunc ) + iLen = socket->sendFunc( socket->stream, socket->sd, + buffer + iSent, iSend - iSent, + socket->iTimeout ); + else + iLen = hb_socketSend( socket->sd, buffer + iSent, iSend - iSent, + 0, socket->iTimeout ); if( iLen > 0 ) { iSent += iLen; diff --git a/harbour/src/rtl/hbznet.c b/harbour/src/rtl/hbznet.c new file mode 100644 index 0000000000..0ee07c7f3f --- /dev/null +++ b/harbour/src/rtl/hbznet.c @@ -0,0 +1,261 @@ +/* + * $Id$ + */ + +/* + * Harbour Project source code: + * ZLIB compression for Harbour stream sockets + * + * Copyright 2010 Przemyslaw Czerpak + * www - http://www.harbour-project.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#define _HB_ZNET_INTERNAL_ + +#include "hbapi.h" +#include "hbapiitm.h" +#include "hbapierr.h" +#include "hbsocket.h" +#include "hbznet.h" +#include "hbzlib.ch" + +#include +#include + +typedef struct _HB_ZNETSTREAM +{ + z_stream rd; /* input stream */ + z_stream wr; /* output stream */ + int err; /* error code for last stream operation */ + Bytef * inbuf; /* input buffer */ + Bytef * outbuf; /* output buffer */ +} +HB_ZNETSTREAM; + +#define HB_ZNET_BUFSIZE 16384 + +/* return status of last compression/decompression operation */ +int hb_znetError( PHB_ZNETSTREAM pStream ) +{ + return pStream->err; +} + +/* release stream structure + */ +void hb_znetClose( PHB_ZNETSTREAM pStream ) +{ + if( pStream->inbuf ) + hb_xfree( pStream->inbuf ); + + if( pStream->outbuf ) + hb_xfree( pStream->outbuf ); + + deflateEnd( &pStream->wr ); + inflateEnd( &pStream->rd ); + + hb_xfree( pStream ); +} + +/* create new stream structure + */ +PHB_ZNETSTREAM hb_znetOpen( int level, int strategy ) +{ + PHB_ZNETSTREAM pStream = ( PHB_ZNETSTREAM ) hb_xgrab( sizeof( HB_ZNETSTREAM ) ); + + memset( pStream, 0, sizeof( HB_ZNETSTREAM ) ); + + if( deflateInit2( &pStream->wr, level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy ) == Z_OK ) + { + pStream->wr.next_out = pStream->outbuf = ( Bytef * ) hb_xgrab( HB_ZNET_BUFSIZE ); + pStream->wr.avail_out = HB_ZNET_BUFSIZE; + + pStream->rd.next_in = pStream->inbuf = ( Bytef * ) hb_xgrab( HB_ZNET_BUFSIZE ); + if( inflateInit2( &pStream->rd, -MAX_WBITS ) == Z_OK ) + return pStream; + } + + hb_znetClose( pStream ); + return NULL; +} + +/* read data using stream structure + */ +long hb_znetRead( PHB_ZNETSTREAM pStream, HB_SOCKET sd, void * buffer, long len, HB_LONG timeout ) +{ + long rec = 0; + + pStream->rd.next_out = ( Bytef * ) buffer; + pStream->rd.avail_out = ( uInt ) len; + pStream->err = Z_OK; + + while( pStream->rd.avail_out ) + { + if( pStream->rd.avail_in == 0 && pStream->err == Z_STREAM_END ) + { + if( pStream->rd.avail_out != ( uInt ) len ) + timeout = 0; + rec = hb_socketRecv( sd, pStream->inbuf, HB_ZNET_BUFSIZE, 0, timeout ); + if( rec <= 0 ) + break; + pStream->rd.avail_in = ( uInt ) rec; + pStream->rd.next_in = pStream->inbuf; + rec = 0; + } + pStream->err = inflate( &pStream->rd, Z_SYNC_FLUSH ); + if( pStream->err == Z_BUF_ERROR ) + pStream->err = Z_STREAM_END; + if( pStream->err != Z_OK && pStream->err != Z_STREAM_END ) + break; + } + + len -= pStream->rd.avail_out; + + return len == 0 ? rec : len; +} + +/* flush data in stream structure - return number of bytes left in the + * buffer which were not sent + */ +long hb_znetFlush( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_LONG timeout ) +{ + if( pStream->wr.avail_out > 0 ) + pStream->err = deflate( &pStream->wr, Z_SYNC_FLUSH ); + else + pStream->err = Z_OK; + + while( pStream->wr.avail_out < HB_ZNET_BUFSIZE ) + { + long tosnd = HB_ZNET_BUFSIZE - pStream->wr.avail_out; + long snd = hb_socketSend( sd, pStream->outbuf, tosnd, 0, timeout ); + if( snd <= 0 ) + break; + if( snd < tosnd ) + memmove( pStream->outbuf, pStream->outbuf + snd, tosnd - snd ); + pStream->wr.avail_out += ( uInt ) snd; + pStream->wr.next_out = pStream->outbuf + tosnd - snd; + + if( pStream->err == Z_OK ) + pStream->err = deflate( &pStream->wr, Z_SYNC_FLUSH ); + } + + return HB_ZNET_BUFSIZE - pStream->wr.avail_out; +} + +/* write data using stream structure + */ +long hb_znetWrite( PHB_ZNETSTREAM pStream, HB_SOCKET sd, const void * buffer, long len, HB_LONG timeout ) +{ + long snd = 0; + + pStream->wr.next_in = ( Bytef * ) buffer; + pStream->wr.avail_in = ( uInt ) len; + pStream->err = Z_OK; + + while( pStream->wr.avail_in ) + { + if( pStream->wr.avail_out == 0 ) + { + snd = hb_socketSend( sd, pStream->outbuf, HB_ZNET_BUFSIZE, 0, timeout ); + if( snd <= 0 ) + break; + if( snd < HB_ZNET_BUFSIZE ) + memmove( pStream->outbuf, pStream->outbuf + snd, HB_ZNET_BUFSIZE - snd ); + pStream->wr.avail_out += ( uInt ) snd; + pStream->wr.next_out = pStream->outbuf + HB_ZNET_BUFSIZE - snd; + snd = 0; + } + pStream->err = deflate( &pStream->wr, Z_NO_FLUSH ); + if( pStream->err != Z_OK ) + break; + } + + if( pStream->wr.avail_in == 0 || ( timeout >= 0 && timeout < 10000 && + ( pStream->err == Z_OK || pStream->err == Z_STREAM_END ) ) ) + hb_znetFlush( pStream, sd, timeout < 0 ? timeout : HB_MAX( timeout, 10000 ) ); + + len -= pStream->wr.avail_in; + + return len == 0 ? snd : len; +} + +/* this function is intentionally not in hbinet.c to not create binding + * to ZLIB if user does not use it + */ +HB_FUNC( HB_INETCOMPRESS ) +{ + PHB_ITEM pItem = hb_param( 1, HB_IT_POINTER ); + int iLevel = Z_DEFAULT_COMPRESSION, iStrategy = Z_DEFAULT_STRATEGY, i; + + if( HB_ISNUM( 2 ) ) + { + i = hb_parni( 2 ); + if( i >= Z_NO_COMPRESSION && i <= Z_BEST_COMPRESSION ) + iLevel = i; + } + + if( HB_ISNUM( 3 ) ) + { + i = hb_parni( 3 ); + if( i == Z_FILTERED || + i == Z_HUFFMAN_ONLY || + i == Z_RLE || + i == Z_FIXED ) + iStrategy = i; + } + + if( iLevel == HB_ZLIB_COMPRESSION_DISABLE ) + hb_znetInetInitialize( pItem, NULL, NULL, NULL, NULL, NULL ); + else + { + PHB_ZNETSTREAM pStream = hb_znetOpen( iLevel, iStrategy ); + if( pStream == NULL ) + pItem = NULL; + if( ! hb_znetInetInitialize( pItem, pStream, hb_znetRead, hb_znetWrite, + hb_znetFlush, hb_znetClose ) ) + { + if( pStream ) + hb_znetClose( pStream ); + } + } +}