diff options
101 files changed, 26940 insertions, 26940 deletions
diff --git a/INSTALL.txt b/INSTALL.txt index 291418f1..ea8666bf 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1 +1 @@ -Left empty for now.
+Left empty for now. diff --git a/RELNOTES.txt b/RELNOTES.txt index 687c79fc..208e017e 100644 --- a/RELNOTES.txt +++ b/RELNOTES.txt @@ -1,171 +1,171 @@ -RELEASE NOTES
-
-Version 0.3-pre4
-Nov 13th, 2005
-====================================
-
-PJLIB
- - Correct error reporting in the whole library. No more vague -1 errors!
- - New super portable socket abstraction.
- - Other headers were made super portable too.
- - Ioqueue supports multiple pending operations in a single socket!
- - No more floating point.
- - Ported to new platforms:
- - i386/linux kernel (!)
- - Sparc/Solaris
- - Alpha/Linux
-
-PJSIP
- - Correct error reporting in the whole library. No more -1 errors!
- - Rewrote event, now much more readable.
- - Per object tracing.
-
-
-Version 0.2.9 - 2005/06/19
-====================================
-Core:
- - Moved authentication stuff to core.
-SIMPLE:
- - Initial implementation of Event framework (SUBSCRIBE/NOTIFY)
- - Initial implementation of Presence
- - Tidying up here and there.
-
-Version 0.2.8.5 - 2005/06/05
-====================================
-Core:
- - Tidying up sip_msg.h (no need to export clone/shallow_clone/print API
- for headers).
- - Endpoint now can respond with 501/Not Supported if incoming request is
- not handled by any modules.
- - Endpoint also supports Allow header now.
- - Changed transport names to capital letters (thanks ...)
- - Fixed bug with locking in select() ioqueue.
- - Add status phrase for >= 700 status codes.
-
-pjsua:
- - Verify URL in arguments to prevent crash.
- - Can read commands from config file.
- - Now has buddy list and can send IM!
-
-SIMPLE:
- - Instant Messaging support!
-
-MEDIA:
- - CLOSING SEQUENCE IS NOT PROPER!!! SOMETIMES THREAD IS DEADLOCKED,
- OR DSOUND IS NOT CLOSED PROPERLY!!!
-
-Version 0.2.8 - 2005/05/28
-====================================
-- Simple STUN client support
- SIP UDP port and media RTP/RTCP ports are now STUN aware.
-- Major changed in I/O queue, now callback is used.
- Callback is better because multiple libraries can register to single I/O queue.
- It was not possible with previous implementation, because the function which does
- polling needs to understand what to do when a key is signalled. The changes was
- initially needed to support STUN, but then the STUN client implementation uses the
- simpler select() (in stun_client.c).
-- Merge SDP library into PJMEDIA (no more PJSDP).
- PJSDP only has couple of files (sdp.[hc]), not worth maintaining a library.
-- Fixed bug in select() I/O queue (not thread safe).
-
-
-Version 0.2.7 - 2005/05/14
-====================================
-PJLIB:
-- Major reorganization in pool, introducing pool factory and policy.
- All libraries now can be completely agnostic about memory management
- choosen by application.
-- Fixed bug in GUID generation on mingw
-- Fixed bug in scanner if ASCII > 127 is fed into the input
-- More doxygen documentation
-
-PJMEDIA:
-- Renamed some functions/structs/etc.
-
-UA library:
-- Registration client completed (including authentication).
-- Fixed a crash condition when tsx has not received any response.
-
-PJSUA:
-- Use getopt.c
-
-
-Version 0.2.6 - 2005/04/17
-====================================
-All:
-- tidying up header files.
-
-Core library:
-- Removed PJSIP_HAS_DUMP macro (now automatically calculated based on log level)
-- Added pjsip_tx_data_invalidate_msg()
-
-UA library:
-- big modification in dialog API to better support injecting custom header
- in outgoing message and to make it more flexible for future features (such
- as caching the outgoing message):
- - sending messages is now done in two steps: (1)create the msg transmit
- buffer (pjsip_dlg_tx_data), (2)send the msg transmit buffer.
- - dialog state won't change in step (1); it will change only
- when the message is actually sent in step (2).
- What won't change:
- - the dialog state
- - outgoing CSeq
- - outgoing message transmit buffer (pjsip_dlg_tx_data) will be deleted
- when sent in step (2). Application MAY save request messages for
- future transmission, even after the request has been sent. To do so,
- it must increment the reference counter and remember that each time
- the request is sent, the reference counter will be decremented. Also
- application CAN NOT re-send the message while the transaction that
- sends the message has not terminated.
-- changed API names: pjsip_dlg_answer_invitation() --> pjsip_dlg_answer(), etc.
-- initial sip_reg.h for SIP registration.
-
-Auth library:
-- the digest authentication should work, however it has not been tested
- with any SIP servers because we don't have REGISTER support yet.
-*Note*:
- authentication in pjsua still uses hardcoded user/pass: hello/world.
-
-
-Version 0.2.5.2 - 2005/03/25
-====================================
-UA library:
-- Major modification in dialog callbacks, now high level callbacks such as
- on_calling(), on_incoming(), on_provisional(), on_established(), and
- on_disconnected() are provided instead of just one callback (on_event()).
-- Added pjsip_dlg_disconnect() which should handle all cases of disconnection
- such as sending CANCEL, sending BYE, or sending final response to INVITE.
-- Added and updated doxygen comments.
-- Changed: pjsip_dialog_xxx --> pjsip_dlg_xxx
-
-New:
-- PJSIP Auth library, which supports digest scheme.
-- Only client functionality is present at the moment.
-
-PJSUA:
-- Remove callgen feature, as it makes the application complicated.
- Will move it to different application, to make way for more sophisticated
- call generator.
-- Support the new callback framework.
-- Support the new digest authentication (UAC only).
-
-SIP core:
-- Added PJSIP_EVENT_BEFORE_TX, triggered by transaction before sending
- outgoing message (including retransmission). Application can use this event
- (via dialog callback) to modify the message before transmission (such as
- adding authorization headers).
-- Added general purpose function to print text body.
-- Move constant strings in parser to public/extern, just in case other
- part of the library need to use them.
-
-PJMEDIA:
-- Protect against NULL in destroy session.
-
-Misc:
-- Rename build output directory x_Win32_x --> x_vc7_x or x_vc6_x, also
- library naming includes _vc7_ or _vc6_ now.
-- Renamed pjsip_test_core --> pjsip_core_test.
-- Renamed pjaudio_tool --> pjmedia_audio_tool.
-- Renamed sdp_test --> pjsdp_test
-- PJLIB test: added second pool test after new/malloc test.
-- Renamed README.txt --> INSTALL.txt
+RELEASE NOTES + +Version 0.3-pre4 +Nov 13th, 2005 +==================================== + +PJLIB + - Correct error reporting in the whole library. No more vague -1 errors! + - New super portable socket abstraction. + - Other headers were made super portable too. + - Ioqueue supports multiple pending operations in a single socket! + - No more floating point. + - Ported to new platforms: + - i386/linux kernel (!) + - Sparc/Solaris + - Alpha/Linux + +PJSIP + - Correct error reporting in the whole library. No more -1 errors! + - Rewrote event, now much more readable. + - Per object tracing. + + +Version 0.2.9 - 2005/06/19 +==================================== +Core: + - Moved authentication stuff to core. +SIMPLE: + - Initial implementation of Event framework (SUBSCRIBE/NOTIFY) + - Initial implementation of Presence + - Tidying up here and there. + +Version 0.2.8.5 - 2005/06/05 +==================================== +Core: + - Tidying up sip_msg.h (no need to export clone/shallow_clone/print API + for headers). + - Endpoint now can respond with 501/Not Supported if incoming request is + not handled by any modules. + - Endpoint also supports Allow header now. + - Changed transport names to capital letters (thanks ...) + - Fixed bug with locking in select() ioqueue. + - Add status phrase for >= 700 status codes. + +pjsua: + - Verify URL in arguments to prevent crash. + - Can read commands from config file. + - Now has buddy list and can send IM! + +SIMPLE: + - Instant Messaging support! + +MEDIA: + - CLOSING SEQUENCE IS NOT PROPER!!! SOMETIMES THREAD IS DEADLOCKED, + OR DSOUND IS NOT CLOSED PROPERLY!!! + +Version 0.2.8 - 2005/05/28 +==================================== +- Simple STUN client support + SIP UDP port and media RTP/RTCP ports are now STUN aware. +- Major changed in I/O queue, now callback is used. + Callback is better because multiple libraries can register to single I/O queue. + It was not possible with previous implementation, because the function which does + polling needs to understand what to do when a key is signalled. The changes was + initially needed to support STUN, but then the STUN client implementation uses the + simpler select() (in stun_client.c). +- Merge SDP library into PJMEDIA (no more PJSDP). + PJSDP only has couple of files (sdp.[hc]), not worth maintaining a library. +- Fixed bug in select() I/O queue (not thread safe). + + +Version 0.2.7 - 2005/05/14 +==================================== +PJLIB: +- Major reorganization in pool, introducing pool factory and policy. + All libraries now can be completely agnostic about memory management + choosen by application. +- Fixed bug in GUID generation on mingw +- Fixed bug in scanner if ASCII > 127 is fed into the input +- More doxygen documentation + +PJMEDIA: +- Renamed some functions/structs/etc. + +UA library: +- Registration client completed (including authentication). +- Fixed a crash condition when tsx has not received any response. + +PJSUA: +- Use getopt.c + + +Version 0.2.6 - 2005/04/17 +==================================== +All: +- tidying up header files. + +Core library: +- Removed PJSIP_HAS_DUMP macro (now automatically calculated based on log level) +- Added pjsip_tx_data_invalidate_msg() + +UA library: +- big modification in dialog API to better support injecting custom header + in outgoing message and to make it more flexible for future features (such + as caching the outgoing message): + - sending messages is now done in two steps: (1)create the msg transmit + buffer (pjsip_dlg_tx_data), (2)send the msg transmit buffer. + - dialog state won't change in step (1); it will change only + when the message is actually sent in step (2). + What won't change: + - the dialog state + - outgoing CSeq + - outgoing message transmit buffer (pjsip_dlg_tx_data) will be deleted + when sent in step (2). Application MAY save request messages for + future transmission, even after the request has been sent. To do so, + it must increment the reference counter and remember that each time + the request is sent, the reference counter will be decremented. Also + application CAN NOT re-send the message while the transaction that + sends the message has not terminated. +- changed API names: pjsip_dlg_answer_invitation() --> pjsip_dlg_answer(), etc. +- initial sip_reg.h for SIP registration. + +Auth library: +- the digest authentication should work, however it has not been tested + with any SIP servers because we don't have REGISTER support yet. +*Note*: + authentication in pjsua still uses hardcoded user/pass: hello/world. + + +Version 0.2.5.2 - 2005/03/25 +==================================== +UA library: +- Major modification in dialog callbacks, now high level callbacks such as + on_calling(), on_incoming(), on_provisional(), on_established(), and + on_disconnected() are provided instead of just one callback (on_event()). +- Added pjsip_dlg_disconnect() which should handle all cases of disconnection + such as sending CANCEL, sending BYE, or sending final response to INVITE. +- Added and updated doxygen comments. +- Changed: pjsip_dialog_xxx --> pjsip_dlg_xxx + +New: +- PJSIP Auth library, which supports digest scheme. +- Only client functionality is present at the moment. + +PJSUA: +- Remove callgen feature, as it makes the application complicated. + Will move it to different application, to make way for more sophisticated + call generator. +- Support the new callback framework. +- Support the new digest authentication (UAC only). + +SIP core: +- Added PJSIP_EVENT_BEFORE_TX, triggered by transaction before sending + outgoing message (including retransmission). Application can use this event + (via dialog callback) to modify the message before transmission (such as + adding authorization headers). +- Added general purpose function to print text body. +- Move constant strings in parser to public/extern, just in case other + part of the library need to use them. + +PJMEDIA: +- Protect against NULL in destroy session. + +Misc: +- Rename build output directory x_Win32_x --> x_vc7_x or x_vc6_x, also + library naming includes _vc7_ or _vc6_ now. +- Renamed pjsip_test_core --> pjsip_core_test. +- Renamed pjaudio_tool --> pjmedia_audio_tool. +- Renamed sdp_test --> pjsdp_test +- PJLIB test: added second pool test after new/malloc test. +- Renamed README.txt --> INSTALL.txt diff --git a/pjlib-util/include/pjlib-util.h b/pjlib-util/include/pjlib-util.h index 2def324b..0040d452 100644 --- a/pjlib-util/include/pjlib-util.h +++ b/pjlib-util/include/pjlib-util.h @@ -1,22 +1,22 @@ -/* $Id */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjlib-util/md5.h>
-#include <pjlib-util/scanner.h>
-#include <pjlib-util/stun.h>
-#include <pjlib-util/xml.h>
+/* $Id */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjlib-util/md5.h> +#include <pjlib-util/scanner.h> +#include <pjlib-util/stun.h> +#include <pjlib-util/xml.h> diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index deb602bc..2a8d5c08 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -1,465 +1,465 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_CONFIG_H__
-#define __PJ_CONFIG_H__
-
-/**
- * @file config.h
- * @brief PJLIB Main configuration settings.
- */
-
-/********************************************************************
- * Include compiler specific configuration.
- */
-#if defined(_MSC_VER)
-# include <pj/compat/cc_msvc.h>
-#elif defined(__GNUC__)
-# include <pj/compat/cc_gcc.h>
-#else
-# error "Unknown compiler."
-#endif
-
-
-/********************************************************************
- * Include target OS specific configuration.
- */
-#if defined(PJ_WIN32) && PJ_WIN32!=0
-# include <pj/compat/os_win32.h>
-#elif defined(PJ_LINUX) && PJ_LINUX!=0
-# include <pj/compat/os_linux.h>
-#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0
-# include <pj/compat/os_linux_kernel.h>
-#elif defined(PJ_PALMOS) && PJ_PALMOS!=0
-# include <pj/compat/os_palmos.h>
-#elif defined(PJ_SUNOS) && PJ_SUNOS!=0
-# include <pj/compat/os_sunos.h>
-#else
-# error "Please specify target os."
-#endif
-
-
-/********************************************************************
- * Target machine specific configuration.
- */
-#if defined (PJ_M_I386) && PJ_M_I386 != 0
-# include <pj/compat/m_i386.h>
-#elif defined (PJ_M_M68K) && PJ_M_M68K != 0
-# include <pj/compat/m_m68k.h>
-#elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
-# include <pj/compat/m_alpha.h>
-#elif defined (PJ_M_SPARC) && PJ_M_SPARC != 0
-# include <pj/compat/m_sparc.h>
-#else
-# error "Please specify target machine."
-#endif
-
-/* Include size_t definition. */
-#include <pj/compat/size_t.h>
-
-/* Include site/user specific configuration to control PJLIB features.
- * YOU MUST CREATE THIS FILE YOURSELF!!
- */
-#include <pj/config_site.h>
-
-/********************************************************************
- * PJLIB Features.
- */
-
-/* Overrides for DOXYGEN */
-#ifdef DOXYGEN
-# undef PJ_FUNCTIONS_ARE_INLINED
-# undef PJ_HAS_FLOATING_POINT
-# undef PJ_LOG_MAX_LEVEL
-# undef PJ_LOG_MAX_SIZE
-# undef PJ_LOG_USE_STACK_BUFFER
-# undef PJ_TERM_HAS_COLOR
-# undef PJ_POOL_DEBUG
-# undef PJ_HAS_TCP
-# undef PJ_MAX_HOSTNAME
-# undef PJ_IOQUEUE_MAX_HANDLES
-# undef FD_SETSIZE
-# undef PJ_HAS_SEMAPHORE
-# undef PJ_HAS_EVENT_OBJ
-# undef PJ_ENABLE_EXTRA_CHECK
-#endif
-
-/**
- * @defgroup pj_config Build Configuration
- * @ingroup PJ
- * @{
- *
- * This section contains macros that can set during PJLIB build process
- * to controll various aspects of the library.
- *
- * <b>Note</b>: the values in this page does NOT necessarily reflect to the
- * macro values during the build process.
- */
-
-/**
- * If this macro is set to 1, it will enable some debugging checking
- * in the library.
- *
- * Default: equal to (NOT NDEBUG).
- */
-#ifndef PJ_DEBUG
-# ifndef NDEBUG
-# define PJ_DEBUG 1
-# else
-# define PJ_DEBUG 0
-# endif
-#endif
-
-/**
- * Expand functions in *_i.h header files as inline.
- *
- * Default: 0.
- */
-#ifndef PJ_FUNCTIONS_ARE_INLINED
-# define PJ_FUNCTIONS_ARE_INLINED 0
-#endif
-
-/**
- * Use floating point computations in the library.
- *
- * Default: 1.
- */
-#ifndef PJ_HAS_FLOATING_POINT
-# define PJ_HAS_FLOATING_POINT 1
-#endif
-
-/**
- * Declare maximum logging level/verbosity. Lower number indicates higher
- * importance, with the highest importance has level zero. The least
- * important level is five in this implementation, but this can be extended
- * by supplying the appropriate implementation.
- *
- * The level conventions:
- * - 0: fatal error
- * - 1: error
- * - 2: warning
- * - 3: info
- * - 4: debug
- * - 5: trace
- * - 6: more detailed trace
- *
- * Default: 4
- */
-#ifndef PJ_LOG_MAX_LEVEL
-# define PJ_LOG_MAX_LEVEL 5
-#endif
-
-/**
- * Maximum message size that can be sent to output device for each call
- * to PJ_LOG(). If the message size is longer than this value, it will be cut.
- * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER
- * flag is set.
- *
- * Default: 800
- */
-#ifndef PJ_LOG_MAX_SIZE
-# define PJ_LOG_MAX_SIZE 800
-#endif
-
-/**
- * Log buffer.
- * Does the log get the buffer from the stack? (default is yes).
- * If the value is set to NO, then the buffer will be taken from static
- * buffer, which in this case will make the log function non-reentrant.
- *
- * Default: 1
- */
-#ifndef PJ_LOG_USE_STACK_BUFFER
-# define PJ_LOG_USE_STACK_BUFFER 1
-#endif
-
-
-/**
- * Colorfull terminal (for logging etc).
- *
- * Default: 1
- */
-#ifndef PJ_TERM_HAS_COLOR
-# define PJ_TERM_HAS_COLOR 1
-#endif
-
-/**
- * Pool debugging.
- *
- * Default: 0
- */
-#ifndef PJ_POOL_DEBUG
-# define PJ_POOL_DEBUG 0
-#endif
-
-/**
- * \def PJ_HAS_TCP
- * Support TCP in the library.
- * Disabling TCP will reduce the footprint slightly (about 6KB).
- *
- * Default: 1
- */
-#ifndef PJ_HAS_TCP
-# define PJ_HAS_TCP 1
-#endif
-
-/**
- * Maximum hostname length.
- * Libraries sometimes needs to make copy of an address to stack buffer;
- * the value here affects the stack usage.
- *
- * Default: 128
- */
-#ifndef PJ_MAX_HOSTNAME
-# define PJ_MAX_HOSTNAME (128)
-#endif
-
-/**
- * Constants for declaring the maximum handles that can be supported by
- * a single IOQ framework. This constant might not be relevant to the
- * underlying I/O queue impelementation, but still, developers should be
- * aware of this constant, to make sure that the program will not break when
- * the underlying implementation changes.
- *
- * For implementation based on select(), the value here will be used as the
- * maximum number of socket handles passed to select() (i.e. FD_SETSIZE will
- * be set to this value).
- *
- * Default: 256
- */
-#ifndef PJ_IOQUEUE_MAX_HANDLES
-# define PJ_IOQUEUE_MAX_HANDLES (256)
-#endif
-
-/**
- * Overrides FD_SETSIZE so it is consistent throughout the library.
- * OS specific configuration header (compat/os_*) might have declared
- * FD_SETSIZE, thus we only set if it hasn't been declared.
- *
- * Default: #PJ_IOQUEUE_MAX_HANDLES
- */
-#ifndef FD_SETSIZE
-# define FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES
-#endif
-
-/**
- * Has semaphore functionality?
- *
- * Default: 1
- */
-#ifndef PJ_HAS_SEMAPHORE
-# define PJ_HAS_SEMAPHORE 1
-#endif
-
-
-/**
- * Event object (for synchronization, e.g. in Win32)
- *
- * Default: 1
- */
-#ifndef PJ_HAS_EVENT_OBJ
-# define PJ_HAS_EVENT_OBJ 1
-#endif
-
-
-/**
- * Enable library's extra check.
- * If this macro is enabled, #PJ_ASSERT_RETURN macro will expand to
- * run-time checking. If this macro is disabled, #PJ_ASSERT_RETURN
- * will simply evaluate to #pj_assert().
- *
- * You can disable this macro to reduce size, at the risk of crashes
- * if invalid value (e.g. NULL) is passed to the library.
- *
- * Default: 1
- */
-#ifndef PJ_ENABLE_EXTRA_CHECK
-# define PJ_ENABLE_EXTRA_CHECK 1
-#endif
-
-
-/**
- * Enable name registration for exceptions with #pj_exception_id_alloc().
- * If this feature is enabled, then the library will keep track of
- * names associated with each exception ID requested by application via
- * #pj_exception_id_alloc().
- *
- * Disabling this macro will reduce the code and .bss size by a tad bit.
- * See also #PJ_MAX_EXCEPTION_ID.
- *
- * Default: 1
- */
-#ifndef PJ_HAS_EXCEPTION_NAMES
-# define PJ_HAS_EXCEPTION_NAMES 1
-#endif
-
-/**
- * Maximum number of unique exception IDs that can be requested
- * with #pj_exception_id_alloc(). For each entry, a small record will
- * be allocated in the .bss segment.
- *
- * Default: 16
- */
-#ifndef PJ_MAX_EXCEPTION_ID
-# define PJ_MAX_EXCEPTION_ID 16
-#endif
-
-/** @} */
-
-/********************************************************************
- * General macros.
- */
-
-/**
- * @def PJ_INLINE(type)
- * @param type The return type of the function.
- * Expand the function as inline.
- */
-#define PJ_INLINE(type) PJ_INLINE_SPECIFIER type
-
-/**
- * @def PJ_DECL(type)
- * @param type The return type of the function.
- * Declare a function.
- */
-/**
- * @def PJ_DECL_NO_RETURN(type)
- * @param type The return type of the function.
- * Declare a function that will not return.
- */
-/**
- * @def PJ_BEGIN_DECL
- * Mark beginning of declaration section in a header file.
- */
-/**
- * @def PJ_END_DECL
- * Mark end of declaration section in a header file.
- */
-#ifdef __cplusplus
-# define PJ_DECL(type) type
-# define PJ_DECL_NO_RETURN(type) type PJ_NORETURN
-# define PJ_BEGIN_DECL extern "C" {
-# define PJ_END_DECL }
-#else
-# define PJ_DECL(type) extern type
-# define PJ_DECL_NO_RETURN(type) PJ_NORETURN type
-# define PJ_BEGIN_DECL
-# define PJ_END_DECL
-#endif
-
-/**
- * @def PJ_DEF(type)
- * @param type The return type of the function.
- * Define a function.
- */
-#define PJ_DEF(type) type
-
-/**
- * @def PJ_EXPORT_SYMBOL(sym)
- * @param sym The symbol to export.
- * Export the specified symbol in compilation type that requires export
- * (e.g. Linux kernel).
- */
-#ifdef __PJ_EXPORT_SYMBOL
-# define PJ_EXPORT_SYMBOL(sym) __PJ_EXPORT_SYMBOL(sym)
-#else
-# define PJ_EXPORT_SYMBOL(sym)
-#endif
-
-/**
- * @def PJ_IDECL(type)
- * @param type The function's return type.
- * Declare a function that may be expanded as inline.
- */
-/**
- * @def PJ_IDEF(type)
- * @param type The function's return type.
- * Define a function that may be expanded as inline.
- */
-
-#if PJ_FUNCTIONS_ARE_INLINED
-# define PJ_IDECL(type) PJ_INLINE(type)
-# define PJ_IDEF(type) PJ_INLINE(type)
-#else
-# define PJ_IDECL(type) PJ_DECL(type)
-# define PJ_IDEF(type) PJ_DEF(type)
-#endif
-
-/**
- * @def PJ_UNUSED_ARG(arg)
- * @param arg The argument name.
- * PJ_UNUSED_ARG prevents warning about unused argument in a function.
- */
-#define PJ_UNUSED_ARG(arg) (void)arg
-
-/**
- * @def PJ_TODO(id)
- * @param id Any identifier that will be printed as TODO message.
- * PJ_TODO macro will display TODO message as warning during compilation.
- * Example: PJ_TODO(CLEAN_UP_ERROR);
- */
-#ifndef PJ_TODO
-# define PJ_TODO(id) TODO___##id:
-#endif
-
-/**
- * Function attributes to inform that the function may throw exception.
- *
- * @param x The exception list, enclosed in parenthesis.
- */
-#define __pj_throw__(x)
-
-
-/********************************************************************
- * Sanity Checks
- */
-#ifndef PJ_HAS_HIGH_RES_TIMER
-# error "PJ_HAS_HIGH_RES_TIMER is not defined!"
-#endif
-
-#if !defined(PJ_HAS_PENTIUM)
-# error "PJ_HAS_PENTIUM is not defined!"
-#endif
-
-#if !defined(PJ_IS_LITTLE_ENDIAN)
-# error "PJ_IS_LITTLE_ENDIAN is not defined!"
-#endif
-
-#if !defined(PJ_IS_BIG_ENDIAN)
-# error "PJ_IS_BIG_ENDIAN is not defined!"
-#endif
-
-
-
-PJ_BEGIN_DECL
-
-/**
- * PJLIB version string.
- */
-extern const char *PJ_VERSION;
-
-/**
- * Dump configuration to log with verbosity equal to info(3).
- */
-PJ_DECL(void) pj_dump_config(void);
-
-PJ_END_DECL
-
-
-#endif /* __PJ_CONFIG_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_CONFIG_H__ +#define __PJ_CONFIG_H__ + +/** + * @file config.h + * @brief PJLIB Main configuration settings. + */ + +/******************************************************************** + * Include compiler specific configuration. + */ +#if defined(_MSC_VER) +# include <pj/compat/cc_msvc.h> +#elif defined(__GNUC__) +# include <pj/compat/cc_gcc.h> +#else +# error "Unknown compiler." +#endif + + +/******************************************************************** + * Include target OS specific configuration. + */ +#if defined(PJ_WIN32) && PJ_WIN32!=0 +# include <pj/compat/os_win32.h> +#elif defined(PJ_LINUX) && PJ_LINUX!=0 +# include <pj/compat/os_linux.h> +#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0 +# include <pj/compat/os_linux_kernel.h> +#elif defined(PJ_PALMOS) && PJ_PALMOS!=0 +# include <pj/compat/os_palmos.h> +#elif defined(PJ_SUNOS) && PJ_SUNOS!=0 +# include <pj/compat/os_sunos.h> +#else +# error "Please specify target os." +#endif + + +/******************************************************************** + * Target machine specific configuration. + */ +#if defined (PJ_M_I386) && PJ_M_I386 != 0 +# include <pj/compat/m_i386.h> +#elif defined (PJ_M_M68K) && PJ_M_M68K != 0 +# include <pj/compat/m_m68k.h> +#elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0 +# include <pj/compat/m_alpha.h> +#elif defined (PJ_M_SPARC) && PJ_M_SPARC != 0 +# include <pj/compat/m_sparc.h> +#else +# error "Please specify target machine." +#endif + +/* Include size_t definition. */ +#include <pj/compat/size_t.h> + +/* Include site/user specific configuration to control PJLIB features. + * YOU MUST CREATE THIS FILE YOURSELF!! + */ +#include <pj/config_site.h> + +/******************************************************************** + * PJLIB Features. + */ + +/* Overrides for DOXYGEN */ +#ifdef DOXYGEN +# undef PJ_FUNCTIONS_ARE_INLINED +# undef PJ_HAS_FLOATING_POINT +# undef PJ_LOG_MAX_LEVEL +# undef PJ_LOG_MAX_SIZE +# undef PJ_LOG_USE_STACK_BUFFER +# undef PJ_TERM_HAS_COLOR +# undef PJ_POOL_DEBUG +# undef PJ_HAS_TCP +# undef PJ_MAX_HOSTNAME +# undef PJ_IOQUEUE_MAX_HANDLES +# undef FD_SETSIZE +# undef PJ_HAS_SEMAPHORE +# undef PJ_HAS_EVENT_OBJ +# undef PJ_ENABLE_EXTRA_CHECK +#endif + +/** + * @defgroup pj_config Build Configuration + * @ingroup PJ + * @{ + * + * This section contains macros that can set during PJLIB build process + * to controll various aspects of the library. + * + * <b>Note</b>: the values in this page does NOT necessarily reflect to the + * macro values during the build process. + */ + +/** + * If this macro is set to 1, it will enable some debugging checking + * in the library. + * + * Default: equal to (NOT NDEBUG). + */ +#ifndef PJ_DEBUG +# ifndef NDEBUG +# define PJ_DEBUG 1 +# else +# define PJ_DEBUG 0 +# endif +#endif + +/** + * Expand functions in *_i.h header files as inline. + * + * Default: 0. + */ +#ifndef PJ_FUNCTIONS_ARE_INLINED +# define PJ_FUNCTIONS_ARE_INLINED 0 +#endif + +/** + * Use floating point computations in the library. + * + * Default: 1. + */ +#ifndef PJ_HAS_FLOATING_POINT +# define PJ_HAS_FLOATING_POINT 1 +#endif + +/** + * Declare maximum logging level/verbosity. Lower number indicates higher + * importance, with the highest importance has level zero. The least + * important level is five in this implementation, but this can be extended + * by supplying the appropriate implementation. + * + * The level conventions: + * - 0: fatal error + * - 1: error + * - 2: warning + * - 3: info + * - 4: debug + * - 5: trace + * - 6: more detailed trace + * + * Default: 4 + */ +#ifndef PJ_LOG_MAX_LEVEL +# define PJ_LOG_MAX_LEVEL 5 +#endif + +/** + * Maximum message size that can be sent to output device for each call + * to PJ_LOG(). If the message size is longer than this value, it will be cut. + * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER + * flag is set. + * + * Default: 800 + */ +#ifndef PJ_LOG_MAX_SIZE +# define PJ_LOG_MAX_SIZE 800 +#endif + +/** + * Log buffer. + * Does the log get the buffer from the stack? (default is yes). + * If the value is set to NO, then the buffer will be taken from static + * buffer, which in this case will make the log function non-reentrant. + * + * Default: 1 + */ +#ifndef PJ_LOG_USE_STACK_BUFFER +# define PJ_LOG_USE_STACK_BUFFER 1 +#endif + + +/** + * Colorfull terminal (for logging etc). + * + * Default: 1 + */ +#ifndef PJ_TERM_HAS_COLOR +# define PJ_TERM_HAS_COLOR 1 +#endif + +/** + * Pool debugging. + * + * Default: 0 + */ +#ifndef PJ_POOL_DEBUG +# define PJ_POOL_DEBUG 0 +#endif + +/** + * \def PJ_HAS_TCP + * Support TCP in the library. + * Disabling TCP will reduce the footprint slightly (about 6KB). + * + * Default: 1 + */ +#ifndef PJ_HAS_TCP +# define PJ_HAS_TCP 1 +#endif + +/** + * Maximum hostname length. + * Libraries sometimes needs to make copy of an address to stack buffer; + * the value here affects the stack usage. + * + * Default: 128 + */ +#ifndef PJ_MAX_HOSTNAME +# define PJ_MAX_HOSTNAME (128) +#endif + +/** + * Constants for declaring the maximum handles that can be supported by + * a single IOQ framework. This constant might not be relevant to the + * underlying I/O queue impelementation, but still, developers should be + * aware of this constant, to make sure that the program will not break when + * the underlying implementation changes. + * + * For implementation based on select(), the value here will be used as the + * maximum number of socket handles passed to select() (i.e. FD_SETSIZE will + * be set to this value). + * + * Default: 256 + */ +#ifndef PJ_IOQUEUE_MAX_HANDLES +# define PJ_IOQUEUE_MAX_HANDLES (256) +#endif + +/** + * Overrides FD_SETSIZE so it is consistent throughout the library. + * OS specific configuration header (compat/os_*) might have declared + * FD_SETSIZE, thus we only set if it hasn't been declared. + * + * Default: #PJ_IOQUEUE_MAX_HANDLES + */ +#ifndef FD_SETSIZE +# define FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES +#endif + +/** + * Has semaphore functionality? + * + * Default: 1 + */ +#ifndef PJ_HAS_SEMAPHORE +# define PJ_HAS_SEMAPHORE 1 +#endif + + +/** + * Event object (for synchronization, e.g. in Win32) + * + * Default: 1 + */ +#ifndef PJ_HAS_EVENT_OBJ +# define PJ_HAS_EVENT_OBJ 1 +#endif + + +/** + * Enable library's extra check. + * If this macro is enabled, #PJ_ASSERT_RETURN macro will expand to + * run-time checking. If this macro is disabled, #PJ_ASSERT_RETURN + * will simply evaluate to #pj_assert(). + * + * You can disable this macro to reduce size, at the risk of crashes + * if invalid value (e.g. NULL) is passed to the library. + * + * Default: 1 + */ +#ifndef PJ_ENABLE_EXTRA_CHECK +# define PJ_ENABLE_EXTRA_CHECK 1 +#endif + + +/** + * Enable name registration for exceptions with #pj_exception_id_alloc(). + * If this feature is enabled, then the library will keep track of + * names associated with each exception ID requested by application via + * #pj_exception_id_alloc(). + * + * Disabling this macro will reduce the code and .bss size by a tad bit. + * See also #PJ_MAX_EXCEPTION_ID. + * + * Default: 1 + */ +#ifndef PJ_HAS_EXCEPTION_NAMES +# define PJ_HAS_EXCEPTION_NAMES 1 +#endif + +/** + * Maximum number of unique exception IDs that can be requested + * with #pj_exception_id_alloc(). For each entry, a small record will + * be allocated in the .bss segment. + * + * Default: 16 + */ +#ifndef PJ_MAX_EXCEPTION_ID +# define PJ_MAX_EXCEPTION_ID 16 +#endif + +/** @} */ + +/******************************************************************** + * General macros. + */ + +/** + * @def PJ_INLINE(type) + * @param type The return type of the function. + * Expand the function as inline. + */ +#define PJ_INLINE(type) PJ_INLINE_SPECIFIER type + +/** + * @def PJ_DECL(type) + * @param type The return type of the function. + * Declare a function. + */ +/** + * @def PJ_DECL_NO_RETURN(type) + * @param type The return type of the function. + * Declare a function that will not return. + */ +/** + * @def PJ_BEGIN_DECL + * Mark beginning of declaration section in a header file. + */ +/** + * @def PJ_END_DECL + * Mark end of declaration section in a header file. + */ +#ifdef __cplusplus +# define PJ_DECL(type) type +# define PJ_DECL_NO_RETURN(type) type PJ_NORETURN +# define PJ_BEGIN_DECL extern "C" { +# define PJ_END_DECL } +#else +# define PJ_DECL(type) extern type +# define PJ_DECL_NO_RETURN(type) PJ_NORETURN type +# define PJ_BEGIN_DECL +# define PJ_END_DECL +#endif + +/** + * @def PJ_DEF(type) + * @param type The return type of the function. + * Define a function. + */ +#define PJ_DEF(type) type + +/** + * @def PJ_EXPORT_SYMBOL(sym) + * @param sym The symbol to export. + * Export the specified symbol in compilation type that requires export + * (e.g. Linux kernel). + */ +#ifdef __PJ_EXPORT_SYMBOL +# define PJ_EXPORT_SYMBOL(sym) __PJ_EXPORT_SYMBOL(sym) +#else +# define PJ_EXPORT_SYMBOL(sym) +#endif + +/** + * @def PJ_IDECL(type) + * @param type The function's return type. + * Declare a function that may be expanded as inline. + */ +/** + * @def PJ_IDEF(type) + * @param type The function's return type. + * Define a function that may be expanded as inline. + */ + +#if PJ_FUNCTIONS_ARE_INLINED +# define PJ_IDECL(type) PJ_INLINE(type) +# define PJ_IDEF(type) PJ_INLINE(type) +#else +# define PJ_IDECL(type) PJ_DECL(type) +# define PJ_IDEF(type) PJ_DEF(type) +#endif + +/** + * @def PJ_UNUSED_ARG(arg) + * @param arg The argument name. + * PJ_UNUSED_ARG prevents warning about unused argument in a function. + */ +#define PJ_UNUSED_ARG(arg) (void)arg + +/** + * @def PJ_TODO(id) + * @param id Any identifier that will be printed as TODO message. + * PJ_TODO macro will display TODO message as warning during compilation. + * Example: PJ_TODO(CLEAN_UP_ERROR); + */ +#ifndef PJ_TODO +# define PJ_TODO(id) TODO___##id: +#endif + +/** + * Function attributes to inform that the function may throw exception. + * + * @param x The exception list, enclosed in parenthesis. + */ +#define __pj_throw__(x) + + +/******************************************************************** + * Sanity Checks + */ +#ifndef PJ_HAS_HIGH_RES_TIMER +# error "PJ_HAS_HIGH_RES_TIMER is not defined!" +#endif + +#if !defined(PJ_HAS_PENTIUM) +# error "PJ_HAS_PENTIUM is not defined!" +#endif + +#if !defined(PJ_IS_LITTLE_ENDIAN) +# error "PJ_IS_LITTLE_ENDIAN is not defined!" +#endif + +#if !defined(PJ_IS_BIG_ENDIAN) +# error "PJ_IS_BIG_ENDIAN is not defined!" +#endif + + + +PJ_BEGIN_DECL + +/** + * PJLIB version string. + */ +extern const char *PJ_VERSION; + +/** + * Dump configuration to log with verbosity equal to info(3). + */ +PJ_DECL(void) pj_dump_config(void); + +PJ_END_DECL + + +#endif /* __PJ_CONFIG_H__ */ + diff --git a/pjlib/include/pj/ctype.h b/pjlib/include/pj/ctype.h index e2ab1033..0319cfdb 100644 --- a/pjlib/include/pj/ctype.h +++ b/pjlib/include/pj/ctype.h @@ -1,173 +1,173 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_CTYPE_H__
-#define __PJ_CTYPE_H__
-
-/**
- * @file ctype.h
- * @brief C type helper macros.
- */
-
-#include <pj/types.h>
-#include <pj/compat/ctype.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup pj_ctype ctype - Character Type
- * @ingroup PJ_MISC
- * @{
- *
- * This module contains several inline functions/macros for testing or
- * manipulating character types. It is provided in PJLIB because PJLIB
- * must not depend to LIBC.
- */
-
-/**
- * Returns a non-zero value if either isalpha or isdigit is true for c.
- * @param c The integer character to test.
- * @return Non-zero value if either isalpha or isdigit is true for c.
- */
-PJ_INLINE(int) pj_isalnum(int c) { return isalnum(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of an
- * alphabetic character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of an
- * alphabetic character.
- */
-PJ_INLINE(int) pj_isalpha(int c) { return isalpha(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of an
- * ASCII character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * an ASCII character.
- */
-PJ_INLINE(int) pj_isascii(int c) { return isascii(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a decimal-digit character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a decimal-digit character.
- */
-PJ_INLINE(int) pj_isdigit(int c) { return isdigit(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a space character (0x09 - 0x0D or 0x20).
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a space character (0x09 - 0x0D or 0x20).
- */
-PJ_INLINE(int) pj_isspace(int c) { return isspace(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a lowercase character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a lowercase character.
- */
-PJ_INLINE(int) pj_islower(int c) { return islower(c); }
-
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a uppercase character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a uppercase character.
- */
-PJ_INLINE(int) pj_isupper(int c) { return isupper(c); }
-
-/**
- * Returns a non-zero value if c is a either a space (' ') or horizontal
- * tab ('\\t') character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a either a space (' ') or horizontal
- * tab ('\\t') character.
- */
-PJ_INLINE(int) pj_isblank(int c) { return isblank(c); }
-
-/**
- * Converts character to lowercase.
- * @param c The integer character to convert.
- * @return Lowercase character of c.
- */
-PJ_INLINE(int) pj_tolower(int c) { return tolower(c); }
-
-/**
- * Converts character to uppercase.
- * @param c The integer character to convert.
- * @return Uppercase character of c.
- */
-PJ_INLINE(int) pj_toupper(int c) { return toupper(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * an hexadecimal digit character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * an hexadecimal digit character.
- */
-PJ_INLINE(int) pj_isxdigit(int c){ return isxdigit(c); }
-
-/**
- * Array of hex digits, in lowerspace.
- */
-extern char pj_hex_digits[];
-
-/**
- * Convert a value to hex representation.
- * @param value Integral value to convert.
- * @param p Buffer to hold the hex representation, which must be
- * at least two bytes length.
- */
-PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p)
-{
- *p++ = pj_hex_digits[ (value & 0xF0) >> 4 ];
- *p = pj_hex_digits[ (value & 0x0F) ];
-}
-
-/**
- * Convert hex digit c to integral value.
- * @param c The hex digit character.
- * @return The integral value between 0 and 15.
- */
-PJ_INLINE(unsigned) pj_hex_digit_to_val(unsigned c)
-{
- if (c <= '9')
- return (c-'0') & 0x0F;
- else if (c <= 'F')
- return (c-'A'+10) & 0x0F;
- else
- return (c-'a'+10) & 0x0F;
-}
-
-/** @} */
-
-PJ_END_DECL
-
-#endif /* __PJ_CTYPE_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_CTYPE_H__ +#define __PJ_CTYPE_H__ + +/** + * @file ctype.h + * @brief C type helper macros. + */ + +#include <pj/types.h> +#include <pj/compat/ctype.h> + +PJ_BEGIN_DECL + +/** + * @defgroup pj_ctype ctype - Character Type + * @ingroup PJ_MISC + * @{ + * + * This module contains several inline functions/macros for testing or + * manipulating character types. It is provided in PJLIB because PJLIB + * must not depend to LIBC. + */ + +/** + * Returns a non-zero value if either isalpha or isdigit is true for c. + * @param c The integer character to test. + * @return Non-zero value if either isalpha or isdigit is true for c. + */ +PJ_INLINE(int) pj_isalnum(int c) { return isalnum(c); } + +/** + * Returns a non-zero value if c is a particular representation of an + * alphabetic character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of an + * alphabetic character. + */ +PJ_INLINE(int) pj_isalpha(int c) { return isalpha(c); } + +/** + * Returns a non-zero value if c is a particular representation of an + * ASCII character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * an ASCII character. + */ +PJ_INLINE(int) pj_isascii(int c) { return isascii(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * a decimal-digit character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a decimal-digit character. + */ +PJ_INLINE(int) pj_isdigit(int c) { return isdigit(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * a space character (0x09 - 0x0D or 0x20). + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a space character (0x09 - 0x0D or 0x20). + */ +PJ_INLINE(int) pj_isspace(int c) { return isspace(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * a lowercase character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a lowercase character. + */ +PJ_INLINE(int) pj_islower(int c) { return islower(c); } + + +/** + * Returns a non-zero value if c is a particular representation of + * a uppercase character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a uppercase character. + */ +PJ_INLINE(int) pj_isupper(int c) { return isupper(c); } + +/** + * Returns a non-zero value if c is a either a space (' ') or horizontal + * tab ('\\t') character. + * @param c The integer character to test. + * @return Non-zero value if c is a either a space (' ') or horizontal + * tab ('\\t') character. + */ +PJ_INLINE(int) pj_isblank(int c) { return isblank(c); } + +/** + * Converts character to lowercase. + * @param c The integer character to convert. + * @return Lowercase character of c. + */ +PJ_INLINE(int) pj_tolower(int c) { return tolower(c); } + +/** + * Converts character to uppercase. + * @param c The integer character to convert. + * @return Uppercase character of c. + */ +PJ_INLINE(int) pj_toupper(int c) { return toupper(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * an hexadecimal digit character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * an hexadecimal digit character. + */ +PJ_INLINE(int) pj_isxdigit(int c){ return isxdigit(c); } + +/** + * Array of hex digits, in lowerspace. + */ +extern char pj_hex_digits[]; + +/** + * Convert a value to hex representation. + * @param value Integral value to convert. + * @param p Buffer to hold the hex representation, which must be + * at least two bytes length. + */ +PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p) +{ + *p++ = pj_hex_digits[ (value & 0xF0) >> 4 ]; + *p = pj_hex_digits[ (value & 0x0F) ]; +} + +/** + * Convert hex digit c to integral value. + * @param c The hex digit character. + * @return The integral value between 0 and 15. + */ +PJ_INLINE(unsigned) pj_hex_digit_to_val(unsigned c) +{ + if (c <= '9') + return (c-'0') & 0x0F; + else if (c <= 'F') + return (c-'A'+10) & 0x0F; + else + return (c-'a'+10) & 0x0F; +} + +/** @} */ + +PJ_END_DECL + +#endif /* __PJ_CTYPE_H__ */ + diff --git a/pjlib/include/pj/doxygen.h b/pjlib/include/pj/doxygen.h index 1b5f479e..f4bf8dbb 100644 --- a/pjlib/include/pj/doxygen.h +++ b/pjlib/include/pj/doxygen.h @@ -1,1019 +1,1019 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_DOXYGEN_H__
-#define __PJ_DOXYGEN_H__
-
-/**
- * @file doxygen.h
- * @brief Doxygen's mainpage.
- */
-
-/*////////////////////////////////////////////////////////////////////////// */
-/*
- INTRODUCTION PAGE
- */
-
-/**
- * @mainpage Welcome to PJLIB!
- *
- * @section intro_sec What is PJLIB
- *
- * PJLIB is a small foundation library written in C for making scalable
- * applications. Because of its small footprint, it can be used in embedded
- * applications (we hope so!), but yet the library is also aimed for
- * facilitating high performance protocol stacks.
- *
- * PJLIB is released under LGPL terms.
- *
- * @section download_sec Download
- *
- * PJLIB and all documentation can be downloaded from
- * http://www.pjproject.net.
- *
- *
- * @section how_to_use_sec About This Documentation
- *
- * This document is generated directly from PJLIB source file using
- * \a doxygen (http://www.doxygen.org). Doxygen is a great (and free!)
- * tools for generating such documentation.
- *
- * @subsection doc_ver_subsec Version
- *
- * This document corresponds to PJLIB version 0.3-pre2.
- *
- *
- * @subsection find_samples_subsec How to Read This Document
- *
- * This documentation is laid out more to be a reference guide instead
- * of tutorial, therefore first time users may find it difficult to
- * grasp PJLIB by reading this document alone.
- *
- * However, we've tried our best to make this document easy to follow.
- * For first time users, we would suggest that you follow these steps
- * when reading this documentation:
- *
- * - continue reading this introduction chapter. At the end of this
- * chapter, you'll find section called \ref pjlib_fundamentals_sec
- * which should guide you to understand basic things about PJLIB.
- *
- * - find information about specific features that you want to use
- * in PJLIB. Use the <b>Module Index</b> to find out about all
- * features in PJLIB (if you're browsing the HTML documentation,
- * click on the \a Module link on top of the page, or if you're
- * reading the PDF documentation, click on \a Module \a Documentation
- * on the navigation pane on the left).
- *
- * @subsection doc_organize_sec How To's
- *
- * Please find below links to specific tasks that you probably
- * want to do:
- *
- * - <b>How to Build PJLIB</b>
- *\n
- * Please refer to \ref pjlib_build_sys_pg page for more information.
- *
- * - <b>How to Use PJLIB in My Application</b>
- *\n
- * Please refer to \ref configure_app_sec for more information.
- *
- * - <b>How to Port PJLIB</b>
- *\n
- * Please refer to \ref porting_pjlib_pg page.
- *
- * - <b>Where to Read Samples Documentation</b>
- *\n
- * Most of the modules provide link to the corresponding sample file.
- * Alternatively, to get the list of all examples, you can click on
- * <b>Related Pages</b> on the top of HTML document or on
- * <b>PJLIB Page Documentation</b> on navigation pane of your PDF reader.
- *
- * - <b>How to Submit Code to PJLIB Project</b>
- *\n
- * Please read \ref pjlib_coding_convention_page before submitting
- * your code. Send your code as patch against current Subversion tree
- * to the appropriate mailing list.
- *
- *
- * @section features_sec Features
- *
- * @subsection open_source_feat It's Open Source!
- *
- * PJLIB is currently released on LGPL license. We may release PJLIB under
- * additional schemes in the future (such as GPL or MPL) to incorporate
- * linking with specific application, however, one thing for sure is
- * we will NEVER be able to make PJLIB a proprietary software.
- *
- * @subsection extreme_portable_feat Extreme Portability
- *
- * PJLIB is designed to be extremely portable. It can run on any kind
- * of processors (16-bit, 32-bit, or 64-bit, big or little endian, single
- * or multi-processors) and operating systems. Floating point or no
- * floating point. Multi-threading or not.
- * It can even run in environment where no ANSI LIBC is available.
- *
- * Currently PJLIB is being ported to:
- * - x86, Win32 (Win95/98/ME, NT/2000/XP/2003, mingw).
- * - x86, Linux (user mode and as <b>kernel module</b>(!)).
- * - alpha, Linux
- * And coming up:
- * - x86, eCos
- * - ultra-II, Solaris.
- * - powerpc, MacOS
- * - m68k, PalmOS.
- * - arm, PocketPC
- *
- * No other library is known to have this extreme portability!
- *
- * @subsection small_size_feat Small in Size
- *
- * One of the primary objectives is to have library that is small in size for
- * typical embedded applications. As a rough guidance, we aim to keep the
- * library size below 100KB for it to be considered as small.
- * As the result, most of the functionalities in the library can be tailored
- * to meet the requirements; user can enable/disable specific functionalities
- * to get the desired size/performance/functionality balance.
- *
- * For more info, please see @ref pj_config.
- *
- * @subsection no_dyn_mem No Dynamic Memory Allocations
- *
- * The central idea of PJLIB is that for applications to run as fast as it can,
- * it should not use \a malloc() at all, but instead should get the memory
- * from a preallocated storage pool. There are few things that can be
- * optimized with this approach:
- *
- * - \a alloc() is a O(1) operation.
- * - no mutex is used inside alloc(). It is assumed that synchronization
- * will be used in higher abstraction by application anyway.
- * - no \a free() is required. All chunks will be deleted when the pool is
- * destroyed.
- *
- * The performance gained on some systems can be as high as 10x speed up
- * against \a malloc() and \a free().
- *
- * For more information, see \ref PJ_POOL_GROUP
- *
- *
- * @subsection os_abstract_feat Operating System Abstraction
- *
- * PJLIB has abstractions for features that are normally not portable
- * across operating systems:
- * - @ref PJ_THREAD
- *\n
- * Portable thread manipulation.
- * - @ref PJ_TLS
- *\n
- * Storing data in thread's private data.
- * - @ref PJ_MUTEX
- *\n
- * Mutual exclusion protection.
- * - @ref PJ_SEM
- *\n
- * Semaphores.
- * - @ref PJ_ATOMIC
- *\n
- * Atomic variables and their operations.
- * - @ref PJ_CRIT_SEC
- *\n
- * Fast locking of critical sections.
- * - @ref PJ_LOCK
- *\n
- * High level abstraction for lock objects.
- * - @ref PJ_EVENT
- *\n
- * Event object.
- * - @ref PJ_TIME
- *\n
- * Portable time manipulation.
- * - @ref PJ_TIMESTAMP
- *\n
- * High resolution time value.
- * - etc.
- *
- *
- * @subsection ll_network_io_sec Low-Level Network I/O
- *
- * PJLIB has very portable abstraction and fairly complete set of API for
- * doing network I/O communications. At the lowest level, PJLIB provides:
- *
- * - @ref PJ_SOCK
- *\n
- * A highly portable socket abstraction, runs on all kind of
- * network APIs such as standard BSD socket, Windows socket, Linux
- * \b kernel socket, PalmOS networking API, etc.
- *
- * - @ref pj_addr_resolve
- *\n
- * Portable address resolution, which implements #pj_gethostbyname().
- *
- * - @ref PJ_SOCK_SELECT
- *\n
- * A portable \a select() like API (#pj_sock_select()) which can be
- * implemented with various back-end.
- *
- *
- * @subsection hl_network_io_sec High-Level Network I/O
- *
- * At higher abstraction, PJLIB provides @ref PJ_IOQUEUE,
- * which promotes creating high performance network
- * applications by managing asynchronous I/O. This is a passive framework
- * that utilizes the most effective way to manage asynchronous I/O
- * on a given platform, such as:
- * - IoCompletionPort on WinNT,
- * - on Linux it can use either /dev/epoll or aio.
- * - or to fall back to use @a select()
- *
- * At even a higher abstraction, PJLIB provides @ref PJ_EQUEUE, which
- * combines asynchronous I/O with timer management and thread management
- * to fasilitate creating trully high performance, event driven
- * application.
- *
- *
- * @subsection timer_mgmt_sec Timer Management
- *
- * A passive framework for managing timer, see @ref PJ_TIMER for more info.
- * There is also function to retrieve high resolution timestamp
- * from the system (see @ref PJ_TIMESTAMP).
- *
- *
- * @subsection data_struct_sec Various Data Structures
- *
- * Various data structures are provided in the library:
- *
- * - @ref PJ_PSTR
- * - @ref PJ_ARRAY
- * - @ref PJ_HASH
- * - @ref PJ_LIST
- * - @ref PJ_RBTREE
- *
- *
- * @subsection exception_sec Exception Construct
- *
- * A convenient TRY/CATCH like construct to propagate errors, which by
- * default are used by the @ref PJ_POOL_GROUP "memory pool" and
- * the lexical scanner in pjlib-util. The exception
- * construct can be used to write programs like below:
- *
- * <pre>
- * #define SYNTAX_ERROR 1
- *
- * PJ_TRY {
- * msg = NULL;
- * msg = parse_msg(buf, len);
- * }
- * PJ_CATCH ( SYNTAX_ERROR ) {
- * .. handle error ..
- * }
- * PJ_END;
- * </pre>
- *
- * Please see @ref PJ_EXCEPT for more information.
- *
- *
- * @subsection logging_sec Logging Facility
- *
- * PJLIB @ref PJ_LOG consists of macros to write logging information to
- * some output device. Some of the features of the logging facility:
- *
- * - the verbosity can be fine-tuned both at compile time (to control
- * the library size) or run-time (to control the verbosity of the
- * information).
- * - output device is configurable (e.g. stdout, printk, file, etc.)
- * - log decoration is configurable.
- *
- * See @ref PJ_LOG for more information.
- *
- *
- * @subsection guid_gen_sec Random and GUID Generation
- *
- * PJLIB provides facility to create random string
- * (#pj_create_random_string()) or globally unique identifier
- * (see @ref PJ_GUID).
- *
- *
- *
- * @section configure_app_sec Configuring Application to use PJLIB
- *
- * @subsection pjlib_compil_sec Building PJLIB
- *
- * Follow the instructions in \ref pjlib_build_sys_pg to build
- * PJLIB.
- *
- * @subsection pjlib_compil_app_sec Building Applications with PJLIB
- *
- * Use the following settings when building applications with PJLIB.
- *
- * @subsubsection compil_inc_dir_sec Include Search Path
- *
- * Add this to your include search path ($PJLIB is PJLIB root directory):
- * <pre>
- * $PJLIB/include
- * </pre>
- *
- * @subsubsection compil_inc_file_sec Include PJLIB Header
- *
- * To include all PJLIB headers:
- * \verbatim
- #include <pjlib.h>
- \endverbatim
- *
- * Alternatively, you can include individual PJLIB headers like this:
- * \verbatim
- #include <pj/log.h>
- #include <pj/os.h>
- \endverbatim
- *
- *
- * @subsubsection compil_lib_dir_sec Library Path
- *
- * Add this to your library search path:
- * <pre>
- * $PJLIB/lib
- * </pre>
- *
- * Then add the appropriate PJLIB library to your link specification. For
- * example, you would add \c libpj-i386-linux-gcc.a when you're building
- * applications in Linux.
- *
- *
- * @subsection pjlib_fundamentals_sec Principles in Using PJLIB
- *
- * Few things that you \b MUST do when using PJLIB, to make sure that
- * you create trully portable applications.
- *
- * @subsubsection call_pjlib_init_sec Call pj_init()
- *
- * Before you do anything else, call \c pj_init(). This would make sure that
- * PJLIB system is properly set up.
- *
- * @subsubsection no_ansi_subsec Do NOT Use ANSI C
- *
- * Contrary to popular teaching, ANSI C (and LIBC) is not the most portable
- * library in the world, nor it's the most ubiquitous. For example, LIBC
- * is not available in Linux kernel. Also normally LIBC will be excluded
- * from compilation of RTOSes to reduce size.
- *
- * So for maximum portability, do NOT use ANSI C. Do not even try to include
- * any other header files outside <include/pj>. Stick with the functionalities
- * provided by PJLIB.
- *
- *
- * @subsubsection string_rep_subsubsec Use pj_str_t instead of C Strings
- *
- * PJLIB uses pj_str_t instead of normal C strings. You SHOULD follow this
- * convention too. Remember, ANSI string-h is not always available. And
- * PJLIB string is faster!
- *
- * @subsubsection mem_alloc_subsubsec Use Pool for Memory Allocations
- *
- * You MUST NOT use \a malloc() or any other memory allocation functions.
- * Use PJLIB pool instead! It's faster and most portable.
- *
- * @subsection logging_subsubsec Use Logging for Text Display
- *
- * DO NOT use <stdio.h> for text output. Use PJLIB logging instead.
- *
- *
- * @section porting_pjlib_sec0 Porting PJLIB
- *
- * Please see \ref porting_pjlib_pg page on more information to port
- * PJLIB to new target.
- *
- * @section enjoy_sec Enjoy Using PJLIB!
- *
- * We hope that you find PJLIB usefull for your application. If you
- * have any questions, suggestions, critics, bug fixes, or anything
- * else, we would be happy to hear it.
- *
- * Enjoy using PJLIB!
- *
- * Benny Prijono < bennylp at pjproject dot net >
- */
-
-
-
-/*////////////////////////////////////////////////////////////////////////// */
-/*
- CODING CONVENTION
- */
-
-/**
- * @page pjlib_coding_convention_page Coding Convention
- *
- * Before you submit your code/patches to be included with PJLIB, you must
- * make sure that your code is compliant with PJLIB coding convention.
- * <b>This is very important!</b> Otherwise we would not accept your code.
- *
- * @section coding_conv_editor_sec Editor Settings
- *
- * The single most important thing in the whole coding convention is editor
- * settings. It's more important than the correctness of your code (bugs will
- * only crash the system, but incorrect tab size is mental!).
- *
- * Kindly set your editor as follows:
- * - tab size to \b 8.
- * - indentation to \b 4.
- *
- * With \c vi, you can do it with:
- * <pre>
- * :se ts=8
- * :se sts=4
- * </pre>
- *
- * You should replace tab with eight spaces.
- *
- * @section coding_conv_detail_sec Coding Style
- *
- * Coding style MUST strictly follow K&R style. The rest of coding style
- * must follow current style. You SHOULD be able to observe the style
- * currently used by PJLIB from PJLIB sources, and apply the style to your
- * code. If you're not able to do simple thing like to observe PJLIB
- * coding style from the sources, then logic dictates that your ability to
- * observe more difficult area in PJLIB such as memory allocation strategy,
- * concurrency, etc is questionable.
- *
- * @section coding_conv_comment_sec Commenting Your Code
- *
- * Public API (e.g. in header files) MUST have doxygen compliant comments.
- *
- */
-
-
-/*////////////////////////////////////////////////////////////////////////// */
-/*
- BUILDING AND INSTALLING PJLIB
- */
-
-
-
-/**
- * @page pjlib_build_sys_pg Building, and Installing PJLIB
- *
- * @section build_sys_install_sec Build and Installation
- *
- * @subsection build_sys_install_win32_sec Visual Studio
- *
- * The PJLIB Visual Studio workspace supports the building of PJLIB
- * for Win32 target. Although currently only the Visual Studio 6 Workspace is
- * actively maintained, developers with later version of Visual Studio
- * can easily imports VS6 workspace into their IDE.
- *
- * To start building PJLIB projects with Visual Studio 6 or later, open
- * the \a workspace file in the corresponding \b \c build directory. You have
- * several choices on which \a dsw file to open:
- \verbatim
- $PJPROJECT/build/pjproject.dsw
- $PJPROJECT/pjlib/build/pjlib.dsw
- $PJPROJECT/pjsip/build/pjsip.dsw
- ..etc
- \endverbatim
- *
- * The easiest way is to open <tt>pjproject.dsw</tt> file in \b \c $PJPROJECT/build
- * directory. However this will only build the required projects, not
- * the complete projects. For example, the PJLIB test and samples projects
- * are not included in this workspace. To build the complete projects, you must
- * open and build each \a dsw file in \c build directory in each
- * subprojects. For example, to open the complete PJLIB workspace, open
- * <tt>pjlib.dsw</tt> in <tt>$PJPROJECT/pjlib/build</tt> directory.
- *
- *
- * @subsubsection config_site_create_vc_sec Create config_site.h
- *
- * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt>
- * is supposed to contain configuration that is specific to your site/target.
- * This file is not part of PJLIB, so you must create it yourself. Normally
- * you just need to create a blank file.
- *
- * The reason why it's not included in PJLIB is so that you would not accidently
- * overwrite your site configuration.
- *
- * If you fail to do this, Visual C will complain with error like:
- *
- * <b>"fatal error C1083: Cannot open include file: 'pj/config_site.h': No such file
- * or directory"</b>.
- *
- * @subsubsection build_vc_subsubsec Build the Projects
- *
- * Just hit the build button!
- *
- *
- * @subsection build_sys_install_unix_sec Make System
- *
- * For other targets, PJLIB provides a rather comprehensive build system
- * that uses GNU \a make (and only GNU \a make will work).
- * Currently, the build system supports building * PJLIB for these targets:
- * - i386/Win32/mingw
- * - i386/Linux
- * - i386/Linux (kernel)
- * - alpha/linux
- * - sparc/SunOS
- * - etc..
- *
- *
- * @subsubsection build_req_sec Requirements
- *
- * In order to use the \c make based build system, you MUST have:
- *
- * - <b>GNU make</b>
- *\n
- * The Makefiles heavily utilize GNU make commands which most likely
- * are not available in other \c make system.
- * - <b>bash</b> shell is recommended.
- *\n
- * Specificly, there is a command <tt>"echo -n"</tt> which may not work
- * in other shells. This command is used when generating dependencies
- * (<tt>make dep</tt>) and it's located in
- * <tt>$PJPROJECT/build/rules.mak</tt>.
- * - <b>ar</b>, <b>ranlib</b> from GNU binutils
- *\n
- * In your system has different <tt>ar</tt> or <tt>ranlib</tt> (e.g. they
- * may have been installed as <tt>gar</tt> and <tt>granlib</tt>), then
- * either you create the relevant symbolic links, <b>or</b> modify
- * <tt>$PJPROJECT/build/cc-gcc.mak</tt> and rename <tt>ar</tt> and
- * <tt>ranlib</tt> to the appropriate names.
- * - <b>gcc</b> to generate dependency.
- *\n
- * Currently the build system uses <tt>"gcc -MM"</tt> to generate build
- * dependencies. If <tt>gcc</tt> is not desired to generate dependency,
- * then either you don't run <tt>make dep</tt>, <b>or</b> edit
- * <tt>$PJPROJECT/build/rules.mak</tt> to calculate dependency using
- * your prefered method. (And let me know when you do so so that I can
- * update the file. :) )
- *
- * @subsubsection build_overview_sec Building the Project
- *
- * Generally, steps required to build the PJLIB are:
- *
- \verbatim
- $ cd /home/user/pjproject # <-- go to $PJPROJECT
- $ vi build.mak # <-- set build target etc
- $ touch pjlib/include/pj/config_site.h
- $ cd pjlib/build # <-- go to projet's build dir
- $ make # <-- build the project
- \endverbatim
- *
- * For other project, \a cd to <tt>build</tt> directory in the project
- * and execute \a make from there.
- *
- * \note For Linux kernel target, there are additional steps required, which
- * will be explained in section \ref linux_kern_target_subsec.
- *
- * @subsubsection build_mak_sec Editing build.mak
- *
- * The \c build.mak file in \c $PJPROJECT root directory is used to
- * specify the build configuration. This file is expected to export
- * the following \a make variables:
- *
- * - <tt><b>MACHINE_NAME</b></tt>
- *\n
- * Target machine/processor, one of: <b>{ i386 | alpha | sparc }</b>.
- *
- * - <tt><b>OS_NAME</b></tt>
- *\n
- * Target operating system, one of: <b>{ win32 | linux |
- * linux-kernel | sunos }</b>.
- *
- * - <tt><b>CC_NAME</b></tt>
- *\n
- * Compiler name: <b>{ gcc | vc }</b>\n
- * (Note that support for Visual C (vc) compiler with the \c make system is
- * experimental, and it will only work when run inside a DOS shell
- * (i.e. <tt>"HOST_NAME=win32"</tt>)).
- *
- * - <tt><b>HOST_NAME</b></tt>
- *\n
- * Build host: <b>{ unix | mingw | win32 }</b>\n
- * (Note: win32 host means a DOS command prompt. Support for this type
- * of development host is experimental).
- *
- * These variables will cause the correct configuration file in
- * \c $PJPROJECT/build directory to be executed by \a make. For
- * example, specifying \c OS_NAME=linux will cause file \c os-linux.mak
- * in \c build directory to be executed. These files contain specific
- * configuration for the option that is selected.
- *
- * For Linux kernel target, you are also required to declare the following
- * variables in this file:
- * - \c KERNEL_DIR: full path of kernel source tree.
- * - \c KERNEL_ARCH: kernel ARCH options (e.g. "ARCH=um"), or leave blank
- * for default.
- * - \c PJPROJECT_DIR: full path of PJPROJECT source tree.
- *
- * Apart from these, there are also additional steps required to build
- * Linux kernel target, which will be explained in \ref linux_kern_target_subsec.
- *
- * @subsubsection build_dir_sec Files in "build" Directory
- *
- * The <tt>*.mak</tt> files in \c $PJPROJECT/build directory are used to specify
- * the configuration for the specified compiler, target machine target
- * operating system, and host options. These files will be executed
- * (included) by \a make during building process, depending on the values
- * specified in <b>$PJPROJECT/build.mak</b> file.
- *
- * Normally you don't need to edit these files, except when you're porting
- * PJLIB to new target.
- *
- * Below are the description of some files in this directory:
- *
- * - <tt>rules.mak</tt>: contains generic rules always included during make.
- * - <tt>cc-gcc.mak</tt>: rules when gcc is used for compiler.
- * - <tt>cc-vc.mak</tt>: rules when MSVC compiler is used.
- * - <tt>host-mingw.mak</tt>: rules for building in mingw host.
- * - <tt>host-unix.mak</tt>: rules for building in Unix/Posix host.
- * - <tt>host-win32.mak</tt>: rules for building in Win32 command console
- * (only valid when VC is used).
- * - <tt>m-i386.mak</tt>: rules when target machine is an i386 processor.
- * - <tt>m-m68k.mak</tt>: rules when target machine is an m68k processor.
- * - <tt>os-linux.mak</tt>: rules when target OS is Linux.
- * - <tt>os-linux-kernel.mak</tt>: rules when PJLIB is to be build as
- * part of Linux kernel.
- * - <tt>os-win32.mak</tt>: rules when target OS is Win32.
- *
- *
- * @subsubsection config_site_create_sec Create config_site.h
- *
- * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt>
- * is supposed to contain configuration that is specific to your site/target.
- * This file is not part of PJLIB, so you must create it yourself.
- *
- * The reason why it's not included in PJLIB is so that you would not accidently
- * overwrite your site configuration.
- *
- *
- * @subsubsection invoking_make_sec Invoking make
- *
- * Normally, \a make is invoked in \c build directory under each project.
- * For example, to build PJLIB, you would invoke \a make in
- * \c $PJPROJECT/pjlib/build directory like below:
- *
- \verbatim
- $ cd pjlib/build
- $ make
- \endverbatim
- *
- * Alternatively you may invoke <tt>make</tt> in <tt>$PJPROJECT</tt>
- * directory, to build all projects under that directory (e.g.
- * PJLIB, PJSIP, etc.).
- *
- *
- * @subsubsection linux_kern_target_subsec Linux Kernel Target
- *
- * \note
- * <b>BUILDING APPLICATIONS IN LINUX KERNEL MODE IS A VERY DANGEROUS BUSINESS.
- * YOU MAY CRASH THE WHOLE OF YOUR SYSTEM, CORRUPT YOUR HARDISK, ETC. PJLIB
- * KERNEL MODULES ARE STILL IN EXPERIMENTAL PHASE. DO NOT RUN IT IN PRODUCTION
- * SYSTEMS OR OTHER SYSTEMS WHERE RISK OF LOSS OF DATA IS NOT ACCEPTABLE.
- * YOU HAVE BEEN WARNED.</b>
- *
- * \note
- * <b>User Mode Linux (UML)</b> provides excellent way to experiment with Linux
- * kernel without risking the stability of the host system. See
- * http://user-mode-linux.sourceforge.net for details.
- *
- * \note
- * I only use <b>UML</b> to experiment with PJLIB kernel modules.
- * <b>I wouldn't be so foolish to use my host Linux machine to experiment
- * with this.</b>
- *
- * \note
- * You have been warned.
- *
- * For building PJLIB for Linux kernel target, there are additional steps required.
- * In general, the additional tasks are:
- * - Declare some more variables in <b><tt>build.mak</tt></b> file (this
- * has been explained in \ref build_mak_sec above).
- * - Perform these two small modifications in kernel source tree.
- *
- * There are two small modification need to be applied to the kernel tree.
- *
- * <b>1. Edit <tt>Makefile</tt> in kernel root source tree.</b>
- *
- * Add the following lines at the end of the <tt>Makefile</tt> in your
- * <tt>$KERNEL_SRC</tt> dir:
- \verbatim
-script:
- $(SCRIPT)
- \endverbatim
- *
- * \note Remember to replace spaces with <b>tab</b> in the Makefile.
- *
- * The modification above is needed to capture kernel's \c $CFLAGS and
- * \c $CFLAGS_MODULE which will be used for PJLIB's compilation.
- *
- * <b>2. Add Additional Exports.</b>
- *
- * We need the kernel to export some more symbols for our use. So we declare
- * the additional symbols to be exported in <tt>extra-exports.c</tt> file, and add
- * a this file to be compiled into the kernel:
- *
- * - Copy the file <tt>extra-exports.c</tt> from <tt>pjlib/src/pj</tt>
- * directory to <tt>$KERNEL_SRC/kernel/</tt> directory.
- * - Edit <tt>Makefile</tt> in that directory, and add this line
- * somewhere after the declaration of that variable:
- \verbatim
-obj-y += extra-exports.o
- \endverbatim
- *
- * To illustrate what have been done in your kernel source tree, below
- * is screenshot of my kernel source tree _after_ the modification.
- *
- \verbatim
-[root@vpc-linux linux-2.6.7]# pwd
-/usr/src/linux-2.6.7
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]# tail Makefile
-
-endif # skip-makefile
-
-FORCE:
-
-.PHONY: script
-
-script:
- $(SCRIPT)
-
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]# head kernel/extra-exports.c
-#include <linux/module.h>
-#include <linux/syscalls.h>
-
-EXPORT_SYMBOL(sys_select);
-
-EXPORT_SYMBOL(sys_epoll_create);
-EXPORT_SYMBOL(sys_epoll_ctl);
-EXPORT_SYMBOL(sys_epoll_wait);
-
-EXPORT_SYMBOL(sys_socket);
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]# head -15 kernel/Makefile
-#
-# Makefile for the linux kernel.
-#
-
-obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
- exit.o itimer.o time.o softirq.o resource.o \
- sysctl.o capability.o ptrace.o timer.o user.o \
- signal.o sys.o kmod.o workqueue.o pid.o \
- rcupdate.o intermodule.o extable.o params.o posix-timers.o \
- kthread.o
-
-obj-y += extra-exports.o
-
-obj-$(CONFIG_FUTEX) += futex.o
-obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
-[root@vpc-linux linux-2.6.7]#
-
- \endverbatim
- *
- * Then you must rebuild the kernel.
- * If you fail to do this, you won't be able to <b>insmod</b> pjlib.
- *
- * \note You will see a lots of warning messages during pjlib-test compilation.
- * The warning messages complain about unresolved symbols which are defined
- * in pjlib module. You can safely ignore these warnings. However, you can not
- * ignore warnings about non-pjlib unresolved symbols.
- *
- *
- * @subsection makefile_explained_sec Makefile Explained
- *
- * The \a Makefile for each project (e.g. PJLIB, PJSIP, etc) should be
- * very similar in the contents. The Makefile is located under \c build
- * directory in each project subdir.
- *
- * @subsubsection pjlib_makefile_subsec PJLIB Makefile.
- *
- * Below is PJLIB's Makefile:
- *
- * \include build/Makefile
- *
- * @subsubsection pjlib_os_makefile_subsec PJLIB os-linux.mak.
- *
- * Below is file <tt><b>os-linux.mak</b></tt> file in
- * <tt>$PJPROJECT/pjlib/build</tt> directory,
- * which is OS specific configuration file for Linux target that is specific
- * for PJLIB project. For \b global OS specific configuration, please see
- * <tt>$PJPROJECT/build/os-*.mak</tt>.
- *
- * \include build/os-linux.mak
- *
- */
-
-
-/*////////////////////////////////////////////////////////////////////////// */
-/*
- PORTING PJLIB
- */
-
-
-
-/**
- * @page porting_pjlib_pg Porting PJLIB
- *
- *
- * @section new_arch_sec Porting to New CPU Architecture
- *
- * Below is step-by-step guide to add support for new CPU architecture.
- * This sample is based on porting to Alpha architecture; however steps for
- * porting to other CPU architectures should be pretty similar.
- *
- * Also note that in this example, the operating system used is <b>Linux</b>.
- * Should you wish to add support for new operating system, then follow
- * the next section \ref porting_os_sec.
- *
- * Step-by-step guide to port to new CPU architecture:
- * - decide the name for the new architecture. In this case, we choose
- * <tt><b>alpha</b></tt>.
- * - edit file <tt>$PJPROJECT/build.mak</tt>, and add new section for
- * the new target:
- * <pre>
- * #
- * # Linux alpha, gcc
- * #
- * export MACHINE_NAME := <b>alpha</b>
- * export OS_NAME := linux
- * export CC_NAME := gcc
- * export HOST_NAME := unix
- * </pre>
- *
- * - create a new file <tt>$PJPROJECT/build/<b>m-alpha</b>.mak</tt>.
- * Alternatively create a copy from other file in this directory.
- * The contents of this file will look something like:
- * <pre>
- * export M_CFLAGS := $(CC_DEF)<b>PJ_M_ALPHA=1</b>
- * export M_CXXFLAGS :=
- * export M_LDFLAGS :=
- * export M_SOURCES :=
- * </pre>
- * - create a new file <tt>$PJPROJECT/pjlib/include/pj/compat/<b>m_alpha.h</b></tt>.
- * Alternatively create a copy from other header file in this directory.
- * The contents of this file will look something like:
- * <pre>
- * #define PJ_HAS_PENTIUM 0
- * #define PJ_IS_LITTLE_ENDIAN 1
- * #define PJ_IS_BIG_ENDIAN 0
- * </pre>
- * - edit <tt>pjlib/include/pj/<b>config.h</b></tt>. Add new processor
- * configuration in this header file, like follows:
- * <pre>
- * ...
- * #elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
- * # include <pj/compat/m_alpha.h>
- * ...
- * </pre>
- * - done. Build PJLIB with:
- * <pre>
- * $ cd $PJPROJECT/pjlib/build
- * $ make dep
- * $ make clean
- * $ make
- * </pre>
- *
- * @section porting_os_sec Porting to New Operating System Target
- *
- * This section will try to give you rough guideline on how to
- * port PJLIB to a new target. As a sample, we give the target a name tag,
- * for example <tt><b>xos</b></tt> (for X OS).
- *
- * @subsection new_compat_os_h_file_sec Create New Compat Header File
- *
- * You'll need to create a new header file
- * <b><tt>include/pj/compat/os_xos.h</tt></b>. You can copy as a
- * template other header file and edit it accordingly.
- *
- * @subsection modify_config_h_file_sec Modify config.h
- *
- * Then modify file <b><tt>include/pj/config.h</tt></b> to include
- * this file accordingly (e.g. when macro <tt><b>PJ_XOS</b></tt> is
- * defined):
- *
- \verbatim
- ...
- #elif defined(PJ_XOS)
- # include <pj/compat/os_xos.h>
- #else
- #...
- \endverbatim
- *
- * @subsection new_target_mak_file_sec Create New Global Make Config File
- *
- * Then you'll need to create global configuration file that
- * is specific for this OS, i.e. <tt><b>os-xos.mak</b></tt> in
- * <tt><b>$PJPROJECT/build</b></tt> directory.
- *
- * At very minimum, the file will normally need to define
- * <tt><b>PJ_XOS=1</b></tt> in the \c CFLAGS section:
- *
- \verbatim
-#
-# $PJPROJECT/build/os-xos.mak:
-#
-export OS_CFLAGS := $(CC_DEF)PJ_XOS=1
-export OS_CXXFLAGS :=
-export OS_LDFLAGS :=
-export OS_SOURCES :=
- \endverbatim
- *
- *
- * @subsection new_target_prj_mak_file_sec Create New Project's Make Config File
- *
- * Then you'll need to create xos-specific configuration file
- * for PJLIB. This file is also named <tt><b>os-xos.mak</b></tt>,
- * but its located in <tt><b>pjlib/build</b></tt> directory.
- * This file will specify source files that are specific to
- * this OS to be included in the build process.
- *
- * Below is a sample:
- \verbatim
-#
-# pjlib/build/os-xos.mak:
-# XOS specific configuration for PJLIB.
-#
-export PJLIB_OBJS += os_core_xos.o \
- os_error_unix.o \
- os_time_ansi.o
-export TEST_OBJS += main.o
-export TARGETS = pjlib pjlib-test
- \endverbatim
- *
- * @subsection new_target_src_sec Create and Edit Source Files
- *
- * You'll normally need to create at least these files:
- * - <tt><b>os_core_xos.c</b></tt>: core OS specific
- * functionality.
- * - <tt><b>os_timestamp_xos.c</b></tt>: how to get timestamp
- * in this OS.
- *
- * Depending on how things are done in your OS, you may need
- * to create these files:
- * - <tt><b>os_error_*.c</b></tt>: how to manipulate
- * OS error codes. Alternatively you may use existing
- * <tt>os_error_unix.c</tt> if the OS has \c errno and
- * \c strerror() function.
- * - <tt><b>ioqueue_*.c</b></tt>: if the OS has specific method
- * to perform asynchronous I/O. Alternatively you may
- * use existing <tt>ioqueue_select.c</tt> if the OS supports
- * \c select() function call.
- * - <tt><b>sock_*.c</b></tt>: if the OS has specific method
- * to perform socket communication. Alternatively you may
- * use existing <tt>sock_bsd.c</tt> if the OS supports
- * BSD socket API, and edit <tt>include/pj/compat/socket.h</tt>
- * file accordingly.
- *
- * You will also need to check various files in
- * <tt><b>include/pj/compat/*.h</b></tt>, to see if they're
- * compatible with your OS.
- *
- * @subsection new_target_build_file_sec Build The Project
- *
- * After basic building blocks have been created for the OS, then
- * the easiest way to see which parts need to be fixed is by building
- * the project and see the error messages.
- *
- * @subsection new_target_edit_vs_new_file_sec Editing Existing Files vs Creating New File
- *
- * When you encounter compatibility errors in PJLIB during porting,
- * you have three options on how to fix the error:
- * - edit the existing <tt>*.c</tt> file, and give it <tt>#ifdef</tt>
- * switch for the new OS, or
- * - edit <tt>include/pj/compat/*.h</tt> instead, or
- * - create a totally new file.
- *
- * Basicly there is no strict rule on which approach is the best
- * to use, however the following guidelines may be used:
- * - if the file is expected to be completely different than
- * any existing file, then perhaps you should create a completely
- * new file. For example, file <tt>os_core_xxx.c</tt> will
- * normally be different for each OS flavour.
- * - if the difference can be localized in <tt>include/compat</tt>
- * header file, and existing <tt>#ifdef</tt> switch is there,
- * then preferably you should edit this <tt>include/compat</tt>
- * header file.
- * - if the existing <tt>*.c</tt> file has <tt>#ifdef</tt> switch,
- * then you may add another <tt>#elif</tt> switch there. This
- * normally is used for behaviors that are not totally
- * different on each platform.
- * - other than that above, use your own judgement on whether
- * to edit the file or create new file etc.
- */
-
-#endif /* __PJ_DOXYGEN_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_DOXYGEN_H__ +#define __PJ_DOXYGEN_H__ + +/** + * @file doxygen.h + * @brief Doxygen's mainpage. + */ + +/*////////////////////////////////////////////////////////////////////////// */ +/* + INTRODUCTION PAGE + */ + +/** + * @mainpage Welcome to PJLIB! + * + * @section intro_sec What is PJLIB + * + * PJLIB is a small foundation library written in C for making scalable + * applications. Because of its small footprint, it can be used in embedded + * applications (we hope so!), but yet the library is also aimed for + * facilitating high performance protocol stacks. + * + * PJLIB is released under LGPL terms. + * + * @section download_sec Download + * + * PJLIB and all documentation can be downloaded from + * http://www.pjproject.net. + * + * + * @section how_to_use_sec About This Documentation + * + * This document is generated directly from PJLIB source file using + * \a doxygen (http://www.doxygen.org). Doxygen is a great (and free!) + * tools for generating such documentation. + * + * @subsection doc_ver_subsec Version + * + * This document corresponds to PJLIB version 0.3-pre2. + * + * + * @subsection find_samples_subsec How to Read This Document + * + * This documentation is laid out more to be a reference guide instead + * of tutorial, therefore first time users may find it difficult to + * grasp PJLIB by reading this document alone. + * + * However, we've tried our best to make this document easy to follow. + * For first time users, we would suggest that you follow these steps + * when reading this documentation: + * + * - continue reading this introduction chapter. At the end of this + * chapter, you'll find section called \ref pjlib_fundamentals_sec + * which should guide you to understand basic things about PJLIB. + * + * - find information about specific features that you want to use + * in PJLIB. Use the <b>Module Index</b> to find out about all + * features in PJLIB (if you're browsing the HTML documentation, + * click on the \a Module link on top of the page, or if you're + * reading the PDF documentation, click on \a Module \a Documentation + * on the navigation pane on the left). + * + * @subsection doc_organize_sec How To's + * + * Please find below links to specific tasks that you probably + * want to do: + * + * - <b>How to Build PJLIB</b> + *\n + * Please refer to \ref pjlib_build_sys_pg page for more information. + * + * - <b>How to Use PJLIB in My Application</b> + *\n + * Please refer to \ref configure_app_sec for more information. + * + * - <b>How to Port PJLIB</b> + *\n + * Please refer to \ref porting_pjlib_pg page. + * + * - <b>Where to Read Samples Documentation</b> + *\n + * Most of the modules provide link to the corresponding sample file. + * Alternatively, to get the list of all examples, you can click on + * <b>Related Pages</b> on the top of HTML document or on + * <b>PJLIB Page Documentation</b> on navigation pane of your PDF reader. + * + * - <b>How to Submit Code to PJLIB Project</b> + *\n + * Please read \ref pjlib_coding_convention_page before submitting + * your code. Send your code as patch against current Subversion tree + * to the appropriate mailing list. + * + * + * @section features_sec Features + * + * @subsection open_source_feat It's Open Source! + * + * PJLIB is currently released on LGPL license. We may release PJLIB under + * additional schemes in the future (such as GPL or MPL) to incorporate + * linking with specific application, however, one thing for sure is + * we will NEVER be able to make PJLIB a proprietary software. + * + * @subsection extreme_portable_feat Extreme Portability + * + * PJLIB is designed to be extremely portable. It can run on any kind + * of processors (16-bit, 32-bit, or 64-bit, big or little endian, single + * or multi-processors) and operating systems. Floating point or no + * floating point. Multi-threading or not. + * It can even run in environment where no ANSI LIBC is available. + * + * Currently PJLIB is being ported to: + * - x86, Win32 (Win95/98/ME, NT/2000/XP/2003, mingw). + * - x86, Linux (user mode and as <b>kernel module</b>(!)). + * - alpha, Linux + * And coming up: + * - x86, eCos + * - ultra-II, Solaris. + * - powerpc, MacOS + * - m68k, PalmOS. + * - arm, PocketPC + * + * No other library is known to have this extreme portability! + * + * @subsection small_size_feat Small in Size + * + * One of the primary objectives is to have library that is small in size for + * typical embedded applications. As a rough guidance, we aim to keep the + * library size below 100KB for it to be considered as small. + * As the result, most of the functionalities in the library can be tailored + * to meet the requirements; user can enable/disable specific functionalities + * to get the desired size/performance/functionality balance. + * + * For more info, please see @ref pj_config. + * + * @subsection no_dyn_mem No Dynamic Memory Allocations + * + * The central idea of PJLIB is that for applications to run as fast as it can, + * it should not use \a malloc() at all, but instead should get the memory + * from a preallocated storage pool. There are few things that can be + * optimized with this approach: + * + * - \a alloc() is a O(1) operation. + * - no mutex is used inside alloc(). It is assumed that synchronization + * will be used in higher abstraction by application anyway. + * - no \a free() is required. All chunks will be deleted when the pool is + * destroyed. + * + * The performance gained on some systems can be as high as 10x speed up + * against \a malloc() and \a free(). + * + * For more information, see \ref PJ_POOL_GROUP + * + * + * @subsection os_abstract_feat Operating System Abstraction + * + * PJLIB has abstractions for features that are normally not portable + * across operating systems: + * - @ref PJ_THREAD + *\n + * Portable thread manipulation. + * - @ref PJ_TLS + *\n + * Storing data in thread's private data. + * - @ref PJ_MUTEX + *\n + * Mutual exclusion protection. + * - @ref PJ_SEM + *\n + * Semaphores. + * - @ref PJ_ATOMIC + *\n + * Atomic variables and their operations. + * - @ref PJ_CRIT_SEC + *\n + * Fast locking of critical sections. + * - @ref PJ_LOCK + *\n + * High level abstraction for lock objects. + * - @ref PJ_EVENT + *\n + * Event object. + * - @ref PJ_TIME + *\n + * Portable time manipulation. + * - @ref PJ_TIMESTAMP + *\n + * High resolution time value. + * - etc. + * + * + * @subsection ll_network_io_sec Low-Level Network I/O + * + * PJLIB has very portable abstraction and fairly complete set of API for + * doing network I/O communications. At the lowest level, PJLIB provides: + * + * - @ref PJ_SOCK + *\n + * A highly portable socket abstraction, runs on all kind of + * network APIs such as standard BSD socket, Windows socket, Linux + * \b kernel socket, PalmOS networking API, etc. + * + * - @ref pj_addr_resolve + *\n + * Portable address resolution, which implements #pj_gethostbyname(). + * + * - @ref PJ_SOCK_SELECT + *\n + * A portable \a select() like API (#pj_sock_select()) which can be + * implemented with various back-end. + * + * + * @subsection hl_network_io_sec High-Level Network I/O + * + * At higher abstraction, PJLIB provides @ref PJ_IOQUEUE, + * which promotes creating high performance network + * applications by managing asynchronous I/O. This is a passive framework + * that utilizes the most effective way to manage asynchronous I/O + * on a given platform, such as: + * - IoCompletionPort on WinNT, + * - on Linux it can use either /dev/epoll or aio. + * - or to fall back to use @a select() + * + * At even a higher abstraction, PJLIB provides @ref PJ_EQUEUE, which + * combines asynchronous I/O with timer management and thread management + * to fasilitate creating trully high performance, event driven + * application. + * + * + * @subsection timer_mgmt_sec Timer Management + * + * A passive framework for managing timer, see @ref PJ_TIMER for more info. + * There is also function to retrieve high resolution timestamp + * from the system (see @ref PJ_TIMESTAMP). + * + * + * @subsection data_struct_sec Various Data Structures + * + * Various data structures are provided in the library: + * + * - @ref PJ_PSTR + * - @ref PJ_ARRAY + * - @ref PJ_HASH + * - @ref PJ_LIST + * - @ref PJ_RBTREE + * + * + * @subsection exception_sec Exception Construct + * + * A convenient TRY/CATCH like construct to propagate errors, which by + * default are used by the @ref PJ_POOL_GROUP "memory pool" and + * the lexical scanner in pjlib-util. The exception + * construct can be used to write programs like below: + * + * <pre> + * #define SYNTAX_ERROR 1 + * + * PJ_TRY { + * msg = NULL; + * msg = parse_msg(buf, len); + * } + * PJ_CATCH ( SYNTAX_ERROR ) { + * .. handle error .. + * } + * PJ_END; + * </pre> + * + * Please see @ref PJ_EXCEPT for more information. + * + * + * @subsection logging_sec Logging Facility + * + * PJLIB @ref PJ_LOG consists of macros to write logging information to + * some output device. Some of the features of the logging facility: + * + * - the verbosity can be fine-tuned both at compile time (to control + * the library size) or run-time (to control the verbosity of the + * information). + * - output device is configurable (e.g. stdout, printk, file, etc.) + * - log decoration is configurable. + * + * See @ref PJ_LOG for more information. + * + * + * @subsection guid_gen_sec Random and GUID Generation + * + * PJLIB provides facility to create random string + * (#pj_create_random_string()) or globally unique identifier + * (see @ref PJ_GUID). + * + * + * + * @section configure_app_sec Configuring Application to use PJLIB + * + * @subsection pjlib_compil_sec Building PJLIB + * + * Follow the instructions in \ref pjlib_build_sys_pg to build + * PJLIB. + * + * @subsection pjlib_compil_app_sec Building Applications with PJLIB + * + * Use the following settings when building applications with PJLIB. + * + * @subsubsection compil_inc_dir_sec Include Search Path + * + * Add this to your include search path ($PJLIB is PJLIB root directory): + * <pre> + * $PJLIB/include + * </pre> + * + * @subsubsection compil_inc_file_sec Include PJLIB Header + * + * To include all PJLIB headers: + * \verbatim + #include <pjlib.h> + \endverbatim + * + * Alternatively, you can include individual PJLIB headers like this: + * \verbatim + #include <pj/log.h> + #include <pj/os.h> + \endverbatim + * + * + * @subsubsection compil_lib_dir_sec Library Path + * + * Add this to your library search path: + * <pre> + * $PJLIB/lib + * </pre> + * + * Then add the appropriate PJLIB library to your link specification. For + * example, you would add \c libpj-i386-linux-gcc.a when you're building + * applications in Linux. + * + * + * @subsection pjlib_fundamentals_sec Principles in Using PJLIB + * + * Few things that you \b MUST do when using PJLIB, to make sure that + * you create trully portable applications. + * + * @subsubsection call_pjlib_init_sec Call pj_init() + * + * Before you do anything else, call \c pj_init(). This would make sure that + * PJLIB system is properly set up. + * + * @subsubsection no_ansi_subsec Do NOT Use ANSI C + * + * Contrary to popular teaching, ANSI C (and LIBC) is not the most portable + * library in the world, nor it's the most ubiquitous. For example, LIBC + * is not available in Linux kernel. Also normally LIBC will be excluded + * from compilation of RTOSes to reduce size. + * + * So for maximum portability, do NOT use ANSI C. Do not even try to include + * any other header files outside <include/pj>. Stick with the functionalities + * provided by PJLIB. + * + * + * @subsubsection string_rep_subsubsec Use pj_str_t instead of C Strings + * + * PJLIB uses pj_str_t instead of normal C strings. You SHOULD follow this + * convention too. Remember, ANSI string-h is not always available. And + * PJLIB string is faster! + * + * @subsubsection mem_alloc_subsubsec Use Pool for Memory Allocations + * + * You MUST NOT use \a malloc() or any other memory allocation functions. + * Use PJLIB pool instead! It's faster and most portable. + * + * @subsection logging_subsubsec Use Logging for Text Display + * + * DO NOT use <stdio.h> for text output. Use PJLIB logging instead. + * + * + * @section porting_pjlib_sec0 Porting PJLIB + * + * Please see \ref porting_pjlib_pg page on more information to port + * PJLIB to new target. + * + * @section enjoy_sec Enjoy Using PJLIB! + * + * We hope that you find PJLIB usefull for your application. If you + * have any questions, suggestions, critics, bug fixes, or anything + * else, we would be happy to hear it. + * + * Enjoy using PJLIB! + * + * Benny Prijono < bennylp at pjproject dot net > + */ + + + +/*////////////////////////////////////////////////////////////////////////// */ +/* + CODING CONVENTION + */ + +/** + * @page pjlib_coding_convention_page Coding Convention + * + * Before you submit your code/patches to be included with PJLIB, you must + * make sure that your code is compliant with PJLIB coding convention. + * <b>This is very important!</b> Otherwise we would not accept your code. + * + * @section coding_conv_editor_sec Editor Settings + * + * The single most important thing in the whole coding convention is editor + * settings. It's more important than the correctness of your code (bugs will + * only crash the system, but incorrect tab size is mental!). + * + * Kindly set your editor as follows: + * - tab size to \b 8. + * - indentation to \b 4. + * + * With \c vi, you can do it with: + * <pre> + * :se ts=8 + * :se sts=4 + * </pre> + * + * You should replace tab with eight spaces. + * + * @section coding_conv_detail_sec Coding Style + * + * Coding style MUST strictly follow K&R style. The rest of coding style + * must follow current style. You SHOULD be able to observe the style + * currently used by PJLIB from PJLIB sources, and apply the style to your + * code. If you're not able to do simple thing like to observe PJLIB + * coding style from the sources, then logic dictates that your ability to + * observe more difficult area in PJLIB such as memory allocation strategy, + * concurrency, etc is questionable. + * + * @section coding_conv_comment_sec Commenting Your Code + * + * Public API (e.g. in header files) MUST have doxygen compliant comments. + * + */ + + +/*////////////////////////////////////////////////////////////////////////// */ +/* + BUILDING AND INSTALLING PJLIB + */ + + + +/** + * @page pjlib_build_sys_pg Building, and Installing PJLIB + * + * @section build_sys_install_sec Build and Installation + * + * @subsection build_sys_install_win32_sec Visual Studio + * + * The PJLIB Visual Studio workspace supports the building of PJLIB + * for Win32 target. Although currently only the Visual Studio 6 Workspace is + * actively maintained, developers with later version of Visual Studio + * can easily imports VS6 workspace into their IDE. + * + * To start building PJLIB projects with Visual Studio 6 or later, open + * the \a workspace file in the corresponding \b \c build directory. You have + * several choices on which \a dsw file to open: + \verbatim + $PJPROJECT/build/pjproject.dsw + $PJPROJECT/pjlib/build/pjlib.dsw + $PJPROJECT/pjsip/build/pjsip.dsw + ..etc + \endverbatim + * + * The easiest way is to open <tt>pjproject.dsw</tt> file in \b \c $PJPROJECT/build + * directory. However this will only build the required projects, not + * the complete projects. For example, the PJLIB test and samples projects + * are not included in this workspace. To build the complete projects, you must + * open and build each \a dsw file in \c build directory in each + * subprojects. For example, to open the complete PJLIB workspace, open + * <tt>pjlib.dsw</tt> in <tt>$PJPROJECT/pjlib/build</tt> directory. + * + * + * @subsubsection config_site_create_vc_sec Create config_site.h + * + * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt> + * is supposed to contain configuration that is specific to your site/target. + * This file is not part of PJLIB, so you must create it yourself. Normally + * you just need to create a blank file. + * + * The reason why it's not included in PJLIB is so that you would not accidently + * overwrite your site configuration. + * + * If you fail to do this, Visual C will complain with error like: + * + * <b>"fatal error C1083: Cannot open include file: 'pj/config_site.h': No such file + * or directory"</b>. + * + * @subsubsection build_vc_subsubsec Build the Projects + * + * Just hit the build button! + * + * + * @subsection build_sys_install_unix_sec Make System + * + * For other targets, PJLIB provides a rather comprehensive build system + * that uses GNU \a make (and only GNU \a make will work). + * Currently, the build system supports building * PJLIB for these targets: + * - i386/Win32/mingw + * - i386/Linux + * - i386/Linux (kernel) + * - alpha/linux + * - sparc/SunOS + * - etc.. + * + * + * @subsubsection build_req_sec Requirements + * + * In order to use the \c make based build system, you MUST have: + * + * - <b>GNU make</b> + *\n + * The Makefiles heavily utilize GNU make commands which most likely + * are not available in other \c make system. + * - <b>bash</b> shell is recommended. + *\n + * Specificly, there is a command <tt>"echo -n"</tt> which may not work + * in other shells. This command is used when generating dependencies + * (<tt>make dep</tt>) and it's located in + * <tt>$PJPROJECT/build/rules.mak</tt>. + * - <b>ar</b>, <b>ranlib</b> from GNU binutils + *\n + * In your system has different <tt>ar</tt> or <tt>ranlib</tt> (e.g. they + * may have been installed as <tt>gar</tt> and <tt>granlib</tt>), then + * either you create the relevant symbolic links, <b>or</b> modify + * <tt>$PJPROJECT/build/cc-gcc.mak</tt> and rename <tt>ar</tt> and + * <tt>ranlib</tt> to the appropriate names. + * - <b>gcc</b> to generate dependency. + *\n + * Currently the build system uses <tt>"gcc -MM"</tt> to generate build + * dependencies. If <tt>gcc</tt> is not desired to generate dependency, + * then either you don't run <tt>make dep</tt>, <b>or</b> edit + * <tt>$PJPROJECT/build/rules.mak</tt> to calculate dependency using + * your prefered method. (And let me know when you do so so that I can + * update the file. :) ) + * + * @subsubsection build_overview_sec Building the Project + * + * Generally, steps required to build the PJLIB are: + * + \verbatim + $ cd /home/user/pjproject # <-- go to $PJPROJECT + $ vi build.mak # <-- set build target etc + $ touch pjlib/include/pj/config_site.h + $ cd pjlib/build # <-- go to projet's build dir + $ make # <-- build the project + \endverbatim + * + * For other project, \a cd to <tt>build</tt> directory in the project + * and execute \a make from there. + * + * \note For Linux kernel target, there are additional steps required, which + * will be explained in section \ref linux_kern_target_subsec. + * + * @subsubsection build_mak_sec Editing build.mak + * + * The \c build.mak file in \c $PJPROJECT root directory is used to + * specify the build configuration. This file is expected to export + * the following \a make variables: + * + * - <tt><b>MACHINE_NAME</b></tt> + *\n + * Target machine/processor, one of: <b>{ i386 | alpha | sparc }</b>. + * + * - <tt><b>OS_NAME</b></tt> + *\n + * Target operating system, one of: <b>{ win32 | linux | + * linux-kernel | sunos }</b>. + * + * - <tt><b>CC_NAME</b></tt> + *\n + * Compiler name: <b>{ gcc | vc }</b>\n + * (Note that support for Visual C (vc) compiler with the \c make system is + * experimental, and it will only work when run inside a DOS shell + * (i.e. <tt>"HOST_NAME=win32"</tt>)). + * + * - <tt><b>HOST_NAME</b></tt> + *\n + * Build host: <b>{ unix | mingw | win32 }</b>\n + * (Note: win32 host means a DOS command prompt. Support for this type + * of development host is experimental). + * + * These variables will cause the correct configuration file in + * \c $PJPROJECT/build directory to be executed by \a make. For + * example, specifying \c OS_NAME=linux will cause file \c os-linux.mak + * in \c build directory to be executed. These files contain specific + * configuration for the option that is selected. + * + * For Linux kernel target, you are also required to declare the following + * variables in this file: + * - \c KERNEL_DIR: full path of kernel source tree. + * - \c KERNEL_ARCH: kernel ARCH options (e.g. "ARCH=um"), or leave blank + * for default. + * - \c PJPROJECT_DIR: full path of PJPROJECT source tree. + * + * Apart from these, there are also additional steps required to build + * Linux kernel target, which will be explained in \ref linux_kern_target_subsec. + * + * @subsubsection build_dir_sec Files in "build" Directory + * + * The <tt>*.mak</tt> files in \c $PJPROJECT/build directory are used to specify + * the configuration for the specified compiler, target machine target + * operating system, and host options. These files will be executed + * (included) by \a make during building process, depending on the values + * specified in <b>$PJPROJECT/build.mak</b> file. + * + * Normally you don't need to edit these files, except when you're porting + * PJLIB to new target. + * + * Below are the description of some files in this directory: + * + * - <tt>rules.mak</tt>: contains generic rules always included during make. + * - <tt>cc-gcc.mak</tt>: rules when gcc is used for compiler. + * - <tt>cc-vc.mak</tt>: rules when MSVC compiler is used. + * - <tt>host-mingw.mak</tt>: rules for building in mingw host. + * - <tt>host-unix.mak</tt>: rules for building in Unix/Posix host. + * - <tt>host-win32.mak</tt>: rules for building in Win32 command console + * (only valid when VC is used). + * - <tt>m-i386.mak</tt>: rules when target machine is an i386 processor. + * - <tt>m-m68k.mak</tt>: rules when target machine is an m68k processor. + * - <tt>os-linux.mak</tt>: rules when target OS is Linux. + * - <tt>os-linux-kernel.mak</tt>: rules when PJLIB is to be build as + * part of Linux kernel. + * - <tt>os-win32.mak</tt>: rules when target OS is Win32. + * + * + * @subsubsection config_site_create_sec Create config_site.h + * + * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt> + * is supposed to contain configuration that is specific to your site/target. + * This file is not part of PJLIB, so you must create it yourself. + * + * The reason why it's not included in PJLIB is so that you would not accidently + * overwrite your site configuration. + * + * + * @subsubsection invoking_make_sec Invoking make + * + * Normally, \a make is invoked in \c build directory under each project. + * For example, to build PJLIB, you would invoke \a make in + * \c $PJPROJECT/pjlib/build directory like below: + * + \verbatim + $ cd pjlib/build + $ make + \endverbatim + * + * Alternatively you may invoke <tt>make</tt> in <tt>$PJPROJECT</tt> + * directory, to build all projects under that directory (e.g. + * PJLIB, PJSIP, etc.). + * + * + * @subsubsection linux_kern_target_subsec Linux Kernel Target + * + * \note + * <b>BUILDING APPLICATIONS IN LINUX KERNEL MODE IS A VERY DANGEROUS BUSINESS. + * YOU MAY CRASH THE WHOLE OF YOUR SYSTEM, CORRUPT YOUR HARDISK, ETC. PJLIB + * KERNEL MODULES ARE STILL IN EXPERIMENTAL PHASE. DO NOT RUN IT IN PRODUCTION + * SYSTEMS OR OTHER SYSTEMS WHERE RISK OF LOSS OF DATA IS NOT ACCEPTABLE. + * YOU HAVE BEEN WARNED.</b> + * + * \note + * <b>User Mode Linux (UML)</b> provides excellent way to experiment with Linux + * kernel without risking the stability of the host system. See + * http://user-mode-linux.sourceforge.net for details. + * + * \note + * I only use <b>UML</b> to experiment with PJLIB kernel modules. + * <b>I wouldn't be so foolish to use my host Linux machine to experiment + * with this.</b> + * + * \note + * You have been warned. + * + * For building PJLIB for Linux kernel target, there are additional steps required. + * In general, the additional tasks are: + * - Declare some more variables in <b><tt>build.mak</tt></b> file (this + * has been explained in \ref build_mak_sec above). + * - Perform these two small modifications in kernel source tree. + * + * There are two small modification need to be applied to the kernel tree. + * + * <b>1. Edit <tt>Makefile</tt> in kernel root source tree.</b> + * + * Add the following lines at the end of the <tt>Makefile</tt> in your + * <tt>$KERNEL_SRC</tt> dir: + \verbatim +script: + $(SCRIPT) + \endverbatim + * + * \note Remember to replace spaces with <b>tab</b> in the Makefile. + * + * The modification above is needed to capture kernel's \c $CFLAGS and + * \c $CFLAGS_MODULE which will be used for PJLIB's compilation. + * + * <b>2. Add Additional Exports.</b> + * + * We need the kernel to export some more symbols for our use. So we declare + * the additional symbols to be exported in <tt>extra-exports.c</tt> file, and add + * a this file to be compiled into the kernel: + * + * - Copy the file <tt>extra-exports.c</tt> from <tt>pjlib/src/pj</tt> + * directory to <tt>$KERNEL_SRC/kernel/</tt> directory. + * - Edit <tt>Makefile</tt> in that directory, and add this line + * somewhere after the declaration of that variable: + \verbatim +obj-y += extra-exports.o + \endverbatim + * + * To illustrate what have been done in your kernel source tree, below + * is screenshot of my kernel source tree _after_ the modification. + * + \verbatim +[root@vpc-linux linux-2.6.7]# pwd +/usr/src/linux-2.6.7 +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# tail Makefile + +endif # skip-makefile + +FORCE: + +.PHONY: script + +script: + $(SCRIPT) + +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# head kernel/extra-exports.c +#include <linux/module.h> +#include <linux/syscalls.h> + +EXPORT_SYMBOL(sys_select); + +EXPORT_SYMBOL(sys_epoll_create); +EXPORT_SYMBOL(sys_epoll_ctl); +EXPORT_SYMBOL(sys_epoll_wait); + +EXPORT_SYMBOL(sys_socket); +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# head -15 kernel/Makefile +# +# Makefile for the linux kernel. +# + +obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ + exit.o itimer.o time.o softirq.o resource.o \ + sysctl.o capability.o ptrace.o timer.o user.o \ + signal.o sys.o kmod.o workqueue.o pid.o \ + rcupdate.o intermodule.o extable.o params.o posix-timers.o \ + kthread.o + +obj-y += extra-exports.o + +obj-$(CONFIG_FUTEX) += futex.o +obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o +[root@vpc-linux linux-2.6.7]# + + \endverbatim + * + * Then you must rebuild the kernel. + * If you fail to do this, you won't be able to <b>insmod</b> pjlib. + * + * \note You will see a lots of warning messages during pjlib-test compilation. + * The warning messages complain about unresolved symbols which are defined + * in pjlib module. You can safely ignore these warnings. However, you can not + * ignore warnings about non-pjlib unresolved symbols. + * + * + * @subsection makefile_explained_sec Makefile Explained + * + * The \a Makefile for each project (e.g. PJLIB, PJSIP, etc) should be + * very similar in the contents. The Makefile is located under \c build + * directory in each project subdir. + * + * @subsubsection pjlib_makefile_subsec PJLIB Makefile. + * + * Below is PJLIB's Makefile: + * + * \include build/Makefile + * + * @subsubsection pjlib_os_makefile_subsec PJLIB os-linux.mak. + * + * Below is file <tt><b>os-linux.mak</b></tt> file in + * <tt>$PJPROJECT/pjlib/build</tt> directory, + * which is OS specific configuration file for Linux target that is specific + * for PJLIB project. For \b global OS specific configuration, please see + * <tt>$PJPROJECT/build/os-*.mak</tt>. + * + * \include build/os-linux.mak + * + */ + + +/*////////////////////////////////////////////////////////////////////////// */ +/* + PORTING PJLIB + */ + + + +/** + * @page porting_pjlib_pg Porting PJLIB + * + * + * @section new_arch_sec Porting to New CPU Architecture + * + * Below is step-by-step guide to add support for new CPU architecture. + * This sample is based on porting to Alpha architecture; however steps for + * porting to other CPU architectures should be pretty similar. + * + * Also note that in this example, the operating system used is <b>Linux</b>. + * Should you wish to add support for new operating system, then follow + * the next section \ref porting_os_sec. + * + * Step-by-step guide to port to new CPU architecture: + * - decide the name for the new architecture. In this case, we choose + * <tt><b>alpha</b></tt>. + * - edit file <tt>$PJPROJECT/build.mak</tt>, and add new section for + * the new target: + * <pre> + * # + * # Linux alpha, gcc + * # + * export MACHINE_NAME := <b>alpha</b> + * export OS_NAME := linux + * export CC_NAME := gcc + * export HOST_NAME := unix + * </pre> + * + * - create a new file <tt>$PJPROJECT/build/<b>m-alpha</b>.mak</tt>. + * Alternatively create a copy from other file in this directory. + * The contents of this file will look something like: + * <pre> + * export M_CFLAGS := $(CC_DEF)<b>PJ_M_ALPHA=1</b> + * export M_CXXFLAGS := + * export M_LDFLAGS := + * export M_SOURCES := + * </pre> + * - create a new file <tt>$PJPROJECT/pjlib/include/pj/compat/<b>m_alpha.h</b></tt>. + * Alternatively create a copy from other header file in this directory. + * The contents of this file will look something like: + * <pre> + * #define PJ_HAS_PENTIUM 0 + * #define PJ_IS_LITTLE_ENDIAN 1 + * #define PJ_IS_BIG_ENDIAN 0 + * </pre> + * - edit <tt>pjlib/include/pj/<b>config.h</b></tt>. Add new processor + * configuration in this header file, like follows: + * <pre> + * ... + * #elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0 + * # include <pj/compat/m_alpha.h> + * ... + * </pre> + * - done. Build PJLIB with: + * <pre> + * $ cd $PJPROJECT/pjlib/build + * $ make dep + * $ make clean + * $ make + * </pre> + * + * @section porting_os_sec Porting to New Operating System Target + * + * This section will try to give you rough guideline on how to + * port PJLIB to a new target. As a sample, we give the target a name tag, + * for example <tt><b>xos</b></tt> (for X OS). + * + * @subsection new_compat_os_h_file_sec Create New Compat Header File + * + * You'll need to create a new header file + * <b><tt>include/pj/compat/os_xos.h</tt></b>. You can copy as a + * template other header file and edit it accordingly. + * + * @subsection modify_config_h_file_sec Modify config.h + * + * Then modify file <b><tt>include/pj/config.h</tt></b> to include + * this file accordingly (e.g. when macro <tt><b>PJ_XOS</b></tt> is + * defined): + * + \verbatim + ... + #elif defined(PJ_XOS) + # include <pj/compat/os_xos.h> + #else + #... + \endverbatim + * + * @subsection new_target_mak_file_sec Create New Global Make Config File + * + * Then you'll need to create global configuration file that + * is specific for this OS, i.e. <tt><b>os-xos.mak</b></tt> in + * <tt><b>$PJPROJECT/build</b></tt> directory. + * + * At very minimum, the file will normally need to define + * <tt><b>PJ_XOS=1</b></tt> in the \c CFLAGS section: + * + \verbatim +# +# $PJPROJECT/build/os-xos.mak: +# +export OS_CFLAGS := $(CC_DEF)PJ_XOS=1 +export OS_CXXFLAGS := +export OS_LDFLAGS := +export OS_SOURCES := + \endverbatim + * + * + * @subsection new_target_prj_mak_file_sec Create New Project's Make Config File + * + * Then you'll need to create xos-specific configuration file + * for PJLIB. This file is also named <tt><b>os-xos.mak</b></tt>, + * but its located in <tt><b>pjlib/build</b></tt> directory. + * This file will specify source files that are specific to + * this OS to be included in the build process. + * + * Below is a sample: + \verbatim +# +# pjlib/build/os-xos.mak: +# XOS specific configuration for PJLIB. +# +export PJLIB_OBJS += os_core_xos.o \ + os_error_unix.o \ + os_time_ansi.o +export TEST_OBJS += main.o +export TARGETS = pjlib pjlib-test + \endverbatim + * + * @subsection new_target_src_sec Create and Edit Source Files + * + * You'll normally need to create at least these files: + * - <tt><b>os_core_xos.c</b></tt>: core OS specific + * functionality. + * - <tt><b>os_timestamp_xos.c</b></tt>: how to get timestamp + * in this OS. + * + * Depending on how things are done in your OS, you may need + * to create these files: + * - <tt><b>os_error_*.c</b></tt>: how to manipulate + * OS error codes. Alternatively you may use existing + * <tt>os_error_unix.c</tt> if the OS has \c errno and + * \c strerror() function. + * - <tt><b>ioqueue_*.c</b></tt>: if the OS has specific method + * to perform asynchronous I/O. Alternatively you may + * use existing <tt>ioqueue_select.c</tt> if the OS supports + * \c select() function call. + * - <tt><b>sock_*.c</b></tt>: if the OS has specific method + * to perform socket communication. Alternatively you may + * use existing <tt>sock_bsd.c</tt> if the OS supports + * BSD socket API, and edit <tt>include/pj/compat/socket.h</tt> + * file accordingly. + * + * You will also need to check various files in + * <tt><b>include/pj/compat/*.h</b></tt>, to see if they're + * compatible with your OS. + * + * @subsection new_target_build_file_sec Build The Project + * + * After basic building blocks have been created for the OS, then + * the easiest way to see which parts need to be fixed is by building + * the project and see the error messages. + * + * @subsection new_target_edit_vs_new_file_sec Editing Existing Files vs Creating New File + * + * When you encounter compatibility errors in PJLIB during porting, + * you have three options on how to fix the error: + * - edit the existing <tt>*.c</tt> file, and give it <tt>#ifdef</tt> + * switch for the new OS, or + * - edit <tt>include/pj/compat/*.h</tt> instead, or + * - create a totally new file. + * + * Basicly there is no strict rule on which approach is the best + * to use, however the following guidelines may be used: + * - if the file is expected to be completely different than + * any existing file, then perhaps you should create a completely + * new file. For example, file <tt>os_core_xxx.c</tt> will + * normally be different for each OS flavour. + * - if the difference can be localized in <tt>include/compat</tt> + * header file, and existing <tt>#ifdef</tt> switch is there, + * then preferably you should edit this <tt>include/compat</tt> + * header file. + * - if the existing <tt>*.c</tt> file has <tt>#ifdef</tt> switch, + * then you may add another <tt>#elif</tt> switch there. This + * normally is used for behaviors that are not totally + * different on each platform. + * - other than that above, use your own judgement on whether + * to edit the file or create new file etc. + */ + +#endif /* __PJ_DOXYGEN_H__ */ + diff --git a/pjlib/include/pj/equeue.h b/pjlib/include/pj/equeue.h index d0bf756c..77374bdb 100644 --- a/pjlib/include/pj/equeue.h +++ b/pjlib/include/pj/equeue.h @@ -1,336 +1,336 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_EQUEUE_H__
-#define __PJ_EQUEUE_H__
-
-/**
- * @file equeue.h
- * @brief Event Queue
- */
-#include <pj/types.h>
-
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_EQUEUE Event Queue
- * @brief Event Queue
- * @ingroup PJ_OS
- * @{
- */
-
-
-/**
- * Opaque data type for Event Queue.
- */
-typedef struct pj_equeue_t pj_equeue_t;
-
-/**
- * Opaque data type for Event Queue key.
- */
-typedef struct pj_equeue_key_t pj_equeue_key_t;
-
-
-/**
- * This structure describes the callbacks to be called when I/O operation
- * completes.
- */
-typedef struct pj_io_callback
-{
- /**
- * This callback is called when #pj_equeue_read, #pj_equeue_recv or
- * #pj_equeue_recvfrom completes.
- *
- * @param key The key.
- * @param bytes_read The size of data that has just been read.
- */
- void (*on_read_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_read);
-
- /**
- * This callback is called when #pj_equeue_write, #pj_equeue_send, or
- * #pj_equeue_sendto completes.
- *
- * @param key The key.
- * @param bytes_read The size of data that has just been written.
- */
- void (*on_write_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_sent);
-
- /**
- * This callback is called when #pj_equeue_accept completes.
- *
- * @param key The key.
- * @param status Zero if the operation completes successfully.
- */
- void (*on_accept_complete)(pj_equeue_key_t *key, int status);
-
- /**
- * This callback is called when #pj_equeue_connect completes.
- *
- * @param key The key.
- * @param status Zero if the operation completes successfully.
- */
- void (*on_connect_complete)(pj_equeue_key_t *key, int status);
-
-} pj_io_callback;
-
-/**
- * Event Queue options.
- */
-typedef struct pj_equeue_options
-{
- /** Maximum number of threads that are allowed to access Event Queue
- * simulteneously.
- */
- unsigned nb_threads;
-
- /** If non-zero, then no mutex protection will be used. */
- pj_bool_t no_lock;
-
- /** Interval of the busy loop inside the event queue.
- * The time resolution here determines the accuracy of the
- * timer in the Event Queue.
- */
- pj_time_val poll_interval;
-
-} pj_equeue_options;
-
-
-/**
- * Error value returned by I/O operations to indicate that the operation
- * can't complete immediately and will complete later.
- */
-#define PJ_EQUEUE_PENDING (-2)
-
-/**
- * Types of Event Queue operation.
- */
-typedef enum pj_equeue_op
-{
- PJ_EQUEUE_OP_NONE = 0, /**< No operation. */
- PJ_EQUEUE_OP_READ = 1, /**< read() operation. */
- PJ_EQUEUE_OP_RECV_FROM = 2, /**< recvfrom() operation. */
- PJ_EQUEUE_OP_WRITE = 4, /**< write() operation. */
- PJ_EQUEUE_OP_SEND_TO = 8, /**< sendto() operation. */
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- PJ_EQUEUE_OP_ACCEPT = 16, /**< accept() operation. */
- PJ_EQUEUE_OP_CONNECT = 32, /**< connect() operation. */
-#endif /* PJ_HAS_TCP */
-} pj_equeue_op;
-
-
-
-/**
- * Initialize Event Queue options with default values.
- *
- * @param options Event Queue options.
- */
-PJ_DECL(void) pj_equeue_options_init(pj_equeue_options *options);
-
-/**
- * Create a new Event Queue framework.
- *
- * @param pool The pool to allocate the event queue structure.
- * @param options Event queue options, or if NULL is given, then
- * default options will be used.
- * @param equeue Pointer to receive event queue structure.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_create( pj_pool_t *pool,
- const pj_equeue_options *options,
- pj_equeue_t **equeue);
-
-/**
- * Get the first instance of Event Queue, or NULL if no Event Queue
- * instance has been created in the application.
- *
- * @return The first instance of Event Queue created, or NULL.
- */
-PJ_DECL(pj_equeue_t*) pj_equeue_instance(void);
-
-/**
- * Destroy the Event Queue.
- *
- * @param equeue The Event Queue instance to be destroyed.
- */
-PJ_DECL(pj_status_t) pj_equeue_destroy( pj_equeue_t *equeue );
-
-/**
- * Customize the lock object that is used by the Event Queue.
- *
- * @param equeue The Event Queue instance.
- * @param lock The lock object.
- * @param auto_del If non-zero, the lock will be destroyed by
- * Event Queue.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_set_lock( pj_equeue_t *equeue,
- pj_lock_t *lock,
- pj_bool_t auto_del);
-
-/**
- * Associate an Event Queue key to particular handle. The key is also
- * associated with the callback and user data, which will be used by
- * the Event Queue framework when signalling event back to application.
- *
- * @param pool To allocate the resource for the specified handle, which
- * must be valid until the handle/key is unregistered
- * from Event Queue.
- * @param equeue The Event Queue.
- * @param hnd The OS handle to be registered, which can be a socket
- * descriptor (pj_sock_t), file descriptor, etc.
- * @param cb Callback to be called when I/O operation completes.
- * @param user_data User data to be associated with the key.
- * @param key Pointer to receive the key.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_register( pj_pool_t *pool,
- pj_equeue_t *equeue,
- pj_oshandle_t hnd,
- pj_io_callback *cb,
- void *user_data,
- pj_equeue_key_t **key);
-
-/**
- * Retrieve user data associated with a key.
- *
- * @param key The Event Queue key.
- *
- * @return User data associated with the key.
- */
-PJ_DECL(void*) pj_equeue_get_user_data( pj_equeue_key_t *key );
-
-
-/**
- * Unregister Event Queue key from the Event Queue.
- *
- * @param equeue The Event Queue.
- * @param key The key.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_unregister( pj_equeue_t *equeue,
- pj_equeue_key_t *key);
-
-/**
- * Instruct the Event Queue to read from the specified handle. This function
- * returns immediately (i.e. non-blocking) regardless whether some data has
- * been transfered. If the operation can't complete immediately, caller will
- * be notified about the completion when it calls pj_equeue_poll().
- *
- * @param key The key that uniquely identifies the handle.
- * @param buffer The buffer to hold the read data. The caller MUST make sure
- * that this buffer remain valid until the framework completes
- * reading the handle.
- * @param size The maximum size to be read.
- *
- * @return
- * - zero or positive number to indicate the number of bytes has been
- * read, and in this case the operation was not queued.
- * - (-1) on error, which in this case operation was not queued.
- * - PJ_EQUEUE_PENDING if the operation has been queued.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_read( pj_equeue_key_t *key,
- void *buffer,
- pj_size_t size);
-
-/**
- * Start recv() operation on the specified handle.
- *
- * @see ::pj_ioqueue_read
- */
-PJ_DECL(pj_ssize_t) pj_equeue_recv( pj_equeue_key_t *key,
- void *buf,
- pj_size_t size,
- unsigned flags);
-
-/**
- * Start recvfrom() operation on the specified handle.
- *
- * @see ::pj_equeue_read
- */
-PJ_DECL(pj_ssize_t) pj_equeue_recvfrom( pj_equeue_key_t *key,
- void *buf,
- pj_size_t size,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen );
-
-/**
- * Write.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_write( pj_equeue_key_t *key,
- const void *buf,
- pj_size_t size);
-
-/**
- * Send.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_send( pj_equeue_key_t *key,
- const void *buf,
- pj_size_t size,
- unsigned flags);
-
-/**
- * Sendto.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_sendto( pj_equeue_key_t *key,
- const void *buf,
- pj_size_t size,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addrlen);
-
-/**
- * Schedule timer.
- */
-PJ_DECL(pj_status_t) pj_equeue_schedule_timer( pj_equeue_t *equeue,
- const pj_time_val *timeout,
- pj_timer_entry *entry);
-
-/**
- * Cancel timer.
- */
-PJ_DECL(pj_status_t) pj_equeue_cancel_timer( pj_equeue_t *equeue,
- pj_timer_entry *entry);
-
-/**
- * Poll for events.
- */
-PJ_DECL(pj_status_t) pj_equeue_poll( pj_equeue_t *equeue,
- const pj_time_val *timeout );
-
-/**
- * Run.
- */
-PJ_DECL(pj_status_t) pj_equeue_run( pj_equeue_t *equeue );
-
-/**
- * Stop all running threads.
- */
-PJ_DECL(pj_status_t) pj_equeue_stop( pj_equeue_t *equeue );
-
-
-/** @} */
-
-PJ_END_DECL
-
-#endif /* __PJ_EQUEUE_H__ */
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_EQUEUE_H__ +#define __PJ_EQUEUE_H__ + +/** + * @file equeue.h + * @brief Event Queue + */ +#include <pj/types.h> + + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_EQUEUE Event Queue + * @brief Event Queue + * @ingroup PJ_OS + * @{ + */ + + +/** + * Opaque data type for Event Queue. + */ +typedef struct pj_equeue_t pj_equeue_t; + +/** + * Opaque data type for Event Queue key. + */ +typedef struct pj_equeue_key_t pj_equeue_key_t; + + +/** + * This structure describes the callbacks to be called when I/O operation + * completes. + */ +typedef struct pj_io_callback +{ + /** + * This callback is called when #pj_equeue_read, #pj_equeue_recv or + * #pj_equeue_recvfrom completes. + * + * @param key The key. + * @param bytes_read The size of data that has just been read. + */ + void (*on_read_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_read); + + /** + * This callback is called when #pj_equeue_write, #pj_equeue_send, or + * #pj_equeue_sendto completes. + * + * @param key The key. + * @param bytes_read The size of data that has just been written. + */ + void (*on_write_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_sent); + + /** + * This callback is called when #pj_equeue_accept completes. + * + * @param key The key. + * @param status Zero if the operation completes successfully. + */ + void (*on_accept_complete)(pj_equeue_key_t *key, int status); + + /** + * This callback is called when #pj_equeue_connect completes. + * + * @param key The key. + * @param status Zero if the operation completes successfully. + */ + void (*on_connect_complete)(pj_equeue_key_t *key, int status); + +} pj_io_callback; + +/** + * Event Queue options. + */ +typedef struct pj_equeue_options +{ + /** Maximum number of threads that are allowed to access Event Queue + * simulteneously. + */ + unsigned nb_threads; + + /** If non-zero, then no mutex protection will be used. */ + pj_bool_t no_lock; + + /** Interval of the busy loop inside the event queue. + * The time resolution here determines the accuracy of the + * timer in the Event Queue. + */ + pj_time_val poll_interval; + +} pj_equeue_options; + + +/** + * Error value returned by I/O operations to indicate that the operation + * can't complete immediately and will complete later. + */ +#define PJ_EQUEUE_PENDING (-2) + +/** + * Types of Event Queue operation. + */ +typedef enum pj_equeue_op +{ + PJ_EQUEUE_OP_NONE = 0, /**< No operation. */ + PJ_EQUEUE_OP_READ = 1, /**< read() operation. */ + PJ_EQUEUE_OP_RECV_FROM = 2, /**< recvfrom() operation. */ + PJ_EQUEUE_OP_WRITE = 4, /**< write() operation. */ + PJ_EQUEUE_OP_SEND_TO = 8, /**< sendto() operation. */ +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + PJ_EQUEUE_OP_ACCEPT = 16, /**< accept() operation. */ + PJ_EQUEUE_OP_CONNECT = 32, /**< connect() operation. */ +#endif /* PJ_HAS_TCP */ +} pj_equeue_op; + + + +/** + * Initialize Event Queue options with default values. + * + * @param options Event Queue options. + */ +PJ_DECL(void) pj_equeue_options_init(pj_equeue_options *options); + +/** + * Create a new Event Queue framework. + * + * @param pool The pool to allocate the event queue structure. + * @param options Event queue options, or if NULL is given, then + * default options will be used. + * @param equeue Pointer to receive event queue structure. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_create( pj_pool_t *pool, + const pj_equeue_options *options, + pj_equeue_t **equeue); + +/** + * Get the first instance of Event Queue, or NULL if no Event Queue + * instance has been created in the application. + * + * @return The first instance of Event Queue created, or NULL. + */ +PJ_DECL(pj_equeue_t*) pj_equeue_instance(void); + +/** + * Destroy the Event Queue. + * + * @param equeue The Event Queue instance to be destroyed. + */ +PJ_DECL(pj_status_t) pj_equeue_destroy( pj_equeue_t *equeue ); + +/** + * Customize the lock object that is used by the Event Queue. + * + * @param equeue The Event Queue instance. + * @param lock The lock object. + * @param auto_del If non-zero, the lock will be destroyed by + * Event Queue. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_set_lock( pj_equeue_t *equeue, + pj_lock_t *lock, + pj_bool_t auto_del); + +/** + * Associate an Event Queue key to particular handle. The key is also + * associated with the callback and user data, which will be used by + * the Event Queue framework when signalling event back to application. + * + * @param pool To allocate the resource for the specified handle, which + * must be valid until the handle/key is unregistered + * from Event Queue. + * @param equeue The Event Queue. + * @param hnd The OS handle to be registered, which can be a socket + * descriptor (pj_sock_t), file descriptor, etc. + * @param cb Callback to be called when I/O operation completes. + * @param user_data User data to be associated with the key. + * @param key Pointer to receive the key. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_register( pj_pool_t *pool, + pj_equeue_t *equeue, + pj_oshandle_t hnd, + pj_io_callback *cb, + void *user_data, + pj_equeue_key_t **key); + +/** + * Retrieve user data associated with a key. + * + * @param key The Event Queue key. + * + * @return User data associated with the key. + */ +PJ_DECL(void*) pj_equeue_get_user_data( pj_equeue_key_t *key ); + + +/** + * Unregister Event Queue key from the Event Queue. + * + * @param equeue The Event Queue. + * @param key The key. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_unregister( pj_equeue_t *equeue, + pj_equeue_key_t *key); + +/** + * Instruct the Event Queue to read from the specified handle. This function + * returns immediately (i.e. non-blocking) regardless whether some data has + * been transfered. If the operation can't complete immediately, caller will + * be notified about the completion when it calls pj_equeue_poll(). + * + * @param key The key that uniquely identifies the handle. + * @param buffer The buffer to hold the read data. The caller MUST make sure + * that this buffer remain valid until the framework completes + * reading the handle. + * @param size The maximum size to be read. + * + * @return + * - zero or positive number to indicate the number of bytes has been + * read, and in this case the operation was not queued. + * - (-1) on error, which in this case operation was not queued. + * - PJ_EQUEUE_PENDING if the operation has been queued. + */ +PJ_DECL(pj_ssize_t) pj_equeue_read( pj_equeue_key_t *key, + void *buffer, + pj_size_t size); + +/** + * Start recv() operation on the specified handle. + * + * @see ::pj_ioqueue_read + */ +PJ_DECL(pj_ssize_t) pj_equeue_recv( pj_equeue_key_t *key, + void *buf, + pj_size_t size, + unsigned flags); + +/** + * Start recvfrom() operation on the specified handle. + * + * @see ::pj_equeue_read + */ +PJ_DECL(pj_ssize_t) pj_equeue_recvfrom( pj_equeue_key_t *key, + void *buf, + pj_size_t size, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen ); + +/** + * Write. + */ +PJ_DECL(pj_ssize_t) pj_equeue_write( pj_equeue_key_t *key, + const void *buf, + pj_size_t size); + +/** + * Send. + */ +PJ_DECL(pj_ssize_t) pj_equeue_send( pj_equeue_key_t *key, + const void *buf, + pj_size_t size, + unsigned flags); + +/** + * Sendto. + */ +PJ_DECL(pj_ssize_t) pj_equeue_sendto( pj_equeue_key_t *key, + const void *buf, + pj_size_t size, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen); + +/** + * Schedule timer. + */ +PJ_DECL(pj_status_t) pj_equeue_schedule_timer( pj_equeue_t *equeue, + const pj_time_val *timeout, + pj_timer_entry *entry); + +/** + * Cancel timer. + */ +PJ_DECL(pj_status_t) pj_equeue_cancel_timer( pj_equeue_t *equeue, + pj_timer_entry *entry); + +/** + * Poll for events. + */ +PJ_DECL(pj_status_t) pj_equeue_poll( pj_equeue_t *equeue, + const pj_time_val *timeout ); + +/** + * Run. + */ +PJ_DECL(pj_status_t) pj_equeue_run( pj_equeue_t *equeue ); + +/** + * Stop all running threads. + */ +PJ_DECL(pj_status_t) pj_equeue_stop( pj_equeue_t *equeue ); + + +/** @} */ + +PJ_END_DECL + +#endif /* __PJ_EQUEUE_H__ */ diff --git a/pjlib/include/pj/errno.h b/pjlib/include/pj/errno.h index c45c3b8a..ffb72c19 100644 --- a/pjlib/include/pj/errno.h +++ b/pjlib/include/pj/errno.h @@ -1,279 +1,279 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_ERRNO_H__
-#define __PJ_ERRNO_H__
-
-/**
- * @file errno.h
- * @brief PJLIB Error Codes
- */
-#include <pj/types.h>
-#include <pj/compat/errno.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup pj_errno Error Codes
- * @ingroup PJ
- * @{
- *
- * In PJLIB, error/status codes from operating system are translated
- * into PJLIB error namespace, and stored in @a pj_status_t. All functions
- * that work with @a pj_status_t expect to get PJLIB error code instead
- * of native codes.
- *
- * @section pj_errno_retval Return Values
- *
- * All functions that returns @a pj_status_t returns @a PJ_SUCCESS if the
- * operation was completed successfully, or non-zero value to indicate
- * error. If the error came from operating system, then the native error
- * code is translated/folded into PJLIB's error namespace by using
- * #PJ_STATUS_FROM_OS() macro. The function will do this automatically
- * before returning the error to caller.
- *
- * @section pj_errno_errmsg Error Message
- *
- * To get the error message corresponding to a particular code, use function
- * #pj_strerror(). This function expects error code in PJLIB error namespace,
- * not the native error code. Application can pass the value from the
- * following sources to this function:
- * - #pj_get_os_error()
- * - #pj_get_netos_error()
- * - any return value from function returning @a pj_status_t.
- *
- * Application MUST NOT pass native error code (such as error code from
- * functions like GetLastError() or errno) to PJLIB functions expecting
- * @a pj_status_t.
- *
- */
-
-/**
- * Get the last platform error/status, folded into pj_status_t.
- * @return OS dependent error code, folded into pj_status_t.
- * @remark This function gets errno, or calls GetLastError() function and
- * convert the code into pj_status_t with PJ_STATUS_FROM_OS. Do
- * not call this for socket functions!
- * @see pj_get_netos_error()
- */
-PJ_DECL(pj_status_t) pj_get_os_error(void);
-
-/**
- * Set last error.
- * @param code pj_status_t
- */
-PJ_DECL(void) pj_set_os_error(pj_status_t code);
-
-/**
- * Get the last error from socket operations.
- * @return Last socket error, folded into pj_status_t.
- */
-PJ_DECL(pj_status_t) pj_get_netos_error(void);
-
-/**
- * Set error code.
- * @param code pj_status_t.
- */
-PJ_DECL(void) pj_set_netos_error(pj_status_t code);
-
-
-/**
- * Get the error message for the specified error code. The message
- * string will be NULL terminated.
- *
- * @param statcode The error code.
- * @param buf Buffer to hold the error message string.
- * @param bufsize Size of the buffer.
- *
- * @return The error message as NULL terminated string,
- * wrapped with pj_str_t.
- */
-PJ_DECL(pj_str_t) pj_strerror( pj_status_t statcode,
- char *buf, pj_size_t bufsize);
-
-
-/**
- * @hideinitializer
- * Return platform os error code folded into pj_status_t code. This is
- * the macro that is used throughout the library for all PJLIB's functions
- * that returns error from operating system. Application may override
- * this macro to reduce size (e.g. by defining it to always return
- * #PJ_EUNKNOWN).
- *
- * Note:
- * This macro MUST return non-zero value regardless whether zero is
- * passed as the argument. The reason is to protect logic error when
- * the operating system doesn't report error codes properly.
- *
- * @param os_code Platform OS error code. This value may be evaluated
- * more than once.
- * @return The platform os error code folded into pj_status_t.
- */
-#ifndef PJ_RETURN_OS_ERROR
-# define PJ_RETURN_OS_ERROR(os_code) (os_code ? \
- PJ_STATUS_FROM_OS(os_code) : -1)
-#endif
-
-
-/**
- * @hideinitializer
- * Fold a platform specific error into an pj_status_t code.
- *
- * @param e The platform os error code.
- * @return pj_status_t
- * @warning Macro implementation; the syserr argument may be evaluated
- * multiple times.
- */
-#define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS)
-
-/**
- * @hideinitializer
- * Fold an pj_status_t code back to the native platform defined error.
- *
- * @param e The pj_status_t folded platform os error code.
- * @return pj_os_err_type
- * @warning macro implementation; the statcode argument may be evaluated
- * multiple times. If the statcode was not created by
- * pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined.
- */
-#define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS)
-
-
-/**
- * @defgroup pj_errnum PJLIB's Own Error Codes
- * @ingroup pj_errno
- * @{
- */
-
-/**
- * @hideinitializer
- * Unknown error has been reported.
- */
-#define PJ_EUNKNOWN (PJ_ERRNO_START_STATUS + 1) /* 70001 */
-/**
- * @hideinitializer
- * The operation is pending and will be completed later.
- */
-#define PJ_EPENDING (PJ_ERRNO_START_STATUS + 2) /* 70002 */
-/**
- * @hideinitializer
- * Too many connecting sockets.
- */
-#define PJ_ETOOMANYCONN (PJ_ERRNO_START_STATUS + 3) /* 70003 */
-/**
- * @hideinitializer
- * Invalid argument.
- */
-#define PJ_EINVAL (PJ_ERRNO_START_STATUS + 4) /* 70004 */
-/**
- * @hideinitializer
- * Name too long (eg. hostname too long).
- */
-#define PJ_ENAMETOOLONG (PJ_ERRNO_START_STATUS + 5) /* 70005 */
-/**
- * @hideinitializer
- * Not found.
- */
-#define PJ_ENOTFOUND (PJ_ERRNO_START_STATUS + 6) /* 70006 */
-/**
- * @hideinitializer
- * Not enough memory.
- */
-#define PJ_ENOMEM (PJ_ERRNO_START_STATUS + 7) /* 70007 */
-/**
- * @hideinitializer
- * Bug detected!
- */
-#define PJ_EBUG (PJ_ERRNO_START_STATUS + 8) /* 70008 */
-/**
- * @hideinitializer
- * Operation timed out.
- */
-#define PJ_ETIMEDOUT (PJ_ERRNO_START_STATUS + 9) /* 70009 */
-/**
- * @hideinitializer
- * Too many objects.
- */
-#define PJ_ETOOMANY (PJ_ERRNO_START_STATUS + 10)/* 70010 */
-/**
- * @hideinitializer
- * Object is busy.
- */
-#define PJ_EBUSY (PJ_ERRNO_START_STATUS + 11)/* 70011 */
-/**
- * @hideinitializer
- * The specified option is not supported.
- */
-#define PJ_ENOTSUP (PJ_ERRNO_START_STATUS + 12)/* 70012 */
-/**
- * @hideinitializer
- * Invalid operation.
- */
-#define PJ_EINVALIDOP (PJ_ERRNO_START_STATUS + 13)/* 70013 */
-/**
- * @hideinitializer
- * Operation is cancelled.
- */
-#define PJ_ECANCELLED (PJ_ERRNO_START_STATUS + 14)/* 70014 */
-/**
- * @hideinitializer
- * Object already exists.
- */
-#define PJ_EEXISTS (PJ_ERRNO_START_STATUS + 15)/* 70015 */
-
-/** @} */ /* pj_errnum */
-
-/** @} */ /* pj_errno */
-
-
-/**
- * PJ_ERRNO_START is where PJLIB specific error values start.
- */
-#define PJ_ERRNO_START 20000
-
-/**
- * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of
- * the error/status range below.
- */
-#define PJ_ERRNO_SPACE_SIZE 50000
-
-/**
- * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start.
- * Effectively the error in this class would be 70000 - 119000.
- */
-#define PJ_ERRNO_START_STATUS (PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE)
-
-/**
- * PJ_ERRNO_START_SYS converts platform specific error codes into
- * pj_status_t values.
- * Effectively the error in this class would be 120000 - 169000.
- */
-#define PJ_ERRNO_START_SYS (PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE)
-
-/**
- * PJ_ERRNO_START_USER are reserved for applications that use error
- * codes along with PJLIB codes.
- * Effectively the error in this class would be 170000 - 219000.
- */
-#define PJ_ERRNO_START_USER (PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE)
-
-
-PJ_END_DECL
-
-#endif /* __PJ_ERRNO_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_ERRNO_H__ +#define __PJ_ERRNO_H__ + +/** + * @file errno.h + * @brief PJLIB Error Codes + */ +#include <pj/types.h> +#include <pj/compat/errno.h> + +PJ_BEGIN_DECL + +/** + * @defgroup pj_errno Error Codes + * @ingroup PJ + * @{ + * + * In PJLIB, error/status codes from operating system are translated + * into PJLIB error namespace, and stored in @a pj_status_t. All functions + * that work with @a pj_status_t expect to get PJLIB error code instead + * of native codes. + * + * @section pj_errno_retval Return Values + * + * All functions that returns @a pj_status_t returns @a PJ_SUCCESS if the + * operation was completed successfully, or non-zero value to indicate + * error. If the error came from operating system, then the native error + * code is translated/folded into PJLIB's error namespace by using + * #PJ_STATUS_FROM_OS() macro. The function will do this automatically + * before returning the error to caller. + * + * @section pj_errno_errmsg Error Message + * + * To get the error message corresponding to a particular code, use function + * #pj_strerror(). This function expects error code in PJLIB error namespace, + * not the native error code. Application can pass the value from the + * following sources to this function: + * - #pj_get_os_error() + * - #pj_get_netos_error() + * - any return value from function returning @a pj_status_t. + * + * Application MUST NOT pass native error code (such as error code from + * functions like GetLastError() or errno) to PJLIB functions expecting + * @a pj_status_t. + * + */ + +/** + * Get the last platform error/status, folded into pj_status_t. + * @return OS dependent error code, folded into pj_status_t. + * @remark This function gets errno, or calls GetLastError() function and + * convert the code into pj_status_t with PJ_STATUS_FROM_OS. Do + * not call this for socket functions! + * @see pj_get_netos_error() + */ +PJ_DECL(pj_status_t) pj_get_os_error(void); + +/** + * Set last error. + * @param code pj_status_t + */ +PJ_DECL(void) pj_set_os_error(pj_status_t code); + +/** + * Get the last error from socket operations. + * @return Last socket error, folded into pj_status_t. + */ +PJ_DECL(pj_status_t) pj_get_netos_error(void); + +/** + * Set error code. + * @param code pj_status_t. + */ +PJ_DECL(void) pj_set_netos_error(pj_status_t code); + + +/** + * Get the error message for the specified error code. The message + * string will be NULL terminated. + * + * @param statcode The error code. + * @param buf Buffer to hold the error message string. + * @param bufsize Size of the buffer. + * + * @return The error message as NULL terminated string, + * wrapped with pj_str_t. + */ +PJ_DECL(pj_str_t) pj_strerror( pj_status_t statcode, + char *buf, pj_size_t bufsize); + + +/** + * @hideinitializer + * Return platform os error code folded into pj_status_t code. This is + * the macro that is used throughout the library for all PJLIB's functions + * that returns error from operating system. Application may override + * this macro to reduce size (e.g. by defining it to always return + * #PJ_EUNKNOWN). + * + * Note: + * This macro MUST return non-zero value regardless whether zero is + * passed as the argument. The reason is to protect logic error when + * the operating system doesn't report error codes properly. + * + * @param os_code Platform OS error code. This value may be evaluated + * more than once. + * @return The platform os error code folded into pj_status_t. + */ +#ifndef PJ_RETURN_OS_ERROR +# define PJ_RETURN_OS_ERROR(os_code) (os_code ? \ + PJ_STATUS_FROM_OS(os_code) : -1) +#endif + + +/** + * @hideinitializer + * Fold a platform specific error into an pj_status_t code. + * + * @param e The platform os error code. + * @return pj_status_t + * @warning Macro implementation; the syserr argument may be evaluated + * multiple times. + */ +#define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS) + +/** + * @hideinitializer + * Fold an pj_status_t code back to the native platform defined error. + * + * @param e The pj_status_t folded platform os error code. + * @return pj_os_err_type + * @warning macro implementation; the statcode argument may be evaluated + * multiple times. If the statcode was not created by + * pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined. + */ +#define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS) + + +/** + * @defgroup pj_errnum PJLIB's Own Error Codes + * @ingroup pj_errno + * @{ + */ + +/** + * @hideinitializer + * Unknown error has been reported. + */ +#define PJ_EUNKNOWN (PJ_ERRNO_START_STATUS + 1) /* 70001 */ +/** + * @hideinitializer + * The operation is pending and will be completed later. + */ +#define PJ_EPENDING (PJ_ERRNO_START_STATUS + 2) /* 70002 */ +/** + * @hideinitializer + * Too many connecting sockets. + */ +#define PJ_ETOOMANYCONN (PJ_ERRNO_START_STATUS + 3) /* 70003 */ +/** + * @hideinitializer + * Invalid argument. + */ +#define PJ_EINVAL (PJ_ERRNO_START_STATUS + 4) /* 70004 */ +/** + * @hideinitializer + * Name too long (eg. hostname too long). + */ +#define PJ_ENAMETOOLONG (PJ_ERRNO_START_STATUS + 5) /* 70005 */ +/** + * @hideinitializer + * Not found. + */ +#define PJ_ENOTFOUND (PJ_ERRNO_START_STATUS + 6) /* 70006 */ +/** + * @hideinitializer + * Not enough memory. + */ +#define PJ_ENOMEM (PJ_ERRNO_START_STATUS + 7) /* 70007 */ +/** + * @hideinitializer + * Bug detected! + */ +#define PJ_EBUG (PJ_ERRNO_START_STATUS + 8) /* 70008 */ +/** + * @hideinitializer + * Operation timed out. + */ +#define PJ_ETIMEDOUT (PJ_ERRNO_START_STATUS + 9) /* 70009 */ +/** + * @hideinitializer + * Too many objects. + */ +#define PJ_ETOOMANY (PJ_ERRNO_START_STATUS + 10)/* 70010 */ +/** + * @hideinitializer + * Object is busy. + */ +#define PJ_EBUSY (PJ_ERRNO_START_STATUS + 11)/* 70011 */ +/** + * @hideinitializer + * The specified option is not supported. + */ +#define PJ_ENOTSUP (PJ_ERRNO_START_STATUS + 12)/* 70012 */ +/** + * @hideinitializer + * Invalid operation. + */ +#define PJ_EINVALIDOP (PJ_ERRNO_START_STATUS + 13)/* 70013 */ +/** + * @hideinitializer + * Operation is cancelled. + */ +#define PJ_ECANCELLED (PJ_ERRNO_START_STATUS + 14)/* 70014 */ +/** + * @hideinitializer + * Object already exists. + */ +#define PJ_EEXISTS (PJ_ERRNO_START_STATUS + 15)/* 70015 */ + +/** @} */ /* pj_errnum */ + +/** @} */ /* pj_errno */ + + +/** + * PJ_ERRNO_START is where PJLIB specific error values start. + */ +#define PJ_ERRNO_START 20000 + +/** + * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of + * the error/status range below. + */ +#define PJ_ERRNO_SPACE_SIZE 50000 + +/** + * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start. + * Effectively the error in this class would be 70000 - 119000. + */ +#define PJ_ERRNO_START_STATUS (PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) + +/** + * PJ_ERRNO_START_SYS converts platform specific error codes into + * pj_status_t values. + * Effectively the error in this class would be 120000 - 169000. + */ +#define PJ_ERRNO_START_SYS (PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) + +/** + * PJ_ERRNO_START_USER are reserved for applications that use error + * codes along with PJLIB codes. + * Effectively the error in this class would be 170000 - 219000. + */ +#define PJ_ERRNO_START_USER (PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) + + +PJ_END_DECL + +#endif /* __PJ_ERRNO_H__ */ + diff --git a/pjlib/include/pj/except.h b/pjlib/include/pj/except.h index a0143b9c..301242a4 100644 --- a/pjlib/include/pj/except.h +++ b/pjlib/include/pj/except.h @@ -1,284 +1,284 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_EXCEPTION_H__
-#define __PJ_EXCEPTION_H__
-
-/**
- * @file except.h
- * @brief Exception Handling in C.
- */
-
-#include <pj/types.h>
-#include <pj/compat/setjmp.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_EXCEPT Exception Handling
- * @ingroup PJ_MISC
- * @{
- *
- * \section pj_except_sample_sec Quick Example
- *
- * For the impatient, take a look at some examples:
- * - @ref page_pjlib_samples_except_c
- * - @ref page_pjlib_exception_test
- *
- * \section pj_except_except Exception Handling
- *
- * This module provides exception handling syntactically similar to C++ in
- * C language. The underlying mechanism use setjmp() and longjmp(), and since
- * these constructs are ANSI standard, the mechanism here should be available
- * on most platforms/compilers which are ANSI compliant.
- *
- * If ANSI libc is not available, then setjmp()/longjmp() implementation will
- * be provided. See <pj/compat/setjmp.h> for compatibility.
- *
- * The exception handling mechanism is completely thread safe, so the exception
- * thrown by one thread will not interfere with other thread.
- *
- * CAVEATS:
- * - unlike C++ exception, the scheme here won't call destructors of local
- * objects if exception is thrown. Care must be taken when a function
- * hold some resorce such as pool or mutex etc.
- * - You CAN NOT make nested exception in one single function without using
- * a nested PJ_USE_EXCEPTION.
- * - Exceptions will always be caught by the first handle (unlike C++ where
- * exception is only caught if the type matches.
- *
- * The exception handling constructs are similar to C++. The blocks will be
- * constructed similar to the following sample:
- *
- * \verbatim
- #define NO_MEMORY 1
- #define SYNTAX_ERROR 2
-
- int main()
- {
- PJ_USE_EXCEPTION; // declare local exception stack.
-
- PJ_TRY {
- ...// do something..
- }
- PJ_CATCH(NO_MEMORY) {
- ... // handle exception 1
- }
- PJ_CATCH(SYNTAX_ERROR) {
- ... // handle exception 2
- }
- PJ_DEFAULT {
- ... // handle other exceptions.
- }
- PJ_END;
- }
- \endverbatim
- *
- * The above sample uses hard coded exception ID. It is @b strongly
- * recommended that applications request a unique exception ID instead
- * of hard coded value like above.
- *
- * \section pj_except_reg Exception ID Allocation
- *
- * To ensure that exception ID (number) are used consistently and to
- * prevent ID collisions in an application, it is strongly suggested that
- * applications allocate an exception ID for each possible exception
- * type. As a bonus of this process, the application can identify
- * the name of the exception when the particular exception is thrown.
- *
- * Exception ID management are performed with the following APIs:
- * - #pj_exception_id_alloc().
- * - #pj_exception_id_free().
- * - #pj_exception_id_name().
- *
- *
- * PJLIB itself automatically allocates one exception id, i.e.
- * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
- * ID is raised by default pool policy when it fails to allocate memory.
- *
- * \section PJ_EX_KEYWORDS Keywords
- *
- * \subsection PJ_THROW PJ_THROW(expression)
- * Throw an exception. The expression thrown is an integer as the result of
- * the \a expression. This keyword can be specified anywhere within the
- * program.
- *
- * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
- * Specify this in the variable definition section of the function block
- * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
- * block.
- * Actually, this is just a macro to declare local variable which is used to
- * push the exception state to the exception stack.
- *
- * \subsection PJ_TRY PJ_TRY
- * The \a PJ_TRY keyword is typically followed by a block. If an exception is
- * thrown in this block, then the execution will resume to the \a PJ_CATCH
- * handler.
- *
- * \subsection PJ_CATCH PJ_CATCH(expression)
- * The \a PJ_CATCH is normally followed by a block. This block will be executed
- * if the exception being thrown is equal to the expression specified in the
- * \a PJ_CATCH.
- *
- * \subsection PJ_DEFAULT PJ_DEFAULT
- * The \a PJ_DEFAULT keyword is normally followed by a block. This block will
- * be executed if the exception being thrown doesn't match any of the \a
- * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the
- * last block of the handlers.
- *
- * \subsection PJ_END PJ_END
- * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
- *
- * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
- * Get the last exception thrown. This macro is normally called inside the
- * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where
- * the \a PJ_USE_EXCEPTION definition is in scope.
- *
- *
- * \section pj_except_examples_sec Examples
- *
- * For some examples on how to use the exception construct, please see:
- * - @ref page_pjlib_samples_except_c
- * - @ref page_pjlib_exception_test
- */
-
-/**
- * Allocate a unique exception id.
- * Applications don't have to allocate a unique exception ID before using
- * the exception construct. However, by doing so it ensures that there is
- * no collisions of exception ID.
- *
- * As a bonus, when exception number is acquired through this function,
- * the library can assign name to the exception (only if
- * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
- * exception name when it catches an exception.
- *
- * @param name Name to be associated with the exception ID.
- * @param id Pointer to receive the ID.
- *
- * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
- * is running out out ids.
- */
-PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
- pj_exception_id_t *id);
-
-/**
- * Free an exception id.
- *
- * @param id The exception ID.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
-
-/**
- * Retrieve name associated with the exception id.
- *
- * @param id The exception ID.
- *
- * @return The name associated with the specified ID.
- */
-PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
-
-
-/** @} */
-
-/**
- * This structure (which should be invisible to user) manages the TRY handler
- * stack.
- */
-struct pj_exception_state_t
-{
- struct pj_exception_state_t *prev; /**< Previous state in the list. */
- pj_jmp_buf state; /**< jmp_buf. */
-};
-
-/**
- * Throw exception.
- * @param id Exception Id.
- */
-PJ_DECL_NO_RETURN(void)
-pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
-
-/**
- * Push exception handler.
- */
-PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
-
-/**
- * Pop exception handler.
- */
-PJ_DECL(void) pj_pop_exception_handler_(void);
-
-/**
- * Declare that the function will use exception.
- * @hideinitializer
- */
-#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
-
-/**
- * Start exception specification block.
- * @hideinitializer
- */
-#define PJ_TRY if (1) { \
- pj_push_exception_handler_(&pj_x_except__); \
- pj_x_code__ = pj_setjmp(pj_x_except__.state); \
- if (pj_x_code__ == 0)
-/**
- * Catch the specified exception Id.
- * @param id The exception number to catch.
- * @hideinitializer
- */
-#define PJ_CATCH(id) else if (pj_x_code__ == (id))
-
-/**
- * Catch any exception number.
- * @hideinitializer
- */
-#define PJ_DEFAULT else
-
-/**
- * End of exception specification block.
- * @hideinitializer
- */
-#define PJ_END pj_pop_exception_handler_(); \
- } else {}
-
-/**
- * Throw exception.
- * @param exception_id The exception number.
- * @hideinitializer
- */
-#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
-
-/**
- * Get current exception.
- * @return Current exception code.
- * @hideinitializer
- */
-#define PJ_GET_EXCEPTION() (pj_x_code__)
-
-PJ_END_DECL
-
-
-
-#endif /* __PJ_EXCEPTION_H__ */
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_EXCEPTION_H__ +#define __PJ_EXCEPTION_H__ + +/** + * @file except.h + * @brief Exception Handling in C. + */ + +#include <pj/types.h> +#include <pj/compat/setjmp.h> + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_EXCEPT Exception Handling + * @ingroup PJ_MISC + * @{ + * + * \section pj_except_sample_sec Quick Example + * + * For the impatient, take a look at some examples: + * - @ref page_pjlib_samples_except_c + * - @ref page_pjlib_exception_test + * + * \section pj_except_except Exception Handling + * + * This module provides exception handling syntactically similar to C++ in + * C language. The underlying mechanism use setjmp() and longjmp(), and since + * these constructs are ANSI standard, the mechanism here should be available + * on most platforms/compilers which are ANSI compliant. + * + * If ANSI libc is not available, then setjmp()/longjmp() implementation will + * be provided. See <pj/compat/setjmp.h> for compatibility. + * + * The exception handling mechanism is completely thread safe, so the exception + * thrown by one thread will not interfere with other thread. + * + * CAVEATS: + * - unlike C++ exception, the scheme here won't call destructors of local + * objects if exception is thrown. Care must be taken when a function + * hold some resorce such as pool or mutex etc. + * - You CAN NOT make nested exception in one single function without using + * a nested PJ_USE_EXCEPTION. + * - Exceptions will always be caught by the first handle (unlike C++ where + * exception is only caught if the type matches. + * + * The exception handling constructs are similar to C++. The blocks will be + * constructed similar to the following sample: + * + * \verbatim + #define NO_MEMORY 1 + #define SYNTAX_ERROR 2 + + int main() + { + PJ_USE_EXCEPTION; // declare local exception stack. + + PJ_TRY { + ...// do something.. + } + PJ_CATCH(NO_MEMORY) { + ... // handle exception 1 + } + PJ_CATCH(SYNTAX_ERROR) { + ... // handle exception 2 + } + PJ_DEFAULT { + ... // handle other exceptions. + } + PJ_END; + } + \endverbatim + * + * The above sample uses hard coded exception ID. It is @b strongly + * recommended that applications request a unique exception ID instead + * of hard coded value like above. + * + * \section pj_except_reg Exception ID Allocation + * + * To ensure that exception ID (number) are used consistently and to + * prevent ID collisions in an application, it is strongly suggested that + * applications allocate an exception ID for each possible exception + * type. As a bonus of this process, the application can identify + * the name of the exception when the particular exception is thrown. + * + * Exception ID management are performed with the following APIs: + * - #pj_exception_id_alloc(). + * - #pj_exception_id_free(). + * - #pj_exception_id_name(). + * + * + * PJLIB itself automatically allocates one exception id, i.e. + * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception + * ID is raised by default pool policy when it fails to allocate memory. + * + * \section PJ_EX_KEYWORDS Keywords + * + * \subsection PJ_THROW PJ_THROW(expression) + * Throw an exception. The expression thrown is an integer as the result of + * the \a expression. This keyword can be specified anywhere within the + * program. + * + * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION + * Specify this in the variable definition section of the function block + * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception + * block. + * Actually, this is just a macro to declare local variable which is used to + * push the exception state to the exception stack. + * + * \subsection PJ_TRY PJ_TRY + * The \a PJ_TRY keyword is typically followed by a block. If an exception is + * thrown in this block, then the execution will resume to the \a PJ_CATCH + * handler. + * + * \subsection PJ_CATCH PJ_CATCH(expression) + * The \a PJ_CATCH is normally followed by a block. This block will be executed + * if the exception being thrown is equal to the expression specified in the + * \a PJ_CATCH. + * + * \subsection PJ_DEFAULT PJ_DEFAULT + * The \a PJ_DEFAULT keyword is normally followed by a block. This block will + * be executed if the exception being thrown doesn't match any of the \a + * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the + * last block of the handlers. + * + * \subsection PJ_END PJ_END + * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks. + * + * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void) + * Get the last exception thrown. This macro is normally called inside the + * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where + * the \a PJ_USE_EXCEPTION definition is in scope. + * + * + * \section pj_except_examples_sec Examples + * + * For some examples on how to use the exception construct, please see: + * - @ref page_pjlib_samples_except_c + * - @ref page_pjlib_exception_test + */ + +/** + * Allocate a unique exception id. + * Applications don't have to allocate a unique exception ID before using + * the exception construct. However, by doing so it ensures that there is + * no collisions of exception ID. + * + * As a bonus, when exception number is acquired through this function, + * the library can assign name to the exception (only if + * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the + * exception name when it catches an exception. + * + * @param name Name to be associated with the exception ID. + * @param id Pointer to receive the ID. + * + * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library + * is running out out ids. + */ +PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name, + pj_exception_id_t *id); + +/** + * Free an exception id. + * + * @param id The exception ID. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id); + +/** + * Retrieve name associated with the exception id. + * + * @param id The exception ID. + * + * @return The name associated with the specified ID. + */ +PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id); + + +/** @} */ + +/** + * This structure (which should be invisible to user) manages the TRY handler + * stack. + */ +struct pj_exception_state_t +{ + struct pj_exception_state_t *prev; /**< Previous state in the list. */ + pj_jmp_buf state; /**< jmp_buf. */ +}; + +/** + * Throw exception. + * @param id Exception Id. + */ +PJ_DECL_NO_RETURN(void) +pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN; + +/** + * Push exception handler. + */ +PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec); + +/** + * Pop exception handler. + */ +PJ_DECL(void) pj_pop_exception_handler_(void); + +/** + * Declare that the function will use exception. + * @hideinitializer + */ +#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__ + +/** + * Start exception specification block. + * @hideinitializer + */ +#define PJ_TRY if (1) { \ + pj_push_exception_handler_(&pj_x_except__); \ + pj_x_code__ = pj_setjmp(pj_x_except__.state); \ + if (pj_x_code__ == 0) +/** + * Catch the specified exception Id. + * @param id The exception number to catch. + * @hideinitializer + */ +#define PJ_CATCH(id) else if (pj_x_code__ == (id)) + +/** + * Catch any exception number. + * @hideinitializer + */ +#define PJ_DEFAULT else + +/** + * End of exception specification block. + * @hideinitializer + */ +#define PJ_END pj_pop_exception_handler_(); \ + } else {} + +/** + * Throw exception. + * @param exception_id The exception number. + * @hideinitializer + */ +#define PJ_THROW(exception_id) pj_throw_exception_(exception_id) + +/** + * Get current exception. + * @return Current exception code. + * @hideinitializer + */ +#define PJ_GET_EXCEPTION() (pj_x_code__) + +PJ_END_DECL + + + +#endif /* __PJ_EXCEPTION_H__ */ + + diff --git a/pjlib/include/pj/fifobuf.h b/pjlib/include/pj/fifobuf.h index ee3a19a0..59af0134 100644 --- a/pjlib/include/pj/fifobuf.h +++ b/pjlib/include/pj/fifobuf.h @@ -1,43 +1,43 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_FIFOBUF_H__
-#define __PJ_FIFOBUF_H__
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-typedef struct pj_fifobuf_t pj_fifobuf_t;
-struct pj_fifobuf_t
-{
- char *first, *last;
- char *ubegin, *uend;
- int full;
-};
-
-PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size);
-PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb);
-PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size);
-PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf);
-PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf);
-
-PJ_END_DECL
-
-#endif /* __PJ_FIFOBUF_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_FIFOBUF_H__ +#define __PJ_FIFOBUF_H__ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +typedef struct pj_fifobuf_t pj_fifobuf_t; +struct pj_fifobuf_t +{ + char *first, *last; + char *ubegin, *uend; + int full; +}; + +PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size); +PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb); +PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size); +PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf); +PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf); + +PJ_END_DECL + +#endif /* __PJ_FIFOBUF_H__ */ + diff --git a/pjlib/include/pj/file_access.h b/pjlib/include/pj/file_access.h index efa71217..5eed23da 100644 --- a/pjlib/include/pj/file_access.h +++ b/pjlib/include/pj/file_access.h @@ -1,108 +1,108 @@ -/* $Id */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_FILE_ACCESS_H__
-#define __PJ_FILE_ACCESS_H__
-
-/**
- * @file file_access.h
- * @brief File manipulation and access.
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_FILE_ACCESS File Access
- * @ingroup PJ_IO
- * @{
- *
- */
-
-/**
- * This structure describes file information, to be obtained by
- * calling #pj_file_getstat(). The time information in this structure
- * is in local time.
- */
-typedef struct pj_file_stat
-{
- pj_off_t size; /**< Total file size. */
- pj_time_val atime; /**< Time of last access. */
- pj_time_val mtime; /**< Time of last modification. */
- pj_time_val ctime; /**< Time of last creation. */
-} pj_file_stat;
-
-
-/**
- * Returns non-zero if the specified file exists.
- *
- * @param filename The file name.
- *
- * @return Non-zero if the file exists.
- */
-PJ_DECL(pj_bool_t) pj_file_exists(const char *filename);
-
-/**
- * Returns the size of the file.
- *
- * @param filename The file name.
- *
- * @return The file size in bytes or -1 on error.
- */
-PJ_DECL(pj_off_t) pj_file_size(const char *filename);
-
-/**
- * Delete a file.
- *
- * @param filename The filename.
- *
- * @return PJ_SUCCESS on success or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_file_delete(const char *filename);
-
-/**
- * Move a \c oldname to \c newname. If \c newname already exists,
- * it will be overwritten.
- *
- * @param oldname The file to rename.
- * @param newname New filename to assign.
- *
- * @return PJ_SUCCESS on success or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_file_move( const char *oldname,
- const char *newname);
-
-
-/**
- * Return information about the specified file. The time information in
- * the \c stat structure will be in local time.
- *
- * @param filename The filename.
- * @param stat Pointer to variable to receive file information.
- *
- * @return PJ_SUCCESS on success or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat);
-
-
-/** @} */
-
-PJ_END_DECL
-
-
-#endif /* __PJ_FILE_ACCESS_H__ */
+/* $Id */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_FILE_ACCESS_H__ +#define __PJ_FILE_ACCESS_H__ + +/** + * @file file_access.h + * @brief File manipulation and access. + */ +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_FILE_ACCESS File Access + * @ingroup PJ_IO + * @{ + * + */ + +/** + * This structure describes file information, to be obtained by + * calling #pj_file_getstat(). The time information in this structure + * is in local time. + */ +typedef struct pj_file_stat +{ + pj_off_t size; /**< Total file size. */ + pj_time_val atime; /**< Time of last access. */ + pj_time_val mtime; /**< Time of last modification. */ + pj_time_val ctime; /**< Time of last creation. */ +} pj_file_stat; + + +/** + * Returns non-zero if the specified file exists. + * + * @param filename The file name. + * + * @return Non-zero if the file exists. + */ +PJ_DECL(pj_bool_t) pj_file_exists(const char *filename); + +/** + * Returns the size of the file. + * + * @param filename The file name. + * + * @return The file size in bytes or -1 on error. + */ +PJ_DECL(pj_off_t) pj_file_size(const char *filename); + +/** + * Delete a file. + * + * @param filename The filename. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_file_delete(const char *filename); + +/** + * Move a \c oldname to \c newname. If \c newname already exists, + * it will be overwritten. + * + * @param oldname The file to rename. + * @param newname New filename to assign. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_file_move( const char *oldname, + const char *newname); + + +/** + * Return information about the specified file. The time information in + * the \c stat structure will be in local time. + * + * @param filename The filename. + * @param stat Pointer to variable to receive file information. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat); + + +/** @} */ + +PJ_END_DECL + + +#endif /* __PJ_FILE_ACCESS_H__ */ diff --git a/pjlib/include/pj/file_io.h b/pjlib/include/pj/file_io.h index e66c2cc3..ea0e207c 100644 --- a/pjlib/include/pj/file_io.h +++ b/pjlib/include/pj/file_io.h @@ -1,172 +1,172 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_FILE_IO_H__
-#define __PJ_FILE_IO_H__
-
-/**
- * @file file_io.h
- * @brief Simple file I/O abstraction.
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_FILE_IO File I/O
- * @ingroup PJ_IO
- * @{
- *
- * This file contains functionalities to perform file I/O. The file
- * I/O can be implemented with various back-end, either using native
- * file API or ANSI stream.
- *
- * @section pj_file_size_limit_sec Size Limits
- *
- * There may be limitation on the size that can be handled by the
- * #pj_file_setpos() or #pj_file_getpos() functions. The API itself
- * uses 64-bit integer for the file offset/position (where available);
- * however some backends (such as ANSI) may only support signed 32-bit
- * offset resolution.
- *
- * Reading and writing operation uses signed 32-bit integer to indicate
- * the size.
- *
- *
- */
-
-/**
- * These enumerations are used when opening file. Values PJ_O_RDONLY,
- * PJ_O_WRONLY, and PJ_O_RDWR are mutually exclusive. Value PJ_O_APPEND
- * can only be used when the file is opened for writing.
- */
-enum pj_file_access
-{
- PJ_O_RDONLY = 0x1101, /**< Open file for reading. */
- PJ_O_WRONLY = 0x1102, /**< Open file for writing. */
- PJ_O_RDWR = 0x1103, /**< Open file for reading and writing.
- File will be truncated. */
- PJ_O_APPEND = 0x1108, /**< Append to existing file. */
-};
-
-/**
- * The seek directive when setting the file position with #pj_file_setpos.
- */
-enum pj_file_seek_type
-{
- PJ_SEEK_SET = 0x1201, /**< Offset from beginning of the file. */
- PJ_SEEK_CUR = 0x1202, /**< Offset from current position. */
- PJ_SEEK_END = 0x1203, /**< Size of the file plus offset. */
-};
-
-/**
- * Open the file as specified in \c pathname with the specified
- * mode, and return the handle in \c fd. All files will be opened
- * as binary.
- *
- * @param pool Pool to allocate memory for the new file descriptor.
- * @param pathname The file name to open.
- * @param flags Open flags, which is bitmask combination of
- * #pj_file_access enum. The flag must be either
- * PJ_O_RDONLY, PJ_O_WRONLY, or PJ_O_RDWR. When file
- * writing is specified, existing file will be
- * truncated unless PJ_O_APPEND is specified.
- * @param fd The returned descriptor.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_open(pj_pool_t *pool,
- const char *pathname,
- unsigned flags,
- pj_oshandle_t *fd);
-
-/**
- * Close an opened file descriptor.
- *
- * @param fd The file descriptor.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_close(pj_oshandle_t fd);
-
-/**
- * Write data with the specified size to an opened file.
- *
- * @param fd The file descriptor.
- * @param data Data to be written to the file.
- * @param size On input, specifies the size of data to be written.
- * On return, it contains the number of data actually
- * written to the file.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_write(pj_oshandle_t fd,
- const void *data,
- pj_ssize_t *size);
-
-/**
- * Read data from the specified file. When end-of-file condition is set,
- * this function will return PJ_SUCCESS but the size will contain zero.
- *
- * @param fd The file descriptor.
- * @param data Pointer to buffer to receive the data.
- * @param size On input, specifies the maximum number of data to
- * read from the file. On output, it contains the size
- * of data actually read from the file. It will contain
- * zero when EOF occurs.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- * When EOF occurs, the return is PJ_SUCCESS but size
- * will report zero.
- */
-PJ_DECL(pj_status_t) pj_file_read(pj_oshandle_t fd,
- void *data,
- pj_ssize_t *size);
-
-/**
- * Set file position to new offset according to directive \c whence.
- *
- * @param fd The file descriptor.
- * @param offset The new file position to set.
- * @param whence The directive.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_setpos(pj_oshandle_t fd,
- pj_off_t offset,
- enum pj_file_seek_type whence);
-
-/**
- * Get current file position.
- *
- * @param fd The file descriptor.
- * @param pos On return contains the file position as measured
- * from the beginning of the file.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_getpos(pj_oshandle_t fd,
- pj_off_t *pos);
-
-/** @} */
-
-
-PJ_END_DECL
-
-#endif /* __PJ_FILE_IO_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_FILE_IO_H__ +#define __PJ_FILE_IO_H__ + +/** + * @file file_io.h + * @brief Simple file I/O abstraction. + */ +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_FILE_IO File I/O + * @ingroup PJ_IO + * @{ + * + * This file contains functionalities to perform file I/O. The file + * I/O can be implemented with various back-end, either using native + * file API or ANSI stream. + * + * @section pj_file_size_limit_sec Size Limits + * + * There may be limitation on the size that can be handled by the + * #pj_file_setpos() or #pj_file_getpos() functions. The API itself + * uses 64-bit integer for the file offset/position (where available); + * however some backends (such as ANSI) may only support signed 32-bit + * offset resolution. + * + * Reading and writing operation uses signed 32-bit integer to indicate + * the size. + * + * + */ + +/** + * These enumerations are used when opening file. Values PJ_O_RDONLY, + * PJ_O_WRONLY, and PJ_O_RDWR are mutually exclusive. Value PJ_O_APPEND + * can only be used when the file is opened for writing. + */ +enum pj_file_access +{ + PJ_O_RDONLY = 0x1101, /**< Open file for reading. */ + PJ_O_WRONLY = 0x1102, /**< Open file for writing. */ + PJ_O_RDWR = 0x1103, /**< Open file for reading and writing. + File will be truncated. */ + PJ_O_APPEND = 0x1108, /**< Append to existing file. */ +}; + +/** + * The seek directive when setting the file position with #pj_file_setpos. + */ +enum pj_file_seek_type +{ + PJ_SEEK_SET = 0x1201, /**< Offset from beginning of the file. */ + PJ_SEEK_CUR = 0x1202, /**< Offset from current position. */ + PJ_SEEK_END = 0x1203, /**< Size of the file plus offset. */ +}; + +/** + * Open the file as specified in \c pathname with the specified + * mode, and return the handle in \c fd. All files will be opened + * as binary. + * + * @param pool Pool to allocate memory for the new file descriptor. + * @param pathname The file name to open. + * @param flags Open flags, which is bitmask combination of + * #pj_file_access enum. The flag must be either + * PJ_O_RDONLY, PJ_O_WRONLY, or PJ_O_RDWR. When file + * writing is specified, existing file will be + * truncated unless PJ_O_APPEND is specified. + * @param fd The returned descriptor. + * + * @return PJ_SUCCESS or the appropriate error code on error. + */ +PJ_DECL(pj_status_t) pj_file_open(pj_pool_t *pool, + const char *pathname, + unsigned flags, + pj_oshandle_t *fd); + +/** + * Close an opened file descriptor. + * + * @param fd The file descriptor. + * + * @return PJ_SUCCESS or the appropriate error code on error. + */ +PJ_DECL(pj_status_t) pj_file_close(pj_oshandle_t fd); + +/** + * Write data with the specified size to an opened file. + * + * @param fd The file descriptor. + * @param data Data to be written to the file. + * @param size On input, specifies the size of data to be written. + * On return, it contains the number of data actually + * written to the file. + * + * @return PJ_SUCCESS or the appropriate error code on error. + */ +PJ_DECL(pj_status_t) pj_file_write(pj_oshandle_t fd, + const void *data, + pj_ssize_t *size); + +/** + * Read data from the specified file. When end-of-file condition is set, + * this function will return PJ_SUCCESS but the size will contain zero. + * + * @param fd The file descriptor. + * @param data Pointer to buffer to receive the data. + * @param size On input, specifies the maximum number of data to + * read from the file. On output, it contains the size + * of data actually read from the file. It will contain + * zero when EOF occurs. + * + * @return PJ_SUCCESS or the appropriate error code on error. + * When EOF occurs, the return is PJ_SUCCESS but size + * will report zero. + */ +PJ_DECL(pj_status_t) pj_file_read(pj_oshandle_t fd, + void *data, + pj_ssize_t *size); + +/** + * Set file position to new offset according to directive \c whence. + * + * @param fd The file descriptor. + * @param offset The new file position to set. + * @param whence The directive. + * + * @return PJ_SUCCESS or the appropriate error code on error. + */ +PJ_DECL(pj_status_t) pj_file_setpos(pj_oshandle_t fd, + pj_off_t offset, + enum pj_file_seek_type whence); + +/** + * Get current file position. + * + * @param fd The file descriptor. + * @param pos On return contains the file position as measured + * from the beginning of the file. + * + * @return PJ_SUCCESS or the appropriate error code on error. + */ +PJ_DECL(pj_status_t) pj_file_getpos(pj_oshandle_t fd, + pj_off_t *pos); + +/** @} */ + + +PJ_END_DECL + +#endif /* __PJ_FILE_IO_H__ */ + diff --git a/pjlib/include/pj/guid.h b/pjlib/include/pj/guid.h index 2fc96563..9c6d30b4 100644 --- a/pjlib/include/pj/guid.h +++ b/pjlib/include/pj/guid.h @@ -1,91 +1,91 @@ -/* $Id */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_GUID_H__
-#define __PJ_GUID_H__
-
-
-/**
- * @file guid.h
- * @brief GUID Globally Unique Identifier.
- */
-#include <pj/types.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_DS Data Structure.
- * @ingroup PJ
- */
-/**
- * @defgroup PJ_GUID Globally Unique Identifier
- * @ingroup PJ_DS
- * @{
- *
- * This module provides API to create string that is globally unique.
- * If application doesn't require that strong requirement, it can just
- * use #pj_create_random_string() instead.
- */
-
-
-/**
- * PJ_GUID_STRING_LENGTH specifies length of GUID string. The value is
- * dependent on the algorithm used internally to generate the GUID string.
- * If real GUID generator is used, then the length will be 128bit or
- * 32 bytes. If shadow GUID generator is used, then the length
- * will be 20 bytes. Application should not assume which algorithm will
- * be used by GUID generator.
- */
-extern const unsigned PJ_GUID_STRING_LENGTH;
-
-/**
- * PJ_GUID_MAX_LENGTH specifies the maximum length of GUID string,
- * regardless of which algorithm to use.
- */
-#define PJ_GUID_MAX_LENGTH 32
-
-/**
- * Create a globally unique string, which length is PJ_GUID_STRING_LENGTH
- * characters. Caller is responsible for preallocating the storage used
- * in the string.
- *
- * @param str The string to store the result.
- *
- * @return The string.
- */
-PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str);
-
-/**
- * Generate a unique string.
- *
- * @param pool Pool to allocate memory from.
- * @param str The string.
- */
-PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif/* __PJ_GUID_H__ */
-
+/* $Id */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_GUID_H__ +#define __PJ_GUID_H__ + + +/** + * @file guid.h + * @brief GUID Globally Unique Identifier. + */ +#include <pj/types.h> + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_DS Data Structure. + * @ingroup PJ + */ +/** + * @defgroup PJ_GUID Globally Unique Identifier + * @ingroup PJ_DS + * @{ + * + * This module provides API to create string that is globally unique. + * If application doesn't require that strong requirement, it can just + * use #pj_create_random_string() instead. + */ + + +/** + * PJ_GUID_STRING_LENGTH specifies length of GUID string. The value is + * dependent on the algorithm used internally to generate the GUID string. + * If real GUID generator is used, then the length will be 128bit or + * 32 bytes. If shadow GUID generator is used, then the length + * will be 20 bytes. Application should not assume which algorithm will + * be used by GUID generator. + */ +extern const unsigned PJ_GUID_STRING_LENGTH; + +/** + * PJ_GUID_MAX_LENGTH specifies the maximum length of GUID string, + * regardless of which algorithm to use. + */ +#define PJ_GUID_MAX_LENGTH 32 + +/** + * Create a globally unique string, which length is PJ_GUID_STRING_LENGTH + * characters. Caller is responsible for preallocating the storage used + * in the string. + * + * @param str The string to store the result. + * + * @return The string. + */ +PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str); + +/** + * Generate a unique string. + * + * @param pool Pool to allocate memory from. + * @param str The string. + */ +PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str); + + +/** + * @} + */ + +PJ_END_DECL + +#endif/* __PJ_GUID_H__ */ + diff --git a/pjlib/include/pj/hash.h b/pjlib/include/pj/hash.h index cee8e71f..41c89bc0 100644 --- a/pjlib/include/pj/hash.h +++ b/pjlib/include/pj/hash.h @@ -1,171 +1,171 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_HASH_H__
-#define __PJ_HASH_H__
-
-/**
- * @file hash.h
- * @brief Hash Table.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_HASH Hash Table
- * @ingroup PJ_DS
- * @{
- * A hash table is a dictionary in which keys are mapped to array positions by
- * hash functions. Having the keys of more than one item map to the same
- * position is called a collision. In this library, we will chain the nodes
- * that have the same key in a list.
- */
-
-/**
- * If this constant is used as keylen, then the key is interpreted as
- * NULL terminated string.
- */
-#define PJ_HASH_KEY_STRING ((unsigned)-1)
-
-/**
- * This is the function that is used by the hash table to calculate hash value
- * of the specified key.
- *
- * @param hval the initial hash value, or zero.
- * @param key the key to calculate.
- * @param keylen the length of the key, or PJ_HASH_KEY_STRING to treat
- * the key as null terminated string.
- *
- * @return the hash value.
- */
-PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval,
- const void *key, unsigned keylen);
-
-
-/**
- * Convert the key to lowercase and calculate the hash value. The resulting
- * string is stored in \c result.
- *
- * @param hval The initial hash value, normally zero.
- * @param result Buffer to store the result, which must be enough to hold
- * the string.
- * @param key The input key to be converted and calculated.
- *
- * @return The hash value.
- */
-PJ_DECL(pj_uint32_t) pj_hash_calc_tolower(pj_uint32_t hval,
- char *result,
- const pj_str_t *key);
-
-/**
- * Create a hash table with the specified 'bucket' size.
- *
- * @param pool the pool from which the hash table will be allocated from.
- * @param size the bucket size, which will be round-up to the nearest 2^n+1
- *
- * @return the hash table.
- */
-PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size);
-
-
-/**
- * Get the value associated with the specified key.
- *
- * @param ht the hash table.
- * @param key the key to look for.
- * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the
- * string length of the key.
- *
- * @return the value associated with the key, or NULL if the key is not found.
- */
-PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht,
- const void *key, unsigned keylen );
-
-
-/**
- * Associate/disassociate a value with the specified key.
- *
- * @param pool the pool to allocate the new entry if a new entry has to be
- * created.
- * @param ht the hash table.
- * @param key the key.
- * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the
- * string length of the key.
- * @param value value to be associated, or NULL to delete the entry with
- * the specified key.
- */
-PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
- const void *key, unsigned keylen,
- void *value );
-
-/**
- * Get the total number of entries in the hash table.
- *
- * @param ht the hash table.
- *
- * @return the number of entries in the hash table.
- */
-PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht );
-
-
-/**
- * Get the iterator to the first element in the hash table.
- *
- * @param ht the hash table.
- * @param it the iterator for iterating hash elements.
- *
- * @return the iterator to the hash element, or NULL if no element presents.
- */
-PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
- pj_hash_iterator_t *it );
-
-
-/**
- * Get the next element from the iterator.
- *
- * @param ht the hash table.
- * @param it the hash iterator.
- *
- * @return the next iterator, or NULL if there's no more element.
- */
-PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
- pj_hash_iterator_t *it );
-
-/**
- * Get the value associated with a hash iterator.
- *
- * @param ht the hash table.
- * @param it the hash iterator.
- *
- * @return the value associated with the current element in iterator.
- */
-PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht,
- pj_hash_iterator_t *it );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_HASH_H__ +#define __PJ_HASH_H__ + +/** + * @file hash.h + * @brief Hash Table. + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_HASH Hash Table + * @ingroup PJ_DS + * @{ + * A hash table is a dictionary in which keys are mapped to array positions by + * hash functions. Having the keys of more than one item map to the same + * position is called a collision. In this library, we will chain the nodes + * that have the same key in a list. + */ + +/** + * If this constant is used as keylen, then the key is interpreted as + * NULL terminated string. + */ +#define PJ_HASH_KEY_STRING ((unsigned)-1) + +/** + * This is the function that is used by the hash table to calculate hash value + * of the specified key. + * + * @param hval the initial hash value, or zero. + * @param key the key to calculate. + * @param keylen the length of the key, or PJ_HASH_KEY_STRING to treat + * the key as null terminated string. + * + * @return the hash value. + */ +PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval, + const void *key, unsigned keylen); + + +/** + * Convert the key to lowercase and calculate the hash value. The resulting + * string is stored in \c result. + * + * @param hval The initial hash value, normally zero. + * @param result Buffer to store the result, which must be enough to hold + * the string. + * @param key The input key to be converted and calculated. + * + * @return The hash value. + */ +PJ_DECL(pj_uint32_t) pj_hash_calc_tolower(pj_uint32_t hval, + char *result, + const pj_str_t *key); + +/** + * Create a hash table with the specified 'bucket' size. + * + * @param pool the pool from which the hash table will be allocated from. + * @param size the bucket size, which will be round-up to the nearest 2^n+1 + * + * @return the hash table. + */ +PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size); + + +/** + * Get the value associated with the specified key. + * + * @param ht the hash table. + * @param key the key to look for. + * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the + * string length of the key. + * + * @return the value associated with the key, or NULL if the key is not found. + */ +PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht, + const void *key, unsigned keylen ); + + +/** + * Associate/disassociate a value with the specified key. + * + * @param pool the pool to allocate the new entry if a new entry has to be + * created. + * @param ht the hash table. + * @param key the key. + * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the + * string length of the key. + * @param value value to be associated, or NULL to delete the entry with + * the specified key. + */ +PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, + void *value ); + +/** + * Get the total number of entries in the hash table. + * + * @param ht the hash table. + * + * @return the number of entries in the hash table. + */ +PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht ); + + +/** + * Get the iterator to the first element in the hash table. + * + * @param ht the hash table. + * @param it the iterator for iterating hash elements. + * + * @return the iterator to the hash element, or NULL if no element presents. + */ +PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, + pj_hash_iterator_t *it ); + + +/** + * Get the next element from the iterator. + * + * @param ht the hash table. + * @param it the hash iterator. + * + * @return the next iterator, or NULL if there's no more element. + */ +PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, + pj_hash_iterator_t *it ); + +/** + * Get the value associated with a hash iterator. + * + * @param ht the hash table. + * @param it the hash iterator. + * + * @return the value associated with the current element in iterator. + */ +PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht, + pj_hash_iterator_t *it ); + + +/** + * @} + */ + +PJ_END_DECL + +#endif + + diff --git a/pjlib/include/pj/ioqueue.h b/pjlib/include/pj/ioqueue.h index 26484cad..86a5309f 100644 --- a/pjlib/include/pj/ioqueue.h +++ b/pjlib/include/pj/ioqueue.h @@ -1,665 +1,665 @@ -/* $Id$
- */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_IOQUEUE_H__
-#define __PJ_IOQUEUE_H__
-
-/**
- * @file ioqueue.h
- * @brief I/O Dispatching Mechanism
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_IO Input/Output
- * @brief Input/Output
- * @ingroup PJ_OS
- *
- * This section contains API building blocks to perform network I/O and
- * communications. If provides:
- * - @ref PJ_SOCK
- *\n
- * A highly portable socket abstraction, runs on all kind of
- * network APIs such as standard BSD socket, Windows socket, Linux
- * \b kernel socket, PalmOS networking API, etc.
- *
- * - @ref pj_addr_resolve
- *\n
- * Portable address resolution, which implements #pj_gethostbyname().
- *
- * - @ref PJ_SOCK_SELECT
- *\n
- * A portable \a select() like API (#pj_sock_select()) which can be
- * implemented with various back-ends.
- *
- * - @ref PJ_IOQUEUE
- *\n
- * Framework for dispatching network events.
- *
- * For more information see the modules below.
- */
-
-/**
- * @defgroup PJ_IOQUEUE I/O Event Dispatching Queue
- * @ingroup PJ_IO
- * @{
- *
- * I/O Queue provides API for performing asynchronous I/O operations. It
- * conforms to proactor pattern, which allows application to submit an
- * asynchronous operation and to be notified later when the operation has
- * completed.
- *
- * The I/O Queue can work on both socket and file descriptors. For
- * asynchronous file operations however, one must make sure that the correct
- * file I/O back-end is used, because not all file I/O back-end can be
- * used with the ioqueue. Please see \ref PJ_FILE_IO for more details.
- *
- * The framework works natively in platforms where asynchronous operation API
- * exists, such as in Windows NT with IoCompletionPort/IOCP. In other
- * platforms, the I/O queue abstracts the operating system's event poll API
- * to provide semantics similar to IoCompletionPort with minimal penalties
- * (i.e. per ioqueue and per handle mutex protection).
- *
- * The I/O queue provides more than just unified abstraction. It also:
- * - makes sure that the operation uses the most effective way to utilize
- * the underlying mechanism, to achieve the maximum theoritical
- * throughput possible on a given platform.
- * - choose the most efficient mechanism for event polling on a given
- * platform.
- *
- * Currently, the I/O Queue is implemented using:
- * - <tt><b>select()</b></tt>, as the common denominator, but the least
- * efficient. Also the number of descriptor is limited to
- * \c PJ_IOQUEUE_MAX_HANDLES (which by default is 64).
- * - <tt><b>/dev/epoll</b></tt> on Linux (user mode and kernel mode),
- * a much faster replacement for select() on Linux (and more importantly
- * doesn't have limitation on number of descriptors).
- * - <b>I/O Completion ports</b> on Windows NT/2000/XP, which is the most
- * efficient way to dispatch events in Windows NT based OSes, and most
- * importantly, it doesn't have the limit on how many handles to monitor.
- * And it works with files (not only sockets) as well.
- *
- *
- * \section pj_ioqueue_concurrency_sec Concurrency Rules
- *
- * The items below describe rules that must be obeyed when using the I/O
- * queue, with regard to concurrency:
- * - simultaneous operations (by different threads) to different key is safe.
- * - simultaneous operations to the same key is also safe, except
- * <b>unregistration</b>, which is described below.
- * - <b>care must be taken when unregistering a key</b> from the
- * ioqueue. Application must take care that when one thread is issuing
- * an unregistration, other thread is not simultaneously invoking an
- * operation <b>to the same key</b>.
- *\n
- * This happens because the ioqueue functions are working with a pointer
- * to the key, and there is a possible race condition where the pointer
- * has been rendered invalid by other threads before the ioqueue has a
- * chance to acquire mutex on it.
- *
- * \section pj_ioqeuue_examples_sec Examples
- *
- * For some examples on how to use the I/O Queue, please see:
- *
- * - \ref page_pjlib_ioqueue_tcp_test
- * - \ref page_pjlib_ioqueue_udp_test
- * - \ref page_pjlib_ioqueue_perf_test
- */
-
-
-/**
- * This structure describes operation specific key to be submitted to
- * I/O Queue when performing the asynchronous operation. This key will
- * be returned to the application when completion callback is called.
- *
- * Application normally wants to attach it's specific data in the
- * \c user_data field so that it can keep track of which operation has
- * completed when the callback is called. Alternatively, application can
- * also extend this struct to include its data, because the pointer that
- * is returned in the completion callback will be exactly the same as
- * the pointer supplied when the asynchronous function is called.
- */
-typedef struct pj_ioqueue_op_key_t
-{
- void *internal__[32]; /**< Internal I/O Queue data. */
- void *user_data; /**< Application data. */
-} pj_ioqueue_op_key_t;
-
-/**
- * This structure describes the callbacks to be called when I/O operation
- * completes.
- */
-typedef struct pj_ioqueue_callback
-{
- /**
- * This callback is called when #pj_ioqueue_recv or #pj_ioqueue_recvfrom
- * completes.
- *
- * @param key The key.
- * @param op_key Operation key.
- * @param bytes_read >= 0 to indicate the amount of data read,
- * otherwise negative value containing the error
- * code. To obtain the pj_status_t error code, use
- * (pj_status_t code = -bytes_read).
- */
- void (*on_read_complete)(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read);
-
- /**
- * This callback is called when #pj_ioqueue_write or #pj_ioqueue_sendto
- * completes.
- *
- * @param key The key.
- * @param op_key Operation key.
- * @param bytes_sent >= 0 to indicate the amount of data written,
- * otherwise negative value containing the error
- * code. To obtain the pj_status_t error code, use
- * (pj_status_t code = -bytes_sent).
- */
- void (*on_write_complete)(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent);
-
- /**
- * This callback is called when #pj_ioqueue_accept completes.
- *
- * @param key The key.
- * @param op_key Operation key.
- * @param sock Newly connected socket.
- * @param status Zero if the operation completes successfully.
- */
- void (*on_accept_complete)(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t sock,
- pj_status_t status);
-
- /**
- * This callback is called when #pj_ioqueue_connect completes.
- *
- * @param key The key.
- * @param status PJ_SUCCESS if the operation completes successfully.
- */
- void (*on_connect_complete)(pj_ioqueue_key_t *key,
- pj_status_t status);
-} pj_ioqueue_callback;
-
-
-/**
- * Types of pending I/O Queue operation. This enumeration is only used
- * internally within the ioqueue.
- */
-typedef enum pj_ioqueue_operation_e
-{
- PJ_IOQUEUE_OP_NONE = 0, /**< No operation. */
- PJ_IOQUEUE_OP_READ = 1, /**< read() operation. */
- PJ_IOQUEUE_OP_RECV = 2, /**< recv() operation. */
- PJ_IOQUEUE_OP_RECV_FROM = 4, /**< recvfrom() operation. */
- PJ_IOQUEUE_OP_WRITE = 8, /**< write() operation. */
- PJ_IOQUEUE_OP_SEND = 16, /**< send() operation. */
- PJ_IOQUEUE_OP_SEND_TO = 32, /**< sendto() operation. */
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- PJ_IOQUEUE_OP_ACCEPT = 64, /**< accept() operation. */
- PJ_IOQUEUE_OP_CONNECT = 128, /**< connect() operation. */
-#endif /* PJ_HAS_TCP */
-} pj_ioqueue_operation_e;
-
-
-/**
- * This macro specifies the maximum number of events that can be
- * processed by the ioqueue on a single poll cycle, on implementation
- * that supports it. The value is only meaningfull when specified
- * during PJLIB build.
- */
-#ifndef PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL
-# define PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL (16)
-#endif
-
-/**
- * When this flag is specified in ioqueue's recv() or send() operations,
- * the ioqueue will always mark the operation as asynchronous.
- */
-#define PJ_IOQUEUE_ALWAYS_ASYNC ((pj_uint32_t)1 << (pj_uint32_t)31)
-
-/**
- * Return the name of the ioqueue implementation.
- *
- * @return Implementation name.
- */
-PJ_DECL(const char*) pj_ioqueue_name(void);
-
-
-/**
- * Create a new I/O Queue framework.
- *
- * @param pool The pool to allocate the I/O queue structure.
- * @param max_fd The maximum number of handles to be supported, which
- * should not exceed PJ_IOQUEUE_MAX_HANDLES.
- * @param ioqueue Pointer to hold the newly created I/O Queue.
- *
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **ioqueue);
-
-/**
- * Destroy the I/O queue.
- *
- * @param ioque The I/O Queue to be destroyed.
- *
- * @return PJ_SUCCESS if success.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque );
-
-/**
- * Set the lock object to be used by the I/O Queue. This function can only
- * be called right after the I/O queue is created, before any handle is
- * registered to the I/O queue.
- *
- * Initially the I/O queue is created with non-recursive mutex protection.
- * Applications can supply alternative lock to be used by calling this
- * function.
- *
- * @param ioque The ioqueue instance.
- * @param lock The lock to be used by the ioqueue.
- * @param auto_delete In non-zero, the lock will be deleted by the ioqueue.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
- pj_lock_t *lock,
- pj_bool_t auto_delete );
-
-/**
- * Register a socket to the I/O queue framework.
- * When a socket is registered to the IOQueue, it may be modified to use
- * non-blocking IO. If it is modified, there is no guarantee that this
- * modification will be restored after the socket is unregistered.
- *
- * @param pool To allocate the resource for the specified handle,
- * which must be valid until the handle/key is unregistered
- * from I/O Queue.
- * @param ioque The I/O Queue.
- * @param sock The socket.
- * @param user_data User data to be associated with the key, which can be
- * retrieved later.
- * @param cb Callback to be called when I/O operation completes.
- * @param key Pointer to receive the key to be associated with this
- * socket. Subsequent I/O queue operation will need this
- * key.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioque,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **key );
-
-/**
- * Unregister from the I/O Queue framework. Caller must make sure that
- * the key doesn't have any pending operations before calling this function,
- * by calling #pj_ioqueue_is_pending() for all previously submitted
- * operations except asynchronous connect, and if necessary call
- * #pj_ioqueue_post_completion() to cancel the pending operations.
- *
- * Note that asynchronous connect operation will automatically be
- * cancelled during the unregistration.
- *
- * @param key The key that was previously obtained from registration.
- *
- * @return PJ_SUCCESS on success or the error code.
- *
- * @see pj_ioqueue_is_pending
- */
-PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key );
-
-
-/**
- * Get user data associated with an ioqueue key.
- *
- * @param key The key that was previously obtained from registration.
- *
- * @return The user data associated with the descriptor, or NULL
- * on error or if no data is associated with the key during
- * registration.
- */
-PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key );
-
-/**
- * Set or change the user data to be associated with the file descriptor or
- * handle or socket descriptor.
- *
- * @param key The key that was previously obtained from registration.
- * @param user_data User data to be associated with the descriptor.
- * @param old_data Optional parameter to retrieve the old user data.
- *
- * @return PJ_SUCCESS on success or the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
- void *user_data,
- void **old_data);
-
-
-/**
- * Initialize operation key.
- *
- * @param op_key The operation key to be initialied.
- * @param size The size of the operation key.
- */
-PJ_DECL(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
- pj_size_t size );
-
-/**
- * Check if operation is pending on the specified operation key.
- * The \c op_key must have been initialized with #pj_ioqueue_op_key_init()
- * or submitted as pending operation before, or otherwise the result
- * is undefined.
- *
- * @param key The key.
- * @param op_key The operation key, previously submitted to any of
- * the I/O functions and has returned PJ_EPENDING.
- *
- * @return Non-zero if operation is still pending.
- */
-PJ_DECL(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key );
-
-
-/**
- * Post completion status to the specified operation key and call the
- * appropriate callback. When the callback is called, the number of bytes
- * received in read/write callback or the status in accept/connect callback
- * will be set from the \c bytes_status parameter.
- *
- * @param key The key.
- * @param op_key Pending operation key.
- * @param bytes_status Number of bytes or status to be set. A good value
- * to put here is -PJ_ECANCELLED.
- *
- * @return PJ_SUCCESS if completion status has been successfully
- * sent.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_status );
-
-
-
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
-/**
- * Instruct I/O Queue to accept incoming connection on the specified
- * listening socket. This function will return immediately (i.e. non-blocking)
- * regardless whether a connection is immediately available. If the function
- * can't complete immediately, the caller will be notified about the incoming
- * connection when it calls pj_ioqueue_poll(). If a new connection is
- * immediately available, the function returns PJ_SUCCESS with the new
- * connection; in this case, the callback WILL NOT be called.
- *
- * @param key The key which registered to the server socket.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param new_sock Argument which contain pointer to receive the new socket
- * for the incoming connection.
- * @param local Optional argument which contain pointer to variable to
- * receive local address.
- * @param remote Optional argument which contain pointer to variable to
- * receive the remote address.
- * @param addrlen On input, contains the length of the buffer for the
- * address, and on output, contains the actual length of the
- * address. This argument is optional.
- * @return
- * - PJ_SUCCESS When connection is available immediately, and the
- * parameters will be updated to contain information about
- * the new connection. In this case, a completion callback
- * WILL NOT be called.
- * - PJ_EPENDING If no connection is available immediately. When a new
- * connection arrives, the callback will be called.
- * - non-zero which indicates the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t *sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen );
-
-/**
- * Initiate non-blocking socket connect. If the socket can NOT be connected
- * immediately, asynchronous connect() will be scheduled and caller will be
- * notified via completion callback when it calls pj_ioqueue_poll(). If
- * socket is connected immediately, the function returns PJ_SUCCESS and
- * completion callback WILL NOT be called.
- *
- * @param key The key associated with TCP socket
- * @param addr The remote address.
- * @param addrlen The remote address length.
- *
- * @return
- * - PJ_SUCCESS If socket is connected immediately. In this case, the
- * completion callback WILL NOT be called.
- * - PJ_EPENDING If operation is queued, or
- * - non-zero Indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen );
-
-#endif /* PJ_HAS_TCP */
-
-/**
- * Poll the I/O Queue for completed events.
- *
- * @param ioque the I/O Queue.
- * @param timeout polling timeout, or NULL if the thread wishes to wait
- * indefinetely for the event.
- *
- * @return
- * - zero if timed out (no event).
- * - (<0) if error occured during polling. Callback will NOT be called.
- * - (>1) to indicate numbers of events. Callbacks have been called.
- */
-PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque,
- const pj_time_val *timeout);
-
-
-/**
- * Instruct the I/O Queue to read from the specified handle. This function
- * returns immediately (i.e. non-blocking) regardless whether some data has
- * been transfered. If the operation can't complete immediately, caller will
- * be notified about the completion when it calls pj_ioqueue_poll(). If data
- * is immediately available, the function will return PJ_SUCCESS and the
- * callback WILL NOT be called.
- *
- * @param key The key that uniquely identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called. Caller must make sure that this key remains
- * valid until the function completes.
- * @param buffer The buffer to hold the read data. The caller MUST make sure
- * that this buffer remain valid until the framework completes
- * reading the handle.
- * @param length On input, it specifies the size of the buffer. If data is
- * available to be read immediately, the function returns
- * PJ_SUCCESS and this argument will be filled with the
- * amount of data read. If the function is pending, caller
- * will be notified about the amount of data read in the
- * callback. This parameter can point to local variable in
- * caller's stack and doesn't have to remain valid for the
- * duration of pending operation.
- * @param flags Recv flag. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then
- * the function will never return PJ_SUCCESS.
- *
- * @return
- * - PJ_SUCCESS If immediate data has been received in the buffer. In this
- * case, the callback WILL NOT be called.
- * - PJ_EPENDING If the operation has been queued, and the callback will be
- * called when data has been received.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- pj_uint32_t flags );
-
-/**
- * This function behaves similarly as #pj_ioqueue_recv(), except that it is
- * normally called for socket, and the remote address will also be returned
- * along with the data. Caller MUST make sure that both buffer and addr
- * remain valid until the framework completes reading the data.
- *
- * @param key The key that uniquely identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param buffer The buffer to hold the read data. The caller MUST make sure
- * that this buffer remain valid until the framework completes
- * reading the handle.
- * @param length On input, it specifies the size of the buffer. If data is
- * available to be read immediately, the function returns
- * PJ_SUCCESS and this argument will be filled with the
- * amount of data read. If the function is pending, caller
- * will be notified about the amount of data read in the
- * callback. This parameter can point to local variable in
- * caller's stack and doesn't have to remain valid for the
- * duration of pending operation.
- * @param flags Recv flag. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then
- * the function will never return PJ_SUCCESS.
- * @param addr Optional Pointer to buffer to receive the address.
- * @param addrlen On input, specifies the length of the address buffer.
- * On output, it will be filled with the actual length of
- * the address. This argument can be NULL if \c addr is not
- * specified.
- *
- * @return
- * - PJ_SUCCESS If immediate data has been received. In this case, the
- * callback must have been called before this function
- * returns, and no pending operation is scheduled.
- * - PJ_EPENDING If the operation has been queued.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- pj_uint32_t flags,
- pj_sockaddr_t *addr,
- int *addrlen);
-
-/**
- * Instruct the I/O Queue to write to the handle. This function will return
- * immediately (i.e. non-blocking) regardless whether some data has been
- * transfered. If the function can't complete immediately, the caller will
- * be notified about the completion when it calls pj_ioqueue_poll(). If
- * operation completes immediately and data has been transfered, the function
- * returns PJ_SUCCESS and the callback will NOT be called.
- *
- * @param key The key that identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param data The data to send. Caller MUST make sure that this buffer
- * remains valid until the write operation completes.
- * @param length On input, it specifies the length of data to send. When
- * data was sent immediately, this function returns PJ_SUCCESS
- * and this parameter contains the length of data sent. If
- * data can not be sent immediately, an asynchronous operation
- * is scheduled and caller will be notified via callback the
- * number of bytes sent. This parameter can point to local
- * variable on caller's stack and doesn't have to remain
- * valid until the operation has completed.
- * @param flags Send flags. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then
- * the function will never return PJ_SUCCESS.
- *
- * @return
- * - PJ_SUCCESS If data was immediately transfered. In this case, no
- * pending operation has been scheduled and the callback
- * WILL NOT be called.
- * - PJ_EPENDING If the operation has been queued. Once data base been
- * transfered, the callback will be called.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- pj_uint32_t flags );
-
-
-/**
- * Instruct the I/O Queue to write to the handle. This function will return
- * immediately (i.e. non-blocking) regardless whether some data has been
- * transfered. If the function can't complete immediately, the caller will
- * be notified about the completion when it calls pj_ioqueue_poll(). If
- * operation completes immediately and data has been transfered, the function
- * returns PJ_SUCCESS and the callback will NOT be called.
- *
- * @param key the key that identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param data the data to send. Caller MUST make sure that this buffer
- * remains valid until the write operation completes.
- * @param length On input, it specifies the length of data to send. When
- * data was sent immediately, this function returns PJ_SUCCESS
- * and this parameter contains the length of data sent. If
- * data can not be sent immediately, an asynchronous operation
- * is scheduled and caller will be notified via callback the
- * number of bytes sent. This parameter can point to local
- * variable on caller's stack and doesn't have to remain
- * valid until the operation has completed.
- * @param flags send flags. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then
- * the function will never return PJ_SUCCESS.
- * @param addr Optional remote address.
- * @param addrlen Remote address length, \c addr is specified.
- *
- * @return
- * - PJ_SUCCESS If data was immediately written.
- * - PJ_EPENDING If the operation has been queued.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- pj_uint32_t flags,
- const pj_sockaddr_t *addr,
- int addrlen);
-
-
-/**
- * !}
- */
-
-PJ_END_DECL
-
-#endif /* __PJ_IOQUEUE_H__ */
-
+/* $Id$ + */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_IOQUEUE_H__ +#define __PJ_IOQUEUE_H__ + +/** + * @file ioqueue.h + * @brief I/O Dispatching Mechanism + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_IO Input/Output + * @brief Input/Output + * @ingroup PJ_OS + * + * This section contains API building blocks to perform network I/O and + * communications. If provides: + * - @ref PJ_SOCK + *\n + * A highly portable socket abstraction, runs on all kind of + * network APIs such as standard BSD socket, Windows socket, Linux + * \b kernel socket, PalmOS networking API, etc. + * + * - @ref pj_addr_resolve + *\n + * Portable address resolution, which implements #pj_gethostbyname(). + * + * - @ref PJ_SOCK_SELECT + *\n + * A portable \a select() like API (#pj_sock_select()) which can be + * implemented with various back-ends. + * + * - @ref PJ_IOQUEUE + *\n + * Framework for dispatching network events. + * + * For more information see the modules below. + */ + +/** + * @defgroup PJ_IOQUEUE I/O Event Dispatching Queue + * @ingroup PJ_IO + * @{ + * + * I/O Queue provides API for performing asynchronous I/O operations. It + * conforms to proactor pattern, which allows application to submit an + * asynchronous operation and to be notified later when the operation has + * completed. + * + * The I/O Queue can work on both socket and file descriptors. For + * asynchronous file operations however, one must make sure that the correct + * file I/O back-end is used, because not all file I/O back-end can be + * used with the ioqueue. Please see \ref PJ_FILE_IO for more details. + * + * The framework works natively in platforms where asynchronous operation API + * exists, such as in Windows NT with IoCompletionPort/IOCP. In other + * platforms, the I/O queue abstracts the operating system's event poll API + * to provide semantics similar to IoCompletionPort with minimal penalties + * (i.e. per ioqueue and per handle mutex protection). + * + * The I/O queue provides more than just unified abstraction. It also: + * - makes sure that the operation uses the most effective way to utilize + * the underlying mechanism, to achieve the maximum theoritical + * throughput possible on a given platform. + * - choose the most efficient mechanism for event polling on a given + * platform. + * + * Currently, the I/O Queue is implemented using: + * - <tt><b>select()</b></tt>, as the common denominator, but the least + * efficient. Also the number of descriptor is limited to + * \c PJ_IOQUEUE_MAX_HANDLES (which by default is 64). + * - <tt><b>/dev/epoll</b></tt> on Linux (user mode and kernel mode), + * a much faster replacement for select() on Linux (and more importantly + * doesn't have limitation on number of descriptors). + * - <b>I/O Completion ports</b> on Windows NT/2000/XP, which is the most + * efficient way to dispatch events in Windows NT based OSes, and most + * importantly, it doesn't have the limit on how many handles to monitor. + * And it works with files (not only sockets) as well. + * + * + * \section pj_ioqueue_concurrency_sec Concurrency Rules + * + * The items below describe rules that must be obeyed when using the I/O + * queue, with regard to concurrency: + * - simultaneous operations (by different threads) to different key is safe. + * - simultaneous operations to the same key is also safe, except + * <b>unregistration</b>, which is described below. + * - <b>care must be taken when unregistering a key</b> from the + * ioqueue. Application must take care that when one thread is issuing + * an unregistration, other thread is not simultaneously invoking an + * operation <b>to the same key</b>. + *\n + * This happens because the ioqueue functions are working with a pointer + * to the key, and there is a possible race condition where the pointer + * has been rendered invalid by other threads before the ioqueue has a + * chance to acquire mutex on it. + * + * \section pj_ioqeuue_examples_sec Examples + * + * For some examples on how to use the I/O Queue, please see: + * + * - \ref page_pjlib_ioqueue_tcp_test + * - \ref page_pjlib_ioqueue_udp_test + * - \ref page_pjlib_ioqueue_perf_test + */ + + +/** + * This structure describes operation specific key to be submitted to + * I/O Queue when performing the asynchronous operation. This key will + * be returned to the application when completion callback is called. + * + * Application normally wants to attach it's specific data in the + * \c user_data field so that it can keep track of which operation has + * completed when the callback is called. Alternatively, application can + * also extend this struct to include its data, because the pointer that + * is returned in the completion callback will be exactly the same as + * the pointer supplied when the asynchronous function is called. + */ +typedef struct pj_ioqueue_op_key_t +{ + void *internal__[32]; /**< Internal I/O Queue data. */ + void *user_data; /**< Application data. */ +} pj_ioqueue_op_key_t; + +/** + * This structure describes the callbacks to be called when I/O operation + * completes. + */ +typedef struct pj_ioqueue_callback +{ + /** + * This callback is called when #pj_ioqueue_recv or #pj_ioqueue_recvfrom + * completes. + * + * @param key The key. + * @param op_key Operation key. + * @param bytes_read >= 0 to indicate the amount of data read, + * otherwise negative value containing the error + * code. To obtain the pj_status_t error code, use + * (pj_status_t code = -bytes_read). + */ + void (*on_read_complete)(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read); + + /** + * This callback is called when #pj_ioqueue_write or #pj_ioqueue_sendto + * completes. + * + * @param key The key. + * @param op_key Operation key. + * @param bytes_sent >= 0 to indicate the amount of data written, + * otherwise negative value containing the error + * code. To obtain the pj_status_t error code, use + * (pj_status_t code = -bytes_sent). + */ + void (*on_write_complete)(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_sent); + + /** + * This callback is called when #pj_ioqueue_accept completes. + * + * @param key The key. + * @param op_key Operation key. + * @param sock Newly connected socket. + * @param status Zero if the operation completes successfully. + */ + void (*on_accept_complete)(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t sock, + pj_status_t status); + + /** + * This callback is called when #pj_ioqueue_connect completes. + * + * @param key The key. + * @param status PJ_SUCCESS if the operation completes successfully. + */ + void (*on_connect_complete)(pj_ioqueue_key_t *key, + pj_status_t status); +} pj_ioqueue_callback; + + +/** + * Types of pending I/O Queue operation. This enumeration is only used + * internally within the ioqueue. + */ +typedef enum pj_ioqueue_operation_e +{ + PJ_IOQUEUE_OP_NONE = 0, /**< No operation. */ + PJ_IOQUEUE_OP_READ = 1, /**< read() operation. */ + PJ_IOQUEUE_OP_RECV = 2, /**< recv() operation. */ + PJ_IOQUEUE_OP_RECV_FROM = 4, /**< recvfrom() operation. */ + PJ_IOQUEUE_OP_WRITE = 8, /**< write() operation. */ + PJ_IOQUEUE_OP_SEND = 16, /**< send() operation. */ + PJ_IOQUEUE_OP_SEND_TO = 32, /**< sendto() operation. */ +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + PJ_IOQUEUE_OP_ACCEPT = 64, /**< accept() operation. */ + PJ_IOQUEUE_OP_CONNECT = 128, /**< connect() operation. */ +#endif /* PJ_HAS_TCP */ +} pj_ioqueue_operation_e; + + +/** + * This macro specifies the maximum number of events that can be + * processed by the ioqueue on a single poll cycle, on implementation + * that supports it. The value is only meaningfull when specified + * during PJLIB build. + */ +#ifndef PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL +# define PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL (16) +#endif + +/** + * When this flag is specified in ioqueue's recv() or send() operations, + * the ioqueue will always mark the operation as asynchronous. + */ +#define PJ_IOQUEUE_ALWAYS_ASYNC ((pj_uint32_t)1 << (pj_uint32_t)31) + +/** + * Return the name of the ioqueue implementation. + * + * @return Implementation name. + */ +PJ_DECL(const char*) pj_ioqueue_name(void); + + +/** + * Create a new I/O Queue framework. + * + * @param pool The pool to allocate the I/O queue structure. + * @param max_fd The maximum number of handles to be supported, which + * should not exceed PJ_IOQUEUE_MAX_HANDLES. + * @param ioqueue Pointer to hold the newly created I/O Queue. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **ioqueue); + +/** + * Destroy the I/O queue. + * + * @param ioque The I/O Queue to be destroyed. + * + * @return PJ_SUCCESS if success. + */ +PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque ); + +/** + * Set the lock object to be used by the I/O Queue. This function can only + * be called right after the I/O queue is created, before any handle is + * registered to the I/O queue. + * + * Initially the I/O queue is created with non-recursive mutex protection. + * Applications can supply alternative lock to be used by calling this + * function. + * + * @param ioque The ioqueue instance. + * @param lock The lock to be used by the ioqueue. + * @param auto_delete In non-zero, the lock will be deleted by the ioqueue. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ); + +/** + * Register a socket to the I/O queue framework. + * When a socket is registered to the IOQueue, it may be modified to use + * non-blocking IO. If it is modified, there is no guarantee that this + * modification will be restored after the socket is unregistered. + * + * @param pool To allocate the resource for the specified handle, + * which must be valid until the handle/key is unregistered + * from I/O Queue. + * @param ioque The I/O Queue. + * @param sock The socket. + * @param user_data User data to be associated with the key, which can be + * retrieved later. + * @param cb Callback to be called when I/O operation completes. + * @param key Pointer to receive the key to be associated with this + * socket. Subsequent I/O queue operation will need this + * key. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **key ); + +/** + * Unregister from the I/O Queue framework. Caller must make sure that + * the key doesn't have any pending operations before calling this function, + * by calling #pj_ioqueue_is_pending() for all previously submitted + * operations except asynchronous connect, and if necessary call + * #pj_ioqueue_post_completion() to cancel the pending operations. + * + * Note that asynchronous connect operation will automatically be + * cancelled during the unregistration. + * + * @param key The key that was previously obtained from registration. + * + * @return PJ_SUCCESS on success or the error code. + * + * @see pj_ioqueue_is_pending + */ +PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ); + + +/** + * Get user data associated with an ioqueue key. + * + * @param key The key that was previously obtained from registration. + * + * @return The user data associated with the descriptor, or NULL + * on error or if no data is associated with the key during + * registration. + */ +PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ); + +/** + * Set or change the user data to be associated with the file descriptor or + * handle or socket descriptor. + * + * @param key The key that was previously obtained from registration. + * @param user_data User data to be associated with the descriptor. + * @param old_data Optional parameter to retrieve the old user data. + * + * @return PJ_SUCCESS on success or the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, + void *user_data, + void **old_data); + + +/** + * Initialize operation key. + * + * @param op_key The operation key to be initialied. + * @param size The size of the operation key. + */ +PJ_DECL(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, + pj_size_t size ); + +/** + * Check if operation is pending on the specified operation key. + * The \c op_key must have been initialized with #pj_ioqueue_op_key_init() + * or submitted as pending operation before, or otherwise the result + * is undefined. + * + * @param key The key. + * @param op_key The operation key, previously submitted to any of + * the I/O functions and has returned PJ_EPENDING. + * + * @return Non-zero if operation is still pending. + */ +PJ_DECL(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key ); + + +/** + * Post completion status to the specified operation key and call the + * appropriate callback. When the callback is called, the number of bytes + * received in read/write callback or the status in accept/connect callback + * will be set from the \c bytes_status parameter. + * + * @param key The key. + * @param op_key Pending operation key. + * @param bytes_status Number of bytes or status to be set. A good value + * to put here is -PJ_ECANCELLED. + * + * @return PJ_SUCCESS if completion status has been successfully + * sent. + */ +PJ_DECL(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status ); + + + +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +/** + * Instruct I/O Queue to accept incoming connection on the specified + * listening socket. This function will return immediately (i.e. non-blocking) + * regardless whether a connection is immediately available. If the function + * can't complete immediately, the caller will be notified about the incoming + * connection when it calls pj_ioqueue_poll(). If a new connection is + * immediately available, the function returns PJ_SUCCESS with the new + * connection; in this case, the callback WILL NOT be called. + * + * @param key The key which registered to the server socket. + * @param op_key An operation specific key to be associated with the + * pending operation, so that application can keep track of + * which operation has been completed when the callback is + * called. + * @param new_sock Argument which contain pointer to receive the new socket + * for the incoming connection. + * @param local Optional argument which contain pointer to variable to + * receive local address. + * @param remote Optional argument which contain pointer to variable to + * receive the remote address. + * @param addrlen On input, contains the length of the buffer for the + * address, and on output, contains the actual length of the + * address. This argument is optional. + * @return + * - PJ_SUCCESS When connection is available immediately, and the + * parameters will be updated to contain information about + * the new connection. In this case, a completion callback + * WILL NOT be called. + * - PJ_EPENDING If no connection is available immediately. When a new + * connection arrives, the callback will be called. + * - non-zero which indicates the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t *sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen ); + +/** + * Initiate non-blocking socket connect. If the socket can NOT be connected + * immediately, asynchronous connect() will be scheduled and caller will be + * notified via completion callback when it calls pj_ioqueue_poll(). If + * socket is connected immediately, the function returns PJ_SUCCESS and + * completion callback WILL NOT be called. + * + * @param key The key associated with TCP socket + * @param addr The remote address. + * @param addrlen The remote address length. + * + * @return + * - PJ_SUCCESS If socket is connected immediately. In this case, the + * completion callback WILL NOT be called. + * - PJ_EPENDING If operation is queued, or + * - non-zero Indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ); + +#endif /* PJ_HAS_TCP */ + +/** + * Poll the I/O Queue for completed events. + * + * @param ioque the I/O Queue. + * @param timeout polling timeout, or NULL if the thread wishes to wait + * indefinetely for the event. + * + * @return + * - zero if timed out (no event). + * - (<0) if error occured during polling. Callback will NOT be called. + * - (>1) to indicate numbers of events. Callbacks have been called. + */ +PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, + const pj_time_val *timeout); + + +/** + * Instruct the I/O Queue to read from the specified handle. This function + * returns immediately (i.e. non-blocking) regardless whether some data has + * been transfered. If the operation can't complete immediately, caller will + * be notified about the completion when it calls pj_ioqueue_poll(). If data + * is immediately available, the function will return PJ_SUCCESS and the + * callback WILL NOT be called. + * + * @param key The key that uniquely identifies the handle. + * @param op_key An operation specific key to be associated with the + * pending operation, so that application can keep track of + * which operation has been completed when the callback is + * called. Caller must make sure that this key remains + * valid until the function completes. + * @param buffer The buffer to hold the read data. The caller MUST make sure + * that this buffer remain valid until the framework completes + * reading the handle. + * @param length On input, it specifies the size of the buffer. If data is + * available to be read immediately, the function returns + * PJ_SUCCESS and this argument will be filled with the + * amount of data read. If the function is pending, caller + * will be notified about the amount of data read in the + * callback. This parameter can point to local variable in + * caller's stack and doesn't have to remain valid for the + * duration of pending operation. + * @param flags Recv flag. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then + * the function will never return PJ_SUCCESS. + * + * @return + * - PJ_SUCCESS If immediate data has been received in the buffer. In this + * case, the callback WILL NOT be called. + * - PJ_EPENDING If the operation has been queued, and the callback will be + * called when data has been received. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags ); + +/** + * This function behaves similarly as #pj_ioqueue_recv(), except that it is + * normally called for socket, and the remote address will also be returned + * along with the data. Caller MUST make sure that both buffer and addr + * remain valid until the framework completes reading the data. + * + * @param key The key that uniquely identifies the handle. + * @param op_key An operation specific key to be associated with the + * pending operation, so that application can keep track of + * which operation has been completed when the callback is + * called. + * @param buffer The buffer to hold the read data. The caller MUST make sure + * that this buffer remain valid until the framework completes + * reading the handle. + * @param length On input, it specifies the size of the buffer. If data is + * available to be read immediately, the function returns + * PJ_SUCCESS and this argument will be filled with the + * amount of data read. If the function is pending, caller + * will be notified about the amount of data read in the + * callback. This parameter can point to local variable in + * caller's stack and doesn't have to remain valid for the + * duration of pending operation. + * @param flags Recv flag. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then + * the function will never return PJ_SUCCESS. + * @param addr Optional Pointer to buffer to receive the address. + * @param addrlen On input, specifies the length of the address buffer. + * On output, it will be filled with the actual length of + * the address. This argument can be NULL if \c addr is not + * specified. + * + * @return + * - PJ_SUCCESS If immediate data has been received. In this case, the + * callback must have been called before this function + * returns, and no pending operation is scheduled. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags, + pj_sockaddr_t *addr, + int *addrlen); + +/** + * Instruct the I/O Queue to write to the handle. This function will return + * immediately (i.e. non-blocking) regardless whether some data has been + * transfered. If the function can't complete immediately, the caller will + * be notified about the completion when it calls pj_ioqueue_poll(). If + * operation completes immediately and data has been transfered, the function + * returns PJ_SUCCESS and the callback will NOT be called. + * + * @param key The key that identifies the handle. + * @param op_key An operation specific key to be associated with the + * pending operation, so that application can keep track of + * which operation has been completed when the callback is + * called. + * @param data The data to send. Caller MUST make sure that this buffer + * remains valid until the write operation completes. + * @param length On input, it specifies the length of data to send. When + * data was sent immediately, this function returns PJ_SUCCESS + * and this parameter contains the length of data sent. If + * data can not be sent immediately, an asynchronous operation + * is scheduled and caller will be notified via callback the + * number of bytes sent. This parameter can point to local + * variable on caller's stack and doesn't have to remain + * valid until the operation has completed. + * @param flags Send flags. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then + * the function will never return PJ_SUCCESS. + * + * @return + * - PJ_SUCCESS If data was immediately transfered. In this case, no + * pending operation has been scheduled and the callback + * WILL NOT be called. + * - PJ_EPENDING If the operation has been queued. Once data base been + * transfered, the callback will be called. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags ); + + +/** + * Instruct the I/O Queue to write to the handle. This function will return + * immediately (i.e. non-blocking) regardless whether some data has been + * transfered. If the function can't complete immediately, the caller will + * be notified about the completion when it calls pj_ioqueue_poll(). If + * operation completes immediately and data has been transfered, the function + * returns PJ_SUCCESS and the callback will NOT be called. + * + * @param key the key that identifies the handle. + * @param op_key An operation specific key to be associated with the + * pending operation, so that application can keep track of + * which operation has been completed when the callback is + * called. + * @param data the data to send. Caller MUST make sure that this buffer + * remains valid until the write operation completes. + * @param length On input, it specifies the length of data to send. When + * data was sent immediately, this function returns PJ_SUCCESS + * and this parameter contains the length of data sent. If + * data can not be sent immediately, an asynchronous operation + * is scheduled and caller will be notified via callback the + * number of bytes sent. This parameter can point to local + * variable on caller's stack and doesn't have to remain + * valid until the operation has completed. + * @param flags send flags. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then + * the function will never return PJ_SUCCESS. + * @param addr Optional remote address. + * @param addrlen Remote address length, \c addr is specified. + * + * @return + * - PJ_SUCCESS If data was immediately written. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags, + const pj_sockaddr_t *addr, + int addrlen); + + +/** + * !} + */ + +PJ_END_DECL + +#endif /* __PJ_IOQUEUE_H__ */ + diff --git a/pjlib/include/pj/list.h b/pjlib/include/pj/list.h index 5aca6888..fc0223cc 100644 --- a/pjlib/include/pj/list.h +++ b/pjlib/include/pj/list.h @@ -1,236 +1,236 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_LIST_H__
-#define __PJ_LIST_H__
-
-/**
- * @file list.h
- * @brief Linked List data structure.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/*
- * @defgroup PJ_DS Data Structure.
- * @ingroup PJ
- */
-
-/**
- * @defgroup PJ_LIST Linked List
- * @ingroup PJ_DS
- * @{
- *
- * List in PJLIB is implemented as doubly-linked list, and it won't require
- * dynamic memory allocation (just as all PJLIB data structures). The list here
- * should be viewed more like a low level C list instead of high level C++ list
- * (which normally are easier to use but require dynamic memory allocations),
- * therefore all caveats with C list apply here too (such as you can NOT put
- * a node in more than one lists).
- *
- * \section pj_list_example_sec Examples
- *
- * See below for examples on how to manipulate linked list:
- * - @ref page_pjlib_samples_list_c
- * - @ref page_pjlib_list_test
- */
-
-
-/**
- * Use this macro in the start of the structure declaration to declare that
- * the structure can be used in the linked list operation. This macro simply
- * declares additional member @a prev and @a next to the structure.
- * @hideinitializer
- */
-#define PJ_DECL_LIST_MEMBER(type) \
- /** List @a prev. */ \
- type *prev; \
- /** List @a next. */ \
- type *next
-
-
-/**
- * This structure describes generic list node and list. The owner of this list
- * must initialize the 'value' member to an appropriate value (typically the
- * owner itself).
- */
-struct pj_list
-{
- PJ_DECL_LIST_MEMBER(void);
-};
-
-
-/**
- * Initialize the list.
- * Initially, the list will have no member, and function pj_list_empty() will
- * always return nonzero (which indicates TRUE) for the newly initialized
- * list.
- *
- * @param node The list head.
- */
-PJ_INLINE(void) pj_list_init(pj_list_type * node)
-{
- ((pj_list*)node)->next = ((pj_list*)node)->prev = node;
-}
-
-
-/**
- * Check that the list is empty.
- *
- * @param node The list head.
- *
- * @return Non-zero if the list is not-empty, or zero if it is empty.
- *
- */
-PJ_INLINE(int) pj_list_empty(const pj_list_type * node)
-{
- return ((pj_list*)node)->next == node;
-}
-
-
-/**
- * Insert the node to the list before the specified element position.
- *
- * @param pos The element to which the node will be inserted before.
- * @param node The element to be inserted.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node);
-
-
-/**
- * Inserts all nodes in \a nodes to the target list.
- *
- * @param lst The target list.
- * @param nodes Nodes list.
- */
-PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst,
- pj_list_type *nodes);
-
-/**
- * Insert a node to the list after the specified element position.
- *
- * @param pos The element in the list which will precede the inserted
- * element.
- * @param node The element to be inserted after the position element.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node);
-
-/**
- * Insert all nodes in \a nodes to the target list.
- *
- * @param lst The target list.
- * @param nodes Nodes list.
- */
-PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst,
- pj_list_type *nodes);
-
-
-/**
- * Remove elements from the source list, and insert them to the destination
- * list. The elements of the source list will occupy the
- * front elements of the target list. Note that the node pointed by \a list2
- * itself is not considered as a node, but rather as the list descriptor, so
- * it will not be inserted to the \a list1. The elements to be inserted starts
- * at \a list2->next. If \a list2 is to be included in the operation, use
- * \a pj_list_insert_nodes_before.
- *
- * @param list1 The destination list.
- * @param list2 The source list.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2);
-
-
-/**
- * Remove elements from the second list argument, and insert them to the list
- * in the first argument. The elements from the second list will be appended
- * to the first list. Note that the node pointed by \a list2
- * itself is not considered as a node, but rather as the list descriptor, so
- * it will not be inserted to the \a list1. The elements to be inserted starts
- * at \a list2->next. If \a list2 is to be included in the operation, use
- * \a pj_list_insert_nodes_before.
- *
- * @param list1 The element in the list which will precede the inserted
- * element.
- * @param list2 The element in the list to be inserted.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2);
-
-
-/**
- * Erase the node from the list it currently belongs.
- *
- * @param node The element to be erased.
- */
-PJ_IDECL(void) pj_list_erase(pj_list_type *node);
-
-
-/**
- * Find node in the list.
- *
- * @param list The list head.
- * @param node The node element to be searched.
- *
- * @return The node itself if it is found in the list, or NULL if it is not
- * found in the list.
- */
-PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list,
- pj_list_type *node);
-
-
-/**
- * Search the list for the specified value, using the specified comparison
- * function. This function iterates on nodes in the list, started with the
- * first node, and call the user supplied comparison function until the
- * comparison function returns ZERO.
- *
- * @param list The list head.
- * @param value The user defined value to be passed in the comparison
- * function
- * @param comp The comparison function, which should return ZERO to
- * indicate that the searched value is found.
- *
- * @return The first node that matched, or NULL if it is not found.
- */
-PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value,
- int (*comp)(void *value,
- const pj_list_type *node)
- );
-
-
-/**
- * @}
- */
-
-#if PJ_FUNCTIONS_ARE_INLINED
-# include "list_i.h"
-#endif
-
-PJ_END_DECL
-
-#endif /* __PJ_LIST_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_LIST_H__ +#define __PJ_LIST_H__ + +/** + * @file list.h + * @brief Linked List data structure. + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/* + * @defgroup PJ_DS Data Structure. + * @ingroup PJ + */ + +/** + * @defgroup PJ_LIST Linked List + * @ingroup PJ_DS + * @{ + * + * List in PJLIB is implemented as doubly-linked list, and it won't require + * dynamic memory allocation (just as all PJLIB data structures). The list here + * should be viewed more like a low level C list instead of high level C++ list + * (which normally are easier to use but require dynamic memory allocations), + * therefore all caveats with C list apply here too (such as you can NOT put + * a node in more than one lists). + * + * \section pj_list_example_sec Examples + * + * See below for examples on how to manipulate linked list: + * - @ref page_pjlib_samples_list_c + * - @ref page_pjlib_list_test + */ + + +/** + * Use this macro in the start of the structure declaration to declare that + * the structure can be used in the linked list operation. This macro simply + * declares additional member @a prev and @a next to the structure. + * @hideinitializer + */ +#define PJ_DECL_LIST_MEMBER(type) \ + /** List @a prev. */ \ + type *prev; \ + /** List @a next. */ \ + type *next + + +/** + * This structure describes generic list node and list. The owner of this list + * must initialize the 'value' member to an appropriate value (typically the + * owner itself). + */ +struct pj_list +{ + PJ_DECL_LIST_MEMBER(void); +}; + + +/** + * Initialize the list. + * Initially, the list will have no member, and function pj_list_empty() will + * always return nonzero (which indicates TRUE) for the newly initialized + * list. + * + * @param node The list head. + */ +PJ_INLINE(void) pj_list_init(pj_list_type * node) +{ + ((pj_list*)node)->next = ((pj_list*)node)->prev = node; +} + + +/** + * Check that the list is empty. + * + * @param node The list head. + * + * @return Non-zero if the list is not-empty, or zero if it is empty. + * + */ +PJ_INLINE(int) pj_list_empty(const pj_list_type * node) +{ + return ((pj_list*)node)->next == node; +} + + +/** + * Insert the node to the list before the specified element position. + * + * @param pos The element to which the node will be inserted before. + * @param node The element to be inserted. + * + * @return void. + */ +PJ_IDECL(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node); + + +/** + * Inserts all nodes in \a nodes to the target list. + * + * @param lst The target list. + * @param nodes Nodes list. + */ +PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst, + pj_list_type *nodes); + +/** + * Insert a node to the list after the specified element position. + * + * @param pos The element in the list which will precede the inserted + * element. + * @param node The element to be inserted after the position element. + * + * @return void. + */ +PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node); + +/** + * Insert all nodes in \a nodes to the target list. + * + * @param lst The target list. + * @param nodes Nodes list. + */ +PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst, + pj_list_type *nodes); + + +/** + * Remove elements from the source list, and insert them to the destination + * list. The elements of the source list will occupy the + * front elements of the target list. Note that the node pointed by \a list2 + * itself is not considered as a node, but rather as the list descriptor, so + * it will not be inserted to the \a list1. The elements to be inserted starts + * at \a list2->next. If \a list2 is to be included in the operation, use + * \a pj_list_insert_nodes_before. + * + * @param list1 The destination list. + * @param list2 The source list. + * + * @return void. + */ +PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2); + + +/** + * Remove elements from the second list argument, and insert them to the list + * in the first argument. The elements from the second list will be appended + * to the first list. Note that the node pointed by \a list2 + * itself is not considered as a node, but rather as the list descriptor, so + * it will not be inserted to the \a list1. The elements to be inserted starts + * at \a list2->next. If \a list2 is to be included in the operation, use + * \a pj_list_insert_nodes_before. + * + * @param list1 The element in the list which will precede the inserted + * element. + * @param list2 The element in the list to be inserted. + * + * @return void. + */ +PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2); + + +/** + * Erase the node from the list it currently belongs. + * + * @param node The element to be erased. + */ +PJ_IDECL(void) pj_list_erase(pj_list_type *node); + + +/** + * Find node in the list. + * + * @param list The list head. + * @param node The node element to be searched. + * + * @return The node itself if it is found in the list, or NULL if it is not + * found in the list. + */ +PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list, + pj_list_type *node); + + +/** + * Search the list for the specified value, using the specified comparison + * function. This function iterates on nodes in the list, started with the + * first node, and call the user supplied comparison function until the + * comparison function returns ZERO. + * + * @param list The list head. + * @param value The user defined value to be passed in the comparison + * function + * @param comp The comparison function, which should return ZERO to + * indicate that the searched value is found. + * + * @return The first node that matched, or NULL if it is not found. + */ +PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value, + int (*comp)(void *value, + const pj_list_type *node) + ); + + +/** + * @} + */ + +#if PJ_FUNCTIONS_ARE_INLINED +# include "list_i.h" +#endif + +PJ_END_DECL + +#endif /* __PJ_LIST_H__ */ + diff --git a/pjlib/include/pj/list_i.h b/pjlib/include/pj/list_i.h index 1ad0c08e..c6d95c80 100644 --- a/pjlib/include/pj/list_i.h +++ b/pjlib/include/pj/list_i.h @@ -1,118 +1,118 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-/* Internal */
-PJ_IDEF(void) pj_link_node(pj_list_type *prev, pj_list_type *next)
-{
- ((pj_list*)prev)->next = next;
- ((pj_list*)next)->prev = prev;
-}
-
-/*
-PJ_IDEF(void)
-pj_list_init(pj_list_type * node)
-{
- ((pj_list*)node)->next = ((pj_list*)node)->prev = node;
-}
-
-PJ_IDEF(int) pj_list_empty(const pj_list_type * node)
-{
- return ((pj_list*)node)->next == node;
-}
-*/
-
-PJ_IDEF(void)
-pj_list_insert_after(pj_list_type *pos, pj_list_type *node)
-{
- ((pj_list*)node)->prev = pos;
- ((pj_list*)node)->next = ((pj_list*)pos)->next;
- ((pj_list*) ((pj_list*)pos)->next) ->prev = node;
- ((pj_list*)pos)->next = node;
-}
-
-
-PJ_IDEF(void)
-pj_list_insert_before(pj_list_type *pos, pj_list_type *node)
-{
- pj_list_insert_after(((pj_list*)pos)->prev, node);
-}
-
-
-PJ_IDEF(void)
-pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst)
-{
- pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev;
- pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next;
-
- pj_link_node(pos, lst);
- pj_link_node(lst_last, pos_next);
-}
-
-PJ_IDEF(void)
-pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst)
-{
- pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst);
-}
-
-PJ_IDEF(void)
-pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2)
-{
- pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next);
- pj_link_node(((pj_list*)lst2)->prev, lst1);
- pj_list_init(lst2);
-}
-
-PJ_IDEF(void)
-pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2)
-{
- pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next);
- pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next);
- pj_list_init(lst2);
-}
-
-PJ_IDEF(void)
-pj_list_erase(pj_list_type *node)
-{
- pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next);
-}
-
-
-PJ_IDEF(pj_list_type*)
-pj_list_find_node(pj_list_type *list, pj_list_type *node)
-{
- pj_list *p = (pj_list *) ((pj_list*)list)->next;
- while (p != list && p != node)
- p = (pj_list *) p->next;
-
- return p==node ? p : NULL;
-}
-
-
-PJ_IDEF(pj_list_type*)
-pj_list_search(pj_list_type *list, void *value,
- int (*comp)(void *value, const pj_list_type *node))
-{
- pj_list *p = (pj_list *) ((pj_list*)list)->next;
- while (p != list && (*comp)(value, p) != 0)
- p = (pj_list *) p->next;
-
- return p==list ? NULL : p;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Internal */ +PJ_IDEF(void) pj_link_node(pj_list_type *prev, pj_list_type *next) +{ + ((pj_list*)prev)->next = next; + ((pj_list*)next)->prev = prev; +} + +/* +PJ_IDEF(void) +pj_list_init(pj_list_type * node) +{ + ((pj_list*)node)->next = ((pj_list*)node)->prev = node; +} + +PJ_IDEF(int) pj_list_empty(const pj_list_type * node) +{ + return ((pj_list*)node)->next == node; +} +*/ + +PJ_IDEF(void) +pj_list_insert_after(pj_list_type *pos, pj_list_type *node) +{ + ((pj_list*)node)->prev = pos; + ((pj_list*)node)->next = ((pj_list*)pos)->next; + ((pj_list*) ((pj_list*)pos)->next) ->prev = node; + ((pj_list*)pos)->next = node; +} + + +PJ_IDEF(void) +pj_list_insert_before(pj_list_type *pos, pj_list_type *node) +{ + pj_list_insert_after(((pj_list*)pos)->prev, node); +} + + +PJ_IDEF(void) +pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst) +{ + pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev; + pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next; + + pj_link_node(pos, lst); + pj_link_node(lst_last, pos_next); +} + +PJ_IDEF(void) +pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst) +{ + pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst); +} + +PJ_IDEF(void) +pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2) +{ + pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next); + pj_link_node(((pj_list*)lst2)->prev, lst1); + pj_list_init(lst2); +} + +PJ_IDEF(void) +pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2) +{ + pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next); + pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next); + pj_list_init(lst2); +} + +PJ_IDEF(void) +pj_list_erase(pj_list_type *node) +{ + pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next); +} + + +PJ_IDEF(pj_list_type*) +pj_list_find_node(pj_list_type *list, pj_list_type *node) +{ + pj_list *p = (pj_list *) ((pj_list*)list)->next; + while (p != list && p != node) + p = (pj_list *) p->next; + + return p==node ? p : NULL; +} + + +PJ_IDEF(pj_list_type*) +pj_list_search(pj_list_type *list, void *value, + int (*comp)(void *value, const pj_list_type *node)) +{ + pj_list *p = (pj_list *) ((pj_list*)list)->next; + while (p != list && (*comp)(value, p) != 0) + p = (pj_list *) p->next; + + return p==list ? NULL : p; +} + diff --git a/pjlib/include/pj/lock.h b/pjlib/include/pj/lock.h index 4eb50af1..96c9e11d 100644 --- a/pjlib/include/pj/lock.h +++ b/pjlib/include/pj/lock.h @@ -1,153 +1,153 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_LOCK_H__
-#define __PJ_LOCK_H__
-
-/**
- * @file lock.h
- * @brief Higher abstraction for locking objects.
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_LOCK Lock Objects
- * @ingroup PJ_OS
- * @{
- *
- * <b>Lock Objects</b> are higher abstraction for different lock mechanisms.
- * It offers the same API for manipulating different lock types (e.g.
- * @ref PJ_MUTEX "mutex", @ref PJ_SEM "semaphores", or null locks).
- * Because Lock Objects have the same API for different types of lock
- * implementation, it can be passed around in function arguments. As the
- * result, it can be used to control locking policy for a particular
- * feature.
- */
-
-
-/**
- * Create simple, non recursive mutex lock object.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock );
-
-/**
- * Create recursive mutex lock object.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock );
-
-
-/**
- * Create NULL mutex. A NULL mutex doesn't actually have any synchronization
- * object attached to it.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock );
-
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-/**
- * Create semaphore lock object.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param initial Initial value of the semaphore.
- * @param max Maximum value of the semaphore.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_lock_t **lock );
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-/**
- * Acquire lock on the specified lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_acquire( pj_lock_t *lock );
-
-
-/**
- * Try to acquire lock on the specified lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock );
-
-
-/**
- * Release lock on the specified lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_release( pj_lock_t *lock );
-
-
-/**
- * Destroy the lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock );
-
-
-/** @} */
-
-PJ_END_DECL
-
-
-#endif /* __PJ_LOCK_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_LOCK_H__ +#define __PJ_LOCK_H__ + +/** + * @file lock.h + * @brief Higher abstraction for locking objects. + */ +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_LOCK Lock Objects + * @ingroup PJ_OS + * @{ + * + * <b>Lock Objects</b> are higher abstraction for different lock mechanisms. + * It offers the same API for manipulating different lock types (e.g. + * @ref PJ_MUTEX "mutex", @ref PJ_SEM "semaphores", or null locks). + * Because Lock Objects have the same API for different types of lock + * implementation, it can be passed around in function arguments. As the + * result, it can be used to control locking policy for a particular + * feature. + */ + + +/** + * Create simple, non recursive mutex lock object. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ); + +/** + * Create recursive mutex lock object. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ); + + +/** + * Create NULL mutex. A NULL mutex doesn't actually have any synchronization + * object attached to it. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ); + + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +/** + * Create semaphore lock object. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param initial Initial value of the semaphore. + * @param max Maximum value of the semaphore. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_lock_t **lock ); + +#endif /* PJ_HAS_SEMAPHORE */ + +/** + * Acquire lock on the specified lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_acquire( pj_lock_t *lock ); + + +/** + * Try to acquire lock on the specified lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ); + + +/** + * Release lock on the specified lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_release( pj_lock_t *lock ); + + +/** + * Destroy the lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock ); + + +/** @} */ + +PJ_END_DECL + + +#endif /* __PJ_LOCK_H__ */ + diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h index 3f192da7..4d8dc14a 100644 --- a/pjlib/include/pj/log.h +++ b/pjlib/include/pj/log.h @@ -1,331 +1,331 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_LOG_H__
-#define __PJ_LOG_H__
-
-/**
- * @file log.h
- * @brief Logging Utility.
- */
-
-#include <pj/types.h>
-#include <stdarg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_MISC Miscelaneous
- * @ingroup PJ
- */
-
-/**
- * @defgroup PJ_LOG Logging Facility
- * @ingroup PJ_MISC
- * @{
- *
- * The PJLIB logging facility is a configurable, flexible, and convenient
- * way to write logging or trace information.
- *
- * To write to the log, one uses construct like below:
- *
- * <pre>
- * ...
- * PJ_LOG(3, ("main.c", "Starting hello..."));
- * ...
- * PJ_LOG(3, ("main.c", "Hello world from process %d", pj_getpid()));
- * ...
- * </pre>
- *
- * In the above example, the number @b 3 controls the verbosity level of
- * the information (which means "information", by convention). The string
- * "main.c" specifies the source or sender of the message.
- *
- *
- * \section pj_log_quick_sample_sec Examples
- *
- * For examples, see:
- * - @ref page_pjlib_samples_log_c.
- *
- */
-
-/**
- * Log decoration flag, to be specified with #pj_log_set_decor().
- */
-enum pj_log_decoration
-{
- PJ_LOG_HAS_DAY_NAME = 1, /**< Include day name [default: no]. */
- PJ_LOG_HAS_YEAR = 2, /**< Include year digit [default: no] */
- PJ_LOG_HAS_MONTH = 4, /**< Include month [default: no] */
- PJ_LOG_HAS_DAY_OF_MON = 8, /**< Include day of month [default: no] */
- PJ_LOG_HAS_TIME = 16, /**< Include time [default: yes]. */
- PJ_LOG_HAS_MICRO_SEC = 32, /**< Include microseconds [yes] */
- PJ_LOG_HAS_SENDER = 64, /**< Include sender in the log [yes]. */
- PJ_LOG_HAS_NEWLINE = 128, /**< Terminate each call with newline [yes].*/
-};
-
-/**
- * Write log message.
- * This is the main macro used to write text to the logging backend.
- *
- * @param level The logging verbosity level. Lower number indicates higher
- * importance, with level zero indicates fatal error. Only
- * numeral argument is permitted (e.g. not variable).
- * @param arg Enclosed 'printf' like arguments, with the first
- * argument is the sender, the second argument is format
- * string and the following arguments are variable number of
- * arguments suitable for the format string.
- *
- * Sample:
- * \verbatim
- PJ_LOG(2, (__FILE__, "current value is %d", value));
- \endverbatim
- * @hideinitializer
- */
-#define PJ_LOG(level,arg) pj_log_wrapper_##level(arg)
-
-/**
- * Signature for function to be registered to the logging subsystem to
- * write the actual log message to some output device.
- *
- * @param level Log level.
- * @param data Log message.
- * @param len Message length.
- */
-typedef void pj_log_func(int level, const char *data, int len);
-
-/**
- * Default logging writer function used by front end logger function.
- * Application normally should NOT need to call this function, but
- * rather use the PJ_LOG macro.
- *
- * @param level Log level.
- * @param buffer Log message.
- * @param len Message length.
- */
-PJ_DECL(void) pj_log_write(int level, const char *buffer, int len);
-
-
-#if PJ_LOG_MAX_LEVEL >= 1
-
-/**
- * Write to log.
- *
- * @param sender Source of the message.
- * @param level Verbosity level.
- * @param format Format.
- * @param marker Marker.
- */
-PJ_DECL(void) pj_log(const char *sender, int level,
- const char *format, va_list marker);
-
-/**
- * Change log output function. The front-end logging functions will call
- * this function to write the actual message to the desired device.
- * By default, the front-end functions use pj_log_write() to write
- * the messages, unless it's changed by calling this function.
- *
- * @param func The function that will be called to write the log
- * messages to the desired device.
- */
-PJ_DECL(void) pj_log_set_log_func( pj_log_func *func );
-
-/**
- * Get the current log output function that is used to write log messages.
- *
- * @return Current log output function.
- */
-PJ_DECL(pj_log_func*) pj_log_get_log_func(void);
-
-/**
- * Set maximum log level. Application can call this function to set
- * the desired level of verbosity of the logging messages. The bigger the
- * value, the more verbose the logging messages will be printed. However,
- * the maximum level of verbosity can not exceed compile time value of
- * PJ_LOG_MAX_LEVEL.
- *
- * @param level The maximum level of verbosity of the logging
- * messages (6=very detailed..1=error only, 0=disabled)
- */
-PJ_DECL(void) pj_log_set_level(int level);
-
-/**
- * Get current maximum log verbositylevel.
- *
- * @return Current log maximum level.
- */
-PJ_DECL(int) pj_log_get_level(void);
-
-/**
- * Set log decoration. The log decoration flag controls what are printed
- * to output device alongside the actual message. For example, application
- * can specify that date/time information should be displayed with each
- * log message.
- *
- * @param decor Bitmask combination of #pj_log_decoration to control
- * the layout of the log message.
- */
-PJ_DECL(void) pj_log_set_decor(unsigned decor);
-
-/**
- * Get current log decoration flag.
- *
- * @return Log decoration flag.
- */
-PJ_DECL(unsigned) pj_log_get_decor(void);
-
-
-#else /* #if PJ_LOG_MAX_LEVEL >= 1 */
-
-/**
- * Change log output function. The front-end logging functions will call
- * this function to write the actual message to the desired device.
- * By default, the front-end functions use pj_log_write() to write
- * the messages, unless it's changed by calling this function.
- *
- * @param func The function that will be called to write the log
- * messages to the desired device.
- */
-# define pj_log_set_log_func(func)
-
-/**
- * Set maximum log level. Application can call this function to set
- * the desired level of verbosity of the logging messages. The bigger the
- * value, the more verbose the logging messages will be printed. However,
- * the maximum level of verbosity can not exceed compile time value of
- * PJ_LOG_MAX_LEVEL.
- *
- * @param level The maximum level of verbosity of the logging
- * messages (6=very detailed..1=error only, 0=disabled)
- */
-# define pj_log_set_level(level)
-
-/**
- * Set log decoration. The log decoration flag controls what are printed
- * to output device alongside the actual message. For example, application
- * can specify that date/time information should be displayed with each
- * log message.
- *
- * @param decor Bitmask combination of #pj_log_decoration to control
- * the layout of the log message.
- */
-# define pj_log_set_decor(decor)
-
-#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Log functions implementation prototypes.
- * These functions are called by PJ_LOG macros according to verbosity
- * level specified when calling the macro. Applications should not normally
- * need to call these functions directly.
- */
-
-/**
- * @def pj_log_wrapper_1(arg)
- * Internal function to write log with verbosity 1. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 1.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 1
- #define pj_log_wrapper_1(arg) pj_log_1 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_1(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_1(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_2(arg)
- * Internal function to write log with verbosity 2. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 2.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 2
- #define pj_log_wrapper_2(arg) pj_log_2 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_2(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_2(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_3(arg)
- * Internal function to write log with verbosity 3. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 3.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 3
- #define pj_log_wrapper_3(arg) pj_log_3 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_3(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_3(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_4(arg)
- * Internal function to write log with verbosity 4. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 4.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 4
- #define pj_log_wrapper_4(arg) pj_log_4 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_4(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_4(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_5(arg)
- * Internal function to write log with verbosity 5. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 5.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 5
- #define pj_log_wrapper_5(arg) pj_log_5 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_5(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_5(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_6(arg)
- * Internal function to write log with verbosity 6. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 6.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 6
- #define pj_log_wrapper_6(arg) pj_log_6 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_6(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_6(arg)
-#endif
-
-
-PJ_END_DECL
-
-#endif /* __PJ_LOG_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_LOG_H__ +#define __PJ_LOG_H__ + +/** + * @file log.h + * @brief Logging Utility. + */ + +#include <pj/types.h> +#include <stdarg.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_MISC Miscelaneous + * @ingroup PJ + */ + +/** + * @defgroup PJ_LOG Logging Facility + * @ingroup PJ_MISC + * @{ + * + * The PJLIB logging facility is a configurable, flexible, and convenient + * way to write logging or trace information. + * + * To write to the log, one uses construct like below: + * + * <pre> + * ... + * PJ_LOG(3, ("main.c", "Starting hello...")); + * ... + * PJ_LOG(3, ("main.c", "Hello world from process %d", pj_getpid())); + * ... + * </pre> + * + * In the above example, the number @b 3 controls the verbosity level of + * the information (which means "information", by convention). The string + * "main.c" specifies the source or sender of the message. + * + * + * \section pj_log_quick_sample_sec Examples + * + * For examples, see: + * - @ref page_pjlib_samples_log_c. + * + */ + +/** + * Log decoration flag, to be specified with #pj_log_set_decor(). + */ +enum pj_log_decoration +{ + PJ_LOG_HAS_DAY_NAME = 1, /**< Include day name [default: no]. */ + PJ_LOG_HAS_YEAR = 2, /**< Include year digit [default: no] */ + PJ_LOG_HAS_MONTH = 4, /**< Include month [default: no] */ + PJ_LOG_HAS_DAY_OF_MON = 8, /**< Include day of month [default: no] */ + PJ_LOG_HAS_TIME = 16, /**< Include time [default: yes]. */ + PJ_LOG_HAS_MICRO_SEC = 32, /**< Include microseconds [yes] */ + PJ_LOG_HAS_SENDER = 64, /**< Include sender in the log [yes]. */ + PJ_LOG_HAS_NEWLINE = 128, /**< Terminate each call with newline [yes].*/ +}; + +/** + * Write log message. + * This is the main macro used to write text to the logging backend. + * + * @param level The logging verbosity level. Lower number indicates higher + * importance, with level zero indicates fatal error. Only + * numeral argument is permitted (e.g. not variable). + * @param arg Enclosed 'printf' like arguments, with the first + * argument is the sender, the second argument is format + * string and the following arguments are variable number of + * arguments suitable for the format string. + * + * Sample: + * \verbatim + PJ_LOG(2, (__FILE__, "current value is %d", value)); + \endverbatim + * @hideinitializer + */ +#define PJ_LOG(level,arg) pj_log_wrapper_##level(arg) + +/** + * Signature for function to be registered to the logging subsystem to + * write the actual log message to some output device. + * + * @param level Log level. + * @param data Log message. + * @param len Message length. + */ +typedef void pj_log_func(int level, const char *data, int len); + +/** + * Default logging writer function used by front end logger function. + * Application normally should NOT need to call this function, but + * rather use the PJ_LOG macro. + * + * @param level Log level. + * @param buffer Log message. + * @param len Message length. + */ +PJ_DECL(void) pj_log_write(int level, const char *buffer, int len); + + +#if PJ_LOG_MAX_LEVEL >= 1 + +/** + * Write to log. + * + * @param sender Source of the message. + * @param level Verbosity level. + * @param format Format. + * @param marker Marker. + */ +PJ_DECL(void) pj_log(const char *sender, int level, + const char *format, va_list marker); + +/** + * Change log output function. The front-end logging functions will call + * this function to write the actual message to the desired device. + * By default, the front-end functions use pj_log_write() to write + * the messages, unless it's changed by calling this function. + * + * @param func The function that will be called to write the log + * messages to the desired device. + */ +PJ_DECL(void) pj_log_set_log_func( pj_log_func *func ); + +/** + * Get the current log output function that is used to write log messages. + * + * @return Current log output function. + */ +PJ_DECL(pj_log_func*) pj_log_get_log_func(void); + +/** + * Set maximum log level. Application can call this function to set + * the desired level of verbosity of the logging messages. The bigger the + * value, the more verbose the logging messages will be printed. However, + * the maximum level of verbosity can not exceed compile time value of + * PJ_LOG_MAX_LEVEL. + * + * @param level The maximum level of verbosity of the logging + * messages (6=very detailed..1=error only, 0=disabled) + */ +PJ_DECL(void) pj_log_set_level(int level); + +/** + * Get current maximum log verbositylevel. + * + * @return Current log maximum level. + */ +PJ_DECL(int) pj_log_get_level(void); + +/** + * Set log decoration. The log decoration flag controls what are printed + * to output device alongside the actual message. For example, application + * can specify that date/time information should be displayed with each + * log message. + * + * @param decor Bitmask combination of #pj_log_decoration to control + * the layout of the log message. + */ +PJ_DECL(void) pj_log_set_decor(unsigned decor); + +/** + * Get current log decoration flag. + * + * @return Log decoration flag. + */ +PJ_DECL(unsigned) pj_log_get_decor(void); + + +#else /* #if PJ_LOG_MAX_LEVEL >= 1 */ + +/** + * Change log output function. The front-end logging functions will call + * this function to write the actual message to the desired device. + * By default, the front-end functions use pj_log_write() to write + * the messages, unless it's changed by calling this function. + * + * @param func The function that will be called to write the log + * messages to the desired device. + */ +# define pj_log_set_log_func(func) + +/** + * Set maximum log level. Application can call this function to set + * the desired level of verbosity of the logging messages. The bigger the + * value, the more verbose the logging messages will be printed. However, + * the maximum level of verbosity can not exceed compile time value of + * PJ_LOG_MAX_LEVEL. + * + * @param level The maximum level of verbosity of the logging + * messages (6=very detailed..1=error only, 0=disabled) + */ +# define pj_log_set_level(level) + +/** + * Set log decoration. The log decoration flag controls what are printed + * to output device alongside the actual message. For example, application + * can specify that date/time information should be displayed with each + * log message. + * + * @param decor Bitmask combination of #pj_log_decoration to control + * the layout of the log message. + */ +# define pj_log_set_decor(decor) + +#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */ + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * Log functions implementation prototypes. + * These functions are called by PJ_LOG macros according to verbosity + * level specified when calling the macro. Applications should not normally + * need to call these functions directly. + */ + +/** + * @def pj_log_wrapper_1(arg) + * Internal function to write log with verbosity 1. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 1. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 1 + #define pj_log_wrapper_1(arg) pj_log_1 arg + /** Internal function. */ + PJ_DECL(void) pj_log_1(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_1(arg) +#endif + +/** + * @def pj_log_wrapper_2(arg) + * Internal function to write log with verbosity 2. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 2. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 2 + #define pj_log_wrapper_2(arg) pj_log_2 arg + /** Internal function. */ + PJ_DECL(void) pj_log_2(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_2(arg) +#endif + +/** + * @def pj_log_wrapper_3(arg) + * Internal function to write log with verbosity 3. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 3. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 3 + #define pj_log_wrapper_3(arg) pj_log_3 arg + /** Internal function. */ + PJ_DECL(void) pj_log_3(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_3(arg) +#endif + +/** + * @def pj_log_wrapper_4(arg) + * Internal function to write log with verbosity 4. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 4. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 4 + #define pj_log_wrapper_4(arg) pj_log_4 arg + /** Internal function. */ + PJ_DECL(void) pj_log_4(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_4(arg) +#endif + +/** + * @def pj_log_wrapper_5(arg) + * Internal function to write log with verbosity 5. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 5. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 5 + #define pj_log_wrapper_5(arg) pj_log_5 arg + /** Internal function. */ + PJ_DECL(void) pj_log_5(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_5(arg) +#endif + +/** + * @def pj_log_wrapper_6(arg) + * Internal function to write log with verbosity 6. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 6. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 6 + #define pj_log_wrapper_6(arg) pj_log_6 arg + /** Internal function. */ + PJ_DECL(void) pj_log_6(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_6(arg) +#endif + + +PJ_END_DECL + +#endif /* __PJ_LOG_H__ */ + diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h index edd5d1d4..7b03c968 100644 --- a/pjlib/include/pj/os.h +++ b/pjlib/include/pj/os.h @@ -1,995 +1,995 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_OS_H__
-#define __PJ_OS_H__
-
-/**
- * @file os.h
- * @brief OS dependent functions
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_OS Operating System Dependent Functionality.
- * @ingroup PJ
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_THREAD Threads
- * @ingroup PJ_OS
- * @{
- * This module provides multithreading API.
- *
- * \section pj_thread_examples_sec Examples
- *
- * For examples, please see:
- * - \ref page_pjlib_thread_test
- * - \ref page_pjlib_sleep_test
- *
- */
-
-/**
- * Thread creation flags:
- * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended.
- */
-typedef enum pj_thread_create_flags
-{
- PJ_THREAD_SUSPENDED = 1
-} pj_thread_create_flags;
-
-
-/**
- * Specify this as \a stack_size argument in #pj_thread_create() to specify
- * that thread should use default stack size for the current platform.
- */
-#define PJ_THREAD_DEFAULT_STACK_SIZE 0
-
-/**
- * Type of thread entry function.
- */
-typedef int (PJ_THREAD_FUNC pj_thread_proc)(void*);
-
-/**
- * Size of thread struct.
- */
-#if !defined(PJ_THREAD_DESC_SIZE)
-# define PJ_THREAD_DESC_SIZE (16)
-#endif
-
-/**
- * Thread structure, to thread's state when the thread is created by external
- * or native API.
- */
-typedef long pj_thread_desc[PJ_THREAD_DESC_SIZE];
-
-/**
- * Get process ID.
- * @return process ID.
- */
-PJ_DECL(pj_uint32_t) pj_getpid(void);
-
-/**
- * Create a new thread.
- *
- * @param pool The memory pool from which the thread record
- * will be allocated from.
- * @param thread_name The optional name to be assigned to the thread.
- * @param proc Thread entry function.
- * @param arg Argument to be passed to the thread entry function.
- * @param stack_size The size of the stack for the new thread, or ZERO or
- * PJ_THREAD_DEFAULT_STACK_SIZE to let the
- * library choose the reasonable size for the stack.
- * For some systems, the stack will be allocated from
- * the pool, so the pool must have suitable capacity.
- * @param flags Flags for thread creation, which is bitmask combination
- * from enum pj_thread_create_flags.
- * @param thread Pointer to hold the newly created thread.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_thread_create( pj_pool_t *pool,
- const char *thread_name,
- pj_thread_proc *proc,
- void *arg,
- pj_size_t stack_size,
- unsigned flags,
- pj_thread_t **thread );
-
-/**
- * Register a thread that was created by external or native API to PJLIB.
- * This function must be called in the context of the thread being registered.
- * When the thread is created by external function or API call,
- * it must be 'registered' to PJLIB using pj_thread_register(), so that it can
- * cooperate with PJLIB's framework. During registration, some data needs to
- * be maintained, and this data must remain available during the thread's
- * lifetime.
- *
- * @param thread_name The optional name to be assigned to the thread.
- * @param desc Thread descriptor, which must be available throughout
- * the lifetime of the thread.
- * @param thread Pointer to hold the created thread handle.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_thread_register ( const char *thread_name,
- pj_thread_desc desc,
- pj_thread_t **thread);
-
-/**
- * Get thread name.
- *
- * @param thread The thread handle.
- *
- * @return Thread name as null terminated string.
- */
-PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread);
-
-/**
- * Resume a suspended thread.
- *
- * @param thread The thread handle.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread);
-
-/**
- * Get the current thread.
- *
- * @return Thread handle of current thread.
- */
-PJ_DECL(pj_thread_t*) pj_thread_this(void);
-
-/**
- * Join thread.
- * This function will block the caller thread until the specified thread exits.
- *
- * @param thread The thread handle.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread);
-
-
-/**
- * Destroy thread and release resources allocated for the thread.
- * However, the memory allocated for the pj_thread_t itself will only be released
- * when the pool used to create the thread is destroyed.
- *
- * @param thread The thread handle.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread);
-
-
-/**
- * Put the current thread to sleep for the specified miliseconds.
- *
- * @param msec Miliseconds delay.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec);
-
-/**
- * @def PJ_CHECK_STACK()
- * PJ_CHECK_STACK() macro is used to check the sanity of the stack.
- * The OS implementation may check that no stack overflow occurs, and
- * it also may collect statistic about stack usage.
- */
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
-
-# define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__)
-
-/** @internal
- * The implementation of stack checking.
- */
-PJ_DECL(void) pj_thread_check_stack(const char *file, int line);
-
-/** @internal
- * Get maximum stack usage statistic.
- */
-PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread);
-
-/** @internal
- * Dump thread stack status.
- */
-PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread,
- const char **file,
- int *line);
-#else
-
-# define PJ_CHECK_STACK()
-/** pj_thread_get_stack_max_usage() for the thread */
-# define pj_thread_get_stack_max_usage(thread) 0
-/** pj_thread_get_stack_info() for the thread */
-# define pj_thread_get_stack_info(thread,f,l) (*(f)="",*(l)=0)
-#endif /* PJ_OS_HAS_CHECK_STACK */
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_TLS Thread Local Storage.
- * @ingroup PJ_OS
- * @{
- */
-
-/**
- * Allocate thread local storage index. The initial value of the variable at
- * the index is zero.
- *
- * @param index Pointer to hold the return value.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index);
-
-/**
- * Deallocate thread local variable.
- *
- * @param index The variable index.
- */
-PJ_DECL(void) pj_thread_local_free(long index);
-
-/**
- * Set the value of thread local variable.
- *
- * @param index The index of the variable.
- * @param value The value.
- */
-PJ_DECL(pj_status_t) pj_thread_local_set(long index, void *value);
-
-/**
- * Get the value of thread local variable.
- *
- * @param index The index of the variable.
- * @return The value.
- */
-PJ_DECL(void*) pj_thread_local_get(long index);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_ATOMIC Atomic Variables
- * @ingroup PJ_OS
- * @{
- *
- * This module provides API to manipulate atomic variables.
- *
- * \section pj_atomic_examples_sec Examples
- *
- * For some example codes, please see:
- * - @ref page_pjlib_atomic_test
- */
-
-
-/**
- * Create atomic variable.
- *
- * @param pool The pool.
- * @param initial The initial value of the atomic variable.
- * @param atomic Pointer to hold the atomic variable upon return.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t initial,
- pj_atomic_t **atomic );
-
-/**
- * Destroy atomic variable.
- *
- * @param atomic_var the atomic variable.
- *
- * @return PJ_SUCCESS if success.
- */
-PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var );
-
-/**
- * Set the value of an atomic type, and return the previous value.
- *
- * @param atomic_var the atomic variable.
- * @param value value to be set to the variable.
- */
-PJ_DECL(void) pj_atomic_set( pj_atomic_t *atomic_var,
- pj_atomic_value_t value);
-
-/**
- * Get the value of an atomic type.
- *
- * @param atomic_var the atomic variable.
- *
- * @return the value of the atomic variable.
- */
-PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var);
-
-/**
- * Increment the value of an atomic type.
- *
- * @param atomic_var the atomic variable.
- */
-PJ_DECL(void) pj_atomic_inc(pj_atomic_t *atomic_var);
-
-/**
- * Increment the value of an atomic type and get the result.
- *
- * @param atomic_var the atomic variable.
- *
- * @return The incremented value.
- */
-PJ_DECL(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var);
-
-/**
- * Decrement the value of an atomic type.
- *
- * @param atomic_var the atomic variable.
- */
-PJ_DECL(void) pj_atomic_dec(pj_atomic_t *atomic_var);
-
-/**
- * Decrement the value of an atomic type and get the result.
- *
- * @param atomic_var the atomic variable.
- *
- * @return The decremented value.
- */
-PJ_DECL(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var);
-
-/**
- * Add a value to an atomic type.
- *
- * @param atomic_var The atomic variable.
- * @param value Value to be added.
- */
-PJ_DECL(void) pj_atomic_add( pj_atomic_t *atomic_var,
- pj_atomic_value_t value);
-
-/**
- * Add a value to an atomic type and get the result.
- *
- * @param atomic_var The atomic variable.
- * @param value Value to be added.
- *
- * @return The result after the addition.
- */
-PJ_DECL(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
- pj_atomic_value_t value);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_MUTEX Mutexes.
- * @ingroup PJ_OS
- * @{
- *
- * Mutex manipulation. Alternatively, application can use higher abstraction
- * for lock objects, which provides uniform API for all kinds of lock
- * mechanisms, including mutex. See @ref PJ_LOCK for more information.
- */
-
-/**
- * Mutex types:
- * - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent.
- * - PJ_MUTEX_SIMPLE: non-recursive mutex.
- * - PJ_MUTEX_RECURSIVE: recursive mutex.
- */
-typedef enum pj_mutex_type_e
-{
- PJ_MUTEX_DEFAULT,
- PJ_MUTEX_SIMPLE,
- PJ_MUTEX_RECURSE,
-} pj_mutex_type_e;
-
-
-/**
- * Create mutex of the specified type.
- *
- * @param pool The pool.
- * @param name Name to be associated with the mutex (for debugging).
- * @param type The type of the mutex, of type #pj_mutex_type_e.
- * @param mutex Pointer to hold the returned mutex instance.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **mutex);
-
-/**
- * Create simple, non-recursive mutex.
- * This function is a simple wrapper for #pj_mutex_create to create
- * non-recursive mutex.
- *
- * @param pool The pool.
- * @param name Mutex name.
- * @param mutex Pointer to hold the returned mutex instance.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
- pj_mutex_t **mutex );
-
-/**
- * Create recursive mutex.
- * This function is a simple wrapper for #pj_mutex_create to create
- * recursive mutex.
- *
- * @param pool The pool.
- * @param name Mutex name.
- * @param mutex Pointer to hold the returned mutex instance.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex );
-
-/**
- * Acquire mutex lock.
- *
- * @param mutex The mutex.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex);
-
-/**
- * Release mutex lock.
- *
- * @param mutex The mutex.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex);
-
-/**
- * Try to acquire mutex lock.
- *
- * @param mutex The mutex.
- * @return PJ_SUCCESS on success, or the error code if the
- * lock couldn't be acquired.
- */
-PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex);
-
-/**
- * Destroy mutex.
- *
- * @param mutex Te mutex.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex);
-
-/**
- * Determine whether calling thread is owning the mutex (only available when
- * PJ_DEBUG is set).
- * @param mutex The mutex.
- * @return Non-zero if yes.
- */
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
- PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex);
-#else
-# define pj_mutex_is_locked(mutex) 1
-#endif
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_CRIT_SEC Critical sections.
- * @ingroup PJ_OS
- * @{
- * Critical section protection can be used to protect regions where:
- * - mutual exclusion protection is needed.
- * - it's rather too expensive to create a mutex.
- * - the time spent in the region is very very brief.
- *
- * Critical section is a global object, and it prevents any threads from
- * entering any regions that are protected by critical section once a thread
- * is already in the section.
- *
- * Critial section is \a not recursive!
- *
- * Application <b>MUST NOT</b> call any functions that may cause current
- * thread to block (such as allocating memory, performing I/O, locking mutex,
- * etc.) while holding the critical section.
- */
-/**
- * Enter critical section.
- */
-PJ_DECL(void) pj_enter_critical_section(void);
-
-/**
- * Leave critical section.
- */
-PJ_DECL(void) pj_leave_critical_section(void);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-/**
- * @defgroup PJ_SEM Semaphores.
- * @ingroup PJ_OS
- * @{
- *
- * This module provides abstraction for semaphores, where available.
- */
-
-/**
- * Create semaphore.
- *
- * @param pool The pool.
- * @param name Name to be assigned to the semaphore (for logging purpose)
- * @param initial The initial count of the semaphore.
- * @param max The maximum count of the semaphore.
- * @param sem Pointer to hold the semaphore created.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **sem);
-
-/**
- * Wait for semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem);
-
-/**
- * Try wait for semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem);
-
-/**
- * Release semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem);
-
-/**
- * Destroy semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem);
-
-/**
- * @}
- */
-#endif /* PJ_HAS_SEMAPHORE */
-
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-/**
- * @defgroup PJ_EVENT Event Object.
- * @ingroup PJ_OS
- * @{
- *
- * This module provides abstraction to event object (e.g. Win32 Event) where
- * available. Event objects can be used for synchronization among threads.
- */
-
-/**
- * Create event object.
- *
- * @param pool The pool.
- * @param name The name of the event object (for logging purpose).
- * @param manual_reset Specify whether the event is manual-reset
- * @param initial Specify the initial state of the event object.
- * @param event Pointer to hold the returned event object.
- *
- * @return event handle, or NULL if failed.
- */
-PJ_DECL(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
- pj_bool_t manual_reset, pj_bool_t initial,
- pj_event_t **event);
-
-/**
- * Wait for event to be signaled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event);
-
-/**
- * Try wait for event object to be signalled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event);
-
-/**
- * Set the event object state to signaled. For auto-reset event, this
- * will only release the first thread that are waiting on the event. For
- * manual reset event, the state remains signaled until the event is reset.
- * If there is no thread waiting on the event, the event object state
- * remains signaled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event);
-
-/**
- * Set the event object to signaled state to release appropriate number of
- * waiting threads and then reset the event object to non-signaled. For
- * manual-reset event, this function will release all waiting threads. For
- * auto-reset event, this function will only release one waiting thread.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event);
-
-/**
- * Set the event object state to non-signaled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event);
-
-/**
- * Destroy the event object.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event);
-
-/**
- * @}
- */
-#endif /* PJ_HAS_EVENT_OBJ */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJ_TIME Time Data Type and Manipulation.
- * @ingroup PJ_OS
- * @{
- * This module provides API for manipulating time.
- *
- * \section pj_time_examples_sec Examples
- *
- * For examples, please see:
- * - \ref page_pjlib_sleep_test
- */
-
-/**
- * Get current time of day in local representation.
- *
- * @param tv Variable to store the result.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv);
-
-
-/**
- * Parse time value into date/time representation.
- *
- * @param tv The time.
- * @param pt Variable to store the date time result.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt);
-
-/**
- * Encode date/time to time value.
- *
- * @param pt The date/time.
- * @param tv Variable to store time value result.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
-
-/**
- * Convert local time to GMT.
- *
- * @param tv Time to convert.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
-
-/**
- * Convert GMT to local time.
- *
- * @param tv Time to convert.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-
-/**
- * @defgroup PJ_TERM Terminal
- * @ingroup PJ_OS
- * @{
- */
-
-/**
- * Set current terminal color.
- *
- * @param color The RGB color.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color);
-
-/**
- * Get current terminal foreground color.
- *
- * @return RGB color.
- */
-PJ_DECL(pj_color_t) pj_term_get_color(void);
-
-/**
- * @}
- */
-
-#endif /* PJ_TERM_HAS_COLOR */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_TIMESTAMP High Resolution Timestamp
- * @ingroup PJ_OS
- * @{
- *
- * PJLIB provides <b>High Resolution Timestamp</b> API to access highest
- * resolution timestamp value provided by the platform. The API is usefull
- * to measure precise elapsed time, and can be used in applications such
- * as profiling.
- *
- * The timestamp value is represented in cycles, and can be related to
- * normal time (in seconds or sub-seconds) using various functions provided.
- *
- * \section pj_timestamp_examples_sec Examples
- *
- * For examples, please see:
- * - \ref page_pjlib_sleep_test
- * - \ref page_pjlib_timestamp_test
- */
-
-/*
- * High resolution timer.
- */
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
-
-/**
- * This structure represents high resolution (64bit) time value. The time
- * values represent time in cycles, which is retrieved by calling
- * #pj_get_timestamp().
- */
-typedef union pj_timestamp
-{
- struct
- {
-#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
- pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
- pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
-#else
- pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
- pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
-#endif
- } u32; /**< The 64-bit value as two 32-bit values. */
-
-#if PJ_HAS_INT64
- pj_uint64_t u64; /**< The whole 64-bit value, where available. */
-#endif
-} pj_timestamp;
-
-
-/**
- * Acquire high resolution timer value. The time value are stored
- * in cycles.
- *
- * @param ts High resolution timer value.
- * @return PJ_SUCCESS or the appropriate error code.
- *
- * @see pj_get_timestamp_freq().
- */
-PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts);
-
-/**
- * Get high resolution timer frequency, in cycles per second.
- *
- * @param freq Timer frequency, in cycles per second.
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq);
-
-/**
- * Add timestamp t2 to t1.
- * @param t1 t1.
- * @param t2 t2.
- */
-PJ_INLINE(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
-{
-#if PJ_HAS_INT64
- t1->u64 += t2->u64;
-#else
- pj_uint32_t old = t1->u32.lo;
- t1->u32.hi += t2->u32.hi;
- t1->u32.lo += t2->u32.lo;
- if (t1->u32.lo < old)
- ++t1->u32.hi;
-#endif
-}
-
-/**
- * Substract timestamp t2 from t1.
- * @param t1 t1.
- * @param t2 t2.
- */
-PJ_INLINE(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
-{
-#if PJ_HAS_INT64
- t1->u64 -= t2->u64;
-#else
- t1->u32.hi -= t2->u32.hi;
- if (t1->u32.lo >= t2->u32.lo)
- t1->u32.lo -= t2->u32.lo;
- else {
- t1->u32.lo -= t2->u32.lo;
- --t1->u32.hi;
- }
-#endif
-}
-
-/**
- * Calculate the elapsed time, and store it in pj_time_val.
- * This function calculates the elapsed time using highest precision
- * calculation that is available for current platform, considering
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time as #pj_time_val.
- *
- * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec()
- */
-PJ_DECL(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-/**
- * Calculate the elapsed time in 32-bit microseconds.
- * This function calculates the elapsed time using highest precision
- * calculation that is available for current platform, considering
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time in microsecond.
- *
- * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec()
- */
-PJ_DECL(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-/**
- * Calculate the elapsed time in 32-bit nanoseconds.
- * This function calculates the elapsed time using highest precision
- * calculation that is available for current platform, considering
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time in nanoseconds.
- *
- * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec()
- */
-PJ_DECL(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-/**
- * Calculate the elapsed time in 32-bit cycles.
- * This function calculates the elapsed time using highest precision
- * calculation that is available for current platform, considering
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time in cycles.
- *
- * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec()
- */
-PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-
-#endif /* PJ_HAS_HIGH_RES_TIMER */
-
-/** @} */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * Internal PJLIB function to initialize the threading subsystem.
- * @return PJ_SUCCESS or the appropriate error code.
- */
-pj_status_t pj_thread_init(void);
-
-
-PJ_END_DECL
-
-#endif /* __PJ_OS_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_OS_H__ +#define __PJ_OS_H__ + +/** + * @file os.h + * @brief OS dependent functions + */ +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_OS Operating System Dependent Functionality. + * @ingroup PJ + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_THREAD Threads + * @ingroup PJ_OS + * @{ + * This module provides multithreading API. + * + * \section pj_thread_examples_sec Examples + * + * For examples, please see: + * - \ref page_pjlib_thread_test + * - \ref page_pjlib_sleep_test + * + */ + +/** + * Thread creation flags: + * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended. + */ +typedef enum pj_thread_create_flags +{ + PJ_THREAD_SUSPENDED = 1 +} pj_thread_create_flags; + + +/** + * Specify this as \a stack_size argument in #pj_thread_create() to specify + * that thread should use default stack size for the current platform. + */ +#define PJ_THREAD_DEFAULT_STACK_SIZE 0 + +/** + * Type of thread entry function. + */ +typedef int (PJ_THREAD_FUNC pj_thread_proc)(void*); + +/** + * Size of thread struct. + */ +#if !defined(PJ_THREAD_DESC_SIZE) +# define PJ_THREAD_DESC_SIZE (16) +#endif + +/** + * Thread structure, to thread's state when the thread is created by external + * or native API. + */ +typedef long pj_thread_desc[PJ_THREAD_DESC_SIZE]; + +/** + * Get process ID. + * @return process ID. + */ +PJ_DECL(pj_uint32_t) pj_getpid(void); + +/** + * Create a new thread. + * + * @param pool The memory pool from which the thread record + * will be allocated from. + * @param thread_name The optional name to be assigned to the thread. + * @param proc Thread entry function. + * @param arg Argument to be passed to the thread entry function. + * @param stack_size The size of the stack for the new thread, or ZERO or + * PJ_THREAD_DEFAULT_STACK_SIZE to let the + * library choose the reasonable size for the stack. + * For some systems, the stack will be allocated from + * the pool, so the pool must have suitable capacity. + * @param flags Flags for thread creation, which is bitmask combination + * from enum pj_thread_create_flags. + * @param thread Pointer to hold the newly created thread. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **thread ); + +/** + * Register a thread that was created by external or native API to PJLIB. + * This function must be called in the context of the thread being registered. + * When the thread is created by external function or API call, + * it must be 'registered' to PJLIB using pj_thread_register(), so that it can + * cooperate with PJLIB's framework. During registration, some data needs to + * be maintained, and this data must remain available during the thread's + * lifetime. + * + * @param thread_name The optional name to be assigned to the thread. + * @param desc Thread descriptor, which must be available throughout + * the lifetime of the thread. + * @param thread Pointer to hold the created thread handle. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_thread_register ( const char *thread_name, + pj_thread_desc desc, + pj_thread_t **thread); + +/** + * Get thread name. + * + * @param thread The thread handle. + * + * @return Thread name as null terminated string. + */ +PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread); + +/** + * Resume a suspended thread. + * + * @param thread The thread handle. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread); + +/** + * Get the current thread. + * + * @return Thread handle of current thread. + */ +PJ_DECL(pj_thread_t*) pj_thread_this(void); + +/** + * Join thread. + * This function will block the caller thread until the specified thread exits. + * + * @param thread The thread handle. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread); + + +/** + * Destroy thread and release resources allocated for the thread. + * However, the memory allocated for the pj_thread_t itself will only be released + * when the pool used to create the thread is destroyed. + * + * @param thread The thread handle. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread); + + +/** + * Put the current thread to sleep for the specified miliseconds. + * + * @param msec Miliseconds delay. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec); + +/** + * @def PJ_CHECK_STACK() + * PJ_CHECK_STACK() macro is used to check the sanity of the stack. + * The OS implementation may check that no stack overflow occurs, and + * it also may collect statistic about stack usage. + */ +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + +# define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__) + +/** @internal + * The implementation of stack checking. + */ +PJ_DECL(void) pj_thread_check_stack(const char *file, int line); + +/** @internal + * Get maximum stack usage statistic. + */ +PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread); + +/** @internal + * Dump thread stack status. + */ +PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, + const char **file, + int *line); +#else + +# define PJ_CHECK_STACK() +/** pj_thread_get_stack_max_usage() for the thread */ +# define pj_thread_get_stack_max_usage(thread) 0 +/** pj_thread_get_stack_info() for the thread */ +# define pj_thread_get_stack_info(thread,f,l) (*(f)="",*(l)=0) +#endif /* PJ_OS_HAS_CHECK_STACK */ + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_TLS Thread Local Storage. + * @ingroup PJ_OS + * @{ + */ + +/** + * Allocate thread local storage index. The initial value of the variable at + * the index is zero. + * + * @param index Pointer to hold the return value. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index); + +/** + * Deallocate thread local variable. + * + * @param index The variable index. + */ +PJ_DECL(void) pj_thread_local_free(long index); + +/** + * Set the value of thread local variable. + * + * @param index The index of the variable. + * @param value The value. + */ +PJ_DECL(pj_status_t) pj_thread_local_set(long index, void *value); + +/** + * Get the value of thread local variable. + * + * @param index The index of the variable. + * @return The value. + */ +PJ_DECL(void*) pj_thread_local_get(long index); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_ATOMIC Atomic Variables + * @ingroup PJ_OS + * @{ + * + * This module provides API to manipulate atomic variables. + * + * \section pj_atomic_examples_sec Examples + * + * For some example codes, please see: + * - @ref page_pjlib_atomic_test + */ + + +/** + * Create atomic variable. + * + * @param pool The pool. + * @param initial The initial value of the atomic variable. + * @param atomic Pointer to hold the atomic variable upon return. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **atomic ); + +/** + * Destroy atomic variable. + * + * @param atomic_var the atomic variable. + * + * @return PJ_SUCCESS if success. + */ +PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ); + +/** + * Set the value of an atomic type, and return the previous value. + * + * @param atomic_var the atomic variable. + * @param value value to be set to the variable. + */ +PJ_DECL(void) pj_atomic_set( pj_atomic_t *atomic_var, + pj_atomic_value_t value); + +/** + * Get the value of an atomic type. + * + * @param atomic_var the atomic variable. + * + * @return the value of the atomic variable. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var); + +/** + * Increment the value of an atomic type. + * + * @param atomic_var the atomic variable. + */ +PJ_DECL(void) pj_atomic_inc(pj_atomic_t *atomic_var); + +/** + * Increment the value of an atomic type and get the result. + * + * @param atomic_var the atomic variable. + * + * @return The incremented value. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var); + +/** + * Decrement the value of an atomic type. + * + * @param atomic_var the atomic variable. + */ +PJ_DECL(void) pj_atomic_dec(pj_atomic_t *atomic_var); + +/** + * Decrement the value of an atomic type and get the result. + * + * @param atomic_var the atomic variable. + * + * @return The decremented value. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var); + +/** + * Add a value to an atomic type. + * + * @param atomic_var The atomic variable. + * @param value Value to be added. + */ +PJ_DECL(void) pj_atomic_add( pj_atomic_t *atomic_var, + pj_atomic_value_t value); + +/** + * Add a value to an atomic type and get the result. + * + * @param atomic_var The atomic variable. + * @param value Value to be added. + * + * @return The result after the addition. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, + pj_atomic_value_t value); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_MUTEX Mutexes. + * @ingroup PJ_OS + * @{ + * + * Mutex manipulation. Alternatively, application can use higher abstraction + * for lock objects, which provides uniform API for all kinds of lock + * mechanisms, including mutex. See @ref PJ_LOCK for more information. + */ + +/** + * Mutex types: + * - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent. + * - PJ_MUTEX_SIMPLE: non-recursive mutex. + * - PJ_MUTEX_RECURSIVE: recursive mutex. + */ +typedef enum pj_mutex_type_e +{ + PJ_MUTEX_DEFAULT, + PJ_MUTEX_SIMPLE, + PJ_MUTEX_RECURSE, +} pj_mutex_type_e; + + +/** + * Create mutex of the specified type. + * + * @param pool The pool. + * @param name Name to be associated with the mutex (for debugging). + * @param type The type of the mutex, of type #pj_mutex_type_e. + * @param mutex Pointer to hold the returned mutex instance. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **mutex); + +/** + * Create simple, non-recursive mutex. + * This function is a simple wrapper for #pj_mutex_create to create + * non-recursive mutex. + * + * @param pool The pool. + * @param name Mutex name. + * @param mutex Pointer to hold the returned mutex instance. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, + pj_mutex_t **mutex ); + +/** + * Create recursive mutex. + * This function is a simple wrapper for #pj_mutex_create to create + * recursive mutex. + * + * @param pool The pool. + * @param name Mutex name. + * @param mutex Pointer to hold the returned mutex instance. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ); + +/** + * Acquire mutex lock. + * + * @param mutex The mutex. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex); + +/** + * Release mutex lock. + * + * @param mutex The mutex. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex); + +/** + * Try to acquire mutex lock. + * + * @param mutex The mutex. + * @return PJ_SUCCESS on success, or the error code if the + * lock couldn't be acquired. + */ +PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex); + +/** + * Destroy mutex. + * + * @param mutex Te mutex. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex); + +/** + * Determine whether calling thread is owning the mutex (only available when + * PJ_DEBUG is set). + * @param mutex The mutex. + * @return Non-zero if yes. + */ +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 + PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex); +#else +# define pj_mutex_is_locked(mutex) 1 +#endif + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_CRIT_SEC Critical sections. + * @ingroup PJ_OS + * @{ + * Critical section protection can be used to protect regions where: + * - mutual exclusion protection is needed. + * - it's rather too expensive to create a mutex. + * - the time spent in the region is very very brief. + * + * Critical section is a global object, and it prevents any threads from + * entering any regions that are protected by critical section once a thread + * is already in the section. + * + * Critial section is \a not recursive! + * + * Application <b>MUST NOT</b> call any functions that may cause current + * thread to block (such as allocating memory, performing I/O, locking mutex, + * etc.) while holding the critical section. + */ +/** + * Enter critical section. + */ +PJ_DECL(void) pj_enter_critical_section(void); + +/** + * Leave critical section. + */ +PJ_DECL(void) pj_leave_critical_section(void); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +/** + * @defgroup PJ_SEM Semaphores. + * @ingroup PJ_OS + * @{ + * + * This module provides abstraction for semaphores, where available. + */ + +/** + * Create semaphore. + * + * @param pool The pool. + * @param name Name to be assigned to the semaphore (for logging purpose) + * @param initial The initial count of the semaphore. + * @param max The maximum count of the semaphore. + * @param sem Pointer to hold the semaphore created. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem); + +/** + * Wait for semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem); + +/** + * Try wait for semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem); + +/** + * Release semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem); + +/** + * Destroy semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem); + +/** + * @} + */ +#endif /* PJ_HAS_SEMAPHORE */ + + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 +/** + * @defgroup PJ_EVENT Event Object. + * @ingroup PJ_OS + * @{ + * + * This module provides abstraction to event object (e.g. Win32 Event) where + * available. Event objects can be used for synchronization among threads. + */ + +/** + * Create event object. + * + * @param pool The pool. + * @param name The name of the event object (for logging purpose). + * @param manual_reset Specify whether the event is manual-reset + * @param initial Specify the initial state of the event object. + * @param event Pointer to hold the returned event object. + * + * @return event handle, or NULL if failed. + */ +PJ_DECL(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, + pj_bool_t manual_reset, pj_bool_t initial, + pj_event_t **event); + +/** + * Wait for event to be signaled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event); + +/** + * Try wait for event object to be signalled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event); + +/** + * Set the event object state to signaled. For auto-reset event, this + * will only release the first thread that are waiting on the event. For + * manual reset event, the state remains signaled until the event is reset. + * If there is no thread waiting on the event, the event object state + * remains signaled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event); + +/** + * Set the event object to signaled state to release appropriate number of + * waiting threads and then reset the event object to non-signaled. For + * manual-reset event, this function will release all waiting threads. For + * auto-reset event, this function will only release one waiting thread. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event); + +/** + * Set the event object state to non-signaled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event); + +/** + * Destroy the event object. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event); + +/** + * @} + */ +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJ_TIME Time Data Type and Manipulation. + * @ingroup PJ_OS + * @{ + * This module provides API for manipulating time. + * + * \section pj_time_examples_sec Examples + * + * For examples, please see: + * - \ref page_pjlib_sleep_test + */ + +/** + * Get current time of day in local representation. + * + * @param tv Variable to store the result. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv); + + +/** + * Parse time value into date/time representation. + * + * @param tv The time. + * @param pt Variable to store the date time result. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt); + +/** + * Encode date/time to time value. + * + * @param pt The date/time. + * @param tv Variable to store time value result. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); + +/** + * Convert local time to GMT. + * + * @param tv Time to convert. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + * + * @param tv Time to convert. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + +/** + * @defgroup PJ_TERM Terminal + * @ingroup PJ_OS + * @{ + */ + +/** + * Set current terminal color. + * + * @param color The RGB color. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color); + +/** + * Get current terminal foreground color. + * + * @return RGB color. + */ +PJ_DECL(pj_color_t) pj_term_get_color(void); + +/** + * @} + */ + +#endif /* PJ_TERM_HAS_COLOR */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_TIMESTAMP High Resolution Timestamp + * @ingroup PJ_OS + * @{ + * + * PJLIB provides <b>High Resolution Timestamp</b> API to access highest + * resolution timestamp value provided by the platform. The API is usefull + * to measure precise elapsed time, and can be used in applications such + * as profiling. + * + * The timestamp value is represented in cycles, and can be related to + * normal time (in seconds or sub-seconds) using various functions provided. + * + * \section pj_timestamp_examples_sec Examples + * + * For examples, please see: + * - \ref page_pjlib_sleep_test + * - \ref page_pjlib_timestamp_test + */ + +/* + * High resolution timer. + */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + +/** + * This structure represents high resolution (64bit) time value. The time + * values represent time in cycles, which is retrieved by calling + * #pj_get_timestamp(). + */ +typedef union pj_timestamp +{ + struct + { +#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0 + pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */ + pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */ +#else + pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */ + pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */ +#endif + } u32; /**< The 64-bit value as two 32-bit values. */ + +#if PJ_HAS_INT64 + pj_uint64_t u64; /**< The whole 64-bit value, where available. */ +#endif +} pj_timestamp; + + +/** + * Acquire high resolution timer value. The time value are stored + * in cycles. + * + * @param ts High resolution timer value. + * @return PJ_SUCCESS or the appropriate error code. + * + * @see pj_get_timestamp_freq(). + */ +PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts); + +/** + * Get high resolution timer frequency, in cycles per second. + * + * @param freq Timer frequency, in cycles per second. + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq); + +/** + * Add timestamp t2 to t1. + * @param t1 t1. + * @param t2 t2. + */ +PJ_INLINE(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2) +{ +#if PJ_HAS_INT64 + t1->u64 += t2->u64; +#else + pj_uint32_t old = t1->u32.lo; + t1->u32.hi += t2->u32.hi; + t1->u32.lo += t2->u32.lo; + if (t1->u32.lo < old) + ++t1->u32.hi; +#endif +} + +/** + * Substract timestamp t2 from t1. + * @param t1 t1. + * @param t2 t2. + */ +PJ_INLINE(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2) +{ +#if PJ_HAS_INT64 + t1->u64 -= t2->u64; +#else + t1->u32.hi -= t2->u32.hi; + if (t1->u32.lo >= t2->u32.lo) + t1->u32.lo -= t2->u32.lo; + else { + t1->u32.lo -= t2->u32.lo; + --t1->u32.hi; + } +#endif +} + +/** + * Calculate the elapsed time, and store it in pj_time_val. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time as #pj_time_val. + * + * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec() + */ +PJ_DECL(pj_time_val) pj_elapsed_time( const pj_timestamp *start, + const pj_timestamp *stop ); + +/** + * Calculate the elapsed time in 32-bit microseconds. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time in microsecond. + * + * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec() + */ +PJ_DECL(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ); + +/** + * Calculate the elapsed time in 32-bit nanoseconds. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time in nanoseconds. + * + * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec() + */ +PJ_DECL(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, + const pj_timestamp *stop ); + +/** + * Calculate the elapsed time in 32-bit cycles. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time in cycles. + * + * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec() + */ +PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, + const pj_timestamp *stop ); + + +#endif /* PJ_HAS_HIGH_RES_TIMER */ + +/** @} */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * Internal PJLIB function to initialize the threading subsystem. + * @return PJ_SUCCESS or the appropriate error code. + */ +pj_status_t pj_thread_init(void); + + +PJ_END_DECL + +#endif /* __PJ_OS_H__ */ + diff --git a/pjlib/include/pj/pool.h b/pjlib/include/pj/pool.h index b3037b31..c47195c8 100644 --- a/pjlib/include/pj/pool.h +++ b/pjlib/include/pj/pool.h @@ -1,586 +1,586 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_POOL_H__
-#define __PJ_POOL_H__
-
-/**
- * @file pool.h
- * @brief Memory Pool.
- */
-
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_POOL_GROUP Memory Pool Management
- * @ingroup PJ
- * @brief
- * Memory pool management provides API to allocate and deallocate memory from
- * memory pool and to manage and establish policy for pool creation and
- * destruction in pool factory.
- *
- * \section PJ_POOL_FACTORY_SEC Pool Factory
- * See: \ref PJ_POOL_FACTORY "Pool Factory"
- *
- * A memory pool must be created through a factory. A factory not only provides
- * generic interface functions to create and release pool, but also provides
- * strategy to manage the life time of pools. One sample implementation,
- * \a pj_caching_pool, can be set to keep the pools released by application for
- * future use as long as the total memory is below the limit.
- *
- * The pool factory interface declared in PJLIB is designed to be extensible.
- * Application can define its own strategy by creating it's own pool factory
- * implementation, and this strategy can be used even by existing library
- * without recompilation.
- *
- *
- * \section PJ_POOL_POLICY_SEC Pool Factory Policy
- * See: \ref PJ_POOL_FACTORY "Pool Factory Policy"
- *
- * A pool factory only defines functions to create and release pool and how
- * to manage pools, but the rest of the functionalities are controlled by
- * policy. A pool policy defines:
- * - how memory block is allocated and deallocated (the default implementation
- * allocates and deallocate memory by calling malloc() and free()).
- * - callback to be called when memory allocation inside a pool fails (the
- * default implementation will throw PJ_NO_MEMORY_EXCEPTION exception).
- * - concurrency when creating and releasing pool from/to the factory.
- *
- * A pool factory can be given different policy during creation to make
- * it behave differently. For example, caching pool factory can be configured
- * to allocate and deallocate from a static/contiguous/preallocated memory
- * instead of using malloc()/free().
- *
- * What strategy/factory and what policy to use is not defined by PJLIB, but
- * instead is left to application to make use whichever is most efficient for
- * itself.
- *
- *
- * \section PJ_POOL_POOL_SEC The Pool
- * See: \ref PJ_POOL "Pool"
- *
- * The memory pool is an opaque object created by pool factory.
- * Application uses this object to request a memory chunk, by calling
- * #pj_pool_alloc or #pj_pool_calloc. When the application has finished using
- * the pool, it must call #pj_pool_release to free all the chunks previously
- * allocated and release the pool back to the factory.
- *
- * \section PJ_POOL_THREADING_SEC More on Threading Policies:
- * - By design, memory allocation from a pool is not thread safe. We assumed
- * that a pool will be owned by an object, and thread safety should be
- * handled by that object. Thus these functions are not thread safe:
- * - #pj_pool_alloc,
- * - #pj_pool_calloc,
- * - and other pool statistic functions.
- * - Threading in the pool factory is decided by the policy set for the
- * factory when it was created.
- *
- * \section PJ_POOL_EXAMPLES_SEC Examples
- *
- * For some sample codes on how to use the pool, please see:
- * - @ref page_pjlib_pool_test
- */
-
-/**
- * @defgroup PJ_POOL Memory Pool.
- * @ingroup PJ_POOL_GROUP
- * @brief
- * A memory pool is initialized with an initial amount of memory, which is
- * called a block. Pool can be configured to dynamically allocate more memory
- * blocks when it runs out of memory. Subsequent memory allocations by user
- * will use up portions of these block.
- * The pool doesn't keep track of individual memory allocations
- * by user, and the user doesn't have to free these indidual allocations. This
- * makes memory allocation simple and very fast. All the memory allocated from
- * the pool will be destroyed when the pool itself is destroyed.
- * @{
- */
-
-/**
- * The type for function to receive callback from the pool when it is unable
- * to allocate memory. The elegant way to handle this condition is to throw
- * exception, and this is what is expected by most of this library
- * components.
- */
-typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size);
-
-/**
- * This class, which is used internally by the pool, describes a single
- * block of memory from which user memory allocations will be allocated from.
- */
-typedef struct pj_pool_block
-{
- PJ_DECL_LIST_MEMBER(struct pj_pool_block); /**< List's prev and next. */
- unsigned char *buf; /**< Start of buffer. */
- unsigned char *cur; /**< Current alloc ptr. */
- unsigned char *end; /**< End of buffer. */
-} pj_pool_block;
-
-
-/**
- * This structure describes the memory pool. Only implementors of pool factory
- * need to care about the contents of this structure.
- */
-struct pj_pool_t
-{
- PJ_DECL_LIST_MEMBER(struct pj_pool_t); /**< Standard list elements. */
-
- /** Pool name */
- char obj_name[PJ_MAX_OBJ_NAME];
-
- /** Pool factory. */
- pj_pool_factory *factory;
-
- /** Current capacity allocated by the pool. */
- pj_size_t capacity;
-
- /** Number of memory used/allocated. */
- pj_size_t used_size;
-
- /** Size of memory block to be allocated when the pool runs out of memory */
- pj_size_t increment_size;
-
- /** List of memory blocks allcoated by the pool. */
- pj_pool_block block_list;
-
- /** The callback to be called when the pool is unable to allocate memory. */
- pj_pool_callback *callback;
-
-};
-
-
-/**
- * Guidance on how much memory required for initial pool administrative data.
- */
-#define PJ_POOL_SIZE (sizeof(struct pj_pool_t))
-
-/**
- * Pool memory alignment (must be power of 2).
- */
-#ifndef PJ_POOL_ALIGNMENT
-# define PJ_POOL_ALIGNMENT 4
-#endif
-
-/**
- * Create a new pool from the pool factory. This wrapper will call create_pool
- * member of the pool factory.
- *
- * @param factory The pool factory.
- * @param name The name to be assigned to the pool. The name should
- * not be longer than PJ_MAX_OBJ_NAME (32 chars), or
- * otherwise it will be truncated.
- * @param initial_size The size of initial memory blocks taken by the pool.
- * Note that the pool will take 68+20 bytes for
- * administrative area from this block.
- * @param increment_size the size of each additional blocks to be allocated
- * when the pool is running out of memory. If user
- * requests memory which is larger than this size, then
- * an error occurs.
- * Note that each time a pool allocates additional block,
- * it needs PJ_POOL_SIZE more to store some
- * administrative info.
- * @param callback Callback to be called when error occurs in the pool.
- * If this value is NULL, then the callback from pool
- * factory policy will be used.
- * Note that when an error occurs during pool creation,
- * the callback itself is not called. Instead, NULL
- * will be returned.
- *
- * @return The memory pool, or NULL.
- */
-PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
-/**
- * Release the pool back to pool factory.
- *
- * @param pool Memory pool.
- */
-PJ_IDECL(void) pj_pool_release( pj_pool_t *pool );
-
-/**
- * Get pool object name.
- *
- * @param pool the pool.
- *
- * @return pool name as NULL terminated string.
- */
-PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool );
-
-/**
- * Reset the pool to its state when it was initialized.
- * This means that if additional blocks have been allocated during runtime,
- * then they will be freed. Only the original block allocated during
- * initialization is retained. This function will also reset the internal
- * counters, such as pool capacity and used size.
- *
- * @param pool the pool.
- */
-PJ_DECL(void) pj_pool_reset( pj_pool_t *pool );
-
-
-/**
- * Get the pool capacity, that is, the system storage that have been allocated
- * by the pool, and have been used/will be used to allocate user requests.
- * There's no guarantee that the returned value represent a single
- * contiguous block, because the capacity may be spread in several blocks.
- *
- * @param pool the pool.
- *
- * @return the capacity.
- */
-PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool );
-
-/**
- * Get the total size of user allocation request.
- *
- * @param pool the pool.
- *
- * @return the total size.
- */
-PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool );
-
-/**
- * Allocate storage with the specified size from the pool.
- * If there's no storage available in the pool, then the pool can allocate more
- * blocks if the increment size is larger than the requested size.
- *
- * @param pool the pool.
- * @param size the requested size.
- *
- * @return pointer to the allocated memory.
- */
-PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size);
-
-/**
- * Allocate storage from the pool, and initialize it to zero.
- * This function behaves like pj_pool_alloc(), except that the storage will
- * be initialized to zero.
- *
- * @param pool the pool.
- * @param count the number of elements in the array.
- * @param elem the size of individual element.
- *
- * @return pointer to the allocated memory.
- */
-PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count,
- pj_size_t elem);
-
-
-/**
- * @def pj_pool_zalloc(pj_pool_t *pool, pj_size_t size)
- * Allocate storage from the pool and initialize it to zero.
- *
- * @param pool The pool.
- * @param size The size to be allocated.
- *
- * @return Pointer to the allocated memory.
- */
-#define pj_pool_zalloc(pool, size) pj_pool_calloc(pool, 1, size)
-
-
-/**
- * @} // PJ_POOL
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_POOL_FACTORY Pool Factory and Policy.
- * @ingroup PJ_POOL_GROUP
- * @brief
- * Pool factory declares an interface to create and destroy pool. There may
- * be several strategies for pool creation, and these strategies should
- * implement the interface defined by pool factory.
- *
- * \section PJ_POOL_FACTORY_ITF Pool Factory Interface
- * The pool factory defines the following interface:
- * - \a policy: the memory pool factory policy.
- * - \a create_pool(): create a new memory pool.
- * - \a release_pool(): release memory pool back to factory.
- *
- * \section PJ_POOL_FACTORY_POL Pool Factory Policy.
- * The pool factory policy controls the behaviour of memory factories, and
- * defines the following interface:
- * - \a block_alloc(): allocate memory block from backend memory mgmt/system.
- * - \a block_free(): free memory block back to backend memory mgmt/system.
- * @{
- */
-
-/* We unfortunately don't have support for factory policy options as now,
- so we keep this commented at the moment.
-enum PJ_POOL_FACTORY_OPTION
-{
- PJ_POOL_FACTORY_SERIALIZE = 1
-};
-*/
-
-/**
- * This structure declares pool factory interface.
- */
-typedef struct pj_pool_factory_policy
-{
- /**
- * Allocate memory block (for use by pool). This function is called
- * by memory pool to allocate memory block.
- *
- * @param factory Pool factory.
- * @param size The size of memory block to allocate.
- *
- * @return Memory block.
- */
- void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size);
-
- /**
- * Free memory block.
- *
- * @param factory Pool factory.
- * @param mem Memory block previously allocated by block_alloc().
- * @param size The size of memory block.
- */
- void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size);
-
- /**
- * Default callback to be called when memory allocation fails.
- */
- pj_pool_callback *callback;
-
- /**
- * Option flags.
- */
- unsigned flags;
-
-} pj_pool_factory_policy;
-
-/**
- * This constant denotes the exception number that will be thrown by default
- * memory factory policy when memory allocation fails.
- */
-extern int PJ_NO_MEMORY_EXCEPTION;
-
-/**
- * This global variable points to default memory pool factory policy.
- * The behaviour of the default policy is:
- * - block allocation and deallocation use malloc() and free().
- * - callback will raise PJ_NO_MEMORY_EXCEPTION exception.
- * - access to pool factory is not serialized (i.e. not thread safe).
- */
-extern pj_pool_factory_policy pj_pool_factory_default_policy;
-
-/**
- * This structure contains the declaration for pool factory interface.
- */
-struct pj_pool_factory
-{
- /**
- * Memory pool policy.
- */
- pj_pool_factory_policy policy;
-
- /**
- * Create a new pool from the pool factory.
- *
- * @param factory The pool factory.
- * @param name the name to be assigned to the pool. The name should
- * not be longer than PJ_MAX_OBJ_NAME (32 chars), or
- * otherwise it will be truncated.
- * @param initial_size the size of initial memory blocks taken by the pool.
- * Note that the pool will take 68+20 bytes for
- * administrative area from this block.
- * @param increment_size the size of each additional blocks to be allocated
- * when the pool is running out of memory. If user
- * requests memory which is larger than this size, then
- * an error occurs.
- * Note that each time a pool allocates additional block,
- * it needs 20 bytes (equal to sizeof(pj_pool_block)) to
- * store some administrative info.
- * @param callback Cllback to be called when error occurs in the pool.
- * Note that when an error occurs during pool creation,
- * the callback itself is not called. Instead, NULL
- * will be returned.
- *
- * @return the memory pool, or NULL.
- */
- pj_pool_t* (*create_pool)( pj_pool_factory *factory,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
- /**
- * Release the pool to the pool factory.
- *
- * @param factory The pool factory.
- * @param pool The pool to be released.
- */
- void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool );
-
- /**
- * Dump pool status to log.
- *
- * @param factory The pool factory.
- */
- void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail );
-};
-
-/**
- * This function is intended to be used by pool factory implementors.
- * @param factory Pool factory.
- * @param name Pool name.
- * @param initial_size Initial size.
- * @param increment_size Increment size.
- * @param callback Callback.
- * @return The pool object, or NULL.
- */
-PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
-/**
- * This function is intended to be used by pool factory implementors.
- * @param pool The pool.
- * @param name Pool name.
- * @param increment_size Increment size.
- * @param callback Callback function.
- */
-PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool,
- const char *name,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
-/**
- * This function is intended to be used by pool factory implementors.
- * @param pool The memory pool.
- */
-PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool );
-
-
-/**
- * @} // PJ_POOL_FACTORY
- */
-
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * @defgroup PJ_CACHING_POOL Caching Pool Factory.
- * @ingroup PJ_POOL_GROUP
- * @brief
- * Caching pool is one sample implementation of pool factory where the
- * factory can reuse memory to create a pool. Application defines what the
- * maximum memory the factory can hold, and when a pool is released the
- * factory decides whether to destroy the pool or to keep it for future use.
- * If the total amount of memory in the internal cache is still within the
- * limit, the factory will keep the pool in the internal cache, otherwise the
- * pool will be destroyed, thus releasing the memory back to the system.
- *
- * @{
- */
-
-/**
- * Number of unique sizes, to be used as index to the free list.
- * Each pool in the free list is organized by it's size.
- */
-#define PJ_CACHING_POOL_ARRAY_SIZE 16
-
-/**
- * Declaration for caching pool. Application doesn't normally need to
- * care about the contents of this struct, it is only provided here because
- * application need to define an instance of this struct (we can not allocate
- * the struct from a pool since there is no pool factory yet!).
- */
-struct pj_caching_pool
-{
- /** Pool factory interface, must be declared first. */
- pj_pool_factory factory;
-
- /** Current factory's capacity, i.e. number of bytes that are allocated
- * and available for application in this factory. The factory's
- * capacity represents the size of all pools kept by this factory
- * in it's free list, which will be returned to application when it
- * requests to create a new pool.
- */
- pj_size_t capacity;
-
- /** Maximum size that can be held by this factory. Once the capacity
- * has exceeded @a max_capacity, further #pj_pool_release() will
- * flush the pool. If the capacity is still below the @a max_capacity,
- * #pj_pool_release() will save the pool to the factory's free list.
- */
- pj_size_t max_capacity;
-
- /**
- * Number of pools currently held by applications. This number gets
- * incremented everytime #pj_pool_create() is called, and gets
- * decremented when #pj_pool_release() is called.
- */
- pj_size_t used_count;
-
- /**
- * Lists of pools in the cache, indexed by pool size.
- */
- pj_list free_list[PJ_CACHING_POOL_ARRAY_SIZE];
-
- /**
- * List of pools currently allocated by applications.
- */
- pj_list used_list;
-};
-
-
-
-/**
- * Initialize caching pool.
- *
- * @param ch_pool The caching pool factory to be initialized.
- * @param policy Pool factory policy.
- * @param max_capacity The total capacity to be retained in the cache. When
- * the pool is returned to the cache, it will be kept in
- * recycling list if the total capacity of pools in this
- * list plus the capacity of the pool is still below this
- * value.
- */
-PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool,
- const pj_pool_factory_policy *policy,
- pj_size_t max_capacity);
-
-
-/**
- * Destroy caching pool, and release all the pools in the recycling list.
- *
- * @param ch_pool The caching pool.
- */
-PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool );
-
-/**
- * @} // PJ_CACHING_POOL
- */
-
-# if PJ_FUNCTIONS_ARE_INLINED
-# include "pool_i.h"
-# endif
-
-PJ_END_DECL
-
-#endif /* __PJ_POOL_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_POOL_H__ +#define __PJ_POOL_H__ + +/** + * @file pool.h + * @brief Memory Pool. + */ + +#include <pj/list.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_POOL_GROUP Memory Pool Management + * @ingroup PJ + * @brief + * Memory pool management provides API to allocate and deallocate memory from + * memory pool and to manage and establish policy for pool creation and + * destruction in pool factory. + * + * \section PJ_POOL_FACTORY_SEC Pool Factory + * See: \ref PJ_POOL_FACTORY "Pool Factory" + * + * A memory pool must be created through a factory. A factory not only provides + * generic interface functions to create and release pool, but also provides + * strategy to manage the life time of pools. One sample implementation, + * \a pj_caching_pool, can be set to keep the pools released by application for + * future use as long as the total memory is below the limit. + * + * The pool factory interface declared in PJLIB is designed to be extensible. + * Application can define its own strategy by creating it's own pool factory + * implementation, and this strategy can be used even by existing library + * without recompilation. + * + * + * \section PJ_POOL_POLICY_SEC Pool Factory Policy + * See: \ref PJ_POOL_FACTORY "Pool Factory Policy" + * + * A pool factory only defines functions to create and release pool and how + * to manage pools, but the rest of the functionalities are controlled by + * policy. A pool policy defines: + * - how memory block is allocated and deallocated (the default implementation + * allocates and deallocate memory by calling malloc() and free()). + * - callback to be called when memory allocation inside a pool fails (the + * default implementation will throw PJ_NO_MEMORY_EXCEPTION exception). + * - concurrency when creating and releasing pool from/to the factory. + * + * A pool factory can be given different policy during creation to make + * it behave differently. For example, caching pool factory can be configured + * to allocate and deallocate from a static/contiguous/preallocated memory + * instead of using malloc()/free(). + * + * What strategy/factory and what policy to use is not defined by PJLIB, but + * instead is left to application to make use whichever is most efficient for + * itself. + * + * + * \section PJ_POOL_POOL_SEC The Pool + * See: \ref PJ_POOL "Pool" + * + * The memory pool is an opaque object created by pool factory. + * Application uses this object to request a memory chunk, by calling + * #pj_pool_alloc or #pj_pool_calloc. When the application has finished using + * the pool, it must call #pj_pool_release to free all the chunks previously + * allocated and release the pool back to the factory. + * + * \section PJ_POOL_THREADING_SEC More on Threading Policies: + * - By design, memory allocation from a pool is not thread safe. We assumed + * that a pool will be owned by an object, and thread safety should be + * handled by that object. Thus these functions are not thread safe: + * - #pj_pool_alloc, + * - #pj_pool_calloc, + * - and other pool statistic functions. + * - Threading in the pool factory is decided by the policy set for the + * factory when it was created. + * + * \section PJ_POOL_EXAMPLES_SEC Examples + * + * For some sample codes on how to use the pool, please see: + * - @ref page_pjlib_pool_test + */ + +/** + * @defgroup PJ_POOL Memory Pool. + * @ingroup PJ_POOL_GROUP + * @brief + * A memory pool is initialized with an initial amount of memory, which is + * called a block. Pool can be configured to dynamically allocate more memory + * blocks when it runs out of memory. Subsequent memory allocations by user + * will use up portions of these block. + * The pool doesn't keep track of individual memory allocations + * by user, and the user doesn't have to free these indidual allocations. This + * makes memory allocation simple and very fast. All the memory allocated from + * the pool will be destroyed when the pool itself is destroyed. + * @{ + */ + +/** + * The type for function to receive callback from the pool when it is unable + * to allocate memory. The elegant way to handle this condition is to throw + * exception, and this is what is expected by most of this library + * components. + */ +typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size); + +/** + * This class, which is used internally by the pool, describes a single + * block of memory from which user memory allocations will be allocated from. + */ +typedef struct pj_pool_block +{ + PJ_DECL_LIST_MEMBER(struct pj_pool_block); /**< List's prev and next. */ + unsigned char *buf; /**< Start of buffer. */ + unsigned char *cur; /**< Current alloc ptr. */ + unsigned char *end; /**< End of buffer. */ +} pj_pool_block; + + +/** + * This structure describes the memory pool. Only implementors of pool factory + * need to care about the contents of this structure. + */ +struct pj_pool_t +{ + PJ_DECL_LIST_MEMBER(struct pj_pool_t); /**< Standard list elements. */ + + /** Pool name */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** Pool factory. */ + pj_pool_factory *factory; + + /** Current capacity allocated by the pool. */ + pj_size_t capacity; + + /** Number of memory used/allocated. */ + pj_size_t used_size; + + /** Size of memory block to be allocated when the pool runs out of memory */ + pj_size_t increment_size; + + /** List of memory blocks allcoated by the pool. */ + pj_pool_block block_list; + + /** The callback to be called when the pool is unable to allocate memory. */ + pj_pool_callback *callback; + +}; + + +/** + * Guidance on how much memory required for initial pool administrative data. + */ +#define PJ_POOL_SIZE (sizeof(struct pj_pool_t)) + +/** + * Pool memory alignment (must be power of 2). + */ +#ifndef PJ_POOL_ALIGNMENT +# define PJ_POOL_ALIGNMENT 4 +#endif + +/** + * Create a new pool from the pool factory. This wrapper will call create_pool + * member of the pool factory. + * + * @param factory The pool factory. + * @param name The name to be assigned to the pool. The name should + * not be longer than PJ_MAX_OBJ_NAME (32 chars), or + * otherwise it will be truncated. + * @param initial_size The size of initial memory blocks taken by the pool. + * Note that the pool will take 68+20 bytes for + * administrative area from this block. + * @param increment_size the size of each additional blocks to be allocated + * when the pool is running out of memory. If user + * requests memory which is larger than this size, then + * an error occurs. + * Note that each time a pool allocates additional block, + * it needs PJ_POOL_SIZE more to store some + * administrative info. + * @param callback Callback to be called when error occurs in the pool. + * If this value is NULL, then the callback from pool + * factory policy will be used. + * Note that when an error occurs during pool creation, + * the callback itself is not called. Instead, NULL + * will be returned. + * + * @return The memory pool, or NULL. + */ +PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback); + +/** + * Release the pool back to pool factory. + * + * @param pool Memory pool. + */ +PJ_IDECL(void) pj_pool_release( pj_pool_t *pool ); + +/** + * Get pool object name. + * + * @param pool the pool. + * + * @return pool name as NULL terminated string. + */ +PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool ); + +/** + * Reset the pool to its state when it was initialized. + * This means that if additional blocks have been allocated during runtime, + * then they will be freed. Only the original block allocated during + * initialization is retained. This function will also reset the internal + * counters, such as pool capacity and used size. + * + * @param pool the pool. + */ +PJ_DECL(void) pj_pool_reset( pj_pool_t *pool ); + + +/** + * Get the pool capacity, that is, the system storage that have been allocated + * by the pool, and have been used/will be used to allocate user requests. + * There's no guarantee that the returned value represent a single + * contiguous block, because the capacity may be spread in several blocks. + * + * @param pool the pool. + * + * @return the capacity. + */ +PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ); + +/** + * Get the total size of user allocation request. + * + * @param pool the pool. + * + * @return the total size. + */ +PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ); + +/** + * Allocate storage with the specified size from the pool. + * If there's no storage available in the pool, then the pool can allocate more + * blocks if the increment size is larger than the requested size. + * + * @param pool the pool. + * @param size the requested size. + * + * @return pointer to the allocated memory. + */ +PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size); + +/** + * Allocate storage from the pool, and initialize it to zero. + * This function behaves like pj_pool_alloc(), except that the storage will + * be initialized to zero. + * + * @param pool the pool. + * @param count the number of elements in the array. + * @param elem the size of individual element. + * + * @return pointer to the allocated memory. + */ +PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, + pj_size_t elem); + + +/** + * @def pj_pool_zalloc(pj_pool_t *pool, pj_size_t size) + * Allocate storage from the pool and initialize it to zero. + * + * @param pool The pool. + * @param size The size to be allocated. + * + * @return Pointer to the allocated memory. + */ +#define pj_pool_zalloc(pool, size) pj_pool_calloc(pool, 1, size) + + +/** + * @} // PJ_POOL + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_POOL_FACTORY Pool Factory and Policy. + * @ingroup PJ_POOL_GROUP + * @brief + * Pool factory declares an interface to create and destroy pool. There may + * be several strategies for pool creation, and these strategies should + * implement the interface defined by pool factory. + * + * \section PJ_POOL_FACTORY_ITF Pool Factory Interface + * The pool factory defines the following interface: + * - \a policy: the memory pool factory policy. + * - \a create_pool(): create a new memory pool. + * - \a release_pool(): release memory pool back to factory. + * + * \section PJ_POOL_FACTORY_POL Pool Factory Policy. + * The pool factory policy controls the behaviour of memory factories, and + * defines the following interface: + * - \a block_alloc(): allocate memory block from backend memory mgmt/system. + * - \a block_free(): free memory block back to backend memory mgmt/system. + * @{ + */ + +/* We unfortunately don't have support for factory policy options as now, + so we keep this commented at the moment. +enum PJ_POOL_FACTORY_OPTION +{ + PJ_POOL_FACTORY_SERIALIZE = 1 +}; +*/ + +/** + * This structure declares pool factory interface. + */ +typedef struct pj_pool_factory_policy +{ + /** + * Allocate memory block (for use by pool). This function is called + * by memory pool to allocate memory block. + * + * @param factory Pool factory. + * @param size The size of memory block to allocate. + * + * @return Memory block. + */ + void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size); + + /** + * Free memory block. + * + * @param factory Pool factory. + * @param mem Memory block previously allocated by block_alloc(). + * @param size The size of memory block. + */ + void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size); + + /** + * Default callback to be called when memory allocation fails. + */ + pj_pool_callback *callback; + + /** + * Option flags. + */ + unsigned flags; + +} pj_pool_factory_policy; + +/** + * This constant denotes the exception number that will be thrown by default + * memory factory policy when memory allocation fails. + */ +extern int PJ_NO_MEMORY_EXCEPTION; + +/** + * This global variable points to default memory pool factory policy. + * The behaviour of the default policy is: + * - block allocation and deallocation use malloc() and free(). + * - callback will raise PJ_NO_MEMORY_EXCEPTION exception. + * - access to pool factory is not serialized (i.e. not thread safe). + */ +extern pj_pool_factory_policy pj_pool_factory_default_policy; + +/** + * This structure contains the declaration for pool factory interface. + */ +struct pj_pool_factory +{ + /** + * Memory pool policy. + */ + pj_pool_factory_policy policy; + + /** + * Create a new pool from the pool factory. + * + * @param factory The pool factory. + * @param name the name to be assigned to the pool. The name should + * not be longer than PJ_MAX_OBJ_NAME (32 chars), or + * otherwise it will be truncated. + * @param initial_size the size of initial memory blocks taken by the pool. + * Note that the pool will take 68+20 bytes for + * administrative area from this block. + * @param increment_size the size of each additional blocks to be allocated + * when the pool is running out of memory. If user + * requests memory which is larger than this size, then + * an error occurs. + * Note that each time a pool allocates additional block, + * it needs 20 bytes (equal to sizeof(pj_pool_block)) to + * store some administrative info. + * @param callback Cllback to be called when error occurs in the pool. + * Note that when an error occurs during pool creation, + * the callback itself is not called. Instead, NULL + * will be returned. + * + * @return the memory pool, or NULL. + */ + pj_pool_t* (*create_pool)( pj_pool_factory *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback); + + /** + * Release the pool to the pool factory. + * + * @param factory The pool factory. + * @param pool The pool to be released. + */ + void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool ); + + /** + * Dump pool status to log. + * + * @param factory The pool factory. + */ + void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail ); +}; + +/** + * This function is intended to be used by pool factory implementors. + * @param factory Pool factory. + * @param name Pool name. + * @param initial_size Initial size. + * @param increment_size Increment size. + * @param callback Callback. + * @return The pool object, or NULL. + */ +PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback); + +/** + * This function is intended to be used by pool factory implementors. + * @param pool The pool. + * @param name Pool name. + * @param increment_size Increment size. + * @param callback Callback function. + */ +PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool, + const char *name, + pj_size_t increment_size, + pj_pool_callback *callback); + +/** + * This function is intended to be used by pool factory implementors. + * @param pool The memory pool. + */ +PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool ); + + +/** + * @} // PJ_POOL_FACTORY + */ + +/////////////////////////////////////////////////////////////////////////////// + +/** + * @defgroup PJ_CACHING_POOL Caching Pool Factory. + * @ingroup PJ_POOL_GROUP + * @brief + * Caching pool is one sample implementation of pool factory where the + * factory can reuse memory to create a pool. Application defines what the + * maximum memory the factory can hold, and when a pool is released the + * factory decides whether to destroy the pool or to keep it for future use. + * If the total amount of memory in the internal cache is still within the + * limit, the factory will keep the pool in the internal cache, otherwise the + * pool will be destroyed, thus releasing the memory back to the system. + * + * @{ + */ + +/** + * Number of unique sizes, to be used as index to the free list. + * Each pool in the free list is organized by it's size. + */ +#define PJ_CACHING_POOL_ARRAY_SIZE 16 + +/** + * Declaration for caching pool. Application doesn't normally need to + * care about the contents of this struct, it is only provided here because + * application need to define an instance of this struct (we can not allocate + * the struct from a pool since there is no pool factory yet!). + */ +struct pj_caching_pool +{ + /** Pool factory interface, must be declared first. */ + pj_pool_factory factory; + + /** Current factory's capacity, i.e. number of bytes that are allocated + * and available for application in this factory. The factory's + * capacity represents the size of all pools kept by this factory + * in it's free list, which will be returned to application when it + * requests to create a new pool. + */ + pj_size_t capacity; + + /** Maximum size that can be held by this factory. Once the capacity + * has exceeded @a max_capacity, further #pj_pool_release() will + * flush the pool. If the capacity is still below the @a max_capacity, + * #pj_pool_release() will save the pool to the factory's free list. + */ + pj_size_t max_capacity; + + /** + * Number of pools currently held by applications. This number gets + * incremented everytime #pj_pool_create() is called, and gets + * decremented when #pj_pool_release() is called. + */ + pj_size_t used_count; + + /** + * Lists of pools in the cache, indexed by pool size. + */ + pj_list free_list[PJ_CACHING_POOL_ARRAY_SIZE]; + + /** + * List of pools currently allocated by applications. + */ + pj_list used_list; +}; + + + +/** + * Initialize caching pool. + * + * @param ch_pool The caching pool factory to be initialized. + * @param policy Pool factory policy. + * @param max_capacity The total capacity to be retained in the cache. When + * the pool is returned to the cache, it will be kept in + * recycling list if the total capacity of pools in this + * list plus the capacity of the pool is still below this + * value. + */ +PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool, + const pj_pool_factory_policy *policy, + pj_size_t max_capacity); + + +/** + * Destroy caching pool, and release all the pools in the recycling list. + * + * @param ch_pool The caching pool. + */ +PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool ); + +/** + * @} // PJ_CACHING_POOL + */ + +# if PJ_FUNCTIONS_ARE_INLINED +# include "pool_i.h" +# endif + +PJ_END_DECL + +#endif /* __PJ_POOL_H__ */ + diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h index df9b45dc..39c04877 100644 --- a/pjlib/include/pj/pool_i.h +++ b/pjlib/include/pj/pool_i.h @@ -1,91 +1,91 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include <pj/string.h>
-
-PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size);
-
-PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
-{
- return pool->capacity;
-}
-
-PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
-{
- return pool->used_size;
-}
-
-PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_t *pool,
- pj_pool_block *block, pj_size_t size )
-{
- /* The operation below is valid for size==0.
- * When size==0, the function will return the pointer to the pool
- * memory address, but no memory will be allocated.
- */
- if (size & (PJ_POOL_ALIGNMENT-1)) {
- size &= ~(PJ_POOL_ALIGNMENT-1);
- size += PJ_POOL_ALIGNMENT;
- }
- if ((unsigned)(block->end - block->cur) >= size) {
- void *ptr = block->cur;
- block->cur += size;
- pool->used_size += size;
- return ptr;
- }
- return NULL;
-}
-
-PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size)
-{
- pj_pool_block *block = pool->block_list.next;
- void *ptr = pj_pool_alloc_from_block(pool, block, size);
- if (!ptr)
- ptr = pj_pool_allocate_find(pool, size);
- return ptr;
-}
-
-
-PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size)
-{
- void *buf = pj_pool_alloc( pool, size*count);
- if (buf)
- pj_memset(buf, 0, size * count);
- return buf;
-}
-
-PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool )
-{
- return pool->obj_name;
-}
-
-PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- return (*f->create_pool)(f, name, initial_size, increment_size, callback);
-}
-
-PJ_IDEF(void) pj_pool_release( pj_pool_t *pool )
-{
- (*pool->factory->release_pool)(pool->factory, pool);
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <pj/string.h> + +PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size); + +PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) +{ + return pool->capacity; +} + +PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) +{ + return pool->used_size; +} + +PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_t *pool, + pj_pool_block *block, pj_size_t size ) +{ + /* The operation below is valid for size==0. + * When size==0, the function will return the pointer to the pool + * memory address, but no memory will be allocated. + */ + if (size & (PJ_POOL_ALIGNMENT-1)) { + size &= ~(PJ_POOL_ALIGNMENT-1); + size += PJ_POOL_ALIGNMENT; + } + if ((unsigned)(block->end - block->cur) >= size) { + void *ptr = block->cur; + block->cur += size; + pool->used_size += size; + return ptr; + } + return NULL; +} + +PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size) +{ + pj_pool_block *block = pool->block_list.next; + void *ptr = pj_pool_alloc_from_block(pool, block, size); + if (!ptr) + ptr = pj_pool_allocate_find(pool, size); + return ptr; +} + + +PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size) +{ + void *buf = pj_pool_alloc( pool, size*count); + if (buf) + pj_memset(buf, 0, size * count); + return buf; +} + +PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool ) +{ + return pool->obj_name; +} + +PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + return (*f->create_pool)(f, name, initial_size, increment_size, callback); +} + +PJ_IDEF(void) pj_pool_release( pj_pool_t *pool ) +{ + (*pool->factory->release_pool)(pool->factory, pool); +} + diff --git a/pjlib/include/pj/rand.h b/pjlib/include/pj/rand.h index 36e55746..a1cd333b 100644 --- a/pjlib/include/pj/rand.h +++ b/pjlib/include/pj/rand.h @@ -1,65 +1,65 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_RAND_H__
-#define __PJ_RAND_H__
-
-/**
- * @file rand.h
- * @brief Random Number Generator.
- */
-
-#include <pj/config.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_RAND Random Number Generator
- * @ingroup PJ_MISC
- * @{
- * This module contains functions for generating random numbers.
- * This abstraction is needed not only because not all platforms have
- * \a rand() and \a srand(), but also on some platforms \a rand()
- * only has 16-bit randomness, which is not good enough.
- */
-
-/**
- * Put in seed to random number generator.
- *
- * @param seed Seed value.
- */
-PJ_DECL(void) pj_srand(unsigned int seed);
-
-
-/**
- * Generate random integer with 32bit randomness.
- *
- * @return a random integer.
- */
-PJ_DECL(int) pj_rand(void);
-
-
-/** @} */
-
-
-PJ_END_DECL
-
-
-#endif /* __PJ_RAND_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_RAND_H__ +#define __PJ_RAND_H__ + +/** + * @file rand.h + * @brief Random Number Generator. + */ + +#include <pj/config.h> + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_RAND Random Number Generator + * @ingroup PJ_MISC + * @{ + * This module contains functions for generating random numbers. + * This abstraction is needed not only because not all platforms have + * \a rand() and \a srand(), but also on some platforms \a rand() + * only has 16-bit randomness, which is not good enough. + */ + +/** + * Put in seed to random number generator. + * + * @param seed Seed value. + */ +PJ_DECL(void) pj_srand(unsigned int seed); + + +/** + * Generate random integer with 32bit randomness. + * + * @return a random integer. + */ +PJ_DECL(int) pj_rand(void); + + +/** @} */ + + +PJ_END_DECL + + +#endif /* __PJ_RAND_H__ */ + diff --git a/pjlib/include/pj/rbtree.h b/pjlib/include/pj/rbtree.h index 1d4df0be..ed6607fd 100644 --- a/pjlib/include/pj/rbtree.h +++ b/pjlib/include/pj/rbtree.h @@ -1,209 +1,209 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_RBTREE_H__
-#define __PJ_RBTREE_H__
-
-/**
- * @file rbtree.h
- * @brief Red/Black Tree
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_RBTREE Red/Black Balanced Tree
- * @ingroup PJ_DS
- * @brief
- * Red/Black tree is the variant of balanced tree, where the search, insert,
- * and delete operation is \b guaranteed to take at most \a O( lg(n) ).
- * @{
- */
-/**
- * Color type for Red-Black tree.
- */
-typedef enum pj_rbcolor_t
-{
- PJ_RBCOLOR_BLACK,
- PJ_RBCOLOR_RED
-} pj_rbcolor_t;
-
-/**
- * The type of the node of the R/B Tree.
- */
-typedef struct pj_rbtree_node
-{
- /** Pointers to the node's parent, and left and right siblings. */
- struct pj_rbtree_node *parent, *left, *right;
-
- /** Key associated with the node. */
- const void *key;
-
- /** User data associated with the node. */
- void *user_data;
-
- /** The R/B Tree node color. */
- pj_rbcolor_t color;
-
-} pj_rbtree_node;
-
-
-/**
- * The type of function use to compare key value of tree node.
- * @return
- * 0 if the keys are equal
- * <0 if key1 is lower than key2
- * >0 if key1 is greater than key2.
- */
-typedef int pj_rbtree_comp(const void *key1, const void *key2);
-
-
-/**
- * Declaration of a red-black tree. All elements in the tree must have UNIQUE
- * key.
- * A red black tree always maintains the balance of the tree, so that the
- * tree height will not be greater than lg(N). Insert, search, and delete
- * operation will take lg(N) on the worst case. But for insert and delete,
- * there is additional time needed to maintain the balance of the tree.
- */
-typedef struct pj_rbtree
-{
- pj_rbtree_node null_node; /**< Constant to indicate NULL node. */
- pj_rbtree_node *null; /**< Constant to indicate NULL node. */
- pj_rbtree_node *root; /**< Root tree node. */
- unsigned size; /**< Number of elements in the tree. */
- pj_rbtree_comp *comp; /**< Key comparison function. */
-} pj_rbtree;
-
-
-/**
- * Guidance on how much memory required for each of the node.
- */
-#define PJ_RBTREE_NODE_SIZE (sizeof(pj_rbtree_node))
-
-
-/**
- * Guidance on memory required for the tree.
- */
-#define PJ_RBTREE_SIZE (sizeof(pj_rbtree))
-
-
-/**
- * Initialize the tree.
- * @param tree the tree to be initialized.
- * @param comp key comparison function to be used for this tree.
- */
-PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp);
-
-/**
- * Get the first element in the tree.
- * The first element always has the least value for the key, according to
- * the comparison function.
- * @param tree the tree.
- * @return the tree node, or NULL if the tree has no element.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree );
-
-/**
- * Get the last element in the tree.
- * The last element always has the greatest key value, according to the
- * comparison function defined for the tree.
- * @param tree the tree.
- * @return the tree node, or NULL if the tree has no element.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree );
-
-/**
- * Get the successive element for the specified node.
- * The successive element is an element with greater key value.
- * @param tree the tree.
- * @param node the node.
- * @return the successive node, or NULL if the node has no successor.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * The the previous node for the specified node.
- * The previous node is an element with less key value.
- * @param tree the tree.
- * @param node the node.
- * @return the previous node, or NULL if the node has no previous node.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Insert a new node.
- * The node will be inserted at sorted location. The key of the node must
- * be UNIQUE, i.e. it hasn't existed in the tree.
- * @param tree the tree.
- * @param node the node to be inserted.
- * @return zero on success, or -1 if the key already exist.
- */
-PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Find a node which has the specified key.
- * @param tree the tree.
- * @param key the key to search.
- * @return the tree node with the specified key, or NULL if the key can not
- * be found.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
- const void *key );
-
-/**
- * Erase a node from the tree.
- * @param tree the tree.
- * @param node the node to be erased.
- * @return the tree node itself.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Get the maximum tree height from the specified node.
- * @param tree the tree.
- * @param node the node, or NULL to get the root of the tree.
- * @return the maximum height, which should be at most lg(N)
- */
-PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Get the minumum tree height from the specified node.
- * @param tree the tree.
- * @param node the node, or NULL to get the root of the tree.
- * @return the height
- */
-PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJ_RBTREE_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_RBTREE_H__ +#define __PJ_RBTREE_H__ + +/** + * @file rbtree.h + * @brief Red/Black Tree + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_RBTREE Red/Black Balanced Tree + * @ingroup PJ_DS + * @brief + * Red/Black tree is the variant of balanced tree, where the search, insert, + * and delete operation is \b guaranteed to take at most \a O( lg(n) ). + * @{ + */ +/** + * Color type for Red-Black tree. + */ +typedef enum pj_rbcolor_t +{ + PJ_RBCOLOR_BLACK, + PJ_RBCOLOR_RED +} pj_rbcolor_t; + +/** + * The type of the node of the R/B Tree. + */ +typedef struct pj_rbtree_node +{ + /** Pointers to the node's parent, and left and right siblings. */ + struct pj_rbtree_node *parent, *left, *right; + + /** Key associated with the node. */ + const void *key; + + /** User data associated with the node. */ + void *user_data; + + /** The R/B Tree node color. */ + pj_rbcolor_t color; + +} pj_rbtree_node; + + +/** + * The type of function use to compare key value of tree node. + * @return + * 0 if the keys are equal + * <0 if key1 is lower than key2 + * >0 if key1 is greater than key2. + */ +typedef int pj_rbtree_comp(const void *key1, const void *key2); + + +/** + * Declaration of a red-black tree. All elements in the tree must have UNIQUE + * key. + * A red black tree always maintains the balance of the tree, so that the + * tree height will not be greater than lg(N). Insert, search, and delete + * operation will take lg(N) on the worst case. But for insert and delete, + * there is additional time needed to maintain the balance of the tree. + */ +typedef struct pj_rbtree +{ + pj_rbtree_node null_node; /**< Constant to indicate NULL node. */ + pj_rbtree_node *null; /**< Constant to indicate NULL node. */ + pj_rbtree_node *root; /**< Root tree node. */ + unsigned size; /**< Number of elements in the tree. */ + pj_rbtree_comp *comp; /**< Key comparison function. */ +} pj_rbtree; + + +/** + * Guidance on how much memory required for each of the node. + */ +#define PJ_RBTREE_NODE_SIZE (sizeof(pj_rbtree_node)) + + +/** + * Guidance on memory required for the tree. + */ +#define PJ_RBTREE_SIZE (sizeof(pj_rbtree)) + + +/** + * Initialize the tree. + * @param tree the tree to be initialized. + * @param comp key comparison function to be used for this tree. + */ +PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp); + +/** + * Get the first element in the tree. + * The first element always has the least value for the key, according to + * the comparison function. + * @param tree the tree. + * @return the tree node, or NULL if the tree has no element. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ); + +/** + * Get the last element in the tree. + * The last element always has the greatest key value, according to the + * comparison function defined for the tree. + * @param tree the tree. + * @return the tree node, or NULL if the tree has no element. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ); + +/** + * Get the successive element for the specified node. + * The successive element is an element with greater key value. + * @param tree the tree. + * @param node the node. + * @return the successive node, or NULL if the node has no successor. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * The the previous node for the specified node. + * The previous node is an element with less key value. + * @param tree the tree. + * @param node the node. + * @return the previous node, or NULL if the node has no previous node. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Insert a new node. + * The node will be inserted at sorted location. The key of the node must + * be UNIQUE, i.e. it hasn't existed in the tree. + * @param tree the tree. + * @param node the node to be inserted. + * @return zero on success, or -1 if the key already exist. + */ +PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Find a node which has the specified key. + * @param tree the tree. + * @param key the key to search. + * @return the tree node with the specified key, or NULL if the key can not + * be found. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, + const void *key ); + +/** + * Erase a node from the tree. + * @param tree the tree. + * @param node the node to be erased. + * @return the tree node itself. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Get the maximum tree height from the specified node. + * @param tree the tree. + * @param node the node, or NULL to get the root of the tree. + * @return the maximum height, which should be at most lg(N) + */ +PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Get the minumum tree height from the specified node. + * @param tree the tree. + * @param node the node, or NULL to get the root of the tree. + * @return the height + */ +PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree, + pj_rbtree_node *node ); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJ_RBTREE_H__ */ + diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h index ebadcf38..02d88c1e 100644 --- a/pjlib/include/pj/sock.h +++ b/pjlib/include/pj/sock.h @@ -1,700 +1,700 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_SOCK_H__
-#define __PJ_SOCK_H__
-
-/**
- * @file sock.h
- * @brief Socket Abstraction.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_SOCK Socket Abstraction
- * @ingroup PJ_IO
- * @{
- *
- * The PJLIB socket abstraction layer is a thin and very portable abstraction
- * for socket API. It provides API similar to BSD socket API. The abstraction
- * is needed because BSD socket API is not always available on all platforms,
- * therefore it wouldn't be possible to create a trully portable network
- * programs unless we provide such abstraction.
- *
- * Applications can use this API directly in their application, just
- * as they would when using traditional BSD socket API, provided they
- * call #pj_init() first.
- *
- * \section pj_sock_examples_sec Examples
- *
- * For some examples on how to use the socket API, please see:
- *
- * - \ref page_pjlib_sock_test
- * - \ref page_pjlib_select_test
- * - \ref page_pjlib_sock_perf_test
- */
-
-
-/**
- * Supported address families.
- * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL AF_*, BECAUSE
- * THE LIBRARY WILL DO TRANSLATION TO THE NATIVE VALUE.
- */
-extern const pj_uint16_t PJ_AF_UNIX; /**< Unix domain socket. */
-#define PJ_AF_LOCAL PJ_AF_UNIX; /**< POSIX name for AF_UNIX */
-extern const pj_uint16_t PJ_AF_INET; /**< Internet IP protocol. */
-extern const pj_uint16_t PJ_AF_INET6; /**< IP version 6. */
-extern const pj_uint16_t PJ_AF_PACKET; /**< Packet family. */
-extern const pj_uint16_t PJ_AF_IRDA; /**< IRDA sockets. */
-
-
-/**
- * Supported types of sockets.
- * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOCK_*, BECAUSE
- * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE.
- */
-
-extern const pj_uint16_t PJ_SOCK_STREAM; /**< Sequenced, reliable, connection-
- based byte streams. */
-extern const pj_uint16_t PJ_SOCK_DGRAM; /**< Connectionless, unreliable
- datagrams of fixed maximum
- lengths. */
-extern const pj_uint16_t PJ_SOCK_RAW; /**< Raw protocol interface. */
-extern const pj_uint16_t PJ_SOCK_RDM; /**< Reliably-delivered messages. */
-
-
-/**
- * Socket level specified in #pj_sock_setsockopt() or #pj_sock_getsockopt().
- * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOL_*, BECAUSE
- * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE.
- */
-extern const pj_uint16_t PJ_SOL_SOCKET; /**< Socket level. */
-extern const pj_uint16_t PJ_SOL_IP; /**< IP level. */
-extern const pj_uint16_t PJ_SOL_TCP; /**< TCP level. */
-extern const pj_uint16_t PJ_SOL_UDP; /**< UDP level. */
-extern const pj_uint16_t PJ_SOL_IPV6; /**< IP version 6 */
-
-/**
- * Values to be specified as \c optname when calling #pj_sock_setsockopt()
- * or #pj_sock_getsockopt().
- */
-extern const pj_uint16_t PJ_SO_TYPE; /**< Socket type. */
-extern const pj_uint16_t PJ_SO_RCVBUF; /**< Buffer size for receive. */
-extern const pj_uint16_t PJ_SO_SNDBUF; /**< Buffer size for send. */
-
-
-/**
- * Flags to be specified in #pj_sock_recv, #pj_sock_send, etc.
- */
-typedef enum pj_sock_msg_flag
-{
- PJ_MSG_OOB = 0x01, /**< Out-of-band messages. */
- PJ_MSG_PEEK = 0x02, /**< Peek, don't remove from buffer. */
- PJ_MSG_DONTROUTE = 0x04, /**< Don't route. */
-} pj_sock_msg_flag;
-
-
-/**
- * Flag to be specified in #pj_sock_shutdown.
- */
-typedef enum pj_socket_sd_type
-{
- PJ_SD_RECEIVE = 0, /**< No more receive. */
- PJ_SHUT_RD = 0, /**< Alias for SD_RECEIVE. */
- PJ_SD_SEND = 1, /**< No more sending. */
- PJ_SHUT_WR = 1, /**< Alias for SD_SEND. */
- PJ_SD_BOTH = 2, /**< No more send and receive. */
- PJ_SHUT_RDWR = 2, /**< Alias for SD_BOTH. */
-} pj_socket_sd_type;
-
-
-
-/** Address to accept any incoming messages. */
-#define PJ_INADDR_ANY ((pj_uint32_t)0)
-
-/** Address indicating an error return */
-#define PJ_INADDR_NONE ((pj_uint32_t)0xffffffff)
-
-/** Address to send to all hosts. */
-#define PJ_INADDR_BROADCAST ((pj_uint32_t)0xffffffff)
-
-
-/**
- * Maximum length specifiable by #pj_sock_listen().
- * If the build system doesn't override this value, then the lowest
- * denominator (five, in Win32 systems) will be used.
- */
-#if !defined(PJ_SOMAXCONN)
-# define PJ_SOMAXCONN 5
-#endif
-
-
-/**
- * Constant for invalid socket returned by #pj_sock_socket() and
- * #pj_sock_accept().
- */
-#define PJ_INVALID_SOCKET (-1)
-
-/**
- * Structure describing a generic socket address.
- */
-typedef struct pj_sockaddr
-{
- pj_uint16_t sa_family; /**< Common data: address family. */
- char sa_data[14]; /**< Address data. */
-} pj_sockaddr;
-
-
-/**
- * This structure describes Internet address.
- */
-typedef struct pj_in_addr
-{
- pj_uint32_t s_addr; /**< The 32bit IP address. */
-} pj_in_addr;
-
-
-/**
- * This structure describes Internet socket address.
- */
-typedef struct pj_sockaddr_in
-{
- pj_uint16_t sin_family; /**< Address family. */
- pj_uint16_t sin_port; /**< Transport layer port number. */
- pj_in_addr sin_addr; /**< IP address. */
- char sin_zero[8]; /**< Padding. */
-} pj_sockaddr_in;
-
-
-/**
- * This structure describes IPv6 address.
- */
-typedef struct pj_in6_addr
-{
- /** Union of address formats. */
- union {
- pj_uint8_t u6_addr8[16]; /**< u6_addr8 */
- pj_uint16_t u6_addr16[8]; /**< u6_addr16 */
- pj_uint32_t u6_addr32[4]; /**< u6_addr32 */
- } in6_u;
-/** Shortcut to access in6_u.u6_addr8. */
-#define s6_addr in6_u.u6_addr8
-/** Shortcut to access in6_u.u6_addr16. */
-#define s6_addr16 in6_u.u6_addr16
-/** Shortcut to access in6_u.u6_addr32. */
-#define s6_addr32 in6_u.u6_addr32
-} pj_in6_addr;
-
-/** Initializer value for pj_in6_addr. */
-#define PJ_IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
-
-/** Initializer value for pj_in6_addr. */
-#define PJ_IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
-
-/**
- * This structure describes IPv6 socket address.
- */
-typedef struct pj_sockaddr_in6
-{
- pj_uint16_t sin6_family; /**< Address family */
- pj_uint16_t sin6_port; /**< Transport layer port number. */
- pj_uint32_t sin6_flowinfo; /**< IPv6 flow information */
- pj_in6_addr sin6_addr; /**< IPv6 address. */
- pj_uint32_t sin6_scope_id; /**< IPv6 scope-id */
-} pj_sockaddr_in6;
-
-
-/*****************************************************************************
- *
- * SOCKET ADDRESS MANIPULATION.
- *
- *****************************************************************************
- */
-
-/**
- * Convert 16-bit value from network byte order to host byte order.
- *
- * @param netshort 16-bit network value.
- * @return 16-bit host value.
- */
-PJ_DECL(pj_uint16_t) pj_ntohs(pj_uint16_t netshort);
-
-/**
- * Convert 16-bit value from host byte order to network byte order.
- *
- * @param hostshort 16-bit host value.
- * @return 16-bit network value.
- */
-PJ_DECL(pj_uint16_t) pj_htons(pj_uint16_t hostshort);
-
-/**
- * Convert 32-bit value from network byte order to host byte order.
- *
- * @param netlong 32-bit network value.
- * @return 32-bit host value.
- */
-PJ_DECL(pj_uint32_t) pj_ntohl(pj_uint32_t netlong);
-
-/**
- * Convert 32-bit value from host byte order to network byte order.
- *
- * @param hostlong 32-bit host value.
- * @return 32-bit network value.
- */
-PJ_DECL(pj_uint32_t) pj_htonl(pj_uint32_t hostlong);
-
-/**
- * Convert an Internet host address given in network byte order
- * to string in standard numbers and dots notation.
- *
- * @param inaddr The host address.
- * @return The string address.
- */
-PJ_DECL(char*) pj_inet_ntoa(pj_in_addr inaddr);
-
-/**
- * This function converts the Internet host address cp from the standard
- * numbers-and-dots notation into binary data and stores it in the structure
- * that inp points to.
- *
- * @param cp IP address in standard numbers-and-dots notation.
- * @param inp Structure that holds the output of the conversion.
- *
- * @return nonzero if the address is valid, zero if not.
- */
-PJ_DECL(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp);
-
-/**
- * Convert address string with numbers and dots to binary IP address.
- *
- * @param cp The IP address in numbers and dots notation.
- * @return If success, the IP address is returned in network
- * byte order. If failed, PJ_INADDR_NONE will be
- * returned.
- * @remark
- * This is an obsolete interface to #pj_inet_aton(); it is obsolete
- * because -1 is a valid address (255.255.255.255), and #pj_inet_aton()
- * provides a cleaner way to indicate error return.
- */
-PJ_DECL(pj_in_addr) pj_inet_addr(const pj_str_t *cp);
-
-
-/**
- * Get the transport layer port number of an Internet socket address.
- * The port is returned in host byte order.
- *
- * @param addr The IP socket address.
- * @return Port number, in host byte order.
- */
-PJ_INLINE(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr)
-{
- return pj_ntohs(addr->sin_port);
-}
-
-/**
- * Set the port number of an Internet socket address.
- *
- * @param addr The IP socket address.
- * @param hostport The port number, in host byte order.
- */
-PJ_INLINE(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr,
- pj_uint16_t hostport)
-{
- addr->sin_port = pj_htons(hostport);
-}
-
-/**
- * Get the IP address of an Internet socket address.
- * The address is returned as 32bit value in host byte order.
- *
- * @param addr The IP socket address.
- * @return 32bit address, in host byte order.
- */
-PJ_INLINE(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr)
-{
- pj_in_addr in_addr;
- in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr);
- return in_addr;
-};
-
-/**
- * Set the IP address of an Internet socket address.
- *
- * @param addr The IP socket address.
- * @param hostaddr The host address, in host byte order.
- */
-PJ_INLINE(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr,
- pj_uint32_t hostaddr)
-{
- addr->sin_addr.s_addr = pj_htonl(hostaddr);
-}
-
-/**
- * Set the IP address of an IP socket address from string address,
- * with resolving the host if necessary. The string address may be in a
- * standard numbers and dots notation or may be a hostname. If hostname
- * is specified, then the function will resolve the host into the IP
- * address.
- *
- * @param addr The IP socket address to be set.
- * @param cp The address string, which can be in a standard
- * dotted numbers or a hostname to be resolved.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
- const pj_str_t *cp);
-
-/**
- * Set the IP address and port of an IP socket address.
- * The string address may be in a standard numbers and dots notation or
- * may be a hostname. If hostname is specified, then the function will
- * resolve the host into the IP address.
- *
- * @param addr The IP socket address to be set.
- * @param cp The address string, which can be in a standard
- * dotted numbers or a hostname to be resolved.
- * @param port The port number, in host byte order.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
- const pj_str_t *cp,
- pj_uint16_t port);
-
-
-/*****************************************************************************
- *
- * HOST NAME AND ADDRESS.
- *
- *****************************************************************************
- */
-
-/**
- * Get system's host name.
- *
- * @return The hostname, or empty string if the hostname can not
- * be identified.
- */
-PJ_DECL(const pj_str_t*) pj_gethostname(void);
-
-/**
- * Get host's IP address, which the the first IP address that is resolved
- * from the hostname.
- *
- * @return The host's IP address, PJ_INADDR_NONE if the host
- * IP address can not be identified.
- */
-PJ_DECL(pj_in_addr) pj_gethostaddr(void);
-
-
-/*****************************************************************************
- *
- * SOCKET API.
- *
- *****************************************************************************
- */
-
-/**
- * Create new socket/endpoint for communication.
- *
- * @param family Specifies a communication domain; this selects the
- * protocol family which will be used for communication.
- * @param type The socket has the indicated type, which specifies the
- * communication semantics.
- * @param protocol Specifies a particular protocol to be used with the
- * socket. Normally only a single protocol exists to support
- * a particular socket type within a given protocol family,
- * in which a case protocol can be specified as 0.
- * @param sock New socket descriptor, or PJ_INVALID_SOCKET on error.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_socket(int family,
- int type,
- int protocol,
- pj_sock_t *sock);
-
-/**
- * Close the socket descriptor.
- *
- * @param sockfd The socket descriptor.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_close(pj_sock_t sockfd);
-
-
-/**
- * This function gives the socket sockfd the local address my_addr. my_addr is
- * addrlen bytes long. Traditionally, this is called assigning a name to
- * a socket. When a socket is created with #pj_sock_socket(), it exists in a
- * name space (address family) but has no name assigned.
- *
- * @param sockfd The socket desriptor.
- * @param my_addr The local address to bind the socket to.
- * @param addrlen The length of the address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_bind( pj_sock_t sockfd,
- const pj_sockaddr_t *my_addr,
- int addrlen);
-
-/**
- * Bind the IP socket sockfd to the given address and port.
- *
- * @param sockfd The socket descriptor.
- * @param addr Local address to bind the socket to, in host byte order.
- * @param port The local port to bind the socket to, in host byte order.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
- pj_uint32_t addr,
- pj_uint16_t port);
-
-#if PJ_HAS_TCP
-/**
- * Listen for incoming connection. This function only applies to connection
- * oriented sockets (such as PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET), and it
- * indicates the willingness to accept incoming connections.
- *
- * @param sockfd The socket descriptor.
- * @param backlog Defines the maximum length the queue of pending
- * connections may grow to.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
- int backlog );
-
-/**
- * Accept new connection on the specified connection oriented server socket.
- *
- * @param serverfd The server socket.
- * @param newsock New socket on success, of PJ_INVALID_SOCKET if failed.
- * @param addr A pointer to sockaddr type. If the argument is not NULL,
- * it will be filled by the address of connecting entity.
- * @param addrlen Initially specifies the length of the address, and upon
- * return will be filled with the exact address length.
- *
- * @return Zero on success, or the error number.
- */
-PJ_DECL(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
- pj_sock_t *newsock,
- pj_sockaddr_t *addr,
- int *addrlen);
-#endif
-
-/**
- * The file descriptor sockfd must refer to a socket. If the socket is of
- * type PJ_SOCK_DGRAM then the serv_addr address is the address to which
- * datagrams are sent by default, and the only address from which datagrams
- * are received. If the socket is of type PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET,
- * this call attempts to make a connection to another socket. The
- * other socket is specified by serv_addr, which is an address (of length
- * addrlen) in the communications space of the socket. Each communications
- * space interprets the serv_addr parameter in its own way.
- *
- * @param sockfd The socket descriptor.
- * @param serv_addr Server address to connect to.
- * @param addrlen The length of server address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
- const pj_sockaddr_t *serv_addr,
- int addrlen);
-
-/**
- * Return the address of peer which is connected to socket sockfd.
- *
- * @param sockfd The socket descriptor.
- * @param addr Pointer to sockaddr structure to which the address
- * will be returned.
- * @param namelen Initially the length of the addr. Upon return the value
- * will be set to the actual length of the address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_getpeername(pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen);
-
-/**
- * Return the current name of the specified socket.
- *
- * @param sockfd The socket descriptor.
- * @param addr Pointer to sockaddr structure to which the address
- * will be returned.
- * @param namelen Initially the length of the addr. Upon return the value
- * will be set to the actual length of the address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen);
-
-/**
- * Get socket option associated with a socket. Options may exist at multiple
- * protocol levels; they are always present at the uppermost socket level.
- *
- * @param sockfd The socket descriptor.
- * @param level The level which to get the option from.
- * @param optname The option name.
- * @param optval Identifies the buffer which the value will be
- * returned.
- * @param optlen Initially contains the length of the buffer, upon
- * return will be set to the actual size of the value.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- void *optval,
- int *optlen);
-/**
- * Manipulate the options associated with a socket. Options may exist at
- * multiple protocol levels; they are always present at the uppermost socket
- * level.
- *
- * @param sockfd The socket descriptor.
- * @param level The level which to get the option from.
- * @param optname The option name.
- * @param optval Identifies the buffer which contain the value.
- * @param optlen The length of the value.
- *
- * @return PJ_SUCCESS or the status code.
- */
-PJ_DECL(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- const void *optval,
- int optlen);
-
-
-/**
- * Receives data stream or message coming to the specified socket.
- *
- * @param sockfd The socket descriptor.
- * @param buf The buffer to receive the data or message.
- * @param len On input, the length of the buffer. On return,
- * contains the length of data received.
- * @param flags Combination of #pj_sock_msg_flag.
- *
- * @return PJ_SUCCESS or the error code.
- */
-PJ_DECL(pj_status_t) pj_sock_recv(pj_sock_t sockfd,
- void *buf,
- pj_ssize_t *len,
- unsigned flags);
-
-/**
- * Receives data stream or message coming to the specified socket.
- *
- * @param sockfd The socket descriptor.
- * @param buf The buffer to receive the data or message.
- * @param len On input, the length of the buffer. On return,
- * contains the length of data received.
- * @param flags Bitmask combination of #pj_sock_msg_flag.
- * @param from If not NULL, it will be filled with the source
- * address of the connection.
- * @param fromlen Initially contains the length of from address,
- * and upon return will be filled with the actual
- * length of the address.
- *
- * @return PJ_SUCCESS or the error code.
- */
-PJ_DECL(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
- void *buf,
- pj_ssize_t *len,
- unsigned flags,
- pj_sockaddr_t *from,
- int *fromlen);
-
-/**
- * Transmit data to the socket.
- *
- * @param sockfd Socket descriptor.
- * @param buf Buffer containing data to be sent.
- * @param len On input, the length of the data in the buffer.
- * Upon return, it will be filled with the length
- * of data sent.
- * @param flags Bitmask combination of #pj_sock_msg_flag.
- *
- * @return PJ_SUCCESS or the status code.
- */
-PJ_DECL(pj_status_t) pj_sock_send(pj_sock_t sockfd,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags);
-
-/**
- * Transmit data to the socket to the specified address.
- *
- * @param sockfd Socket descriptor.
- * @param buf Buffer containing data to be sent.
- * @param len On input, the length of the data in the buffer.
- * Upon return, it will be filled with the length
- * of data sent.
- * @param flags Bitmask combination of #pj_sock_msg_flag.
- * @param to The address to send.
- * @param tolen The length of the address in bytes.
- *
- * @return The length of data successfully sent.
- */
-PJ_DECL(pj_status_t) pj_sock_sendto(pj_sock_t sockfd,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags,
- const pj_sockaddr_t *to,
- int tolen);
-
-#if PJ_HAS_TCP
-/**
- * The shutdown call causes all or part of a full-duplex connection on the
- * socket associated with sockfd to be shut down.
- *
- * @param sockfd The socket descriptor.
- * @param how If how is PJ_SHUT_RD, further receptions will be
- * disallowed. If how is PJ_SHUT_WR, further transmissions
- * will be disallowed. If how is PJ_SHUT_RDWR, further
- * receptions andtransmissions will be disallowed.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
- int how);
-#endif
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-#endif /* __PJ_SOCK_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_SOCK_H__ +#define __PJ_SOCK_H__ + +/** + * @file sock.h + * @brief Socket Abstraction. + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_SOCK Socket Abstraction + * @ingroup PJ_IO + * @{ + * + * The PJLIB socket abstraction layer is a thin and very portable abstraction + * for socket API. It provides API similar to BSD socket API. The abstraction + * is needed because BSD socket API is not always available on all platforms, + * therefore it wouldn't be possible to create a trully portable network + * programs unless we provide such abstraction. + * + * Applications can use this API directly in their application, just + * as they would when using traditional BSD socket API, provided they + * call #pj_init() first. + * + * \section pj_sock_examples_sec Examples + * + * For some examples on how to use the socket API, please see: + * + * - \ref page_pjlib_sock_test + * - \ref page_pjlib_select_test + * - \ref page_pjlib_sock_perf_test + */ + + +/** + * Supported address families. + * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL AF_*, BECAUSE + * THE LIBRARY WILL DO TRANSLATION TO THE NATIVE VALUE. + */ +extern const pj_uint16_t PJ_AF_UNIX; /**< Unix domain socket. */ +#define PJ_AF_LOCAL PJ_AF_UNIX; /**< POSIX name for AF_UNIX */ +extern const pj_uint16_t PJ_AF_INET; /**< Internet IP protocol. */ +extern const pj_uint16_t PJ_AF_INET6; /**< IP version 6. */ +extern const pj_uint16_t PJ_AF_PACKET; /**< Packet family. */ +extern const pj_uint16_t PJ_AF_IRDA; /**< IRDA sockets. */ + + +/** + * Supported types of sockets. + * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOCK_*, BECAUSE + * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. + */ + +extern const pj_uint16_t PJ_SOCK_STREAM; /**< Sequenced, reliable, connection- + based byte streams. */ +extern const pj_uint16_t PJ_SOCK_DGRAM; /**< Connectionless, unreliable + datagrams of fixed maximum + lengths. */ +extern const pj_uint16_t PJ_SOCK_RAW; /**< Raw protocol interface. */ +extern const pj_uint16_t PJ_SOCK_RDM; /**< Reliably-delivered messages. */ + + +/** + * Socket level specified in #pj_sock_setsockopt() or #pj_sock_getsockopt(). + * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOL_*, BECAUSE + * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. + */ +extern const pj_uint16_t PJ_SOL_SOCKET; /**< Socket level. */ +extern const pj_uint16_t PJ_SOL_IP; /**< IP level. */ +extern const pj_uint16_t PJ_SOL_TCP; /**< TCP level. */ +extern const pj_uint16_t PJ_SOL_UDP; /**< UDP level. */ +extern const pj_uint16_t PJ_SOL_IPV6; /**< IP version 6 */ + +/** + * Values to be specified as \c optname when calling #pj_sock_setsockopt() + * or #pj_sock_getsockopt(). + */ +extern const pj_uint16_t PJ_SO_TYPE; /**< Socket type. */ +extern const pj_uint16_t PJ_SO_RCVBUF; /**< Buffer size for receive. */ +extern const pj_uint16_t PJ_SO_SNDBUF; /**< Buffer size for send. */ + + +/** + * Flags to be specified in #pj_sock_recv, #pj_sock_send, etc. + */ +typedef enum pj_sock_msg_flag +{ + PJ_MSG_OOB = 0x01, /**< Out-of-band messages. */ + PJ_MSG_PEEK = 0x02, /**< Peek, don't remove from buffer. */ + PJ_MSG_DONTROUTE = 0x04, /**< Don't route. */ +} pj_sock_msg_flag; + + +/** + * Flag to be specified in #pj_sock_shutdown. + */ +typedef enum pj_socket_sd_type +{ + PJ_SD_RECEIVE = 0, /**< No more receive. */ + PJ_SHUT_RD = 0, /**< Alias for SD_RECEIVE. */ + PJ_SD_SEND = 1, /**< No more sending. */ + PJ_SHUT_WR = 1, /**< Alias for SD_SEND. */ + PJ_SD_BOTH = 2, /**< No more send and receive. */ + PJ_SHUT_RDWR = 2, /**< Alias for SD_BOTH. */ +} pj_socket_sd_type; + + + +/** Address to accept any incoming messages. */ +#define PJ_INADDR_ANY ((pj_uint32_t)0) + +/** Address indicating an error return */ +#define PJ_INADDR_NONE ((pj_uint32_t)0xffffffff) + +/** Address to send to all hosts. */ +#define PJ_INADDR_BROADCAST ((pj_uint32_t)0xffffffff) + + +/** + * Maximum length specifiable by #pj_sock_listen(). + * If the build system doesn't override this value, then the lowest + * denominator (five, in Win32 systems) will be used. + */ +#if !defined(PJ_SOMAXCONN) +# define PJ_SOMAXCONN 5 +#endif + + +/** + * Constant for invalid socket returned by #pj_sock_socket() and + * #pj_sock_accept(). + */ +#define PJ_INVALID_SOCKET (-1) + +/** + * Structure describing a generic socket address. + */ +typedef struct pj_sockaddr +{ + pj_uint16_t sa_family; /**< Common data: address family. */ + char sa_data[14]; /**< Address data. */ +} pj_sockaddr; + + +/** + * This structure describes Internet address. + */ +typedef struct pj_in_addr +{ + pj_uint32_t s_addr; /**< The 32bit IP address. */ +} pj_in_addr; + + +/** + * This structure describes Internet socket address. + */ +typedef struct pj_sockaddr_in +{ + pj_uint16_t sin_family; /**< Address family. */ + pj_uint16_t sin_port; /**< Transport layer port number. */ + pj_in_addr sin_addr; /**< IP address. */ + char sin_zero[8]; /**< Padding. */ +} pj_sockaddr_in; + + +/** + * This structure describes IPv6 address. + */ +typedef struct pj_in6_addr +{ + /** Union of address formats. */ + union { + pj_uint8_t u6_addr8[16]; /**< u6_addr8 */ + pj_uint16_t u6_addr16[8]; /**< u6_addr16 */ + pj_uint32_t u6_addr32[4]; /**< u6_addr32 */ + } in6_u; +/** Shortcut to access in6_u.u6_addr8. */ +#define s6_addr in6_u.u6_addr8 +/** Shortcut to access in6_u.u6_addr16. */ +#define s6_addr16 in6_u.u6_addr16 +/** Shortcut to access in6_u.u6_addr32. */ +#define s6_addr32 in6_u.u6_addr32 +} pj_in6_addr; + +/** Initializer value for pj_in6_addr. */ +#define PJ_IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } + +/** Initializer value for pj_in6_addr. */ +#define PJ_IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } + +/** + * This structure describes IPv6 socket address. + */ +typedef struct pj_sockaddr_in6 +{ + pj_uint16_t sin6_family; /**< Address family */ + pj_uint16_t sin6_port; /**< Transport layer port number. */ + pj_uint32_t sin6_flowinfo; /**< IPv6 flow information */ + pj_in6_addr sin6_addr; /**< IPv6 address. */ + pj_uint32_t sin6_scope_id; /**< IPv6 scope-id */ +} pj_sockaddr_in6; + + +/***************************************************************************** + * + * SOCKET ADDRESS MANIPULATION. + * + ***************************************************************************** + */ + +/** + * Convert 16-bit value from network byte order to host byte order. + * + * @param netshort 16-bit network value. + * @return 16-bit host value. + */ +PJ_DECL(pj_uint16_t) pj_ntohs(pj_uint16_t netshort); + +/** + * Convert 16-bit value from host byte order to network byte order. + * + * @param hostshort 16-bit host value. + * @return 16-bit network value. + */ +PJ_DECL(pj_uint16_t) pj_htons(pj_uint16_t hostshort); + +/** + * Convert 32-bit value from network byte order to host byte order. + * + * @param netlong 32-bit network value. + * @return 32-bit host value. + */ +PJ_DECL(pj_uint32_t) pj_ntohl(pj_uint32_t netlong); + +/** + * Convert 32-bit value from host byte order to network byte order. + * + * @param hostlong 32-bit host value. + * @return 32-bit network value. + */ +PJ_DECL(pj_uint32_t) pj_htonl(pj_uint32_t hostlong); + +/** + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + * + * @param inaddr The host address. + * @return The string address. + */ +PJ_DECL(char*) pj_inet_ntoa(pj_in_addr inaddr); + +/** + * This function converts the Internet host address cp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + * + * @param cp IP address in standard numbers-and-dots notation. + * @param inp Structure that holds the output of the conversion. + * + * @return nonzero if the address is valid, zero if not. + */ +PJ_DECL(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp); + +/** + * Convert address string with numbers and dots to binary IP address. + * + * @param cp The IP address in numbers and dots notation. + * @return If success, the IP address is returned in network + * byte order. If failed, PJ_INADDR_NONE will be + * returned. + * @remark + * This is an obsolete interface to #pj_inet_aton(); it is obsolete + * because -1 is a valid address (255.255.255.255), and #pj_inet_aton() + * provides a cleaner way to indicate error return. + */ +PJ_DECL(pj_in_addr) pj_inet_addr(const pj_str_t *cp); + + +/** + * Get the transport layer port number of an Internet socket address. + * The port is returned in host byte order. + * + * @param addr The IP socket address. + * @return Port number, in host byte order. + */ +PJ_INLINE(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr) +{ + return pj_ntohs(addr->sin_port); +} + +/** + * Set the port number of an Internet socket address. + * + * @param addr The IP socket address. + * @param hostport The port number, in host byte order. + */ +PJ_INLINE(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, + pj_uint16_t hostport) +{ + addr->sin_port = pj_htons(hostport); +} + +/** + * Get the IP address of an Internet socket address. + * The address is returned as 32bit value in host byte order. + * + * @param addr The IP socket address. + * @return 32bit address, in host byte order. + */ +PJ_INLINE(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr) +{ + pj_in_addr in_addr; + in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr); + return in_addr; +}; + +/** + * Set the IP address of an Internet socket address. + * + * @param addr The IP socket address. + * @param hostaddr The host address, in host byte order. + */ +PJ_INLINE(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, + pj_uint32_t hostaddr) +{ + addr->sin_addr.s_addr = pj_htonl(hostaddr); +} + +/** + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + * + * @param addr The IP socket address to be set. + * @param cp The address string, which can be in a standard + * dotted numbers or a hostname to be resolved. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *cp); + +/** + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + * + * @param addr The IP socket address to be set. + * @param cp The address string, which can be in a standard + * dotted numbers or a hostname to be resolved. + * @param port The port number, in host byte order. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *cp, + pj_uint16_t port); + + +/***************************************************************************** + * + * HOST NAME AND ADDRESS. + * + ***************************************************************************** + */ + +/** + * Get system's host name. + * + * @return The hostname, or empty string if the hostname can not + * be identified. + */ +PJ_DECL(const pj_str_t*) pj_gethostname(void); + +/** + * Get host's IP address, which the the first IP address that is resolved + * from the hostname. + * + * @return The host's IP address, PJ_INADDR_NONE if the host + * IP address can not be identified. + */ +PJ_DECL(pj_in_addr) pj_gethostaddr(void); + + +/***************************************************************************** + * + * SOCKET API. + * + ***************************************************************************** + */ + +/** + * Create new socket/endpoint for communication. + * + * @param family Specifies a communication domain; this selects the + * protocol family which will be used for communication. + * @param type The socket has the indicated type, which specifies the + * communication semantics. + * @param protocol Specifies a particular protocol to be used with the + * socket. Normally only a single protocol exists to support + * a particular socket type within a given protocol family, + * in which a case protocol can be specified as 0. + * @param sock New socket descriptor, or PJ_INVALID_SOCKET on error. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_socket(int family, + int type, + int protocol, + pj_sock_t *sock); + +/** + * Close the socket descriptor. + * + * @param sockfd The socket descriptor. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_close(pj_sock_t sockfd); + + +/** + * This function gives the socket sockfd the local address my_addr. my_addr is + * addrlen bytes long. Traditionally, this is called assigning a name to + * a socket. When a socket is created with #pj_sock_socket(), it exists in a + * name space (address family) but has no name assigned. + * + * @param sockfd The socket desriptor. + * @param my_addr The local address to bind the socket to. + * @param addrlen The length of the address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_bind( pj_sock_t sockfd, + const pj_sockaddr_t *my_addr, + int addrlen); + +/** + * Bind the IP socket sockfd to the given address and port. + * + * @param sockfd The socket descriptor. + * @param addr Local address to bind the socket to, in host byte order. + * @param port The local port to bind the socket to, in host byte order. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, + pj_uint32_t addr, + pj_uint16_t port); + +#if PJ_HAS_TCP +/** + * Listen for incoming connection. This function only applies to connection + * oriented sockets (such as PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET), and it + * indicates the willingness to accept incoming connections. + * + * @param sockfd The socket descriptor. + * @param backlog Defines the maximum length the queue of pending + * connections may grow to. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_listen( pj_sock_t sockfd, + int backlog ); + +/** + * Accept new connection on the specified connection oriented server socket. + * + * @param serverfd The server socket. + * @param newsock New socket on success, of PJ_INVALID_SOCKET if failed. + * @param addr A pointer to sockaddr type. If the argument is not NULL, + * it will be filled by the address of connecting entity. + * @param addrlen Initially specifies the length of the address, and upon + * return will be filled with the exact address length. + * + * @return Zero on success, or the error number. + */ +PJ_DECL(pj_status_t) pj_sock_accept( pj_sock_t serverfd, + pj_sock_t *newsock, + pj_sockaddr_t *addr, + int *addrlen); +#endif + +/** + * The file descriptor sockfd must refer to a socket. If the socket is of + * type PJ_SOCK_DGRAM then the serv_addr address is the address to which + * datagrams are sent by default, and the only address from which datagrams + * are received. If the socket is of type PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET, + * this call attempts to make a connection to another socket. The + * other socket is specified by serv_addr, which is an address (of length + * addrlen) in the communications space of the socket. Each communications + * space interprets the serv_addr parameter in its own way. + * + * @param sockfd The socket descriptor. + * @param serv_addr Server address to connect to. + * @param addrlen The length of server address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_connect( pj_sock_t sockfd, + const pj_sockaddr_t *serv_addr, + int addrlen); + +/** + * Return the address of peer which is connected to socket sockfd. + * + * @param sockfd The socket descriptor. + * @param addr Pointer to sockaddr structure to which the address + * will be returned. + * @param namelen Initially the length of the addr. Upon return the value + * will be set to the actual length of the address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_getpeername(pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen); + +/** + * Return the current name of the specified socket. + * + * @param sockfd The socket descriptor. + * @param addr Pointer to sockaddr structure to which the address + * will be returned. + * @param namelen Initially the length of the addr. Upon return the value + * will be set to the actual length of the address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen); + +/** + * Get socket option associated with a socket. Options may exist at multiple + * protocol levels; they are always present at the uppermost socket level. + * + * @param sockfd The socket descriptor. + * @param level The level which to get the option from. + * @param optname The option name. + * @param optval Identifies the buffer which the value will be + * returned. + * @param optlen Initially contains the length of the buffer, upon + * return will be set to the actual size of the value. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, + pj_uint16_t level, + pj_uint16_t optname, + void *optval, + int *optlen); +/** + * Manipulate the options associated with a socket. Options may exist at + * multiple protocol levels; they are always present at the uppermost socket + * level. + * + * @param sockfd The socket descriptor. + * @param level The level which to get the option from. + * @param optname The option name. + * @param optval Identifies the buffer which contain the value. + * @param optlen The length of the value. + * + * @return PJ_SUCCESS or the status code. + */ +PJ_DECL(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, + pj_uint16_t level, + pj_uint16_t optname, + const void *optval, + int optlen); + + +/** + * Receives data stream or message coming to the specified socket. + * + * @param sockfd The socket descriptor. + * @param buf The buffer to receive the data or message. + * @param len On input, the length of the buffer. On return, + * contains the length of data received. + * @param flags Combination of #pj_sock_msg_flag. + * + * @return PJ_SUCCESS or the error code. + */ +PJ_DECL(pj_status_t) pj_sock_recv(pj_sock_t sockfd, + void *buf, + pj_ssize_t *len, + unsigned flags); + +/** + * Receives data stream or message coming to the specified socket. + * + * @param sockfd The socket descriptor. + * @param buf The buffer to receive the data or message. + * @param len On input, the length of the buffer. On return, + * contains the length of data received. + * @param flags Bitmask combination of #pj_sock_msg_flag. + * @param from If not NULL, it will be filled with the source + * address of the connection. + * @param fromlen Initially contains the length of from address, + * and upon return will be filled with the actual + * length of the address. + * + * @return PJ_SUCCESS or the error code. + */ +PJ_DECL(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, + void *buf, + pj_ssize_t *len, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen); + +/** + * Transmit data to the socket. + * + * @param sockfd Socket descriptor. + * @param buf Buffer containing data to be sent. + * @param len On input, the length of the data in the buffer. + * Upon return, it will be filled with the length + * of data sent. + * @param flags Bitmask combination of #pj_sock_msg_flag. + * + * @return PJ_SUCCESS or the status code. + */ +PJ_DECL(pj_status_t) pj_sock_send(pj_sock_t sockfd, + const void *buf, + pj_ssize_t *len, + unsigned flags); + +/** + * Transmit data to the socket to the specified address. + * + * @param sockfd Socket descriptor. + * @param buf Buffer containing data to be sent. + * @param len On input, the length of the data in the buffer. + * Upon return, it will be filled with the length + * of data sent. + * @param flags Bitmask combination of #pj_sock_msg_flag. + * @param to The address to send. + * @param tolen The length of the address in bytes. + * + * @return The length of data successfully sent. + */ +PJ_DECL(pj_status_t) pj_sock_sendto(pj_sock_t sockfd, + const void *buf, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *to, + int tolen); + +#if PJ_HAS_TCP +/** + * The shutdown call causes all or part of a full-duplex connection on the + * socket associated with sockfd to be shut down. + * + * @param sockfd The socket descriptor. + * @param how If how is PJ_SHUT_RD, further receptions will be + * disallowed. If how is PJ_SHUT_WR, further transmissions + * will be disallowed. If how is PJ_SHUT_RDWR, further + * receptions andtransmissions will be disallowed. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, + int how); +#endif + +/** + * @} + */ + + +PJ_END_DECL + +#endif /* __PJ_SOCK_H__ */ + diff --git a/pjlib/include/pj/sock_select.h b/pjlib/include/pj/sock_select.h index 4a974384..0b94de86 100644 --- a/pjlib/include/pj/sock_select.h +++ b/pjlib/include/pj/sock_select.h @@ -1,136 +1,136 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_SELECT_H__
-#define __PJ_SELECT_H__
-
-/**
- * @file sock_select.h
- * @brief Socket select().
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_SOCK_SELECT Socket select() API.
- * @ingroup PJ_IO
- * @{
- * This module provides portable abstraction for \a select() like API.
- * The abstraction is needed so that it can utilize various event
- * dispatching mechanisms that are available across platforms.
- *
- * The API is very similar to normal \a select() usage.
- *
- * \section pj_sock_select_examples_sec Examples
- *
- * For some examples on how to use the select API, please see:
- *
- * - \ref page_pjlib_select_test
- */
-
-/**
- * Portable structure declarations for pj_fd_set.
- * The implementation of pj_sock_select() does not use this structure
- * per-se, but instead it will use the native fd_set structure. However,
- * we must make sure that the size of pj_fd_set_t can accomodate the
- * native fd_set structure.
- */
-typedef struct pj_fd_set_t
-{
- pj_sock_t data[FD_SETSIZE + 4]; /**< Opaque buffer for fd_set */
-} pj_fd_set_t;
-
-
-/**
- * Initialize the descriptor set pointed to by fdsetp to the null set.
- *
- * @param fdsetp The descriptor set.
- */
-PJ_DECL(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp);
-
-
-/**
- * Add the file descriptor fd to the set pointed to by fdsetp.
- * If the file descriptor fd is already in this set, there shall be no effect
- * on the set, nor will an error be returned.
- *
- * @param fd The socket descriptor.
- * @param fdsetp The descriptor set.
- */
-PJ_DECL(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp);
-
-/**
- * Remove the file descriptor fd from the set pointed to by fdsetp.
- * If fd is not a member of this set, there shall be no effect on the set,
- * nor will an error be returned.
- *
- * @param fd The socket descriptor.
- * @param fdsetp The descriptor set.
- */
-PJ_DECL(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp);
-
-
-/**
- * Evaluate to non-zero if the file descriptor fd is a member of the set
- * pointed to by fdsetp, and shall evaluate to zero otherwise.
- *
- * @param fd The socket descriptor.
- * @param fdsetp The descriptor set.
- *
- * @return Nonzero if fd is member of the descriptor set.
- */
-PJ_DECL(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp);
-
-
-/**
- * This function wait for a number of file descriptors to change status.
- * The behaviour is the same as select() function call which appear in
- * standard BSD socket libraries.
- *
- * @param n On Unices, this specifies the highest-numbered
- * descriptor in any of the three set, plus 1. On Windows,
- * the value is ignored.
- * @param readfds Optional pointer to a set of sockets to be checked for
- * readability.
- * @param writefds Optional pointer to a set of sockets to be checked for
- * writability.
- * @param exceptfds Optional pointer to a set of sockets to be checked for
- * errors.
- * @param timeout Maximum time for select to wait, or null for blocking
- * operations.
- *
- * @return The total number of socket handles that are ready, or
- * zero if the time limit expired, or -1 if an error occurred.
- */
-PJ_DECL(int) pj_sock_select( int n,
- pj_fd_set_t *readfds,
- pj_fd_set_t *writefds,
- pj_fd_set_t *exceptfds,
- const pj_time_val *timeout);
-
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-#endif /* __PJ_SELECT_H__ */
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_SELECT_H__ +#define __PJ_SELECT_H__ + +/** + * @file sock_select.h + * @brief Socket select(). + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_SOCK_SELECT Socket select() API. + * @ingroup PJ_IO + * @{ + * This module provides portable abstraction for \a select() like API. + * The abstraction is needed so that it can utilize various event + * dispatching mechanisms that are available across platforms. + * + * The API is very similar to normal \a select() usage. + * + * \section pj_sock_select_examples_sec Examples + * + * For some examples on how to use the select API, please see: + * + * - \ref page_pjlib_select_test + */ + +/** + * Portable structure declarations for pj_fd_set. + * The implementation of pj_sock_select() does not use this structure + * per-se, but instead it will use the native fd_set structure. However, + * we must make sure that the size of pj_fd_set_t can accomodate the + * native fd_set structure. + */ +typedef struct pj_fd_set_t +{ + pj_sock_t data[FD_SETSIZE + 4]; /**< Opaque buffer for fd_set */ +} pj_fd_set_t; + + +/** + * Initialize the descriptor set pointed to by fdsetp to the null set. + * + * @param fdsetp The descriptor set. + */ +PJ_DECL(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp); + + +/** + * Add the file descriptor fd to the set pointed to by fdsetp. + * If the file descriptor fd is already in this set, there shall be no effect + * on the set, nor will an error be returned. + * + * @param fd The socket descriptor. + * @param fdsetp The descriptor set. + */ +PJ_DECL(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp); + +/** + * Remove the file descriptor fd from the set pointed to by fdsetp. + * If fd is not a member of this set, there shall be no effect on the set, + * nor will an error be returned. + * + * @param fd The socket descriptor. + * @param fdsetp The descriptor set. + */ +PJ_DECL(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp); + + +/** + * Evaluate to non-zero if the file descriptor fd is a member of the set + * pointed to by fdsetp, and shall evaluate to zero otherwise. + * + * @param fd The socket descriptor. + * @param fdsetp The descriptor set. + * + * @return Nonzero if fd is member of the descriptor set. + */ +PJ_DECL(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp); + + +/** + * This function wait for a number of file descriptors to change status. + * The behaviour is the same as select() function call which appear in + * standard BSD socket libraries. + * + * @param n On Unices, this specifies the highest-numbered + * descriptor in any of the three set, plus 1. On Windows, + * the value is ignored. + * @param readfds Optional pointer to a set of sockets to be checked for + * readability. + * @param writefds Optional pointer to a set of sockets to be checked for + * writability. + * @param exceptfds Optional pointer to a set of sockets to be checked for + * errors. + * @param timeout Maximum time for select to wait, or null for blocking + * operations. + * + * @return The total number of socket handles that are ready, or + * zero if the time limit expired, or -1 if an error occurred. + */ +PJ_DECL(int) pj_sock_select( int n, + pj_fd_set_t *readfds, + pj_fd_set_t *writefds, + pj_fd_set_t *exceptfds, + const pj_time_val *timeout); + + +/** + * @} + */ + + +PJ_END_DECL + +#endif /* __PJ_SELECT_H__ */ diff --git a/pjlib/include/pj/string.h b/pjlib/include/pj/string.h index 8edcba8d..c7310b59 100644 --- a/pjlib/include/pj/string.h +++ b/pjlib/include/pj/string.h @@ -1,561 +1,561 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_STRING_H__
-#define __PJ_STRING_H__
-
-/**
- * @file string.h
- * @brief PJLIB String Operations.
- */
-
-#include <pj/types.h>
-#include <pj/compat/string.h>
-#include <pj/compat/sprintf.h>
-#include <pj/compat/vsprintf.h>
-
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_PSTR String Operations
- * @ingroup PJ_DS
- * @{
- * This module provides string manipulation API.
- *
- * \section pj_pstr_not_null_sec PJLIB String is NOT Null Terminated!
- *
- * That is the first information that developers need to know. Instead
- * of using normal C string, strings in PJLIB are represented as
- * pj_str_t structure below:
- *
- * <pre>
- * typedef struct pj_str_t
- * {
- * char *ptr;
- * pj_size_t slen;
- * } pj_str_t;
- * </pre>
- *
- * There are some advantages of using this approach:
- * - the string can point to arbitrary location in memory even
- * if the string in that location is not null terminated. This is
- * most usefull for text parsing, where the parsed text can just
- * point to the original text in the input. If we use C string,
- * then we will have to copy the text portion from the input
- * to a string variable.
- * - because the length of the string is known, string copy operation
- * can be made more efficient.
- *
- * Most of APIs in PJLIB that expect or return string will represent
- * the string as pj_str_t instead of normal C string.
- *
- * \section pj_pstr_examples_sec Examples
- *
- * For some examples, please see:
- * - @ref page_pjlib_string_test
- */
-
-/**
- * Create string initializer from a normal C string.
- *
- * @param str Null terminated string to be stored.
- *
- * @return pj_str_t.
- */
-PJ_IDECL(pj_str_t) pj_str(char *str);
-
-/**
- * Create constant string from normal C string.
- *
- * @param str The string to be initialized.
- * @param s Null terminated string.
- *
- * @return pj_str_t.
- */
-PJ_INLINE(const pj_str_t*) pj_cstr(pj_str_t *str, const char *s)
-{
- str->ptr = (char*)s;
- str->slen = s ? strlen(s) : 0;
- return str;
-}
-
-/**
- * Set the pointer and length to the specified value.
- *
- * @param str the string.
- * @param ptr pointer to set.
- * @param length length to set.
- *
- * @return the string.
- */
-PJ_INLINE(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length)
-{
- str->ptr = ptr;
- str->slen = length;
- return str;
-}
-
-/**
- * Set the pointer and length of the string to the source string, which
- * must be NULL terminated.
- *
- * @param str the string.
- * @param src pointer to set.
- *
- * @return the string.
- */
-PJ_INLINE(pj_str_t*) pj_strset2( pj_str_t *str, char *src)
-{
- str->ptr = src;
- str->slen = src ? strlen(src) : 0;
- return str;
-}
-
-/**
- * Set the pointer and the length of the string.
- *
- * @param str The target string.
- * @param begin The start of the string.
- * @param end The end of the string.
- *
- * @return the target string.
- */
-PJ_INLINE(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end )
-{
- str->ptr = begin;
- str->slen = end-begin;
- return str;
-}
-
-/**
- * Assign string.
- *
- * @param dst The target string.
- * @param src The source string.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src );
-
-/**
- * Copy string contents.
- *
- * @param dst The target string.
- * @param src The source string.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src);
-
-/**
- * Copy string contents.
- *
- * @param dst The target string.
- * @param src The source string.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src);
-
-/**
- * Copy source string to destination up to the specified max length.
- *
- * @param dst The target string.
- * @param src The source string.
- * @param max Maximum characters to copy.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strncpy(pj_str_t *dst, const pj_str_t *src,
- pj_ssize_t max);
-
-/**
- * Copy source string to destination up to the specified max length,
- * and NULL terminate the destination. If source string length is
- * greater than or equal to max, then max-1 will be copied.
- *
- * @param dst The target string.
- * @param src The source string.
- * @param max Maximum characters to copy.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strncpy_with_null(pj_str_t *dst, const pj_str_t *src,
- pj_ssize_t max);
-
-/**
- * Duplicate string.
- *
- * @param pool The pool.
- * @param dst The string result.
- * @param src The string to duplicate.
- *
- * @return the string result.
- */
-PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src);
-
-/**
- * Duplicate string and NULL terminate the destination string.
- *
- * @param pool
- * @param dst
- * @param src
- */
-PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src);
-
-/**
- * Duplicate string.
- *
- * @param pool The pool.
- * @param dst The string result.
- * @param src The string to duplicate.
- *
- * @return the string result.
- */
-PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool,
- pj_str_t *dst,
- const char *src);
-
-/**
- * Duplicate string.
- *
- * @param pool The pool.
- * @param src The string to duplicate.
- *
- * @return the string result.
- */
-PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src);
-
-/**
- * Return the length of the string.
- *
- * @param str The string.
- *
- * @return the length of the string.
- */
-PJ_INLINE(pj_size_t) pj_strlen( const pj_str_t *str )
-{
- return str->slen;
-}
-
-/**
- * Return the pointer to the string data.
- *
- * @param str The string.
- *
- * @return the pointer to the string buffer.
- */
-PJ_INLINE(const char*) pj_strbuf( const pj_str_t *str )
-{
- return str->ptr;
-}
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2);
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 );
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len);
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len);
-
-/**
- * Concatenate strings.
- *
- * @param dst The destination string.
- * @param src The source string.
- */
-PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src);
-
-/**
- * Finds a character in a string.
- *
- * @param str The string.
- * @param chr The character to find.
- *
- * @return the pointer to first character found, or NULL.
- */
-PJ_INLINE(char*) pj_strchr( const pj_str_t *str, int chr)
-{
- return (char*) memchr(str->ptr, chr, str->slen);
-}
-
-/**
- * Remove (trim) leading whitespaces from the string.
- *
- * @param str The string.
- *
- * @return the string.
- */
-PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str );
-
-/**
- * Remove (trim) the trailing whitespaces from the string.
- *
- * @param str The string.
- *
- * @return the string.
- */
-PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str );
-
-/**
- * Remove (trim) leading and trailing whitespaces from the string.
- *
- * @param str The string.
- *
- * @return the string.
- */
-PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str );
-
-/**
- * Initialize the buffer with some random string.
- *
- * @param str the string to store the result.
- * @param length the length of the random string to generate.
- *
- * @return the string.
- */
-PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length);
-
-/**
- * Convert string to unsigned integer.
- *
- * @param str the string.
- *
- * @return the unsigned integer.
- */
-PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str);
-
-/**
- * Utility to convert unsigned integer to string. Note that the
- * string will be NULL terminated.
- *
- * @param val the unsigned integer value.
- * @param buf the buffer
- *
- * @return the number of characters written
- */
-PJ_DECL(int) pj_utoa(unsigned long val, char *buf);
-
-/**
- * Convert unsigned integer to string with minimum digits. Note that the
- * string will be NULL terminated.
- *
- * @param val The unsigned integer value.
- * @param buf The buffer.
- * @param min_dig Minimum digits to be printed, or zero to specify no
- * minimum digit.
- * @param pad The padding character to be put in front of the string
- * when the digits is less than minimum.
- *
- * @return the number of characters written.
- */
-PJ_DECL(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad);
-
-/**
- * Fill the memory location with value.
- *
- * @param dst The destination buffer.
- * @param c Character to set.
- * @param size The number of characters.
- *
- * @return the value of dst.
- */
-PJ_INLINE(void*) pj_memset(void *dst, int c, pj_size_t size)
-{
- return memset(dst, c, size);
-}
-
-/**
- * Copy buffer.
- *
- * @param dst The destination buffer.
- * @param src The source buffer.
- * @param size The size to copy.
- *
- * @return the destination buffer.
- */
-PJ_INLINE(void*) pj_memcpy(void *dst, const void *src, pj_size_t size)
-{
- return memcpy(dst, src, size);
-}
-
-/**
- * Move memory.
- *
- * @param dst The destination buffer.
- * @param src The source buffer.
- * @param size The size to copy.
- *
- * @return the destination buffer.
- */
-PJ_INLINE(void*) pj_memmove(void *dst, const void *src, pj_size_t size)
-{
- return memmove(dst, src, size);
-}
-
-/**
- * Compare buffers.
- *
- * @param buf1 The first buffer.
- * @param buf2 The second buffer.
- * @param size The size to compare.
- *
- * @return negative, zero, or positive value.
- */
-PJ_INLINE(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size)
-{
- return memcmp(buf1, buf2, size);
-}
-
-/**
- * Find character in the buffer.
- *
- * @param buf The buffer.
- * @param c The character to find.
- * @param size The size to check.
- *
- * @return the pointer to location where the character is found, or NULL if
- * not found.
- */
-PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size)
-{
- return memchr(buf, c, size);
-}
-
-
-/**
- * @}
- */
-
-#if PJ_FUNCTIONS_ARE_INLINED
-# include <pj/string_i.h>
-#endif
-
-PJ_END_DECL
-
-#endif /* __PJ_STRING_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_STRING_H__ +#define __PJ_STRING_H__ + +/** + * @file string.h + * @brief PJLIB String Operations. + */ + +#include <pj/types.h> +#include <pj/compat/string.h> +#include <pj/compat/sprintf.h> +#include <pj/compat/vsprintf.h> + + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_PSTR String Operations + * @ingroup PJ_DS + * @{ + * This module provides string manipulation API. + * + * \section pj_pstr_not_null_sec PJLIB String is NOT Null Terminated! + * + * That is the first information that developers need to know. Instead + * of using normal C string, strings in PJLIB are represented as + * pj_str_t structure below: + * + * <pre> + * typedef struct pj_str_t + * { + * char *ptr; + * pj_size_t slen; + * } pj_str_t; + * </pre> + * + * There are some advantages of using this approach: + * - the string can point to arbitrary location in memory even + * if the string in that location is not null terminated. This is + * most usefull for text parsing, where the parsed text can just + * point to the original text in the input. If we use C string, + * then we will have to copy the text portion from the input + * to a string variable. + * - because the length of the string is known, string copy operation + * can be made more efficient. + * + * Most of APIs in PJLIB that expect or return string will represent + * the string as pj_str_t instead of normal C string. + * + * \section pj_pstr_examples_sec Examples + * + * For some examples, please see: + * - @ref page_pjlib_string_test + */ + +/** + * Create string initializer from a normal C string. + * + * @param str Null terminated string to be stored. + * + * @return pj_str_t. + */ +PJ_IDECL(pj_str_t) pj_str(char *str); + +/** + * Create constant string from normal C string. + * + * @param str The string to be initialized. + * @param s Null terminated string. + * + * @return pj_str_t. + */ +PJ_INLINE(const pj_str_t*) pj_cstr(pj_str_t *str, const char *s) +{ + str->ptr = (char*)s; + str->slen = s ? strlen(s) : 0; + return str; +} + +/** + * Set the pointer and length to the specified value. + * + * @param str the string. + * @param ptr pointer to set. + * @param length length to set. + * + * @return the string. + */ +PJ_INLINE(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length) +{ + str->ptr = ptr; + str->slen = length; + return str; +} + +/** + * Set the pointer and length of the string to the source string, which + * must be NULL terminated. + * + * @param str the string. + * @param src pointer to set. + * + * @return the string. + */ +PJ_INLINE(pj_str_t*) pj_strset2( pj_str_t *str, char *src) +{ + str->ptr = src; + str->slen = src ? strlen(src) : 0; + return str; +} + +/** + * Set the pointer and the length of the string. + * + * @param str The target string. + * @param begin The start of the string. + * @param end The end of the string. + * + * @return the target string. + */ +PJ_INLINE(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end ) +{ + str->ptr = begin; + str->slen = end-begin; + return str; +} + +/** + * Assign string. + * + * @param dst The target string. + * @param src The source string. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ); + +/** + * Copy string contents. + * + * @param dst The target string. + * @param src The source string. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src); + +/** + * Copy string contents. + * + * @param dst The target string. + * @param src The source string. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src); + +/** + * Copy source string to destination up to the specified max length. + * + * @param dst The target string. + * @param src The source string. + * @param max Maximum characters to copy. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strncpy(pj_str_t *dst, const pj_str_t *src, + pj_ssize_t max); + +/** + * Copy source string to destination up to the specified max length, + * and NULL terminate the destination. If source string length is + * greater than or equal to max, then max-1 will be copied. + * + * @param dst The target string. + * @param src The source string. + * @param max Maximum characters to copy. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strncpy_with_null(pj_str_t *dst, const pj_str_t *src, + pj_ssize_t max); + +/** + * Duplicate string. + * + * @param pool The pool. + * @param dst The string result. + * @param src The string to duplicate. + * + * @return the string result. + */ +PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src); + +/** + * Duplicate string and NULL terminate the destination string. + * + * @param pool + * @param dst + * @param src + */ +PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src); + +/** + * Duplicate string. + * + * @param pool The pool. + * @param dst The string result. + * @param src The string to duplicate. + * + * @return the string result. + */ +PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool, + pj_str_t *dst, + const char *src); + +/** + * Duplicate string. + * + * @param pool The pool. + * @param src The string to duplicate. + * + * @return the string result. + */ +PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src); + +/** + * Return the length of the string. + * + * @param str The string. + * + * @return the length of the string. + */ +PJ_INLINE(pj_size_t) pj_strlen( const pj_str_t *str ) +{ + return str->slen; +} + +/** + * Return the pointer to the string data. + * + * @param str The string. + * + * @return the pointer to the string buffer. + */ +PJ_INLINE(const char*) pj_strbuf( const pj_str_t *str ) +{ + return str->ptr; +} + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2); + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ); + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len); + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2, + pj_size_t len); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, + pj_size_t len); + +/** + * Concatenate strings. + * + * @param dst The destination string. + * @param src The source string. + */ +PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src); + +/** + * Finds a character in a string. + * + * @param str The string. + * @param chr The character to find. + * + * @return the pointer to first character found, or NULL. + */ +PJ_INLINE(char*) pj_strchr( const pj_str_t *str, int chr) +{ + return (char*) memchr(str->ptr, chr, str->slen); +} + +/** + * Remove (trim) leading whitespaces from the string. + * + * @param str The string. + * + * @return the string. + */ +PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str ); + +/** + * Remove (trim) the trailing whitespaces from the string. + * + * @param str The string. + * + * @return the string. + */ +PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str ); + +/** + * Remove (trim) leading and trailing whitespaces from the string. + * + * @param str The string. + * + * @return the string. + */ +PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str ); + +/** + * Initialize the buffer with some random string. + * + * @param str the string to store the result. + * @param length the length of the random string to generate. + * + * @return the string. + */ +PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length); + +/** + * Convert string to unsigned integer. + * + * @param str the string. + * + * @return the unsigned integer. + */ +PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str); + +/** + * Utility to convert unsigned integer to string. Note that the + * string will be NULL terminated. + * + * @param val the unsigned integer value. + * @param buf the buffer + * + * @return the number of characters written + */ +PJ_DECL(int) pj_utoa(unsigned long val, char *buf); + +/** + * Convert unsigned integer to string with minimum digits. Note that the + * string will be NULL terminated. + * + * @param val The unsigned integer value. + * @param buf The buffer. + * @param min_dig Minimum digits to be printed, or zero to specify no + * minimum digit. + * @param pad The padding character to be put in front of the string + * when the digits is less than minimum. + * + * @return the number of characters written. + */ +PJ_DECL(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad); + +/** + * Fill the memory location with value. + * + * @param dst The destination buffer. + * @param c Character to set. + * @param size The number of characters. + * + * @return the value of dst. + */ +PJ_INLINE(void*) pj_memset(void *dst, int c, pj_size_t size) +{ + return memset(dst, c, size); +} + +/** + * Copy buffer. + * + * @param dst The destination buffer. + * @param src The source buffer. + * @param size The size to copy. + * + * @return the destination buffer. + */ +PJ_INLINE(void*) pj_memcpy(void *dst, const void *src, pj_size_t size) +{ + return memcpy(dst, src, size); +} + +/** + * Move memory. + * + * @param dst The destination buffer. + * @param src The source buffer. + * @param size The size to copy. + * + * @return the destination buffer. + */ +PJ_INLINE(void*) pj_memmove(void *dst, const void *src, pj_size_t size) +{ + return memmove(dst, src, size); +} + +/** + * Compare buffers. + * + * @param buf1 The first buffer. + * @param buf2 The second buffer. + * @param size The size to compare. + * + * @return negative, zero, or positive value. + */ +PJ_INLINE(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size) +{ + return memcmp(buf1, buf2, size); +} + +/** + * Find character in the buffer. + * + * @param buf The buffer. + * @param c The character to find. + * @param size The size to check. + * + * @return the pointer to location where the character is found, or NULL if + * not found. + */ +PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size) +{ + return memchr(buf, c, size); +} + + +/** + * @} + */ + +#if PJ_FUNCTIONS_ARE_INLINED +# include <pj/string_i.h> +#endif + +PJ_END_DECL + +#endif /* __PJ_STRING_H__ */ + diff --git a/pjlib/include/pj/string_i.h b/pjlib/include/pj/string_i.h index 7163b134..3fdcd2c3 100644 --- a/pjlib/include/pj/string_i.h +++ b/pjlib/include/pj/string_i.h @@ -1,207 +1,207 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-PJ_IDEF(pj_str_t) pj_str(char *str)
-{
- pj_str_t dst;
- dst.ptr = str;
- dst.slen = str ? pj_native_strlen(str) : 0;
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src)
-{
- if (src->slen) {
- dst->ptr = (char*)pj_pool_alloc(pool, src->slen);
- pj_memcpy(dst->ptr, src->ptr, src->slen);
- }
- dst->slen = src->slen;
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src)
-{
- if (src->slen) {
- dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1);
- pj_memcpy(dst->ptr, src->ptr, src->slen);
- } else {
- dst->ptr = (char*)pj_pool_alloc(pool, 1);
- }
- dst->slen = src->slen;
- dst->ptr[dst->slen] = '\0';
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool,
- pj_str_t *dst,
- const char *src)
-{
- dst->slen = src ? pj_native_strlen(src) : 0;
- if (dst->slen) {
- dst->ptr = (char*)pj_pool_alloc(pool, dst->slen);
- pj_memcpy(dst->ptr, src, dst->slen);
- } else {
- dst->ptr = NULL;
- }
- return dst;
-}
-
-
-PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src)
-{
- pj_str_t temp;
- pj_strdup2(pool, &temp, src);
- return temp;
-}
-
-PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src )
-{
- dst->ptr = src->ptr;
- dst->slen = src->slen;
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src)
-{
- dst->slen = src->slen;
- if (src->slen > 0)
- pj_memcpy(dst->ptr, src->ptr, src->slen);
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src)
-{
- dst->slen = src ? pj_native_strlen(src) : 0;
- if (dst->slen > 0)
- pj_memcpy(dst->ptr, src, dst->slen);
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strncpy( pj_str_t *dst, const pj_str_t *src,
- pj_ssize_t max)
-{
- if (max > src->slen) max = src->slen;
- pj_memcpy(dst->ptr, src->ptr, max);
- dst->slen = max;
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strncpy_with_null( pj_str_t *dst, const pj_str_t *src,
- pj_ssize_t max)
-{
- if (max <= src->slen)
- max = max-1;
- else
- max = src->slen;
-
- pj_memcpy(dst->ptr, src->ptr, max);
- dst->ptr[max] = '\0';
- dst->slen = max;
- return dst;
-}
-
-
-PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2)
-{
- pj_ssize_t diff;
-
- diff = str1->slen - str2->slen;
- if (diff) {
- return (int)diff;
- } else if (str1->ptr && str1->slen) {
- return pj_native_strncmp(str1->ptr, str2->ptr, str1->slen);
- } else {
- return 0;
- }
-}
-
-PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2->ptr) ?
- pj_native_strncmp(str1->ptr, str2->ptr, len) :
- (str1->ptr == str2->ptr ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2) ? pj_native_strncmp(str1->ptr, str2, len) :
- (str1->ptr==str2 ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 )
-{
- return pj_strncmp2( str1, str2, str1->slen);
-}
-
-PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2)
-{
- pj_ssize_t diff;
-
- diff = str1->slen - str2->slen;
- if (diff) {
- return (int)diff;
- } else {
- return pj_native_strnicmp(str1->ptr, str2->ptr, str1->slen);
- }
-}
-
-PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2)
-{
- return (str1->ptr && str2) ?
- pj_native_strnicmp(str1->ptr, str2, str1->slen) :
- (str1->ptr==str2 ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2->ptr) ?
- pj_native_strnicmp(str1->ptr, str2->ptr, len) :
- (str1->ptr == str2->ptr ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2) ?
- pj_native_strnicmp(str1->ptr, str2, len) :
- (str1->ptr == str2 ? 0 : 1);
-}
-
-PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src)
-{
- if (src->slen) {
- pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen);
- dst->slen += src->slen;
- }
-}
-
-PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str )
-{
- pj_strltrim(str);
- pj_strrtrim(str);
- return str;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +PJ_IDEF(pj_str_t) pj_str(char *str) +{ + pj_str_t dst; + dst.ptr = str; + dst.slen = str ? pj_native_strlen(str) : 0; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src) +{ + if (src->slen) { + dst->ptr = (char*)pj_pool_alloc(pool, src->slen); + pj_memcpy(dst->ptr, src->ptr, src->slen); + } + dst->slen = src->slen; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src) +{ + if (src->slen) { + dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1); + pj_memcpy(dst->ptr, src->ptr, src->slen); + } else { + dst->ptr = (char*)pj_pool_alloc(pool, 1); + } + dst->slen = src->slen; + dst->ptr[dst->slen] = '\0'; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool, + pj_str_t *dst, + const char *src) +{ + dst->slen = src ? pj_native_strlen(src) : 0; + if (dst->slen) { + dst->ptr = (char*)pj_pool_alloc(pool, dst->slen); + pj_memcpy(dst->ptr, src, dst->slen); + } else { + dst->ptr = NULL; + } + return dst; +} + + +PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src) +{ + pj_str_t temp; + pj_strdup2(pool, &temp, src); + return temp; +} + +PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ) +{ + dst->ptr = src->ptr; + dst->slen = src->slen; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src) +{ + dst->slen = src->slen; + if (src->slen > 0) + pj_memcpy(dst->ptr, src->ptr, src->slen); + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src) +{ + dst->slen = src ? pj_native_strlen(src) : 0; + if (dst->slen > 0) + pj_memcpy(dst->ptr, src, dst->slen); + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strncpy( pj_str_t *dst, const pj_str_t *src, + pj_ssize_t max) +{ + if (max > src->slen) max = src->slen; + pj_memcpy(dst->ptr, src->ptr, max); + dst->slen = max; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strncpy_with_null( pj_str_t *dst, const pj_str_t *src, + pj_ssize_t max) +{ + if (max <= src->slen) + max = max-1; + else + max = src->slen; + + pj_memcpy(dst->ptr, src->ptr, max); + dst->ptr[max] = '\0'; + dst->slen = max; + return dst; +} + + +PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2) +{ + pj_ssize_t diff; + + diff = str1->slen - str2->slen; + if (diff) { + return (int)diff; + } else if (str1->ptr && str1->slen) { + return pj_native_strncmp(str1->ptr, str2->ptr, str1->slen); + } else { + return 0; + } +} + +PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len) +{ + return (str1->ptr && str2->ptr) ? + pj_native_strncmp(str1->ptr, str2->ptr, len) : + (str1->ptr == str2->ptr ? 0 : 1); +} + +PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2, + pj_size_t len) +{ + return (str1->ptr && str2) ? pj_native_strncmp(str1->ptr, str2, len) : + (str1->ptr==str2 ? 0 : 1); +} + +PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ) +{ + return pj_strncmp2( str1, str2, str1->slen); +} + +PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2) +{ + pj_ssize_t diff; + + diff = str1->slen - str2->slen; + if (diff) { + return (int)diff; + } else { + return pj_native_strnicmp(str1->ptr, str2->ptr, str1->slen); + } +} + +PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2) +{ + return (str1->ptr && str2) ? + pj_native_strnicmp(str1->ptr, str2, str1->slen) : + (str1->ptr==str2 ? 0 : 1); +} + +PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len) +{ + return (str1->ptr && str2->ptr) ? + pj_native_strnicmp(str1->ptr, str2->ptr, len) : + (str1->ptr == str2->ptr ? 0 : 1); +} + +PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, + pj_size_t len) +{ + return (str1->ptr && str2) ? + pj_native_strnicmp(str1->ptr, str2, len) : + (str1->ptr == str2 ? 0 : 1); +} + +PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src) +{ + if (src->slen) { + pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen); + dst->slen += src->slen; + } +} + +PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str ) +{ + pj_strltrim(str); + pj_strrtrim(str); + return str; +} + diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h index eac6ba50..bcc4bb92 100644 --- a/pjlib/include/pj/timer.h +++ b/pjlib/include/pj/timer.h @@ -1,265 +1,265 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __PJ_TIMER_H__
-#define __PJ_TIMER_H__
-
-/**
- * @file timer.h
- * @brief Timer Heap
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_TIMER Timer Heap Management.
- * @ingroup PJ_MISC
- * @brief
- * The timer scheduling implementation here is based on ACE library's
- * ACE_Timer_Heap, with only little modification to suit our library's style
- * (I even left most of the comments in the original source).
- *
- * To quote the original quote in ACE_Timer_Heap_T class:
- *
- * This implementation uses a heap-based callout queue of
- * absolute times. Therefore, in the average and worst case,
- * scheduling, canceling, and expiring timers is O(log N) (where
- * N is the total number of timers). In addition, we can also
- * preallocate as many \a ACE_Timer_Nodes as there are slots in
- * the heap. This allows us to completely remove the need for
- * dynamic memory allocation, which is important for real-time
- * systems.
- * @{
- *
- * \section pj_timer_examples_sec Examples
- *
- * For some examples on how to use the timer heap, please see the link below.
- *
- * - \ref page_pjlib_timer_test
- */
-
-
-/**
- * The type for internal timer ID.
- */
-typedef int pj_timer_id_t;
-
-/**
- * Forward declaration for pj_timer_entry.
- */
-struct pj_timer_entry;
-
-/**
- * The type of callback function to be called by timer scheduler when a timer
- * has expired.
- *
- * @param timer_heap The timer heap.
- * @param entry Timer entry which timer's has expired.
- */
-typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry);
-
-
-/**
- * This structure represents an entry to the timer.
- */
-struct pj_timer_entry
-{
- /**
- * User data to be associated with this entry.
- * Applications normally will put the instance of object that
- * owns the timer entry in this field.
- */
- void *user_data;
-
- /**
- * Arbitrary ID assigned by the user/owner of this entry.
- * Applications can use this ID to distinguish multiple
- * timer entries that share the same callback and user_data.
- */
- int id;
-
- /**
- * Callback to be called when the timer expires.
- */
- pj_timer_heap_callback *cb;
-
- /**
- * Internal unique timer ID, which is assigned by the timer heap.
- * Application should not touch this ID.
- */
- pj_timer_id_t _timer_id;
-
- /**
- * The future time when the timer expires, which the value is updated
- * by timer heap when the timer is scheduled.
- */
- pj_time_val _timer_value;
-};
-
-
-/**
- * Calculate memory size required to create a timer heap.
- *
- * @param count Number of timer entries to be supported.
- * @return Memory size requirement in bytes.
- */
-PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count);
-
-/**
- * Create a timer heap.
- *
- * @param pool The pool where allocations in the timer heap will be
- * allocated. The timer heap will dynamicly allocate
- * more storate from the pool if the number of timer
- * entries registered is more than the size originally
- * requested when calling this function.
- * @param count The maximum number of timer entries to be supported
- * initially. If the application registers more entries
- * during runtime, then the timer heap will resize.
- * @param ht Pointer to receive the created timer heap.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
- pj_size_t count,
- pj_timer_heap_t **ht);
-
-/**
- * Destroy the timer heap.
- *
- * @param ht The timer heap.
- */
-PJ_DECL(void) pj_timer_heap_destroy( pj_timer_heap_t *ht );
-
-
-/**
- * Set lock object to be used by the timer heap. By default, the timer heap
- * uses dummy synchronization.
- *
- * @param ht The timer heap.
- * @param lock The lock object to be used for synchronization.
- * @param auto_del If nonzero, the lock object will be destroyed when
- * the timer heap is destroyed.
- */
-PJ_DECL(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht,
- pj_lock_t *lock,
- pj_bool_t auto_del );
-
-/**
- * Set maximum number of timed out entries to process in a single poll.
- *
- * @param ht The timer heap.
- * @param count Number of entries.
- *
- * @return The old number.
- */
-PJ_DECL(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
- unsigned count );
-
-/**
- * Initialize a timer entry. Application should call this function at least
- * once before scheduling the entry to the timer heap, to properly initialize
- * the timer entry.
- *
- * @param entry The timer entry to be initialized.
- * @param id Arbitrary ID assigned by the user/owner of this entry.
- * Applications can use this ID to distinguish multiple
- * timer entries that share the same callback and user_data.
- * @param user_data User data to be associated with this entry.
- * Applications normally will put the instance of object that
- * owns the timer entry in this field.
- * @param cb Callback function to be called when the timer elapses.
- *
- * @return The timer entry itself.
- */
-PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
- int id,
- void *user_data,
- pj_timer_heap_callback *cb );
-
-/**
- * Schedule a timer entry which will expire AFTER the specified delay.
- *
- * @param ht The timer heap.
- * @param entry The entry to be registered.
- * @param delay The interval to expire.
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *delay);
-
-/**
- * Cancel a previously registered timer.
- *
- * @param ht The timer heap.
- * @param entry The entry to be cancelled.
- * @return The number of timer cancelled, which should be one if the
- * entry has really been registered, or zero if no timer was
- * cancelled.
- */
-PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry);
-
-/**
- * Get the number of timer entries.
- *
- * @param ht The timer heap.
- * @return The number of timer entries.
- */
-PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht );
-
-/**
- * Get the earliest time registered in the timer heap. The timer heap
- * MUST have at least one timer being scheduled (application should use
- * #pj_timer_heap_count() before calling this function).
- *
- * @param ht The timer heap.
- * @param timeval The time deadline of the earliest timer entry.
- *
- * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled.
- */
-PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht,
- pj_time_val *timeval);
-
-/**
- * Poll the timer heap, check for expired timers and call the callback for
- * each of the expired timers.
- *
- * @param ht The timer heap.
- * @param next_delay If this parameter is not NULL, it will be filled up with
- * the time delay until the next timer elapsed, or -1 in
- * the sec part if no entry exist.
- *
- * @return The number of timers expired.
- */
-PJ_DECL(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
- pj_time_val *next_delay);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJ_TIMER_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PJ_TIMER_H__ +#define __PJ_TIMER_H__ + +/** + * @file timer.h + * @brief Timer Heap + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_TIMER Timer Heap Management. + * @ingroup PJ_MISC + * @brief + * The timer scheduling implementation here is based on ACE library's + * ACE_Timer_Heap, with only little modification to suit our library's style + * (I even left most of the comments in the original source). + * + * To quote the original quote in ACE_Timer_Heap_T class: + * + * This implementation uses a heap-based callout queue of + * absolute times. Therefore, in the average and worst case, + * scheduling, canceling, and expiring timers is O(log N) (where + * N is the total number of timers). In addition, we can also + * preallocate as many \a ACE_Timer_Nodes as there are slots in + * the heap. This allows us to completely remove the need for + * dynamic memory allocation, which is important for real-time + * systems. + * @{ + * + * \section pj_timer_examples_sec Examples + * + * For some examples on how to use the timer heap, please see the link below. + * + * - \ref page_pjlib_timer_test + */ + + +/** + * The type for internal timer ID. + */ +typedef int pj_timer_id_t; + +/** + * Forward declaration for pj_timer_entry. + */ +struct pj_timer_entry; + +/** + * The type of callback function to be called by timer scheduler when a timer + * has expired. + * + * @param timer_heap The timer heap. + * @param entry Timer entry which timer's has expired. + */ +typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry); + + +/** + * This structure represents an entry to the timer. + */ +struct pj_timer_entry +{ + /** + * User data to be associated with this entry. + * Applications normally will put the instance of object that + * owns the timer entry in this field. + */ + void *user_data; + + /** + * Arbitrary ID assigned by the user/owner of this entry. + * Applications can use this ID to distinguish multiple + * timer entries that share the same callback and user_data. + */ + int id; + + /** + * Callback to be called when the timer expires. + */ + pj_timer_heap_callback *cb; + + /** + * Internal unique timer ID, which is assigned by the timer heap. + * Application should not touch this ID. + */ + pj_timer_id_t _timer_id; + + /** + * The future time when the timer expires, which the value is updated + * by timer heap when the timer is scheduled. + */ + pj_time_val _timer_value; +}; + + +/** + * Calculate memory size required to create a timer heap. + * + * @param count Number of timer entries to be supported. + * @return Memory size requirement in bytes. + */ +PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count); + +/** + * Create a timer heap. + * + * @param pool The pool where allocations in the timer heap will be + * allocated. The timer heap will dynamicly allocate + * more storate from the pool if the number of timer + * entries registered is more than the size originally + * requested when calling this function. + * @param count The maximum number of timer entries to be supported + * initially. If the application registers more entries + * during runtime, then the timer heap will resize. + * @param ht Pointer to receive the created timer heap. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, + pj_size_t count, + pj_timer_heap_t **ht); + +/** + * Destroy the timer heap. + * + * @param ht The timer heap. + */ +PJ_DECL(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ); + + +/** + * Set lock object to be used by the timer heap. By default, the timer heap + * uses dummy synchronization. + * + * @param ht The timer heap. + * @param lock The lock object to be used for synchronization. + * @param auto_del If nonzero, the lock object will be destroyed when + * the timer heap is destroyed. + */ +PJ_DECL(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht, + pj_lock_t *lock, + pj_bool_t auto_del ); + +/** + * Set maximum number of timed out entries to process in a single poll. + * + * @param ht The timer heap. + * @param count Number of entries. + * + * @return The old number. + */ +PJ_DECL(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht, + unsigned count ); + +/** + * Initialize a timer entry. Application should call this function at least + * once before scheduling the entry to the timer heap, to properly initialize + * the timer entry. + * + * @param entry The timer entry to be initialized. + * @param id Arbitrary ID assigned by the user/owner of this entry. + * Applications can use this ID to distinguish multiple + * timer entries that share the same callback and user_data. + * @param user_data User data to be associated with this entry. + * Applications normally will put the instance of object that + * owns the timer entry in this field. + * @param cb Callback function to be called when the timer elapses. + * + * @return The timer entry itself. + */ +PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, + int id, + void *user_data, + pj_timer_heap_callback *cb ); + +/** + * Schedule a timer entry which will expire AFTER the specified delay. + * + * @param ht The timer heap. + * @param entry The entry to be registered. + * @param delay The interval to expire. + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *delay); + +/** + * Cancel a previously registered timer. + * + * @param ht The timer heap. + * @param entry The entry to be cancelled. + * @return The number of timer cancelled, which should be one if the + * entry has really been registered, or zero if no timer was + * cancelled. + */ +PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry); + +/** + * Get the number of timer entries. + * + * @param ht The timer heap. + * @return The number of timer entries. + */ +PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ); + +/** + * Get the earliest time registered in the timer heap. The timer heap + * MUST have at least one timer being scheduled (application should use + * #pj_timer_heap_count() before calling this function). + * + * @param ht The timer heap. + * @param timeval The time deadline of the earliest timer entry. + * + * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled. + */ +PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht, + pj_time_val *timeval); + +/** + * Poll the timer heap, check for expired timers and call the callback for + * each of the expired timers. + * + * @param ht The timer heap. + * @param next_delay If this parameter is not NULL, it will be filled up with + * the time delay until the next timer elapsed, or -1 in + * the sec part if no entry exist. + * + * @return The number of timers expired. + */ +PJ_DECL(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, + pj_time_val *next_delay); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJ_TIMER_H__ */ + diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h index e1e0df40..62e2c0f4 100644 --- a/pjlib/include/pj/types.h +++ b/pjlib/include/pj/types.h @@ -1,442 +1,442 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_TYPES_H__
-#define __PJ_TYPES_H__
-
-
-/**
- * @defgroup PJ PJ Library
- */
-/**
- * @file types.h
- * @brief Declaration of basic types and utility.
- */
-/**
- * @defgroup PJ_BASIC Basic Data Types and Library Functionality.
- * @ingroup PJ_DS
- * @{
- */
-#include <pj/config.h>
-
-PJ_BEGIN_DECL
-
-///////////////////////////////////////////////////////////////////////////////
-
-/** Unsigned 32bit integer. */
-typedef int pj_int32_t;
-
-/** Signed 32bit integer. */
-typedef unsigned int pj_uint32_t;
-
-/** Unsigned 16bit integer. */
-typedef short pj_int16_t;
-
-/** Signed 16bit integer. */
-typedef unsigned short pj_uint16_t;
-
-/** Unsigned 8bit integer. */
-typedef signed char pj_int8_t;
-
-/** Signed 16bit integer. */
-typedef unsigned char pj_uint8_t;
-
-/** Large unsigned integer. */
-typedef size_t pj_size_t;
-
-/** Large signed integer. */
-typedef long pj_ssize_t;
-
-/** Status code. */
-typedef int pj_status_t;
-
-/** Boolean. */
-typedef int pj_bool_t;
-
-/** Status is OK. */
-#define PJ_SUCCESS 0
-
-/** True value. */
-#define PJ_TRUE 1
-
-/** False value. */
-#define PJ_FALSE 0
-
-/**
- * File offset type.
- */
-#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
-typedef pj_int64_t pj_off_t;
-#else
-typedef pj_ssize_t pj_off_t;
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Data structure types.
- */
-/**
- * This type is used as replacement to legacy C string, and used throughout
- * the library. By convention, the string is NOT null terminated.
- */
-struct pj_str_t
-{
- /** Buffer pointer, which is by convention NOT null terminated. */
- char *ptr;
-
- /** The length of the string. */
- pj_ssize_t slen;
-};
-
-
-/**
- * The opaque data type for linked list, which is used as arguments throughout
- * the linked list operations.
- */
-typedef void pj_list_type;
-
-/**
- * List.
- */
-typedef struct pj_list pj_list;
-
-/**
- * Opaque data type for hash tables.
- */
-typedef struct pj_hash_table_t pj_hash_table_t;
-
-/**
- * Opaque data type for hash entry (only used internally by hash table).
- */
-typedef struct pj_hash_entry pj_hash_entry;
-
-/**
- * Data type for hash search iterator.
- * This structure should be opaque, however applications need to declare
- * concrete variable of this type, that's why the declaration is visible here.
- */
-typedef struct pj_hash_iterator_t
-{
- pj_uint32_t index; /**< Internal index. */
- pj_hash_entry *entry; /**< Internal entry. */
-} pj_hash_iterator_t;
-
-
-/**
- * Forward declaration for memory pool factory.
- */
-typedef struct pj_pool_factory pj_pool_factory;
-
-/**
- * Opaque data type for memory pool.
- */
-typedef struct pj_pool_t pj_pool_t;
-
-/**
- * Forward declaration for caching pool, a pool factory implementation.
- */
-typedef struct pj_caching_pool pj_caching_pool;
-
-/**
- * This type is used as replacement to legacy C string, and used throughout
- * the library.
- */
-typedef struct pj_str_t pj_str_t;
-
-/**
- * Opaque data type for I/O Queue structure.
- */
-typedef struct pj_ioqueue_t pj_ioqueue_t;
-
-/**
- * Opaque data type for key that identifies a handle registered to the
- * I/O queue framework.
- */
-typedef struct pj_ioqueue_key_t pj_ioqueue_key_t;
-
-/**
- * Opaque data to identify timer heap.
- */
-typedef struct pj_timer_heap_t pj_timer_heap_t;
-
-/**
- * Forward declaration for timer entry.
- */
-typedef struct pj_timer_entry pj_timer_entry;
-
-/**
- * Opaque data type for atomic operations.
- */
-typedef struct pj_atomic_t pj_atomic_t;
-
-/**
- * Value type of an atomic variable.
- */
-typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t;
-
-///////////////////////////////////////////////////////////////////////////////
-
-/** Thread handle. */
-typedef struct pj_thread_t pj_thread_t;
-
-/** Lock object. */
-typedef struct pj_lock_t pj_lock_t;
-
-/** Mutex handle. */
-typedef struct pj_mutex_t pj_mutex_t;
-
-/** Semaphore handle. */
-typedef struct pj_sem_t pj_sem_t;
-
-/** Event object. */
-typedef struct pj_event_t pj_event_t;
-
-/** Unidirectional stream pipe object. */
-typedef struct pj_pipe_t pj_pipe_t;
-
-/** Operating system handle. */
-typedef void *pj_oshandle_t;
-
-/** Socket handle. */
-typedef long pj_sock_t;
-
-/** Generic socket address. */
-typedef void pj_sockaddr_t;
-
-/** Color type. */
-typedef unsigned int pj_color_t;
-
-/** Exception id. */
-typedef int pj_exception_id_t;
-
-///////////////////////////////////////////////////////////////////////////////
-
-/** Utility macro to compute the number of elements in static array. */
-#define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-/** Maximum value for signed 32-bit integer. */
-#define PJ_MAXINT32 0x7FFFFFFFL
-
-/**
- * Length of object names.
- */
-#define PJ_MAX_OBJ_NAME 16
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * General.
- */
-/**
- * Initialize the PJ Library.
- * This function must be called before using the library. The purpose of this
- * function is to initialize static library data, such as character table used
- * in random string generation, and to initialize operating system dependent
- * functionality (such as WSAStartup() in Windows).
- */
-PJ_DECL(pj_status_t) pj_init(void);
-
-
-/**
- * @}
- */
-/**
- * @addtogroup PJ_TIME Time Data Type and Manipulation.
- * @ingroup PJ_MISC
- * @{
- */
-
-/**
- * Representation of time value in this library.
- * This type can be used to represent either an interval or a specific time
- * or date.
- */
-typedef struct pj_time_val
-{
- /** The seconds part of the time. */
- long sec;
-
- /** The miliseconds fraction of the time. */
- long msec;
-
-} pj_time_val;
-
-/**
- * Normalize the value in time value.
- * @param t Time value to be normalized.
- */
-PJ_DECL(void) pj_time_val_normalize(pj_time_val *t);
-
-/**
- * Get the total time value in miliseconds. This is the same as
- * multiplying the second part with 1000 and then add the miliseconds
- * part to the result.
- *
- * @param t The time value.
- * @return Total time in miliseconds.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_MSEC(t) ((t).sec * 1000 + (t).msec)
-
-/**
- * This macro will check if \a t1 is equal to \a t2.
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if both time values are equal.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_EQ(t1, t2) ((t1).sec==(t2).sec && (t1).msec==(t2).msec)
-
-/**
- * This macro will check if \a t1 is greater than \a t2
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is greater than t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_GT(t1, t2) ((t1).sec>(t2).sec || \
- ((t1).sec==(t2).sec && (t1).msec>(t2).msec))
-
-/**
- * This macro will check if \a t1 is greater than or equal to \a t2
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is greater than or equal to t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_GTE(t1, t2) (PJ_TIME_VAL_GT(t1,t2) || \
- PJ_TIME_VAL_EQ(t1,t2))
-
-/**
- * This macro will check if \a t1 is less than \a t2
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is less than t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_LT(t1, t2) (!(PJ_TIME_VAL_GTE(t1,t2)))
-
-/**
- * This macro will check if \a t1 is less than or equal to \a t2.
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is less than or equal to t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_LTE(t1, t2) (!PJ_TIME_VAL_GT(t1, t2))
-
-/**
- * Add \a t2 to \a t1 and store the result in \a t1. Effectively
- *
- * this macro will expand as: (\a t1 += \a t2).
- * @param t1 The time value to add.
- * @param t2 The time value to be added to \a t1.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_ADD(t1, t2) do { \
- (t1).sec += (t2).sec; \
- (t1).msec += (t2).msec; \
- pj_time_val_normalize(&(t1)); \
- } while (0)
-
-
-/**
- * Substract \a t2 from \a t1 and store the result in \a t1. Effectively
- * this macro will expand as (\a t1 -= \a t2).
- *
- * @param t1 The time value to subsctract.
- * @param t2 The time value to be substracted from \a t1.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_SUB(t1, t2) do { \
- (t1).sec -= (t2).sec; \
- (t1).msec -= (t2).msec; \
- pj_time_val_normalize(&(t1)); \
- } while (0)
-
-
-/**
- * This structure represent the parsed representation of time.
- * It is acquired by calling #pj_time_decode().
- */
-typedef struct pj_parsed_time
-{
- /** This represents day of week where value zero means Sunday */
- int wday;
-
- /** This represents day of the year, 0-365, where zero means
- * 1st of January.
- */
- int yday;
-
- /** This represents day of month: 1-31 */
- int day;
-
- /** This represents month, with the value is 0 - 11 (zero is January) */
- int mon;
-
- /** This represent the actual year (unlike in ANSI libc where
- * the value must be added by 1900).
- */
- int year;
-
- /** This represents the second part, with the value is 0-59 */
- int sec;
-
- /** This represents the minute part, with the value is: 0-59 */
- int min;
-
- /** This represents the hour part, with the value is 0-23 */
- int hour;
-
- /** This represents the milisecond part, with the value is 0-999 */
- int msec;
-
-} pj_parsed_time;
-
-
-/**
- * @} // Time Management
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Terminal.
- */
-/**
- * Color code combination.
- */
-enum {
- PJ_TERM_COLOR_R = 2, /**< Red */
- PJ_TERM_COLOR_G = 4, /**< Green */
- PJ_TERM_COLOR_B = 1, /**< Blue. */
- PJ_TERM_COLOR_BRIGHT = 8 /**< Bright mask. */
-};
-
-
-
-
-PJ_END_DECL
-
-
-#endif /* __PJ_TYPES_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_TYPES_H__ +#define __PJ_TYPES_H__ + + +/** + * @defgroup PJ PJ Library + */ +/** + * @file types.h + * @brief Declaration of basic types and utility. + */ +/** + * @defgroup PJ_BASIC Basic Data Types and Library Functionality. + * @ingroup PJ_DS + * @{ + */ +#include <pj/config.h> + +PJ_BEGIN_DECL + +/////////////////////////////////////////////////////////////////////////////// + +/** Unsigned 32bit integer. */ +typedef int pj_int32_t; + +/** Signed 32bit integer. */ +typedef unsigned int pj_uint32_t; + +/** Unsigned 16bit integer. */ +typedef short pj_int16_t; + +/** Signed 16bit integer. */ +typedef unsigned short pj_uint16_t; + +/** Unsigned 8bit integer. */ +typedef signed char pj_int8_t; + +/** Signed 16bit integer. */ +typedef unsigned char pj_uint8_t; + +/** Large unsigned integer. */ +typedef size_t pj_size_t; + +/** Large signed integer. */ +typedef long pj_ssize_t; + +/** Status code. */ +typedef int pj_status_t; + +/** Boolean. */ +typedef int pj_bool_t; + +/** Status is OK. */ +#define PJ_SUCCESS 0 + +/** True value. */ +#define PJ_TRUE 1 + +/** False value. */ +#define PJ_FALSE 0 + +/** + * File offset type. + */ +#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 +typedef pj_int64_t pj_off_t; +#else +typedef pj_ssize_t pj_off_t; +#endif + +/////////////////////////////////////////////////////////////////////////////// +/* + * Data structure types. + */ +/** + * This type is used as replacement to legacy C string, and used throughout + * the library. By convention, the string is NOT null terminated. + */ +struct pj_str_t +{ + /** Buffer pointer, which is by convention NOT null terminated. */ + char *ptr; + + /** The length of the string. */ + pj_ssize_t slen; +}; + + +/** + * The opaque data type for linked list, which is used as arguments throughout + * the linked list operations. + */ +typedef void pj_list_type; + +/** + * List. + */ +typedef struct pj_list pj_list; + +/** + * Opaque data type for hash tables. + */ +typedef struct pj_hash_table_t pj_hash_table_t; + +/** + * Opaque data type for hash entry (only used internally by hash table). + */ +typedef struct pj_hash_entry pj_hash_entry; + +/** + * Data type for hash search iterator. + * This structure should be opaque, however applications need to declare + * concrete variable of this type, that's why the declaration is visible here. + */ +typedef struct pj_hash_iterator_t +{ + pj_uint32_t index; /**< Internal index. */ + pj_hash_entry *entry; /**< Internal entry. */ +} pj_hash_iterator_t; + + +/** + * Forward declaration for memory pool factory. + */ +typedef struct pj_pool_factory pj_pool_factory; + +/** + * Opaque data type for memory pool. + */ +typedef struct pj_pool_t pj_pool_t; + +/** + * Forward declaration for caching pool, a pool factory implementation. + */ +typedef struct pj_caching_pool pj_caching_pool; + +/** + * This type is used as replacement to legacy C string, and used throughout + * the library. + */ +typedef struct pj_str_t pj_str_t; + +/** + * Opaque data type for I/O Queue structure. + */ +typedef struct pj_ioqueue_t pj_ioqueue_t; + +/** + * Opaque data type for key that identifies a handle registered to the + * I/O queue framework. + */ +typedef struct pj_ioqueue_key_t pj_ioqueue_key_t; + +/** + * Opaque data to identify timer heap. + */ +typedef struct pj_timer_heap_t pj_timer_heap_t; + +/** + * Forward declaration for timer entry. + */ +typedef struct pj_timer_entry pj_timer_entry; + +/** + * Opaque data type for atomic operations. + */ +typedef struct pj_atomic_t pj_atomic_t; + +/** + * Value type of an atomic variable. + */ +typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t; + +/////////////////////////////////////////////////////////////////////////////// + +/** Thread handle. */ +typedef struct pj_thread_t pj_thread_t; + +/** Lock object. */ +typedef struct pj_lock_t pj_lock_t; + +/** Mutex handle. */ +typedef struct pj_mutex_t pj_mutex_t; + +/** Semaphore handle. */ +typedef struct pj_sem_t pj_sem_t; + +/** Event object. */ +typedef struct pj_event_t pj_event_t; + +/** Unidirectional stream pipe object. */ +typedef struct pj_pipe_t pj_pipe_t; + +/** Operating system handle. */ +typedef void *pj_oshandle_t; + +/** Socket handle. */ +typedef long pj_sock_t; + +/** Generic socket address. */ +typedef void pj_sockaddr_t; + +/** Color type. */ +typedef unsigned int pj_color_t; + +/** Exception id. */ +typedef int pj_exception_id_t; + +/////////////////////////////////////////////////////////////////////////////// + +/** Utility macro to compute the number of elements in static array. */ +#define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) + +/** Maximum value for signed 32-bit integer. */ +#define PJ_MAXINT32 0x7FFFFFFFL + +/** + * Length of object names. + */ +#define PJ_MAX_OBJ_NAME 16 + +/////////////////////////////////////////////////////////////////////////////// +/* + * General. + */ +/** + * Initialize the PJ Library. + * This function must be called before using the library. The purpose of this + * function is to initialize static library data, such as character table used + * in random string generation, and to initialize operating system dependent + * functionality (such as WSAStartup() in Windows). + */ +PJ_DECL(pj_status_t) pj_init(void); + + +/** + * @} + */ +/** + * @addtogroup PJ_TIME Time Data Type and Manipulation. + * @ingroup PJ_MISC + * @{ + */ + +/** + * Representation of time value in this library. + * This type can be used to represent either an interval or a specific time + * or date. + */ +typedef struct pj_time_val +{ + /** The seconds part of the time. */ + long sec; + + /** The miliseconds fraction of the time. */ + long msec; + +} pj_time_val; + +/** + * Normalize the value in time value. + * @param t Time value to be normalized. + */ +PJ_DECL(void) pj_time_val_normalize(pj_time_val *t); + +/** + * Get the total time value in miliseconds. This is the same as + * multiplying the second part with 1000 and then add the miliseconds + * part to the result. + * + * @param t The time value. + * @return Total time in miliseconds. + * @hideinitializer + */ +#define PJ_TIME_VAL_MSEC(t) ((t).sec * 1000 + (t).msec) + +/** + * This macro will check if \a t1 is equal to \a t2. + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if both time values are equal. + * @hideinitializer + */ +#define PJ_TIME_VAL_EQ(t1, t2) ((t1).sec==(t2).sec && (t1).msec==(t2).msec) + +/** + * This macro will check if \a t1 is greater than \a t2 + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is greater than t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_GT(t1, t2) ((t1).sec>(t2).sec || \ + ((t1).sec==(t2).sec && (t1).msec>(t2).msec)) + +/** + * This macro will check if \a t1 is greater than or equal to \a t2 + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is greater than or equal to t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_GTE(t1, t2) (PJ_TIME_VAL_GT(t1,t2) || \ + PJ_TIME_VAL_EQ(t1,t2)) + +/** + * This macro will check if \a t1 is less than \a t2 + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is less than t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_LT(t1, t2) (!(PJ_TIME_VAL_GTE(t1,t2))) + +/** + * This macro will check if \a t1 is less than or equal to \a t2. + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is less than or equal to t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_LTE(t1, t2) (!PJ_TIME_VAL_GT(t1, t2)) + +/** + * Add \a t2 to \a t1 and store the result in \a t1. Effectively + * + * this macro will expand as: (\a t1 += \a t2). + * @param t1 The time value to add. + * @param t2 The time value to be added to \a t1. + * @hideinitializer + */ +#define PJ_TIME_VAL_ADD(t1, t2) do { \ + (t1).sec += (t2).sec; \ + (t1).msec += (t2).msec; \ + pj_time_val_normalize(&(t1)); \ + } while (0) + + +/** + * Substract \a t2 from \a t1 and store the result in \a t1. Effectively + * this macro will expand as (\a t1 -= \a t2). + * + * @param t1 The time value to subsctract. + * @param t2 The time value to be substracted from \a t1. + * @hideinitializer + */ +#define PJ_TIME_VAL_SUB(t1, t2) do { \ + (t1).sec -= (t2).sec; \ + (t1).msec -= (t2).msec; \ + pj_time_val_normalize(&(t1)); \ + } while (0) + + +/** + * This structure represent the parsed representation of time. + * It is acquired by calling #pj_time_decode(). + */ +typedef struct pj_parsed_time +{ + /** This represents day of week where value zero means Sunday */ + int wday; + + /** This represents day of the year, 0-365, where zero means + * 1st of January. + */ + int yday; + + /** This represents day of month: 1-31 */ + int day; + + /** This represents month, with the value is 0 - 11 (zero is January) */ + int mon; + + /** This represent the actual year (unlike in ANSI libc where + * the value must be added by 1900). + */ + int year; + + /** This represents the second part, with the value is 0-59 */ + int sec; + + /** This represents the minute part, with the value is: 0-59 */ + int min; + + /** This represents the hour part, with the value is 0-23 */ + int hour; + + /** This represents the milisecond part, with the value is 0-999 */ + int msec; + +} pj_parsed_time; + + +/** + * @} // Time Management + */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * Terminal. + */ +/** + * Color code combination. + */ +enum { + PJ_TERM_COLOR_R = 2, /**< Red */ + PJ_TERM_COLOR_G = 4, /**< Green */ + PJ_TERM_COLOR_B = 1, /**< Blue. */ + PJ_TERM_COLOR_BRIGHT = 8 /**< Bright mask. */ +}; + + + + +PJ_END_DECL + + +#endif /* __PJ_TYPES_H__ */ + diff --git a/pjlib/include/pjlib++.hpp b/pjlib/include/pjlib++.hpp index 833f9a0e..59a3d611 100644 --- a/pjlib/include/pjlib++.hpp +++ b/pjlib/include/pjlib++.hpp @@ -1,33 +1,33 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJLIBPP_H__
-#define __PJLIBPP_H__
-
-#include <pj++/pool.hpp>
-#include <pj++/hash.hpp>
-#include <pj++/list.hpp>
-#include <pj++/os.hpp>
-#include <pj++/proactor.hpp>
-#include <pj++/scanner.hpp>
-#include <pj++/sock.hpp>
-#include <pj++/string.hpp>
-#include <pj++/timer.hpp>
-#include <pj++/tree.hpp>
-
-#endif /* __PJLIBPP_H__ */
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJLIBPP_H__ +#define __PJLIBPP_H__ + +#include <pj++/pool.hpp> +#include <pj++/hash.hpp> +#include <pj++/list.hpp> +#include <pj++/os.hpp> +#include <pj++/proactor.hpp> +#include <pj++/scanner.hpp> +#include <pj++/sock.hpp> +#include <pj++/string.hpp> +#include <pj++/timer.hpp> +#include <pj++/tree.hpp> + +#endif /* __PJLIBPP_H__ */ diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index a63f57ea..45a9e495 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -1,55 +1,55 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __PJLIB_H__
-#define __PJLIB_H__
-
-/**
- * @file pjlib.h
- * @brief Include all PJLIB header files.
- */
-
-#include <pj/addr_resolv.h>
-#include <pj/array.h>
-#include <pj/assert.h>
-#include <pj/ctype.h>
-#include <pj/errno.h>
-#include <pj/except.h>
-#include <pj/fifobuf.h>
-#include <pj/file_access.h>
-#include <pj/file_io.h>
-#include <pj/guid.h>
-#include <pj/hash.h>
-#include <pj/ioqueue.h>
-#include <pj/list.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/rand.h>
-#include <pj/rbtree.h>
-#include <pj/sock.h>
-#include <pj/sock_select.h>
-#include <pj/string.h>
-#include <pj/timer.h>
-
-#include <pj/compat/high_precision.h>
-
-#endif /* __PJLIB_H__ */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PJLIB_H__ +#define __PJLIB_H__ + +/** + * @file pjlib.h + * @brief Include all PJLIB header files. + */ + +#include <pj/addr_resolv.h> +#include <pj/array.h> +#include <pj/assert.h> +#include <pj/ctype.h> +#include <pj/errno.h> +#include <pj/except.h> +#include <pj/fifobuf.h> +#include <pj/file_access.h> +#include <pj/file_io.h> +#include <pj/guid.h> +#include <pj/hash.h> +#include <pj/ioqueue.h> +#include <pj/list.h> +#include <pj/lock.h> +#include <pj/log.h> +#include <pj/os.h> +#include <pj/pool.h> +#include <pj/rand.h> +#include <pj/rbtree.h> +#include <pj/sock.h> +#include <pj/sock_select.h> +#include <pj/string.h> +#include <pj/timer.h> + +#include <pj/compat/high_precision.h> + +#endif /* __PJLIB_H__ */ + diff --git a/pjlib/src/pj/config.c b/pjlib/src/pj/config.c index d6e55b3e..8c7ed522 100644 --- a/pjlib/src/pj/config.c +++ b/pjlib/src/pj/config.c @@ -1,48 +1,48 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/config.h>
-#include <pj/log.h>
-#include <pj/ioqueue.h>
-
-static const char *id = "config.c";
-const char *PJ_VERSION = "0.3.0-pre4";
-
-PJ_DEF(void) pj_dump_config(void)
-{
- PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono"));
- PJ_LOG(3, (id, "Dumping configurations:"));
- PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION));
- PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG));
- PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED));
- PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG));
- PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS));
- PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL));
- PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE));
- PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER));
- PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP));
- PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME));
- PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE));
- PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ));
- PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER));
- PJ_LOG(3, (id, " PJ_(endianness) : %s",
- (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian")));
- PJ_LOG(3, (id, " ioqueue type : %s", pj_ioqueue_name()));
- PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES));
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/config.h> +#include <pj/log.h> +#include <pj/ioqueue.h> + +static const char *id = "config.c"; +const char *PJ_VERSION = "0.3.0-pre4"; + +PJ_DEF(void) pj_dump_config(void) +{ + PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono")); + PJ_LOG(3, (id, "Dumping configurations:")); + PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION)); + PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG)); + PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED)); + PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG)); + PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS)); + PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL)); + PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE)); + PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER)); + PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP)); + PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME)); + PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE)); + PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ)); + PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER)); + PJ_LOG(3, (id, " PJ_(endianness) : %s", + (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian"))); + PJ_LOG(3, (id, " ioqueue type : %s", pj_ioqueue_name())); + PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES)); +} + diff --git a/pjlib/src/pj/ctype.c b/pjlib/src/pj/ctype.c index b274ccaf..a5b83b46 100644 --- a/pjlib/src/pj/ctype.c +++ b/pjlib/src/pj/ctype.c @@ -1,24 +1,24 @@ -/* $Id: $ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/ctype.h>
-
-
-char pj_hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
+/* $Id: $ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/ctype.h> + + +char pj_hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + diff --git a/pjlib/src/pj/equeue_winnt.c b/pjlib/src/pj/equeue_winnt.c index c395b2f0..e7a20c47 100644 --- a/pjlib/src/pj/equeue_winnt.c +++ b/pjlib/src/pj/equeue_winnt.c @@ -1,19 +1,19 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/equeue.h>
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/equeue.h> diff --git a/pjlib/src/pj/errno.c b/pjlib/src/pj/errno.c index 0b471215..d214d687 100644 --- a/pjlib/src/pj/errno.c +++ b/pjlib/src/pj/errno.c @@ -1,115 +1,115 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/errno.h>
-#include <pj/string.h>
-#include <pj/compat/sprintf.h>
-
-/* Prototype for platform specific error message, which will be defined
- * in separate file.
- */
-extern int platform_strerror( pj_os_err_type code,
- char *buf, pj_size_t bufsize );
-
-/* PJLIB's own error codes/messages */
-static const struct
-{
- int code;
- const char *msg;
-} err_str[] =
-{
- { PJ_EUNKNOWN, "Unknown Error" },
- { PJ_EPENDING, "Pending operation" },
- { PJ_ETOOMANYCONN, "Too many connecting sockets" },
- { PJ_EINVAL, "Invalid value or argument" },
- { PJ_ENAMETOOLONG, "Name too long" },
- { PJ_ENOTFOUND, "Not found" },
- { PJ_ENOMEM, "Not enough memory" },
- { PJ_EBUG, "BUG DETECTED!" },
- { PJ_ETIMEDOUT, "Operation timed out" },
- { PJ_ETOOMANY, "Too many objects of the specified type"},
- { PJ_EBUSY, "Object is busy"},
- { PJ_ENOTSUP, "Option/operation is not supported"},
- { PJ_EINVALIDOP, "Invalid operation"},
- { PJ_ECANCELLED, "Operation cancelled"},
- { PJ_EEXISTS, "Object already exists" }
-};
-
-/*
- * pjlib_error()
- *
- * Retrieve message string for PJLIB's own error code.
- */
-static int pjlib_error(pj_status_t code, char *buf, pj_size_t size)
-{
- unsigned i;
-
- for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) {
- if (err_str[i].code == code) {
- pj_size_t len = strlen(err_str[i].msg);
- if (len >= size) len = size-1;
- pj_memcpy(buf, err_str[i].msg, len);
- buf[len] = '\0';
- return len;
- }
- }
-
- *buf++ = '?';
- *buf++ = '?';
- *buf++ = '?';
- *buf++ = '\0';
- return 3;
-}
-
-/*
- * pj_strerror()
- */
-PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode,
- char *buf, pj_size_t bufsize )
-{
- int len = -1;
- pj_str_t errstr;
-
- if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
- len = pj_snprintf( buf, bufsize, "Unknown error %d", statcode);
-
- } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
- len = pjlib_error(statcode, buf, bufsize);
-
- } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
- len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);
-
- } else if (statcode < PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) {
- len = pj_snprintf( buf, bufsize, "User error %d", statcode);
-
- } else {
- len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode);
-
- }
-
- if (len < 1) {
- *buf = '\0';
- len = 0;
- }
-
- errstr.ptr = buf;
- errstr.slen = len;
-
- return errstr;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/errno.h> +#include <pj/string.h> +#include <pj/compat/sprintf.h> + +/* Prototype for platform specific error message, which will be defined + * in separate file. + */ +extern int platform_strerror( pj_os_err_type code, + char *buf, pj_size_t bufsize ); + +/* PJLIB's own error codes/messages */ +static const struct +{ + int code; + const char *msg; +} err_str[] = +{ + { PJ_EUNKNOWN, "Unknown Error" }, + { PJ_EPENDING, "Pending operation" }, + { PJ_ETOOMANYCONN, "Too many connecting sockets" }, + { PJ_EINVAL, "Invalid value or argument" }, + { PJ_ENAMETOOLONG, "Name too long" }, + { PJ_ENOTFOUND, "Not found" }, + { PJ_ENOMEM, "Not enough memory" }, + { PJ_EBUG, "BUG DETECTED!" }, + { PJ_ETIMEDOUT, "Operation timed out" }, + { PJ_ETOOMANY, "Too many objects of the specified type"}, + { PJ_EBUSY, "Object is busy"}, + { PJ_ENOTSUP, "Option/operation is not supported"}, + { PJ_EINVALIDOP, "Invalid operation"}, + { PJ_ECANCELLED, "Operation cancelled"}, + { PJ_EEXISTS, "Object already exists" } +}; + +/* + * pjlib_error() + * + * Retrieve message string for PJLIB's own error code. + */ +static int pjlib_error(pj_status_t code, char *buf, pj_size_t size) +{ + unsigned i; + + for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) { + if (err_str[i].code == code) { + pj_size_t len = strlen(err_str[i].msg); + if (len >= size) len = size-1; + pj_memcpy(buf, err_str[i].msg, len); + buf[len] = '\0'; + return len; + } + } + + *buf++ = '?'; + *buf++ = '?'; + *buf++ = '?'; + *buf++ = '\0'; + return 3; +} + +/* + * pj_strerror() + */ +PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode, + char *buf, pj_size_t bufsize ) +{ + int len = -1; + pj_str_t errstr; + + if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) { + len = pj_snprintf( buf, bufsize, "Unknown error %d", statcode); + + } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) { + len = pjlib_error(statcode, buf, bufsize); + + } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) { + len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize); + + } else if (statcode < PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) { + len = pj_snprintf( buf, bufsize, "User error %d", statcode); + + } else { + len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode); + + } + + if (len < 1) { + *buf = '\0'; + len = 0; + } + + errstr.ptr = buf; + errstr.slen = len; + + return errstr; +} + diff --git a/pjlib/src/pj/except.c b/pjlib/src/pj/except.c index b58c8f71..ceaaad16 100644 --- a/pjlib/src/pj/except.c +++ b/pjlib/src/pj/except.c @@ -1,151 +1,151 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/except.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/errno.h>
-
-static long thread_local_id = -1;
-
-#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
- static const char *exception_id_names[PJ_MAX_EXCEPTION_ID];
-#else
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- static int last_exception_id = 1;
-#endif /* PJ_HAS_EXCEPTION_NAMES */
-
-
-PJ_DEF(void) pj_throw_exception_(int exception_id)
-{
- struct pj_exception_state_t *handler;
-
- handler = pj_thread_local_get(thread_local_id);
- if (handler == NULL) {
- PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %d!\n", exception_id));
- pj_assert(handler != NULL);
- /* This will crash the system! */
- }
- pj_longjmp(handler->state, exception_id);
-}
-
-PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec)
-{
- struct pj_exception_state_t *parent_handler = NULL;
-
- if (thread_local_id == -1) {
- pj_thread_local_alloc(&thread_local_id);
- pj_assert(thread_local_id != -1);
- }
- parent_handler = pj_thread_local_get(thread_local_id);
- rec->prev = parent_handler;
- pj_thread_local_set(thread_local_id, rec);
-}
-
-PJ_DEF(void) pj_pop_exception_handler_(void)
-{
- struct pj_exception_state_t *handler;
-
- handler = pj_thread_local_get(thread_local_id);
- pj_assert(handler != NULL);
- pj_thread_local_set(thread_local_id, handler->prev);
-}
-
-#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
-PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
- pj_exception_id_t *id)
-{
- unsigned i;
-
- pj_enter_critical_section();
-
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) {
- if (exception_id_names[i] == NULL) {
- exception_id_names[i] = name;
- *id = i;
- pj_leave_critical_section();
- return PJ_SUCCESS;
- }
- }
-
- pj_leave_critical_section();
- return PJ_ETOOMANY;
-}
-
-PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
-{
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL);
-
- pj_enter_critical_section();
- exception_id_names[id] = NULL;
- pj_leave_critical_section();
-
- return PJ_SUCCESS;
-
-}
-
-PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
-{
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>");
-
- if (exception_id_names[id] == NULL)
- return "<Unallocated ID>";
-
- return exception_id_names[id];
-}
-
-#else /* PJ_HAS_EXCEPTION_NAMES */
-PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
- pj_exception_id_t *id)
-{
- PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY);
-
- *id = last_exception_id++
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
-{
- return PJ_SUCCESS;
-}
-
-PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
-{
- return "";
-}
-
-#endif /* PJ_HAS_EXCEPTION_NAMES */
-
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/except.h> +#include <pj/os.h> +#include <pj/assert.h> +#include <pj/log.h> +#include <pj/errno.h> + +static long thread_local_id = -1; + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 + static const char *exception_id_names[PJ_MAX_EXCEPTION_ID]; +#else + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + static int last_exception_id = 1; +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + +PJ_DEF(void) pj_throw_exception_(int exception_id) +{ + struct pj_exception_state_t *handler; + + handler = pj_thread_local_get(thread_local_id); + if (handler == NULL) { + PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %d!\n", exception_id)); + pj_assert(handler != NULL); + /* This will crash the system! */ + } + pj_longjmp(handler->state, exception_id); +} + +PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec) +{ + struct pj_exception_state_t *parent_handler = NULL; + + if (thread_local_id == -1) { + pj_thread_local_alloc(&thread_local_id); + pj_assert(thread_local_id != -1); + } + parent_handler = pj_thread_local_get(thread_local_id); + rec->prev = parent_handler; + pj_thread_local_set(thread_local_id, rec); +} + +PJ_DEF(void) pj_pop_exception_handler_(void) +{ + struct pj_exception_state_t *handler; + + handler = pj_thread_local_get(thread_local_id); + pj_assert(handler != NULL); + pj_thread_local_set(thread_local_id, handler->prev); +} + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + unsigned i; + + pj_enter_critical_section(); + + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) { + if (exception_id_names[i] == NULL) { + exception_id_names[i] = name; + *id = i; + pj_leave_critical_section(); + return PJ_SUCCESS; + } + } + + pj_leave_critical_section(); + return PJ_ETOOMANY; +} + +PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) +{ + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL); + + pj_enter_critical_section(); + exception_id_names[id] = NULL; + pj_leave_critical_section(); + + return PJ_SUCCESS; + +} + +PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) +{ + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>"); + + if (exception_id_names[id] == NULL) + return "<Unallocated ID>"; + + return exception_id_names[id]; +} + +#else /* PJ_HAS_EXCEPTION_NAMES */ +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY); + + *id = last_exception_id++ + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) +{ + return PJ_SUCCESS; +} + +PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) +{ + return ""; +} + +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + + diff --git a/pjlib/src/pj/extra-exports.c b/pjlib/src/pj/extra-exports.c index f86ed31e..05f7a3f8 100644 --- a/pjlib/src/pj/extra-exports.c +++ b/pjlib/src/pj/extra-exports.c @@ -1,40 +1,40 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <linux/module.h>
-#include <linux/syscalls.h>
-
-EXPORT_SYMBOL(sys_select);
-
-EXPORT_SYMBOL(sys_epoll_create);
-EXPORT_SYMBOL(sys_epoll_ctl);
-EXPORT_SYMBOL(sys_epoll_wait);
-
-EXPORT_SYMBOL(sys_socket);
-EXPORT_SYMBOL(sys_bind);
-EXPORT_SYMBOL(sys_getpeername);
-EXPORT_SYMBOL(sys_getsockname);
-EXPORT_SYMBOL(sys_sendto);
-EXPORT_SYMBOL(sys_recvfrom);
-EXPORT_SYMBOL(sys_getsockopt);
-EXPORT_SYMBOL(sys_setsockopt);
-EXPORT_SYMBOL(sys_listen);
-EXPORT_SYMBOL(sys_shutdown);
-EXPORT_SYMBOL(sys_connect);
-EXPORT_SYMBOL(sys_accept);
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/syscalls.h> + +EXPORT_SYMBOL(sys_select); + +EXPORT_SYMBOL(sys_epoll_create); +EXPORT_SYMBOL(sys_epoll_ctl); +EXPORT_SYMBOL(sys_epoll_wait); + +EXPORT_SYMBOL(sys_socket); +EXPORT_SYMBOL(sys_bind); +EXPORT_SYMBOL(sys_getpeername); +EXPORT_SYMBOL(sys_getsockname); +EXPORT_SYMBOL(sys_sendto); +EXPORT_SYMBOL(sys_recvfrom); +EXPORT_SYMBOL(sys_getsockopt); +EXPORT_SYMBOL(sys_setsockopt); +EXPORT_SYMBOL(sys_listen); +EXPORT_SYMBOL(sys_shutdown); +EXPORT_SYMBOL(sys_connect); +EXPORT_SYMBOL(sys_accept); + diff --git a/pjlib/src/pj/fifobuf.c b/pjlib/src/pj/fifobuf.c index 99b082e4..00b48b65 100644 --- a/pjlib/src/pj/fifobuf.c +++ b/pjlib/src/pj/fifobuf.c @@ -1,193 +1,193 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/fifobuf.h>
-#include <pj/log.h>
-#include <pj/assert.h>
-#include <pj/os.h>
-
-#define THIS_FILE "fifobuf"
-
-#define SZ sizeof(unsigned)
-
-PJ_DEF(void)
-pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size)
-{
- PJ_CHECK_STACK();
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_init fifobuf=%p buffer=%p, size=%d",
- fifobuf, buffer, size));
-
- fifobuf->first = buffer;
- fifobuf->last = fifobuf->first + size;
- fifobuf->ubegin = fifobuf->uend = fifobuf->first;
- fifobuf->full = 0;
-}
-
-PJ_DEF(unsigned)
-pj_fifobuf_max_size (pj_fifobuf_t *fifobuf)
-{
- unsigned s1, s2;
-
- PJ_CHECK_STACK();
-
- if (fifobuf->uend >= fifobuf->ubegin) {
- s1 = fifobuf->last - fifobuf->uend;
- s2 = fifobuf->ubegin - fifobuf->first;
- } else {
- s1 = s2 = fifobuf->ubegin - fifobuf->uend;
- }
-
- return s1<s2 ? s2 : s1;
-}
-
-PJ_DEF(void*)
-pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size)
-{
- unsigned available;
- char *start;
-
- PJ_CHECK_STACK();
-
- if (fifobuf->full) {
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: full!",
- fifobuf, size));
- return NULL;
- }
-
- /* try to allocate from the end part of the fifo */
- if (fifobuf->uend >= fifobuf->ubegin) {
- available = fifobuf->last - fifobuf->uend;
- if (available >= size+SZ) {
- char *ptr = fifobuf->uend;
- fifobuf->uend += (size+SZ);
- if (fifobuf->uend == fifobuf->last)
- fifobuf->uend = fifobuf->first;
- if (fifobuf->uend == fifobuf->ubegin)
- fifobuf->full = 1;
- *(unsigned*)ptr = size+SZ;
- ptr += SZ;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
- fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
- return ptr;
- }
- }
-
- /* try to allocate from the start part of the fifo */
- start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first;
- available = fifobuf->ubegin - start;
- if (available >= size+SZ) {
- char *ptr = start;
- fifobuf->uend = start + size + SZ;
- if (fifobuf->uend == fifobuf->ubegin)
- fifobuf->full = 1;
- *(unsigned*)ptr = size+SZ;
- ptr += SZ;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
- fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
- return ptr;
- }
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p",
- fifobuf, size, fifobuf->ubegin, fifobuf->uend));
- return NULL;
-}
-
-PJ_DEF(pj_status_t)
-pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf)
-{
- char *ptr = buf;
- char *endptr;
- unsigned sz;
-
- PJ_CHECK_STACK();
-
- ptr -= SZ;
- sz = *(unsigned*)ptr;
-
- endptr = fifobuf->uend;
- if (endptr == fifobuf->first)
- endptr = fifobuf->last;
-
- if (ptr+sz != endptr) {
- pj_assert(!"Invalid pointer to undo alloc");
- return -1;
- }
-
- fifobuf->uend = ptr;
- fifobuf->full = 0;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
- fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
-
- return 0;
-}
-
-PJ_DEF(pj_status_t)
-pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf)
-{
- char *ptr = buf;
- char *end;
- unsigned sz;
-
- PJ_CHECK_STACK();
-
- ptr -= SZ;
- if (ptr < fifobuf->first || ptr >= fifobuf->last) {
- pj_assert(!"Invalid pointer to free");
- return -1;
- }
-
- if (ptr != fifobuf->ubegin && ptr != fifobuf->first) {
- pj_assert(!"Invalid free() sequence!");
- return -1;
- }
-
- end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last;
- sz = *(unsigned*)ptr;
- if (ptr+sz > end) {
- pj_assert(!"Invalid size!");
- return -1;
- }
-
- fifobuf->ubegin = ptr + sz;
-
- /* Rollover */
- if (fifobuf->ubegin == fifobuf->last)
- fifobuf->ubegin = fifobuf->first;
-
- /* Reset if fifobuf is empty */
- if (fifobuf->ubegin == fifobuf->uend)
- fifobuf->ubegin = fifobuf->uend = fifobuf->first;
-
- fifobuf->full = 0;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
- fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
-
- return 0;
-}
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/fifobuf.h> +#include <pj/log.h> +#include <pj/assert.h> +#include <pj/os.h> + +#define THIS_FILE "fifobuf" + +#define SZ sizeof(unsigned) + +PJ_DEF(void) +pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size) +{ + PJ_CHECK_STACK(); + + PJ_LOG(6, (THIS_FILE, + "fifobuf_init fifobuf=%p buffer=%p, size=%d", + fifobuf, buffer, size)); + + fifobuf->first = buffer; + fifobuf->last = fifobuf->first + size; + fifobuf->ubegin = fifobuf->uend = fifobuf->first; + fifobuf->full = 0; +} + +PJ_DEF(unsigned) +pj_fifobuf_max_size (pj_fifobuf_t *fifobuf) +{ + unsigned s1, s2; + + PJ_CHECK_STACK(); + + if (fifobuf->uend >= fifobuf->ubegin) { + s1 = fifobuf->last - fifobuf->uend; + s2 = fifobuf->ubegin - fifobuf->first; + } else { + s1 = s2 = fifobuf->ubegin - fifobuf->uend; + } + + return s1<s2 ? s2 : s1; +} + +PJ_DEF(void*) +pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size) +{ + unsigned available; + char *start; + + PJ_CHECK_STACK(); + + if (fifobuf->full) { + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: full!", + fifobuf, size)); + return NULL; + } + + /* try to allocate from the end part of the fifo */ + if (fifobuf->uend >= fifobuf->ubegin) { + available = fifobuf->last - fifobuf->uend; + if (available >= size+SZ) { + char *ptr = fifobuf->uend; + fifobuf->uend += (size+SZ); + if (fifobuf->uend == fifobuf->last) + fifobuf->uend = fifobuf->first; + if (fifobuf->uend == fifobuf->ubegin) + fifobuf->full = 1; + *(unsigned*)ptr = size+SZ; + ptr += SZ; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", + fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); + return ptr; + } + } + + /* try to allocate from the start part of the fifo */ + start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first; + available = fifobuf->ubegin - start; + if (available >= size+SZ) { + char *ptr = start; + fifobuf->uend = start + size + SZ; + if (fifobuf->uend == fifobuf->ubegin) + fifobuf->full = 1; + *(unsigned*)ptr = size+SZ; + ptr += SZ; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", + fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); + return ptr; + } + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p", + fifobuf, size, fifobuf->ubegin, fifobuf->uend)); + return NULL; +} + +PJ_DEF(pj_status_t) +pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf) +{ + char *ptr = buf; + char *endptr; + unsigned sz; + + PJ_CHECK_STACK(); + + ptr -= SZ; + sz = *(unsigned*)ptr; + + endptr = fifobuf->uend; + if (endptr == fifobuf->first) + endptr = fifobuf->last; + + if (ptr+sz != endptr) { + pj_assert(!"Invalid pointer to undo alloc"); + return -1; + } + + fifobuf->uend = ptr; + fifobuf->full = 0; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", + fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); + + return 0; +} + +PJ_DEF(pj_status_t) +pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) +{ + char *ptr = buf; + char *end; + unsigned sz; + + PJ_CHECK_STACK(); + + ptr -= SZ; + if (ptr < fifobuf->first || ptr >= fifobuf->last) { + pj_assert(!"Invalid pointer to free"); + return -1; + } + + if (ptr != fifobuf->ubegin && ptr != fifobuf->first) { + pj_assert(!"Invalid free() sequence!"); + return -1; + } + + end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last; + sz = *(unsigned*)ptr; + if (ptr+sz > end) { + pj_assert(!"Invalid size!"); + return -1; + } + + fifobuf->ubegin = ptr + sz; + + /* Rollover */ + if (fifobuf->ubegin == fifobuf->last) + fifobuf->ubegin = fifobuf->first; + + /* Reset if fifobuf is empty */ + if (fifobuf->ubegin == fifobuf->uend) + fifobuf->ubegin = fifobuf->uend = fifobuf->first; + + fifobuf->full = 0; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", + fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); + + return 0; +} diff --git a/pjlib/src/pj/file_access_unistd.c b/pjlib/src/pj/file_access_unistd.c index ba5fa854..f456f193 100644 --- a/pjlib/src/pj/file_access_unistd.c +++ b/pjlib/src/pj/file_access_unistd.c @@ -1,113 +1,113 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/file_access.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdio.h> /* rename() */
-#include <errno.h>
-
-/*
- * pj_file_exists()
- */
-PJ_DEF(pj_bool_t) pj_file_exists(const char *filename)
-{
- struct stat buf;
-
- PJ_ASSERT_RETURN(filename, 0);
-
- if (stat(filename, &buf) != 0)
- return 0;
-
- return PJ_TRUE;
-}
-
-
-/*
- * pj_file_size()
- */
-PJ_DEF(pj_off_t) pj_file_size(const char *filename)
-{
- struct stat buf;
-
- PJ_ASSERT_RETURN(filename, -1);
-
- if (stat(filename, &buf) != 0)
- return -1;
-
- return buf.st_size;
-}
-
-
-/*
- * pj_file_delete()
- */
-PJ_DEF(pj_status_t) pj_file_delete(const char *filename)
-{
- PJ_ASSERT_RETURN(filename, PJ_EINVAL);
-
- if (unlink(filename)!=0) {
- return PJ_RETURN_OS_ERROR(errno);
- }
- return PJ_SUCCESS;
-}
-
-
-/*
- * pj_file_move()
- */
-PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname)
-{
- PJ_ASSERT_RETURN(oldname && newname, PJ_EINVAL);
-
- if (rename(oldname, newname) != 0) {
- return PJ_RETURN_OS_ERROR(errno);
- }
- return PJ_SUCCESS;
-}
-
-
-/*
- * pj_file_getstat()
- */
-PJ_DEF(pj_status_t) pj_file_getstat(const char *filename,
- pj_file_stat *statbuf)
-{
- struct stat buf;
-
- PJ_ASSERT_RETURN(filename && statbuf, PJ_EINVAL);
-
- if (stat(filename, &buf) != 0) {
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- statbuf->size = buf.st_size;
- statbuf->ctime.sec = buf.st_ctime;
- statbuf->ctime.msec = 0;
- statbuf->mtime.sec = buf.st_mtime;
- statbuf->mtime.msec = 0;
- statbuf->atime.sec = buf.st_atime;
- statbuf->atime.msec = 0;
-
- return PJ_SUCCESS;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/file_access.h> +#include <pj/assert.h> +#include <pj/errno.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> /* rename() */ +#include <errno.h> + +/* + * pj_file_exists() + */ +PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) +{ + struct stat buf; + + PJ_ASSERT_RETURN(filename, 0); + + if (stat(filename, &buf) != 0) + return 0; + + return PJ_TRUE; +} + + +/* + * pj_file_size() + */ +PJ_DEF(pj_off_t) pj_file_size(const char *filename) +{ + struct stat buf; + + PJ_ASSERT_RETURN(filename, -1); + + if (stat(filename, &buf) != 0) + return -1; + + return buf.st_size; +} + + +/* + * pj_file_delete() + */ +PJ_DEF(pj_status_t) pj_file_delete(const char *filename) +{ + PJ_ASSERT_RETURN(filename, PJ_EINVAL); + + if (unlink(filename)!=0) { + return PJ_RETURN_OS_ERROR(errno); + } + return PJ_SUCCESS; +} + + +/* + * pj_file_move() + */ +PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname) +{ + PJ_ASSERT_RETURN(oldname && newname, PJ_EINVAL); + + if (rename(oldname, newname) != 0) { + return PJ_RETURN_OS_ERROR(errno); + } + return PJ_SUCCESS; +} + + +/* + * pj_file_getstat() + */ +PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, + pj_file_stat *statbuf) +{ + struct stat buf; + + PJ_ASSERT_RETURN(filename && statbuf, PJ_EINVAL); + + if (stat(filename, &buf) != 0) { + return PJ_RETURN_OS_ERROR(errno); + } + + statbuf->size = buf.st_size; + statbuf->ctime.sec = buf.st_ctime; + statbuf->ctime.msec = 0; + statbuf->mtime.sec = buf.st_mtime; + statbuf->mtime.msec = 0; + statbuf->atime.sec = buf.st_atime; + statbuf->atime.msec = 0; + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/file_access_win32.c b/pjlib/src/pj/file_access_win32.c index e8378bdf..79eb1f89 100644 --- a/pjlib/src/pj/file_access_win32.c +++ b/pjlib/src/pj/file_access_win32.c @@ -1,189 +1,189 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/file_access.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <windows.h>
-#include <time.h>
-
-/*
- * pj_file_exists()
- */
-PJ_DEF(pj_bool_t) pj_file_exists(const char *filename)
-{
- HANDLE hFile;
-
- PJ_ASSERT_RETURN(filename != NULL, 0);
-
- hFile = CreateFile(filename, READ_CONTROL, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- return 0;
-
- CloseHandle(hFile);
- return PJ_TRUE;
-}
-
-
-/*
- * pj_file_size()
- */
-PJ_DEF(pj_off_t) pj_file_size(const char *filename)
-{
- HANDLE hFile;
- DWORD sizeLo, sizeHi;
- pj_off_t size;
-
- PJ_ASSERT_RETURN(filename != NULL, -1);
-
- hFile = CreateFile(filename, READ_CONTROL,
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- return -1;
-
- sizeLo = GetFileSize(hFile, &sizeHi);
- if (sizeLo == INVALID_FILE_SIZE) {
- DWORD dwStatus = GetLastError();
- if (dwStatus != NO_ERROR) {
- CloseHandle(hFile);
- return -1;
- }
- }
-
- size = sizeHi;
- size = (size << 32) + sizeLo;
-
- CloseHandle(hFile);
- return size;
-}
-
-
-/*
- * pj_file_delete()
- */
-PJ_DEF(pj_status_t) pj_file_delete(const char *filename)
-{
- PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);
-
- if (DeleteFile(filename) == FALSE)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * pj_file_move()
- */
-PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname)
-{
- BOOL rc;
-
- PJ_ASSERT_RETURN(oldname!=NULL && newname!=NULL, PJ_EINVAL);
-
-#if PJ_WIN32_WINNT >= 0x0400
- rc = MoveFileEx(oldname, newname,
- MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING);
-#else
- rc = MoveFile(oldname, newname);
-#endif
-
- if (!rc)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- return PJ_SUCCESS;
-}
-
-
-static pj_status_t file_time_to_time_val(const FILETIME *file_time,
- pj_time_val *time_val)
-{
- SYSTEMTIME systemTime, localTime;
- struct tm tm;
-
- if (!FileTimeToSystemTime(file_time, &systemTime))
- return -1;
-
- if (!SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime))
- return -1;
-
- memset(&tm, 0, sizeof(struct tm));
- tm.tm_year = localTime.wYear - 1900;
- tm.tm_mon = localTime.wMonth - 1;
- tm.tm_mday = localTime.wDay;
- tm.tm_hour = localTime.wHour;
- tm.tm_min = localTime.wMinute;
- tm.tm_sec = localTime.wSecond;
- tm.tm_isdst = 0;
-
- time_val->sec = mktime(&tm);
- if (time_val->sec == (time_t)-1)
- return -1;
-
- time_val->msec = localTime.wMilliseconds;
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_file_getstat()
- */
-PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat)
-{
- HANDLE hFile;
- DWORD sizeLo, sizeHi;
- FILETIME creationTime, accessTime, writeTime;
-
- PJ_ASSERT_RETURN(filename!=NULL && stat!=NULL, PJ_EINVAL);
-
- hFile = CreateFile(filename, READ_CONTROL, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- sizeLo = GetFileSize(hFile, &sizeHi);
- if (sizeLo == INVALID_FILE_SIZE) {
- DWORD dwStatus = GetLastError();
- if (dwStatus != NO_ERROR) {
- CloseHandle(hFile);
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
- }
-
- stat->size = sizeHi;
- stat->size = (stat->size << 32) + sizeLo;
-
- if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime)==FALSE) {
- DWORD dwStatus = GetLastError();
- CloseHandle(hFile);
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
-
- CloseHandle(hFile);
-
- if (file_time_to_time_val(&creationTime, &stat->ctime) != PJ_SUCCESS)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- file_time_to_time_val(&accessTime, &stat->atime);
- file_time_to_time_val(&writeTime, &stat->mtime);
-
- return PJ_SUCCESS;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/file_access.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <windows.h> +#include <time.h> + +/* + * pj_file_exists() + */ +PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) +{ + HANDLE hFile; + + PJ_ASSERT_RETURN(filename != NULL, 0); + + hFile = CreateFile(filename, READ_CONTROL, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + + CloseHandle(hFile); + return PJ_TRUE; +} + + +/* + * pj_file_size() + */ +PJ_DEF(pj_off_t) pj_file_size(const char *filename) +{ + HANDLE hFile; + DWORD sizeLo, sizeHi; + pj_off_t size; + + PJ_ASSERT_RETURN(filename != NULL, -1); + + hFile = CreateFile(filename, READ_CONTROL, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return -1; + + sizeLo = GetFileSize(hFile, &sizeHi); + if (sizeLo == INVALID_FILE_SIZE) { + DWORD dwStatus = GetLastError(); + if (dwStatus != NO_ERROR) { + CloseHandle(hFile); + return -1; + } + } + + size = sizeHi; + size = (size << 32) + sizeLo; + + CloseHandle(hFile); + return size; +} + + +/* + * pj_file_delete() + */ +PJ_DEF(pj_status_t) pj_file_delete(const char *filename) +{ + PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL); + + if (DeleteFile(filename) == FALSE) + return PJ_RETURN_OS_ERROR(GetLastError()); + + return PJ_SUCCESS; +} + + +/* + * pj_file_move() + */ +PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname) +{ + BOOL rc; + + PJ_ASSERT_RETURN(oldname!=NULL && newname!=NULL, PJ_EINVAL); + +#if PJ_WIN32_WINNT >= 0x0400 + rc = MoveFileEx(oldname, newname, + MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING); +#else + rc = MoveFile(oldname, newname); +#endif + + if (!rc) + return PJ_RETURN_OS_ERROR(GetLastError()); + + return PJ_SUCCESS; +} + + +static pj_status_t file_time_to_time_val(const FILETIME *file_time, + pj_time_val *time_val) +{ + SYSTEMTIME systemTime, localTime; + struct tm tm; + + if (!FileTimeToSystemTime(file_time, &systemTime)) + return -1; + + if (!SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime)) + return -1; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = localTime.wYear - 1900; + tm.tm_mon = localTime.wMonth - 1; + tm.tm_mday = localTime.wDay; + tm.tm_hour = localTime.wHour; + tm.tm_min = localTime.wMinute; + tm.tm_sec = localTime.wSecond; + tm.tm_isdst = 0; + + time_val->sec = mktime(&tm); + if (time_val->sec == (time_t)-1) + return -1; + + time_val->msec = localTime.wMilliseconds; + + return PJ_SUCCESS; +} + +/* + * pj_file_getstat() + */ +PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat) +{ + HANDLE hFile; + DWORD sizeLo, sizeHi; + FILETIME creationTime, accessTime, writeTime; + + PJ_ASSERT_RETURN(filename!=NULL && stat!=NULL, PJ_EINVAL); + + hFile = CreateFile(filename, READ_CONTROL, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return PJ_RETURN_OS_ERROR(GetLastError()); + + sizeLo = GetFileSize(hFile, &sizeHi); + if (sizeLo == INVALID_FILE_SIZE) { + DWORD dwStatus = GetLastError(); + if (dwStatus != NO_ERROR) { + CloseHandle(hFile); + return PJ_RETURN_OS_ERROR(dwStatus); + } + } + + stat->size = sizeHi; + stat->size = (stat->size << 32) + sizeLo; + + if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime)==FALSE) { + DWORD dwStatus = GetLastError(); + CloseHandle(hFile); + return PJ_RETURN_OS_ERROR(dwStatus); + } + + CloseHandle(hFile); + + if (file_time_to_time_val(&creationTime, &stat->ctime) != PJ_SUCCESS) + return PJ_RETURN_OS_ERROR(GetLastError()); + + file_time_to_time_val(&accessTime, &stat->atime); + file_time_to_time_val(&writeTime, &stat->mtime); + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/file_io_ansi.c b/pjlib/src/pj/file_io_ansi.c index 057af04a..dafa329d 100644 --- a/pjlib/src/pj/file_io_ansi.c +++ b/pjlib/src/pj/file_io_ansi.c @@ -1,157 +1,157 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/file_io.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <stdio.h>
-#include <errno.h>
-
-PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
- const char *pathname,
- unsigned flags,
- pj_oshandle_t *fd)
-{
- char mode[8];
- char *p = mode;
-
- PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL);
- PJ_UNUSED_ARG(pool);
-
- if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
- if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
- *p++ = 'a';
- if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY)
- *p++ = '+';
- } else {
- /* This is invalid.
- * Can not specify PJ_O_RDONLY with PJ_O_APPEND!
- */
- }
- } else {
- if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
- *p++ = 'r';
- if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY)
- *p++ = '+';
- } else {
- *p++ = 'w';
- }
- }
-
- if (p==mode)
- return PJ_EINVAL;
-
- *p++ = 'b';
- *p++ = '\0';
-
- *fd = fopen(pathname, mode);
- if (*fd == NULL)
- return PJ_RETURN_OS_ERROR(errno);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
-{
- PJ_ASSERT_RETURN(fd, PJ_EINVAL);
- if (fclose((FILE*)fd) != 0)
- return PJ_RETURN_OS_ERROR(errno);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
- const void *data,
- pj_ssize_t *size)
-{
- size_t written;
-
- clearerr((FILE*)fd);
- written = fwrite(data, 1, *size, (FILE*)fd);
- if (ferror((FILE*)fd)) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- *size = written;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
- void *data,
- pj_ssize_t *size)
-{
- size_t bytes;
-
- clearerr((FILE*)fd);
- bytes = fread(data, 1, *size, (FILE*)fd);
- if (ferror((FILE*)fd)) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- *size = bytes;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
-{
- PJ_UNUSED_ARG(access);
- return feof((FILE*)fd) ? PJ_TRUE : 0;
-}
-
-PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
- pj_off_t offset,
- enum pj_file_seek_type whence)
-{
- int mode;
-
- switch (whence) {
- case PJ_SEEK_SET:
- mode = SEEK_SET; break;
- case PJ_SEEK_CUR:
- mode = SEEK_CUR; break;
- case PJ_SEEK_END:
- mode = SEEK_END; break;
- default:
- pj_assert(!"Invalid whence in file_setpos");
- return PJ_EINVAL;
- }
-
- if (fseek((FILE*)fd, (long)offset, mode) != 0)
- return PJ_RETURN_OS_ERROR(errno);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
- pj_off_t *pos)
-{
- long offset;
-
- offset = ftell((FILE*)fd);
- if (offset == -1) {
- *pos = -1;
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- *pos = offset;
- return PJ_SUCCESS;
-}
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/file_io.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <stdio.h> +#include <errno.h> + +PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, + const char *pathname, + unsigned flags, + pj_oshandle_t *fd) +{ + char mode[8]; + char *p = mode; + + PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL); + PJ_UNUSED_ARG(pool); + + if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { + if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) { + *p++ = 'a'; + if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) + *p++ = '+'; + } else { + /* This is invalid. + * Can not specify PJ_O_RDONLY with PJ_O_APPEND! + */ + } + } else { + if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) { + *p++ = 'r'; + if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) + *p++ = '+'; + } else { + *p++ = 'w'; + } + } + + if (p==mode) + return PJ_EINVAL; + + *p++ = 'b'; + *p++ = '\0'; + + *fd = fopen(pathname, mode); + if (*fd == NULL) + return PJ_RETURN_OS_ERROR(errno); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd) +{ + PJ_ASSERT_RETURN(fd, PJ_EINVAL); + if (fclose((FILE*)fd) != 0) + return PJ_RETURN_OS_ERROR(errno); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd, + const void *data, + pj_ssize_t *size) +{ + size_t written; + + clearerr((FILE*)fd); + written = fwrite(data, 1, *size, (FILE*)fd); + if (ferror((FILE*)fd)) { + *size = -1; + return PJ_RETURN_OS_ERROR(errno); + } + + *size = written; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd, + void *data, + pj_ssize_t *size) +{ + size_t bytes; + + clearerr((FILE*)fd); + bytes = fread(data, 1, *size, (FILE*)fd); + if (ferror((FILE*)fd)) { + *size = -1; + return PJ_RETURN_OS_ERROR(errno); + } + + *size = bytes; + return PJ_SUCCESS; +} + +PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access) +{ + PJ_UNUSED_ARG(access); + return feof((FILE*)fd) ? PJ_TRUE : 0; +} + +PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, + pj_off_t offset, + enum pj_file_seek_type whence) +{ + int mode; + + switch (whence) { + case PJ_SEEK_SET: + mode = SEEK_SET; break; + case PJ_SEEK_CUR: + mode = SEEK_CUR; break; + case PJ_SEEK_END: + mode = SEEK_END; break; + default: + pj_assert(!"Invalid whence in file_setpos"); + return PJ_EINVAL; + } + + if (fseek((FILE*)fd, (long)offset, mode) != 0) + return PJ_RETURN_OS_ERROR(errno); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd, + pj_off_t *pos) +{ + long offset; + + offset = ftell((FILE*)fd); + if (offset == -1) { + *pos = -1; + return PJ_RETURN_OS_ERROR(errno); + } + + *pos = offset; + return PJ_SUCCESS; +} + + diff --git a/pjlib/src/pj/file_io_win32.c b/pjlib/src/pj/file_io_win32.c index 2f6e3cba..f7a4f162 100644 --- a/pjlib/src/pj/file_io_win32.c +++ b/pjlib/src/pj/file_io_win32.c @@ -1,203 +1,203 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/file_io.h>
-#include <pj/errno.h>
-#include <pj/assert.h>
-
-#include <windows.h>
-
-#ifndef INVALID_SET_FILE_POINTER
-# define INVALID_SET_FILE_POINTER ((DWORD)-1)
-#endif
-
-/**
- * Check for end-of-file condition on the specified descriptor.
- *
- * @param fd The file descriptor.
- * @param access The desired access.
- *
- * @return Non-zero if file is EOF.
- */
-PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd,
- enum pj_file_access access);
-
-
-PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
- const char *pathname,
- unsigned flags,
- pj_oshandle_t *fd)
-{
- HANDLE hFile;
- DWORD dwDesiredAccess = 0;
- DWORD dwShareMode = 0;
- DWORD dwCreationDisposition = 0;
- DWORD dwFlagsAndAttributes = 0;
-
- PJ_UNUSED_ARG(pool);
-
- PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL);
-
- if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
- dwDesiredAccess |= GENERIC_WRITE;
- if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
- dwDesiredAccess |= FILE_APPEND_DATA;
- } else {
- dwDesiredAccess &= ~(FILE_APPEND_DATA);
- dwCreationDisposition |= CREATE_ALWAYS;
- }
- }
- if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
- dwDesiredAccess |= GENERIC_READ;
- if (flags == PJ_O_RDONLY)
- dwCreationDisposition |= OPEN_EXISTING;
- }
-
- if (dwDesiredAccess == 0) {
- pj_assert(!"Invalid file open flags");
- return PJ_EINVAL;
- }
-
- dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
- dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
-
- hFile = CreateFile(pathname, dwDesiredAccess, dwShareMode, NULL,
- dwCreationDisposition, dwFlagsAndAttributes, NULL);
- if (hFile == INVALID_HANDLE_VALUE) {
- *fd = 0;
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *fd = hFile;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
-{
- if (CloseHandle(fd)==0)
- return PJ_RETURN_OS_ERROR(GetLastError());
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
- const void *data,
- pj_ssize_t *size)
-{
- BOOL rc;
- DWORD bytesWritten;
-
- rc = WriteFile(fd, data, *size, &bytesWritten, NULL);
- if (!rc) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *size = bytesWritten;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
- void *data,
- pj_ssize_t *size)
-{
- BOOL rc;
- DWORD bytesRead;
-
- rc = ReadFile(fd, data, *size, &bytesRead, NULL);
- if (!rc) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *size = bytesRead;
- return PJ_SUCCESS;
-}
-
-/*
-PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
-{
- BOOL rc;
- DWORD dummy = 0, bytes;
- DWORD dwStatus;
-
- if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) {
- rc = ReadFile(fd, &dummy, 0, &bytes, NULL);
- } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) {
- rc = WriteFile(fd, &dummy, 0, &bytes, NULL);
- } else {
- pj_assert(!"Invalid access");
- return PJ_TRUE;
- }
-
- dwStatus = GetLastError();
- if (dwStatus==ERROR_HANDLE_EOF)
- return PJ_TRUE;
-
- return 0;
-}
-*/
-
-PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
- pj_off_t offset,
- enum pj_file_seek_type whence)
-{
- DWORD dwMoveMethod;
- DWORD dwNewPos;
- LONG hi32;
-
- if (whence == PJ_SEEK_SET)
- dwMoveMethod = FILE_BEGIN;
- else if (whence == PJ_SEEK_CUR)
- dwMoveMethod = FILE_CURRENT;
- else if (whence == PJ_SEEK_END)
- dwMoveMethod = FILE_END;
- else {
- pj_assert(!"Invalid whence in file_setpos");
- return PJ_EINVAL;
- }
-
- hi32 = (LONG)(offset >> 32);
- dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod);
- if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) {
- DWORD dwStatus = GetLastError();
- if (dwStatus != 0)
- return PJ_RETURN_OS_ERROR(dwStatus);
- /* dwNewPos actually is not an error. */
- }
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
- pj_off_t *pos)
-{
- LONG hi32 = 0;
- DWORD lo32;
-
- lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT);
- if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) {
- DWORD dwStatus = GetLastError();
- if (dwStatus != 0)
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
-
- *pos = hi32;
- *pos = (*pos << 32) + lo32;
- return PJ_SUCCESS;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/file_io.h> +#include <pj/errno.h> +#include <pj/assert.h> + +#include <windows.h> + +#ifndef INVALID_SET_FILE_POINTER +# define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +/** + * Check for end-of-file condition on the specified descriptor. + * + * @param fd The file descriptor. + * @param access The desired access. + * + * @return Non-zero if file is EOF. + */ +PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd, + enum pj_file_access access); + + +PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, + const char *pathname, + unsigned flags, + pj_oshandle_t *fd) +{ + HANDLE hFile; + DWORD dwDesiredAccess = 0; + DWORD dwShareMode = 0; + DWORD dwCreationDisposition = 0; + DWORD dwFlagsAndAttributes = 0; + + PJ_UNUSED_ARG(pool); + + PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL); + + if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) { + dwDesiredAccess |= GENERIC_WRITE; + if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { + dwDesiredAccess |= FILE_APPEND_DATA; + } else { + dwDesiredAccess &= ~(FILE_APPEND_DATA); + dwCreationDisposition |= CREATE_ALWAYS; + } + } + if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) { + dwDesiredAccess |= GENERIC_READ; + if (flags == PJ_O_RDONLY) + dwCreationDisposition |= OPEN_EXISTING; + } + + if (dwDesiredAccess == 0) { + pj_assert(!"Invalid file open flags"); + return PJ_EINVAL; + } + + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + + hFile = CreateFile(pathname, dwDesiredAccess, dwShareMode, NULL, + dwCreationDisposition, dwFlagsAndAttributes, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + *fd = 0; + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *fd = hFile; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd) +{ + if (CloseHandle(fd)==0) + return PJ_RETURN_OS_ERROR(GetLastError()); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd, + const void *data, + pj_ssize_t *size) +{ + BOOL rc; + DWORD bytesWritten; + + rc = WriteFile(fd, data, *size, &bytesWritten, NULL); + if (!rc) { + *size = -1; + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *size = bytesWritten; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd, + void *data, + pj_ssize_t *size) +{ + BOOL rc; + DWORD bytesRead; + + rc = ReadFile(fd, data, *size, &bytesRead, NULL); + if (!rc) { + *size = -1; + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *size = bytesRead; + return PJ_SUCCESS; +} + +/* +PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access) +{ + BOOL rc; + DWORD dummy = 0, bytes; + DWORD dwStatus; + + if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) { + rc = ReadFile(fd, &dummy, 0, &bytes, NULL); + } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) { + rc = WriteFile(fd, &dummy, 0, &bytes, NULL); + } else { + pj_assert(!"Invalid access"); + return PJ_TRUE; + } + + dwStatus = GetLastError(); + if (dwStatus==ERROR_HANDLE_EOF) + return PJ_TRUE; + + return 0; +} +*/ + +PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, + pj_off_t offset, + enum pj_file_seek_type whence) +{ + DWORD dwMoveMethod; + DWORD dwNewPos; + LONG hi32; + + if (whence == PJ_SEEK_SET) + dwMoveMethod = FILE_BEGIN; + else if (whence == PJ_SEEK_CUR) + dwMoveMethod = FILE_CURRENT; + else if (whence == PJ_SEEK_END) + dwMoveMethod = FILE_END; + else { + pj_assert(!"Invalid whence in file_setpos"); + return PJ_EINVAL; + } + + hi32 = (LONG)(offset >> 32); + dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod); + if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) { + DWORD dwStatus = GetLastError(); + if (dwStatus != 0) + return PJ_RETURN_OS_ERROR(dwStatus); + /* dwNewPos actually is not an error. */ + } + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd, + pj_off_t *pos) +{ + LONG hi32 = 0; + DWORD lo32; + + lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT); + if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) { + DWORD dwStatus = GetLastError(); + if (dwStatus != 0) + return PJ_RETURN_OS_ERROR(dwStatus); + } + + *pos = hi32; + *pos = (*pos << 32) + lo32; + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/guid.c b/pjlib/src/pj/guid.c index bd9343e0..c8048792 100644 --- a/pjlib/src/pj/guid.c +++ b/pjlib/src/pj/guid.c @@ -1,26 +1,26 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/guid.h>
-#include <pj/pool.h>
-
-PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str)
-{
- str->ptr = pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH);
- pj_generate_unique_string(str);
-}
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/guid.h> +#include <pj/pool.h> + +PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str) +{ + str->ptr = pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH); + pj_generate_unique_string(str); +} diff --git a/pjlib/src/pj/guid_simple.c b/pjlib/src/pj/guid_simple.c index d2b25569..300abace 100644 --- a/pjlib/src/pj/guid_simple.c +++ b/pjlib/src/pj/guid_simple.c @@ -1,67 +1,67 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/guid.h>
-#include <pj/os.h>
-#include <pj/rand.h>
-#include <pj/string.h>
-#include <pj/compat/sprintf.h>
-
-const unsigned PJ_GUID_STRING_LENGTH=20;
-
-static void init_mac_address(unsigned char mac_addr[16])
-{
- unsigned long *ulval1 = (unsigned long*) &mac_addr[0];
- unsigned short *usval1 = (unsigned short*) &mac_addr[4];
-
- *ulval1 = pj_rand();
- *usval1 = (unsigned short) pj_rand();
-}
-
-PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
-{
- static int guid_initialized;
- static unsigned pid;
- static char str_pid[5];
- static unsigned char mac_addr[6];
- static char str_mac_addr[16];
- static unsigned clock_seq;
-
- PJ_CHECK_STACK();
-
- if (guid_initialized == 0) {
- pid = pj_getpid();
- init_mac_address(mac_addr);
- clock_seq = 0;
-
- sprintf(str_pid, "%04x", pid);
- sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x",
- mac_addr[0], mac_addr[1], mac_addr[2],
- mac_addr[3], mac_addr[4], mac_addr[5]);
-
- guid_initialized = 1;
- }
-
- strcpy(str->ptr, str_pid);
- sprintf(str->ptr+4, "%04x", clock_seq++);
- pj_memcpy(str->ptr+8, str_mac_addr, 12);
- str->slen = 20;
-
- return str;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/guid.h> +#include <pj/os.h> +#include <pj/rand.h> +#include <pj/string.h> +#include <pj/compat/sprintf.h> + +const unsigned PJ_GUID_STRING_LENGTH=20; + +static void init_mac_address(unsigned char mac_addr[16]) +{ + unsigned long *ulval1 = (unsigned long*) &mac_addr[0]; + unsigned short *usval1 = (unsigned short*) &mac_addr[4]; + + *ulval1 = pj_rand(); + *usval1 = (unsigned short) pj_rand(); +} + +PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) +{ + static int guid_initialized; + static unsigned pid; + static char str_pid[5]; + static unsigned char mac_addr[6]; + static char str_mac_addr[16]; + static unsigned clock_seq; + + PJ_CHECK_STACK(); + + if (guid_initialized == 0) { + pid = pj_getpid(); + init_mac_address(mac_addr); + clock_seq = 0; + + sprintf(str_pid, "%04x", pid); + sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + guid_initialized = 1; + } + + strcpy(str->ptr, str_pid); + sprintf(str->ptr+4, "%04x", clock_seq++); + pj_memcpy(str->ptr+8, str_mac_addr, 12); + str->slen = 20; + + return str; +} + diff --git a/pjlib/src/pj/guid_win32.c b/pjlib/src/pj/guid_win32.c index d35fbc70..0ffb3559 100644 --- a/pjlib/src/pj/guid_win32.c +++ b/pjlib/src/pj/guid_win32.c @@ -1,68 +1,68 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/guid.h>
-#include <pj/string.h>
-#include <pj/sock.h>
-#include <windows.h>
-#include <objbase.h>
-#include <pj/os.h>
-
-
-const unsigned PJ_GUID_STRING_LENGTH=32;
-
-PJ_INLINE(void) hex2digit(unsigned value, char *p)
-{
- static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- *p++ = hex[ (value & 0xF0) >> 4 ];
- *p++ = hex[ (value & 0x0F) ];
-}
-
-static void guid_to_str( const GUID *guid, pj_str_t *str )
-{
- unsigned i;
- GUID guid_copy;
- const unsigned char *src = (const unsigned char*)&guid_copy;
- char *dst = str->ptr;
-
- pj_memcpy(&guid_copy, guid, sizeof(*guid));
- guid_copy.Data1 = pj_ntohl(guid_copy.Data1);
- guid_copy.Data2 = pj_ntohs(guid_copy.Data2);
- guid_copy.Data3 = pj_ntohs(guid_copy.Data3);
-
- for (i=0; i<16; ++i) {
- hex2digit( *src, dst );
- dst += 2;
- ++src;
- }
- str->slen = 32;
-}
-
-
-PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
-{
- GUID guid;
-
- PJ_CHECK_STACK();
-
- CoCreateGuid(&guid);
- guid_to_str( &guid, str );
- return str;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/guid.h> +#include <pj/string.h> +#include <pj/sock.h> +#include <windows.h> +#include <objbase.h> +#include <pj/os.h> + + +const unsigned PJ_GUID_STRING_LENGTH=32; + +PJ_INLINE(void) hex2digit(unsigned value, char *p) +{ + static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + *p++ = hex[ (value & 0xF0) >> 4 ]; + *p++ = hex[ (value & 0x0F) ]; +} + +static void guid_to_str( const GUID *guid, pj_str_t *str ) +{ + unsigned i; + GUID guid_copy; + const unsigned char *src = (const unsigned char*)&guid_copy; + char *dst = str->ptr; + + pj_memcpy(&guid_copy, guid, sizeof(*guid)); + guid_copy.Data1 = pj_ntohl(guid_copy.Data1); + guid_copy.Data2 = pj_ntohs(guid_copy.Data2); + guid_copy.Data3 = pj_ntohs(guid_copy.Data3); + + for (i=0; i<16; ++i) { + hex2digit( *src, dst ); + dst += 2; + ++src; + } + str->slen = 32; +} + + +PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) +{ + GUID guid; + + PJ_CHECK_STACK(); + + CoCreateGuid(&guid); + guid_to_str( &guid, str ); + return str; +} + diff --git a/pjlib/src/pj/hash.c b/pjlib/src/pj/hash.c index 61b3cd80..28d6d0ff 100644 --- a/pjlib/src/pj/hash.c +++ b/pjlib/src/pj/hash.c @@ -1,274 +1,274 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/hash.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/os.h>
-#include <pj/ctype.h>
-
-/**
- * The hash multiplier used to calculate hash value.
- */
-#define PJ_HASH_MULTIPLIER 33
-
-
-struct pj_hash_entry
-{
- struct pj_hash_entry *next;
- const void *key;
- pj_uint32_t hash;
- pj_uint32_t keylen;
- void *value;
-};
-
-
-struct pj_hash_table_t
-{
- pj_hash_entry **table;
- unsigned count, rows;
- pj_hash_iterator_t iterator;
-};
-
-
-
-PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen)
-{
- PJ_CHECK_STACK();
-
- if (keylen==PJ_HASH_KEY_STRING) {
- const unsigned char *p = key;
- for ( ; *p; ++p ) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- keylen = p - (const unsigned char*)key;
- } else {
- const unsigned char *p = key,
- *end = p + keylen;
- for ( ; p!=end; ++p) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- }
- return hash;
-}
-
-PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval,
- char *result,
- const pj_str_t *key)
-{
- long i;
-
- for (i=0; i<key->slen; ++i) {
- result[i] = (char)pj_tolower(key->ptr[i]);
- hval = hval * PJ_HASH_MULTIPLIER + result[i];
- }
-
- return hval;
-}
-
-
-PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
-{
- pj_hash_table_t *h;
- unsigned table_size;
-
- h = pj_pool_alloc(pool, sizeof(pj_hash_table_t));
- h->count = 0;
-
- PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
-
- /* size must be 2^n - 1.
- round-up the size to this rule, except when size is 2^n, then size
- will be round-down to 2^n-1.
- */
- table_size = 8;
- do {
- table_size <<= 1;
- } while (table_size <= size);
- table_size -= 1;
-
- h->rows = table_size;
- h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
- return h;
-}
-
-static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
- const void *key, unsigned keylen,
- void *val)
-{
- pj_uint32_t hash;
- pj_hash_entry **p_entry, *entry;
-
- hash=0;
- if (keylen==PJ_HASH_KEY_STRING) {
- const unsigned char *p = key;
- for ( ; *p; ++p ) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- keylen = p - (const unsigned char*)key;
- } else {
- const unsigned char *p = key,
- *end = p + keylen;
- for ( ; p!=end; ++p) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- }
-
- /* scan the linked list */
- for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry;
- entry;
- p_entry = &entry->next, entry = *p_entry)
- {
- if (entry->hash==hash && entry->keylen==keylen &&
- memcmp(entry->key, key, keylen)==0)
- {
- break;
- }
- }
-
- if (entry || val==NULL)
- return p_entry;
-
- /* create a new entry */
- entry = pj_pool_alloc(pool, sizeof(pj_hash_entry));
- PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry,
- pj_pool_get_used_size(pool), pj_pool_get_capacity(pool)));
- entry->next = NULL;
- entry->hash = hash;
- entry->key = key;
- entry->keylen = keylen;
- entry->value = val;
- *p_entry = entry;
-
- ++ht->count;
-
- return p_entry;
-}
-
-PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
- const void *key, unsigned keylen )
-{
- pj_hash_entry *entry;
- entry = *find_entry( NULL, ht, key, keylen, NULL);
- return entry ? entry->value : NULL;
-}
-
-PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
- const void *key, unsigned keylen,
- void *value )
-{
- pj_hash_entry **p_entry;
-
- p_entry = find_entry( pool, ht, key, keylen, value );
- if (*p_entry) {
- if (value == NULL) {
- /* delete entry */
- PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
- *p_entry = (*p_entry)->next;
- --ht->count;
-
- } else {
- /* overwrite */
- (*p_entry)->value = value;
- PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value));
- }
- }
-}
-
-PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
-{
- return ht->count;
-}
-
-PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
- pj_hash_iterator_t *it )
-{
- it->index = 0;
- it->entry = NULL;
-
- for (; it->index < ht->rows; ++it->index) {
- it->entry = ht->table[it->index];
- if (it->entry) {
- break;
- }
- }
-
- return it->entry ? it : NULL;
-}
-
-PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
- pj_hash_iterator_t *it )
-{
- it->entry = it->entry->next;
- if (it->entry) {
- return it;
- }
-
- for (++it->index; it->index < ht->rows; ++it->index) {
- it->entry = ht->table[it->index];
- if (it->entry) {
- break;
- }
- }
-
- return it->entry ? it : NULL;
-}
-
-PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(ht);
- return it->entry->value;
-}
-
-#if 0
-void pj_hash_dump_collision( pj_hash_table_t *ht )
-{
- unsigned min=0xFFFFFFFF, max=0;
- unsigned i;
- char line[120];
- int len, totlen = 0;
-
- for (i=0; i<ht->rows; ++i) {
- unsigned count = 0;
- pj_hash_entry *entry = ht->table[i];
- while (entry) {
- ++count;
- entry = entry->next;
- }
- if (count < min)
- min = count;
- if (count > max)
- max = count;
- len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
- if (len < 1)
- break;
- totlen += len;
-
- if ((i+1) % 10 == 0) {
- line[totlen] = '\0';
- PJ_LOG(4,(__FILE__, line));
- }
- }
-
- PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
-}
-#endif
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/hash.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/pool.h> +#include <pj/os.h> +#include <pj/ctype.h> + +/** + * The hash multiplier used to calculate hash value. + */ +#define PJ_HASH_MULTIPLIER 33 + + +struct pj_hash_entry +{ + struct pj_hash_entry *next; + const void *key; + pj_uint32_t hash; + pj_uint32_t keylen; + void *value; +}; + + +struct pj_hash_table_t +{ + pj_hash_entry **table; + unsigned count, rows; + pj_hash_iterator_t iterator; +}; + + + +PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen) +{ + PJ_CHECK_STACK(); + + if (keylen==PJ_HASH_KEY_STRING) { + const unsigned char *p = key; + for ( ; *p; ++p ) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + keylen = p - (const unsigned char*)key; + } else { + const unsigned char *p = key, + *end = p + keylen; + for ( ; p!=end; ++p) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + } + return hash; +} + +PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval, + char *result, + const pj_str_t *key) +{ + long i; + + for (i=0; i<key->slen; ++i) { + result[i] = (char)pj_tolower(key->ptr[i]); + hval = hval * PJ_HASH_MULTIPLIER + result[i]; + } + + return hval; +} + + +PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size) +{ + pj_hash_table_t *h; + unsigned table_size; + + h = pj_pool_alloc(pool, sizeof(pj_hash_table_t)); + h->count = 0; + + PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool))); + + /* size must be 2^n - 1. + round-up the size to this rule, except when size is 2^n, then size + will be round-down to 2^n-1. + */ + table_size = 8; + do { + table_size <<= 1; + } while (table_size <= size); + table_size -= 1; + + h->rows = table_size; + h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*)); + return h; +} + +static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, + void *val) +{ + pj_uint32_t hash; + pj_hash_entry **p_entry, *entry; + + hash=0; + if (keylen==PJ_HASH_KEY_STRING) { + const unsigned char *p = key; + for ( ; *p; ++p ) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + keylen = p - (const unsigned char*)key; + } else { + const unsigned char *p = key, + *end = p + keylen; + for ( ; p!=end; ++p) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + } + + /* scan the linked list */ + for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry; + entry; + p_entry = &entry->next, entry = *p_entry) + { + if (entry->hash==hash && entry->keylen==keylen && + memcmp(entry->key, key, keylen)==0) + { + break; + } + } + + if (entry || val==NULL) + return p_entry; + + /* create a new entry */ + entry = pj_pool_alloc(pool, sizeof(pj_hash_entry)); + PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry, + pj_pool_get_used_size(pool), pj_pool_get_capacity(pool))); + entry->next = NULL; + entry->hash = hash; + entry->key = key; + entry->keylen = keylen; + entry->value = val; + *p_entry = entry; + + ++ht->count; + + return p_entry; +} + +PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht, + const void *key, unsigned keylen ) +{ + pj_hash_entry *entry; + entry = *find_entry( NULL, ht, key, keylen, NULL); + return entry ? entry->value : NULL; +} + +PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, + void *value ) +{ + pj_hash_entry **p_entry; + + p_entry = find_entry( pool, ht, key, keylen, value ); + if (*p_entry) { + if (value == NULL) { + /* delete entry */ + PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry)); + *p_entry = (*p_entry)->next; + --ht->count; + + } else { + /* overwrite */ + (*p_entry)->value = value; + PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value)); + } + } +} + +PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht ) +{ + return ht->count; +} + +PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, + pj_hash_iterator_t *it ) +{ + it->index = 0; + it->entry = NULL; + + for (; it->index < ht->rows; ++it->index) { + it->entry = ht->table[it->index]; + if (it->entry) { + break; + } + } + + return it->entry ? it : NULL; +} + +PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, + pj_hash_iterator_t *it ) +{ + it->entry = it->entry->next; + if (it->entry) { + return it; + } + + for (++it->index; it->index < ht->rows; ++it->index) { + it->entry = ht->table[it->index]; + if (it->entry) { + break; + } + } + + return it->entry ? it : NULL; +} + +PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ht); + return it->entry->value; +} + +#if 0 +void pj_hash_dump_collision( pj_hash_table_t *ht ) +{ + unsigned min=0xFFFFFFFF, max=0; + unsigned i; + char line[120]; + int len, totlen = 0; + + for (i=0; i<ht->rows; ++i) { + unsigned count = 0; + pj_hash_entry *entry = ht->table[i]; + while (entry) { + ++count; + entry = entry->next; + } + if (count < min) + min = count; + if (count > max) + max = count; + len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count); + if (len < 1) + break; + totlen += len; + + if ((i+1) % 10 == 0) { + line[totlen] = '\0'; + PJ_LOG(4,(__FILE__, line)); + } + } + + PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max)); +} +#endif + + diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c index b383a31f..285aefc9 100644 --- a/pjlib/src/pj/ioqueue_common_abs.c +++ b/pjlib/src/pj/ioqueue_common_abs.c @@ -1,955 +1,955 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/*
- * ioqueue_common_abs.c
- *
- * This contains common functionalities to emulate proactor pattern with
- * various event dispatching mechanisms (e.g. select, epoll).
- *
- * This file will be included by the appropriate ioqueue implementation.
- * This file is NOT supposed to be compiled as stand-alone source.
- */
-
-static void ioqueue_init( pj_ioqueue_t *ioqueue )
-{
- ioqueue->lock = NULL;
- ioqueue->auto_delete_lock = 0;
-}
-
-static pj_status_t ioqueue_destroy(pj_ioqueue_t *ioqueue)
-{
- if (ioqueue->auto_delete_lock && ioqueue->lock ) {
- pj_lock_release(ioqueue->lock);
- return pj_lock_destroy(ioqueue->lock);
- } else
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_set_lock()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue,
- pj_lock_t *lock,
- pj_bool_t auto_delete )
-{
- PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
-
- if (ioqueue->auto_delete_lock && ioqueue->lock) {
- pj_lock_destroy(ioqueue->lock);
- }
-
- ioqueue->lock = lock;
- ioqueue->auto_delete_lock = auto_delete;
-
- return PJ_SUCCESS;
-}
-
-static pj_status_t ioqueue_init_key( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb)
-{
- pj_status_t rc;
- int optlen;
-
- key->ioqueue = ioqueue;
- key->fd = sock;
- key->user_data = user_data;
- pj_list_init(&key->read_list);
- pj_list_init(&key->write_list);
-#if PJ_HAS_TCP
- pj_list_init(&key->accept_list);
-#endif
-
- /* Save callback. */
- pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback));
-
- /* Get socket type. When socket type is datagram, some optimization
- * will be performed during send to allow parallel send operations.
- */
- optlen = sizeof(key->fd_type);
- rc = pj_sock_getsockopt(sock, PJ_SOL_SOCKET, PJ_SO_TYPE,
- &key->fd_type, &optlen);
- if (rc != PJ_SUCCESS)
- key->fd_type = PJ_SOCK_STREAM;
-
- /* Create mutex for the key. */
- rc = pj_mutex_create_simple(pool, NULL, &key->mutex);
-
- return rc;
-}
-
-static void ioqueue_destroy_key( pj_ioqueue_key_t *key )
-{
- pj_mutex_destroy(key->mutex);
-}
-
-/*
- * pj_ioqueue_get_user_data()
- *
- * Obtain value associated with a key.
- */
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- PJ_ASSERT_RETURN(key != NULL, NULL);
- return key->user_data;
-}
-
-/*
- * pj_ioqueue_set_user_data()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
- void *user_data,
- void **old_data)
-{
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
- if (old_data)
- *old_data = key->user_data;
- key->user_data = user_data;
-
- return PJ_SUCCESS;
-}
-
-PJ_INLINE(int) key_has_pending_write(pj_ioqueue_key_t *key)
-{
- return !pj_list_empty(&key->write_list);
-}
-
-PJ_INLINE(int) key_has_pending_read(pj_ioqueue_key_t *key)
-{
- return !pj_list_empty(&key->read_list);
-}
-
-PJ_INLINE(int) key_has_pending_accept(pj_ioqueue_key_t *key)
-{
-#if PJ_HAS_TCP
- return !pj_list_empty(&key->accept_list);
-#else
- return 0;
-#endif
-}
-
-PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key)
-{
- return key->connecting;
-}
-
-
-/*
- * ioqueue_dispatch_event()
- *
- * Report occurence of an event in the key to be processed by the
- * framework.
- */
-void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
-{
- /* Lock the key. */
- pj_mutex_lock(h->mutex);
-
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
- if (h->connecting) {
- /* Completion of connect() operation */
- pj_ssize_t bytes_transfered;
-
- /* Clear operation. */
- h->connecting = 0;
-
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
- ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT);
-
- /* Unlock; from this point we don't need to hold key's mutex. */
- pj_mutex_unlock(h->mutex);
-
-#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0)
- /* from connect(2):
- * On Linux, use getsockopt to read the SO_ERROR option at
- * level SOL_SOCKET to determine whether connect() completed
- * successfully (if SO_ERROR is zero).
- */
- {
- int value;
- socklen_t vallen = sizeof(value);
- int gs_rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR,
- &value, &vallen);
- if (gs_rc != 0) {
- /* Argh!! What to do now???
- * Just indicate that the socket is connected. The
- * application will get error as soon as it tries to use
- * the socket to send/receive.
- */
- bytes_transfered = 0;
- } else {
- bytes_transfered = value;
- }
- }
-#elif defined(PJ_WIN32) && PJ_WIN32!=0
- bytes_transfered = 0; /* success */
-#else
- /* Excellent information in D.J. Bernstein page:
- * http://cr.yp.to/docs/connect.html
- *
- * Seems like the most portable way of detecting connect()
- * failure is to call getpeername(). If socket is connected,
- * getpeername() will return 0. If the socket is not connected,
- * it will return ENOTCONN, and read(fd, &ch, 1) will produce
- * the right errno through error slippage. This is a combination
- * of suggestions from Douglas C. Schmidt and Ken Keys.
- */
- int gp_rc;
- struct sockaddr_in addr;
- socklen_t addrlen = sizeof(addr);
-
- gp_rc = getpeername(h->fd, (struct sockaddr*)&addr, &addrlen);
- bytes_transfered = gp_rc;
-#endif
-
- /* Call callback. */
- if (h->cb.on_connect_complete)
- (*h->cb.on_connect_complete)(h, bytes_transfered);
-
- /* Done. */
-
- } else
-#endif /* PJ_HAS_TCP */
- if (key_has_pending_write(h)) {
- /* Socket is writable. */
- struct write_operation *write_op;
- pj_ssize_t sent;
- pj_status_t send_rc;
-
- /* Get the first in the queue. */
- write_op = h->write_list.next;
-
- /* For datagrams, we can remove the write_op from the list
- * so that send() can work in parallel.
- */
- if (h->fd_type == PJ_SOCK_DGRAM) {
- pj_list_erase(write_op);
- write_op->op = 0;
-
- if (pj_list_empty(&h->write_list))
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
-
- pj_mutex_unlock(h->mutex);
- }
-
- /* Send the data.
- * Unfortunately we must do this while holding key's mutex, thus
- * preventing parallel write on a single key.. :-((
- */
- sent = write_op->size - write_op->written;
- if (write_op->op == PJ_IOQUEUE_OP_SEND) {
- send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written,
- &sent, write_op->flags);
- } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) {
- send_rc = pj_sock_sendto(h->fd,
- write_op->buf+write_op->written,
- &sent, write_op->flags,
- &write_op->rmt_addr,
- write_op->rmt_addrlen);
- } else {
- pj_assert(!"Invalid operation type!");
- send_rc = PJ_EBUG;
- }
-
- if (send_rc == PJ_SUCCESS) {
- write_op->written += sent;
- } else {
- pj_assert(send_rc > 0);
- write_op->written = -send_rc;
- }
-
- /* Are we finished with this buffer? */
- if (send_rc!=PJ_SUCCESS ||
- write_op->written == (pj_ssize_t)write_op->size ||
- h->fd_type == PJ_SOCK_DGRAM)
- {
- if (h->fd_type != PJ_SOCK_DGRAM) {
- /* Write completion of the whole stream. */
- pj_list_erase(write_op);
- write_op->op = 0;
-
- /* Clear operation if there's no more data to send. */
- if (pj_list_empty(&h->write_list))
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
-
- /* No need to hold mutex anymore */
- pj_mutex_unlock(h->mutex);
- }
-
- /* Call callback. */
- if (h->cb.on_write_complete) {
- (*h->cb.on_write_complete)(h,
- (pj_ioqueue_op_key_t*)write_op,
- write_op->written);
- }
-
- } else {
- pj_mutex_unlock(h->mutex);
- }
-
- /* Done. */
- } else {
- /*
- * This is normal; execution may fall here when multiple threads
- * are signalled for the same event, but only one thread eventually
- * able to process the event.
- */
- pj_mutex_unlock(h->mutex);
- }
-}
-
-void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
-{
- pj_status_t rc;
-
- /* Lock the key. */
- pj_mutex_lock(h->mutex);
-
-# if PJ_HAS_TCP
- if (!pj_list_empty(&h->accept_list)) {
-
- struct accept_operation *accept_op;
-
- /* Get one accept operation from the list. */
- accept_op = h->accept_list.next;
- pj_list_erase(accept_op);
- accept_op->op = 0;
-
- /* Clear bit in fdset if there is no more pending accept */
- if (pj_list_empty(&h->accept_list))
- ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT);
-
- /* Unlock; from this point we don't need to hold key's mutex. */
- pj_mutex_unlock(h->mutex);
-
- rc=pj_sock_accept(h->fd, accept_op->accept_fd,
- accept_op->rmt_addr, accept_op->addrlen);
- if (rc==PJ_SUCCESS && accept_op->local_addr) {
- rc = pj_sock_getsockname(*accept_op->accept_fd,
- accept_op->local_addr,
- accept_op->addrlen);
- }
-
- /* Call callback. */
- if (h->cb.on_accept_complete) {
- (*h->cb.on_accept_complete)(h,
- (pj_ioqueue_op_key_t*)accept_op,
- *accept_op->accept_fd, rc);
- }
-
- }
- else
-# endif
- if (key_has_pending_read(h)) {
- struct read_operation *read_op;
- pj_ssize_t bytes_read;
-
- /* Get one pending read operation from the list. */
- read_op = h->read_list.next;
- pj_list_erase(read_op);
- read_op->op = 0;
-
- /* Clear fdset if there is no pending read. */
- if (pj_list_empty(&h->read_list))
- ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT);
-
- /* Unlock; from this point we don't need to hold key's mutex. */
- pj_mutex_unlock(h->mutex);
-
- bytes_read = read_op->size;
-
- if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) {
- rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, 0,
- read_op->rmt_addr,
- read_op->rmt_addrlen);
- } else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) {
- rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0);
- } else {
- pj_assert(read_op->op == PJ_IOQUEUE_OP_READ);
- /*
- * User has specified pj_ioqueue_read().
- * On Win32, we should do ReadFile(). But because we got
- * here because of select() anyway, user must have put a
- * socket descriptor on h->fd, which in this case we can
- * just call pj_sock_recv() instead of ReadFile().
- * On Unix, user may put a file in h->fd, so we'll have
- * to call read() here.
- * This may not compile on systems which doesn't have
- * read(). That's why we only specify PJ_LINUX here so
- * that error is easier to catch.
- */
-# if defined(PJ_WIN32) && PJ_WIN32 != 0
- rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0);
- //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size,
- // &bytes_read, NULL);
-# elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0)
- bytes_read = read(h->fd, read_op->buf, bytes_read);
- rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error();
-# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
- bytes_read = sys_read(h->fd, read_op->buf, bytes_read);
- rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read;
-# else
-# error "Implement read() for this platform!"
-# endif
- }
-
- if (rc != PJ_SUCCESS) {
-# if defined(PJ_WIN32) && PJ_WIN32 != 0
- /* On Win32, for UDP, WSAECONNRESET on the receive side
- * indicates that previous sending has triggered ICMP Port
- * Unreachable message.
- * But we wouldn't know at this point which one of previous
- * key that has triggered the error, since UDP socket can
- * be shared!
- * So we'll just ignore it!
- */
-
- if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) {
- //PJ_LOG(4,(THIS_FILE,
- // "Ignored ICMP port unreach. on key=%p", h));
- }
-# endif
-
- /* In any case we would report this to caller. */
- bytes_read = -rc;
- }
-
- /* Call callback. */
- if (h->cb.on_read_complete) {
- (*h->cb.on_read_complete)(h,
- (pj_ioqueue_op_key_t*)read_op,
- bytes_read);
- }
-
- } else {
- /*
- * This is normal; execution may fall here when multiple threads
- * are signalled for the same event, but only one thread eventually
- * able to process the event.
- */
- pj_mutex_unlock(h->mutex);
- }
-}
-
-
-void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *h )
-{
- pj_mutex_lock(h->mutex);
-
- if (!h->connecting) {
- /* It is possible that more than one thread was woken up, thus
- * the remaining thread will see h->connecting as zero because
- * it has been processed by other thread.
- */
- pj_mutex_unlock(h->mutex);
- return;
- }
-
- /* Clear operation. */
- h->connecting = 0;
-
- pj_mutex_unlock(h->mutex);
-
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
- ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT);
-
- /* Call callback. */
- if (h->cb.on_connect_complete)
- (*h->cb.on_connect_complete)(h, -1);
-}
-
-/*
- * pj_ioqueue_recv()
- *
- * Start asynchronous recv() from the socket.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags )
-{
- struct read_operation *read_op;
-
- PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
- PJ_CHECK_STACK();
-
- read_op = (struct read_operation*)op_key;
- read_op->op = 0;
-
- /* Try to see if there's data immediately available.
- */
- if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
- pj_status_t status;
- pj_ssize_t size;
-
- size = *length;
- status = pj_sock_recv(key->fd, buffer, &size, flags);
- if (status == PJ_SUCCESS) {
- /* Yes! Data is available! */
- *length = size;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
- return status;
- }
- }
-
- flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
-
- /*
- * No data is immediately available.
- * Must schedule asynchronous operation to the ioqueue.
- */
- read_op->op = PJ_IOQUEUE_OP_RECV;
- read_op->buf = buffer;
- read_op->size = *length;
- read_op->flags = flags;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->read_list, read_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_recvfrom()
- *
- * Start asynchronous recvfrom() from the socket.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- struct read_operation *read_op;
-
- PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
- PJ_CHECK_STACK();
-
- read_op = (struct read_operation*)op_key;
- read_op->op = 0;
-
- /* Try to see if there's data immediately available.
- */
- if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
- pj_status_t status;
- pj_ssize_t size;
-
- size = *length;
- status = pj_sock_recvfrom(key->fd, buffer, &size, flags,
- addr, addrlen);
- if (status == PJ_SUCCESS) {
- /* Yes! Data is available! */
- *length = size;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
- return status;
- }
- }
-
- flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
-
- /*
- * No data is immediately available.
- * Must schedule asynchronous operation to the ioqueue.
- */
- read_op->op = PJ_IOQUEUE_OP_RECV_FROM;
- read_op->buf = buffer;
- read_op->size = *length;
- read_op->flags = flags;
- read_op->rmt_addr = addr;
- read_op->rmt_addrlen = addrlen;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->read_list, read_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_send()
- *
- * Start asynchronous send() to the descriptor.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- unsigned flags)
-{
- struct write_operation *write_op;
- pj_status_t status;
- pj_ssize_t sent;
-
- PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
- PJ_CHECK_STACK();
-
- write_op = (struct write_operation*)op_key;
- write_op->op = 0;
-
- /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write. */
- flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
-
- /* Fast track:
- * Try to send data immediately, only if there's no pending write!
- * Note:
- * We are speculating that the list is empty here without properly
- * acquiring ioqueue's mutex first. This is intentional, to maximize
- * performance via parallelism.
- *
- * This should be safe, because:
- * - by convention, we require caller to make sure that the
- * key is not unregistered while other threads are invoking
- * an operation on the same key.
- * - pj_list_empty() is safe to be invoked by multiple threads,
- * even when other threads are modifying the list.
- */
- if (pj_list_empty(&key->write_list)) {
- /*
- * See if data can be sent immediately.
- */
- sent = *length;
- status = pj_sock_send(key->fd, data, &sent, flags);
- if (status == PJ_SUCCESS) {
- /* Success! */
- *length = sent;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
- return status;
- }
- }
- }
-
- /*
- * Schedule asynchronous send.
- */
- write_op->op = PJ_IOQUEUE_OP_SEND;
- write_op->buf = (void*)data;
- write_op->size = *length;
- write_op->written = 0;
- write_op->flags = flags;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->write_list, write_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-
-/*
- * pj_ioqueue_sendto()
- *
- * Start asynchronous write() to the descriptor.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- pj_uint32_t flags,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- struct write_operation *write_op;
- pj_status_t status;
- pj_ssize_t sent;
-
- PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
- PJ_CHECK_STACK();
-
- write_op = (struct write_operation*)op_key;
- write_op->op = 0;
-
- /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write */
- flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
-
- /* Fast track:
- * Try to send data immediately, only if there's no pending write!
- * Note:
- * We are speculating that the list is empty here without properly
- * acquiring ioqueue's mutex first. This is intentional, to maximize
- * performance via parallelism.
- *
- * This should be safe, because:
- * - by convention, we require caller to make sure that the
- * key is not unregistered while other threads are invoking
- * an operation on the same key.
- * - pj_list_empty() is safe to be invoked by multiple threads,
- * even when other threads are modifying the list.
- */
- if (pj_list_empty(&key->write_list)) {
- /*
- * See if data can be sent immediately.
- */
- sent = *length;
- status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen);
- if (status == PJ_SUCCESS) {
- /* Success! */
- *length = sent;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
- return status;
- }
- }
- }
-
- /*
- * Check that address storage can hold the address parameter.
- */
- PJ_ASSERT_RETURN(addrlen <= sizeof(pj_sockaddr_in), PJ_EBUG);
-
- /*
- * Schedule asynchronous send.
- */
- write_op->op = PJ_IOQUEUE_OP_SEND_TO;
- write_op->buf = (void*)data;
- write_op->size = *length;
- write_op->written = 0;
- write_op->flags = flags;
- pj_memcpy(&write_op->rmt_addr, addr, addrlen);
- write_op->rmt_addrlen = addrlen;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->write_list, write_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-#if PJ_HAS_TCP
-/*
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- struct accept_operation *accept_op;
- pj_status_t status;
-
- /* check parameters. All must be specified! */
- PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
-
- accept_op = (struct accept_operation*)op_key;
- accept_op->op = 0;
-
- /* Fast track:
- * See if there's new connection available immediately.
- */
- if (pj_list_empty(&key->accept_list)) {
- status = pj_sock_accept(key->fd, new_sock, remote, addrlen);
- if (status == PJ_SUCCESS) {
- /* Yes! New connection is available! */
- if (local && addrlen) {
- status = pj_sock_getsockname(*new_sock, local, addrlen);
- if (status != PJ_SUCCESS) {
- pj_sock_close(*new_sock);
- *new_sock = PJ_INVALID_SOCKET;
- return status;
- }
- }
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
- return status;
- }
- }
- }
-
- /*
- * No connection is available immediately.
- * Schedule accept() operation to be completed when there is incoming
- * connection available.
- */
- accept_op->op = PJ_IOQUEUE_OP_ACCEPT;
- accept_op->accept_fd = new_sock;
- accept_op->rmt_addr = remote;
- accept_op->addrlen= addrlen;
- accept_op->local_addr = local;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->accept_list, accept_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-/*
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- pj_status_t status;
-
- /* check parameters. All must be specified! */
- PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
-
- /* Check if socket has not been marked for connecting */
- if (key->connecting != 0)
- return PJ_EPENDING;
-
- status = pj_sock_connect(key->fd, addr, addrlen);
- if (status == PJ_SUCCESS) {
- /* Connected! */
- return PJ_SUCCESS;
- } else {
- if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) {
- /* Pending! */
- pj_mutex_lock(key->mutex);
- key->connecting = PJ_TRUE;
- ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
- ioqueue_add_to_set(key->ioqueue, key->fd, EXCEPTION_EVENT);
- pj_mutex_unlock(key->mutex);
- return PJ_EPENDING;
- } else {
- /* Error! */
- return status;
- }
- }
-}
-#endif /* PJ_HAS_TCP */
-
-
-PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
- pj_size_t size )
-{
- pj_memset(op_key, 0, size);
-}
-
-
-/*
- * pj_ioqueue_is_pending()
- */
-PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key )
-{
- struct generic_operation *op_rec;
-
- PJ_UNUSED_ARG(key);
-
- op_rec = (struct generic_operation*)op_key;
- return op_rec->op != 0;
-}
-
-
-/*
- * pj_ioqueue_post_completion()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_status )
-{
- struct generic_operation *op_rec;
-
- /*
- * Find the operation key in all pending operation list to
- * really make sure that it's still there; then call the callback.
- */
- pj_mutex_lock(key->mutex);
-
- /* Find the operation in the pending read list. */
- op_rec = (struct generic_operation*)key->read_list.next;
- while (op_rec != (void*)&key->read_list) {
- if (op_rec == (void*)op_key) {
- pj_list_erase(op_rec);
- op_rec->op = 0;
- pj_mutex_unlock(key->mutex);
-
- (*key->cb.on_read_complete)(key, op_key, bytes_status);
- return PJ_SUCCESS;
- }
- op_rec = op_rec->next;
- }
-
- /* Find the operation in the pending write list. */
- op_rec = (struct generic_operation*)key->write_list.next;
- while (op_rec != (void*)&key->write_list) {
- if (op_rec == (void*)op_key) {
- pj_list_erase(op_rec);
- op_rec->op = 0;
- pj_mutex_unlock(key->mutex);
-
- (*key->cb.on_write_complete)(key, op_key, bytes_status);
- return PJ_SUCCESS;
- }
- op_rec = op_rec->next;
- }
-
- /* Find the operation in the pending accept list. */
- op_rec = (struct generic_operation*)key->accept_list.next;
- while (op_rec != (void*)&key->accept_list) {
- if (op_rec == (void*)op_key) {
- pj_list_erase(op_rec);
- op_rec->op = 0;
- pj_mutex_unlock(key->mutex);
-
- (*key->cb.on_accept_complete)(key, op_key,
- PJ_INVALID_SOCKET,
- bytes_status);
- return PJ_SUCCESS;
- }
- op_rec = op_rec->next;
- }
-
- pj_mutex_unlock(key->mutex);
-
- return PJ_EINVALIDOP;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * ioqueue_common_abs.c + * + * This contains common functionalities to emulate proactor pattern with + * various event dispatching mechanisms (e.g. select, epoll). + * + * This file will be included by the appropriate ioqueue implementation. + * This file is NOT supposed to be compiled as stand-alone source. + */ + +static void ioqueue_init( pj_ioqueue_t *ioqueue ) +{ + ioqueue->lock = NULL; + ioqueue->auto_delete_lock = 0; +} + +static pj_status_t ioqueue_destroy(pj_ioqueue_t *ioqueue) +{ + if (ioqueue->auto_delete_lock && ioqueue->lock ) { + pj_lock_release(ioqueue->lock); + return pj_lock_destroy(ioqueue->lock); + } else + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_set_lock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL); + + if (ioqueue->auto_delete_lock && ioqueue->lock) { + pj_lock_destroy(ioqueue->lock); + } + + ioqueue->lock = lock; + ioqueue->auto_delete_lock = auto_delete; + + return PJ_SUCCESS; +} + +static pj_status_t ioqueue_init_key( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb) +{ + pj_status_t rc; + int optlen; + + key->ioqueue = ioqueue; + key->fd = sock; + key->user_data = user_data; + pj_list_init(&key->read_list); + pj_list_init(&key->write_list); +#if PJ_HAS_TCP + pj_list_init(&key->accept_list); +#endif + + /* Save callback. */ + pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); + + /* Get socket type. When socket type is datagram, some optimization + * will be performed during send to allow parallel send operations. + */ + optlen = sizeof(key->fd_type); + rc = pj_sock_getsockopt(sock, PJ_SOL_SOCKET, PJ_SO_TYPE, + &key->fd_type, &optlen); + if (rc != PJ_SUCCESS) + key->fd_type = PJ_SOCK_STREAM; + + /* Create mutex for the key. */ + rc = pj_mutex_create_simple(pool, NULL, &key->mutex); + + return rc; +} + +static void ioqueue_destroy_key( pj_ioqueue_key_t *key ) +{ + pj_mutex_destroy(key->mutex); +} + +/* + * pj_ioqueue_get_user_data() + * + * Obtain value associated with a key. + */ +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key != NULL, NULL); + return key->user_data; +} + +/* + * pj_ioqueue_set_user_data() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, + void *user_data, + void **old_data) +{ + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + if (old_data) + *old_data = key->user_data; + key->user_data = user_data; + + return PJ_SUCCESS; +} + +PJ_INLINE(int) key_has_pending_write(pj_ioqueue_key_t *key) +{ + return !pj_list_empty(&key->write_list); +} + +PJ_INLINE(int) key_has_pending_read(pj_ioqueue_key_t *key) +{ + return !pj_list_empty(&key->read_list); +} + +PJ_INLINE(int) key_has_pending_accept(pj_ioqueue_key_t *key) +{ +#if PJ_HAS_TCP + return !pj_list_empty(&key->accept_list); +#else + return 0; +#endif +} + +PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key) +{ + return key->connecting; +} + + +/* + * ioqueue_dispatch_event() + * + * Report occurence of an event in the key to be processed by the + * framework. + */ +void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h) +{ + /* Lock the key. */ + pj_mutex_lock(h->mutex); + +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 + if (h->connecting) { + /* Completion of connect() operation */ + pj_ssize_t bytes_transfered; + + /* Clear operation. */ + h->connecting = 0; + + ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT); + ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT); + + /* Unlock; from this point we don't need to hold key's mutex. */ + pj_mutex_unlock(h->mutex); + +#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0) + /* from connect(2): + * On Linux, use getsockopt to read the SO_ERROR option at + * level SOL_SOCKET to determine whether connect() completed + * successfully (if SO_ERROR is zero). + */ + { + int value; + socklen_t vallen = sizeof(value); + int gs_rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, + &value, &vallen); + if (gs_rc != 0) { + /* Argh!! What to do now??? + * Just indicate that the socket is connected. The + * application will get error as soon as it tries to use + * the socket to send/receive. + */ + bytes_transfered = 0; + } else { + bytes_transfered = value; + } + } +#elif defined(PJ_WIN32) && PJ_WIN32!=0 + bytes_transfered = 0; /* success */ +#else + /* Excellent information in D.J. Bernstein page: + * http://cr.yp.to/docs/connect.html + * + * Seems like the most portable way of detecting connect() + * failure is to call getpeername(). If socket is connected, + * getpeername() will return 0. If the socket is not connected, + * it will return ENOTCONN, and read(fd, &ch, 1) will produce + * the right errno through error slippage. This is a combination + * of suggestions from Douglas C. Schmidt and Ken Keys. + */ + int gp_rc; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + gp_rc = getpeername(h->fd, (struct sockaddr*)&addr, &addrlen); + bytes_transfered = gp_rc; +#endif + + /* Call callback. */ + if (h->cb.on_connect_complete) + (*h->cb.on_connect_complete)(h, bytes_transfered); + + /* Done. */ + + } else +#endif /* PJ_HAS_TCP */ + if (key_has_pending_write(h)) { + /* Socket is writable. */ + struct write_operation *write_op; + pj_ssize_t sent; + pj_status_t send_rc; + + /* Get the first in the queue. */ + write_op = h->write_list.next; + + /* For datagrams, we can remove the write_op from the list + * so that send() can work in parallel. + */ + if (h->fd_type == PJ_SOCK_DGRAM) { + pj_list_erase(write_op); + write_op->op = 0; + + if (pj_list_empty(&h->write_list)) + ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT); + + pj_mutex_unlock(h->mutex); + } + + /* Send the data. + * Unfortunately we must do this while holding key's mutex, thus + * preventing parallel write on a single key.. :-(( + */ + sent = write_op->size - write_op->written; + if (write_op->op == PJ_IOQUEUE_OP_SEND) { + send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written, + &sent, write_op->flags); + } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) { + send_rc = pj_sock_sendto(h->fd, + write_op->buf+write_op->written, + &sent, write_op->flags, + &write_op->rmt_addr, + write_op->rmt_addrlen); + } else { + pj_assert(!"Invalid operation type!"); + send_rc = PJ_EBUG; + } + + if (send_rc == PJ_SUCCESS) { + write_op->written += sent; + } else { + pj_assert(send_rc > 0); + write_op->written = -send_rc; + } + + /* Are we finished with this buffer? */ + if (send_rc!=PJ_SUCCESS || + write_op->written == (pj_ssize_t)write_op->size || + h->fd_type == PJ_SOCK_DGRAM) + { + if (h->fd_type != PJ_SOCK_DGRAM) { + /* Write completion of the whole stream. */ + pj_list_erase(write_op); + write_op->op = 0; + + /* Clear operation if there's no more data to send. */ + if (pj_list_empty(&h->write_list)) + ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT); + + /* No need to hold mutex anymore */ + pj_mutex_unlock(h->mutex); + } + + /* Call callback. */ + if (h->cb.on_write_complete) { + (*h->cb.on_write_complete)(h, + (pj_ioqueue_op_key_t*)write_op, + write_op->written); + } + + } else { + pj_mutex_unlock(h->mutex); + } + + /* Done. */ + } else { + /* + * This is normal; execution may fall here when multiple threads + * are signalled for the same event, but only one thread eventually + * able to process the event. + */ + pj_mutex_unlock(h->mutex); + } +} + +void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h ) +{ + pj_status_t rc; + + /* Lock the key. */ + pj_mutex_lock(h->mutex); + +# if PJ_HAS_TCP + if (!pj_list_empty(&h->accept_list)) { + + struct accept_operation *accept_op; + + /* Get one accept operation from the list. */ + accept_op = h->accept_list.next; + pj_list_erase(accept_op); + accept_op->op = 0; + + /* Clear bit in fdset if there is no more pending accept */ + if (pj_list_empty(&h->accept_list)) + ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT); + + /* Unlock; from this point we don't need to hold key's mutex. */ + pj_mutex_unlock(h->mutex); + + rc=pj_sock_accept(h->fd, accept_op->accept_fd, + accept_op->rmt_addr, accept_op->addrlen); + if (rc==PJ_SUCCESS && accept_op->local_addr) { + rc = pj_sock_getsockname(*accept_op->accept_fd, + accept_op->local_addr, + accept_op->addrlen); + } + + /* Call callback. */ + if (h->cb.on_accept_complete) { + (*h->cb.on_accept_complete)(h, + (pj_ioqueue_op_key_t*)accept_op, + *accept_op->accept_fd, rc); + } + + } + else +# endif + if (key_has_pending_read(h)) { + struct read_operation *read_op; + pj_ssize_t bytes_read; + + /* Get one pending read operation from the list. */ + read_op = h->read_list.next; + pj_list_erase(read_op); + read_op->op = 0; + + /* Clear fdset if there is no pending read. */ + if (pj_list_empty(&h->read_list)) + ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT); + + /* Unlock; from this point we don't need to hold key's mutex. */ + pj_mutex_unlock(h->mutex); + + bytes_read = read_op->size; + + if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) { + rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, 0, + read_op->rmt_addr, + read_op->rmt_addrlen); + } else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) { + rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0); + } else { + pj_assert(read_op->op == PJ_IOQUEUE_OP_READ); + /* + * User has specified pj_ioqueue_read(). + * On Win32, we should do ReadFile(). But because we got + * here because of select() anyway, user must have put a + * socket descriptor on h->fd, which in this case we can + * just call pj_sock_recv() instead of ReadFile(). + * On Unix, user may put a file in h->fd, so we'll have + * to call read() here. + * This may not compile on systems which doesn't have + * read(). That's why we only specify PJ_LINUX here so + * that error is easier to catch. + */ +# if defined(PJ_WIN32) && PJ_WIN32 != 0 + rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0); + //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size, + // &bytes_read, NULL); +# elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0) + bytes_read = read(h->fd, read_op->buf, bytes_read); + rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); +# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 + bytes_read = sys_read(h->fd, read_op->buf, bytes_read); + rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read; +# else +# error "Implement read() for this platform!" +# endif + } + + if (rc != PJ_SUCCESS) { +# if defined(PJ_WIN32) && PJ_WIN32 != 0 + /* On Win32, for UDP, WSAECONNRESET on the receive side + * indicates that previous sending has triggered ICMP Port + * Unreachable message. + * But we wouldn't know at this point which one of previous + * key that has triggered the error, since UDP socket can + * be shared! + * So we'll just ignore it! + */ + + if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) { + //PJ_LOG(4,(THIS_FILE, + // "Ignored ICMP port unreach. on key=%p", h)); + } +# endif + + /* In any case we would report this to caller. */ + bytes_read = -rc; + } + + /* Call callback. */ + if (h->cb.on_read_complete) { + (*h->cb.on_read_complete)(h, + (pj_ioqueue_op_key_t*)read_op, + bytes_read); + } + + } else { + /* + * This is normal; execution may fall here when multiple threads + * are signalled for the same event, but only one thread eventually + * able to process the event. + */ + pj_mutex_unlock(h->mutex); + } +} + + +void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *h ) +{ + pj_mutex_lock(h->mutex); + + if (!h->connecting) { + /* It is possible that more than one thread was woken up, thus + * the remaining thread will see h->connecting as zero because + * it has been processed by other thread. + */ + pj_mutex_unlock(h->mutex); + return; + } + + /* Clear operation. */ + h->connecting = 0; + + pj_mutex_unlock(h->mutex); + + ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT); + ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT); + + /* Call callback. */ + if (h->cb.on_connect_complete) + (*h->cb.on_connect_complete)(h, -1); +} + +/* + * pj_ioqueue_recv() + * + * Start asynchronous recv() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + unsigned flags ) +{ + struct read_operation *read_op; + + PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); + PJ_CHECK_STACK(); + + read_op = (struct read_operation*)op_key; + read_op->op = 0; + + /* Try to see if there's data immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + pj_status_t status; + pj_ssize_t size; + + size = *length; + status = pj_sock_recv(key->fd, buffer, &size, flags); + if (status == PJ_SUCCESS) { + /* Yes! Data is available! */ + *length = size; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) + return status; + } + } + + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No data is immediately available. + * Must schedule asynchronous operation to the ioqueue. + */ + read_op->op = PJ_IOQUEUE_OP_RECV; + read_op->buf = buffer; + read_op->size = *length; + read_op->flags = flags; + + pj_mutex_lock(key->mutex); + pj_list_insert_before(&key->read_list, read_op); + ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +/* + * pj_ioqueue_recvfrom() + * + * Start asynchronous recvfrom() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + struct read_operation *read_op; + + PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); + PJ_CHECK_STACK(); + + read_op = (struct read_operation*)op_key; + read_op->op = 0; + + /* Try to see if there's data immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + pj_status_t status; + pj_ssize_t size; + + size = *length; + status = pj_sock_recvfrom(key->fd, buffer, &size, flags, + addr, addrlen); + if (status == PJ_SUCCESS) { + /* Yes! Data is available! */ + *length = size; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) + return status; + } + } + + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No data is immediately available. + * Must schedule asynchronous operation to the ioqueue. + */ + read_op->op = PJ_IOQUEUE_OP_RECV_FROM; + read_op->buf = buffer; + read_op->size = *length; + read_op->flags = flags; + read_op->rmt_addr = addr; + read_op->rmt_addrlen = addrlen; + + pj_mutex_lock(key->mutex); + pj_list_insert_before(&key->read_list, read_op); + ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +/* + * pj_ioqueue_send() + * + * Start asynchronous send() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + unsigned flags) +{ + struct write_operation *write_op; + pj_status_t status; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); + PJ_CHECK_STACK(); + + write_op = (struct write_operation*)op_key; + write_op->op = 0; + + /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write. */ + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* Fast track: + * Try to send data immediately, only if there's no pending write! + * Note: + * We are speculating that the list is empty here without properly + * acquiring ioqueue's mutex first. This is intentional, to maximize + * performance via parallelism. + * + * This should be safe, because: + * - by convention, we require caller to make sure that the + * key is not unregistered while other threads are invoking + * an operation on the same key. + * - pj_list_empty() is safe to be invoked by multiple threads, + * even when other threads are modifying the list. + */ + if (pj_list_empty(&key->write_list)) { + /* + * See if data can be sent immediately. + */ + sent = *length; + status = pj_sock_send(key->fd, data, &sent, flags); + if (status == PJ_SUCCESS) { + /* Success! */ + *length = sent; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { + return status; + } + } + } + + /* + * Schedule asynchronous send. + */ + write_op->op = PJ_IOQUEUE_OP_SEND; + write_op->buf = (void*)data; + write_op->size = *length; + write_op->written = 0; + write_op->flags = flags; + + pj_mutex_lock(key->mutex); + pj_list_insert_before(&key->write_list, write_op); + ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_sendto() + * + * Start asynchronous write() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + struct write_operation *write_op; + pj_status_t status; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); + PJ_CHECK_STACK(); + + write_op = (struct write_operation*)op_key; + write_op->op = 0; + + /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write */ + flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* Fast track: + * Try to send data immediately, only if there's no pending write! + * Note: + * We are speculating that the list is empty here without properly + * acquiring ioqueue's mutex first. This is intentional, to maximize + * performance via parallelism. + * + * This should be safe, because: + * - by convention, we require caller to make sure that the + * key is not unregistered while other threads are invoking + * an operation on the same key. + * - pj_list_empty() is safe to be invoked by multiple threads, + * even when other threads are modifying the list. + */ + if (pj_list_empty(&key->write_list)) { + /* + * See if data can be sent immediately. + */ + sent = *length; + status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); + if (status == PJ_SUCCESS) { + /* Success! */ + *length = sent; + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { + return status; + } + } + } + + /* + * Check that address storage can hold the address parameter. + */ + PJ_ASSERT_RETURN(addrlen <= sizeof(pj_sockaddr_in), PJ_EBUG); + + /* + * Schedule asynchronous send. + */ + write_op->op = PJ_IOQUEUE_OP_SEND_TO; + write_op->buf = (void*)data; + write_op->size = *length; + write_op->written = 0; + write_op->flags = flags; + pj_memcpy(&write_op->rmt_addr, addr, addrlen); + write_op->rmt_addrlen = addrlen; + + pj_mutex_lock(key->mutex); + pj_list_insert_before(&key->write_list, write_op); + ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + struct accept_operation *accept_op; + pj_status_t status; + + /* check parameters. All must be specified! */ + PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); + + accept_op = (struct accept_operation*)op_key; + accept_op->op = 0; + + /* Fast track: + * See if there's new connection available immediately. + */ + if (pj_list_empty(&key->accept_list)) { + status = pj_sock_accept(key->fd, new_sock, remote, addrlen); + if (status == PJ_SUCCESS) { + /* Yes! New connection is available! */ + if (local && addrlen) { + status = pj_sock_getsockname(*new_sock, local, addrlen); + if (status != PJ_SUCCESS) { + pj_sock_close(*new_sock); + *new_sock = PJ_INVALID_SOCKET; + return status; + } + } + return PJ_SUCCESS; + } else { + /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report + * the error to caller. + */ + if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { + return status; + } + } + } + + /* + * No connection is available immediately. + * Schedule accept() operation to be completed when there is incoming + * connection available. + */ + accept_op->op = PJ_IOQUEUE_OP_ACCEPT; + accept_op->accept_fd = new_sock; + accept_op->rmt_addr = remote; + accept_op->addrlen= addrlen; + accept_op->local_addr = local; + + pj_mutex_lock(key->mutex); + pj_list_insert_before(&key->accept_list, accept_op); + ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT); + pj_mutex_unlock(key->mutex); + + return PJ_EPENDING; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + pj_status_t status; + + /* check parameters. All must be specified! */ + PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); + + /* Check if socket has not been marked for connecting */ + if (key->connecting != 0) + return PJ_EPENDING; + + status = pj_sock_connect(key->fd, addr, addrlen); + if (status == PJ_SUCCESS) { + /* Connected! */ + return PJ_SUCCESS; + } else { + if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) { + /* Pending! */ + pj_mutex_lock(key->mutex); + key->connecting = PJ_TRUE; + ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT); + ioqueue_add_to_set(key->ioqueue, key->fd, EXCEPTION_EVENT); + pj_mutex_unlock(key->mutex); + return PJ_EPENDING; + } else { + /* Error! */ + return status; + } + } +} +#endif /* PJ_HAS_TCP */ + + +PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, + pj_size_t size ) +{ + pj_memset(op_key, 0, size); +} + + +/* + * pj_ioqueue_is_pending() + */ +PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key ) +{ + struct generic_operation *op_rec; + + PJ_UNUSED_ARG(key); + + op_rec = (struct generic_operation*)op_key; + return op_rec->op != 0; +} + + +/* + * pj_ioqueue_post_completion() + */ +PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status ) +{ + struct generic_operation *op_rec; + + /* + * Find the operation key in all pending operation list to + * really make sure that it's still there; then call the callback. + */ + pj_mutex_lock(key->mutex); + + /* Find the operation in the pending read list. */ + op_rec = (struct generic_operation*)key->read_list.next; + while (op_rec != (void*)&key->read_list) { + if (op_rec == (void*)op_key) { + pj_list_erase(op_rec); + op_rec->op = 0; + pj_mutex_unlock(key->mutex); + + (*key->cb.on_read_complete)(key, op_key, bytes_status); + return PJ_SUCCESS; + } + op_rec = op_rec->next; + } + + /* Find the operation in the pending write list. */ + op_rec = (struct generic_operation*)key->write_list.next; + while (op_rec != (void*)&key->write_list) { + if (op_rec == (void*)op_key) { + pj_list_erase(op_rec); + op_rec->op = 0; + pj_mutex_unlock(key->mutex); + + (*key->cb.on_write_complete)(key, op_key, bytes_status); + return PJ_SUCCESS; + } + op_rec = op_rec->next; + } + + /* Find the operation in the pending accept list. */ + op_rec = (struct generic_operation*)key->accept_list.next; + while (op_rec != (void*)&key->accept_list) { + if (op_rec == (void*)op_key) { + pj_list_erase(op_rec); + op_rec->op = 0; + pj_mutex_unlock(key->mutex); + + (*key->cb.on_accept_complete)(key, op_key, + PJ_INVALID_SOCKET, + bytes_status); + return PJ_SUCCESS; + } + op_rec = op_rec->next; + } + + pj_mutex_unlock(key->mutex); + + return PJ_EINVALIDOP; +} + diff --git a/pjlib/src/pj/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h index 9cb4ee29..8c3a1ff9 100644 --- a/pjlib/src/pj/ioqueue_common_abs.h +++ b/pjlib/src/pj/ioqueue_common_abs.h @@ -1,125 +1,125 @@ -/* $Id */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* ioqueue_common_abs.h
- *
- * This file contains private declarations for abstracting various
- * event polling/dispatching mechanisms (e.g. select, poll, epoll)
- * to the ioqueue.
- */
-
-#include <pj/list.h>
-
-/*
- * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
- * the correct error code.
- */
-#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
-# error "Proper error reporting must be enabled for ioqueue to work!"
-#endif
-
-
-struct generic_operation
-{
- PJ_DECL_LIST_MEMBER(struct generic_operation);
- pj_ioqueue_operation_e op;
-};
-
-struct read_operation
-{
- PJ_DECL_LIST_MEMBER(struct read_operation);
- pj_ioqueue_operation_e op;
-
- void *buf;
- pj_size_t size;
- unsigned flags;
- pj_sockaddr_t *rmt_addr;
- int *rmt_addrlen;
-};
-
-struct write_operation
-{
- PJ_DECL_LIST_MEMBER(struct write_operation);
- pj_ioqueue_operation_e op;
-
- char *buf;
- pj_size_t size;
- pj_ssize_t written;
- unsigned flags;
- pj_sockaddr_in rmt_addr;
- int rmt_addrlen;
-};
-
-#if PJ_HAS_TCP
-struct accept_operation
-{
- PJ_DECL_LIST_MEMBER(struct accept_operation);
- pj_ioqueue_operation_e op;
-
- pj_sock_t *accept_fd;
- pj_sockaddr_t *local_addr;
- pj_sockaddr_t *rmt_addr;
- int *addrlen;
-};
-#endif
-
-union operation_key
-{
- struct generic_operation generic;
- struct read_operation read;
- struct write_operation write;
-#if PJ_HAS_TCP
- struct accept_operation accept;
-#endif
-};
-
-#define DECLARE_COMMON_KEY \
- PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); \
- pj_ioqueue_t *ioqueue; \
- pj_mutex_t *mutex; \
- pj_sock_t fd; \
- int fd_type; \
- void *user_data; \
- pj_ioqueue_callback cb; \
- int connecting; \
- struct read_operation read_list; \
- struct write_operation write_list; \
- struct accept_operation accept_list;
-
-
-#define DECLARE_COMMON_IOQUEUE \
- pj_lock_t *lock; \
- pj_bool_t auto_delete_lock;
-
-
-enum ioqueue_event_type
-{
- NO_EVENT,
- READABLE_EVENT,
- WRITEABLE_EVENT,
- EXCEPTION_EVENT,
-};
-
-static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type );
-static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type);
-
+/* $Id */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ioqueue_common_abs.h + * + * This file contains private declarations for abstracting various + * event polling/dispatching mechanisms (e.g. select, poll, epoll) + * to the ioqueue. + */ + +#include <pj/list.h> + +/* + * The select ioqueue relies on socket functions (pj_sock_xxx()) to return + * the correct error code. + */ +#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100) +# error "Proper error reporting must be enabled for ioqueue to work!" +#endif + + +struct generic_operation +{ + PJ_DECL_LIST_MEMBER(struct generic_operation); + pj_ioqueue_operation_e op; +}; + +struct read_operation +{ + PJ_DECL_LIST_MEMBER(struct read_operation); + pj_ioqueue_operation_e op; + + void *buf; + pj_size_t size; + unsigned flags; + pj_sockaddr_t *rmt_addr; + int *rmt_addrlen; +}; + +struct write_operation +{ + PJ_DECL_LIST_MEMBER(struct write_operation); + pj_ioqueue_operation_e op; + + char *buf; + pj_size_t size; + pj_ssize_t written; + unsigned flags; + pj_sockaddr_in rmt_addr; + int rmt_addrlen; +}; + +#if PJ_HAS_TCP +struct accept_operation +{ + PJ_DECL_LIST_MEMBER(struct accept_operation); + pj_ioqueue_operation_e op; + + pj_sock_t *accept_fd; + pj_sockaddr_t *local_addr; + pj_sockaddr_t *rmt_addr; + int *addrlen; +}; +#endif + +union operation_key +{ + struct generic_operation generic; + struct read_operation read; + struct write_operation write; +#if PJ_HAS_TCP + struct accept_operation accept; +#endif +}; + +#define DECLARE_COMMON_KEY \ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); \ + pj_ioqueue_t *ioqueue; \ + pj_mutex_t *mutex; \ + pj_sock_t fd; \ + int fd_type; \ + void *user_data; \ + pj_ioqueue_callback cb; \ + int connecting; \ + struct read_operation read_list; \ + struct write_operation write_list; \ + struct accept_operation accept_list; + + +#define DECLARE_COMMON_IOQUEUE \ + pj_lock_t *lock; \ + pj_bool_t auto_delete_lock; + + +enum ioqueue_event_type +{ + NO_EVENT, + READABLE_EVENT, + WRITEABLE_EVENT, + EXCEPTION_EVENT, +}; + +static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_sock_t fd, + enum ioqueue_event_type event_type ); +static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, + pj_sock_t fd, + enum ioqueue_event_type event_type); + diff --git a/pjlib/src/pj/ioqueue_dummy.c b/pjlib/src/pj/ioqueue_dummy.c index 5f15e63c..31a6c7d3 100644 --- a/pjlib/src/pj/ioqueue_dummy.c +++ b/pjlib/src/pj/ioqueue_dummy.c @@ -1,193 +1,193 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/sock.h>
-#include <pj/errno.h>
-
-#define THIS_FILE "ioqueue"
-
-#define PJ_IOQUEUE_IS_READ_OP(op) \
- ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
-#define PJ_IOQUEUE_IS_WRITE_OP(op) \
- ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
-
-
-#if PJ_HAS_TCP
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
-#else
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
-#endif
-
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-# define VALIDATE_FD_SET 1
-#else
-# define VALIDATE_FD_SET 0
-#endif
-
-struct pj_ioqueue_key_t
-{
- PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
- pj_sock_t fd;
- pj_ioqueue_operation_e op;
- void *user_data;
- pj_ioqueue_callback cb;
-};
-
-struct pj_ioqueue_t
-{
-};
-
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- int max_threads,
- pj_ioqueue_t **ptr_ioqueue)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
- pj_lock_t *lock,
- pj_bool_t auto_delete )
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioque,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **ptr_key)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- return NULL;
-}
-
-
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen,
- unsigned flags)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen,
- unsigned flags)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- return -1;
-}
-
-#if PJ_HAS_TCP
-/*
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- return -1;
-}
-
-/*
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- return -1;
-}
-#endif /* PJ_HAS_TCP */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/ioqueue.h> +#include <pj/os.h> +#include <pj/log.h> +#include <pj/list.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/sock.h> +#include <pj/errno.h> + +#define THIS_FILE "ioqueue" + +#define PJ_IOQUEUE_IS_READ_OP(op) \ + ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) \ + ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif + +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; +}; + +struct pj_ioqueue_t +{ +}; + +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + int max_threads, + pj_ioqueue_t **ptr_ioqueue) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **ptr_key) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + return NULL; +} + + +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + return -1; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + return -1; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + return -1; +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c index 3c80f008..780ea37b 100644 --- a/pjlib/src/pj/ioqueue_epoll.c +++ b/pjlib/src/pj/ioqueue_epoll.c @@ -1,474 +1,474 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-/*
- * ioqueue_epoll.c
- *
- * This is the implementation of IOQueue framework using /dev/epoll
- * API in _both_ Linux user-mode and kernel-mode.
- */
-
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <pj/sock.h>
-#include <pj/compat/socket.h>
-
-#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0
- /*
- * Linux user mode
- */
-# include <sys/epoll.h>
-# include <errno.h>
-# include <unistd.h>
-
-# define epoll_data data.ptr
-# define epoll_data_type void*
-# define ioctl_val_type unsigned long
-# define getsockopt_val_ptr int*
-# define os_getsockopt getsockopt
-# define os_ioctl ioctl
-# define os_read read
-# define os_close close
-# define os_epoll_create epoll_create
-# define os_epoll_ctl epoll_ctl
-# define os_epoll_wait epoll_wait
-#else
- /*
- * Linux kernel mode.
- */
-# include <linux/config.h>
-# include <linux/version.h>
-# if defined(MODVERSIONS)
-# include <linux/modversions.h>
-# endif
-# include <linux/kernel.h>
-# include <linux/poll.h>
-# include <linux/eventpoll.h>
-# include <linux/syscalls.h>
-# include <linux/errno.h>
-# include <linux/unistd.h>
-# include <asm/ioctls.h>
- enum EPOLL_EVENTS
- {
- EPOLLIN = 0x001,
- EPOLLOUT = 0x004,
- EPOLLERR = 0x008,
- };
-# define os_epoll_create sys_epoll_create
- static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
- {
- long rc;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- rc = sys_epoll_ctl(epfd, op, fd, event);
- set_fs(oldfs);
- if (rc) {
- errno = -rc;
- return -1;
- } else {
- return 0;
- }
- }
- static int os_epoll_wait(int epfd, struct epoll_event *events,
- int maxevents, int timeout)
- {
- int count;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- count = sys_epoll_wait(epfd, events, maxevents, timeout);
- set_fs(oldfs);
- return count;
- }
-# define os_close sys_close
-# define os_getsockopt pj_sock_getsockopt
- static int os_read(int fd, void *buf, size_t len)
- {
- long rc;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- rc = sys_read(fd, buf, len);
- set_fs(oldfs);
- if (rc) {
- errno = -rc;
- return -1;
- } else {
- return 0;
- }
- }
-# define socklen_t unsigned
-# define ioctl_val_type unsigned long
- int ioctl(int fd, int opt, ioctl_val_type value);
- static int os_ioctl(int fd, int opt, ioctl_val_type value)
- {
- int rc;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- rc = ioctl(fd, opt, value);
- set_fs(oldfs);
- if (rc < 0) {
- errno = -rc;
- return rc;
- } else
- return rc;
- }
-# define getsockopt_val_ptr char*
-
-# define epoll_data data
-# define epoll_data_type __u32
-#endif
-
-#define THIS_FILE "ioq_epoll"
-
-//#define TRACE_(expr) PJ_LOG(3,expr)
-#define TRACE_(expr)
-
-/*
- * Include common ioqueue abstraction.
- */
-#include "ioqueue_common_abs.h"
-
-/*
- * This describes each key.
- */
-struct pj_ioqueue_key_t
-{
- DECLARE_COMMON_KEY
-};
-
-/*
- * This describes the I/O queue.
- */
-struct pj_ioqueue_t
-{
- DECLARE_COMMON_IOQUEUE
-
- unsigned max, count;
- pj_ioqueue_key_t hlist;
- int epfd;
-};
-
-/* Include implementation for common abstraction after we declare
- * pj_ioqueue_key_t and pj_ioqueue_t.
- */
-#include "ioqueue_common_abs.c"
-
-/*
- * pj_ioqueue_name()
- */
-PJ_DEF(const char*) pj_ioqueue_name(void)
-{
-#if defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0
- return "epoll-kernel";
-#else
- return "epoll";
-#endif
-}
-
-/*
- * pj_ioqueue_create()
- *
- * Create select ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **p_ioqueue)
-{
- pj_ioqueue_t *ioqueue;
- pj_status_t rc;
- pj_lock_t *lock;
-
- /* Check that arguments are valid. */
- PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL &&
- max_fd > 0, PJ_EINVAL);
-
- /* Check that size of pj_ioqueue_op_key_t is sufficient */
- PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
- sizeof(union operation_key), PJ_EBUG);
-
- ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
-
- ioqueue_init(ioqueue);
-
- ioqueue->max = max_fd;
- ioqueue->count = 0;
- pj_list_init(&ioqueue->hlist);
-
- rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
- if (rc != PJ_SUCCESS)
- return rc;
-
- rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
- if (rc != PJ_SUCCESS)
- return rc;
-
- ioqueue->epfd = os_epoll_create(max_fd);
- if (ioqueue->epfd < 0) {
- ioqueue_destroy(ioqueue);
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
- }
-
- PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue));
-
- *p_ioqueue = ioqueue;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_destroy()
- *
- * Destroy ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
-{
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
- PJ_ASSERT_RETURN(ioqueue->epfd > 0, PJ_EINVALIDOP);
-
- pj_lock_acquire(ioqueue->lock);
- os_close(ioqueue->epfd);
- ioqueue->epfd = 0;
- return ioqueue_destroy(ioqueue);
-}
-
-/*
- * pj_ioqueue_register_sock()
- *
- * Register a socket to ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **p_key)
-{
- pj_ioqueue_key_t *key = NULL;
- pj_uint32_t value;
- struct epoll_event ev;
- int status;
- pj_status_t rc = PJ_SUCCESS;
-
- PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
- cb && p_key, PJ_EINVAL);
-
- pj_lock_acquire(ioqueue->lock);
-
- if (ioqueue->count >= ioqueue->max) {
- rc = PJ_ETOOMANY;
- TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files"));
- goto on_return;
- }
-
- /* Set socket to nonblocking. */
- value = 1;
- if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) {
- TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d",
- rc));
- rc = pj_get_netos_error();
- goto on_return;
- }
-
- /* Create key. */
- key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
- rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
- if (rc != PJ_SUCCESS) {
- key = NULL;
- goto on_return;
- }
-
- /* os_epoll_ctl. */
- ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
- ev.epoll_data = (epoll_data_type)key;
- status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev);
- if (status < 0) {
- rc = pj_get_os_error();
- key = NULL;
- TRACE_((THIS_FILE,
- "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d",
- status));
- goto on_return;
- }
-
- /* Register */
- pj_list_insert_before(&ioqueue->hlist, key);
- ++ioqueue->count;
-
-on_return:
- *p_key = key;
- pj_lock_release(ioqueue->lock);
-
- return rc;
-}
-
-/*
- * pj_ioqueue_unregister()
- *
- * Unregister handle from ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
-{
- pj_ioqueue_t *ioqueue;
- struct epoll_event ev;
- int status;
-
- PJ_ASSERT_RETURN(key != NULL, PJ_EINVAL);
-
- ioqueue = key->ioqueue;
- pj_lock_acquire(ioqueue->lock);
-
- pj_assert(ioqueue->count > 0);
- --ioqueue->count;
- pj_list_erase(key);
-
- ev.events = 0;
- ev.epoll_data = (epoll_data_type)key;
- status = os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_DEL, key->fd, &ev);
- if (status != 0) {
- pj_status_t rc = pj_get_os_error();
- pj_lock_release(ioqueue->lock);
- return rc;
- }
-
- pj_lock_release(ioqueue->lock);
-
- /* Destroy the key. */
- ioqueue_destroy_key(key);
-
- return PJ_SUCCESS;
-}
-
-/* ioqueue_remove_from_set()
- * This function is called from ioqueue_dispatch_event() to instruct
- * the ioqueue to remove the specified descriptor from ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type)
-{
-}
-
-/*
- * ioqueue_add_to_set()
- * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
- * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type )
-{
-}
-
-/*
- * pj_ioqueue_poll()
- *
- */
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
-{
- int i, count, processed;
- struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
- int msec;
- struct queue {
- pj_ioqueue_key_t *key;
- enum ioqueue_event_type event_type;
- } queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
-
- PJ_CHECK_STACK();
-
- msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000;
-
- count = os_epoll_wait( ioqueue->epfd, events, PJ_ARRAY_SIZE(events), msec);
- if (count <= 0)
- return count;
-
- /* Lock ioqueue. */
- pj_lock_acquire(ioqueue->lock);
-
- for (processed=0, i=0; i<count; ++i) {
- pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type)
- events[i].epoll_data;
-
- /*
- * Check readability.
- */
- if ((events[i].events & EPOLLIN) &&
- (key_has_pending_read(h) || key_has_pending_accept(h))) {
- queue[processed].key = h;
- queue[processed].event_type = READABLE_EVENT;
- ++processed;
- }
-
- /*
- * Check for writeability.
- */
- if ((events[i].events & EPOLLOUT) && key_has_pending_write(h)) {
- queue[processed].key = h;
- queue[processed].event_type = WRITEABLE_EVENT;
- ++processed;
- }
-
-#if PJ_HAS_TCP
- /*
- * Check for completion of connect() operation.
- */
- if ((events[i].events & EPOLLOUT) && (h->connecting)) {
- queue[processed].key = h;
- queue[processed].event_type = WRITEABLE_EVENT;
- ++processed;
- }
-#endif /* PJ_HAS_TCP */
-
- /*
- * Check for error condition.
- */
- if (events[i].events & EPOLLERR && (h->connecting)) {
- queue[processed].key = h;
- queue[processed].event_type = EXCEPTION_EVENT;
- ++processed;
- }
- }
- pj_lock_release(ioqueue->lock);
-
- /* Now process the events. */
- for (i=0; i<processed; ++i) {
- switch (queue[i].event_type) {
- case READABLE_EVENT:
- ioqueue_dispatch_read_event(ioqueue, queue[i].key);
- break;
- case WRITEABLE_EVENT:
- ioqueue_dispatch_write_event(ioqueue, queue[i].key);
- break;
- case EXCEPTION_EVENT:
- ioqueue_dispatch_exception_event(ioqueue, queue[i].key);
- break;
- case NO_EVENT:
- pj_assert(!"Invalid event!");
- break;
- }
- }
-
- return processed;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * ioqueue_epoll.c + * + * This is the implementation of IOQueue framework using /dev/epoll + * API in _both_ Linux user-mode and kernel-mode. + */ + +#include <pj/ioqueue.h> +#include <pj/os.h> +#include <pj/lock.h> +#include <pj/log.h> +#include <pj/list.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <pj/sock.h> +#include <pj/compat/socket.h> + +#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0 + /* + * Linux user mode + */ +# include <sys/epoll.h> +# include <errno.h> +# include <unistd.h> + +# define epoll_data data.ptr +# define epoll_data_type void* +# define ioctl_val_type unsigned long +# define getsockopt_val_ptr int* +# define os_getsockopt getsockopt +# define os_ioctl ioctl +# define os_read read +# define os_close close +# define os_epoll_create epoll_create +# define os_epoll_ctl epoll_ctl +# define os_epoll_wait epoll_wait +#else + /* + * Linux kernel mode. + */ +# include <linux/config.h> +# include <linux/version.h> +# if defined(MODVERSIONS) +# include <linux/modversions.h> +# endif +# include <linux/kernel.h> +# include <linux/poll.h> +# include <linux/eventpoll.h> +# include <linux/syscalls.h> +# include <linux/errno.h> +# include <linux/unistd.h> +# include <asm/ioctls.h> + enum EPOLL_EVENTS + { + EPOLLIN = 0x001, + EPOLLOUT = 0x004, + EPOLLERR = 0x008, + }; +# define os_epoll_create sys_epoll_create + static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) + { + long rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = sys_epoll_ctl(epfd, op, fd, event); + set_fs(oldfs); + if (rc) { + errno = -rc; + return -1; + } else { + return 0; + } + } + static int os_epoll_wait(int epfd, struct epoll_event *events, + int maxevents, int timeout) + { + int count; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + count = sys_epoll_wait(epfd, events, maxevents, timeout); + set_fs(oldfs); + return count; + } +# define os_close sys_close +# define os_getsockopt pj_sock_getsockopt + static int os_read(int fd, void *buf, size_t len) + { + long rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = sys_read(fd, buf, len); + set_fs(oldfs); + if (rc) { + errno = -rc; + return -1; + } else { + return 0; + } + } +# define socklen_t unsigned +# define ioctl_val_type unsigned long + int ioctl(int fd, int opt, ioctl_val_type value); + static int os_ioctl(int fd, int opt, ioctl_val_type value) + { + int rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = ioctl(fd, opt, value); + set_fs(oldfs); + if (rc < 0) { + errno = -rc; + return rc; + } else + return rc; + } +# define getsockopt_val_ptr char* + +# define epoll_data data +# define epoll_data_type __u32 +#endif + +#define THIS_FILE "ioq_epoll" + +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(expr) + +/* + * Include common ioqueue abstraction. + */ +#include "ioqueue_common_abs.h" + +/* + * This describes each key. + */ +struct pj_ioqueue_key_t +{ + DECLARE_COMMON_KEY +}; + +/* + * This describes the I/O queue. + */ +struct pj_ioqueue_t +{ + DECLARE_COMMON_IOQUEUE + + unsigned max, count; + pj_ioqueue_key_t hlist; + int epfd; +}; + +/* Include implementation for common abstraction after we declare + * pj_ioqueue_key_t and pj_ioqueue_t. + */ +#include "ioqueue_common_abs.c" + +/* + * pj_ioqueue_name() + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ +#if defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0 + return "epoll-kernel"; +#else + return "epoll"; +#endif +} + +/* + * pj_ioqueue_create() + * + * Create select ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioqueue; + pj_status_t rc; + pj_lock_t *lock; + + /* Check that arguments are valid. */ + PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && + max_fd > 0, PJ_EINVAL); + + /* Check that size of pj_ioqueue_op_key_t is sufficient */ + PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= + sizeof(union operation_key), PJ_EBUG); + + ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); + + ioqueue_init(ioqueue); + + ioqueue->max = max_fd; + ioqueue->count = 0; + pj_list_init(&ioqueue->hlist); + + rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock); + if (rc != PJ_SUCCESS) + return rc; + + rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE); + if (rc != PJ_SUCCESS) + return rc; + + ioqueue->epfd = os_epoll_create(max_fd); + if (ioqueue->epfd < 0) { + ioqueue_destroy(ioqueue); + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + } + + PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue)); + + *p_ioqueue = ioqueue; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + * + * Destroy ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue) +{ + PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); + PJ_ASSERT_RETURN(ioqueue->epfd > 0, PJ_EINVALIDOP); + + pj_lock_acquire(ioqueue->lock); + os_close(ioqueue->epfd); + ioqueue->epfd = 0; + return ioqueue_destroy(ioqueue); +} + +/* + * pj_ioqueue_register_sock() + * + * Register a socket to ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key) +{ + pj_ioqueue_key_t *key = NULL; + pj_uint32_t value; + struct epoll_event ev; + int status; + pj_status_t rc = PJ_SUCCESS; + + PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET && + cb && p_key, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + + if (ioqueue->count >= ioqueue->max) { + rc = PJ_ETOOMANY; + TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files")); + goto on_return; + } + + /* Set socket to nonblocking. */ + value = 1; + if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) { + TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d", + rc)); + rc = pj_get_netos_error(); + goto on_return; + } + + /* Create key. */ + key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); + rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb); + if (rc != PJ_SUCCESS) { + key = NULL; + goto on_return; + } + + /* os_epoll_ctl. */ + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + ev.epoll_data = (epoll_data_type)key; + status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev); + if (status < 0) { + rc = pj_get_os_error(); + key = NULL; + TRACE_((THIS_FILE, + "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d", + status)); + goto on_return; + } + + /* Register */ + pj_list_insert_before(&ioqueue->hlist, key); + ++ioqueue->count; + +on_return: + *p_key = key; + pj_lock_release(ioqueue->lock); + + return rc; +} + +/* + * pj_ioqueue_unregister() + * + * Unregister handle from ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key) +{ + pj_ioqueue_t *ioqueue; + struct epoll_event ev; + int status; + + PJ_ASSERT_RETURN(key != NULL, PJ_EINVAL); + + ioqueue = key->ioqueue; + pj_lock_acquire(ioqueue->lock); + + pj_assert(ioqueue->count > 0); + --ioqueue->count; + pj_list_erase(key); + + ev.events = 0; + ev.epoll_data = (epoll_data_type)key; + status = os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_DEL, key->fd, &ev); + if (status != 0) { + pj_status_t rc = pj_get_os_error(); + pj_lock_release(ioqueue->lock); + return rc; + } + + pj_lock_release(ioqueue->lock); + + /* Destroy the key. */ + ioqueue_destroy_key(key); + + return PJ_SUCCESS; +} + +/* ioqueue_remove_from_set() + * This function is called from ioqueue_dispatch_event() to instruct + * the ioqueue to remove the specified descriptor from ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, + pj_sock_t fd, + enum ioqueue_event_type event_type) +{ +} + +/* + * ioqueue_add_to_set() + * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc + * to instruct the ioqueue to add the specified handle to ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_sock_t fd, + enum ioqueue_event_type event_type ) +{ +} + +/* + * pj_ioqueue_poll() + * + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) +{ + int i, count, processed; + struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; + int msec; + struct queue { + pj_ioqueue_key_t *key; + enum ioqueue_event_type event_type; + } queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; + + PJ_CHECK_STACK(); + + msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000; + + count = os_epoll_wait( ioqueue->epfd, events, PJ_ARRAY_SIZE(events), msec); + if (count <= 0) + return count; + + /* Lock ioqueue. */ + pj_lock_acquire(ioqueue->lock); + + for (processed=0, i=0; i<count; ++i) { + pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type) + events[i].epoll_data; + + /* + * Check readability. + */ + if ((events[i].events & EPOLLIN) && + (key_has_pending_read(h) || key_has_pending_accept(h))) { + queue[processed].key = h; + queue[processed].event_type = READABLE_EVENT; + ++processed; + } + + /* + * Check for writeability. + */ + if ((events[i].events & EPOLLOUT) && key_has_pending_write(h)) { + queue[processed].key = h; + queue[processed].event_type = WRITEABLE_EVENT; + ++processed; + } + +#if PJ_HAS_TCP + /* + * Check for completion of connect() operation. + */ + if ((events[i].events & EPOLLOUT) && (h->connecting)) { + queue[processed].key = h; + queue[processed].event_type = WRITEABLE_EVENT; + ++processed; + } +#endif /* PJ_HAS_TCP */ + + /* + * Check for error condition. + */ + if (events[i].events & EPOLLERR && (h->connecting)) { + queue[processed].key = h; + queue[processed].event_type = EXCEPTION_EVENT; + ++processed; + } + } + pj_lock_release(ioqueue->lock); + + /* Now process the events. */ + for (i=0; i<processed; ++i) { + switch (queue[i].event_type) { + case READABLE_EVENT: + ioqueue_dispatch_read_event(ioqueue, queue[i].key); + break; + case WRITEABLE_EVENT: + ioqueue_dispatch_write_event(ioqueue, queue[i].key); + break; + case EXCEPTION_EVENT: + ioqueue_dispatch_exception_event(ioqueue, queue[i].key); + break; + case NO_EVENT: + pj_assert(!"Invalid event!"); + break; + } + } + + return processed; +} + diff --git a/pjlib/src/pj/ioqueue_linux_kernel.c b/pjlib/src/pj/ioqueue_linux_kernel.c index eb351986..dba0f134 100644 --- a/pjlib/src/pj/ioqueue_linux_kernel.c +++ b/pjlib/src/pj/ioqueue_linux_kernel.c @@ -1,161 +1,161 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/sock.h>
-
-#define THIS_FILE "ioqueue"
-
-#define PJ_IOQUEUE_IS_READ_OP(op) \
- ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
-#define PJ_IOQUEUE_IS_WRITE_OP(op) \
- ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
-
-
-#if PJ_HAS_TCP
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
-#else
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
-#endif
-
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-# define VALIDATE_FD_SET 1
-#else
-# define VALIDATE_FD_SET 0
-#endif
-
-struct pj_ioqueue_key_t
-{
- PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
- pj_sock_t fd;
- pj_ioqueue_operation_e op;
- void *user_data;
- pj_ioqueue_callback cb;
-};
-
-struct pj_ioqueue_t
-{
-};
-
-PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd)
-{
- return NULL;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
-{
- return 0;
-}
-
-PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool,
- pj_ioqueue_t *ioque,
- pj_oshandle_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb)
-{
- return NULL;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key)
-{
- return -1;
-}
-
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- return NULL;
-}
-
-
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- return -1;
-}
-
-#if PJ_HAS_TCP
-/*
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- return -1;
-}
-
-/*
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- return -1;
-}
-#endif /* PJ_HAS_TCP */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/ioqueue.h> +#include <pj/os.h> +#include <pj/log.h> +#include <pj/list.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/sock.h> + +#define THIS_FILE "ioqueue" + +#define PJ_IOQUEUE_IS_READ_OP(op) \ + ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) \ + ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif + +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; +}; + +struct pj_ioqueue_t +{ +}; + +PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd) +{ + return NULL; +} + +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + return 0; +} + +PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_oshandle_t sock, + void *user_data, + const pj_ioqueue_callback *cb) +{ + return NULL; +} + +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + return -1; +} + +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + return NULL; +} + + +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + pj_sockaddr_t *addr, + int *addrlen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + const pj_sockaddr_t *addr, + int addrlen) +{ + return -1; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + return -1; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + return -1; +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c index b313c449..df08d0c5 100644 --- a/pjlib/src/pj/ioqueue_select.c +++ b/pjlib/src/pj/ioqueue_select.c @@ -1,531 +1,531 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/*
- * sock_select.c
- *
- * This is the implementation of IOQueue using pj_sock_select().
- * It runs anywhere where pj_sock_select() is available (currently
- * Win32, Linux, Linux kernel, etc.).
- */
-
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/sock.h>
-#include <pj/compat/socket.h>
-#include <pj/sock_select.h>
-#include <pj/errno.h>
-
-/*
- * Include declaration from common abstraction.
- */
-#include "ioqueue_common_abs.h"
-
-/*
- * ISSUES with ioqueue_select()
- *
- * EAGAIN/EWOULDBLOCK error in recv():
- * - when multiple threads are working with the ioqueue, application
- * may receive EAGAIN or EWOULDBLOCK in the receive callback.
- * This error happens because more than one thread is watching for
- * the same descriptor set, so when all of them call recv() or recvfrom()
- * simultaneously, only one will succeed and the rest will get the error.
- *
- */
-#define THIS_FILE "ioq_select"
-
-/*
- * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
- * the correct error code.
- */
-#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
-# error "Error reporting must be enabled for this function to work!"
-#endif
-
-/**
- * Get the number of descriptors in the set. This is defined in sock_select.c
- * This function will only return the number of sockets set from PJ_FD_SET
- * operation. When the set is modified by other means (such as by select()),
- * the count will not be reflected here.
- *
- * That's why don't export this function in the header file, to avoid
- * misunderstanding.
- *
- * @param fdsetp The descriptor set.
- *
- * @return Number of descriptors in the set.
- */
-PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp);
-
-
-/*
- * During debugging build, VALIDATE_FD_SET is set.
- * This will check the validity of the fd_sets.
- */
-/*
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-# define VALIDATE_FD_SET 1
-#else
-# define VALIDATE_FD_SET 0
-#endif
-*/
-#define VALIDATE_FD_SET 0
-
-/*
- * This describes each key.
- */
-struct pj_ioqueue_key_t
-{
- DECLARE_COMMON_KEY
-};
-
-/*
- * This describes the I/O queue itself.
- */
-struct pj_ioqueue_t
-{
- DECLARE_COMMON_IOQUEUE
-
- unsigned max, count;
- pj_ioqueue_key_t key_list;
- pj_fd_set_t rfdset;
- pj_fd_set_t wfdset;
-#if PJ_HAS_TCP
- pj_fd_set_t xfdset;
-#endif
-};
-
-/* Include implementation for common abstraction after we declare
- * pj_ioqueue_key_t and pj_ioqueue_t.
- */
-#include "ioqueue_common_abs.c"
-
-/*
- * pj_ioqueue_name()
- */
-PJ_DEF(const char*) pj_ioqueue_name(void)
-{
- return "select";
-}
-
-/*
- * pj_ioqueue_create()
- *
- * Create select ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **p_ioqueue)
-{
- pj_ioqueue_t *ioqueue;
- pj_lock_t *lock;
- pj_status_t rc;
-
- /* Check that arguments are valid. */
- PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL &&
- max_fd > 0 && max_fd <= PJ_IOQUEUE_MAX_HANDLES,
- PJ_EINVAL);
-
- /* Check that size of pj_ioqueue_op_key_t is sufficient */
- PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
- sizeof(union operation_key), PJ_EBUG);
-
- ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
-
- ioqueue_init(ioqueue);
-
- ioqueue->max = max_fd;
- ioqueue->count = 0;
- PJ_FD_ZERO(&ioqueue->rfdset);
- PJ_FD_ZERO(&ioqueue->wfdset);
-#if PJ_HAS_TCP
- PJ_FD_ZERO(&ioqueue->xfdset);
-#endif
- pj_list_init(&ioqueue->key_list);
-
- rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
- if (rc != PJ_SUCCESS)
- return rc;
-
- rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
- if (rc != PJ_SUCCESS)
- return rc;
-
- PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioqueue));
-
- *p_ioqueue = ioqueue;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_destroy()
- *
- * Destroy ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
-{
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
-
- pj_lock_acquire(ioqueue->lock);
- return ioqueue_destroy(ioqueue);
-}
-
-
-/*
- * pj_ioqueue_register_sock()
- *
- * Register a handle to ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **p_key)
-{
- pj_ioqueue_key_t *key = NULL;
- pj_uint32_t value;
- pj_status_t rc = PJ_SUCCESS;
-
- PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
- cb && p_key, PJ_EINVAL);
-
- pj_lock_acquire(ioqueue->lock);
-
- if (ioqueue->count >= ioqueue->max) {
- rc = PJ_ETOOMANY;
- goto on_return;
- }
-
- /* Set socket to nonblocking. */
- value = 1;
-#ifdef PJ_WIN32
- if (ioctlsocket(sock, FIONBIO, (u_long*)&value)) {
-#else
- if (ioctl(sock, FIONBIO, &value)) {
-#endif
- rc = pj_get_netos_error();
- goto on_return;
- }
-
- /* Create key. */
- key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
- rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
- if (rc != PJ_SUCCESS) {
- key = NULL;
- goto on_return;
- }
-
- /* Register */
- pj_list_insert_before(&ioqueue->key_list, key);
- ++ioqueue->count;
-
-on_return:
- /* On error, socket may be left in non-blocking mode. */
- *p_key = key;
- pj_lock_release(ioqueue->lock);
-
- return rc;
-}
-
-/*
- * pj_ioqueue_unregister()
- *
- * Unregister handle from ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
-{
- pj_ioqueue_t *ioqueue;
-
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
- ioqueue = key->ioqueue;
-
- pj_lock_acquire(ioqueue->lock);
-
- pj_assert(ioqueue->count > 0);
- --ioqueue->count;
- pj_list_erase(key);
- PJ_FD_CLR(key->fd, &ioqueue->rfdset);
- PJ_FD_CLR(key->fd, &ioqueue->wfdset);
-#if PJ_HAS_TCP
- PJ_FD_CLR(key->fd, &ioqueue->xfdset);
-#endif
-
- /* ioqueue_destroy may try to acquire key's mutex.
- * Since normally the order of locking is to lock key's mutex first
- * then ioqueue's mutex, ioqueue_destroy may deadlock unless we
- * release ioqueue's mutex first.
- */
- pj_lock_release(ioqueue->lock);
-
- /* Destroy the key. */
- ioqueue_destroy_key(key);
-
- return PJ_SUCCESS;
-}
-
-
-/* This supposed to check whether the fd_set values are consistent
- * with the operation currently set in each key.
- */
-#if VALIDATE_FD_SET
-static void validate_sets(const pj_ioqueue_t *ioqueue,
- const pj_fd_set_t *rfdset,
- const pj_fd_set_t *wfdset,
- const pj_fd_set_t *xfdset)
-{
- pj_ioqueue_key_t *key;
-
- /*
- * This basicly would not work anymore.
- * We need to lock key before performing the check, but we can't do
- * so because we're holding ioqueue mutex. If we acquire key's mutex
- * now, the will cause deadlock.
- */
- pj_assert(0);
-
- key = ioqueue->key_list.next;
- while (key != &ioqueue->key_list) {
- if (!pj_list_empty(&key->read_list)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- || !pj_list_empty(&key->accept_list)
-#endif
- )
- {
- pj_assert(PJ_FD_ISSET(key->fd, rfdset));
- }
- else {
- pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0);
- }
- if (!pj_list_empty(&key->write_list)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- || key->connecting
-#endif
- )
- {
- pj_assert(PJ_FD_ISSET(key->fd, wfdset));
- }
- else {
- pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0);
- }
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- if (key->connecting)
- {
- pj_assert(PJ_FD_ISSET(key->fd, xfdset));
- }
- else {
- pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0);
- }
-#endif /* PJ_HAS_TCP */
-
- key = key->next;
- }
-}
-#endif /* VALIDATE_FD_SET */
-
-
-/* ioqueue_remove_from_set()
- * This function is called from ioqueue_dispatch_event() to instruct
- * the ioqueue to remove the specified descriptor from ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type)
-{
- pj_lock_acquire(ioqueue->lock);
-
- if (event_type == READABLE_EVENT)
- PJ_FD_CLR((pj_sock_t)fd, &ioqueue->rfdset);
- else if (event_type == WRITEABLE_EVENT)
- PJ_FD_CLR((pj_sock_t)fd, &ioqueue->wfdset);
- else if (event_type == EXCEPTION_EVENT)
- PJ_FD_CLR((pj_sock_t)fd, &ioqueue->xfdset);
- else
- pj_assert(0);
-
- pj_lock_release(ioqueue->lock);
-}
-
-/*
- * ioqueue_add_to_set()
- * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
- * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type )
-{
- pj_lock_acquire(ioqueue->lock);
-
- if (event_type == READABLE_EVENT)
- PJ_FD_SET((pj_sock_t)fd, &ioqueue->rfdset);
- else if (event_type == WRITEABLE_EVENT)
- PJ_FD_SET((pj_sock_t)fd, &ioqueue->wfdset);
- else if (event_type == EXCEPTION_EVENT)
- PJ_FD_SET((pj_sock_t)fd, &ioqueue->xfdset);
- else
- pj_assert(0);
-
- pj_lock_release(ioqueue->lock);
-}
-
-/*
- * pj_ioqueue_poll()
- *
- * Few things worth written:
- *
- * - we used to do only one callback called per poll, but it didn't go
- * very well. The reason is because on some situation, the write
- * callback gets called all the time, thus doesn't give the read
- * callback to get called. This happens, for example, when user
- * submit write operation inside the write callback.
- * As the result, we changed the behaviour so that now multiple
- * callbacks are called in a single poll. It should be fast too,
- * just that we need to be carefull with the ioqueue data structs.
- *
- * - to guarantee preemptiveness etc, the poll function must strictly
- * work on fd_set copy of the ioqueue (not the original one).
- */
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
-{
- pj_fd_set_t rfdset, wfdset, xfdset;
- int count, counter;
- pj_ioqueue_key_t *h;
- struct event
- {
- pj_ioqueue_key_t *key;
- enum ioqueue_event_type event_type;
- } event[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
-
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
-
- /* Lock ioqueue before making fd_set copies */
- pj_lock_acquire(ioqueue->lock);
-
- /* We will only do select() when there are sockets to be polled.
- * Otherwise select() will return error.
- */
- if (PJ_FD_COUNT(&ioqueue->rfdset)==0 &&
- PJ_FD_COUNT(&ioqueue->wfdset)==0 &&
- PJ_FD_COUNT(&ioqueue->xfdset)==0)
- {
- pj_lock_release(ioqueue->lock);
- if (timeout)
- pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout));
- return 0;
- }
-
- /* Copy ioqueue's pj_fd_set_t to local variables. */
- pj_memcpy(&rfdset, &ioqueue->rfdset, sizeof(pj_fd_set_t));
- pj_memcpy(&wfdset, &ioqueue->wfdset, sizeof(pj_fd_set_t));
-#if PJ_HAS_TCP
- pj_memcpy(&xfdset, &ioqueue->xfdset, sizeof(pj_fd_set_t));
-#else
- PJ_FD_ZERO(&xfdset);
-#endif
-
-#if VALIDATE_FD_SET
- validate_sets(ioqueue, &rfdset, &wfdset, &xfdset);
-#endif
-
- /* Unlock ioqueue before select(). */
- pj_lock_release(ioqueue->lock);
-
- count = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout);
-
- if (count <= 0)
- return count;
- else if (count > PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL)
- count = PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL;
-
- /* Scan descriptor sets for event and add the events in the event
- * array to be processed later in this function. We do this so that
- * events can be processed in parallel without holding ioqueue lock.
- */
- pj_lock_acquire(ioqueue->lock);
-
- counter = 0;
-
- /* Scan for writable sockets first to handle piggy-back data
- * coming with accept().
- */
- h = ioqueue->key_list.next;
- for ( ; h!=&ioqueue->key_list && counter<count; h = h->next) {
- if ( (key_has_pending_write(h) || key_has_pending_connect(h))
- && PJ_FD_ISSET(h->fd, &wfdset))
- {
- event[counter].key = h;
- event[counter].event_type = WRITEABLE_EVENT;
- ++counter;
- }
-
- /* Scan for readable socket. */
- if ((key_has_pending_read(h) || key_has_pending_accept(h))
- && PJ_FD_ISSET(h->fd, &rfdset))
- {
- event[counter].key = h;
- event[counter].event_type = READABLE_EVENT;
- ++counter;
- }
-
-#if PJ_HAS_TCP
- if (key_has_pending_connect(h) && PJ_FD_ISSET(h->fd, &xfdset)) {
- event[counter].key = h;
- event[counter].event_type = EXCEPTION_EVENT;
- ++counter;
- }
-#endif
- }
-
- pj_lock_release(ioqueue->lock);
-
- count = counter;
-
- /* Now process all events. The dispatch functions will take care
- * of locking in each of the key
- */
- for (counter=0; counter<count; ++counter) {
- switch (event[counter].event_type) {
- case READABLE_EVENT:
- ioqueue_dispatch_read_event(ioqueue, event[counter].key);
- break;
- case WRITEABLE_EVENT:
- ioqueue_dispatch_write_event(ioqueue, event[counter].key);
- break;
- case EXCEPTION_EVENT:
- ioqueue_dispatch_exception_event(ioqueue, event[counter].key);
- break;
- case NO_EVENT:
- pj_assert(!"Invalid event!");
- break;
- }
- }
-
- return count;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * sock_select.c + * + * This is the implementation of IOQueue using pj_sock_select(). + * It runs anywhere where pj_sock_select() is available (currently + * Win32, Linux, Linux kernel, etc.). + */ + +#include <pj/ioqueue.h> +#include <pj/os.h> +#include <pj/lock.h> +#include <pj/log.h> +#include <pj/list.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/sock.h> +#include <pj/compat/socket.h> +#include <pj/sock_select.h> +#include <pj/errno.h> + +/* + * Include declaration from common abstraction. + */ +#include "ioqueue_common_abs.h" + +/* + * ISSUES with ioqueue_select() + * + * EAGAIN/EWOULDBLOCK error in recv(): + * - when multiple threads are working with the ioqueue, application + * may receive EAGAIN or EWOULDBLOCK in the receive callback. + * This error happens because more than one thread is watching for + * the same descriptor set, so when all of them call recv() or recvfrom() + * simultaneously, only one will succeed and the rest will get the error. + * + */ +#define THIS_FILE "ioq_select" + +/* + * The select ioqueue relies on socket functions (pj_sock_xxx()) to return + * the correct error code. + */ +#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100) +# error "Error reporting must be enabled for this function to work!" +#endif + +/** + * Get the number of descriptors in the set. This is defined in sock_select.c + * This function will only return the number of sockets set from PJ_FD_SET + * operation. When the set is modified by other means (such as by select()), + * the count will not be reflected here. + * + * That's why don't export this function in the header file, to avoid + * misunderstanding. + * + * @param fdsetp The descriptor set. + * + * @return Number of descriptors in the set. + */ +PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp); + + +/* + * During debugging build, VALIDATE_FD_SET is set. + * This will check the validity of the fd_sets. + */ +/* +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif +*/ +#define VALIDATE_FD_SET 0 + +/* + * This describes each key. + */ +struct pj_ioqueue_key_t +{ + DECLARE_COMMON_KEY +}; + +/* + * This describes the I/O queue itself. + */ +struct pj_ioqueue_t +{ + DECLARE_COMMON_IOQUEUE + + unsigned max, count; + pj_ioqueue_key_t key_list; + pj_fd_set_t rfdset; + pj_fd_set_t wfdset; +#if PJ_HAS_TCP + pj_fd_set_t xfdset; +#endif +}; + +/* Include implementation for common abstraction after we declare + * pj_ioqueue_key_t and pj_ioqueue_t. + */ +#include "ioqueue_common_abs.c" + +/* + * pj_ioqueue_name() + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ + return "select"; +} + +/* + * pj_ioqueue_create() + * + * Create select ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioqueue; + pj_lock_t *lock; + pj_status_t rc; + + /* Check that arguments are valid. */ + PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && + max_fd > 0 && max_fd <= PJ_IOQUEUE_MAX_HANDLES, + PJ_EINVAL); + + /* Check that size of pj_ioqueue_op_key_t is sufficient */ + PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= + sizeof(union operation_key), PJ_EBUG); + + ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); + + ioqueue_init(ioqueue); + + ioqueue->max = max_fd; + ioqueue->count = 0; + PJ_FD_ZERO(&ioqueue->rfdset); + PJ_FD_ZERO(&ioqueue->wfdset); +#if PJ_HAS_TCP + PJ_FD_ZERO(&ioqueue->xfdset); +#endif + pj_list_init(&ioqueue->key_list); + + rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock); + if (rc != PJ_SUCCESS) + return rc; + + rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE); + if (rc != PJ_SUCCESS) + return rc; + + PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioqueue)); + + *p_ioqueue = ioqueue; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + * + * Destroy ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue) +{ + PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + return ioqueue_destroy(ioqueue); +} + + +/* + * pj_ioqueue_register_sock() + * + * Register a handle to ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key) +{ + pj_ioqueue_key_t *key = NULL; + pj_uint32_t value; + pj_status_t rc = PJ_SUCCESS; + + PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET && + cb && p_key, PJ_EINVAL); + + pj_lock_acquire(ioqueue->lock); + + if (ioqueue->count >= ioqueue->max) { + rc = PJ_ETOOMANY; + goto on_return; + } + + /* Set socket to nonblocking. */ + value = 1; +#ifdef PJ_WIN32 + if (ioctlsocket(sock, FIONBIO, (u_long*)&value)) { +#else + if (ioctl(sock, FIONBIO, &value)) { +#endif + rc = pj_get_netos_error(); + goto on_return; + } + + /* Create key. */ + key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); + rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb); + if (rc != PJ_SUCCESS) { + key = NULL; + goto on_return; + } + + /* Register */ + pj_list_insert_before(&ioqueue->key_list, key); + ++ioqueue->count; + +on_return: + /* On error, socket may be left in non-blocking mode. */ + *p_key = key; + pj_lock_release(ioqueue->lock); + + return rc; +} + +/* + * pj_ioqueue_unregister() + * + * Unregister handle from ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key) +{ + pj_ioqueue_t *ioqueue; + + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + ioqueue = key->ioqueue; + + pj_lock_acquire(ioqueue->lock); + + pj_assert(ioqueue->count > 0); + --ioqueue->count; + pj_list_erase(key); + PJ_FD_CLR(key->fd, &ioqueue->rfdset); + PJ_FD_CLR(key->fd, &ioqueue->wfdset); +#if PJ_HAS_TCP + PJ_FD_CLR(key->fd, &ioqueue->xfdset); +#endif + + /* ioqueue_destroy may try to acquire key's mutex. + * Since normally the order of locking is to lock key's mutex first + * then ioqueue's mutex, ioqueue_destroy may deadlock unless we + * release ioqueue's mutex first. + */ + pj_lock_release(ioqueue->lock); + + /* Destroy the key. */ + ioqueue_destroy_key(key); + + return PJ_SUCCESS; +} + + +/* This supposed to check whether the fd_set values are consistent + * with the operation currently set in each key. + */ +#if VALIDATE_FD_SET +static void validate_sets(const pj_ioqueue_t *ioqueue, + const pj_fd_set_t *rfdset, + const pj_fd_set_t *wfdset, + const pj_fd_set_t *xfdset) +{ + pj_ioqueue_key_t *key; + + /* + * This basicly would not work anymore. + * We need to lock key before performing the check, but we can't do + * so because we're holding ioqueue mutex. If we acquire key's mutex + * now, the will cause deadlock. + */ + pj_assert(0); + + key = ioqueue->key_list.next; + while (key != &ioqueue->key_list) { + if (!pj_list_empty(&key->read_list) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + || !pj_list_empty(&key->accept_list) +#endif + ) + { + pj_assert(PJ_FD_ISSET(key->fd, rfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0); + } + if (!pj_list_empty(&key->write_list) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + || key->connecting +#endif + ) + { + pj_assert(PJ_FD_ISSET(key->fd, wfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0); + } +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + if (key->connecting) + { + pj_assert(PJ_FD_ISSET(key->fd, xfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0); + } +#endif /* PJ_HAS_TCP */ + + key = key->next; + } +} +#endif /* VALIDATE_FD_SET */ + + +/* ioqueue_remove_from_set() + * This function is called from ioqueue_dispatch_event() to instruct + * the ioqueue to remove the specified descriptor from ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, + pj_sock_t fd, + enum ioqueue_event_type event_type) +{ + pj_lock_acquire(ioqueue->lock); + + if (event_type == READABLE_EVENT) + PJ_FD_CLR((pj_sock_t)fd, &ioqueue->rfdset); + else if (event_type == WRITEABLE_EVENT) + PJ_FD_CLR((pj_sock_t)fd, &ioqueue->wfdset); + else if (event_type == EXCEPTION_EVENT) + PJ_FD_CLR((pj_sock_t)fd, &ioqueue->xfdset); + else + pj_assert(0); + + pj_lock_release(ioqueue->lock); +} + +/* + * ioqueue_add_to_set() + * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc + * to instruct the ioqueue to add the specified handle to ioqueue's descriptor + * set for the specified event. + */ +static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_sock_t fd, + enum ioqueue_event_type event_type ) +{ + pj_lock_acquire(ioqueue->lock); + + if (event_type == READABLE_EVENT) + PJ_FD_SET((pj_sock_t)fd, &ioqueue->rfdset); + else if (event_type == WRITEABLE_EVENT) + PJ_FD_SET((pj_sock_t)fd, &ioqueue->wfdset); + else if (event_type == EXCEPTION_EVENT) + PJ_FD_SET((pj_sock_t)fd, &ioqueue->xfdset); + else + pj_assert(0); + + pj_lock_release(ioqueue->lock); +} + +/* + * pj_ioqueue_poll() + * + * Few things worth written: + * + * - we used to do only one callback called per poll, but it didn't go + * very well. The reason is because on some situation, the write + * callback gets called all the time, thus doesn't give the read + * callback to get called. This happens, for example, when user + * submit write operation inside the write callback. + * As the result, we changed the behaviour so that now multiple + * callbacks are called in a single poll. It should be fast too, + * just that we need to be carefull with the ioqueue data structs. + * + * - to guarantee preemptiveness etc, the poll function must strictly + * work on fd_set copy of the ioqueue (not the original one). + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) +{ + pj_fd_set_t rfdset, wfdset, xfdset; + int count, counter; + pj_ioqueue_key_t *h; + struct event + { + pj_ioqueue_key_t *key; + enum ioqueue_event_type event_type; + } event[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; + + PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); + + /* Lock ioqueue before making fd_set copies */ + pj_lock_acquire(ioqueue->lock); + + /* We will only do select() when there are sockets to be polled. + * Otherwise select() will return error. + */ + if (PJ_FD_COUNT(&ioqueue->rfdset)==0 && + PJ_FD_COUNT(&ioqueue->wfdset)==0 && + PJ_FD_COUNT(&ioqueue->xfdset)==0) + { + pj_lock_release(ioqueue->lock); + if (timeout) + pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout)); + return 0; + } + + /* Copy ioqueue's pj_fd_set_t to local variables. */ + pj_memcpy(&rfdset, &ioqueue->rfdset, sizeof(pj_fd_set_t)); + pj_memcpy(&wfdset, &ioqueue->wfdset, sizeof(pj_fd_set_t)); +#if PJ_HAS_TCP + pj_memcpy(&xfdset, &ioqueue->xfdset, sizeof(pj_fd_set_t)); +#else + PJ_FD_ZERO(&xfdset); +#endif + +#if VALIDATE_FD_SET + validate_sets(ioqueue, &rfdset, &wfdset, &xfdset); +#endif + + /* Unlock ioqueue before select(). */ + pj_lock_release(ioqueue->lock); + + count = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout); + + if (count <= 0) + return count; + else if (count > PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL) + count = PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL; + + /* Scan descriptor sets for event and add the events in the event + * array to be processed later in this function. We do this so that + * events can be processed in parallel without holding ioqueue lock. + */ + pj_lock_acquire(ioqueue->lock); + + counter = 0; + + /* Scan for writable sockets first to handle piggy-back data + * coming with accept(). + */ + h = ioqueue->key_list.next; + for ( ; h!=&ioqueue->key_list && counter<count; h = h->next) { + if ( (key_has_pending_write(h) || key_has_pending_connect(h)) + && PJ_FD_ISSET(h->fd, &wfdset)) + { + event[counter].key = h; + event[counter].event_type = WRITEABLE_EVENT; + ++counter; + } + + /* Scan for readable socket. */ + if ((key_has_pending_read(h) || key_has_pending_accept(h)) + && PJ_FD_ISSET(h->fd, &rfdset)) + { + event[counter].key = h; + event[counter].event_type = READABLE_EVENT; + ++counter; + } + +#if PJ_HAS_TCP + if (key_has_pending_connect(h) && PJ_FD_ISSET(h->fd, &xfdset)) { + event[counter].key = h; + event[counter].event_type = EXCEPTION_EVENT; + ++counter; + } +#endif + } + + pj_lock_release(ioqueue->lock); + + count = counter; + + /* Now process all events. The dispatch functions will take care + * of locking in each of the key + */ + for (counter=0; counter<count; ++counter) { + switch (event[counter].event_type) { + case READABLE_EVENT: + ioqueue_dispatch_read_event(ioqueue, event[counter].key); + break; + case WRITEABLE_EVENT: + ioqueue_dispatch_write_event(ioqueue, event[counter].key); + break; + case EXCEPTION_EVENT: + ioqueue_dispatch_exception_event(ioqueue, event[counter].key); + break; + case NO_EVENT: + pj_assert(!"Invalid event!"); + break; + } + } + + return count; +} + diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index 828b568e..e46d36a3 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -1,960 +1,960 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/sock.h>
-#include <pj/array.h>
-#include <pj/log.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-
-#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
-# include <winsock2.h>
-#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
-# include <winsock.h>
-#endif
-
-#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0
-# include <mswsock.h>
-#endif
-
-
-/* The address specified in AcceptEx() must be 16 more than the size of
- * SOCKADDR (source: MSDN).
- */
-#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+16)
-
-typedef struct generic_overlapped
-{
- WSAOVERLAPPED overlapped;
- pj_ioqueue_operation_e operation;
-} generic_overlapped;
-
-/*
- * OVERLAPPPED structure for send and receive.
- */
-typedef struct ioqueue_overlapped
-{
- WSAOVERLAPPED overlapped;
- pj_ioqueue_operation_e operation;
- WSABUF wsabuf;
- pj_sockaddr_in dummy_addr;
- int dummy_addrlen;
-} ioqueue_overlapped;
-
-#if PJ_HAS_TCP
-/*
- * OVERLAP structure for accept.
- */
-typedef struct ioqueue_accept_rec
-{
- WSAOVERLAPPED overlapped;
- pj_ioqueue_operation_e operation;
- pj_sock_t newsock;
- pj_sock_t *newsock_ptr;
- int *addrlen;
- void *remote;
- void *local;
- char accept_buf[2 * ACCEPT_ADDR_LEN];
-} ioqueue_accept_rec;
-#endif
-
-/*
- * Structure to hold pending operation key.
- */
-union operation_key
-{
- generic_overlapped generic;
- ioqueue_overlapped overlapped;
-#if PJ_HAS_TCP
- ioqueue_accept_rec accept;
-#endif
-};
-
-/* Type of handle in the key. */
-enum handle_type
-{
- HND_IS_UNKNOWN,
- HND_IS_FILE,
- HND_IS_SOCKET,
-};
-
-/*
- * Structure for individual socket.
- */
-struct pj_ioqueue_key_t
-{
- pj_ioqueue_t *ioqueue;
- HANDLE hnd;
- void *user_data;
- enum handle_type hnd_type;
-#if PJ_HAS_TCP
- int connecting;
-#endif
- pj_ioqueue_callback cb;
-};
-
-/*
- * IO Queue structure.
- */
-struct pj_ioqueue_t
-{
- HANDLE iocp;
- pj_lock_t *lock;
- pj_bool_t auto_delete_lock;
- unsigned event_count;
- HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1];
-#if PJ_HAS_TCP
- unsigned connecting_count;
- HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1];
- pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1];
-#endif
-};
-
-
-#if PJ_HAS_TCP
-/*
- * Process the socket when the overlapped accept() completed.
- */
-static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped)
-{
- struct sockaddr *local;
- struct sockaddr *remote;
- int locallen, remotelen;
-
- PJ_CHECK_STACK();
-
- /* Operation complete immediately. */
- GetAcceptExSockaddrs( accept_overlapped->accept_buf,
- 0,
- ACCEPT_ADDR_LEN,
- ACCEPT_ADDR_LEN,
- &local,
- &locallen,
- &remote,
- &remotelen);
- if (*accept_overlapped->addrlen > locallen) {
- pj_memcpy(accept_overlapped->local, local, locallen);
- pj_memcpy(accept_overlapped->remote, remote, locallen);
- } else {
- pj_memset(accept_overlapped->local, 0, *accept_overlapped->addrlen);
- pj_memset(accept_overlapped->remote, 0, *accept_overlapped->addrlen);
- }
- *accept_overlapped->addrlen = locallen;
- if (accept_overlapped->newsock_ptr)
- *accept_overlapped->newsock_ptr = accept_overlapped->newsock;
- accept_overlapped->operation = 0;
- accept_overlapped->newsock = PJ_INVALID_SOCKET;
-}
-
-static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos)
-{
- pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos];
- HANDLE hEvent = ioqueue->connecting_handles[pos];
-
- /* Remove key from array of connecting handles. */
- pj_array_erase(ioqueue->connecting_keys, sizeof(key),
- ioqueue->connecting_count, pos);
- pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE),
- ioqueue->connecting_count, pos);
- --ioqueue->connecting_count;
-
- /* Disassociate the socket from the event. */
- WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0);
-
- /* Put event object to pool. */
- if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) {
- ioqueue->event_pool[ioqueue->event_count++] = hEvent;
- } else {
- /* Shouldn't happen. There should be no more pending connections
- * than max.
- */
- pj_assert(0);
- CloseHandle(hEvent);
- }
-
-}
-
-/*
- * Poll for the completion of non-blocking connect().
- * If there's a completion, the function return the key of the completed
- * socket, and 'result' argument contains the connect() result. If connect()
- * succeeded, 'result' will have value zero, otherwise will have the error
- * code.
- */
-static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue,
- pj_ssize_t *connect_err )
-{
- pj_ioqueue_key_t *key = NULL;
-
- if (ioqueue->connecting_count) {
- DWORD result;
-
- pj_lock_acquire(ioqueue->lock);
- result = WaitForMultipleObjects(ioqueue->connecting_count,
- ioqueue->connecting_handles,
- FALSE, 0);
- if (result >= WAIT_OBJECT_0 &&
- result < WAIT_OBJECT_0+ioqueue->connecting_count)
- {
- WSANETWORKEVENTS net_events;
-
- /* Got completed connect(). */
- unsigned pos = result - WAIT_OBJECT_0;
- key = ioqueue->connecting_keys[pos];
-
- /* See whether connect has succeeded. */
- WSAEnumNetworkEvents((pj_sock_t)key->hnd,
- ioqueue->connecting_handles[pos],
- &net_events);
- *connect_err =
- PJ_STATUS_FROM_OS(net_events.iErrorCode[FD_CONNECT_BIT]);
-
- /* Erase socket from pending connect. */
- erase_connecting_socket(ioqueue, pos);
- }
- pj_lock_release(ioqueue->lock);
- }
- return key;
-}
-#endif
-
-/*
- * pj_ioqueue_name()
- */
-PJ_DEF(const char*) pj_ioqueue_name(void)
-{
- return "iocp";
-}
-
-/*
- * pj_ioqueue_create()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **p_ioqueue)
-{
- pj_ioqueue_t *ioqueue;
- pj_status_t rc;
-
- PJ_UNUSED_ARG(max_fd);
- PJ_ASSERT_RETURN(pool && p_ioqueue, PJ_EINVAL);
-
- rc = sizeof(union operation_key);
-
- /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */
- PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
- sizeof(union operation_key), PJ_EBUG);
-
- ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue));
- ioqueue->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
- if (ioqueue->iocp == NULL)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- rc = pj_lock_create_simple_mutex(pool, NULL, &ioqueue->lock);
- if (rc != PJ_SUCCESS) {
- CloseHandle(ioqueue->iocp);
- return rc;
- }
-
- ioqueue->auto_delete_lock = PJ_TRUE;
-
- *p_ioqueue = ioqueue;
-
- PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue));
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_destroy()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue )
-{
- unsigned i;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
-
- /* Destroy events in the pool */
- for (i=0; i<ioqueue->event_count; ++i) {
- CloseHandle(ioqueue->event_pool[i]);
- }
- ioqueue->event_count = 0;
-
- if (CloseHandle(ioqueue->iocp) != TRUE)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- if (ioqueue->auto_delete_lock)
- pj_lock_destroy(ioqueue->lock);
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_set_lock()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue,
- pj_lock_t *lock,
- pj_bool_t auto_delete )
-{
- PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
-
- if (ioqueue->auto_delete_lock) {
- pj_lock_destroy(ioqueue->lock);
- }
-
- ioqueue->lock = lock;
- ioqueue->auto_delete_lock = auto_delete;
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_register_sock()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **key )
-{
- HANDLE hioq;
- pj_ioqueue_key_t *rec;
- u_long value;
- int rc;
-
- PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL);
-
- /* Build the key for this socket. */
- rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
- rec->ioqueue = ioqueue;
- rec->hnd = (HANDLE)sock;
- rec->hnd_type = HND_IS_SOCKET;
- rec->user_data = user_data;
- pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
-
- /* Set socket to nonblocking. */
- value = 1;
- rc = ioctlsocket(sock, FIONBIO, &value);
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(WSAGetLastError());
- }
-
- /* Associate with IOCP */
- hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0);
- if (!hioq) {
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *key = rec;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_unregister()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key )
-{
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
-#if PJ_HAS_TCP
- if (key->connecting) {
- unsigned pos;
- pj_ioqueue_t *ioqueue;
-
- ioqueue = key->ioqueue;
-
- /* Erase from connecting_handles */
- pj_lock_acquire(ioqueue->lock);
- for (pos=0; pos < ioqueue->connecting_count; ++pos) {
- if (ioqueue->connecting_keys[pos] == key) {
- erase_connecting_socket(ioqueue, pos);
- break;
- }
- }
- key->connecting = 0;
- pj_lock_release(ioqueue->lock);
- }
-#endif
- if (key->hnd_type == HND_IS_FILE) {
- CloseHandle(key->hnd);
- }
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_get_user_data()
- */
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- PJ_ASSERT_RETURN(key, NULL);
- return key->user_data;
-}
-
-/*
- * pj_ioqueue_set_user_data()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
- void *user_data,
- void **old_data )
-{
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
- if (old_data)
- *old_data = key->user_data;
-
- key->user_data = user_data;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_poll()
- *
- * Poll for events.
- */
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
-{
- DWORD dwMsec, dwBytesTransfered, dwKey;
- generic_overlapped *pOv;
- pj_ioqueue_key_t *key;
- pj_ssize_t size_status;
- BOOL rc;
-
- PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL);
-
- /* Check the connecting array. */
-#if PJ_HAS_TCP
- key = check_connecting(ioqueue, &size_status);
- if (key != NULL) {
- key->cb.on_connect_complete(key, (int)size_status);
- return 1;
- }
-#endif
-
- /* Calculate miliseconds timeout for GetQueuedCompletionStatus */
- dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE;
-
- /* Poll for completion status. */
- rc = GetQueuedCompletionStatus(ioqueue->iocp, &dwBytesTransfered, &dwKey,
- (OVERLAPPED**)&pOv, dwMsec);
-
- /* The return value is:
- * - nonzero if event was dequeued.
- * - zero and pOv==NULL if no event was dequeued.
- * - zero and pOv!=NULL if event for failed I/O was dequeued.
- */
- if (pOv) {
- /* Event was dequeued for either successfull or failed I/O */
- key = (pj_ioqueue_key_t*)dwKey;
- size_status = dwBytesTransfered;
- switch (pOv->operation) {
- case PJ_IOQUEUE_OP_READ:
- case PJ_IOQUEUE_OP_RECV:
- case PJ_IOQUEUE_OP_RECV_FROM:
- pOv->operation = 0;
- if (key->cb.on_read_complete)
- key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv,
- size_status);
- break;
- case PJ_IOQUEUE_OP_WRITE:
- case PJ_IOQUEUE_OP_SEND:
- case PJ_IOQUEUE_OP_SEND_TO:
- pOv->operation = 0;
- if (key->cb.on_write_complete)
- key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv,
- size_status);
- break;
-#if PJ_HAS_TCP
- case PJ_IOQUEUE_OP_ACCEPT:
- /* special case for accept. */
- ioqueue_on_accept_complete((ioqueue_accept_rec*)pOv);
- if (key->cb.on_accept_complete) {
- ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv;
- key->cb.on_accept_complete(key,
- (pj_ioqueue_op_key_t*)pOv,
- accept_rec->newsock,
- PJ_SUCCESS);
- }
- break;
- case PJ_IOQUEUE_OP_CONNECT:
-#endif
- case PJ_IOQUEUE_OP_NONE:
- pj_assert(0);
- break;
- }
- return 1;
- }
-
- if (GetLastError()==WAIT_TIMEOUT) {
- /* Check the connecting array (again). */
-#if PJ_HAS_TCP
- key = check_connecting(ioqueue, &size_status);
- if (key != NULL) {
- key->cb.on_connect_complete(key, (int)size_status);
- return 1;
- }
-#endif
- return 0;
- }
- return -1;
-}
-
-/*
- * pj_ioqueue_recv()
- *
- * Initiate overlapped WSARecv() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- pj_uint32_t flags )
-{
- /*
- * Ideally we should just call pj_ioqueue_recvfrom() with NULL addr and
- * addrlen here. But unfortunately it generates EINVAL... :-(
- * -bennylp
- */
- int rc;
- DWORD bytesRead;
- DWORD dwFlags = 0;
- union operation_key *op_key_rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
-
- op_key_rec = (union operation_key*)op_key->internal__;
- op_key_rec->overlapped.wsabuf.buf = buffer;
- op_key_rec->overlapped.wsabuf.len = *length;
-
- dwFlags = flags;
-
- /* Try non-overlapped received first to see if data is
- * immediately available.
- */
- if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
- rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags, NULL, NULL);
- if (rc == 0) {
- *length = bytesRead;
- return PJ_SUCCESS;
- } else {
- DWORD dwError = WSAGetLastError();
- if (dwError != WSAEWOULDBLOCK) {
- *length = -1;
- return PJ_RETURN_OS_ERROR(dwError);
- }
- }
- }
-
- dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
-
- /*
- * No immediate data available.
- * Register overlapped Recv() operation.
- */
- pj_memset(&op_key_rec->overlapped.overlapped, 0,
- sizeof(op_key_rec->overlapped.overlapped));
- op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
-
- rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags,
- &op_key_rec->overlapped.overlapped, NULL);
- if (rc == SOCKET_ERROR) {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING) {
- *length = -1;
- return PJ_STATUS_FROM_OS(dwStatus);
- }
- }
-
- /* Pending operation has been scheduled. */
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_recvfrom()
- *
- * Initiate overlapped RecvFrom() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- pj_uint32_t flags,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- int rc;
- DWORD bytesRead;
- DWORD dwFlags = 0;
- union operation_key *op_key_rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL);
-
- op_key_rec = (union operation_key*)op_key->internal__;
- op_key_rec->overlapped.wsabuf.buf = buffer;
- op_key_rec->overlapped.wsabuf.len = *length;
-
- dwFlags = flags;
-
- /* Try non-overlapped received first to see if data is
- * immediately available.
- */
- if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
- rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags, addr, addrlen, NULL, NULL);
- if (rc == 0) {
- *length = bytesRead;
- return PJ_SUCCESS;
- } else {
- DWORD dwError = WSAGetLastError();
- if (dwError != WSAEWOULDBLOCK) {
- *length = -1;
- return PJ_RETURN_OS_ERROR(dwError);
- }
- }
- }
-
- dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
-
- /*
- * No immediate data available.
- * Register overlapped Recv() operation.
- */
- pj_memset(&op_key_rec->overlapped.overlapped, 0,
- sizeof(op_key_rec->overlapped.overlapped));
- op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
-
- rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags, addr, addrlen,
- &op_key_rec->overlapped.overlapped, NULL);
- if (rc == SOCKET_ERROR) {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING) {
- *length = -1;
- return PJ_STATUS_FROM_OS(dwStatus);
- }
- }
-
- /* Pending operation has been scheduled. */
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_send()
- *
- * Initiate overlapped Send operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- pj_uint32_t flags )
-{
- return pj_ioqueue_sendto(key, op_key, data, length, flags, NULL, 0);
-}
-
-
-/*
- * pj_ioqueue_sendto()
- *
- * Initiate overlapped SendTo operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- pj_uint32_t flags,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- int rc;
- DWORD bytesWritten;
- DWORD dwFlags;
- union operation_key *op_key_rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL);
-
- op_key_rec = (union operation_key*)op_key->internal__;
-
- /*
- * First try blocking write.
- */
- op_key_rec->overlapped.wsabuf.buf = (void*)data;
- op_key_rec->overlapped.wsabuf.len = *length;
-
- dwFlags = flags;
-
- if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
- rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesWritten, dwFlags, addr, addrlen,
- NULL, NULL);
- if (rc == 0) {
- *length = bytesWritten;
- return PJ_SUCCESS;
- } else {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus != WSAEWOULDBLOCK) {
- *length = -1;
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
- }
- }
-
- dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
-
- /*
- * Data can't be sent immediately.
- * Schedule asynchronous WSASend().
- */
- pj_memset(&op_key_rec->overlapped.overlapped, 0,
- sizeof(op_key_rec->overlapped.overlapped));
- op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND;
-
- rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesWritten, dwFlags, addr, addrlen,
- &op_key_rec->overlapped.overlapped, NULL);
- if (rc == SOCKET_ERROR) {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING)
- return PJ_STATUS_FROM_OS(dwStatus);
- }
-
- /* Asynchronous operation successfully submitted. */
- return PJ_EPENDING;
-}
-
-#if PJ_HAS_TCP
-
-/*
- * pj_ioqueue_accept()
- *
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- BOOL rc;
- DWORD bytesReceived;
- pj_status_t status;
- union operation_key *op_key_rec;
- SOCKET sock;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
-
- /*
- * See if there is a new connection immediately available.
- */
- sock = WSAAccept((SOCKET)key->hnd, remote, addrlen, NULL, 0);
- if (sock != INVALID_SOCKET) {
- /* Yes! New socket is available! */
- int status;
-
- status = getsockname(sock, local, addrlen);
- if (status != 0) {
- DWORD dwError = WSAGetLastError();
- closesocket(sock);
- return PJ_RETURN_OS_ERROR(dwError);
- }
-
- *new_sock = sock;
- return PJ_SUCCESS;
-
- } else {
- DWORD dwError = WSAGetLastError();
- if (dwError != WSAEWOULDBLOCK) {
- return PJ_RETURN_OS_ERROR(dwError);
- }
- }
-
- /*
- * No connection is immediately available.
- * Must schedule an asynchronous operation.
- */
- op_key_rec = (union operation_key*)op_key->internal__;
-
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0,
- &op_key_rec->accept.newsock);
- if (status != PJ_SUCCESS)
- return status;
-
- /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket
- * addresses can be obtained with getsockname() and getpeername().
- */
- status = setsockopt(op_key_rec->accept.newsock, SOL_SOCKET,
- SO_UPDATE_ACCEPT_CONTEXT,
- (char*)&key->hnd, sizeof(SOCKET));
- /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later.
- * So ignore the error status.
- */
-
- op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT;
- op_key_rec->accept.addrlen = addrlen;
- op_key_rec->accept.local = local;
- op_key_rec->accept.remote = remote;
- op_key_rec->accept.newsock_ptr = new_sock;
- pj_memset(&op_key_rec->accept.overlapped, 0,
- sizeof(op_key_rec->accept.overlapped));
-
- rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)op_key_rec->accept.newsock,
- op_key_rec->accept.accept_buf,
- 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN,
- &bytesReceived,
- &op_key_rec->accept.overlapped );
-
- if (rc == TRUE) {
- ioqueue_on_accept_complete(&op_key_rec->accept);
- return PJ_SUCCESS;
- } else {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING)
- return PJ_STATUS_FROM_OS(dwStatus);
- }
-
- /* Asynchronous Accept() has been submitted. */
- return PJ_EPENDING;
-}
-
-
-/*
- * pj_ioqueue_connect()
- *
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- HANDLE hEvent;
- pj_ioqueue_t *ioqueue;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
-
- /* Initiate connect() */
- if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) {
- DWORD dwStatus;
- dwStatus = WSAGetLastError();
- if (dwStatus != WSAEWOULDBLOCK) {
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
- } else {
- /* Connect has completed immediately! */
- return PJ_SUCCESS;
- }
-
- ioqueue = key->ioqueue;
-
- /* Add to the array of connecting socket to be polled */
- pj_lock_acquire(ioqueue->lock);
-
- if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) {
- pj_lock_release(ioqueue->lock);
- return PJ_ETOOMANYCONN;
- }
-
- /* Get or create event object. */
- if (ioqueue->event_count) {
- hEvent = ioqueue->event_pool[ioqueue->event_count - 1];
- --ioqueue->event_count;
- } else {
- hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (hEvent == NULL) {
- DWORD dwStatus = GetLastError();
- pj_lock_release(ioqueue->lock);
- return PJ_STATUS_FROM_OS(dwStatus);
- }
- }
-
- /* Mark key as connecting.
- * We can't use array index since key can be removed dynamically.
- */
- key->connecting = 1;
-
- /* Associate socket events to the event object. */
- if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) {
- CloseHandle(hEvent);
- pj_lock_release(ioqueue->lock);
- return PJ_RETURN_OS_ERROR(WSAGetLastError());
- }
-
- /* Add to array. */
- ioqueue->connecting_keys[ ioqueue->connecting_count ] = key;
- ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent;
- ioqueue->connecting_count++;
-
- pj_lock_release(ioqueue->lock);
-
- return PJ_EPENDING;
-}
-#endif /* #if PJ_HAS_TCP */
-
-
-PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
- pj_size_t size )
-{
- pj_memset(op_key, 0, size);
-}
-
-PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key )
-{
- BOOL rc;
- DWORD bytesTransfered;
-
- rc = GetOverlappedResult( key->hnd, (LPOVERLAPPED)op_key,
- &bytesTransfered, FALSE );
-
- if (rc == FALSE) {
- return GetLastError()==ERROR_IO_INCOMPLETE;
- }
-
- return FALSE;
-}
-
-
-PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_status )
-{
- BOOL rc;
-
- rc = PostQueuedCompletionStatus(key->ioqueue->iocp, bytes_status,
- (long)key, (OVERLAPPED*)op_key );
- if (rc == FALSE) {
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- return PJ_SUCCESS;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/ioqueue.h> +#include <pj/os.h> +#include <pj/lock.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/sock.h> +#include <pj/array.h> +#include <pj/log.h> +#include <pj/assert.h> +#include <pj/errno.h> + + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include <winsock2.h> +#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include <winsock.h> +#endif + +#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0 +# include <mswsock.h> +#endif + + +/* The address specified in AcceptEx() must be 16 more than the size of + * SOCKADDR (source: MSDN). + */ +#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+16) + +typedef struct generic_overlapped +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; +} generic_overlapped; + +/* + * OVERLAPPPED structure for send and receive. + */ +typedef struct ioqueue_overlapped +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; + WSABUF wsabuf; + pj_sockaddr_in dummy_addr; + int dummy_addrlen; +} ioqueue_overlapped; + +#if PJ_HAS_TCP +/* + * OVERLAP structure for accept. + */ +typedef struct ioqueue_accept_rec +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; + pj_sock_t newsock; + pj_sock_t *newsock_ptr; + int *addrlen; + void *remote; + void *local; + char accept_buf[2 * ACCEPT_ADDR_LEN]; +} ioqueue_accept_rec; +#endif + +/* + * Structure to hold pending operation key. + */ +union operation_key +{ + generic_overlapped generic; + ioqueue_overlapped overlapped; +#if PJ_HAS_TCP + ioqueue_accept_rec accept; +#endif +}; + +/* Type of handle in the key. */ +enum handle_type +{ + HND_IS_UNKNOWN, + HND_IS_FILE, + HND_IS_SOCKET, +}; + +/* + * Structure for individual socket. + */ +struct pj_ioqueue_key_t +{ + pj_ioqueue_t *ioqueue; + HANDLE hnd; + void *user_data; + enum handle_type hnd_type; +#if PJ_HAS_TCP + int connecting; +#endif + pj_ioqueue_callback cb; +}; + +/* + * IO Queue structure. + */ +struct pj_ioqueue_t +{ + HANDLE iocp; + pj_lock_t *lock; + pj_bool_t auto_delete_lock; + unsigned event_count; + HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1]; +#if PJ_HAS_TCP + unsigned connecting_count; + HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1]; + pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1]; +#endif +}; + + +#if PJ_HAS_TCP +/* + * Process the socket when the overlapped accept() completed. + */ +static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped) +{ + struct sockaddr *local; + struct sockaddr *remote; + int locallen, remotelen; + + PJ_CHECK_STACK(); + + /* Operation complete immediately. */ + GetAcceptExSockaddrs( accept_overlapped->accept_buf, + 0, + ACCEPT_ADDR_LEN, + ACCEPT_ADDR_LEN, + &local, + &locallen, + &remote, + &remotelen); + if (*accept_overlapped->addrlen > locallen) { + pj_memcpy(accept_overlapped->local, local, locallen); + pj_memcpy(accept_overlapped->remote, remote, locallen); + } else { + pj_memset(accept_overlapped->local, 0, *accept_overlapped->addrlen); + pj_memset(accept_overlapped->remote, 0, *accept_overlapped->addrlen); + } + *accept_overlapped->addrlen = locallen; + if (accept_overlapped->newsock_ptr) + *accept_overlapped->newsock_ptr = accept_overlapped->newsock; + accept_overlapped->operation = 0; + accept_overlapped->newsock = PJ_INVALID_SOCKET; +} + +static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos) +{ + pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos]; + HANDLE hEvent = ioqueue->connecting_handles[pos]; + + /* Remove key from array of connecting handles. */ + pj_array_erase(ioqueue->connecting_keys, sizeof(key), + ioqueue->connecting_count, pos); + pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE), + ioqueue->connecting_count, pos); + --ioqueue->connecting_count; + + /* Disassociate the socket from the event. */ + WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0); + + /* Put event object to pool. */ + if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) { + ioqueue->event_pool[ioqueue->event_count++] = hEvent; + } else { + /* Shouldn't happen. There should be no more pending connections + * than max. + */ + pj_assert(0); + CloseHandle(hEvent); + } + +} + +/* + * Poll for the completion of non-blocking connect(). + * If there's a completion, the function return the key of the completed + * socket, and 'result' argument contains the connect() result. If connect() + * succeeded, 'result' will have value zero, otherwise will have the error + * code. + */ +static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue, + pj_ssize_t *connect_err ) +{ + pj_ioqueue_key_t *key = NULL; + + if (ioqueue->connecting_count) { + DWORD result; + + pj_lock_acquire(ioqueue->lock); + result = WaitForMultipleObjects(ioqueue->connecting_count, + ioqueue->connecting_handles, + FALSE, 0); + if (result >= WAIT_OBJECT_0 && + result < WAIT_OBJECT_0+ioqueue->connecting_count) + { + WSANETWORKEVENTS net_events; + + /* Got completed connect(). */ + unsigned pos = result - WAIT_OBJECT_0; + key = ioqueue->connecting_keys[pos]; + + /* See whether connect has succeeded. */ + WSAEnumNetworkEvents((pj_sock_t)key->hnd, + ioqueue->connecting_handles[pos], + &net_events); + *connect_err = + PJ_STATUS_FROM_OS(net_events.iErrorCode[FD_CONNECT_BIT]); + + /* Erase socket from pending connect. */ + erase_connecting_socket(ioqueue, pos); + } + pj_lock_release(ioqueue->lock); + } + return key; +} +#endif + +/* + * pj_ioqueue_name() + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ + return "iocp"; +} + +/* + * pj_ioqueue_create() + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioqueue; + pj_status_t rc; + + PJ_UNUSED_ARG(max_fd); + PJ_ASSERT_RETURN(pool && p_ioqueue, PJ_EINVAL); + + rc = sizeof(union operation_key); + + /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */ + PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= + sizeof(union operation_key), PJ_EBUG); + + ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue)); + ioqueue->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (ioqueue->iocp == NULL) + return PJ_RETURN_OS_ERROR(GetLastError()); + + rc = pj_lock_create_simple_mutex(pool, NULL, &ioqueue->lock); + if (rc != PJ_SUCCESS) { + CloseHandle(ioqueue->iocp); + return rc; + } + + ioqueue->auto_delete_lock = PJ_TRUE; + + *p_ioqueue = ioqueue; + + PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue)); + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue ) +{ + unsigned i; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); + + /* Destroy events in the pool */ + for (i=0; i<ioqueue->event_count; ++i) { + CloseHandle(ioqueue->event_pool[i]); + } + ioqueue->event_count = 0; + + if (CloseHandle(ioqueue->iocp) != TRUE) + return PJ_RETURN_OS_ERROR(GetLastError()); + + if (ioqueue->auto_delete_lock) + pj_lock_destroy(ioqueue->lock); + + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_set_lock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL); + + if (ioqueue->auto_delete_lock) { + pj_lock_destroy(ioqueue->lock); + } + + ioqueue->lock = lock; + ioqueue->auto_delete_lock = auto_delete; + + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_register_sock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **key ) +{ + HANDLE hioq; + pj_ioqueue_key_t *rec; + u_long value; + int rc; + + PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL); + + /* Build the key for this socket. */ + rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); + rec->ioqueue = ioqueue; + rec->hnd = (HANDLE)sock; + rec->hnd_type = HND_IS_SOCKET; + rec->user_data = user_data; + pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); + + /* Set socket to nonblocking. */ + value = 1; + rc = ioctlsocket(sock, FIONBIO, &value); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Associate with IOCP */ + hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0); + if (!hioq) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *key = rec; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_unregister() + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key, PJ_EINVAL); + +#if PJ_HAS_TCP + if (key->connecting) { + unsigned pos; + pj_ioqueue_t *ioqueue; + + ioqueue = key->ioqueue; + + /* Erase from connecting_handles */ + pj_lock_acquire(ioqueue->lock); + for (pos=0; pos < ioqueue->connecting_count; ++pos) { + if (ioqueue->connecting_keys[pos] == key) { + erase_connecting_socket(ioqueue, pos); + break; + } + } + key->connecting = 0; + pj_lock_release(ioqueue->lock); + } +#endif + if (key->hnd_type == HND_IS_FILE) { + CloseHandle(key->hnd); + } + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_get_user_data() + */ +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key, NULL); + return key->user_data; +} + +/* + * pj_ioqueue_set_user_data() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, + void *user_data, + void **old_data ) +{ + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + if (old_data) + *old_data = key->user_data; + + key->user_data = user_data; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_poll() + * + * Poll for events. + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) +{ + DWORD dwMsec, dwBytesTransfered, dwKey; + generic_overlapped *pOv; + pj_ioqueue_key_t *key; + pj_ssize_t size_status; + BOOL rc; + + PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL); + + /* Check the connecting array. */ +#if PJ_HAS_TCP + key = check_connecting(ioqueue, &size_status); + if (key != NULL) { + key->cb.on_connect_complete(key, (int)size_status); + return 1; + } +#endif + + /* Calculate miliseconds timeout for GetQueuedCompletionStatus */ + dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE; + + /* Poll for completion status. */ + rc = GetQueuedCompletionStatus(ioqueue->iocp, &dwBytesTransfered, &dwKey, + (OVERLAPPED**)&pOv, dwMsec); + + /* The return value is: + * - nonzero if event was dequeued. + * - zero and pOv==NULL if no event was dequeued. + * - zero and pOv!=NULL if event for failed I/O was dequeued. + */ + if (pOv) { + /* Event was dequeued for either successfull or failed I/O */ + key = (pj_ioqueue_key_t*)dwKey; + size_status = dwBytesTransfered; + switch (pOv->operation) { + case PJ_IOQUEUE_OP_READ: + case PJ_IOQUEUE_OP_RECV: + case PJ_IOQUEUE_OP_RECV_FROM: + pOv->operation = 0; + if (key->cb.on_read_complete) + key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv, + size_status); + break; + case PJ_IOQUEUE_OP_WRITE: + case PJ_IOQUEUE_OP_SEND: + case PJ_IOQUEUE_OP_SEND_TO: + pOv->operation = 0; + if (key->cb.on_write_complete) + key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv, + size_status); + break; +#if PJ_HAS_TCP + case PJ_IOQUEUE_OP_ACCEPT: + /* special case for accept. */ + ioqueue_on_accept_complete((ioqueue_accept_rec*)pOv); + if (key->cb.on_accept_complete) { + ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv; + key->cb.on_accept_complete(key, + (pj_ioqueue_op_key_t*)pOv, + accept_rec->newsock, + PJ_SUCCESS); + } + break; + case PJ_IOQUEUE_OP_CONNECT: +#endif + case PJ_IOQUEUE_OP_NONE: + pj_assert(0); + break; + } + return 1; + } + + if (GetLastError()==WAIT_TIMEOUT) { + /* Check the connecting array (again). */ +#if PJ_HAS_TCP + key = check_connecting(ioqueue, &size_status); + if (key != NULL) { + key->cb.on_connect_complete(key, (int)size_status); + return 1; + } +#endif + return 0; + } + return -1; +} + +/* + * pj_ioqueue_recv() + * + * Initiate overlapped WSARecv() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags ) +{ + /* + * Ideally we should just call pj_ioqueue_recvfrom() with NULL addr and + * addrlen here. But unfortunately it generates EINVAL... :-( + * -bennylp + */ + int rc; + DWORD bytesRead; + DWORD dwFlags = 0; + union operation_key *op_key_rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); + + op_key_rec = (union operation_key*)op_key->internal__; + op_key_rec->overlapped.wsabuf.buf = buffer; + op_key_rec->overlapped.wsabuf.len = *length; + + dwFlags = flags; + + /* Try non-overlapped received first to see if data is + * immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, NULL, NULL); + if (rc == 0) { + *length = bytesRead; + return PJ_SUCCESS; + } else { + DWORD dwError = WSAGetLastError(); + if (dwError != WSAEWOULDBLOCK) { + *length = -1; + return PJ_RETURN_OS_ERROR(dwError); + } + } + } + + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No immediate data available. + * Register overlapped Recv() operation. + */ + pj_memset(&op_key_rec->overlapped.overlapped, 0, + sizeof(op_key_rec->overlapped.overlapped)); + op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; + + rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, + &op_key_rec->overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) { + *length = -1; + return PJ_STATUS_FROM_OS(dwStatus); + } + } + + /* Pending operation has been scheduled. */ + return PJ_EPENDING; +} + +/* + * pj_ioqueue_recvfrom() + * + * Initiate overlapped RecvFrom() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + void *buffer, + pj_ssize_t *length, + pj_uint32_t flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + int rc; + DWORD bytesRead; + DWORD dwFlags = 0; + union operation_key *op_key_rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL); + + op_key_rec = (union operation_key*)op_key->internal__; + op_key_rec->overlapped.wsabuf.buf = buffer; + op_key_rec->overlapped.wsabuf.len = *length; + + dwFlags = flags; + + /* Try non-overlapped received first to see if data is + * immediately available. + */ + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, addr, addrlen, NULL, NULL); + if (rc == 0) { + *length = bytesRead; + return PJ_SUCCESS; + } else { + DWORD dwError = WSAGetLastError(); + if (dwError != WSAEWOULDBLOCK) { + *length = -1; + return PJ_RETURN_OS_ERROR(dwError); + } + } + } + + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * No immediate data available. + * Register overlapped Recv() operation. + */ + pj_memset(&op_key_rec->overlapped.overlapped, 0, + sizeof(op_key_rec->overlapped.overlapped)); + op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; + + rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesRead, &dwFlags, addr, addrlen, + &op_key_rec->overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) { + *length = -1; + return PJ_STATUS_FROM_OS(dwStatus); + } + } + + /* Pending operation has been scheduled. */ + return PJ_EPENDING; +} + +/* + * pj_ioqueue_send() + * + * Initiate overlapped Send operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags ) +{ + return pj_ioqueue_sendto(key, op_key, data, length, flags, NULL, 0); +} + + +/* + * pj_ioqueue_sendto() + * + * Initiate overlapped SendTo operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + const void *data, + pj_ssize_t *length, + pj_uint32_t flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + int rc; + DWORD bytesWritten; + DWORD dwFlags; + union operation_key *op_key_rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL); + + op_key_rec = (union operation_key*)op_key->internal__; + + /* + * First try blocking write. + */ + op_key_rec->overlapped.wsabuf.buf = (void*)data; + op_key_rec->overlapped.wsabuf.len = *length; + + dwFlags = flags; + + if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { + rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesWritten, dwFlags, addr, addrlen, + NULL, NULL); + if (rc == 0) { + *length = bytesWritten; + return PJ_SUCCESS; + } else { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus != WSAEWOULDBLOCK) { + *length = -1; + return PJ_RETURN_OS_ERROR(dwStatus); + } + } + } + + dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); + + /* + * Data can't be sent immediately. + * Schedule asynchronous WSASend(). + */ + pj_memset(&op_key_rec->overlapped.overlapped, 0, + sizeof(op_key_rec->overlapped.overlapped)); + op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND; + + rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, + &bytesWritten, dwFlags, addr, addrlen, + &op_key_rec->overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) + return PJ_STATUS_FROM_OS(dwStatus); + } + + /* Asynchronous operation successfully submitted. */ + return PJ_EPENDING; +} + +#if PJ_HAS_TCP + +/* + * pj_ioqueue_accept() + * + * Initiate overlapped accept() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + BOOL rc; + DWORD bytesReceived; + pj_status_t status; + union operation_key *op_key_rec; + SOCKET sock; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); + + /* + * See if there is a new connection immediately available. + */ + sock = WSAAccept((SOCKET)key->hnd, remote, addrlen, NULL, 0); + if (sock != INVALID_SOCKET) { + /* Yes! New socket is available! */ + int status; + + status = getsockname(sock, local, addrlen); + if (status != 0) { + DWORD dwError = WSAGetLastError(); + closesocket(sock); + return PJ_RETURN_OS_ERROR(dwError); + } + + *new_sock = sock; + return PJ_SUCCESS; + + } else { + DWORD dwError = WSAGetLastError(); + if (dwError != WSAEWOULDBLOCK) { + return PJ_RETURN_OS_ERROR(dwError); + } + } + + /* + * No connection is immediately available. + * Must schedule an asynchronous operation. + */ + op_key_rec = (union operation_key*)op_key->internal__; + + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, + &op_key_rec->accept.newsock); + if (status != PJ_SUCCESS) + return status; + + /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket + * addresses can be obtained with getsockname() and getpeername(). + */ + status = setsockopt(op_key_rec->accept.newsock, SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char*)&key->hnd, sizeof(SOCKET)); + /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later. + * So ignore the error status. + */ + + op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT; + op_key_rec->accept.addrlen = addrlen; + op_key_rec->accept.local = local; + op_key_rec->accept.remote = remote; + op_key_rec->accept.newsock_ptr = new_sock; + pj_memset(&op_key_rec->accept.overlapped, 0, + sizeof(op_key_rec->accept.overlapped)); + + rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)op_key_rec->accept.newsock, + op_key_rec->accept.accept_buf, + 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN, + &bytesReceived, + &op_key_rec->accept.overlapped ); + + if (rc == TRUE) { + ioqueue_on_accept_complete(&op_key_rec->accept); + return PJ_SUCCESS; + } else { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus!=WSA_IO_PENDING) + return PJ_STATUS_FROM_OS(dwStatus); + } + + /* Asynchronous Accept() has been submitted. */ + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_connect() + * + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + HANDLE hEvent; + pj_ioqueue_t *ioqueue; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); + + /* Initiate connect() */ + if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) { + DWORD dwStatus; + dwStatus = WSAGetLastError(); + if (dwStatus != WSAEWOULDBLOCK) { + return PJ_RETURN_OS_ERROR(dwStatus); + } + } else { + /* Connect has completed immediately! */ + return PJ_SUCCESS; + } + + ioqueue = key->ioqueue; + + /* Add to the array of connecting socket to be polled */ + pj_lock_acquire(ioqueue->lock); + + if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) { + pj_lock_release(ioqueue->lock); + return PJ_ETOOMANYCONN; + } + + /* Get or create event object. */ + if (ioqueue->event_count) { + hEvent = ioqueue->event_pool[ioqueue->event_count - 1]; + --ioqueue->event_count; + } else { + hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEvent == NULL) { + DWORD dwStatus = GetLastError(); + pj_lock_release(ioqueue->lock); + return PJ_STATUS_FROM_OS(dwStatus); + } + } + + /* Mark key as connecting. + * We can't use array index since key can be removed dynamically. + */ + key->connecting = 1; + + /* Associate socket events to the event object. */ + if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) { + CloseHandle(hEvent); + pj_lock_release(ioqueue->lock); + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Add to array. */ + ioqueue->connecting_keys[ ioqueue->connecting_count ] = key; + ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent; + ioqueue->connecting_count++; + + pj_lock_release(ioqueue->lock); + + return PJ_EPENDING; +} +#endif /* #if PJ_HAS_TCP */ + + +PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, + pj_size_t size ) +{ + pj_memset(op_key, 0, size); +} + +PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key ) +{ + BOOL rc; + DWORD bytesTransfered; + + rc = GetOverlappedResult( key->hnd, (LPOVERLAPPED)op_key, + &bytesTransfered, FALSE ); + + if (rc == FALSE) { + return GetLastError()==ERROR_IO_INCOMPLETE; + } + + return FALSE; +} + + +PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_status ) +{ + BOOL rc; + + rc = PostQueuedCompletionStatus(key->ioqueue->iocp, bytes_status, + (long)key, (OVERLAPPED*)op_key ); + if (rc == FALSE) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/list.c b/pjlib/src/pj/list.c index c39814d5..98d98bb7 100644 --- a/pjlib/src/pj/list.c +++ b/pjlib/src/pj/list.c @@ -1,25 +1,25 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/list.h>
-
-#if !PJ_FUNCTIONS_ARE_INLINED
-# include <pj/list_i.h>
-#endif
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/list.h> + +#if !PJ_FUNCTIONS_ARE_INLINED +# include <pj/list_i.h> +#endif + + diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c index 2422905f..65de868e 100644 --- a/pjlib/src/pj/lock.c +++ b/pjlib/src/pj/lock.c @@ -1,194 +1,194 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/lock.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/errno.h>
-
-
-typedef void LOCK_OBJ;
-
-/*
- * Lock structure.
- */
-struct pj_lock_t
-{
- LOCK_OBJ *lock_object;
-
- pj_status_t (*acquire) (LOCK_OBJ*);
- pj_status_t (*tryacquire) (LOCK_OBJ*);
- pj_status_t (*release) (LOCK_OBJ*);
- pj_status_t (*destroy) (LOCK_OBJ*);
-};
-
-typedef pj_status_t (*FPTR)(LOCK_OBJ*);
-
-/******************************************************************************
- * Implementation of lock object with mutex.
- */
-static pj_lock_t mutex_lock_template =
-{
- NULL,
- (FPTR) &pj_mutex_lock,
- (FPTR) &pj_mutex_trylock,
- (FPTR) &pj_mutex_unlock,
- (FPTR) &pj_mutex_destroy
-};
-
-static pj_status_t create_mutex_lock( pj_pool_t *pool,
- const char *name,
- int type,
- pj_lock_t **lock )
-{
- pj_lock_t *p_lock;
- pj_status_t rc;
-
- PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
-
- p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
- if (!p_lock)
- return PJ_ENOMEM;
-
- pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
- rc = pj_mutex_create(pool, name, type, (pj_mutex_t**)&p_lock->lock_object);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *lock = p_lock;
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock )
-{
- return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
-}
-
-PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock )
-{
- return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
-}
-
-
-/******************************************************************************
- * Implementation of NULL lock object.
- */
-static pj_status_t null_op(void *arg)
-{
- PJ_UNUSED_ARG(arg);
- return PJ_SUCCESS;
-}
-
-static pj_lock_t null_lock_template =
-{
- NULL,
- &null_op,
- &null_op,
- &null_op,
- &null_op
-};
-
-PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock )
-{
- PJ_UNUSED_ARG(name);
- PJ_UNUSED_ARG(pool);
-
- PJ_ASSERT_RETURN(lock, PJ_EINVAL);
-
- *lock = &null_lock_template;
- return PJ_SUCCESS;
-}
-
-
-/******************************************************************************
- * Implementation of semaphore lock object.
- */
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-static pj_lock_t sem_lock_template =
-{
- NULL,
- (FPTR) &pj_sem_wait,
- (FPTR) &pj_sem_trywait,
- (FPTR) &pj_sem_post,
- (FPTR) &pj_sem_destroy
-};
-
-PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_lock_t **lock )
-{
- pj_lock_t *p_lock;
- pj_status_t rc;
-
- PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
-
- p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
- if (!p_lock)
- return PJ_ENOMEM;
-
- pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t));
- rc = pj_sem_create( pool, name, initial, max,
- (pj_sem_t**)&p_lock->lock_object);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *lock = p_lock;
-
- return PJ_SUCCESS;
-}
-
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-
-PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->acquire)(lock->lock_object);
-}
-
-PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->tryacquire)(lock->lock_object);
-}
-
-PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->release)(lock->lock_object);
-}
-
-PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->destroy)(lock->lock_object);
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/lock.h> +#include <pj/os.h> +#include <pj/assert.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/errno.h> + + +typedef void LOCK_OBJ; + +/* + * Lock structure. + */ +struct pj_lock_t +{ + LOCK_OBJ *lock_object; + + pj_status_t (*acquire) (LOCK_OBJ*); + pj_status_t (*tryacquire) (LOCK_OBJ*); + pj_status_t (*release) (LOCK_OBJ*); + pj_status_t (*destroy) (LOCK_OBJ*); +}; + +typedef pj_status_t (*FPTR)(LOCK_OBJ*); + +/****************************************************************************** + * Implementation of lock object with mutex. + */ +static pj_lock_t mutex_lock_template = +{ + NULL, + (FPTR) &pj_mutex_lock, + (FPTR) &pj_mutex_trylock, + (FPTR) &pj_mutex_unlock, + (FPTR) &pj_mutex_destroy +}; + +static pj_status_t create_mutex_lock( pj_pool_t *pool, + const char *name, + int type, + pj_lock_t **lock ) +{ + pj_lock_t *p_lock; + pj_status_t rc; + + PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); + + p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t)); + if (!p_lock) + return PJ_ENOMEM; + + pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t)); + rc = pj_mutex_create(pool, name, type, (pj_mutex_t**)&p_lock->lock_object); + if (rc != PJ_SUCCESS) + return rc; + + *lock = p_lock; + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock); +} + +PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock); +} + + +/****************************************************************************** + * Implementation of NULL lock object. + */ +static pj_status_t null_op(void *arg) +{ + PJ_UNUSED_ARG(arg); + return PJ_SUCCESS; +} + +static pj_lock_t null_lock_template = +{ + NULL, + &null_op, + &null_op, + &null_op, + &null_op +}; + +PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(pool); + + PJ_ASSERT_RETURN(lock, PJ_EINVAL); + + *lock = &null_lock_template; + return PJ_SUCCESS; +} + + +/****************************************************************************** + * Implementation of semaphore lock object. + */ +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +static pj_lock_t sem_lock_template = +{ + NULL, + (FPTR) &pj_sem_wait, + (FPTR) &pj_sem_trywait, + (FPTR) &pj_sem_post, + (FPTR) &pj_sem_destroy +}; + +PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_lock_t **lock ) +{ + pj_lock_t *p_lock; + pj_status_t rc; + + PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); + + p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t)); + if (!p_lock) + return PJ_ENOMEM; + + pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t)); + rc = pj_sem_create( pool, name, initial, max, + (pj_sem_t**)&p_lock->lock_object); + if (rc != PJ_SUCCESS) + return rc; + + *lock = p_lock; + + return PJ_SUCCESS; +} + + +#endif /* PJ_HAS_SEMAPHORE */ + + +PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->acquire)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->tryacquire)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->release)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->destroy)(lock->lock_object); +} + diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c index 55e7cf39..2077fc95 100644 --- a/pjlib/src/pj/log.c +++ b/pjlib/src/pj/log.c @@ -1,224 +1,224 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/types.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/compat/vsprintf.h>
-#include <pj/compat/stdarg.h>
-
-#if PJ_LOG_MAX_LEVEL >= 1
-
-static int log_max_level = PJ_LOG_MAX_LEVEL;
-static pj_log_func *log_writer = &pj_log_write;
-static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
- PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE;
-
-#if PJ_LOG_USE_STACK_BUFFER==0
-static char log_buffer[PJ_LOG_MAX_SIZE];
-#endif
-
-PJ_DEF(void) pj_log_set_decor(unsigned decor)
-{
- log_decor = decor;
-}
-
-PJ_DEF(unsigned) pj_log_get_decor(void)
-{
- return log_decor;
-}
-
-PJ_DEF(void) pj_log_set_level(int level)
-{
- log_max_level = level;
-}
-
-PJ_DEF(int) pj_log_get_level(void)
-{
- return log_max_level;
-}
-
-PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
-{
- log_writer = func;
-}
-
-PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
-{
- return log_writer;
-}
-
-PJ_DEF(void) pj_log( const char *sender, int level,
- const char *format, va_list marker)
-{
- pj_time_val now;
- pj_parsed_time ptime;
- char *pre;
-#if PJ_LOG_USE_STACK_BUFFER
- char log_buffer[PJ_LOG_MAX_SIZE];
-#endif
- int len;
-
- PJ_CHECK_STACK();
-
- if (level > log_max_level)
- return;
-
- /* Get current date/time. */
- pj_gettimeofday(&now);
- pj_time_decode(&now, &ptime);
-
- pre = log_buffer;
- if (log_decor & PJ_LOG_HAS_DAY_NAME) {
- static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat"};
- strcpy(pre, wdays[ptime.wday]);
- pre += 3;
- }
- if (log_decor & PJ_LOG_HAS_YEAR) {
- *pre++ = ' ';
- pre += pj_utoa(ptime.year, pre);
- }
- if (log_decor & PJ_LOG_HAS_MONTH) {
- *pre++ = '-';
- pre += pj_utoa_pad(ptime.mon, pre, 2, '0');
- }
- if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
- *pre++ = ' ';
- pre += pj_utoa_pad(ptime.day, pre, 2, '0');
- }
- if (log_decor & PJ_LOG_HAS_TIME) {
- *pre++ = ' ';
- pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
- *pre++ = ':';
- pre += pj_utoa_pad(ptime.min, pre, 2, '0');
- *pre++ = ':';
- pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
- }
- if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
- *pre++ = '.';
- pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
- }
- if (log_decor & PJ_LOG_HAS_SENDER) {
- enum { SENDER_WIDTH = 12 };
- int sender_len = strlen(sender);
- *pre++ = ' ';
- if (sender_len <= SENDER_WIDTH) {
- while (sender_len < SENDER_WIDTH)
- *pre++ = ' ', ++sender_len;
- while (*sender)
- *pre++ = *sender++;
- } else {
- int i;
- for (i=0; i<SENDER_WIDTH; ++i)
- *pre++ = *sender++;
- }
- }
-
- if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
- *pre++ = ' ';
-
- len = pre - log_buffer;
-
- /* Print the whole message to the string log_buffer. */
- len = len + vsnprintf(pre, sizeof(log_buffer)-len, format, marker);
- if (len > 0 && len < sizeof(log_buffer)-1) {
- if (log_decor & PJ_LOG_HAS_NEWLINE) {
- log_buffer[len++] = '\n';
- }
- log_buffer[len++] = '\0';
- } else {
- len = sizeof(log_buffer)-1;
- if (log_decor & PJ_LOG_HAS_NEWLINE) {
- log_buffer[sizeof(log_buffer)-2] = '\n';
- }
- log_buffer[sizeof(log_buffer)-1] = '\0';
- }
-
- if (log_writer)
- (*log_writer)(level, log_buffer, len);
-}
-
-PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 0, format, arg);
- va_end(arg);
-}
-
-PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 1, format, arg);
- va_end(arg);
-}
-#endif /* PJ_LOG_MAX_LEVEL >= 1 */
-
-#if PJ_LOG_MAX_LEVEL >= 2
-PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 2, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 3
-PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 3, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 4
-PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 4, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 5
-PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 5, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 6
-PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 6, format, arg);
- va_end(arg);
-}
-#endif
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/types.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/os.h> +#include <pj/compat/vsprintf.h> +#include <pj/compat/stdarg.h> + +#if PJ_LOG_MAX_LEVEL >= 1 + +static int log_max_level = PJ_LOG_MAX_LEVEL; +static pj_log_func *log_writer = &pj_log_write; +static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | + PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE; + +#if PJ_LOG_USE_STACK_BUFFER==0 +static char log_buffer[PJ_LOG_MAX_SIZE]; +#endif + +PJ_DEF(void) pj_log_set_decor(unsigned decor) +{ + log_decor = decor; +} + +PJ_DEF(unsigned) pj_log_get_decor(void) +{ + return log_decor; +} + +PJ_DEF(void) pj_log_set_level(int level) +{ + log_max_level = level; +} + +PJ_DEF(int) pj_log_get_level(void) +{ + return log_max_level; +} + +PJ_DEF(void) pj_log_set_log_func( pj_log_func *func ) +{ + log_writer = func; +} + +PJ_DEF(pj_log_func*) pj_log_get_log_func(void) +{ + return log_writer; +} + +PJ_DEF(void) pj_log( const char *sender, int level, + const char *format, va_list marker) +{ + pj_time_val now; + pj_parsed_time ptime; + char *pre; +#if PJ_LOG_USE_STACK_BUFFER + char log_buffer[PJ_LOG_MAX_SIZE]; +#endif + int len; + + PJ_CHECK_STACK(); + + if (level > log_max_level) + return; + + /* Get current date/time. */ + pj_gettimeofday(&now); + pj_time_decode(&now, &ptime); + + pre = log_buffer; + if (log_decor & PJ_LOG_HAS_DAY_NAME) { + static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + strcpy(pre, wdays[ptime.wday]); + pre += 3; + } + if (log_decor & PJ_LOG_HAS_YEAR) { + *pre++ = ' '; + pre += pj_utoa(ptime.year, pre); + } + if (log_decor & PJ_LOG_HAS_MONTH) { + *pre++ = '-'; + pre += pj_utoa_pad(ptime.mon, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_DAY_OF_MON) { + *pre++ = ' '; + pre += pj_utoa_pad(ptime.day, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_TIME) { + *pre++ = ' '; + pre += pj_utoa_pad(ptime.hour, pre, 2, '0'); + *pre++ = ':'; + pre += pj_utoa_pad(ptime.min, pre, 2, '0'); + *pre++ = ':'; + pre += pj_utoa_pad(ptime.sec, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_MICRO_SEC) { + *pre++ = '.'; + pre += pj_utoa_pad(ptime.msec, pre, 3, '0'); + } + if (log_decor & PJ_LOG_HAS_SENDER) { + enum { SENDER_WIDTH = 12 }; + int sender_len = strlen(sender); + *pre++ = ' '; + if (sender_len <= SENDER_WIDTH) { + while (sender_len < SENDER_WIDTH) + *pre++ = ' ', ++sender_len; + while (*sender) + *pre++ = *sender++; + } else { + int i; + for (i=0; i<SENDER_WIDTH; ++i) + *pre++ = *sender++; + } + } + + if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE) + *pre++ = ' '; + + len = pre - log_buffer; + + /* Print the whole message to the string log_buffer. */ + len = len + vsnprintf(pre, sizeof(log_buffer)-len, format, marker); + if (len > 0 && len < sizeof(log_buffer)-1) { + if (log_decor & PJ_LOG_HAS_NEWLINE) { + log_buffer[len++] = '\n'; + } + log_buffer[len++] = '\0'; + } else { + len = sizeof(log_buffer)-1; + if (log_decor & PJ_LOG_HAS_NEWLINE) { + log_buffer[sizeof(log_buffer)-2] = '\n'; + } + log_buffer[sizeof(log_buffer)-1] = '\0'; + } + + if (log_writer) + (*log_writer)(level, log_buffer, len); +} + +PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 0, format, arg); + va_end(arg); +} + +PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 1, format, arg); + va_end(arg); +} +#endif /* PJ_LOG_MAX_LEVEL >= 1 */ + +#if PJ_LOG_MAX_LEVEL >= 2 +PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 2, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 3 +PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 3, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 4 +PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 4, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 5 +PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 5, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 6 +PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 6, format, arg); + va_end(arg); +} +#endif + diff --git a/pjlib/src/pj/log_writer_printk.c b/pjlib/src/pj/log_writer_printk.c index fa901bbb..bf846b4d 100644 --- a/pjlib/src/pj/log_writer_printk.c +++ b/pjlib/src/pj/log_writer_printk.c @@ -1,27 +1,27 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/log.h>
-#include <pj/os.h>
-
-PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
-{
- PJ_CHECK_STACK();
- printk(KERN_INFO "%s", buffer);
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/log.h> +#include <pj/os.h> + +PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) +{ + PJ_CHECK_STACK(); + printk(KERN_INFO "%s", buffer); +} + diff --git a/pjlib/src/pj/log_writer_stdout.c b/pjlib/src/pj/log_writer_stdout.c index 506e97e8..4a1f9d9e 100644 --- a/pjlib/src/pj/log_writer_stdout.c +++ b/pjlib/src/pj/log_writer_stdout.c @@ -1,70 +1,70 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/compat/stdfileio.h>
-
-#define CLR_FATAL (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R)
-#define CLR_WARNING (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G)
-#define CLR_INFO (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \
- PJ_TERM_COLOR_B)
-#define CLR_DEFAULT (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B)
-
-static void term_set_color(int level)
-{
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
- unsigned attr = 0;
- switch (level) {
- case 0:
- case 1: attr = CLR_FATAL;
- break;
- case 2: attr = CLR_WARNING;
- break;
- case 3: attr = CLR_INFO;
- break;
- default:
- attr = CLR_DEFAULT;
- break;
- }
-
- pj_term_set_color(attr);
-#endif
-}
-
-static void term_restore_color(void)
-{
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
- pj_term_set_color(CLR_DEFAULT);
-#endif
-}
-
-
-PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(len);
-
- /* Copy to terminal/file. */
- term_set_color(level);
- fputs(buffer, stdout);
- term_restore_color();
-
- fflush(stdout);
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/log.h> +#include <pj/os.h> +#include <pj/compat/stdfileio.h> + +#define CLR_FATAL (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R) +#define CLR_WARNING (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G) +#define CLR_INFO (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \ + PJ_TERM_COLOR_B) +#define CLR_DEFAULT (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B) + +static void term_set_color(int level) +{ +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + unsigned attr = 0; + switch (level) { + case 0: + case 1: attr = CLR_FATAL; + break; + case 2: attr = CLR_WARNING; + break; + case 3: attr = CLR_INFO; + break; + default: + attr = CLR_DEFAULT; + break; + } + + pj_term_set_color(attr); +#endif +} + +static void term_restore_color(void) +{ +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + pj_term_set_color(CLR_DEFAULT); +#endif +} + + +PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(len); + + /* Copy to terminal/file. */ + term_set_color(level); + fputs(buffer, stdout); + term_restore_color(); + + fflush(stdout); +} + diff --git a/pjlib/src/pj/os_core_linux_kernel.c b/pjlib/src/pj/os_core_linux_kernel.c index d9084577..b91e4e0f 100644 --- a/pjlib/src/pj/os_core_linux_kernel.c +++ b/pjlib/src/pj/os_core_linux_kernel.c @@ -1,689 +1,689 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/except.h>
-#include <pj/errno.h>
-#include <pj/string.h>
-#include <pj/compat/high_precision.h>
-#include <pj/compat/sprintf.h>
-
-#include <linux/config.h>
-#include <linux/version.h>
-#if defined(MODVERSIONS)
-#include <linux/modversions.h>
-#endif
-#include <linux/kernel.h>
-#include <linux/sched.h>
-//#include <linux/tqueue.h>
-#include <linux/wait.h>
-#include <linux/signal.h>
-
-#include <asm/atomic.h>
-#include <asm/unistd.h>
-#include <asm/semaphore.h>
-
-#define THIS_FILE "oslinuxkern"
-
-struct pj_thread_t
-{
- /** Thread's name. */
- char obj_name[PJ_MAX_OBJ_NAME];
-
- /** Linux task structure for thread. */
- struct task_struct *thread;
-
- /** Flags (specified in pj_thread_create) */
- unsigned flags;
-
- /** Task queue needed to launch thread. */
- //struct tq_struct tq;
-
- /** Semaphore needed to control thread startup. */
- struct semaphore startstop_sem;
-
- /** Semaphore to suspend thread during startup. */
- struct semaphore suspend_sem;
-
- /** Queue thread is waiting on. Gets initialized by
- thread_initialize, can be used by thread itself.
- */
- wait_queue_head_t queue;
-
- /** Flag to tell thread whether to die or not.
- When the thread receives a signal, it must check
- the value of terminate and call thread_deinitialize and terminate
- if set.
- */
- int terminate;
-
- /** Thread's entry. */
- pj_thread_proc *func;
-
- /** Argument. */
- void *arg;
-};
-
-struct pj_atomic_t
-{
- atomic_t atom;
-};
-
-struct pj_mutex_t
-{
- struct semaphore sem;
- pj_bool_t recursive;
- pj_thread_t *owner;
- int own_count;
-};
-
-struct pj_sem_t
-{
- struct semaphore sem;
-};
-
-/*
- * Static global variables.
- */
-#define MAX_TLS_ID 32
-static void *tls_values[MAX_TLS_ID];
-static int tls_id;
-static long thread_tls_id;
-static spinlock_t critical_section = SPIN_LOCK_UNLOCKED;
-static unsigned long spinlock_flags;
-static pj_thread_t main_thread;
-
-/* private functions */
-//#define TRACE_(expr) PJ_LOG(3,expr)
-#define TRACE_(x)
-
-
-/* This must be called in the context of the new thread. */
-static void thread_initialize( pj_thread_t *thread )
-{
- TRACE_((THIS_FILE, "---new thread initializing..."));
-
- /* Set TLS */
- pj_thread_local_set(thread_tls_id, thread);
-
- /* fill in thread structure */
- thread->thread = current;
- pj_assert(thread->thread != NULL);
-
- /* set signal mask to what we want to respond */
- siginitsetinv(¤t->blocked,
- sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
-
- /* initialise wait queue */
- init_waitqueue_head(&thread->queue);
-
- /* initialise termination flag */
- thread->terminate = 0;
-
- /* set name of this process (max 15 chars + 0 !) */
- thread->obj_name[15] = '\0';
- sprintf(current->comm, thread->obj_name);
-
- /* tell the creator that we are ready and let him continue */
- up(&thread->startstop_sem);
-}
-
-/* cleanup of thread. Called by the exiting thread. */
-static void thread_deinitialize(pj_thread_t *thread)
-{
- /* we are terminating */
-
- /* lock the kernel, the exit will unlock it */
- thread->thread = NULL;
- mb();
-
- /* notify the stop_kthread() routine that we are terminating. */
- up(&thread->startstop_sem);
-
- /* the kernel_thread that called clone() does a do_exit here. */
-
- /* there is no race here between execution of the "killer" and
- real termination of the thread (race window between up and do_exit),
- since both the thread and the "killer" function are running with
- the kernel lock held.
- The kernel lock will be freed after the thread exited, so the code
- is really not executed anymore as soon as the unload functions gets
- the kernel lock back.
- The init process may not have made the cleanup of the process here,
- but the cleanup can be done safely with the module unloaded.
- */
-
-}
-
-static int thread_proc(void *arg)
-{
- pj_thread_t *thread = arg;
-
- TRACE_((THIS_FILE, "---new thread starting!"));
-
- /* Initialize thread. */
- thread_initialize( thread );
-
- /* Wait if created suspended. */
- if (thread->flags & PJ_THREAD_SUSPENDED) {
- TRACE_((THIS_FILE, "---new thread suspended..."));
- down(&thread->suspend_sem);
- }
-
- TRACE_((THIS_FILE, "---new thread running..."));
-
- pj_assert(thread->func != NULL);
-
- /* Call thread's entry. */
- (*thread->func)(thread->arg);
-
- TRACE_((THIS_FILE, "---thread exiting..."));
-
- /* Cleanup thread. */
- thread_deinitialize(thread);
-
- return 0;
-}
-
-/* The very task entry. */
-static void kthread_launcher(void *arg)
-{
- TRACE_((THIS_FILE, "...launching thread!..."));
- kernel_thread(&thread_proc, arg, 0);
-}
-
-PJ_DEF(pj_status_t) pj_init(void)
-{
- pj_status_t rc;
-
- PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
-
- rc = pj_thread_init();
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_uint32_t) pj_getpid(void)
-{
- return 1;
-}
-
-PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
- pj_thread_desc desc,
- pj_thread_t **ptr_thread)
-{
- char stack_ptr;
- pj_thread_t *thread = (pj_thread_t *)desc;
- pj_str_t thread_name = pj_str((char*)cstr_thread_name);
-
- /* Size sanity check. */
- if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
- pj_assert(!"Not enough pj_thread_desc size!");
- return PJ_EBUG;
- }
-
- /* If a thread descriptor has been registered before, just return it. */
- if (pj_thread_local_get (thread_tls_id) != 0) {
- *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
- return PJ_SUCCESS;
- }
-
- /* Initialize and set the thread entry. */
- pj_memset(desc, 0, sizeof(struct pj_thread_t));
-
- if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
- pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
- else
- pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
-
- /* Initialize. */
- thread_initialize(thread);
-
- /* Eat semaphore. */
- down(&thread->startstop_sem);
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- thread->stk_start = &stack_ptr;
- thread->stk_size = 0xFFFFFFFFUL;
- thread->stk_max_usage = 0;
-#else
- stack_ptr = '\0';
-#endif
-
- *ptr_thread = thread;
- return PJ_SUCCESS;
-}
-
-
-pj_status_t pj_thread_init(void)
-{
- pj_status_t rc;
- pj_thread_t *dummy;
-
- rc = pj_thread_local_alloc(&thread_tls_id);
- if (rc != PJ_SUCCESS)
- return rc;
-
- return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy);
-}
-
-PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name,
- pj_thread_proc *proc, void *arg,
- pj_size_t stack_size, unsigned flags,
- pj_thread_t **ptr_thread)
-{
- pj_thread_t *thread;
-
- TRACE_((THIS_FILE, "pj_thread_create()"));
-
- PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
-
- thread = pj_pool_zalloc(pool, sizeof(pj_thread_t));
- if (!thread)
- return PJ_ENOMEM;
-
- PJ_UNUSED_ARG(stack_size);
-
- /* Thread name. */
- if (!thread_name)
- thread_name = "thr%p";
-
- if (strchr(thread_name, '%')) {
- pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread);
- } else {
- strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME);
- thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- /* Init thread's semaphore. */
- TRACE_((THIS_FILE, "...init semaphores..."));
- init_MUTEX_LOCKED(&thread->startstop_sem);
- init_MUTEX_LOCKED(&thread->suspend_sem);
-
- thread->flags = flags;
-
- if ((flags & PJ_THREAD_SUSPENDED) == 0) {
- up(&thread->suspend_sem);
- }
-
- /* Store the functions and argument. */
- thread->func = proc;
- thread->arg = arg;
-
- /* Save return value. */
- *ptr_thread = thread;
-
- /* Create the new thread by running a task through keventd. */
-
-#if 0
- /* Initialize the task queue struct. */
- thread->tq.sync = 0;
- INIT_LIST_HEAD(&thread->tq.list);
- thread->tq.routine = kthread_launcher;
- thread->tq.data = thread;
-
- /* and schedule it for execution. */
- schedule_task(&thread->tq);
-#endif
- kthread_launcher(thread);
-
- /* Wait until thread has reached the setup_thread routine. */
- TRACE_((THIS_FILE, "...wait for the new thread..."));
- down(&thread->startstop_sem);
-
- TRACE_((THIS_FILE, "...main thread resumed..."));
- return PJ_SUCCESS;
-}
-
-PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread)
-{
- return thread->obj_name;
-}
-
-PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread)
-{
- up(&thread->suspend_sem);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_thread_t*) pj_thread_this(void)
-{
- return (pj_thread_t*)pj_thread_local_get(thread_tls_id);
-}
-
-PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
-{
- TRACE_((THIS_FILE, "pj_thread_join()"));
- down(&p->startstop_sem);
- TRACE_((THIS_FILE, " joined!"));
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread)
-{
- PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
-{
- pj_highprec_t ticks;
- pj_thread_t *thread = pj_thread_this();
-
- PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG);
-
- /* Use high precision calculation to make sure we don't
- * crop values:
- *
- * ticks = HZ * msec / 1000
- */
- ticks = HZ;
- pj_highprec_mul(ticks, msec);
- pj_highprec_div(ticks, 1000);
-
- TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks));
- interruptible_sleep_on_timeout( &thread->queue, ticks);
- return PJ_SUCCESS;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t value,
- pj_atomic_t **ptr_var)
-{
- pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
- if (!t) return PJ_ENOMEM;
-
- atomic_set(&t->atom, value);
- *ptr_var = t;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
-{
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pj_atomic_set(pj_atomic_t *var, pj_atomic_value_t value)
-{
- atomic_set(&var->atom, value);
-}
-
-PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var)
-{
- return atomic_read(&var->atom);
-}
-
-PJ_DEF(void) pj_atomic_inc(pj_atomic_t *var)
-{
- atomic_inc(&var->atom);
-}
-
-PJ_DEF(void) pj_atomic_dec(pj_atomic_t *var)
-{
- atomic_dec(&var->atom);
-}
-
-PJ_DEF(void) pj_atomic_add( pj_atomic_t *var, pj_atomic_value_t value )
-{
- atomic_add(value, &var->atom);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
-{
- if (tls_id >= MAX_TLS_ID)
- return PJ_ETOOMANY;
-
- *index = tls_id++;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pj_thread_local_free(long index)
-{
- pj_assert(index >= 0 && index < MAX_TLS_ID);
-}
-
-PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
-{
- pj_assert(index >= 0 && index < MAX_TLS_ID);
- tls_values[index] = value;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void*) pj_thread_local_get(long index)
-{
- pj_assert(index >= 0 && index < MAX_TLS_ID);
- return tls_values[index];
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(void) pj_enter_critical_section(void)
-{
- spin_lock_irqsave(&critical_section, spinlock_flags);
-}
-
-PJ_DEF(void) pj_leave_critical_section(void)
-{
- spin_unlock_irqrestore(&critical_section, spinlock_flags);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **ptr_mutex)
-{
- pj_mutex_t *mutex;
-
- PJ_UNUSED_ARG(name);
-
- mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t));
- if (!mutex)
- return PJ_ENOMEM;
-
- init_MUTEX(&mutex->sem);
-
- mutex->recursive = (type == PJ_MUTEX_RECURSE);
- mutex->owner = NULL;
- mutex->own_count = 0;
-
- /* Done. */
- *ptr_mutex = mutex;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
-}
-
-PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex);
-}
-
-PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
-{
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- if (mutex->recursive) {
- pj_thread_t *this_thread = pj_thread_this();
- if (mutex->owner == this_thread) {
- ++mutex->own_count;
- } else {
- down(&mutex->sem);
- pj_assert(mutex->own_count == 0);
- mutex->owner = this_thread;
- mutex->own_count = 1;
- }
- } else {
- down(&mutex->sem);
- }
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
-{
- long rc;
-
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- if (mutex->recursive) {
- pj_thread_t *this_thread = pj_thread_this();
- if (mutex->owner == this_thread) {
- ++mutex->own_count;
- } else {
- rc = down_interruptible(&mutex->sem);
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(-rc);
- pj_assert(mutex->own_count == 0);
- mutex->owner = this_thread;
- mutex->own_count = 1;
- }
- } else {
- int rc = down_trylock(&mutex->sem);
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(-rc);
- }
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
-{
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- if (mutex->recursive) {
- pj_thread_t *this_thread = pj_thread_this();
- if (mutex->owner == this_thread) {
- pj_assert(mutex->own_count > 0);
- --mutex->own_count;
- if (mutex->own_count == 0) {
- mutex->owner = NULL;
- up(&mutex->sem);
- }
- } else {
- pj_assert(!"Not owner!");
- return PJ_EINVALIDOP;
- }
- } else {
- up(&mutex->sem);
- }
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
-{
- PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL);
-
- return PJ_SUCCESS;
-}
-
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
-{
- if (mutex->recursive)
- return mutex->owner == pj_thread_this();
- else
- return 1;
-}
-#endif /* PJ_DEBUG */
-
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **sem)
-{
- pj_sem_t *sem;
-
- PJ_UNUSED_ARG(max);
-
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- sem = pj_pool_alloc(pool, sizeof(pj_sem_t));
- sema_init(&sem->sem, initial);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
-{
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- down(&sem->sem);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
-{
- int rc;
-
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- rc = down_trylock(&sem->sem);
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(-rc);
- } else {
- return PJ_SUCCESS;
- }
-}
-
-PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
-{
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- up(&sem->sem);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
-{
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- return PJ_SUCCESS;
-}
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <pj/assert.h> +#include <pj/pool.h> +#include <pj/log.h> +#include <pj/except.h> +#include <pj/errno.h> +#include <pj/string.h> +#include <pj/compat/high_precision.h> +#include <pj/compat/sprintf.h> + +#include <linux/config.h> +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/kernel.h> +#include <linux/sched.h> +//#include <linux/tqueue.h> +#include <linux/wait.h> +#include <linux/signal.h> + +#include <asm/atomic.h> +#include <asm/unistd.h> +#include <asm/semaphore.h> + +#define THIS_FILE "oslinuxkern" + +struct pj_thread_t +{ + /** Thread's name. */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** Linux task structure for thread. */ + struct task_struct *thread; + + /** Flags (specified in pj_thread_create) */ + unsigned flags; + + /** Task queue needed to launch thread. */ + //struct tq_struct tq; + + /** Semaphore needed to control thread startup. */ + struct semaphore startstop_sem; + + /** Semaphore to suspend thread during startup. */ + struct semaphore suspend_sem; + + /** Queue thread is waiting on. Gets initialized by + thread_initialize, can be used by thread itself. + */ + wait_queue_head_t queue; + + /** Flag to tell thread whether to die or not. + When the thread receives a signal, it must check + the value of terminate and call thread_deinitialize and terminate + if set. + */ + int terminate; + + /** Thread's entry. */ + pj_thread_proc *func; + + /** Argument. */ + void *arg; +}; + +struct pj_atomic_t +{ + atomic_t atom; +}; + +struct pj_mutex_t +{ + struct semaphore sem; + pj_bool_t recursive; + pj_thread_t *owner; + int own_count; +}; + +struct pj_sem_t +{ + struct semaphore sem; +}; + +/* + * Static global variables. + */ +#define MAX_TLS_ID 32 +static void *tls_values[MAX_TLS_ID]; +static int tls_id; +static long thread_tls_id; +static spinlock_t critical_section = SPIN_LOCK_UNLOCKED; +static unsigned long spinlock_flags; +static pj_thread_t main_thread; + +/* private functions */ +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(x) + + +/* This must be called in the context of the new thread. */ +static void thread_initialize( pj_thread_t *thread ) +{ + TRACE_((THIS_FILE, "---new thread initializing...")); + + /* Set TLS */ + pj_thread_local_set(thread_tls_id, thread); + + /* fill in thread structure */ + thread->thread = current; + pj_assert(thread->thread != NULL); + + /* set signal mask to what we want to respond */ + siginitsetinv(¤t->blocked, + sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)); + + /* initialise wait queue */ + init_waitqueue_head(&thread->queue); + + /* initialise termination flag */ + thread->terminate = 0; + + /* set name of this process (max 15 chars + 0 !) */ + thread->obj_name[15] = '\0'; + sprintf(current->comm, thread->obj_name); + + /* tell the creator that we are ready and let him continue */ + up(&thread->startstop_sem); +} + +/* cleanup of thread. Called by the exiting thread. */ +static void thread_deinitialize(pj_thread_t *thread) +{ + /* we are terminating */ + + /* lock the kernel, the exit will unlock it */ + thread->thread = NULL; + mb(); + + /* notify the stop_kthread() routine that we are terminating. */ + up(&thread->startstop_sem); + + /* the kernel_thread that called clone() does a do_exit here. */ + + /* there is no race here between execution of the "killer" and + real termination of the thread (race window between up and do_exit), + since both the thread and the "killer" function are running with + the kernel lock held. + The kernel lock will be freed after the thread exited, so the code + is really not executed anymore as soon as the unload functions gets + the kernel lock back. + The init process may not have made the cleanup of the process here, + but the cleanup can be done safely with the module unloaded. + */ + +} + +static int thread_proc(void *arg) +{ + pj_thread_t *thread = arg; + + TRACE_((THIS_FILE, "---new thread starting!")); + + /* Initialize thread. */ + thread_initialize( thread ); + + /* Wait if created suspended. */ + if (thread->flags & PJ_THREAD_SUSPENDED) { + TRACE_((THIS_FILE, "---new thread suspended...")); + down(&thread->suspend_sem); + } + + TRACE_((THIS_FILE, "---new thread running...")); + + pj_assert(thread->func != NULL); + + /* Call thread's entry. */ + (*thread->func)(thread->arg); + + TRACE_((THIS_FILE, "---thread exiting...")); + + /* Cleanup thread. */ + thread_deinitialize(thread); + + return 0; +} + +/* The very task entry. */ +static void kthread_launcher(void *arg) +{ + TRACE_((THIS_FILE, "...launching thread!...")); + kernel_thread(&thread_proc, arg, 0); +} + +PJ_DEF(pj_status_t) pj_init(void) +{ + pj_status_t rc; + + PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); + + rc = pj_thread_init(); + if (rc != PJ_SUCCESS) + return rc; + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + return 1; +} + +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **ptr_thread) +{ + char stack_ptr; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_memset(desc, 0, sizeof(struct pj_thread_t)); + + if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); + else + pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread); + + /* Initialize. */ + thread_initialize(thread); + + /* Eat semaphore. */ + down(&thread->startstop_sem); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + *ptr_thread = thread; + return PJ_SUCCESS; +} + + +pj_status_t pj_thread_init(void) +{ + pj_status_t rc; + pj_thread_t *dummy; + + rc = pj_thread_local_alloc(&thread_tls_id); + if (rc != PJ_SUCCESS) + return rc; + + return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy); +} + +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, + pj_thread_proc *proc, void *arg, + pj_size_t stack_size, unsigned flags, + pj_thread_t **ptr_thread) +{ + pj_thread_t *thread; + + TRACE_((THIS_FILE, "pj_thread_create()")); + + PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); + + thread = pj_pool_zalloc(pool, sizeof(pj_thread_t)); + if (!thread) + return PJ_ENOMEM; + + PJ_UNUSED_ARG(stack_size); + + /* Thread name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread); + } else { + strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME); + thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + /* Init thread's semaphore. */ + TRACE_((THIS_FILE, "...init semaphores...")); + init_MUTEX_LOCKED(&thread->startstop_sem); + init_MUTEX_LOCKED(&thread->suspend_sem); + + thread->flags = flags; + + if ((flags & PJ_THREAD_SUSPENDED) == 0) { + up(&thread->suspend_sem); + } + + /* Store the functions and argument. */ + thread->func = proc; + thread->arg = arg; + + /* Save return value. */ + *ptr_thread = thread; + + /* Create the new thread by running a task through keventd. */ + +#if 0 + /* Initialize the task queue struct. */ + thread->tq.sync = 0; + INIT_LIST_HEAD(&thread->tq.list); + thread->tq.routine = kthread_launcher; + thread->tq.data = thread; + + /* and schedule it for execution. */ + schedule_task(&thread->tq); +#endif + kthread_launcher(thread); + + /* Wait until thread has reached the setup_thread routine. */ + TRACE_((THIS_FILE, "...wait for the new thread...")); + down(&thread->startstop_sem); + + TRACE_((THIS_FILE, "...main thread resumed...")); + return PJ_SUCCESS; +} + +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread) +{ + return thread->obj_name; +} + +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread) +{ + up(&thread->suspend_sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ + return (pj_thread_t*)pj_thread_local_get(thread_tls_id); +} + +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ + TRACE_((THIS_FILE, "pj_thread_join()")); + down(&p->startstop_sem); + TRACE_((THIS_FILE, " joined!")); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread) +{ + PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + pj_highprec_t ticks; + pj_thread_t *thread = pj_thread_this(); + + PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG); + + /* Use high precision calculation to make sure we don't + * crop values: + * + * ticks = HZ * msec / 1000 + */ + ticks = HZ; + pj_highprec_mul(ticks, msec); + pj_highprec_div(ticks, 1000); + + TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks)); + interruptible_sleep_on_timeout( &thread->queue, ticks); + return PJ_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t value, + pj_atomic_t **ptr_var) +{ + pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); + if (!t) return PJ_ENOMEM; + + atomic_set(&t->atom, value); + *ptr_var = t; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) +{ + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_atomic_set(pj_atomic_t *var, pj_atomic_value_t value) +{ + atomic_set(&var->atom, value); +} + +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var) +{ + return atomic_read(&var->atom); +} + +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *var) +{ + atomic_inc(&var->atom); +} + +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *var) +{ + atomic_dec(&var->atom); +} + +PJ_DEF(void) pj_atomic_add( pj_atomic_t *var, pj_atomic_value_t value ) +{ + atomic_add(value, &var->atom); +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) +{ + if (tls_id >= MAX_TLS_ID) + return PJ_ETOOMANY; + + *index = tls_id++; + + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_thread_local_free(long index) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); +} + +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); + tls_values[index] = value; + return PJ_SUCCESS; +} + +PJ_DEF(void*) pj_thread_local_get(long index) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); + return tls_values[index]; +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void) pj_enter_critical_section(void) +{ + spin_lock_irqsave(&critical_section, spinlock_flags); +} + +PJ_DEF(void) pj_leave_critical_section(void) +{ + spin_unlock_irqrestore(&critical_section, spinlock_flags); +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **ptr_mutex) +{ + pj_mutex_t *mutex; + + PJ_UNUSED_ARG(name); + + mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t)); + if (!mutex) + return PJ_ENOMEM; + + init_MUTEX(&mutex->sem); + + mutex->recursive = (type == PJ_MUTEX_RECURSE); + mutex->owner = NULL; + mutex->own_count = 0; + + /* Done. */ + *ptr_mutex = mutex; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex); +} + +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + ++mutex->own_count; + } else { + down(&mutex->sem); + pj_assert(mutex->own_count == 0); + mutex->owner = this_thread; + mutex->own_count = 1; + } + } else { + down(&mutex->sem); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ + long rc; + + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + ++mutex->own_count; + } else { + rc = down_interruptible(&mutex->sem); + if (rc != 0) + return PJ_RETURN_OS_ERROR(-rc); + pj_assert(mutex->own_count == 0); + mutex->owner = this_thread; + mutex->own_count = 1; + } + } else { + int rc = down_trylock(&mutex->sem); + if (rc != 0) + return PJ_RETURN_OS_ERROR(-rc); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + pj_assert(mutex->own_count > 0); + --mutex->own_count; + if (mutex->own_count == 0) { + mutex->owner = NULL; + up(&mutex->sem); + } + } else { + pj_assert(!"Not owner!"); + return PJ_EINVALIDOP; + } + } else { + up(&mutex->sem); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL); + + return PJ_SUCCESS; +} + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ + if (mutex->recursive) + return mutex->owner == pj_thread_this(); + else + return 1; +} +#endif /* PJ_DEBUG */ + + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem) +{ + pj_sem_t *sem; + + PJ_UNUSED_ARG(max); + + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(pj_sem_t)); + sema_init(&sem->sem, initial); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + down(&sem->sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ + int rc; + + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + rc = down_trylock(&sem->sem); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(-rc); + } else { + return PJ_SUCCESS; + } +} + +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + up(&sem->sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + return PJ_SUCCESS; +} + +#endif /* PJ_HAS_SEMAPHORE */ + + + + diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c index 41dfbae2..91305be8 100644 --- a/pjlib/src/pj/os_core_unix.c +++ b/pjlib/src/pj/os_core_unix.c @@ -1,1255 +1,1255 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/rand.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/compat/sprintf.h>
-#include <pj/except.h>
-#include <pj/errno.h>
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-# include <semaphore.h>
-#endif
-
-#include <unistd.h> // getpid()
-#include <errno.h> // errno
-
-#define __USE_GNU
-//uncomment this to get pthread_mutexattr_settype declaration.
-//unfortunately this causes syntax error in pthread.h! :(
-//#define __USE_UNIX98
-#include <pthread.h>
-
-#define THIS_FILE "osunix"
-
-struct pj_thread_t
-{
- char obj_name[PJ_MAX_OBJ_NAME];
- pthread_t thread;
- pj_thread_proc *proc;
- void *arg;
-
- pj_mutex_t *suspended_mutex;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- pj_uint32_t stk_size;
- pj_uint32_t stk_max_usage;
- char *stk_start;
- const char *caller_file;
- int caller_line;
-#endif
-};
-
-struct pj_atomic_t
-{
- pj_mutex_t *mutex;
- pj_atomic_value_t value;
-};
-
-struct pj_mutex_t
-{
- pthread_mutex_t mutex;
- char obj_name[PJ_MAX_OBJ_NAME];
-#if PJ_DEBUG
- int nesting_level;
- pj_thread_t *owner;
-#endif
-};
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-struct pj_sem_t
-{
- sem_t sem;
- char obj_name[PJ_MAX_OBJ_NAME];
-};
-#endif /* PJ_HAS_SEMAPHORE */
-
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-struct pj_event_t
-{
- char obj_name[PJ_MAX_OBJ_NAME];
-};
-#endif /* PJ_HAS_EVENT_OBJ */
-
-
-#if PJ_HAS_THREADS
- static pj_thread_t main_thread;
- static long thread_tls_id;
- static pj_mutex_t critical_section;
-#else
-# define MAX_THREADS 32
- static int tls_flag[MAX_THREADS];
- static void *tls[MAX_THREADS];
-#endif
-
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type);
-
-/*
- * pj_init(void).
- * Init PJLIB!
- */
-PJ_DEF(pj_status_t) pj_init(void)
-{
- char dummy_guid[PJ_GUID_MAX_LENGTH];
- pj_str_t guid;
- pj_status_t rc;
-
- PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
-
-#if PJ_HAS_THREADS
- /* Init this thread's TLS. */
- if ((rc=pj_thread_init()) != 0) {
- return rc;
- }
-
- /* Critical section. */
- if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE)) != 0)
- return rc;
-
-#endif
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Init random seed. */
- pj_srand( clock() );
-
- /* Startup GUID. */
- guid.ptr = dummy_guid;
- pj_generate_unique_string( &guid );
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Startup timestamp */
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
- {
- pj_timestamp dummy_ts;
- if ((rc=pj_get_timestamp(&dummy_ts)) != 0) {
- return rc;
- }
- }
-#endif
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_getpid(void)
- */
-PJ_DEF(pj_uint32_t) pj_getpid(void)
-{
- PJ_CHECK_STACK();
- return getpid();
-}
-
-/*
- * pj_thread_register(..)
- */
-PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
- pj_thread_desc desc,
- pj_thread_t **ptr_thread)
-{
-#if PJ_HAS_THREADS
- char stack_ptr;
- pj_status_t rc;
- pj_thread_t *thread = (pj_thread_t *)desc;
- pj_str_t thread_name = pj_str((char*)cstr_thread_name);
-
- /* Size sanity check. */
- if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
- pj_assert(!"Not enough pj_thread_desc size!");
- return PJ_EBUG;
- }
-
- /* If a thread descriptor has been registered before, just return it. */
- if (pj_thread_local_get (thread_tls_id) != 0) {
- *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
- return PJ_SUCCESS;
- }
-
- /* Initialize and set the thread entry. */
- pj_memset(desc, 0, sizeof(struct pj_thread_t));
- thread->thread = pthread_self();
-
- if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
- pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
- else
- pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
-
- rc = pj_thread_local_set(thread_tls_id, thread);
- if (rc != PJ_SUCCESS)
- return rc;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- thread->stk_start = &stack_ptr;
- thread->stk_size = 0xFFFFFFFFUL;
- thread->stk_max_usage = 0;
-#else
- stack_ptr = '\0';
-#endif
-
- *ptr_thread = thread;
- return PJ_SUCCESS;
-#else
- pj_thread_t *thread = (pj_thread_t*)desc;
- *ptr_thread = thread;
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_thread_init(void)
- */
-pj_status_t pj_thread_init(void)
-{
-#if PJ_HAS_THREADS
- pj_status_t rc;
- pj_thread_t *dummy;
-
- rc = pj_thread_local_alloc(&thread_tls_id );
- if (rc != PJ_SUCCESS) {
- return rc;
- }
- return pj_thread_register("thr%p", (long*)&main_thread, &dummy);
-#else
- PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!"));
- return PJ_EINVALIDOP;
-#endif
-}
-
-#if PJ_HAS_THREADS
-/*
- * thread_main()
- *
- * This is the main entry for all threads.
- */
-static void *thread_main(void *param)
-{
- pj_thread_t *rec = param;
- void *result;
- pj_status_t rc;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_start = (char*)&rec;
-#endif
-
- /* Set current thread id. */
- rc = pj_thread_local_set(thread_tls_id, rec);
- if (rc != PJ_SUCCESS) {
- pj_assert(!"Thread TLS ID is not set (pj_init() error?)");
- }
-
- /* Check if suspension is required. */
- if (rec->suspended_mutex)
- pj_mutex_lock(rec->suspended_mutex);
-
- PJ_LOG(6,(rec->obj_name, "Thread started"));
-
- /* Call user's entry! */
- result = (void*)(long)(*rec->proc)(rec->arg);
-
- /* Done. */
- PJ_LOG(6,(rec->obj_name, "Thread quitting"));
- return result;
-}
-#endif
-
-/*
- * pj_thread_create(...)
- */
-PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
- const char *thread_name,
- pj_thread_proc *proc,
- void *arg,
- pj_size_t stack_size,
- unsigned flags,
- pj_thread_t **ptr_thread)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec;
- int rc;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
-
- /* Create thread record and assign name for the thread */
- rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t));
- if (!rec)
- return PJ_ENOMEM;
-
- /* Set name. */
- if (!thread_name)
- thread_name = "thr%p";
-
- if (strchr(thread_name, '%')) {
- pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
- } else {
- strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
- rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
- rec->stk_max_usage = 0;
-#endif
-
- /* Emulate suspended thread with mutex. */
- if (flags & PJ_THREAD_SUSPENDED) {
- rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex);
- if (rc != PJ_SUCCESS)
- return rc;
-
- pj_mutex_lock(rec->suspended_mutex);
- } else {
- pj_assert(rec->suspended_mutex == NULL);
- }
-
- PJ_LOG(6, (rec->obj_name, "Thread created"));
-
- /* Create the thread. */
- rec->proc = proc;
- rec->arg = arg;
- rc = pthread_create( &rec->thread, NULL, thread_main, rec);
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(rc);
-
- *ptr_thread = rec;
- return PJ_SUCCESS;
-#else
- pj_assert(!"Threading is disabled!");
- return PJ_EINVALIDOP;
-#endif
-}
-
-/*
- * pj_thread-get_name()
- */
-PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec = (pj_thread_t*)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, "");
-
- return rec->obj_name;
-#else
- return "";
-#endif
-}
-
-/*
- * pj_thread_resume()
- */
-PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
-{
- pj_status_t rc;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- rc = pj_mutex_unlock(p->suspended_mutex);
-
- return rc;
-}
-
-/*
- * pj_thread_this()
- */
-PJ_DEF(pj_thread_t*) pj_thread_this(void)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
- pj_assert(rec != NULL);
-
- /*
- * MUST NOT check stack because this function is called
- * by PJ_CHECK_STACK() itself!!!
- *
- */
-
- return rec;
-#else
- pj_assert(!"Threading is not enabled!");
- return NULL;
-#endif
-}
-
-/*
- * pj_thread_join()
- */
-PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec = (pj_thread_t *)p;
- void *ret;
- int result;
-
- PJ_CHECK_STACK();
-
- PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
- result = pthread_join( rec->thread, &ret);
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(result);
-#else
- PJ_CHECK_STACK();
- pj_assert(!"No multithreading support!");
- return PJ_EINVALIDOP;
-#endif
-}
-
-/*
- * pj_thread_destroy()
- */
-PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
-{
- /* This function is used to destroy thread handle in other platforms.
- * I suppose there's nothing to do here..
- */
- PJ_CHECK_STACK();
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_sleep()
- */
-PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
-{
- PJ_CHECK_STACK();
- return usleep(msec * 1000);
-}
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
-/*
- * pj_thread_check_stack()
- * Implementation for PJ_CHECK_STACK()
- */
-PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
-{
- char stk_ptr;
- pj_uint32_t usage;
- pj_thread_t *thread = pj_thread_this();
-
- /* Calculate current usage. */
- usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
- thread->stk_start - &stk_ptr;
-
- /* Assert if stack usage is dangerously high. */
- pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
-
- /* Keep statistic. */
- if (usage > thread->stk_max_usage) {
- thread->stk_max_usage = usage;
- thread->caller_file = file;
- thread->caller_line = line;
- }
-}
-
-/*
- * pj_thread_get_stack_max_usage()
- */
-PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
-{
- return thread->stk_max_usage;
-}
-
-/*
- * pj_thread_get_stack_info()
- */
-PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
- const char **file,
- int *line )
-{
- pj_assert(thread);
-
- *file = thread->caller_file;
- *line = thread->caller_line;
- return 0;
-}
-
-#endif /* PJ_OS_HAS_CHECK_STACK */
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_atomic_create()
- */
-PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t initial,
- pj_atomic_t **ptr_atomic)
-{
- pj_status_t rc;
- pj_atomic_t *atomic_var = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
- if (!atomic_var)
- return PJ_ENOMEM;
-
-#if PJ_HAS_THREADS
- rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex);
- if (rc != PJ_SUCCESS)
- return rc;
-#endif
- atomic_var->value = initial;
-
- *ptr_atomic = atomic_var;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_atomic_destroy()
- */
-PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
-{
- PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
-#if PJ_HAS_THREADS
- return pj_mutex_destroy( atomic_var->mutex );
-#else
- return 0;
-#endif
-}
-
-/*
- * pj_atomic_set()
- */
-PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value)
-{
- PJ_CHECK_STACK();
-
-#if PJ_HAS_THREADS
- pj_mutex_lock( atomic_var->mutex );
-#endif
- atomic_var->value = value;
-#if PJ_HAS_THREADS
- pj_mutex_unlock( atomic_var->mutex);
-#endif
-}
-
-/*
- * pj_atomic_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
-{
- pj_atomic_value_t oldval;
-
- PJ_CHECK_STACK();
-
-#if PJ_HAS_THREADS
- pj_mutex_lock( atomic_var->mutex );
-#endif
- oldval = atomic_var->value;
-#if PJ_HAS_THREADS
- pj_mutex_unlock( atomic_var->mutex);
-#endif
- return oldval;
-}
-
-/*
- * pj_atomic_inc_and_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
-{
- pj_atomic_value_t new_value;
-
- PJ_CHECK_STACK();
-
-#if PJ_HAS_THREADS
- pj_mutex_lock( atomic_var->mutex );
-#endif
- new_value = ++atomic_var->value;
-#if PJ_HAS_THREADS
- pj_mutex_unlock( atomic_var->mutex);
-#endif
-
- return new_value;
-}
-/*
- * pj_atomic_inc()
- */
-PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
-{
- pj_atomic_inc_and_get(atomic_var);
-}
-
-/*
- * pj_atomic_dec_and_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
-{
- pj_atomic_value_t new_value;
-
- PJ_CHECK_STACK();
-
-#if PJ_HAS_THREADS
- pj_mutex_lock( atomic_var->mutex );
-#endif
- new_value = --atomic_var->value;
-#if PJ_HAS_THREADS
- pj_mutex_unlock( atomic_var->mutex);
-#endif
-
- return new_value;
-}
-
-/*
- * pj_atomic_dec()
- */
-PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
-{
- pj_atomic_dec_and_get(atomic_var);
-}
-
-/*
- * pj_atomic_add_and_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
- pj_atomic_value_t value )
-{
- pj_atomic_value_t new_value;
-
-#if PJ_HAS_THREADS
- pj_mutex_lock(atomic_var->mutex);
-#endif
-
- atomic_var->value += value;
- new_value = atomic_var->value;
-
-#if PJ_HAS_THREADS
- pj_mutex_unlock(atomic_var->mutex);
-#endif
-
- return new_value;
-}
-
-/*
- * pj_atomic_add()
- */
-PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
- pj_atomic_value_t value )
-{
- pj_atomic_add_and_get(atomic_var, value);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_thread_local_alloc()
- */
-PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index)
-{
-#if PJ_HAS_THREADS
- pthread_key_t key;
- int rc;
-
- PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL);
-
- pj_assert( sizeof(pthread_key_t) <= sizeof(long));
- if ((rc=pthread_key_create(&key, NULL)) != 0)
- return PJ_RETURN_OS_ERROR(rc);
-
- *p_index = key;
- return PJ_SUCCESS;
-#else
- int i;
- for (i=0; i<MAX_THREADS; ++i) {
- if (tls_flag[i] == 0)
- break;
- }
- if (i == MAX_THREADS)
- return PJ_ETOOMANY;
-
- tls_flag[i] = 1;
- tls[i] = NULL;
-
- *p_index = i;
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_thread_local_free()
- */
-PJ_DEF(void) pj_thread_local_free(long index)
-{
- PJ_CHECK_STACK();
-#if PJ_HAS_THREADS
- pthread_key_delete(index);
-#else
- tls_flag[index] = 0;
-#endif
-}
-
-/*
- * pj_thread_local_set()
- */
-PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
-{
- //Can't check stack because this function is called in the
- //beginning before main thread is initialized.
- //PJ_CHECK_STACK();
-#if PJ_HAS_THREADS
- int rc=pthread_setspecific(index, value);
- return rc==0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc);
-#else
- pj_assert(index >= 0 && index < MAX_THREADS);
- tls[index] = value;
- return PJ_SUCCESS;
-#endif
-}
-
-PJ_DEF(void*) pj_thread_local_get(long index)
-{
- //Can't check stack because this function is called
- //by PJ_CHECK_STACK() itself!!!
- //PJ_CHECK_STACK();
-#if PJ_HAS_THREADS
- return pthread_getspecific(index);
-#else
- pj_assert(index >= 0 && index < MAX_THREADS);
- return tls[index];
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(void) pj_enter_critical_section(void)
-{
-#if PJ_HAS_THREADS
- pj_mutex_lock(&critical_section);
-#endif
-}
-
-PJ_DEF(void) pj_leave_critical_section(void)
-{
-#if PJ_HAS_THREADS
- pj_mutex_unlock(&critical_section);
-#endif
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
-{
-#if PJ_HAS_THREADS
- pthread_mutexattr_t attr;
- int rc;
-
- PJ_CHECK_STACK();
-
- pthread_mutexattr_init(&attr);
-
- if (type == PJ_MUTEX_SIMPLE) {
-#if defined(PJ_LINUX) && PJ_LINUX!=0
- extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int);
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP);
-#else
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
-#endif
- } else {
-#if defined(PJ_LINUX) && PJ_LINUX!=0
- extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int);
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
-#else
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-#endif
- }
-
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(rc);
- }
-
- rc = pthread_mutex_init(&mutex->mutex, &attr);
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(rc);
- }
-
-#if PJ_DEBUG
- /* Set owner. */
- mutex->nesting_level = 0;
- mutex->owner = NULL;
-#endif
-
- /* Set name. */
- if (!name) {
- name = "mtx%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
- } else {
- strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
- mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (mutex->obj_name, "Mutex created"));
- return PJ_SUCCESS;
-#else /* PJ_HAS_THREADS */
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_create()
- */
-PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **ptr_mutex)
-{
-#if PJ_HAS_THREADS
- pj_status_t rc;
- pj_mutex_t *mutex;
-
- PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL);
-
- mutex = pj_pool_alloc(pool, sizeof(*mutex));
- if (!mutex) return PJ_ENOMEM;
-
- if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS)
- return rc;
-
- *ptr_mutex = mutex;
- return PJ_SUCCESS;
-#else /* PJ_HAS_THREADS */
- return (pj_mutex_t*)1;
-#endif
-}
-
-/*
- * pj_mutex_create_simple()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
-}
-
-/*
- * pj_mutex_create_recursive()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
-}
-
-/*
- * pj_mutex_lock()
- */
-PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- status = pthread_mutex_lock( &mutex->mutex );
-
- PJ_LOG(6,(mutex->obj_name,
- (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"),
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- if (status == PJ_SUCCESS) {
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
- }
-#endif
-
- if (status == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-#else /* PJ_HAS_THREADS */
- pj_assert( mutex == (pj_mutex_t*)1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_unlock()
- */
-PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_DEBUG
- pj_assert(mutex->owner == pj_thread_this());
- if (--mutex->nesting_level == 0) {
- mutex->owner = NULL;
- }
-#endif
-
- PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
- pj_thread_this()->obj_name));
-
- status = pthread_mutex_unlock( &mutex->mutex );
- if (status == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-
-#else /* PJ_HAS_THREADS */
- pj_assert( mutex == (pj_mutex_t*)1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_trylock()
- */
-PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- int status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- status = pthread_mutex_trylock( &mutex->mutex );
-
- if (status==0) {
- PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
-#endif
- }
-
- if (status==0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-#else /* PJ_HAS_THREADS */
- pj_assert( mutex == (pj_mutex_t*)1);
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_destroy()
- */
-PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
-{
- int status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_HAS_THREADS
- PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
- status = pthread_mutex_destroy( &mutex->mutex );
- if (status == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-#else
- pj_assert( mutex == (pj_mutex_t*)1 );
- status = PJ_SUCCESS;
- return status;
-#endif
-}
-
-#if PJ_DEBUG
-PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- return mutex->owner == pj_thread_this();
-#else
- return 1;
-#endif
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-/*
- * pj_sem_create()
- */
-PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **ptr_sem)
-{
-#if PJ_HAS_THREADS
- pj_sem_t *sem;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL);
-
- sem = pj_pool_alloc(pool, sizeof(*sem));
- if (!sem) return PJ_ENOMEM;
-
- if (sem_init( &sem->sem, 0, initial) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-
- /* Set name. */
- if (!name) {
- name = "sem%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
- } else {
- strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
- sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (sem->obj_name, "Semaphore created"));
-
- *ptr_sem = sem;
- return PJ_SUCCESS;
-#else
- *ptr_sem = (pj_sem_t*)1;
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_wait()
- */
-PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- result = sem_wait( &sem->sem );
-
- if (result == 0) {
- PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
- pj_thread_this()->obj_name));
- } else {
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
- pj_thread_this()->obj_name));
- }
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*) 1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_trywait()
- */
-PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- result = sem_trywait( &sem->sem );
-
- if (result == 0) {
- PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
- pj_thread_this()->obj_name));
- }
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*)1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_post()
- */
-PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
- PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
- pj_thread_this()->obj_name));
- result = sem_post( &sem->sem );
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*) 1);
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_destroy()
- */
-PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
- pj_thread_this()->obj_name));
- result = sem_destroy( &sem->sem );
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*) 1 );
- return PJ_SUCCESS;
-#endif
-}
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-
-/*
- * pj_event_create()
- */
-PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
- pj_bool_t manual_reset, pj_bool_t initial,
- pj_event_t **ptr_event)
-{
- pj_assert(!"Not supported!");
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(name);
- PJ_UNUSED_ARG(manual_reset);
- PJ_UNUSED_ARG(initial);
- PJ_UNUSED_ARG(ptr_event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_wait()
- */
-PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_trywait()
- */
-PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_set()
- */
-PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_pulse()
- */
-PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_reset()
- */
-PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_destroy()
- */
-PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-#endif /* PJ_HAS_EVENT_OBJ */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-/*
- * Terminal
- */
-
-/**
- * Set terminal color.
- */
-PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
-{
- PJ_UNUSED_ARG(color);
- return PJ_EINVALIDOP;
-}
-
-/**
- * Get current terminal foreground color.
- */
-PJ_DEF(pj_color_t) pj_term_get_color(void)
-{
- return 0;
-}
-
-#endif /* PJ_TERM_HAS_COLOR */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <pj/assert.h> +#include <pj/pool.h> +#include <pj/log.h> +#include <pj/rand.h> +#include <pj/string.h> +#include <pj/guid.h> +#include <pj/compat/sprintf.h> +#include <pj/except.h> +#include <pj/errno.h> + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +# include <semaphore.h> +#endif + +#include <unistd.h> // getpid() +#include <errno.h> // errno + +#define __USE_GNU +//uncomment this to get pthread_mutexattr_settype declaration. +//unfortunately this causes syntax error in pthread.h! :( +//#define __USE_UNIX98 +#include <pthread.h> + +#define THIS_FILE "osunix" + +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + pthread_t thread; + pj_thread_proc *proc; + void *arg; + + pj_mutex_t *suspended_mutex; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif +}; + +struct pj_atomic_t +{ + pj_mutex_t *mutex; + pj_atomic_value_t value; +}; + +struct pj_mutex_t +{ + pthread_mutex_t mutex; + char obj_name[PJ_MAX_OBJ_NAME]; +#if PJ_DEBUG + int nesting_level; + pj_thread_t *owner; +#endif +}; + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +struct pj_sem_t +{ + sem_t sem; + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_SEMAPHORE */ + +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 +struct pj_event_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_EVENT_OBJ */ + + +#if PJ_HAS_THREADS + static pj_thread_t main_thread; + static long thread_tls_id; + static pj_mutex_t critical_section; +#else +# define MAX_THREADS 32 + static int tls_flag[MAX_THREADS]; + static void *tls[MAX_THREADS]; +#endif + +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type); + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + char dummy_guid[PJ_GUID_MAX_LENGTH]; + pj_str_t guid; + pj_status_t rc; + + PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); + +#if PJ_HAS_THREADS + /* Init this thread's TLS. */ + if ((rc=pj_thread_init()) != 0) { + return rc; + } + + /* Critical section. */ + if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE)) != 0) + return rc; + +#endif + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Init random seed. */ + pj_srand( clock() ); + + /* Startup GUID. */ + guid.ptr = dummy_guid; + pj_generate_unique_string( &guid ); + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Startup timestamp */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + { + pj_timestamp dummy_ts; + if ((rc=pj_get_timestamp(&dummy_ts)) != 0) { + return rc; + } + } +#endif + + return PJ_SUCCESS; +} + +/* + * pj_getpid(void) + */ +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + PJ_CHECK_STACK(); + return getpid(); +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + char stack_ptr; + pj_status_t rc; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_memset(desc, 0, sizeof(struct pj_thread_t)); + thread->thread = pthread_self(); + + if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); + else + pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread); + + rc = pj_thread_local_set(thread_tls_id, thread); + if (rc != PJ_SUCCESS) + return rc; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + *ptr_thread = thread; + return PJ_SUCCESS; +#else + pj_thread_t *thread = (pj_thread_t*)desc; + *ptr_thread = thread; + return PJ_SUCCESS; +#endif +} + +/* + * pj_thread_init(void) + */ +pj_status_t pj_thread_init(void) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_thread_t *dummy; + + rc = pj_thread_local_alloc(&thread_tls_id ); + if (rc != PJ_SUCCESS) { + return rc; + } + return pj_thread_register("thr%p", (long*)&main_thread, &dummy); +#else + PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!")); + return PJ_EINVALIDOP; +#endif +} + +#if PJ_HAS_THREADS +/* + * thread_main() + * + * This is the main entry for all threads. + */ +static void *thread_main(void *param) +{ + pj_thread_t *rec = param; + void *result; + pj_status_t rc; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_start = (char*)&rec; +#endif + + /* Set current thread id. */ + rc = pj_thread_local_set(thread_tls_id, rec); + if (rc != PJ_SUCCESS) { + pj_assert(!"Thread TLS ID is not set (pj_init() error?)"); + } + + /* Check if suspension is required. */ + if (rec->suspended_mutex) + pj_mutex_lock(rec->suspended_mutex); + + PJ_LOG(6,(rec->obj_name, "Thread started")); + + /* Call user's entry! */ + result = (void*)(long)(*rec->proc)(rec->arg); + + /* Done. */ + PJ_LOG(6,(rec->obj_name, "Thread quitting")); + return result; +} +#endif + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec; + int rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); + + /* Create thread record and assign name for the thread */ + rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t)); + if (!rec) + return PJ_ENOMEM; + + /* Set name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); + } else { + strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); + rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL; + rec->stk_max_usage = 0; +#endif + + /* Emulate suspended thread with mutex. */ + if (flags & PJ_THREAD_SUSPENDED) { + rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex); + if (rc != PJ_SUCCESS) + return rc; + + pj_mutex_lock(rec->suspended_mutex); + } else { + pj_assert(rec->suspended_mutex == NULL); + } + + PJ_LOG(6, (rec->obj_name, "Thread created")); + + /* Create the thread. */ + rec->proc = proc; + rec->arg = arg; + rc = pthread_create( &rec->thread, NULL, thread_main, rec); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + *ptr_thread = rec; + return PJ_SUCCESS; +#else + pj_assert(!"Threading is disabled!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, ""); + + return rec->obj_name; +#else + return ""; +#endif +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + pj_status_t rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + rc = pj_mutex_unlock(p->suspended_mutex); + + return rc; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = pj_thread_local_get(thread_tls_id); + pj_assert(rec != NULL); + + /* + * MUST NOT check stack because this function is called + * by PJ_CHECK_STACK() itself!!! + * + */ + + return rec; +#else + pj_assert(!"Threading is not enabled!"); + return NULL; +#endif +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t *)p; + void *ret; + int result; + + PJ_CHECK_STACK(); + + PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); + result = pthread_join( rec->thread, &ret); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(result); +#else + PJ_CHECK_STACK(); + pj_assert(!"No multithreading support!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) +{ + /* This function is used to destroy thread handle in other platforms. + * I suppose there's nothing to do here.. + */ + PJ_CHECK_STACK(); + return PJ_SUCCESS; +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + PJ_CHECK_STACK(); + return usleep(msec * 1000); +} + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 +/* + * pj_thread_check_stack() + * Implementation for PJ_CHECK_STACK() + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } +} + +/* + * pj_thread_get_stack_max_usage() + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * pj_thread_get_stack_info() + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, + const char **file, + int *line ) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_atomic_create() + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **ptr_atomic) +{ + pj_status_t rc; + pj_atomic_t *atomic_var = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); + if (!atomic_var) + return PJ_ENOMEM; + +#if PJ_HAS_THREADS + rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex); + if (rc != PJ_SUCCESS) + return rc; +#endif + atomic_var->value = initial; + + *ptr_atomic = atomic_var; + return PJ_SUCCESS; +} + +/* + * pj_atomic_destroy() + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) +{ + PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); +#if PJ_HAS_THREADS + return pj_mutex_destroy( atomic_var->mutex ); +#else + return 0; +#endif +} + +/* + * pj_atomic_set() + */ +PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value) +{ + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + atomic_var->value = value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif +} + +/* + * pj_atomic_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t oldval; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + oldval = atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + return oldval; +} + +/* + * pj_atomic_inc_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t new_value; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + new_value = ++atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + + return new_value; +} +/* + * pj_atomic_inc() + */ +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + pj_atomic_inc_and_get(atomic_var); +} + +/* + * pj_atomic_dec_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t new_value; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + new_value = --atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + + return new_value; +} + +/* + * pj_atomic_dec() + */ +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + pj_atomic_dec_and_get(atomic_var); +} + +/* + * pj_atomic_add_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ + pj_atomic_value_t new_value; + +#if PJ_HAS_THREADS + pj_mutex_lock(atomic_var->mutex); +#endif + + atomic_var->value += value; + new_value = atomic_var->value; + +#if PJ_HAS_THREADS + pj_mutex_unlock(atomic_var->mutex); +#endif + + return new_value; +} + +/* + * pj_atomic_add() + */ +PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ + pj_atomic_add_and_get(atomic_var, value); +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index) +{ +#if PJ_HAS_THREADS + pthread_key_t key; + int rc; + + PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL); + + pj_assert( sizeof(pthread_key_t) <= sizeof(long)); + if ((rc=pthread_key_create(&key, NULL)) != 0) + return PJ_RETURN_OS_ERROR(rc); + + *p_index = key; + return PJ_SUCCESS; +#else + int i; + for (i=0; i<MAX_THREADS; ++i) { + if (tls_flag[i] == 0) + break; + } + if (i == MAX_THREADS) + return PJ_ETOOMANY; + + tls_flag[i] = 1; + tls[i] = NULL; + + *p_index = i; + return PJ_SUCCESS; +#endif +} + +/* + * pj_thread_local_free() + */ +PJ_DEF(void) pj_thread_local_free(long index) +{ + PJ_CHECK_STACK(); +#if PJ_HAS_THREADS + pthread_key_delete(index); +#else + tls_flag[index] = 0; +#endif +} + +/* + * pj_thread_local_set() + */ +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + //Can't check stack because this function is called in the + //beginning before main thread is initialized. + //PJ_CHECK_STACK(); +#if PJ_HAS_THREADS + int rc=pthread_setspecific(index, value); + return rc==0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc); +#else + pj_assert(index >= 0 && index < MAX_THREADS); + tls[index] = value; + return PJ_SUCCESS; +#endif +} + +PJ_DEF(void*) pj_thread_local_get(long index) +{ + //Can't check stack because this function is called + //by PJ_CHECK_STACK() itself!!! + //PJ_CHECK_STACK(); +#if PJ_HAS_THREADS + return pthread_getspecific(index); +#else + pj_assert(index >= 0 && index < MAX_THREADS); + return tls[index]; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void) pj_enter_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_lock(&critical_section); +#endif +} + +PJ_DEF(void) pj_leave_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_unlock(&critical_section); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type) +{ +#if PJ_HAS_THREADS + pthread_mutexattr_t attr; + int rc; + + PJ_CHECK_STACK(); + + pthread_mutexattr_init(&attr); + + if (type == PJ_MUTEX_SIMPLE) { +#if defined(PJ_LINUX) && PJ_LINUX!=0 + extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int); + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP); +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); +#endif + } else { +#if defined(PJ_LINUX) && PJ_LINUX!=0 + extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int); + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + } + + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + rc = pthread_mutex_init(&mutex->mutex, &attr); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + +#if PJ_DEBUG + /* Set owner. */ + mutex->nesting_level = 0; + mutex->owner = NULL; +#endif + + /* Set name. */ + if (!name) { + name = "mtx%p"; + } + if (strchr(name, '%')) { + pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); + } else { + strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); + mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (mutex->obj_name, "Mutex created")); + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_create() + */ +PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **ptr_mutex) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL); + + mutex = pj_pool_alloc(pool, sizeof(*mutex)); + if (!mutex) return PJ_ENOMEM; + + if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS) + return rc; + + *ptr_mutex = mutex; + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + return (pj_mutex_t*)1; +#endif +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +/* + * pj_mutex_create_recursive() + */ +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", + pj_thread_this()->obj_name)); + + status = pthread_mutex_lock( &mutex->mutex ); + + PJ_LOG(6,(mutex->obj_name, + (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + if (status == PJ_SUCCESS) { + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; + } +#endif + + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + pj_assert(mutex->owner == pj_thread_this()); + if (--mutex->nesting_level == 0) { + mutex->owner = NULL; + } +#endif + + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", + pj_thread_this()->obj_name)); + + status = pthread_mutex_unlock( &mutex->mutex ); + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); + +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + int status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + status = pthread_mutex_trylock( &mutex->mutex ); + + if (status==0) { + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; +#endif + } + + if (status==0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + int status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_HAS_THREADS + PJ_LOG(6,(mutex->obj_name, "Mutex destroyed")); + status = pthread_mutex_destroy( &mutex->mutex ); + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else + pj_assert( mutex == (pj_mutex_t*)1 ); + status = PJ_SUCCESS; + return status; +#endif +} + +#if PJ_DEBUG +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + return mutex->owner == pj_thread_this(); +#else + return 1; +#endif +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +/* + * pj_sem_create() + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **ptr_sem) +{ +#if PJ_HAS_THREADS + pj_sem_t *sem; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(*sem)); + if (!sem) return PJ_ENOMEM; + + if (sem_init( &sem->sem, 0, initial) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + + /* Set name. */ + if (!name) { + name = "sem%p"; + } + if (strchr(name, '%')) { + pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); + } else { + strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); + sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (sem->obj_name, "Semaphore created")); + + *ptr_sem = sem; + return PJ_SUCCESS; +#else + *ptr_sem = (pj_sem_t*)1; + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_wait() + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = sem_wait( &sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_trywait() + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + result = sem_trywait( &sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_post() + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); + result = sem_post( &sem->sem ); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_destroy() + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); + result = sem_destroy( &sem->sem ); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +#endif /* PJ_HAS_SEMAPHORE */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 + +/* + * pj_event_create() + */ +PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, + pj_bool_t manual_reset, pj_bool_t initial, + pj_event_t **ptr_event) +{ + pj_assert(!"Not supported!"); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(manual_reset); + PJ_UNUSED_ARG(initial); + PJ_UNUSED_ARG(ptr_event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_wait() + */ +PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_trywait() + */ +PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_set() + */ +PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_pulse() + */ +PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_reset() + */ +PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_destroy() + */ +PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 +/* + * Terminal + */ + +/** + * Set terminal color. + */ +PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) +{ + PJ_UNUSED_ARG(color); + return PJ_EINVALIDOP; +} + +/** + * Get current terminal foreground color. + */ +PJ_DEF(pj_color_t) pj_term_get_color(void) +{ + return 0; +} + +#endif /* PJ_TERM_HAS_COLOR */ + diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index b8e6b281..91add770 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -1,1227 +1,1227 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/rand.h>
-#include <pj/assert.h>
-#include <pj/compat/vsprintf.h>
-#include <pj/compat/sprintf.h>
-#include <pj/errno.h>
-#include <pj/except.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
-# include <winsock.h>
-#endif
-
-#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
-# include <winsock2.h>
-#endif
-
-/*
- * Implementation of pj_thread_t.
- */
-struct pj_thread_t
-{
- char obj_name[PJ_MAX_OBJ_NAME];
- HANDLE hthread;
- DWORD idthread;
- pj_thread_proc *proc;
- void *arg;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- pj_uint32_t stk_size;
- pj_uint32_t stk_max_usage;
- char *stk_start;
- const char *caller_file;
- int caller_line;
-#endif
-};
-
-
-/*
- * Implementation of pj_mutex_t.
- */
-struct pj_mutex_t
-{
-#if PJ_WIN32_WINNT >= 0x0400
- CRITICAL_SECTION crit;
-#else
- HANDLE hMutex;
-#endif
- char obj_name[PJ_MAX_OBJ_NAME];
-#if PJ_DEBUG
- int nesting_level;
- pj_thread_t *owner;
-#endif
-};
-
-/*
- * Implementation of pj_sem_t.
- */
-typedef struct pj_sem_t
-{
- HANDLE hSemaphore;
- char obj_name[PJ_MAX_OBJ_NAME];
-} pj_mem_t;
-
-
-/*
- * Implementation of pj_event_t.
- */
-struct pj_event_t
-{
- HANDLE hEvent;
- char obj_name[PJ_MAX_OBJ_NAME];
-};
-
-/*
- * Implementation of pj_atomic_t.
- */
-struct pj_atomic_t
-{
- long value;
-};
-
-/*
- * Static global variables.
- */
-static pj_thread_desc main_thread;
-static long thread_tls_id;
-static pj_mutex_t critical_section_mutex;
-
-
-/*
- * Some static prototypes.
- */
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name);
-
-
-/*
- * pj_init(void).
- * Init PJLIB!
- */
-PJ_DEF(pj_status_t) pj_init(void)
-{
- WSADATA wsa;
- char dummy_guid[32]; /* use maximum GUID length */
- pj_str_t guid;
- pj_status_t rc;
-
- /* Init Winsock.. */
- if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
- return PJ_RETURN_OS_ERROR(WSAGetLastError());
- }
-
- /* Init this thread's TLS. */
- if ((rc=pj_thread_init()) != PJ_SUCCESS) {
- return rc;
- }
-
- /* Init random seed. */
- pj_srand( GetCurrentProcessId() );
-
- /* Startup GUID. */
- guid.ptr = dummy_guid;
- pj_generate_unique_string( &guid );
-
- /* Initialize critical section. */
- if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS)
- return rc;
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Startup timestamp */
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
- {
- pj_timestamp dummy_ts;
- if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) {
- PJ_LOG(1, ("pj_init", "Unable to initialize timestamp"));
- return rc;
- }
- }
-#endif
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_getpid(void)
- */
-PJ_DEF(pj_uint32_t) pj_getpid(void)
-{
- PJ_CHECK_STACK();
- return GetCurrentProcessId();
-}
-
-/*
- * pj_thread_register(..)
- */
-PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
- pj_thread_desc desc,
- pj_thread_t **thread_ptr)
-{
- char stack_ptr;
- pj_status_t rc;
- pj_thread_t *thread = (pj_thread_t *)desc;
- pj_str_t thread_name = pj_str((char*)cstr_thread_name);
-
- /* Size sanity check. */
- if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
- pj_assert(!"Not enough pj_thread_desc size!");
- return PJ_EBUG;
- }
-
- /* If a thread descriptor has been registered before, just return it. */
- if (pj_thread_local_get (thread_tls_id) != 0) {
- *thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
- return PJ_SUCCESS;
- }
-
- /* Initialize and set the thread entry. */
- pj_memset(desc, 0, sizeof(struct pj_thread_t));
- thread->hthread = GetCurrentThread();
- thread->idthread = GetCurrentThreadId();
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- thread->stk_start = &stack_ptr;
- thread->stk_size = 0xFFFFFFFFUL;
- thread->stk_max_usage = 0;
-#else
- stack_ptr = '\0';
-#endif
-
- if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
- pj_sprintf(thread->obj_name, cstr_thread_name, thread->idthread);
- else
- pj_sprintf(thread->obj_name, "thr%p", (void*)thread->idthread);
-
- rc = pj_thread_local_set(thread_tls_id, thread);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *thread_ptr = thread;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_init(void)
- */
-pj_status_t pj_thread_init(void)
-{
- pj_status_t rc;
- pj_thread_t *thread;
-
- rc = pj_thread_local_alloc(&thread_tls_id);
- if (rc != PJ_SUCCESS)
- return rc;
-
- return pj_thread_register("thr%p", main_thread, &thread);
-}
-
-static DWORD WINAPI thread_main(void *param)
-{
- pj_thread_t *rec = param;
- DWORD result;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_start = (char*)&rec;
-#endif
-
- PJ_LOG(6,(rec->obj_name, "Thread started"));
-
- if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) {
- pj_assert(!"TLS is not set (pj_init() error?)");
- }
-
- result = (*rec->proc)(rec->arg);
-
- PJ_LOG(6,(rec->obj_name, "Thread quitting"));
- return (DWORD)result;
-}
-
-/*
- * pj_thread_create(...)
- */
-PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
- const char *thread_name,
- pj_thread_proc *proc,
- void *arg,
- pj_size_t stack_size,
- unsigned flags,
- pj_thread_t **thread_ptr)
-{
- DWORD dwflags = 0;
- pj_thread_t *rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL);
-
- /* Set flags */
- if (flags & PJ_THREAD_SUSPENDED)
- dwflags |= CREATE_SUSPENDED;
-
- /* Create thread record and assign name for the thread */
- rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
- if (!rec)
- return PJ_ENOMEM;
-
- /* Set name. */
- if (!thread_name)
- thread_name = "thr%p";
-
- if (strchr(thread_name, '%')) {
- pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
- } else {
- strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
- rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (rec->obj_name, "Thread created"));
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
- rec->stk_max_usage = 0;
-#endif
-
- /* Create the thread. */
- rec->proc = proc;
- rec->arg = arg;
- rec->hthread = CreateThread(NULL, stack_size,
- thread_main, rec,
- dwflags, &rec->idthread);
- if (rec->hthread == NULL)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- /* Success! */
- *thread_ptr = rec;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread-get_name()
- */
-PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t*)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, "");
-
- return rec->obj_name;
-}
-
-/*
- * pj_thread_resume()
- */
-PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t*)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- if (ResumeThread(rec->hthread) == (DWORD)-1)
- return PJ_RETURN_OS_ERROR(GetLastError());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_this()
- */
-PJ_DEF(pj_thread_t*) pj_thread_this(void)
-{
- pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
- pj_assert(rec != NULL);
-
- /*
- * MUST NOT check stack because this function is called
- * by PJ_CHECK_STACK() itself!!!
- *
- */
-
- return rec;
-}
-
-/*
- * pj_thread_join()
- */
-PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t *)p;
- DWORD rc;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
-
- rc = WaitForSingleObject(rec->hthread, INFINITE);
-
- if (rc==WAIT_OBJECT_0)
- return PJ_SUCCESS;
- else if (rc==WAIT_TIMEOUT)
- return PJ_ETIMEDOUT;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_thread_destroy()
- */
-PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t *)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- if (CloseHandle(rec->hthread) == TRUE)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_thread_sleep()
- */
-PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
-{
- PJ_CHECK_STACK();
- Sleep(msec);
- return PJ_SUCCESS;
-}
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
-/*
- * pj_thread_check_stack()
- * Implementation for PJ_CHECK_STACK()
- */
-PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
-{
- char stk_ptr;
- pj_uint32_t usage;
- pj_thread_t *thread = pj_thread_this();
-
- pj_assert(thread);
-
- /* Calculate current usage. */
- usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
- thread->stk_start - &stk_ptr;
-
- /* Assert if stack usage is dangerously high. */
- pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
-
- /* Keep statistic. */
- if (usage > thread->stk_max_usage) {
- thread->stk_max_usage = usage;
- thread->caller_file = file;
- thread->caller_line = line;
- }
-
-}
-
-/*
- * pj_thread_get_stack_max_usage()
- */
-PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
-{
- return thread->stk_max_usage;
-}
-
-/*
- * pj_thread_get_stack_info()
- */
-PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
- const char **file,
- int *line )
-{
- pj_assert(thread);
-
- *file = thread->caller_file;
- *line = thread->caller_line;
- return 0;
-}
-
-#endif /* PJ_OS_HAS_CHECK_STACK */
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-/*
- * pj_atomic_create()
- */
-PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t initial,
- pj_atomic_t **atomic_ptr)
-{
- pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
- if (!atomic_var)
- return PJ_ENOMEM;
-
- atomic_var->value = initial;
- *atomic_ptr = atomic_var;
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_atomic_destroy()
- */
-PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
-{
- PJ_UNUSED_ARG(var);
- PJ_ASSERT_RETURN(var, PJ_EINVAL);
-
- return 0;
-}
-
-/*
- * pj_atomic_set()
- */
-PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
-{
- PJ_CHECK_STACK();
-
- InterlockedExchange(&atomic_var->value, value);
-}
-
-/*
- * pj_atomic_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(atomic_var, 0);
-
- return atomic_var->value;
-}
-
-/*
- * pj_atomic_inc_and_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
-{
- PJ_CHECK_STACK();
-
-#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
- return InterlockedIncrement(&atomic_var->value);
-#else
-# error Fix Me
-#endif
-}
-
-/*
- * pj_atomic_inc()
- */
-PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
-{
- pj_atomic_inc_and_get(atomic_var);
-}
-
-/*
- * pj_atomic_dec_and_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
-{
- PJ_CHECK_STACK();
-
-#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
- return InterlockedDecrement(&atomic_var->value);
-#else
-# error Fix me
-#endif
-}
-
-/*
- * pj_atomic_dec()
- */
-PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
-{
- pj_atomic_dec_and_get(atomic_var);
-}
-
-/*
- * pj_atomic_add()
- */
-PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
- pj_atomic_value_t value )
-{
-#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
- InterlockedExchangeAdd( &atomic_var->value, value );
-#else
-# error Fix me
-#endif
-}
-
-/*
- * pj_atomic_add_and_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
- pj_atomic_value_t value)
-{
-#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
- long oldValue = InterlockedExchangeAdd( &atomic_var->value, value);
- return oldValue + value;
-#else
-# error Fix me
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_thread_local_alloc()
- */
-PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
-{
- PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL);
-
- //Can't check stack because this function is called in the
- //beginning before main thread is initialized.
- //PJ_CHECK_STACK();
-
- *index = TlsAlloc();
-
- if (*index == TLS_OUT_OF_INDEXES)
- return PJ_RETURN_OS_ERROR(GetLastError());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_local_free()
- */
-PJ_DEF(void) pj_thread_local_free(long index)
-{
- PJ_CHECK_STACK();
- TlsFree(index);
-}
-
-/*
- * pj_thread_local_set()
- */
-PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
-{
- BOOL rc;
-
- //Can't check stack because this function is called in the
- //beginning before main thread is initialized.
- //PJ_CHECK_STACK();
- rc = TlsSetValue(index, value);
- return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_thread_local_get()
- */
-PJ_DEF(void*) pj_thread_local_get(long index)
-{
- //Can't check stack because this function is called
- //by PJ_CHECK_STACK() itself!!!
- //PJ_CHECK_STACK();
- return TlsGetValue(index);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name)
-{
-
- PJ_CHECK_STACK();
-
-#if PJ_WIN32_WINNT >= 0x0400
- InitializeCriticalSection(&mutex->crit);
-#else
- mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
- if (!mutex->hMutex) {
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-#endif
-
-#if PJ_DEBUG
- /* Set owner. */
- mutex->nesting_level = 0;
- mutex->owner = NULL;
-#endif
-
- /* Set name. */
- if (!name) {
- name = "mtx%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
- } else {
- strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
- mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (mutex->obj_name, "Mutex created"));
- return PJ_SUCCESS;
-}
-
-/*
- * pj_mutex_create()
- */
-PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **mutex_ptr)
-{
- pj_status_t rc;
- pj_mutex_t *mutex;
-
- PJ_UNUSED_ARG(type);
- PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL);
-
- mutex = pj_pool_alloc(pool, sizeof(*mutex));
- if (!mutex)
- return PJ_ENOMEM;
-
- rc = init_mutex(mutex, name);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *mutex_ptr = mutex;
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_mutex_create_simple()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
-}
-
-/*
- * pj_mutex_create_recursive()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
-}
-
-/*
- * pj_mutex_lock()
- */
-PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
-{
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
- pj_thread_this()->obj_name));
-
-#if PJ_WIN32_WINNT >= 0x0400
- EnterCriticalSection(&mutex->crit);
- status=PJ_SUCCESS;
-#else
- if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0)
- status = PJ_SUCCESS;
- else
- status = PJ_STATUS_FROM_OS(GetLastError());
-
-#endif
- PJ_LOG(6,(mutex->obj_name,
- (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"),
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- if (status == PJ_SUCCESS) {
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
- }
-#endif
-
- return status;
-}
-
-/*
- * pj_mutex_unlock()
- */
-PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
-{
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_DEBUG
- pj_assert(mutex->owner == pj_thread_this());
- if (--mutex->nesting_level == 0) {
- mutex->owner = NULL;
- }
-#endif
-
- PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
- pj_thread_this()->obj_name));
-
-#if PJ_WIN32_WINNT >= 0x0400
- LeaveCriticalSection(&mutex->crit);
- status=PJ_SUCCESS;
-#else
- status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS :
- PJ_STATUS_FROM_OS(GetLastError());
-#endif
- return status;
-}
-
-/*
- * pj_mutex_trylock()
- */
-PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
-{
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_WIN32_WINNT >= 0x0400
- status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN;
-#else
- status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ?
- PJ_SUCCESS : PJ_ETIMEDOUT;
-#endif
- if (status==PJ_SUCCESS) {
- PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
-#endif
- }
- return status;
-}
-
-/*
- * pj_mutex_destroy()
- */
-PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
-
-#if PJ_WIN32_WINNT >= 0x0400
- DeleteCriticalSection(&mutex->crit);
- return PJ_SUCCESS;
-#else
- return CloseHandle(mutex->hMutex) ? PJ_SUCCESS :
- PJ_RETURN_OS_ERROR(GetLastError());
-#endif
-}
-
-#if PJ_DEBUG
-/*
- * pj_mutex_is_locked()
- */
-PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
-{
- return mutex->owner == pj_thread_this();
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_enter_critical_section()
- */
-PJ_DEF(void) pj_enter_critical_section(void)
-{
- pj_mutex_lock(&critical_section_mutex);
-}
-
-
-/*
- * pj_leave_critical_section()
- */
-PJ_DEF(void) pj_leave_critical_section(void)
-{
- pj_mutex_unlock(&critical_section_mutex);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-/*
- * pj_sem_create()
- */
-PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **sem_ptr)
-{
- pj_sem_t *sem;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL);
-
- sem = pj_pool_alloc(pool, sizeof(*sem));
- sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
- if (!sem->hSemaphore)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- /* Set name. */
- if (!name) {
- name = "sem%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
- } else {
- strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
- sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (sem->obj_name, "Semaphore created"));
-
- *sem_ptr = sem;
- return PJ_SUCCESS;
-}
-
-static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
-{
- DWORD result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- result = WaitForSingleObject(sem->hSemaphore, timeout);
- if (result == WAIT_OBJECT_0) {
- PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
- pj_thread_this()->obj_name));
- } else {
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
- pj_thread_this()->obj_name));
- }
-
- if (result==WAIT_OBJECT_0)
- return PJ_SUCCESS;
- else if (result==WAIT_TIMEOUT)
- return PJ_ETIMEDOUT;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_sem_wait()
- */
-PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- return pj_sem_wait_for(sem, INFINITE);
-}
-
-/*
- * pj_sem_trywait()
- */
-PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- return pj_sem_wait_for(sem, 0);
-}
-
-/*
- * pj_sem_post()
- */
-PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
- pj_thread_this()->obj_name));
-
- if (ReleaseSemaphore(sem->hSemaphore, 1, NULL))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_sem_destroy()
- */
-PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
- pj_thread_this()->obj_name));
-
- if (CloseHandle(sem->hSemaphore))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-#endif /* PJ_HAS_SEMAPHORE */
-///////////////////////////////////////////////////////////////////////////////
-
-
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-
-/*
- * pj_event_create()
- */
-PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool,
- const char *name,
- pj_bool_t manual_reset,
- pj_bool_t initial,
- pj_event_t **event_ptr)
-{
- pj_event_t *event;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL);
-
- event = pj_pool_alloc(pool, sizeof(*event));
- if (!event)
- return PJ_ENOMEM;
-
- event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE,
- initial?TRUE:FALSE, NULL);
-
- if (!event->hEvent)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- /* Set name. */
- if (!name) {
- name = "evt%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
- } else {
- strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
- event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (event->obj_name, "Event created"));
-
- *event_ptr = event;
- return PJ_SUCCESS;
-}
-
-static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
-{
- DWORD result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- result = WaitForSingleObject(event->hEvent, timeout);
- if (result == WAIT_OBJECT_0) {
- PJ_LOG(6, (event->obj_name, "Event: thread %s is released",
- pj_thread_this()->obj_name));
- } else {
- PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire",
- pj_thread_this()->obj_name));
- }
-
- if (result==WAIT_OBJECT_0)
- return PJ_SUCCESS;
- else if (result==WAIT_TIMEOUT)
- return PJ_ETIMEDOUT;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_wait()
- */
-PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
-{
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- return pj_event_wait_for(event, INFINITE);
-}
-
-/*
- * pj_event_trywait()
- */
-PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
-{
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- return pj_event_wait_for(event, 0);
-}
-
-/*
- * pj_event_set()
- */
-PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Setting event"));
-
- if (SetEvent(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_pulse()
- */
-PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Pulsing event"));
-
- if (PulseEvent(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_reset()
- */
-PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Event is reset"));
-
- if (ResetEvent(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_destroy()
- */
-PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Event is destroying"));
-
- if (CloseHandle(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-#endif /* PJ_HAS_EVENT_OBJ */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-/*
- * Terminal color
- */
-
-static WORD pj_color_to_os_attr(pj_color_t color)
-{
- WORD attr = 0;
-
- if (color & PJ_TERM_COLOR_R)
- attr |= FOREGROUND_RED;
- if (color & PJ_TERM_COLOR_G)
- attr |= FOREGROUND_GREEN;
- if (color & PJ_TERM_COLOR_B)
- attr |= FOREGROUND_BLUE;
- if (color & PJ_TERM_COLOR_BRIGHT)
- attr |= FOREGROUND_INTENSITY;
-
- return attr;
-}
-
-static pj_color_t os_attr_to_pj_color(WORD attr)
-{
- int color = 0;
-
- if (attr & FOREGROUND_RED)
- color |= PJ_TERM_COLOR_R;
- if (attr & FOREGROUND_GREEN)
- color |= PJ_TERM_COLOR_G;
- if (attr & FOREGROUND_BLUE)
- color |= PJ_TERM_COLOR_B;
- if (attr & FOREGROUND_INTENSITY)
- color |= PJ_TERM_COLOR_BRIGHT;
-
- return color;
-}
-
-
-/*
- * pj_term_set_color()
- */
-PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
-{
- BOOL rc;
- WORD attr = 0;
-
- PJ_CHECK_STACK();
-
- attr = pj_color_to_os_attr(color);
- rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
- return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_term_get_color()
- * Get current terminal foreground color.
- */
-PJ_DEF(pj_color_t) pj_term_get_color(void)
-{
- CONSOLE_SCREEN_BUFFER_INFO info;
-
- PJ_CHECK_STACK();
-
- GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
- return os_attr_to_pj_color(info.wAttributes);
-}
-
-#endif /* PJ_TERM_HAS_COLOR */
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <pj/pool.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/guid.h> +#include <pj/rand.h> +#include <pj/assert.h> +#include <pj/compat/vsprintf.h> +#include <pj/compat/sprintf.h> +#include <pj/errno.h> +#include <pj/except.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> + +#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include <winsock.h> +#endif + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include <winsock2.h> +#endif + +/* + * Implementation of pj_thread_t. + */ +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + HANDLE hthread; + DWORD idthread; + pj_thread_proc *proc; + void *arg; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif +}; + + +/* + * Implementation of pj_mutex_t. + */ +struct pj_mutex_t +{ +#if PJ_WIN32_WINNT >= 0x0400 + CRITICAL_SECTION crit; +#else + HANDLE hMutex; +#endif + char obj_name[PJ_MAX_OBJ_NAME]; +#if PJ_DEBUG + int nesting_level; + pj_thread_t *owner; +#endif +}; + +/* + * Implementation of pj_sem_t. + */ +typedef struct pj_sem_t +{ + HANDLE hSemaphore; + char obj_name[PJ_MAX_OBJ_NAME]; +} pj_mem_t; + + +/* + * Implementation of pj_event_t. + */ +struct pj_event_t +{ + HANDLE hEvent; + char obj_name[PJ_MAX_OBJ_NAME]; +}; + +/* + * Implementation of pj_atomic_t. + */ +struct pj_atomic_t +{ + long value; +}; + +/* + * Static global variables. + */ +static pj_thread_desc main_thread; +static long thread_tls_id; +static pj_mutex_t critical_section_mutex; + + +/* + * Some static prototypes. + */ +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name); + + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + WSADATA wsa; + char dummy_guid[32]; /* use maximum GUID length */ + pj_str_t guid; + pj_status_t rc; + + /* Init Winsock.. */ + if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Init this thread's TLS. */ + if ((rc=pj_thread_init()) != PJ_SUCCESS) { + return rc; + } + + /* Init random seed. */ + pj_srand( GetCurrentProcessId() ); + + /* Startup GUID. */ + guid.ptr = dummy_guid; + pj_generate_unique_string( &guid ); + + /* Initialize critical section. */ + if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS) + return rc; + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Startup timestamp */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + { + pj_timestamp dummy_ts; + if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) { + PJ_LOG(1, ("pj_init", "Unable to initialize timestamp")); + return rc; + } + } +#endif + + return PJ_SUCCESS; +} + +/* + * pj_getpid(void) + */ +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + PJ_CHECK_STACK(); + return GetCurrentProcessId(); +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **thread_ptr) +{ + char stack_ptr; + pj_status_t rc; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + *thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_memset(desc, 0, sizeof(struct pj_thread_t)); + thread->hthread = GetCurrentThread(); + thread->idthread = GetCurrentThreadId(); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_sprintf(thread->obj_name, cstr_thread_name, thread->idthread); + else + pj_sprintf(thread->obj_name, "thr%p", (void*)thread->idthread); + + rc = pj_thread_local_set(thread_tls_id, thread); + if (rc != PJ_SUCCESS) + return rc; + + *thread_ptr = thread; + return PJ_SUCCESS; +} + +/* + * pj_thread_init(void) + */ +pj_status_t pj_thread_init(void) +{ + pj_status_t rc; + pj_thread_t *thread; + + rc = pj_thread_local_alloc(&thread_tls_id); + if (rc != PJ_SUCCESS) + return rc; + + return pj_thread_register("thr%p", main_thread, &thread); +} + +static DWORD WINAPI thread_main(void *param) +{ + pj_thread_t *rec = param; + DWORD result; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_start = (char*)&rec; +#endif + + PJ_LOG(6,(rec->obj_name, "Thread started")); + + if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) { + pj_assert(!"TLS is not set (pj_init() error?)"); + } + + result = (*rec->proc)(rec->arg); + + PJ_LOG(6,(rec->obj_name, "Thread quitting")); + return (DWORD)result; +} + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **thread_ptr) +{ + DWORD dwflags = 0; + pj_thread_t *rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL); + + /* Set flags */ + if (flags & PJ_THREAD_SUSPENDED) + dwflags |= CREATE_SUSPENDED; + + /* Create thread record and assign name for the thread */ + rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t)); + if (!rec) + return PJ_ENOMEM; + + /* Set name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); + } else { + strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); + rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (rec->obj_name, "Thread created")); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL; + rec->stk_max_usage = 0; +#endif + + /* Create the thread. */ + rec->proc = proc; + rec->arg = arg; + rec->hthread = CreateThread(NULL, stack_size, + thread_main, rec, + dwflags, &rec->idthread); + if (rec->hthread == NULL) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Success! */ + *thread_ptr = rec; + return PJ_SUCCESS; +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, ""); + + return rec->obj_name; +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + if (ResumeThread(rec->hthread) == (DWORD)-1) + return PJ_RETURN_OS_ERROR(GetLastError()); + else + return PJ_SUCCESS; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ + pj_thread_t *rec = pj_thread_local_get(thread_tls_id); + pj_assert(rec != NULL); + + /* + * MUST NOT check stack because this function is called + * by PJ_CHECK_STACK() itself!!! + * + */ + + return rec; +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t *)p; + DWORD rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); + + rc = WaitForSingleObject(rec->hthread, INFINITE); + + if (rc==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (rc==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t *)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + if (CloseHandle(rec->hthread) == TRUE) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + PJ_CHECK_STACK(); + Sleep(msec); + return PJ_SUCCESS; +} + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 +/* + * pj_thread_check_stack() + * Implementation for PJ_CHECK_STACK() + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + pj_assert(thread); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } + +} + +/* + * pj_thread_get_stack_max_usage() + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * pj_thread_get_stack_info() + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, + const char **file, + int *line ) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + + +/////////////////////////////////////////////////////////////////////////////// + +/* + * pj_atomic_create() + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **atomic_ptr) +{ + pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t)); + if (!atomic_var) + return PJ_ENOMEM; + + atomic_var->value = initial; + *atomic_ptr = atomic_var; + + return PJ_SUCCESS; +} + +/* + * pj_atomic_destroy() + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) +{ + PJ_UNUSED_ARG(var); + PJ_ASSERT_RETURN(var, PJ_EINVAL); + + return 0; +} + +/* + * pj_atomic_set() + */ +PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value) +{ + PJ_CHECK_STACK(); + + InterlockedExchange(&atomic_var->value, value); +} + +/* + * pj_atomic_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + + return atomic_var->value; +} + +/* + * pj_atomic_inc_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + return InterlockedIncrement(&atomic_var->value); +#else +# error Fix Me +#endif +} + +/* + * pj_atomic_inc() + */ +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + pj_atomic_inc_and_get(atomic_var); +} + +/* + * pj_atomic_dec_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + return InterlockedDecrement(&atomic_var->value); +#else +# error Fix me +#endif +} + +/* + * pj_atomic_dec() + */ +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + pj_atomic_dec_and_get(atomic_var); +} + +/* + * pj_atomic_add() + */ +PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + InterlockedExchangeAdd( &atomic_var->value, value ); +#else +# error Fix me +#endif +} + +/* + * pj_atomic_add_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, + pj_atomic_value_t value) +{ +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + long oldValue = InterlockedExchangeAdd( &atomic_var->value, value); + return oldValue + value; +#else +# error Fix me +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) +{ + PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL); + + //Can't check stack because this function is called in the + //beginning before main thread is initialized. + //PJ_CHECK_STACK(); + + *index = TlsAlloc(); + + if (*index == TLS_OUT_OF_INDEXES) + return PJ_RETURN_OS_ERROR(GetLastError()); + else + return PJ_SUCCESS; +} + +/* + * pj_thread_local_free() + */ +PJ_DEF(void) pj_thread_local_free(long index) +{ + PJ_CHECK_STACK(); + TlsFree(index); +} + +/* + * pj_thread_local_set() + */ +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + BOOL rc; + + //Can't check stack because this function is called in the + //beginning before main thread is initialized. + //PJ_CHECK_STACK(); + rc = TlsSetValue(index, value); + return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_local_get() + */ +PJ_DEF(void*) pj_thread_local_get(long index) +{ + //Can't check stack because this function is called + //by PJ_CHECK_STACK() itself!!! + //PJ_CHECK_STACK(); + return TlsGetValue(index); +} + +/////////////////////////////////////////////////////////////////////////////// +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name) +{ + + PJ_CHECK_STACK(); + +#if PJ_WIN32_WINNT >= 0x0400 + InitializeCriticalSection(&mutex->crit); +#else + mutex->hMutex = CreateMutex(NULL, FALSE, NULL); + if (!mutex->hMutex) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } +#endif + +#if PJ_DEBUG + /* Set owner. */ + mutex->nesting_level = 0; + mutex->owner = NULL; +#endif + + /* Set name. */ + if (!name) { + name = "mtx%p"; + } + if (strchr(name, '%')) { + pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); + } else { + strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); + mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (mutex->obj_name, "Mutex created")); + return PJ_SUCCESS; +} + +/* + * pj_mutex_create() + */ +PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **mutex_ptr) +{ + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_UNUSED_ARG(type); + PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL); + + mutex = pj_pool_alloc(pool, sizeof(*mutex)); + if (!mutex) + return PJ_ENOMEM; + + rc = init_mutex(mutex, name); + if (rc != PJ_SUCCESS) + return rc; + + *mutex_ptr = mutex; + + return PJ_SUCCESS; +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +/* + * pj_mutex_create_recursive() + */ +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", + pj_thread_this()->obj_name)); + +#if PJ_WIN32_WINNT >= 0x0400 + EnterCriticalSection(&mutex->crit); + status=PJ_SUCCESS; +#else + if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0) + status = PJ_SUCCESS; + else + status = PJ_STATUS_FROM_OS(GetLastError()); + +#endif + PJ_LOG(6,(mutex->obj_name, + (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"), + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + if (status == PJ_SUCCESS) { + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; + } +#endif + + return status; +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + pj_assert(mutex->owner == pj_thread_this()); + if (--mutex->nesting_level == 0) { + mutex->owner = NULL; + } +#endif + + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_WIN32_WINNT >= 0x0400 + LeaveCriticalSection(&mutex->crit); + status=PJ_SUCCESS; +#else + status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS : + PJ_STATUS_FROM_OS(GetLastError()); +#endif + return status; +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_WIN32_WINNT >= 0x0400 + status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN; +#else + status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? + PJ_SUCCESS : PJ_ETIMEDOUT; +#endif + if (status==PJ_SUCCESS) { + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; +#endif + } + return status; +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex destroyed")); + +#if PJ_WIN32_WINNT >= 0x0400 + DeleteCriticalSection(&mutex->crit); + return PJ_SUCCESS; +#else + return CloseHandle(mutex->hMutex) ? PJ_SUCCESS : + PJ_RETURN_OS_ERROR(GetLastError()); +#endif +} + +#if PJ_DEBUG +/* + * pj_mutex_is_locked() + */ +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ + return mutex->owner == pj_thread_this(); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_enter_critical_section() + */ +PJ_DEF(void) pj_enter_critical_section(void) +{ + pj_mutex_lock(&critical_section_mutex); +} + + +/* + * pj_leave_critical_section() + */ +PJ_DEF(void) pj_leave_critical_section(void) +{ + pj_mutex_unlock(&critical_section_mutex); +} + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +/* + * pj_sem_create() + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem_ptr) +{ + pj_sem_t *sem; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(*sem)); + sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL); + if (!sem->hSemaphore) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Set name. */ + if (!name) { + name = "sem%p"; + } + if (strchr(name, '%')) { + pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); + } else { + strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); + sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (sem->obj_name, "Semaphore created")); + + *sem_ptr = sem; + return PJ_SUCCESS; +} + +static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) +{ + DWORD result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = WaitForSingleObject(sem->hSemaphore, timeout); + if (result == WAIT_OBJECT_0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (result==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_sem_wait() + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + return pj_sem_wait_for(sem, INFINITE); +} + +/* + * pj_sem_trywait() + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + return pj_sem_wait_for(sem, 0); +} + +/* + * pj_sem_post() + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); + + if (ReleaseSemaphore(sem->hSemaphore, 1, NULL)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_sem_destroy() + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); + + if (CloseHandle(sem->hSemaphore)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +#endif /* PJ_HAS_SEMAPHORE */ +/////////////////////////////////////////////////////////////////////////////// + + +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 + +/* + * pj_event_create() + */ +PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, + const char *name, + pj_bool_t manual_reset, + pj_bool_t initial, + pj_event_t **event_ptr) +{ + pj_event_t *event; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL); + + event = pj_pool_alloc(pool, sizeof(*event)); + if (!event) + return PJ_ENOMEM; + + event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, + initial?TRUE:FALSE, NULL); + + if (!event->hEvent) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Set name. */ + if (!name) { + name = "evt%p"; + } + if (strchr(name, '%')) { + pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event); + } else { + strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME); + event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (event->obj_name, "Event created")); + + *event_ptr = event; + return PJ_SUCCESS; +} + +static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout) +{ + DWORD result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = WaitForSingleObject(event->hEvent, timeout); + if (result == WAIT_OBJECT_0) { + PJ_LOG(6, (event->obj_name, "Event: thread %s is released", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (result==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_wait() + */ +PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) +{ + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + return pj_event_wait_for(event, INFINITE); +} + +/* + * pj_event_trywait() + */ +PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) +{ + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + return pj_event_wait_for(event, 0); +} + +/* + * pj_event_set() + */ +PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Setting event")); + + if (SetEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_pulse() + */ +PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Pulsing event")); + + if (PulseEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_reset() + */ +PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event is reset")); + + if (ResetEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_destroy() + */ +PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event is destroying")); + + if (CloseHandle(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 +/* + * Terminal color + */ + +static WORD pj_color_to_os_attr(pj_color_t color) +{ + WORD attr = 0; + + if (color & PJ_TERM_COLOR_R) + attr |= FOREGROUND_RED; + if (color & PJ_TERM_COLOR_G) + attr |= FOREGROUND_GREEN; + if (color & PJ_TERM_COLOR_B) + attr |= FOREGROUND_BLUE; + if (color & PJ_TERM_COLOR_BRIGHT) + attr |= FOREGROUND_INTENSITY; + + return attr; +} + +static pj_color_t os_attr_to_pj_color(WORD attr) +{ + int color = 0; + + if (attr & FOREGROUND_RED) + color |= PJ_TERM_COLOR_R; + if (attr & FOREGROUND_GREEN) + color |= PJ_TERM_COLOR_G; + if (attr & FOREGROUND_BLUE) + color |= PJ_TERM_COLOR_B; + if (attr & FOREGROUND_INTENSITY) + color |= PJ_TERM_COLOR_BRIGHT; + + return color; +} + + +/* + * pj_term_set_color() + */ +PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) +{ + BOOL rc; + WORD attr = 0; + + PJ_CHECK_STACK(); + + attr = pj_color_to_os_attr(color); + rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr); + return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_term_get_color() + * Get current terminal foreground color. + */ +PJ_DEF(pj_color_t) pj_term_get_color(void) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + + PJ_CHECK_STACK(); + + GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info); + return os_attr_to_pj_color(info.wAttributes); +} + +#endif /* PJ_TERM_HAS_COLOR */ diff --git a/pjlib/src/pj/os_error_linux_kernel.c b/pjlib/src/pj/os_error_linux_kernel.c index b4f661e4..e9c7e2af 100644 --- a/pjlib/src/pj/os_error_linux_kernel.c +++ b/pjlib/src/pj/os_error_linux_kernel.c @@ -1,80 +1,80 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/string.h>
-#include <pj/compat/errno.h>
-#include <linux/config.h>
-#include <linux/version.h>
-#if defined(MODVERSIONS)
-#include <linux/modversions.h>
-#endif
-#include <linux/kernel.h>
-#include <linux/errno.h>
-
-int kernel_errno;
-
-PJ_DEF(pj_status_t) pj_get_os_error(void)
-{
- return errno;
-}
-
-PJ_DEF(void) pj_set_os_error(pj_status_t code)
-{
- errno = code;
-}
-
-PJ_DEF(pj_status_t) pj_get_netos_error(void)
-{
- return errno;
-}
-
-PJ_DEF(void) pj_set_netos_error(pj_status_t code)
-{
- errno = code;
-}
-
-/*
- * platform_strerror()
- *
- * Platform specific error message. This file is called by pj_strerror()
- * in errno.c
- */
-int platform_strerror( pj_os_err_type os_errcode,
- char *buf, pj_size_t bufsize)
-{
- char errmsg[32];
- int len;
-
- /* Handle EINVAL as special case so that it'll pass errno test. */
- if (os_errcode==EINVAL)
- strcpy(errmsg, "Invalid value");
- else
- sprintf(errmsg, "errno=%d", os_errcode);
-
- len = strlen(errmsg);
-
- if (len >= bufsize)
- len = bufsize-1;
-
- pj_memcpy(buf, errmsg, len);
- buf[len] = '\0';
-
- return len;
-}
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/string.h> +#include <pj/compat/errno.h> +#include <linux/config.h> +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/kernel.h> +#include <linux/errno.h> + +int kernel_errno; + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return errno; +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + errno = code; +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return errno; +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + errno = code; +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + char errmsg[32]; + int len; + + /* Handle EINVAL as special case so that it'll pass errno test. */ + if (os_errcode==EINVAL) + strcpy(errmsg, "Invalid value"); + else + sprintf(errmsg, "errno=%d", os_errcode); + + len = strlen(errmsg); + + if (len >= bufsize) + len = bufsize-1; + + pj_memcpy(buf, errmsg, len); + buf[len] = '\0'; + + return len; +} + + diff --git a/pjlib/src/pj/os_error_unix.c b/pjlib/src/pj/os_error_unix.c index 5eee7765..0f4fbe57 100644 --- a/pjlib/src/pj/os_error_unix.c +++ b/pjlib/src/pj/os_error_unix.c @@ -1,62 +1,62 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/errno.h>
-#include <pj/string.h>
-#include <errno.h>
-
-PJ_DEF(pj_status_t) pj_get_os_error(void)
-{
- return PJ_STATUS_FROM_OS(errno);
-}
-
-PJ_DEF(void) pj_set_os_error(pj_status_t code)
-{
- errno = PJ_STATUS_TO_OS(code);
-}
-
-PJ_DEF(pj_status_t) pj_get_netos_error(void)
-{
- return PJ_STATUS_FROM_OS(errno);
-}
-
-PJ_DEF(void) pj_set_netos_error(pj_status_t code)
-{
- errno = PJ_STATUS_TO_OS(code);
-}
-
-/*
- * platform_strerror()
- *
- * Platform specific error message. This file is called by pj_strerror()
- * in errno.c
- */
-int platform_strerror( pj_os_err_type os_errcode,
- char *buf, pj_size_t bufsize)
-{
- const char *syserr = strerror(os_errcode);
- pj_size_t len = syserr ? strlen(syserr) : 0;
-
- if (len >= bufsize) len = bufsize - 1;
- if (len > 0)
- pj_memcpy(buf, syserr, len);
- buf[len] = '\0';
- return len;
-}
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/errno.h> +#include <pj/string.h> +#include <errno.h> + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return PJ_STATUS_FROM_OS(errno); +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + errno = PJ_STATUS_TO_OS(code); +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return PJ_STATUS_FROM_OS(errno); +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + errno = PJ_STATUS_TO_OS(code); +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + const char *syserr = strerror(os_errcode); + pj_size_t len = syserr ? strlen(syserr) : 0; + + if (len >= bufsize) len = bufsize - 1; + if (len > 0) + pj_memcpy(buf, syserr, len); + buf[len] = '\0'; + return len; +} + + diff --git a/pjlib/src/pj/os_error_win32.c b/pjlib/src/pj/os_error_win32.c index 27147047..cbc87eba 100644 --- a/pjlib/src/pj/os_error_win32.c +++ b/pjlib/src/pj/os_error_win32.c @@ -1,165 +1,165 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/errno.h>
-#include <pj/assert.h>
-#include <pj/compat/stdarg.h>
-#include <pj/compat/sprintf.h>
-#include <pj/compat/vsprintf.h>
-#include <pj/string.h>
-
-
-#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
-# include <winsock2.h>
-#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
-# include <winsock.h>
-#endif
-
-
-/*
- * From Apache's APR:
- */
-static const struct {
- pj_os_err_type code;
- const char *msg;
-} gaErrorList[] = {
- {WSAEINTR, "Interrupted system call"},
- {WSAEBADF, "Bad file number"},
- {WSAEACCES, "Permission denied"},
- {WSAEFAULT, "Bad address"},
- {WSAEINVAL, "Invalid argument"},
- {WSAEMFILE, "Too many open sockets"},
- {WSAEWOULDBLOCK, "Operation would block"},
- {WSAEINPROGRESS, "Operation now in progress"},
- {WSAEALREADY, "Operation already in progress"},
- {WSAENOTSOCK, "Socket operation on non-socket"},
- {WSAEDESTADDRREQ, "Destination address required"},
- {WSAEMSGSIZE, "Message too long"},
- {WSAEPROTOTYPE, "Protocol wrong type for socket"},
- {WSAENOPROTOOPT, "Bad protocol option"},
- {WSAEPROTONOSUPPORT, "Protocol not supported"},
- {WSAESOCKTNOSUPPORT, "Socket type not supported"},
- {WSAEOPNOTSUPP, "Operation not supported on socket"},
- {WSAEPFNOSUPPORT, "Protocol family not supported"},
- {WSAEAFNOSUPPORT, "Address family not supported"},
- {WSAEADDRINUSE, "Address already in use"},
- {WSAEADDRNOTAVAIL, "Can't assign requested address"},
- {WSAENETDOWN, "Network is down"},
- {WSAENETUNREACH, "Network is unreachable"},
- {WSAENETRESET, "Net connection reset"},
- {WSAECONNABORTED, "Software caused connection abort"},
- {WSAECONNRESET, "Connection reset by peer"},
- {WSAENOBUFS, "No buffer space available"},
- {WSAEISCONN, "Socket is already connected"},
- {WSAENOTCONN, "Socket is not connected"},
- {WSAESHUTDOWN, "Can't send after socket shutdown"},
- {WSAETOOMANYREFS, "Too many references, can't splice"},
- {WSAETIMEDOUT, "Connection timed out"},
- {WSAECONNREFUSED, "Connection refused"},
- {WSAELOOP, "Too many levels of symbolic links"},
- {WSAENAMETOOLONG, "File name too long"},
- {WSAEHOSTDOWN, "Host is down"},
- {WSAEHOSTUNREACH, "No route to host"},
- {WSAENOTEMPTY, "Directory not empty"},
- {WSAEPROCLIM, "Too many processes"},
- {WSAEUSERS, "Too many users"},
- {WSAEDQUOT, "Disc quota exceeded"},
- {WSAESTALE, "Stale NFS file handle"},
- {WSAEREMOTE, "Too many levels of remote in path"},
- {WSASYSNOTREADY, "Network system is unavailable"},
- {WSAVERNOTSUPPORTED, "Winsock version out of range"},
- {WSANOTINITIALISED, "WSAStartup not yet called"},
- {WSAEDISCON, "Graceful shutdown in progress"},
- {WSAHOST_NOT_FOUND, "Host not found"},
- {WSANO_DATA, "No host data of that type was found"},
- {0, NULL}
-};
-
-
-PJ_DEF(pj_status_t) pj_get_os_error(void)
-{
- return PJ_STATUS_FROM_OS(GetLastError());
-}
-
-PJ_DEF(void) pj_set_os_error(pj_status_t code)
-{
- SetLastError(PJ_STATUS_TO_OS(code));
-}
-
-PJ_DEF(pj_status_t) pj_get_netos_error(void)
-{
- return PJ_STATUS_FROM_OS(WSAGetLastError());
-}
-
-PJ_DEF(void) pj_set_netos_error(pj_status_t code)
-{
- WSASetLastError(PJ_STATUS_TO_OS(code));
-}
-
-/*
- * platform_strerror()
- *
- * Platform specific error message. This file is called by pj_strerror()
- * in errno.c
- */
-int platform_strerror( pj_os_err_type os_errcode,
- char *buf, pj_size_t bufsize)
-{
- int len;
-
- pj_assert(buf != NULL);
- pj_assert(bufsize >= 0);
-
- /*
- * MUST NOT check stack here.
- * This function might be called from PJ_CHECK_STACK() itself!
- //PJ_CHECK_STACK();
- */
-
- len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
- | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- os_errcode,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)buf,
- (DWORD)bufsize,
- NULL);
-
- if (!len) {
- int i;
- for (i = 0; gaErrorList[i].msg; ++i) {
- if (gaErrorList[i].code == os_errcode) {
- len = strlen(gaErrorList[i].msg);
- if ((pj_size_t)len >= bufsize) {
- len = bufsize-1;
- }
- pj_memcpy(buf, gaErrorList[i].msg, len);
- buf[len] = '\0';
- break;
- }
- }
- }
-
- if (!len) {
- len = snprintf( buf, bufsize, "Unknown native error %u", (unsigned)os_errcode);
- buf[len] = '\0';
- }
-
- return len;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/errno.h> +#include <pj/assert.h> +#include <pj/compat/stdarg.h> +#include <pj/compat/sprintf.h> +#include <pj/compat/vsprintf.h> +#include <pj/string.h> + + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include <winsock2.h> +#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include <winsock.h> +#endif + + +/* + * From Apache's APR: + */ +static const struct { + pj_os_err_type code; + const char *msg; +} gaErrorList[] = { + {WSAEINTR, "Interrupted system call"}, + {WSAEBADF, "Bad file number"}, + {WSAEACCES, "Permission denied"}, + {WSAEFAULT, "Bad address"}, + {WSAEINVAL, "Invalid argument"}, + {WSAEMFILE, "Too many open sockets"}, + {WSAEWOULDBLOCK, "Operation would block"}, + {WSAEINPROGRESS, "Operation now in progress"}, + {WSAEALREADY, "Operation already in progress"}, + {WSAENOTSOCK, "Socket operation on non-socket"}, + {WSAEDESTADDRREQ, "Destination address required"}, + {WSAEMSGSIZE, "Message too long"}, + {WSAEPROTOTYPE, "Protocol wrong type for socket"}, + {WSAENOPROTOOPT, "Bad protocol option"}, + {WSAEPROTONOSUPPORT, "Protocol not supported"}, + {WSAESOCKTNOSUPPORT, "Socket type not supported"}, + {WSAEOPNOTSUPP, "Operation not supported on socket"}, + {WSAEPFNOSUPPORT, "Protocol family not supported"}, + {WSAEAFNOSUPPORT, "Address family not supported"}, + {WSAEADDRINUSE, "Address already in use"}, + {WSAEADDRNOTAVAIL, "Can't assign requested address"}, + {WSAENETDOWN, "Network is down"}, + {WSAENETUNREACH, "Network is unreachable"}, + {WSAENETRESET, "Net connection reset"}, + {WSAECONNABORTED, "Software caused connection abort"}, + {WSAECONNRESET, "Connection reset by peer"}, + {WSAENOBUFS, "No buffer space available"}, + {WSAEISCONN, "Socket is already connected"}, + {WSAENOTCONN, "Socket is not connected"}, + {WSAESHUTDOWN, "Can't send after socket shutdown"}, + {WSAETOOMANYREFS, "Too many references, can't splice"}, + {WSAETIMEDOUT, "Connection timed out"}, + {WSAECONNREFUSED, "Connection refused"}, + {WSAELOOP, "Too many levels of symbolic links"}, + {WSAENAMETOOLONG, "File name too long"}, + {WSAEHOSTDOWN, "Host is down"}, + {WSAEHOSTUNREACH, "No route to host"}, + {WSAENOTEMPTY, "Directory not empty"}, + {WSAEPROCLIM, "Too many processes"}, + {WSAEUSERS, "Too many users"}, + {WSAEDQUOT, "Disc quota exceeded"}, + {WSAESTALE, "Stale NFS file handle"}, + {WSAEREMOTE, "Too many levels of remote in path"}, + {WSASYSNOTREADY, "Network system is unavailable"}, + {WSAVERNOTSUPPORTED, "Winsock version out of range"}, + {WSANOTINITIALISED, "WSAStartup not yet called"}, + {WSAEDISCON, "Graceful shutdown in progress"}, + {WSAHOST_NOT_FOUND, "Host not found"}, + {WSANO_DATA, "No host data of that type was found"}, + {0, NULL} +}; + + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return PJ_STATUS_FROM_OS(GetLastError()); +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + SetLastError(PJ_STATUS_TO_OS(code)); +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return PJ_STATUS_FROM_OS(WSAGetLastError()); +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + WSASetLastError(PJ_STATUS_TO_OS(code)); +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + int len; + + pj_assert(buf != NULL); + pj_assert(bufsize >= 0); + + /* + * MUST NOT check stack here. + * This function might be called from PJ_CHECK_STACK() itself! + //PJ_CHECK_STACK(); + */ + + len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + os_errcode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)buf, + (DWORD)bufsize, + NULL); + + if (!len) { + int i; + for (i = 0; gaErrorList[i].msg; ++i) { + if (gaErrorList[i].code == os_errcode) { + len = strlen(gaErrorList[i].msg); + if ((pj_size_t)len >= bufsize) { + len = bufsize-1; + } + pj_memcpy(buf, gaErrorList[i].msg, len); + buf[len] = '\0'; + break; + } + } + } + + if (!len) { + len = snprintf( buf, bufsize, "Unknown native error %u", (unsigned)os_errcode); + buf[len] = '\0'; + } + + return len; +} + diff --git a/pjlib/src/pj/os_time_ansi.c b/pjlib/src/pj/os_time_ansi.c index 616095cb..21a117a6 100644 --- a/pjlib/src/pj/os_time_ansi.c +++ b/pjlib/src/pj/os_time_ansi.c @@ -1,72 +1,72 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <pj/compat/time.h>
-
-///////////////////////////////////////////////////////////////////////////////
-
-PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
-{
- struct timeb tb;
-
- PJ_CHECK_STACK();
-
- ftime(&tb);
- tv->sec = tb.time;
- tv->msec = tb.millitm;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
-{
- struct tm *local_time;
-
- PJ_CHECK_STACK();
-
- local_time = localtime((time_t*)&tv->sec);
-
- pt->year = local_time->tm_year+1900;
- pt->mon = local_time->tm_mon;
- pt->day = local_time->tm_mday;
- pt->hour = local_time->tm_hour;
- pt->min = local_time->tm_min;
- pt->sec = local_time->tm_sec;
- pt->wday = local_time->tm_wday;
- pt->yday = local_time->tm_yday;
- pt->msec = tv->msec;
-
- return PJ_SUCCESS;
-}
-
-/**
- * Encode parsed time to time value.
- */
-PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
-
-/**
- * Convert local time to GMT.
- */
-PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
-
-/**
- * Convert GMT to local time.
- */
-PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <pj/compat/time.h> + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) +{ + struct timeb tb; + + PJ_CHECK_STACK(); + + ftime(&tb); + tv->sec = tb.time; + tv->msec = tb.millitm; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) +{ + struct tm *local_time; + + PJ_CHECK_STACK(); + + local_time = localtime((time_t*)&tv->sec); + + pt->year = local_time->tm_year+1900; + pt->mon = local_time->tm_mon; + pt->day = local_time->tm_mday; + pt->hour = local_time->tm_hour; + pt->min = local_time->tm_min; + pt->sec = local_time->tm_sec; + pt->wday = local_time->tm_wday; + pt->yday = local_time->tm_yday; + pt->msec = tv->msec; + + return PJ_SUCCESS; +} + +/** + * Encode parsed time to time value. + */ +PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); + +/** + * Convert local time to GMT. + */ +PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + */ +PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + + diff --git a/pjlib/src/pj/os_time_linux_kernel.c b/pjlib/src/pj/os_time_linux_kernel.c index 7d695ada..de53216a 100644 --- a/pjlib/src/pj/os_time_linux_kernel.c +++ b/pjlib/src/pj/os_time_linux_kernel.c @@ -1,65 +1,65 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <linux/time.h>
-
-///////////////////////////////////////////////////////////////////////////////
-
-PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
-{
- struct timeval tval;
-
- do_gettimeofday(&tval);
- tv->sec = tval.tv_sec;
- tv->msec = tval.tv_usec / 1000;
-
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
-{
- pt->year = 2005;
- pt->mon = 8;
- pt->day = 20;
- pt->hour = 16;
- pt->min = 30;
- pt->sec = 30;
- pt->wday = 3;
- pt->yday = 200;
- pt->msec = 777;
-
- return -1;
-}
-
-/**
- * Encode parsed time to time value.
- */
-PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
-
-/**
- * Convert local time to GMT.
- */
-PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
-
-/**
- * Convert GMT to local time.
- */
-PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <linux/time.h> + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) +{ + struct timeval tval; + + do_gettimeofday(&tval); + tv->sec = tval.tv_sec; + tv->msec = tval.tv_usec / 1000; + + return 0; +} + +PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) +{ + pt->year = 2005; + pt->mon = 8; + pt->day = 20; + pt->hour = 16; + pt->min = 30; + pt->sec = 30; + pt->wday = 3; + pt->yday = 200; + pt->msec = 777; + + return -1; +} + +/** + * Encode parsed time to time value. + */ +PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); + +/** + * Convert local time to GMT. + */ +PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + */ +PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + + diff --git a/pjlib/src/pj/os_timestamp_common.c b/pjlib/src/pj/os_timestamp_common.c index cab43f89..4dc410f3 100644 --- a/pjlib/src/pj/os_timestamp_common.c +++ b/pjlib/src/pj/os_timestamp_common.c @@ -1,135 +1,135 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <pj/compat/high_precision.h>
-
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
-
-#define U32MAX (0xFFFFFFFFUL)
-#define NANOSEC (1000000000UL)
-#define USEC (1000000UL)
-#define MSEC (1000)
-
-static pj_highprec_t get_elapsed( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_highprec_t elapsed_hi, elapsed_lo;
-
- elapsed_hi = stop->u32.hi - start->u32.hi;
- elapsed_lo = stop->u32.lo - start->u32.lo;
-
- /* elapsed_hi = elapsed_hi * U32MAX */
- pj_highprec_mul(elapsed_hi, U32MAX);
-
- return elapsed_hi + elapsed_lo;
-}
-
-static pj_highprec_t elapsed_usec( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_timestamp ts_freq;
- pj_highprec_t freq, elapsed;
-
- if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
- return 0;
-
- /* Convert frequency timestamp */
- freq = ts_freq.u32.hi;
- pj_highprec_mul(freq, U32MAX);
- freq += ts_freq.u32.lo;
-
- /* Avoid division by zero. */
- if (freq == 0) freq = 1;
-
- /* Get elapsed time in cycles. */
- elapsed = get_elapsed(start, stop);
-
- /* usec = elapsed * USEC / freq */
- pj_highprec_mul(elapsed, USEC);
- pj_highprec_div(elapsed, freq);
-
- return elapsed;
-}
-
-PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_timestamp ts_freq;
- pj_highprec_t freq, elapsed;
-
- if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
- return 0;
-
- /* Convert frequency timestamp */
- freq = ts_freq.u32.hi;
- pj_highprec_mul(freq, U32MAX);
- freq += ts_freq.u32.lo;
-
- /* Avoid division by zero. */
- if (freq == 0) freq = 1;
-
- /* Get elapsed time in cycles. */
- elapsed = get_elapsed(start, stop);
-
- /* usec = elapsed * USEC / freq */
- pj_highprec_mul(elapsed, NANOSEC);
- pj_highprec_div(elapsed, freq);
-
- return (pj_uint32_t)elapsed;
-}
-
-PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- return (pj_uint32_t)elapsed_usec(start, stop);
-}
-
-PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_highprec_t elapsed = elapsed_usec(start, stop);
- pj_time_val tv_elapsed;
-
- if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) {
- tv_elapsed.sec = tv_elapsed.msec = 0;
- return tv_elapsed;
- } else {
- pj_highprec_t sec, msec;
-
- sec = elapsed;
- pj_highprec_div(sec, USEC);
- tv_elapsed.sec = (long)sec;
-
- msec = elapsed;
- pj_highprec_mod(msec, USEC);
- pj_highprec_div(msec, 1000);
- tv_elapsed.msec = (long)msec;
-
- return tv_elapsed;
- }
-}
-
-PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- return stop->u32.lo - start->u32.lo;
-}
-
-#endif /* PJ_HAS_HIGH_RES_TIMER */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <pj/compat/high_precision.h> + +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + +#define U32MAX (0xFFFFFFFFUL) +#define NANOSEC (1000000000UL) +#define USEC (1000000UL) +#define MSEC (1000) + +static pj_highprec_t get_elapsed( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_highprec_t elapsed_hi, elapsed_lo; + + elapsed_hi = stop->u32.hi - start->u32.hi; + elapsed_lo = stop->u32.lo - start->u32.lo; + + /* elapsed_hi = elapsed_hi * U32MAX */ + pj_highprec_mul(elapsed_hi, U32MAX); + + return elapsed_hi + elapsed_lo; +} + +static pj_highprec_t elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_timestamp ts_freq; + pj_highprec_t freq, elapsed; + + if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) + return 0; + + /* Convert frequency timestamp */ + freq = ts_freq.u32.hi; + pj_highprec_mul(freq, U32MAX); + freq += ts_freq.u32.lo; + + /* Avoid division by zero. */ + if (freq == 0) freq = 1; + + /* Get elapsed time in cycles. */ + elapsed = get_elapsed(start, stop); + + /* usec = elapsed * USEC / freq */ + pj_highprec_mul(elapsed, USEC); + pj_highprec_div(elapsed, freq); + + return elapsed; +} + +PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_timestamp ts_freq; + pj_highprec_t freq, elapsed; + + if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) + return 0; + + /* Convert frequency timestamp */ + freq = ts_freq.u32.hi; + pj_highprec_mul(freq, U32MAX); + freq += ts_freq.u32.lo; + + /* Avoid division by zero. */ + if (freq == 0) freq = 1; + + /* Get elapsed time in cycles. */ + elapsed = get_elapsed(start, stop); + + /* usec = elapsed * USEC / freq */ + pj_highprec_mul(elapsed, NANOSEC); + pj_highprec_div(elapsed, freq); + + return (pj_uint32_t)elapsed; +} + +PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return (pj_uint32_t)elapsed_usec(start, stop); +} + +PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_highprec_t elapsed = elapsed_usec(start, stop); + pj_time_val tv_elapsed; + + if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) { + tv_elapsed.sec = tv_elapsed.msec = 0; + return tv_elapsed; + } else { + pj_highprec_t sec, msec; + + sec = elapsed; + pj_highprec_div(sec, USEC); + tv_elapsed.sec = (long)sec; + + msec = elapsed; + pj_highprec_mod(msec, USEC); + pj_highprec_div(msec, 1000); + tv_elapsed.msec = (long)msec; + + return tv_elapsed; + } +} + +PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return stop->u32.lo - start->u32.lo; +} + +#endif /* PJ_HAS_HIGH_RES_TIMER */ + diff --git a/pjlib/src/pj/os_timestamp_linux.c b/pjlib/src/pj/os_timestamp_linux.c index 7b7b11c5..c78fdd61 100644 --- a/pjlib/src/pj/os_timestamp_linux.c +++ b/pjlib/src/pj/os_timestamp_linux.c @@ -1,139 +1,139 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <pj/errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-
-#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0
-static int machine_speed_mhz;
-static pj_timestamp machine_speed;
-
-static __inline__ unsigned long long int rdtsc()
-{
- unsigned long long int x;
- __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
- return x;
-}
-
-/* Determine machine's CPU MHz to get the counter's frequency.
- */
-static int get_machine_speed_mhz()
-{
- FILE *strm;
- char buf[512];
- int len;
- char *pos, *end;
-
- PJ_CHECK_STACK();
-
- /* Open /proc/cpuinfo and read the file */
- strm = fopen("/proc/cpuinfo", "r");
- if (!strm)
- return -1;
- len = fread(buf, 1, sizeof(buf), strm);
- fclose(strm);
- if (len < 1) {
- return -1;
- }
- buf[len] = '\0';
-
- /* Locate the MHz digit. */
- pos = strstr(buf, "cpu MHz");
- if (!pos)
- return -1;
- pos = strchr(pos, ':');
- if (!pos)
- return -1;
- end = (pos += 2);
- while (isdigit(*end)) ++end;
- *end = '\0';
-
- /* Return the Mhz part, and give it a +1. */
- return atoi(pos)+1;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- if (machine_speed_mhz == 0) {
- machine_speed_mhz = get_machine_speed_mhz();
- if (machine_speed_mhz > 0) {
- machine_speed.u64 = machine_speed_mhz * 1000000.0;
- }
- }
-
- if (machine_speed_mhz == -1) {
- ts->u64 = 0;
- return -1;
- }
- ts->u64 = rdtsc();
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- if (machine_speed_mhz == 0) {
- machine_speed_mhz = get_machine_speed_mhz();
- if (machine_speed_mhz > 0) {
- machine_speed.u64 = machine_speed_mhz * 1000000.0;
- }
- }
-
- if (machine_speed_mhz == -1) {
- freq->u64 = 1; /* return 1 to prevent division by zero in apps. */
- return -1;
- }
-
- freq->u64 = machine_speed.u64;
- return 0;
-}
-
-#else
-#include <sys/time.h>
-#include <errno.h>
-
-#define USEC_PER_SEC 1000000
-
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- struct timeval tv;
-
- if (gettimeofday(&tv, NULL) != 0) {
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
- }
-
- ts->u64 = tv.tv_sec;
- ts->u64 *= USEC_PER_SEC;
- ts->u64 += tv.tv_usec;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = USEC_PER_SEC;
-
- return PJ_SUCCESS;
-}
-
-#endif
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <pj/errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 +static int machine_speed_mhz; +static pj_timestamp machine_speed; + +static __inline__ unsigned long long int rdtsc() +{ + unsigned long long int x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} + +/* Determine machine's CPU MHz to get the counter's frequency. + */ +static int get_machine_speed_mhz() +{ + FILE *strm; + char buf[512]; + int len; + char *pos, *end; + + PJ_CHECK_STACK(); + + /* Open /proc/cpuinfo and read the file */ + strm = fopen("/proc/cpuinfo", "r"); + if (!strm) + return -1; + len = fread(buf, 1, sizeof(buf), strm); + fclose(strm); + if (len < 1) { + return -1; + } + buf[len] = '\0'; + + /* Locate the MHz digit. */ + pos = strstr(buf, "cpu MHz"); + if (!pos) + return -1; + pos = strchr(pos, ':'); + if (!pos) + return -1; + end = (pos += 2); + while (isdigit(*end)) ++end; + *end = '\0'; + + /* Return the Mhz part, and give it a +1. */ + return atoi(pos)+1; +} + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + if (machine_speed_mhz == 0) { + machine_speed_mhz = get_machine_speed_mhz(); + if (machine_speed_mhz > 0) { + machine_speed.u64 = machine_speed_mhz * 1000000.0; + } + } + + if (machine_speed_mhz == -1) { + ts->u64 = 0; + return -1; + } + ts->u64 = rdtsc(); + return 0; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + if (machine_speed_mhz == 0) { + machine_speed_mhz = get_machine_speed_mhz(); + if (machine_speed_mhz > 0) { + machine_speed.u64 = machine_speed_mhz * 1000000.0; + } + } + + if (machine_speed_mhz == -1) { + freq->u64 = 1; /* return 1 to prevent division by zero in apps. */ + return -1; + } + + freq->u64 = machine_speed.u64; + return 0; +} + +#else +#include <sys/time.h> +#include <errno.h> + +#define USEC_PER_SEC 1000000 + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) { + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + } + + ts->u64 = tv.tv_sec; + ts->u64 *= USEC_PER_SEC; + ts->u64 += tv.tv_usec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = USEC_PER_SEC; + + return PJ_SUCCESS; +} + +#endif + diff --git a/pjlib/src/pj/os_timestamp_linux_kernel.c b/pjlib/src/pj/os_timestamp_linux_kernel.c index ed21851d..ebc6c1c8 100644 --- a/pjlib/src/pj/os_timestamp_linux_kernel.c +++ b/pjlib/src/pj/os_timestamp_linux_kernel.c @@ -1,78 +1,78 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <linux/time.h>
-
-#if 0
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- ts->u32.hi = 0;
- ts->u32.lo = jiffies;
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = HZ;
- return 0;
-}
-#elif 0
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- struct timespec tv;
-
- tv = CURRENT_TIME;
-
- ts->u64 = tv.tv_sec;
- ts->u64 *= NSEC_PER_SEC;
- ts->u64 += tv.tv_nsec;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = NSEC_PER_SEC;
- return 0;
-}
-#else
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- struct timeval tv;
-
- do_gettimeofday(&tv);
-
- ts->u64 = tv.tv_sec;
- ts->u64 *= USEC_PER_SEC;
- ts->u64 += tv.tv_usec;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = USEC_PER_SEC;
- return 0;
-}
-
-#endif
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <linux/time.h> + +#if 0 +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + ts->u32.hi = 0; + ts->u32.lo = jiffies; + return 0; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = HZ; + return 0; +} +#elif 0 +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timespec tv; + + tv = CURRENT_TIME; + + ts->u64 = tv.tv_sec; + ts->u64 *= NSEC_PER_SEC; + ts->u64 += tv.tv_nsec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = NSEC_PER_SEC; + return 0; +} +#else +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timeval tv; + + do_gettimeofday(&tv); + + ts->u64 = tv.tv_sec; + ts->u64 *= USEC_PER_SEC; + ts->u64 += tv.tv_usec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = USEC_PER_SEC; + return 0; +} + +#endif + diff --git a/pjlib/src/pj/os_timestamp_win32.c b/pjlib/src/pj/os_timestamp_win32.c index a56254f2..eeff6c00 100644 --- a/pjlib/src/pj/os_timestamp_win32.c +++ b/pjlib/src/pj/os_timestamp_win32.c @@ -1,44 +1,44 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/os.h>
-#include <pj/errno.h>
-#include <windows.h>
-
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- LARGE_INTEGER val;
-
- if (!QueryPerformanceCounter(&val))
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- ts->u64 = val.QuadPart;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- LARGE_INTEGER val;
-
- if (!QueryPerformanceFrequency(&val))
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- freq->u64 = val.QuadPart;
- return PJ_SUCCESS;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/os.h> +#include <pj/errno.h> +#include <windows.h> + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + LARGE_INTEGER val; + + if (!QueryPerformanceCounter(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + ts->u64 = val.QuadPart; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + LARGE_INTEGER val; + + if (!QueryPerformanceFrequency(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + freq->u64 = val.QuadPart; + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c index 3ad9d1b0..29966809 100644 --- a/pjlib/src/pj/pool.c +++ b/pjlib/src/pj/pool.c @@ -1,272 +1,272 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/except.h>
-#include <pj/assert.h>
-#include <pj/os.h>
-#include <pj/compat/sprintf.h>
-
-/* Include inline definitions when inlining is disabled. */
-#if !PJ_FUNCTIONS_ARE_INLINED
-# include <pj/pool_i.h>
-#endif
-
-#define LOG(expr) PJ_LOG(5,expr)
-
-int PJ_NO_MEMORY_EXCEPTION;
-
-/*
- * Create new block.
- * Create a new big chunk of memory block, from which user allocation will be
- * taken from.
- */
-static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size)
-{
- pj_pool_block *block;
-
- PJ_CHECK_STACK();
- pj_assert(size >= sizeof(pj_pool_block));
-
- LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u",
- size, pool->capacity, pool->used_size));
-
- /* Request memory from allocator. */
- block = (pj_pool_block*)
- (*pool->factory->policy.block_alloc)(pool->factory, size);
- if (block == NULL) {
- (*pool->callback)(pool, size);
- return NULL;
- }
-
- /* Add capacity. */
- pool->capacity += size;
- pool->used_size += sizeof(pj_pool_block);
-
- /* Set block attribytes. */
- block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
- block->end = ((unsigned char*)block) + size;
-
- /* Insert in the front of the list. */
- pj_list_insert_after(&pool->block_list, block);
-
- LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end));
-
- return block;
-}
-
-/*
- * Allocate memory chunk for user from available blocks.
- * This will iterate through block list to find space to allocate the chunk.
- * If no space is available in all the blocks, a new block might be created
- * (depending on whether the pool is allowed to resize).
- */
-PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size)
-{
- pj_pool_block *block = pool->block_list.next;
- void *p;
- unsigned block_size;
-
- PJ_CHECK_STACK();
-
- while (block != &pool->block_list) {
- p = pj_pool_alloc_from_block(pool, block, size);
- if (p != NULL)
- return p;
- block = block->next;
- }
- /* No available space in all blocks. */
-
- /* If pool is configured NOT to expand, return error. */
- if (pool->increment_size == 0) {
- LOG((pool->obj_name, "Can't expand pool to allocate %u bytes "
- "(used=%u, cap=%u)",
- size, pool->used_size, pool->capacity));
- (*pool->callback)(pool, size);
- return NULL;
- }
-
- /* If pool is configured to expand, but the increment size
- * is less than the required size, expand the pool by multiple
- * increment size
- */
- if (pool->increment_size < size + sizeof(pj_pool_block)) {
- unsigned count;
- count = (size + pool->increment_size + sizeof(pj_pool_block)) /
- pool->increment_size;
- block_size = count * pool->increment_size;
-
- } else {
- block_size = pool->increment_size;
- }
-
- LOG((pool->obj_name,
- "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)",
- size, block_size, pool->used_size, pool->capacity));
-
- block = pj_pool_create_block(pool, block_size);
- if (!block)
- return NULL;
-
- p = pj_pool_alloc_from_block(pool, block, size);
- pj_assert(p != NULL);
-#if PJ_DEBUG
- if (p == NULL) {
- p = p;
- }
-#endif
- return p;
-}
-
-/*
- * Internal function to initialize pool.
- */
-PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool,
- const char *name,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- pj_pool_block *block;
-
- PJ_CHECK_STACK();
-
- pool->increment_size = increment_size;
- pool->callback = callback;
- pool->used_size = sizeof(*pool);
- block = pool->block_list.next;
- while (block != &pool->block_list) {
- pool->used_size += sizeof(pj_pool_block);
- block = block->next;
- }
-
- if (name) {
- if (strchr(name, '%') != NULL) {
- sprintf(pool->obj_name, name, pool);
- } else {
- strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
- }
- } else {
- pool->obj_name[0] = '\0';
- }
-}
-
-/*
- * Create new memory pool.
- */
-PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- pj_pool_t *pool;
- pj_pool_block *block;
- unsigned char *buffer;
-
- PJ_CHECK_STACK();
-
- buffer = (*f->policy.block_alloc)(f, initial_size);
- if (!buffer)
- return NULL;
-
- /* Set pool administrative data. */
- pool = (pj_pool_t*)buffer;
- pj_memset(pool, 0, sizeof(*pool));
-
- pj_list_init(&pool->block_list);
- pool->factory = f;
-
- /* Create the first block from the memory. */
- block = (pj_pool_block*) (buffer + sizeof(*pool));
- block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
- block->end = buffer + initial_size;
- pj_list_insert_after(&pool->block_list, block);
-
- pj_pool_init_int(pool, name, increment_size, callback);
-
- /* Pool initial capacity and used size */
- pool->capacity = initial_size;
-
- LOG((pool->obj_name, "pool created, size=%u", pool->capacity));
- return pool;
-}
-
-/*
- * Reset the pool to the state when it was created.
- * All blocks will be deallocated except the first block. All memory areas
- * are marked as free.
- */
-static void reset_pool(pj_pool_t *pool)
-{
- pj_pool_block *block;
-
- PJ_CHECK_STACK();
-
- block = pool->block_list.prev;
- if (block == &pool->block_list)
- return;
-
- /* Skip the first block because it is occupying the same memory
- as the pool itself.
- */
- block = block->prev;
-
- while (block != &pool->block_list) {
- pj_pool_block *prev = block->prev;
- pj_list_erase(block);
- (*pool->factory->policy.block_free)(pool->factory, block,
- block->end - (unsigned char*)block);
- block = prev;
- }
-
- block = pool->block_list.next;
- block->cur = block->buf;
- pool->capacity = block->end - (unsigned char*)pool;
- pool->used_size = 0;
-}
-
-/*
- * The public function to reset pool.
- */
-PJ_DEF(void) pj_pool_reset(pj_pool_t *pool)
-{
- LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)",
- pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
-
- reset_pool(pool);
-}
-
-/*
- * Destroy the pool.
- */
-PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool)
-{
- pj_size_t initial_size;
-
- LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p",
- pool->capacity, pool->used_size, pool->used_size*100/pool->capacity,
- ((pj_pool_block*)pool->block_list.next)->buf,
- ((pj_pool_block*)pool->block_list.next)->end));
-
- reset_pool(pool);
- initial_size = ((pj_pool_block*)pool->block_list.next)->end -
- (unsigned char*)pool;
- (*pool->factory->policy.block_free)(pool->factory, pool, initial_size);
-}
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/pool.h> +#include <pj/log.h> +#include <pj/except.h> +#include <pj/assert.h> +#include <pj/os.h> +#include <pj/compat/sprintf.h> + +/* Include inline definitions when inlining is disabled. */ +#if !PJ_FUNCTIONS_ARE_INLINED +# include <pj/pool_i.h> +#endif + +#define LOG(expr) PJ_LOG(5,expr) + +int PJ_NO_MEMORY_EXCEPTION; + +/* + * Create new block. + * Create a new big chunk of memory block, from which user allocation will be + * taken from. + */ +static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + pj_assert(size >= sizeof(pj_pool_block)); + + LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u", + size, pool->capacity, pool->used_size)); + + /* Request memory from allocator. */ + block = (pj_pool_block*) + (*pool->factory->policy.block_alloc)(pool->factory, size); + if (block == NULL) { + (*pool->callback)(pool, size); + return NULL; + } + + /* Add capacity. */ + pool->capacity += size; + pool->used_size += sizeof(pj_pool_block); + + /* Set block attribytes. */ + block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); + block->end = ((unsigned char*)block) + size; + + /* Insert in the front of the list. */ + pj_list_insert_after(&pool->block_list, block); + + LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end)); + + return block; +} + +/* + * Allocate memory chunk for user from available blocks. + * This will iterate through block list to find space to allocate the chunk. + * If no space is available in all the blocks, a new block might be created + * (depending on whether the pool is allowed to resize). + */ +PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size) +{ + pj_pool_block *block = pool->block_list.next; + void *p; + unsigned block_size; + + PJ_CHECK_STACK(); + + while (block != &pool->block_list) { + p = pj_pool_alloc_from_block(pool, block, size); + if (p != NULL) + return p; + block = block->next; + } + /* No available space in all blocks. */ + + /* If pool is configured NOT to expand, return error. */ + if (pool->increment_size == 0) { + LOG((pool->obj_name, "Can't expand pool to allocate %u bytes " + "(used=%u, cap=%u)", + size, pool->used_size, pool->capacity)); + (*pool->callback)(pool, size); + return NULL; + } + + /* If pool is configured to expand, but the increment size + * is less than the required size, expand the pool by multiple + * increment size + */ + if (pool->increment_size < size + sizeof(pj_pool_block)) { + unsigned count; + count = (size + pool->increment_size + sizeof(pj_pool_block)) / + pool->increment_size; + block_size = count * pool->increment_size; + + } else { + block_size = pool->increment_size; + } + + LOG((pool->obj_name, + "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)", + size, block_size, pool->used_size, pool->capacity)); + + block = pj_pool_create_block(pool, block_size); + if (!block) + return NULL; + + p = pj_pool_alloc_from_block(pool, block, size); + pj_assert(p != NULL); +#if PJ_DEBUG + if (p == NULL) { + p = p; + } +#endif + return p; +} + +/* + * Internal function to initialize pool. + */ +PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, + const char *name, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + + pool->increment_size = increment_size; + pool->callback = callback; + pool->used_size = sizeof(*pool); + block = pool->block_list.next; + while (block != &pool->block_list) { + pool->used_size += sizeof(pj_pool_block); + block = block->next; + } + + if (name) { + if (strchr(name, '%') != NULL) { + sprintf(pool->obj_name, name, pool); + } else { + strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); + } + } else { + pool->obj_name[0] = '\0'; + } +} + +/* + * Create new memory pool. + */ +PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + pj_pool_t *pool; + pj_pool_block *block; + unsigned char *buffer; + + PJ_CHECK_STACK(); + + buffer = (*f->policy.block_alloc)(f, initial_size); + if (!buffer) + return NULL; + + /* Set pool administrative data. */ + pool = (pj_pool_t*)buffer; + pj_memset(pool, 0, sizeof(*pool)); + + pj_list_init(&pool->block_list); + pool->factory = f; + + /* Create the first block from the memory. */ + block = (pj_pool_block*) (buffer + sizeof(*pool)); + block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); + block->end = buffer + initial_size; + pj_list_insert_after(&pool->block_list, block); + + pj_pool_init_int(pool, name, increment_size, callback); + + /* Pool initial capacity and used size */ + pool->capacity = initial_size; + + LOG((pool->obj_name, "pool created, size=%u", pool->capacity)); + return pool; +} + +/* + * Reset the pool to the state when it was created. + * All blocks will be deallocated except the first block. All memory areas + * are marked as free. + */ +static void reset_pool(pj_pool_t *pool) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + + block = pool->block_list.prev; + if (block == &pool->block_list) + return; + + /* Skip the first block because it is occupying the same memory + as the pool itself. + */ + block = block->prev; + + while (block != &pool->block_list) { + pj_pool_block *prev = block->prev; + pj_list_erase(block); + (*pool->factory->policy.block_free)(pool->factory, block, + block->end - (unsigned char*)block); + block = prev; + } + + block = pool->block_list.next; + block->cur = block->buf; + pool->capacity = block->end - (unsigned char*)pool; + pool->used_size = 0; +} + +/* + * The public function to reset pool. + */ +PJ_DEF(void) pj_pool_reset(pj_pool_t *pool) +{ + LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)", + pool->capacity, pool->used_size, pool->used_size*100/pool->capacity)); + + reset_pool(pool); +} + +/* + * Destroy the pool. + */ +PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool) +{ + pj_size_t initial_size; + + LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p", + pool->capacity, pool->used_size, pool->used_size*100/pool->capacity, + ((pj_pool_block*)pool->block_list.next)->buf, + ((pj_pool_block*)pool->block_list.next)->end)); + + reset_pool(pool); + initial_size = ((pj_pool_block*)pool->block_list.next)->end - + (unsigned char*)pool; + (*pool->factory->policy.block_free)(pool->factory, pool, initial_size); +} + + diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c index afeadae5..5fa5ff74 100644 --- a/pjlib/src/pj/pool_caching.c +++ b/pjlib/src/pj/pool_caching.c @@ -1,221 +1,221 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/os.h>
-
-static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_sz,
- pj_pool_callback *callback);
-static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool);
-static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail );
-
-static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] =
-{
- 256, 512, 1024, 2048, 4096, 8192, 12288, 16384,
- 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
-};
-
-
-PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp,
- const pj_pool_factory_policy *policy,
- pj_size_t max_capacity)
-{
- int i;
-
- PJ_CHECK_STACK();
-
- pj_memset(cp, 0, sizeof(*cp));
-
- cp->max_capacity = max_capacity;
- pj_list_init(&cp->used_list);
- for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i)
- pj_list_init(&cp->free_list[i]);
-
- pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy));
- cp->factory.create_pool = &cpool_create_pool;
- cp->factory.release_pool = &cpool_release_pool;
- cp->factory.dump_status = &cpool_dump_status;
-}
-
-PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp )
-{
- int i;
- pj_pool_t *pool;
-
- PJ_CHECK_STACK();
-
- /* Delete all pool in free list */
- for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) {
- pj_pool_t *pool = cp->free_list[i].next;
- pj_pool_t *next;
- for (; pool != (void*)&cp->free_list[i]; pool = next) {
- next = pool->next;
- pj_list_erase(pool);
- pj_pool_destroy_int(pool);
- }
- }
-
- /* Delete all pools in used list */
- pool = cp->used_list.next;
- while (pool != (pj_pool_t*) &cp->used_list) {
- pj_pool_t *next = pool->next;
- pj_list_erase(pool);
- pj_pool_destroy_int(pool);
- pool = next;
- }
-}
-
-static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_sz,
- pj_pool_callback *callback)
-{
- pj_caching_pool *cp = (pj_caching_pool*)pf;
- pj_pool_t *pool;
- int idx;
-
- PJ_CHECK_STACK();
-
- /* Use pool factory's policy when callback is NULL */
- if (callback == NULL) {
- callback = pf->policy.callback;
- }
-
- /* Search the suitable size for the pool.
- * We'll just do linear search to the size array, as the array size itself
- * is only a few elements. Binary search I suspect will be less efficient
- * for this purpose.
- */
- for (idx=0;
- idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size;
- ++idx)
- ;
-
- /* Check whether there's a pool in the list. */
- if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
- /* No pool is available. */
- /* Set minimum size. */
- if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
- initial_size = pool_sizes[idx];
-
- /* Create new pool */
- pool = pj_pool_create_int(&cp->factory, name, initial_size,
- increment_sz, callback);
- if (!pool)
- return NULL;
-
- } else {
- /* Get one pool from the list. */
- pool = cp->free_list[idx].next;
- pj_list_erase(pool);
-
- /* Initialize the pool. */
- pj_pool_init_int(pool, name, increment_sz, callback);
-
- /* Update pool manager's free capacity. */
- cp->capacity -= pj_pool_get_capacity(pool);
-
- PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity));
- }
-
- /* Put in used list. */
- pj_list_insert_before( &cp->used_list, pool );
-
- /* Increment used count. */
- ++cp->used_count;
- return pool;
-}
-
-static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
-{
- pj_caching_pool *cp = (pj_caching_pool*)pf;
- int i;
-
- PJ_CHECK_STACK();
-
- /* Erase from the used list. */
- pj_list_erase(pool);
-
- /* Decrement used count. */
- --cp->used_count;
-
- /* Destroy the pool if the size is greater than our size or if the total
- * capacity in our recycle list (plus the size of the pool) exceeds
- * maximum capacity.
- . */
- if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
- cp->capacity + pool->capacity > cp->max_capacity)
- {
- pj_pool_destroy_int(pool);
- return;
- }
-
- /* Reset pool. */
- PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)",
- pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
- pj_pool_reset(pool);
-
- /*
- * Otherwise put the pool in our recycle list.
- */
- for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i)
- ;
-
- pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE );
- if (i == PJ_CACHING_POOL_ARRAY_SIZE) {
- /* Something has gone wrong with the pool. */
- pj_pool_destroy_int(pool);
- return;
- }
-
- pj_list_insert_after(&cp->free_list[i], pool);
- cp->capacity += pool->capacity;
-}
-
-static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail )
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- pj_caching_pool *cp = (pj_caching_pool*)factory;
- PJ_LOG(3,("cachpool", " Dumping caching pool:"));
- PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \
- cp->capacity, cp->max_capacity, cp->used_count));
- if (detail) {
- pj_pool_t *pool = cp->used_list.next;
- pj_uint32_t total_used = 0, total_capacity = 0;
- PJ_LOG(3,("cachpool", " Dumping all active pools:"));
- while (pool != (void*)&cp->used_list) {
- PJ_LOG(3,("cachpool", " %12s: %8d of %8d (%d%%) used", pool->obj_name,
- pool->used_size, pool->capacity,
- pool->used_size*100/pool->capacity));
- total_used += pool->used_size;
- total_capacity += pool->capacity;
- pool = pool->next;
- }
- PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!",
- total_used, total_capacity,
- total_used * 100 / total_capacity));
- }
-#endif
-}
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/pool.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/os.h> + +static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, + const char *name, + pj_size_t initial_size, + pj_size_t increment_sz, + pj_pool_callback *callback); +static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool); +static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ); + +static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = +{ + 256, 512, 1024, 2048, 4096, 8192, 12288, 16384, + 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536 +}; + + +PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, + const pj_pool_factory_policy *policy, + pj_size_t max_capacity) +{ + int i; + + PJ_CHECK_STACK(); + + pj_memset(cp, 0, sizeof(*cp)); + + cp->max_capacity = max_capacity; + pj_list_init(&cp->used_list); + for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i) + pj_list_init(&cp->free_list[i]); + + pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy)); + cp->factory.create_pool = &cpool_create_pool; + cp->factory.release_pool = &cpool_release_pool; + cp->factory.dump_status = &cpool_dump_status; +} + +PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp ) +{ + int i; + pj_pool_t *pool; + + PJ_CHECK_STACK(); + + /* Delete all pool in free list */ + for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) { + pj_pool_t *pool = cp->free_list[i].next; + pj_pool_t *next; + for (; pool != (void*)&cp->free_list[i]; pool = next) { + next = pool->next; + pj_list_erase(pool); + pj_pool_destroy_int(pool); + } + } + + /* Delete all pools in used list */ + pool = cp->used_list.next; + while (pool != (pj_pool_t*) &cp->used_list) { + pj_pool_t *next = pool->next; + pj_list_erase(pool); + pj_pool_destroy_int(pool); + pool = next; + } +} + +static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, + const char *name, + pj_size_t initial_size, + pj_size_t increment_sz, + pj_pool_callback *callback) +{ + pj_caching_pool *cp = (pj_caching_pool*)pf; + pj_pool_t *pool; + int idx; + + PJ_CHECK_STACK(); + + /* Use pool factory's policy when callback is NULL */ + if (callback == NULL) { + callback = pf->policy.callback; + } + + /* Search the suitable size for the pool. + * We'll just do linear search to the size array, as the array size itself + * is only a few elements. Binary search I suspect will be less efficient + * for this purpose. + */ + for (idx=0; + idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size; + ++idx) + ; + + /* Check whether there's a pool in the list. */ + if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) { + /* No pool is available. */ + /* Set minimum size. */ + if (idx < PJ_CACHING_POOL_ARRAY_SIZE) + initial_size = pool_sizes[idx]; + + /* Create new pool */ + pool = pj_pool_create_int(&cp->factory, name, initial_size, + increment_sz, callback); + if (!pool) + return NULL; + + } else { + /* Get one pool from the list. */ + pool = cp->free_list[idx].next; + pj_list_erase(pool); + + /* Initialize the pool. */ + pj_pool_init_int(pool, name, increment_sz, callback); + + /* Update pool manager's free capacity. */ + cp->capacity -= pj_pool_get_capacity(pool); + + PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity)); + } + + /* Put in used list. */ + pj_list_insert_before( &cp->used_list, pool ); + + /* Increment used count. */ + ++cp->used_count; + return pool; +} + +static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool) +{ + pj_caching_pool *cp = (pj_caching_pool*)pf; + int i; + + PJ_CHECK_STACK(); + + /* Erase from the used list. */ + pj_list_erase(pool); + + /* Decrement used count. */ + --cp->used_count; + + /* Destroy the pool if the size is greater than our size or if the total + * capacity in our recycle list (plus the size of the pool) exceeds + * maximum capacity. + . */ + if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] || + cp->capacity + pool->capacity > cp->max_capacity) + { + pj_pool_destroy_int(pool); + return; + } + + /* Reset pool. */ + PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", + pool->capacity, pool->used_size, pool->used_size*100/pool->capacity)); + pj_pool_reset(pool); + + /* + * Otherwise put the pool in our recycle list. + */ + for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i) + ; + + pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE ); + if (i == PJ_CACHING_POOL_ARRAY_SIZE) { + /* Something has gone wrong with the pool. */ + pj_pool_destroy_int(pool); + return; + } + + pj_list_insert_after(&cp->free_list[i], pool); + cp->capacity += pool->capacity; +} + +static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + pj_caching_pool *cp = (pj_caching_pool*)factory; + PJ_LOG(3,("cachpool", " Dumping caching pool:")); + PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \ + cp->capacity, cp->max_capacity, cp->used_count)); + if (detail) { + pj_pool_t *pool = cp->used_list.next; + pj_uint32_t total_used = 0, total_capacity = 0; + PJ_LOG(3,("cachpool", " Dumping all active pools:")); + while (pool != (void*)&cp->used_list) { + PJ_LOG(3,("cachpool", " %12s: %8d of %8d (%d%%) used", pool->obj_name, + pool->used_size, pool->capacity, + pool->used_size*100/pool->capacity)); + total_used += pool->used_size; + total_capacity += pool->capacity; + pool = pool->next; + } + PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!", + total_used, total_capacity, + total_used * 100 / total_capacity)); + } +#endif +} diff --git a/pjlib/src/pj/pool_dbg_win32.c b/pjlib/src/pj/pool_dbg_win32.c index e354d373..a3ccf3ed 100644 --- a/pjlib/src/pj/pool_dbg_win32.c +++ b/pjlib/src/pj/pool_dbg_win32.c @@ -1,237 +1,237 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/pool.h>
-
-/* Only if we ARE debugging memory allocations. */
-#if PJ_POOL_DEBUG
-
-#include <pj/list.h>
-#include <pj/log.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-typedef struct memory_entry
-{
- PJ_DECL_LIST_MEMBER(struct memory_entry)
- void *ptr;
- char *file;
- int line;
-} memory_entry;
-
-struct pj_pool_t
-{
- char obj_name[32];
- HANDLE hHeap;
- memory_entry first;
- pj_size_t initial_size;
- pj_size_t increment;
- pj_size_t used_size;
- char *file;
- int line;
-};
-
-PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t),
- void (*free_func)(void *ptr, pj_size_t))
-{
- /* Ignored. */
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(malloc_func)
- PJ_UNUSED_ARG(free_func)
-}
-
-PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback,
- char *file, int line)
-{
- pj_pool_t *pool;
- HANDLE hHeap;
-
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(callback)
-
- /* Create Win32 heap for the pool. */
- hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- initial_size, 0);
- if (!hHeap) {
- return NULL;
- }
-
-
- /* Create and initialize the pool structure. */
- pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- sizeof(*pool));
- memset(pool, 0, sizeof(*pool));
- pool->file = file;
- pool->line = line;
- pool->hHeap = hHeap;
- pool->initial_size = initial_size;
- pool->increment = increment_size;
- pool->used_size = 0;
-
- /* Set name. */
- if (name) {
- if (strchr(name, '%') != NULL) {
- sprintf(pool->obj_name, name, pool);
- } else {
- strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
- }
- } else {
- pool->obj_name[0] = '\0';
- }
-
- /* List pool's entry. */
- pj_list_init(&pool->first);
-
- PJ_LOG(3,(pool->obj_name, "Pool created"));
- return pool;
-}
-
-PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool )
-{
- memory_entry *entry;
-
- PJ_CHECK_STACK();
-
- PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u",
- pool->initial_size, pool->used_size));
-
- if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) {
- PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d",
- pool->file, pool->line));
- }
-
- /* Validate all memory entries in the pool. */
- for (entry=pool->first.next; entry != &pool->first; entry = entry->next) {
- if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) {
- PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d",
- entry->file, entry->line));
- }
-
- if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) {
- PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d",
- entry->file, entry->line));
- }
- }
-
- /* Destroy heap. */
- HeapDestroy(pool->hHeap);
-}
-
-PJ_DEF(void) pj_pool_reset( pj_pool_t *pool )
-{
- /* Do nothing. */
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
-}
-
-PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
- return 0;
-}
-
-PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
- return 0;
-}
-
-PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
- return 0;
-}
-
-PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size,
- char *file, int line)
-{
- memory_entry *entry;
- int entry_size;
-
- PJ_CHECK_STACK();
-
- entry_size = sizeof(*entry);
- entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- entry_size);
- entry->file = file;
- entry->line = line;
- entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- size);
- pj_list_insert_before( &pool->first, entry);
-
- pool->used_size += size;
- return entry->ptr;
-}
-
-PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem,
- char *file, int line)
-{
- void *ptr;
-
- PJ_CHECK_STACK();
-
- ptr = pj_pool_alloc_dbg(pool, count*elem, file, line);
- memset(ptr, 0, count*elem);
- return ptr;
-}
-
-
-PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool,
- pj_size_t max_capacity)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
- PJ_UNUSED_ARG(max_capacity)
-}
-
-PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
-}
-
-PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
- return pj_pool_create(name, initial_size, increment_size, callback);
-}
-
-PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool,
- pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
- pj_pool_destroy(pool);
-}
-
-
-#endif /* PJ_POOL_DEBUG */
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/pool.h> + +/* Only if we ARE debugging memory allocations. */ +#if PJ_POOL_DEBUG + +#include <pj/list.h> +#include <pj/log.h> + +#include <stdlib.h> +#include <stdio.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +typedef struct memory_entry +{ + PJ_DECL_LIST_MEMBER(struct memory_entry) + void *ptr; + char *file; + int line; +} memory_entry; + +struct pj_pool_t +{ + char obj_name[32]; + HANDLE hHeap; + memory_entry first; + pj_size_t initial_size; + pj_size_t increment; + pj_size_t used_size; + char *file; + int line; +}; + +PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t), + void (*free_func)(void *ptr, pj_size_t)) +{ + /* Ignored. */ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(malloc_func) + PJ_UNUSED_ARG(free_func) +} + +PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback, + char *file, int line) +{ + pj_pool_t *pool; + HANDLE hHeap; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(callback) + + /* Create Win32 heap for the pool. */ + hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + initial_size, 0); + if (!hHeap) { + return NULL; + } + + + /* Create and initialize the pool structure. */ + pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + sizeof(*pool)); + memset(pool, 0, sizeof(*pool)); + pool->file = file; + pool->line = line; + pool->hHeap = hHeap; + pool->initial_size = initial_size; + pool->increment = increment_size; + pool->used_size = 0; + + /* Set name. */ + if (name) { + if (strchr(name, '%') != NULL) { + sprintf(pool->obj_name, name, pool); + } else { + strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); + } + } else { + pool->obj_name[0] = '\0'; + } + + /* List pool's entry. */ + pj_list_init(&pool->first); + + PJ_LOG(3,(pool->obj_name, "Pool created")); + return pool; +} + +PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool ) +{ + memory_entry *entry; + + PJ_CHECK_STACK(); + + PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u", + pool->initial_size, pool->used_size)); + + if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) { + PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d", + pool->file, pool->line)); + } + + /* Validate all memory entries in the pool. */ + for (entry=pool->first.next; entry != &pool->first; entry = entry->next) { + if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) { + PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d", + entry->file, entry->line)); + } + + if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) { + PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d", + entry->file, entry->line)); + } + } + + /* Destroy heap. */ + HeapDestroy(pool->hHeap); +} + +PJ_DEF(void) pj_pool_reset( pj_pool_t *pool ) +{ + /* Do nothing. */ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) +} + +PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) + return 0; +} + +PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) + return 0; +} + +PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) + return 0; +} + +PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size, + char *file, int line) +{ + memory_entry *entry; + int entry_size; + + PJ_CHECK_STACK(); + + entry_size = sizeof(*entry); + entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + entry_size); + entry->file = file; + entry->line = line; + entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + size); + pj_list_insert_before( &pool->first, entry); + + pool->used_size += size; + return entry->ptr; +} + +PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem, + char *file, int line) +{ + void *ptr; + + PJ_CHECK_STACK(); + + ptr = pj_pool_alloc_dbg(pool, count*elem, file, line); + memset(ptr, 0, count*elem); + return ptr; +} + + +PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool, + pj_size_t max_capacity) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) + PJ_UNUSED_ARG(max_capacity) +} + +PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) +} + +PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) + return pj_pool_create(name, initial_size, increment_size, callback); +} + +PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool, + pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) + pj_pool_destroy(pool); +} + + +#endif /* PJ_POOL_DEBUG */ diff --git a/pjlib/src/pj/pool_policy_kmalloc.c b/pjlib/src/pj/pool_policy_kmalloc.c index 0b848d07..020ab1e1 100644 --- a/pjlib/src/pj/pool_policy_kmalloc.c +++ b/pjlib/src/pj/pool_policy_kmalloc.c @@ -1,58 +1,58 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/pool.h>
-#include <pj/except.h>
-#include <pj/os.h>
-
-
-static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
-
- return kmalloc(size, GFP_ATOMIC);
-}
-
-static void default_block_free(pj_pool_factory *factory,
- void *mem, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
- PJ_UNUSED_ARG(size);
-
- kfree(mem);
-}
-
-static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(size);
-
- PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
-}
-
-pj_pool_factory_policy pj_pool_factory_default_policy =
-{
- &default_block_alloc,
- &default_block_free,
- &default_pool_callback,
- 0
-};
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/pool.h> +#include <pj/except.h> +#include <pj/os.h> + + +static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + + return kmalloc(size, GFP_ATOMIC); +} + +static void default_block_free(pj_pool_factory *factory, + void *mem, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(size); + + kfree(mem); +} + +static void default_pool_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJ_NO_MEMORY_EXCEPTION); +} + +pj_pool_factory_policy pj_pool_factory_default_policy = +{ + &default_block_alloc, + &default_block_free, + &default_pool_callback, + 0 +}; + diff --git a/pjlib/src/pj/pool_policy_malloc.c b/pjlib/src/pj/pool_policy_malloc.c index 4a80708f..05aca5bb 100644 --- a/pjlib/src/pj/pool_policy_malloc.c +++ b/pjlib/src/pj/pool_policy_malloc.c @@ -1,62 +1,62 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/pool.h>
-#include <pj/except.h>
-#include <pj/os.h>
-#include <pj/compat/malloc.h>
-
-/*
- * This file contains pool default policy definition and implementation.
- */
-
-
-static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
- PJ_UNUSED_ARG(size);
-
- return malloc(size);
-}
-
-static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
- PJ_UNUSED_ARG(size);
-
- free(mem);
-}
-
-static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(size);
-
- PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
-}
-
-pj_pool_factory_policy pj_pool_factory_default_policy =
-{
- &default_block_alloc,
- &default_block_free,
- &default_pool_callback,
- 0
-};
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/pool.h> +#include <pj/except.h> +#include <pj/os.h> +#include <pj/compat/malloc.h> + +/* + * This file contains pool default policy definition and implementation. + */ + + +static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(size); + + return malloc(size); +} + +static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(size); + + free(mem); +} + +static void default_pool_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJ_NO_MEMORY_EXCEPTION); +} + +pj_pool_factory_policy pj_pool_factory_default_policy = +{ + &default_block_alloc, + &default_block_free, + &default_pool_callback, + 0 +}; diff --git a/pjlib/src/pj/rand.c b/pjlib/src/pj/rand.c index 87704edf..a3a4b88d 100644 --- a/pjlib/src/pj/rand.c +++ b/pjlib/src/pj/rand.c @@ -1,34 +1,34 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/rand.h>
-#include <pj/os.h>
-#include <pj/compat/rand.h>
-
-PJ_DEF(void) pj_srand(unsigned int seed)
-{
- PJ_CHECK_STACK();
- platform_srand(seed);
-}
-
-PJ_DEF(int) pj_rand(void)
-{
- PJ_CHECK_STACK();
- return platform_rand();
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/rand.h> +#include <pj/os.h> +#include <pj/compat/rand.h> + +PJ_DEF(void) pj_srand(unsigned int seed) +{ + PJ_CHECK_STACK(); + platform_srand(seed); +} + +PJ_DEF(int) pj_rand(void) +{ + PJ_CHECK_STACK(); + return platform_rand(); +} + diff --git a/pjlib/src/pj/rbtree.c b/pjlib/src/pj/rbtree.c index fa9f85a6..af5d7e52 100644 --- a/pjlib/src/pj/rbtree.c +++ b/pjlib/src/pj/rbtree.c @@ -1,427 +1,427 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/rbtree.h>
-#include <pj/os.h>
-
-static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *rnode, *parent;
-
- PJ_CHECK_STACK();
-
- rnode = node->right;
- if (rnode == tree->null)
- return;
-
- node->right = rnode->left;
- if (rnode->left != tree->null)
- rnode->left->parent = node;
- parent = node->parent;
- rnode->parent = parent;
- if (parent != tree->null) {
- if (parent->left == node)
- parent->left = rnode;
- else
- parent->right = rnode;
- } else {
- tree->root = rnode;
- }
- rnode->left = node;
- node->parent = rnode;
-}
-
-static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *lnode, *parent;
-
- PJ_CHECK_STACK();
-
- lnode = node->left;
- if (lnode == tree->null)
- return;
-
- node->left = lnode->right;
- if (lnode->right != tree->null)
- lnode->right->parent = node;
- parent = node->parent;
- lnode->parent = parent;
-
- if (parent != tree->null) {
- if (parent->left == node)
- parent->left = lnode;
- else
- parent->right = lnode;
- } else {
- tree->root = lnode;
- }
- lnode->right = node;
- node->parent = lnode;
-}
-
-static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *temp, *parent;
-
- PJ_CHECK_STACK();
-
- while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) {
- parent = node->parent;
- if (parent == parent->parent->left) {
- temp = parent->parent->right;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node = parent;
- node->color = PJ_RBCOLOR_BLACK;
- node = node->parent;
- node->color = PJ_RBCOLOR_RED;
- } else {
- if (node == parent->right) {
- node = parent;
- left_rotate(tree, node);
- }
- temp = node->parent;
- temp->color = PJ_RBCOLOR_BLACK;
- temp = temp->parent;
- temp->color = PJ_RBCOLOR_RED;
- right_rotate( tree, temp);
- }
- } else {
- temp = parent->parent->left;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node = parent;
- node->color = PJ_RBCOLOR_BLACK;
- node = node->parent;
- node->color = PJ_RBCOLOR_RED;
- } else {
- if (node == parent->left) {
- node = parent;
- right_rotate(tree, node);
- }
- temp = node->parent;
- temp->color = PJ_RBCOLOR_BLACK;
- temp = temp->parent;
- temp->color = PJ_RBCOLOR_RED;
- left_rotate(tree, temp);
- }
- }
- }
-
- tree->root->color = PJ_RBCOLOR_BLACK;
-}
-
-
-static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *temp;
-
- PJ_CHECK_STACK();
-
- while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) {
- if (node->parent->left == node) {
- temp = node->parent->right;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node->parent->color = PJ_RBCOLOR_RED;
- left_rotate(tree, node->parent);
- temp = node->parent->right;
- }
- if (temp->left->color == PJ_RBCOLOR_BLACK &&
- temp->right->color == PJ_RBCOLOR_BLACK)
- {
- temp->color = PJ_RBCOLOR_RED;
- node = node->parent;
- } else {
- if (temp->right->color == PJ_RBCOLOR_BLACK) {
- temp->left->color = PJ_RBCOLOR_BLACK;
- temp->color = PJ_RBCOLOR_RED;
- right_rotate( tree, temp);
- temp = node->parent->right;
- }
- temp->color = node->parent->color;
- temp->right->color = PJ_RBCOLOR_BLACK;
- node->parent->color = PJ_RBCOLOR_BLACK;
- left_rotate(tree, node->parent);
- node = tree->root;
- }
- } else {
- temp = node->parent->left;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node->parent->color = PJ_RBCOLOR_RED;
- right_rotate( tree, node->parent);
- temp = node->parent->left;
- }
- if (temp->right->color == PJ_RBCOLOR_BLACK &&
- temp->left->color == PJ_RBCOLOR_BLACK)
- {
- temp->color = PJ_RBCOLOR_RED;
- node = node->parent;
- } else {
- if (temp->left->color == PJ_RBCOLOR_BLACK) {
- temp->right->color = PJ_RBCOLOR_BLACK;
- temp->color = PJ_RBCOLOR_RED;
- left_rotate( tree, temp);
- temp = node->parent->left;
- }
- temp->color = node->parent->color;
- node->parent->color = PJ_RBCOLOR_BLACK;
- temp->left->color = PJ_RBCOLOR_BLACK;
- right_rotate(tree, node->parent);
- node = tree->root;
- }
- }
- }
-
- node->color = PJ_RBCOLOR_BLACK;
-}
-
-
-PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp )
-{
- PJ_CHECK_STACK();
-
- tree->null = tree->root = &tree->null_node;
- tree->null->key = NULL;
- tree->null->user_data = NULL;
- tree->size = 0;
- tree->null->left = tree->null->right = tree->null->parent = tree->null;
- tree->null->color = PJ_RBCOLOR_BLACK;
- tree->comp = comp;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree )
-{
- register pj_rbtree_node *node = tree->root;
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- while (node->left != null)
- node = node->left;
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree )
-{
- register pj_rbtree_node *node = tree->root;
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- while (node->right != null)
- node = node->right;
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree,
- register pj_rbtree_node *node )
-{
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- if (node->right != null) {
- for (node=node->right; node->left!=null; node = node->left)
- /* void */;
- } else {
- register pj_rbtree_node *temp = node->parent;
- while (temp!=null && temp->right==node) {
- node = temp;
- temp = temp->parent;
- }
- node = temp;
- }
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree,
- register pj_rbtree_node *node )
-{
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- if (node->left != null) {
- for (node=node->left; node->right!=null; node=node->right)
- /* void */;
- } else {
- register pj_rbtree_node *temp = node->parent;
- while (temp!=null && temp->left==node) {
- node = temp;
- temp = temp->parent;
- }
- node = temp;
- }
- return node != null ? node : NULL;
-}
-
-PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree,
- pj_rbtree_node *element )
-{
- int rv = 0;
- pj_rbtree_node *node, *parent = tree->null,
- *null = tree->null;
- pj_rbtree_comp *comp = tree->comp;
-
- PJ_CHECK_STACK();
-
- node = tree->root;
- while (node != null) {
- rv = (*comp)(element->key, node->key);
- if (rv == 0) {
- /* found match, i.e. entry with equal key already exist */
- return -1;
- }
- parent = node;
- node = rv < 0 ? node->left : node->right;
- }
-
- element->color = PJ_RBCOLOR_RED;
- element->left = element->right = null;
-
- node = element;
- if (parent != null) {
- node->parent = parent;
- if (rv < 0)
- parent->left = node;
- else
- parent->right = node;
- insert_fixup( tree, node);
- } else {
- tree->root = node;
- node->parent = null;
- node->color = PJ_RBCOLOR_BLACK;
- }
-
- ++tree->size;
- return 0;
-}
-
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
- const void *key )
-{
- int rv;
- pj_rbtree_node *node = tree->root;
- pj_rbtree_node *null = tree->null;
- pj_rbtree_comp *comp = tree->comp;
-
- while (node != null) {
- rv = (*comp)(key, node->key);
- if (rv == 0)
- return node;
- node = rv < 0 ? node->left : node->right;
- }
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
- pj_rbtree_node *node )
-{
- pj_rbtree_node *succ;
- pj_rbtree_node *null = tree->null;
- pj_rbtree_node *child;
- pj_rbtree_node *parent;
-
- PJ_CHECK_STACK();
-
- if (node->left == null || node->right == null) {
- succ = node;
- } else {
- for (succ=node->right; succ->left!=null; succ=succ->left)
- /* void */;
- }
-
- child = succ->left != null ? succ->left : succ->right;
- parent = succ->parent;
- child->parent = parent;
-
- if (parent != null) {
- if (parent->left == succ)
- parent->left = child;
- else
- parent->right = child;
- } else
- tree->root = child;
-
- if (succ != node) {
- succ->parent = node->parent;
- succ->left = node->left;
- succ->right = node->right;
- succ->color = node->color;
-
- parent = node->parent;
- if (parent != null) {
- if (parent->left==node)
- parent->left=succ;
- else
- parent->right=succ;
- }
- if (node->left != null)
- node->left->parent = succ;;
- if (node->right != null)
- node->right->parent = succ;
-
- if (tree->root == node)
- tree->root = succ;
- }
-
- if (succ->color == PJ_RBCOLOR_BLACK) {
- if (child != null)
- delete_fixup(tree, child);
- tree->null->color = PJ_RBCOLOR_BLACK;
- }
-
- --tree->size;
- return node;
-}
-
-
-PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
- pj_rbtree_node *node )
-{
- unsigned l, r;
-
- PJ_CHECK_STACK();
-
- if (node==NULL)
- node = tree->root;
-
- l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0;
- r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0;
- return l > r ? l : r;
-}
-
-PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
- pj_rbtree_node *node )
-{
- unsigned l, r;
-
- PJ_CHECK_STACK();
-
- if (node==NULL)
- node=tree->root;
-
- l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0;
- r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0;
- return l > r ? r : l;
-}
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/rbtree.h> +#include <pj/os.h> + +static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *rnode, *parent; + + PJ_CHECK_STACK(); + + rnode = node->right; + if (rnode == tree->null) + return; + + node->right = rnode->left; + if (rnode->left != tree->null) + rnode->left->parent = node; + parent = node->parent; + rnode->parent = parent; + if (parent != tree->null) { + if (parent->left == node) + parent->left = rnode; + else + parent->right = rnode; + } else { + tree->root = rnode; + } + rnode->left = node; + node->parent = rnode; +} + +static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *lnode, *parent; + + PJ_CHECK_STACK(); + + lnode = node->left; + if (lnode == tree->null) + return; + + node->left = lnode->right; + if (lnode->right != tree->null) + lnode->right->parent = node; + parent = node->parent; + lnode->parent = parent; + + if (parent != tree->null) { + if (parent->left == node) + parent->left = lnode; + else + parent->right = lnode; + } else { + tree->root = lnode; + } + lnode->right = node; + node->parent = lnode; +} + +static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *temp, *parent; + + PJ_CHECK_STACK(); + + while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) { + parent = node->parent; + if (parent == parent->parent->left) { + temp = parent->parent->right; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node = parent; + node->color = PJ_RBCOLOR_BLACK; + node = node->parent; + node->color = PJ_RBCOLOR_RED; + } else { + if (node == parent->right) { + node = parent; + left_rotate(tree, node); + } + temp = node->parent; + temp->color = PJ_RBCOLOR_BLACK; + temp = temp->parent; + temp->color = PJ_RBCOLOR_RED; + right_rotate( tree, temp); + } + } else { + temp = parent->parent->left; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node = parent; + node->color = PJ_RBCOLOR_BLACK; + node = node->parent; + node->color = PJ_RBCOLOR_RED; + } else { + if (node == parent->left) { + node = parent; + right_rotate(tree, node); + } + temp = node->parent; + temp->color = PJ_RBCOLOR_BLACK; + temp = temp->parent; + temp->color = PJ_RBCOLOR_RED; + left_rotate(tree, temp); + } + } + } + + tree->root->color = PJ_RBCOLOR_BLACK; +} + + +static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *temp; + + PJ_CHECK_STACK(); + + while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) { + if (node->parent->left == node) { + temp = node->parent->right; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_RED; + left_rotate(tree, node->parent); + temp = node->parent->right; + } + if (temp->left->color == PJ_RBCOLOR_BLACK && + temp->right->color == PJ_RBCOLOR_BLACK) + { + temp->color = PJ_RBCOLOR_RED; + node = node->parent; + } else { + if (temp->right->color == PJ_RBCOLOR_BLACK) { + temp->left->color = PJ_RBCOLOR_BLACK; + temp->color = PJ_RBCOLOR_RED; + right_rotate( tree, temp); + temp = node->parent->right; + } + temp->color = node->parent->color; + temp->right->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_BLACK; + left_rotate(tree, node->parent); + node = tree->root; + } + } else { + temp = node->parent->left; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_RED; + right_rotate( tree, node->parent); + temp = node->parent->left; + } + if (temp->right->color == PJ_RBCOLOR_BLACK && + temp->left->color == PJ_RBCOLOR_BLACK) + { + temp->color = PJ_RBCOLOR_RED; + node = node->parent; + } else { + if (temp->left->color == PJ_RBCOLOR_BLACK) { + temp->right->color = PJ_RBCOLOR_BLACK; + temp->color = PJ_RBCOLOR_RED; + left_rotate( tree, temp); + temp = node->parent->left; + } + temp->color = node->parent->color; + node->parent->color = PJ_RBCOLOR_BLACK; + temp->left->color = PJ_RBCOLOR_BLACK; + right_rotate(tree, node->parent); + node = tree->root; + } + } + } + + node->color = PJ_RBCOLOR_BLACK; +} + + +PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp ) +{ + PJ_CHECK_STACK(); + + tree->null = tree->root = &tree->null_node; + tree->null->key = NULL; + tree->null->user_data = NULL; + tree->size = 0; + tree->null->left = tree->null->right = tree->null->parent = tree->null; + tree->null->color = PJ_RBCOLOR_BLACK; + tree->comp = comp; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ) +{ + register pj_rbtree_node *node = tree->root; + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + while (node->left != null) + node = node->left; + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ) +{ + register pj_rbtree_node *node = tree->root; + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + while (node->right != null) + node = node->right; + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, + register pj_rbtree_node *node ) +{ + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + if (node->right != null) { + for (node=node->right; node->left!=null; node = node->left) + /* void */; + } else { + register pj_rbtree_node *temp = node->parent; + while (temp!=null && temp->right==node) { + node = temp; + temp = temp->parent; + } + node = temp; + } + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, + register pj_rbtree_node *node ) +{ + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + if (node->left != null) { + for (node=node->left; node->right!=null; node=node->right) + /* void */; + } else { + register pj_rbtree_node *temp = node->parent; + while (temp!=null && temp->left==node) { + node = temp; + temp = temp->parent; + } + node = temp; + } + return node != null ? node : NULL; +} + +PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree, + pj_rbtree_node *element ) +{ + int rv = 0; + pj_rbtree_node *node, *parent = tree->null, + *null = tree->null; + pj_rbtree_comp *comp = tree->comp; + + PJ_CHECK_STACK(); + + node = tree->root; + while (node != null) { + rv = (*comp)(element->key, node->key); + if (rv == 0) { + /* found match, i.e. entry with equal key already exist */ + return -1; + } + parent = node; + node = rv < 0 ? node->left : node->right; + } + + element->color = PJ_RBCOLOR_RED; + element->left = element->right = null; + + node = element; + if (parent != null) { + node->parent = parent; + if (rv < 0) + parent->left = node; + else + parent->right = node; + insert_fixup( tree, node); + } else { + tree->root = node; + node->parent = null; + node->color = PJ_RBCOLOR_BLACK; + } + + ++tree->size; + return 0; +} + + +PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, + const void *key ) +{ + int rv; + pj_rbtree_node *node = tree->root; + pj_rbtree_node *null = tree->null; + pj_rbtree_comp *comp = tree->comp; + + while (node != null) { + rv = (*comp)(key, node->key); + if (rv == 0) + return node; + node = rv < 0 ? node->left : node->right; + } + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + pj_rbtree_node *succ; + pj_rbtree_node *null = tree->null; + pj_rbtree_node *child; + pj_rbtree_node *parent; + + PJ_CHECK_STACK(); + + if (node->left == null || node->right == null) { + succ = node; + } else { + for (succ=node->right; succ->left!=null; succ=succ->left) + /* void */; + } + + child = succ->left != null ? succ->left : succ->right; + parent = succ->parent; + child->parent = parent; + + if (parent != null) { + if (parent->left == succ) + parent->left = child; + else + parent->right = child; + } else + tree->root = child; + + if (succ != node) { + succ->parent = node->parent; + succ->left = node->left; + succ->right = node->right; + succ->color = node->color; + + parent = node->parent; + if (parent != null) { + if (parent->left==node) + parent->left=succ; + else + parent->right=succ; + } + if (node->left != null) + node->left->parent = succ;; + if (node->right != null) + node->right->parent = succ; + + if (tree->root == node) + tree->root = succ; + } + + if (succ->color == PJ_RBCOLOR_BLACK) { + if (child != null) + delete_fixup(tree, child); + tree->null->color = PJ_RBCOLOR_BLACK; + } + + --tree->size; + return node; +} + + +PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + unsigned l, r; + + PJ_CHECK_STACK(); + + if (node==NULL) + node = tree->root; + + l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0; + r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0; + return l > r ? l : r; +} + +PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + unsigned l, r; + + PJ_CHECK_STACK(); + + if (node==NULL) + node=tree->root; + + l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0; + r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0; + return l > r ? r : l; +} + + diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c index 4db8e9c5..5a096936 100644 --- a/pjlib/src/pj/sock_bsd.c +++ b/pjlib/src/pj/sock_bsd.c @@ -1,584 +1,584 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/sock.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/string.h>
-#include <pj/compat/socket.h>
-#include <pj/addr_resolv.h>
-#include <pj/errno.h>
-
-/*
- * Address families conversion.
- * The values here are indexed based on pj_addr_family-0xFF00.
- */
-const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
-const pj_uint16_t PJ_AF_INET = AF_INET;
-const pj_uint16_t PJ_AF_INET6 = AF_INET6;
-#ifdef AF_PACKET
-const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
-#else
-const pj_uint16_t PJ_AF_PACKET = 0xFFFF;
-#endif
-#ifdef AF_IRDA
-const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
-#else
-const pj_uint16_t PJ_AF_IRDA = 0xFFFF;
-#endif
-
-/*
- * Socket types conversion.
- * The values here are indexed based on pj_sock_type-0xFF00
- */
-const pj_uint16_t PJ_SOCK_STREAM = SOCK_STREAM;
-const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
-const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
-const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
-
-/*
- * Socket level values.
- */
-const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
-#ifdef SOL_IP
-const pj_uint16_t PJ_SOL_IP = SOL_IP;
-#else
-const pj_uint16_t PJ_SOL_IP = 0xFFFF;
-#endif /* SOL_IP */
-#if defined(SOL_TCP)
-const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
-#elif defined(IPPROTO_TCP)
-const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP;
-#endif /* SOL_TCP */
-#ifdef SOL_UDP
-const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
-#else
-const pj_uint16_t PJ_SOL_UDP = 0xFFFF;
-#endif
-#ifdef SOL_IPV6
-const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
-#else
-const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF;
-#endif
-
-/* optname values. */
-const pj_uint16_t PJ_SO_TYPE = SO_TYPE;
-const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF;
-const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF;
-
-
-/*
- * Convert 16-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
-{
- return ntohs(netshort);
-}
-
-/*
- * Convert 16-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
-{
- return htons(hostshort);
-}
-
-/*
- * Convert 32-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
-{
- return ntohl(netlong);
-}
-
-/*
- * Convert 32-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
-{
- return htonl(hostlong);
-}
-
-/*
- * Convert an Internet host address given in network byte order
- * to string in standard numbers and dots notation.
- */
-PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
-{
-#if !defined(PJ_LINUX) && !defined(PJ_LINUX_KERNEL)
- return inet_ntoa(*(struct in_addr*)&inaddr);
-#else
- struct in_addr addr;
- addr.s_addr = inaddr.s_addr;
- return inet_ntoa(addr);
-#endif
-}
-
-/*
- * This function converts the Internet host address cp from the standard
- * numbers-and-dots notation into binary data and stores it in the structure
- * that inp points to.
- */
-PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp)
-{
- char tempaddr[16];
-
- /* Initialize output with PJ_INADDR_NONE.
- * Some apps relies on this instead of the return value
- * (and anyway the return value is quite confusing!)
- */
- inp->s_addr = PJ_INADDR_NONE;
-
- /* Caution:
- * this function might be called with cp->slen >= 16
- * (i.e. when called with hostname to check if it's an IP addr).
- */
- PJ_ASSERT_RETURN(cp && cp->slen && inp, 0);
- if (cp->slen >= 16) {
- return 0;
- }
-
- pj_memcpy(tempaddr, cp->ptr, cp->slen);
- tempaddr[cp->slen] = '\0';
-
-#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0
- return inet_aton(tempaddr, (struct in_addr*)inp);
-#else
- inp->s_addr = inet_addr(tempaddr);
- return inp->s_addr == PJ_INADDR_NONE ? 0 : 1;
-#endif
-}
-
-/*
- * Convert address string with numbers and dots to binary IP address.
- */
-PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
-{
- pj_in_addr addr;
-
- pj_inet_aton(cp, &addr);
- return addr;
-}
-
-/*
- * Set the IP address of an IP socket address from string address,
- * with resolving the host if necessary. The string address may be in a
- * standard numbers and dots notation or may be a hostname. If hostname
- * is specified, then the function will resolve the host into the IP
- * address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
- const pj_str_t *str_addr)
-{
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME,
- (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
-
- addr->sin_family = AF_INET;
-
- if (str_addr && str_addr->slen) {
- addr->sin_addr = pj_inet_addr(str_addr);
- if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
- pj_hostent he;
- pj_status_t rc;
-
- rc = pj_gethostbyname(str_addr, &he);
- if (rc == 0) {
- addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
- } else {
- addr->sin_addr.s_addr = PJ_INADDR_NONE;
- return rc;
- }
- }
-
- } else {
- addr->sin_addr.s_addr = 0;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set the IP address and port of an IP socket address.
- * The string address may be in a standard numbers and dots notation or
- * may be a hostname. If hostname is specified, then the function will
- * resolve the host into the IP address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
- const pj_str_t *str_addr,
- pj_uint16_t port)
-{
- PJ_ASSERT_RETURN(addr && str_addr,
- (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
-
- addr->sin_family = PJ_AF_INET;
- pj_sockaddr_in_set_port(addr, port);
- return pj_sockaddr_in_set_str_addr(addr, str_addr);
-}
-
-
-/*
- * Get hostname.
- */
-PJ_DEF(const pj_str_t*) pj_gethostname(void)
-{
- static char buf[PJ_MAX_HOSTNAME];
- static pj_str_t hostname;
-
- PJ_CHECK_STACK();
-
- if (hostname.ptr == NULL) {
- hostname.ptr = buf;
- if (gethostname(buf, sizeof(buf)) != 0) {
- hostname.ptr[0] = '\0';
- hostname.slen = 0;
- } else {
- hostname.slen = strlen(buf);
- }
- }
- return &hostname;
-}
-
-/*
- * Get first IP address associated with the hostname.
- */
-PJ_DEF(pj_in_addr) pj_gethostaddr(void)
-{
- pj_sockaddr_in addr;
- const pj_str_t *hostname = pj_gethostname();
-
- pj_sockaddr_in_set_str_addr(&addr, hostname);
- return addr.sin_addr;
-}
-
-
-#if defined(PJ_WIN32)
-/*
- * Create new socket/endpoint for communication and returns a descriptor.
- */
-PJ_DEF(pj_status_t) pj_sock_socket(int af,
- int type,
- int proto,
- pj_sock_t *sock)
-{
- PJ_CHECK_STACK();
-
- /* Sanity checks. */
- PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
- PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET,
- (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
-
- *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED);
-
- if (*sock == PJ_INVALID_SOCKET)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-#else
-/*
- * Create new socket/endpoint for communication and returns a descriptor.
- */
-PJ_DEF(pj_status_t) pj_sock_socket(int af,
- int type,
- int proto,
- pj_sock_t *sock)
-{
-
- PJ_CHECK_STACK();
-
- /* Sanity checks. */
- PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
- PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1,
- (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
-
- *sock = socket(af, type, proto);
- if (*sock == PJ_INVALID_SOCKET)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-#endif
-
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock,
- const pj_sockaddr_t *addr,
- int len)
-{
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(addr && len > 0, PJ_EINVAL);
-
- if (bind(sock, (struct sockaddr*)addr, len) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock,
- pj_uint32_t addr32,
- pj_uint16_t port)
-{
- pj_sockaddr_in addr;
-
- PJ_CHECK_STACK();
-
- addr.sin_family = PJ_AF_INET;
- addr.sin_addr.s_addr = pj_htonl(addr32);
- addr.sin_port = pj_htons(port);
-
- return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in));
-}
-
-
-/*
- * Close socket.
- */
-PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock)
-{
- int rc;
-
- PJ_CHECK_STACK();
-#if defined(PJ_WIN32) && PJ_WIN32==1
- rc = closesocket(sock);
-#else
- rc = close(sock);
-#endif
-
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get remote's name.
- */
-PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- PJ_CHECK_STACK();
- if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get socket name.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- PJ_CHECK_STACK();
- if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Send data
- */
-PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(len, PJ_EINVAL);
-
- *len = send(sock, (const char*)buf, *len, flags);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-
-/*
- * Send data.
- */
-PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags,
- const pj_sockaddr_t *to,
- int tolen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(len, PJ_EINVAL);
-
- *len = sendto(sock, (const char*)buf, *len, flags,
- (const struct sockaddr*)to, tolen);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock,
- void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
-
- *len = recv(sock, (char*)buf, *len, flags);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
- void *buf,
- pj_ssize_t *len,
- unsigned flags,
- pj_sockaddr_t *from,
- int *fromlen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
- PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL));
-
- *len = recvfrom(sock, (char*)buf, *len, flags,
- (struct sockaddr*)from, (socklen_t*)fromlen);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
- pj_uint16_t level,
- pj_uint16_t optname,
- void *optval,
- int *optlen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL);
-
- if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Set socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
- pj_uint16_t level,
- pj_uint16_t optname,
- const void *optval,
- int optlen)
-{
- PJ_CHECK_STACK();
- if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Shutdown socket.
- */
-#if PJ_HAS_TCP
-PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock,
- int how)
-{
- PJ_CHECK_STACK();
- if (shutdown(sock, how) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Start listening to incoming connections.
- */
-PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
- int backlog)
-{
- PJ_CHECK_STACK();
- if (listen(sock, backlog) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Connect socket.
- */
-PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
- const pj_sockaddr_t *addr,
- int namelen)
-{
- PJ_CHECK_STACK();
- if (connect(sock, (struct sockaddr*)addr, namelen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Accept incoming connections
- */
-PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
- pj_sock_t *newsock,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL);
-
- *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen);
- if (*newsock==PJ_INVALID_SOCKET)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-#endif /* PJ_HAS_TCP */
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/sock.h> +#include <pj/os.h> +#include <pj/assert.h> +#include <pj/string.h> +#include <pj/compat/socket.h> +#include <pj/addr_resolv.h> +#include <pj/errno.h> + +/* + * Address families conversion. + * The values here are indexed based on pj_addr_family-0xFF00. + */ +const pj_uint16_t PJ_AF_UNIX = AF_UNIX; +const pj_uint16_t PJ_AF_INET = AF_INET; +const pj_uint16_t PJ_AF_INET6 = AF_INET6; +#ifdef AF_PACKET +const pj_uint16_t PJ_AF_PACKET = AF_PACKET; +#else +const pj_uint16_t PJ_AF_PACKET = 0xFFFF; +#endif +#ifdef AF_IRDA +const pj_uint16_t PJ_AF_IRDA = AF_IRDA; +#else +const pj_uint16_t PJ_AF_IRDA = 0xFFFF; +#endif + +/* + * Socket types conversion. + * The values here are indexed based on pj_sock_type-0xFF00 + */ +const pj_uint16_t PJ_SOCK_STREAM = SOCK_STREAM; +const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; +const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; +const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; + +/* + * Socket level values. + */ +const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; +#ifdef SOL_IP +const pj_uint16_t PJ_SOL_IP = SOL_IP; +#else +const pj_uint16_t PJ_SOL_IP = 0xFFFF; +#endif /* SOL_IP */ +#if defined(SOL_TCP) +const pj_uint16_t PJ_SOL_TCP = SOL_TCP; +#elif defined(IPPROTO_TCP) +const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; +#endif /* SOL_TCP */ +#ifdef SOL_UDP +const pj_uint16_t PJ_SOL_UDP = SOL_UDP; +#else +const pj_uint16_t PJ_SOL_UDP = 0xFFFF; +#endif +#ifdef SOL_IPV6 +const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; +#else +const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF; +#endif + +/* optname values. */ +const pj_uint16_t PJ_SO_TYPE = SO_TYPE; +const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; +const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; + + +/* + * Convert 16-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) +{ + return ntohs(netshort); +} + +/* + * Convert 16-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) +{ + return htons(hostshort); +} + +/* + * Convert 32-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) +{ + return ntohl(netlong); +} + +/* + * Convert 32-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) +{ + return htonl(hostlong); +} + +/* + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + */ +PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) +{ +#if !defined(PJ_LINUX) && !defined(PJ_LINUX_KERNEL) + return inet_ntoa(*(struct in_addr*)&inaddr); +#else + struct in_addr addr; + addr.s_addr = inaddr.s_addr; + return inet_ntoa(addr); +#endif +} + +/* + * This function converts the Internet host address cp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + */ +PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp) +{ + char tempaddr[16]; + + /* Initialize output with PJ_INADDR_NONE. + * Some apps relies on this instead of the return value + * (and anyway the return value is quite confusing!) + */ + inp->s_addr = PJ_INADDR_NONE; + + /* Caution: + * this function might be called with cp->slen >= 16 + * (i.e. when called with hostname to check if it's an IP addr). + */ + PJ_ASSERT_RETURN(cp && cp->slen && inp, 0); + if (cp->slen >= 16) { + return 0; + } + + pj_memcpy(tempaddr, cp->ptr, cp->slen); + tempaddr[cp->slen] = '\0'; + +#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0 + return inet_aton(tempaddr, (struct in_addr*)inp); +#else + inp->s_addr = inet_addr(tempaddr); + return inp->s_addr == PJ_INADDR_NONE ? 0 : 1; +#endif +} + +/* + * Convert address string with numbers and dots to binary IP address. + */ +PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) +{ + pj_in_addr addr; + + pj_inet_aton(cp, &addr); + return addr; +} + +/* + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *str_addr) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME, + (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); + + addr->sin_family = AF_INET; + + if (str_addr && str_addr->slen) { + addr->sin_addr = pj_inet_addr(str_addr); + if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { + pj_hostent he; + pj_status_t rc; + + rc = pj_gethostbyname(str_addr, &he); + if (rc == 0) { + addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; + } else { + addr->sin_addr.s_addr = PJ_INADDR_NONE; + return rc; + } + } + + } else { + addr->sin_addr.s_addr = 0; + } + + return PJ_SUCCESS; +} + +/* + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *str_addr, + pj_uint16_t port) +{ + PJ_ASSERT_RETURN(addr && str_addr, + (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); + + addr->sin_family = PJ_AF_INET; + pj_sockaddr_in_set_port(addr, port); + return pj_sockaddr_in_set_str_addr(addr, str_addr); +} + + +/* + * Get hostname. + */ +PJ_DEF(const pj_str_t*) pj_gethostname(void) +{ + static char buf[PJ_MAX_HOSTNAME]; + static pj_str_t hostname; + + PJ_CHECK_STACK(); + + if (hostname.ptr == NULL) { + hostname.ptr = buf; + if (gethostname(buf, sizeof(buf)) != 0) { + hostname.ptr[0] = '\0'; + hostname.slen = 0; + } else { + hostname.slen = strlen(buf); + } + } + return &hostname; +} + +/* + * Get first IP address associated with the hostname. + */ +PJ_DEF(pj_in_addr) pj_gethostaddr(void) +{ + pj_sockaddr_in addr; + const pj_str_t *hostname = pj_gethostname(); + + pj_sockaddr_in_set_str_addr(&addr, hostname); + return addr.sin_addr; +} + + +#if defined(PJ_WIN32) +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, + pj_sock_t *sock) +{ + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); + PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED); + + if (*sock == PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +#else +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, + pj_sock_t *sock) +{ + + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); + PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + *sock = socket(af, type, proto); + if (*sock == PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} +#endif + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, + const pj_sockaddr_t *addr, + int len) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(addr && len > 0, PJ_EINVAL); + + if (bind(sock, (struct sockaddr*)addr, len) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, + pj_uint32_t addr32, + pj_uint16_t port) +{ + pj_sockaddr_in addr; + + PJ_CHECK_STACK(); + + addr.sin_family = PJ_AF_INET; + addr.sin_addr.s_addr = pj_htonl(addr32); + addr.sin_port = pj_htons(port); + + return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in)); +} + + +/* + * Close socket. + */ +PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock) +{ + int rc; + + PJ_CHECK_STACK(); +#if defined(PJ_WIN32) && PJ_WIN32==1 + rc = closesocket(sock); +#else + rc = close(sock); +#endif + + if (rc != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Get remote's name. + */ +PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Get socket name. + */ +PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Send data + */ +PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(len, PJ_EINVAL); + + *len = send(sock, (const char*)buf, *len, flags); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + + +/* + * Send data. + */ +PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *to, + int tolen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(len, PJ_EINVAL); + + *len = sendto(sock, (const char*)buf, *len, flags, + (const struct sockaddr*)to, tolen); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); + + *len = recv(sock, (char*)buf, *len, flags); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); + PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL)); + + *len = recvfrom(sock, (char*)buf, *len, flags, + (struct sockaddr*)from, (socklen_t*)fromlen); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Get socket option. + */ +PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock, + pj_uint16_t level, + pj_uint16_t optname, + void *optval, + int *optlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL); + + if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Set socket option. + */ +PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, + pj_uint16_t level, + pj_uint16_t optname, + const void *optval, + int optlen) +{ + PJ_CHECK_STACK(); + if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Shutdown socket. + */ +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock, + int how) +{ + PJ_CHECK_STACK(); + if (shutdown(sock, how) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Start listening to incoming connections. + */ +PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock, + int backlog) +{ + PJ_CHECK_STACK(); + if (listen(sock, backlog) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Connect socket. + */ +PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock, + const pj_sockaddr_t *addr, + int namelen) +{ + PJ_CHECK_STACK(); + if (connect(sock, (struct sockaddr*)addr, namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Accept incoming connections + */ +PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, + pj_sock_t *newsock, + pj_sockaddr_t *addr, + int *addrlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL); + + *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); + if (*newsock==PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} +#endif /* PJ_HAS_TCP */ + + diff --git a/pjlib/src/pj/sock_linux_kernel.c b/pjlib/src/pj/sock_linux_kernel.c index d0778ebf..27fc3342 100644 --- a/pjlib/src/pj/sock_linux_kernel.c +++ b/pjlib/src/pj/sock_linux_kernel.c @@ -1,754 +1,754 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/sock.h>
-#include <pj/assert.h>
-#include <pj/string.h> /* pj_memcpy() */
-#include <pj/os.h> /* PJ_CHECK_STACK() */
-#include <pj/addr_resolv.h> /* pj_gethostbyname() */
-#include <pj/ctype.h>
-#include <pj/compat/sprintf.h>
-#include <pj/log.h>
-#include <pj/errno.h>
-
-/* Linux kernel specific. */
-#include <linux/socket.h>
-#include <linux/net.h>
-//#include <net/sock.h>
-#include <linux/security.h>
-#include <linux/syscalls.h> /* sys_xxx() */
-#include <asm/ioctls.h> /* FIONBIO */
-#include <linux/utsname.h> /* for pj_gethostname() */
-
-/*
- * Address families conversion.
- * The values here are indexed based on pj_addr_family-0xFF00.
- */
-const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
-const pj_uint16_t PJ_AF_INET = AF_INET;
-const pj_uint16_t PJ_AF_INET6 = AF_INET6;
-#ifdef AF_PACKET
-const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
-#else
-# error "AF_PACKET undeclared!"
-#endif
-#ifdef AF_IRDA
-const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
-#else
-# error "AF_IRDA undeclared!"
-#endif
-
-/*
- * Socket types conversion.
- * The values here are indexed based on pj_sock_type-0xFF00
- */
-const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM;
-const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
-const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
-const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
-
-/*
- * Socket level values.
- */
-const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
-#ifdef SOL_IP
-const pj_uint16_t PJ_SOL_IP = SOL_IP;
-#else
-# error "SOL_IP undeclared!"
-#endif /* SOL_IP */
-#if defined(SOL_TCP)
-const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
-#else
-# error "SOL_TCP undeclared!"
-#endif /* SOL_TCP */
-#ifdef SOL_UDP
-const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
-#else
-# error "SOL_UDP undeclared!"
-#endif
-#ifdef SOL_IPV6
-const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
-#else
-# error "SOL_IPV6 undeclared!"
-#endif
-
-/* optname values. */
-const pj_uint16_t PJ_SO_TYPE = SO_TYPE;
-const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF;
-const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF;
-
-/*
- * Convert 16-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
-{
- return ntohs(netshort);
-}
-
-/*
- * Convert 16-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
-{
- return htons(hostshort);
-}
-
-/*
- * Convert 32-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
-{
- return ntohl(netlong);
-}
-
-/*
- * Convert 32-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
-{
- return htonl(hostlong);
-}
-
-/*
- * Convert an Internet host address given in network byte order
- * to string in standard numbers and dots notation.
- */
-PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in)
-{
-#define UC(b) (((int)b)&0xff)
- static char b[18];
- char *p;
-
- p = (char *)∈
- pj_snprintf(b, sizeof(b), "%d.%d.%d.%d",
- UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
-
- return b;
-}
-
-/*
- * This function converts the Internet host address ccp from the standard
- * numbers-and-dots notation into binary data and stores it in the structure
- * that inp points to.
- */
-PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr)
-{
- pj_uint32_t val;
- int base, n;
- char c;
- unsigned parts[4];
- unsigned *pp = parts;
- char cp_copy[18];
- char *cp = cp_copy;
-
- addr->s_addr = PJ_INADDR_NONE;
-
- if (ccp->slen > 15) return 0;
-
- pj_memcpy(cp, ccp->ptr, ccp->slen);
- cp[ccp->slen] = '\0';
-
- c = *cp;
- for (;;) {
- /*
- * Collect number up to ``.''.
- * Values are specified as for C:
- * 0x=hex, 0=octal, isdigit=decimal.
- */
- if (!pj_isdigit((int)c))
- return (0);
- val = 0; base = 10;
- if (c == '0') {
- c = *++cp;
- if (c == 'x' || c == 'X')
- base = 16, c = *++cp;
- else
- base = 8;
- }
-
- for (;;) {
- if (pj_isascii((int)c) && pj_isdigit((int)c)) {
- val = (val * base) + (c - '0');
- c = *++cp;
- } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) {
- val = (val << 4) |
- (c + 10 - (pj_islower((int)c) ? 'a' : 'A'));
- c = *++cp;
- } else
- break;
- }
-
- if (c == '.') {
- /*
- * Internet format:
- * a.b.c.d
- * a.b.c (with c treated as 16 bits)
- * a.b (with b treated as 24 bits)
- */
- if (pp >= parts + 3)
- return (0);
- *pp++ = val;
- c = *++cp;
- } else
- break;
- }
-
- /*
- * Check for trailing characters.
- */
- if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c)))
- return (0);
- /*
- * Concoct the address according to
- * the number of parts specified.
- */
- n = pp - parts + 1;
- switch (n) {
- case 0:
- return (0); /* initial nondigit */
- case 1: /* a -- 32 bits */
- break;
- case 2: /* a.b -- 8.24 bits */
- if (val > 0xffffff)
- return (0);
- val |= parts[0] << 24;
- break;
- case 3: /* a.b.c -- 8.8.16 bits */
- if (val > 0xffff)
- return (0);
- val |= (parts[0] << 24) | (parts[1] << 16);
- break;
- case 4: /* a.b.c.d -- 8.8.8.8 bits */
- if (val > 0xff)
- return (0);
- val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
- break;
- }
-
- if (addr)
- addr->s_addr = pj_htonl(val);
- return (1);
-}
-
-/*
- * Convert address string with numbers and dots to binary IP address.
- */
-PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
-{
- pj_in_addr addr;
- pj_inet_aton(cp, &addr);
- return addr;
-}
-
-/*
- * Set the IP address of an IP socket address from string address,
- * with resolving the host if necessary. The string address may be in a
- * standard numbers and dots notation or may be a hostname. If hostname
- * is specified, then the function will resolve the host into the IP
- * address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
- const pj_str_t *str_addr)
-{
- PJ_CHECK_STACK();
-
- pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME);
-
- addr->sin_family = AF_INET;
-
- if (str_addr && str_addr->slen) {
- addr->sin_addr = pj_inet_addr(str_addr);
- if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
- pj_hostent he;
- if (pj_gethostbyname(str_addr, &he) == 0) {
- addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
- } else {
- addr->sin_addr.s_addr = PJ_INADDR_NONE;
- return -1;
- }
- }
-
- } else {
- addr->sin_addr.s_addr = 0;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set the IP address and port of an IP socket address.
- * The string address may be in a standard numbers and dots notation or
- * may be a hostname. If hostname is specified, then the function will
- * resolve the host into the IP address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
- const pj_str_t *str_addr,
- pj_uint16_t port)
-{
- pj_assert(addr && str_addr);
-
- addr->sin_family = PJ_AF_INET;
- pj_sockaddr_in_set_port(addr, port);
- return pj_sockaddr_in_set_str_addr(addr, str_addr);
-}
-
-
-/*
- * Get hostname.
- */
-PJ_DEF(const pj_str_t*) pj_gethostname(void)
-{
- static char buf[PJ_MAX_HOSTNAME];
- static pj_str_t hostname;
-
- PJ_CHECK_STACK();
-
- if (hostname.ptr == NULL) {
- hostname.ptr = buf;
- down_read(&uts_sem);
- hostname.slen = strlen(system_utsname.nodename);
- if (hostname.slen > PJ_MAX_HOSTNAME) {
- hostname.ptr[0] = '\0';
- hostname.slen = 0;
- } else {
- pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen);
- }
- up_read(&uts_sem);
- }
- return &hostname;
-}
-
-/*
- * Get first IP address associated with the hostname.
- */
-PJ_DEF(pj_in_addr) pj_gethostaddr(void)
-{
- pj_sockaddr_in addr;
- const pj_str_t *hostname = pj_gethostname();
-
- pj_sockaddr_in_set_str_addr(&addr, hostname);
- return addr.sin_addr;
-}
-
-
-/*
- * Create new socket/endpoint for communication and returns a descriptor.
- */
-PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto,
- pj_sock_t *sock_fd)
-{
- long result;
-
- PJ_CHECK_STACK();
-
- /* Sanity checks. */
- PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL);
-
- /* Initialize returned socket */
- *sock_fd = PJ_INVALID_SOCKET;
-
- /* Create socket. */
- result = sys_socket(af, type, proto);
- if (result < 0) {
- return PJ_RETURN_OS_ERROR((-result));
- }
-
- *sock_fd = result;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd,
- const pj_sockaddr_t *addr,
- int len)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr),
- PJ_EINVAL);
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_bind(sockfd, (struct sockaddr*)addr, len);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
- pj_uint32_t addr32,
- pj_uint16_t port)
-{
- pj_sockaddr_in addr;
-
- PJ_CHECK_STACK();
-
- addr.sin_family = PJ_AF_INET;
- addr.sin_addr.s_addr = pj_htonl(addr32);
- addr.sin_port = pj_htons(port);
-
- return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in));
-}
-
-/*
- * Close socket.
- */
-PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd)
-{
- long err;
-
- err = sys_close(sockfd);
-
- if (err != 0)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get remote's name.
- */
-PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- mm_segment_t oldfs;
- long err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_getpeername( sockfd, addr, namelen);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get socket name.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- mm_segment_t oldfs;
- int err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_getsockname( sockfd, addr, namelen );
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Send data
- */
-PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0);
-}
-
-
-/*
- * Send data.
- */
-PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd,
- const void *buff,
- pj_ssize_t *len,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addr_len)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = *len = sys_sendto( sockfd, (void*)buff, *len, flags,
- (void*)addr, addr_len );
-
- set_fs(oldfs);
-
- if (err >= 0) {
- return PJ_SUCCESS;
- }
- else {
- return PJ_RETURN_OS_ERROR(-err);
- }
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd,
- void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL);
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
- void *buff,
- pj_ssize_t *size,
- unsigned flags,
- pj_sockaddr_t *from,
- int *fromlen)
-{
- mm_segment_t oldfs;
- long err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen);
-
- set_fs(oldfs);
-
- if (err >= 0) {
- return PJ_SUCCESS;
- }
- else {
- return PJ_RETURN_OS_ERROR(-err);
- }
-}
-
-/*
- * Get socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- void *optval,
- int *optlen)
-{
- mm_segment_t oldfs;
- long err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_getsockopt( sockfd, level, optname, optval, optlen);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Set socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- const void *optval,
- int optlen)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Shutdown socket.
- */
-#if PJ_HAS_TCP
-PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
- int how)
-{
- long err;
-
- PJ_CHECK_STACK();
-
- err = sys_shutdown(sockfd, how);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Start listening to incoming connections.
- */
-PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
- int backlog)
-{
- long err;
-
- PJ_CHECK_STACK();
-
- err = sys_listen( sockfd, backlog );
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Connect socket.
- */
-PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
- const pj_sockaddr_t *addr,
- int namelen)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_connect( sockfd, (void*)addr, namelen );
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Accept incoming connections
- */
-PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd,
- pj_sock_t *newsockfd,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- long err;
-
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL);
-
- err = sys_accept( sockfd, addr, addrlen);
-
- if (err < 0) {
- *newsockfd = PJ_INVALID_SOCKET;
- return PJ_RETURN_OS_ERROR(-err);
- }
- else {
- *newsockfd = err;
- return PJ_SUCCESS;
- }
-}
-#endif /* PJ_HAS_TCP */
-
-
-
-/*
- * Permission to steal inet_ntoa() and inet_aton() as long as this notice below
- * is included:
- */
-/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/sock.h> +#include <pj/assert.h> +#include <pj/string.h> /* pj_memcpy() */ +#include <pj/os.h> /* PJ_CHECK_STACK() */ +#include <pj/addr_resolv.h> /* pj_gethostbyname() */ +#include <pj/ctype.h> +#include <pj/compat/sprintf.h> +#include <pj/log.h> +#include <pj/errno.h> + +/* Linux kernel specific. */ +#include <linux/socket.h> +#include <linux/net.h> +//#include <net/sock.h> +#include <linux/security.h> +#include <linux/syscalls.h> /* sys_xxx() */ +#include <asm/ioctls.h> /* FIONBIO */ +#include <linux/utsname.h> /* for pj_gethostname() */ + +/* + * Address families conversion. + * The values here are indexed based on pj_addr_family-0xFF00. + */ +const pj_uint16_t PJ_AF_UNIX = AF_UNIX; +const pj_uint16_t PJ_AF_INET = AF_INET; +const pj_uint16_t PJ_AF_INET6 = AF_INET6; +#ifdef AF_PACKET +const pj_uint16_t PJ_AF_PACKET = AF_PACKET; +#else +# error "AF_PACKET undeclared!" +#endif +#ifdef AF_IRDA +const pj_uint16_t PJ_AF_IRDA = AF_IRDA; +#else +# error "AF_IRDA undeclared!" +#endif + +/* + * Socket types conversion. + * The values here are indexed based on pj_sock_type-0xFF00 + */ +const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM; +const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; +const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; +const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; + +/* + * Socket level values. + */ +const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; +#ifdef SOL_IP +const pj_uint16_t PJ_SOL_IP = SOL_IP; +#else +# error "SOL_IP undeclared!" +#endif /* SOL_IP */ +#if defined(SOL_TCP) +const pj_uint16_t PJ_SOL_TCP = SOL_TCP; +#else +# error "SOL_TCP undeclared!" +#endif /* SOL_TCP */ +#ifdef SOL_UDP +const pj_uint16_t PJ_SOL_UDP = SOL_UDP; +#else +# error "SOL_UDP undeclared!" +#endif +#ifdef SOL_IPV6 +const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; +#else +# error "SOL_IPV6 undeclared!" +#endif + +/* optname values. */ +const pj_uint16_t PJ_SO_TYPE = SO_TYPE; +const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; +const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; + +/* + * Convert 16-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) +{ + return ntohs(netshort); +} + +/* + * Convert 16-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) +{ + return htons(hostshort); +} + +/* + * Convert 32-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) +{ + return ntohl(netlong); +} + +/* + * Convert 32-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) +{ + return htonl(hostlong); +} + +/* + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + */ +PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in) +{ +#define UC(b) (((int)b)&0xff) + static char b[18]; + char *p; + + p = (char *)∈ + pj_snprintf(b, sizeof(b), "%d.%d.%d.%d", + UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); + + return b; +} + +/* + * This function converts the Internet host address ccp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + */ +PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr) +{ + pj_uint32_t val; + int base, n; + char c; + unsigned parts[4]; + unsigned *pp = parts; + char cp_copy[18]; + char *cp = cp_copy; + + addr->s_addr = PJ_INADDR_NONE; + + if (ccp->slen > 15) return 0; + + pj_memcpy(cp, ccp->ptr, ccp->slen); + cp[ccp->slen] = '\0'; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!pj_isdigit((int)c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + + for (;;) { + if (pj_isascii((int)c) && pj_isdigit((int)c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) { + val = (val << 4) | + (c + 10 - (pj_islower((int)c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + + /* + * Check for trailing characters. + */ + if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + case 0: + return (0); /* initial nondigit */ + case 1: /* a -- 32 bits */ + break; + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + + if (addr) + addr->s_addr = pj_htonl(val); + return (1); +} + +/* + * Convert address string with numbers and dots to binary IP address. + */ +PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) +{ + pj_in_addr addr; + pj_inet_aton(cp, &addr); + return addr; +} + +/* + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *str_addr) +{ + PJ_CHECK_STACK(); + + pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME); + + addr->sin_family = AF_INET; + + if (str_addr && str_addr->slen) { + addr->sin_addr = pj_inet_addr(str_addr); + if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { + pj_hostent he; + if (pj_gethostbyname(str_addr, &he) == 0) { + addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; + } else { + addr->sin_addr.s_addr = PJ_INADDR_NONE; + return -1; + } + } + + } else { + addr->sin_addr.s_addr = 0; + } + + return PJ_SUCCESS; +} + +/* + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *str_addr, + pj_uint16_t port) +{ + pj_assert(addr && str_addr); + + addr->sin_family = PJ_AF_INET; + pj_sockaddr_in_set_port(addr, port); + return pj_sockaddr_in_set_str_addr(addr, str_addr); +} + + +/* + * Get hostname. + */ +PJ_DEF(const pj_str_t*) pj_gethostname(void) +{ + static char buf[PJ_MAX_HOSTNAME]; + static pj_str_t hostname; + + PJ_CHECK_STACK(); + + if (hostname.ptr == NULL) { + hostname.ptr = buf; + down_read(&uts_sem); + hostname.slen = strlen(system_utsname.nodename); + if (hostname.slen > PJ_MAX_HOSTNAME) { + hostname.ptr[0] = '\0'; + hostname.slen = 0; + } else { + pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen); + } + up_read(&uts_sem); + } + return &hostname; +} + +/* + * Get first IP address associated with the hostname. + */ +PJ_DEF(pj_in_addr) pj_gethostaddr(void) +{ + pj_sockaddr_in addr; + const pj_str_t *hostname = pj_gethostname(); + + pj_sockaddr_in_set_str_addr(&addr, hostname); + return addr.sin_addr; +} + + +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, + pj_sock_t *sock_fd) +{ + long result; + + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL); + + /* Initialize returned socket */ + *sock_fd = PJ_INVALID_SOCKET; + + /* Create socket. */ + result = sys_socket(af, type, proto); + if (result < 0) { + return PJ_RETURN_OS_ERROR((-result)); + } + + *sock_fd = result; + + return PJ_SUCCESS; +} + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd, + const pj_sockaddr_t *addr, + int len) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr), + PJ_EINVAL); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_bind(sockfd, (struct sockaddr*)addr, len); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, + pj_uint32_t addr32, + pj_uint16_t port) +{ + pj_sockaddr_in addr; + + PJ_CHECK_STACK(); + + addr.sin_family = PJ_AF_INET; + addr.sin_addr.s_addr = pj_htonl(addr32); + addr.sin_port = pj_htons(port); + + return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in)); +} + +/* + * Close socket. + */ +PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd) +{ + long err; + + err = sys_close(sockfd); + + if (err != 0) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Get remote's name. + */ +PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getpeername( sockfd, addr, namelen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Get socket name. + */ +PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen) +{ + mm_segment_t oldfs; + int err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getsockname( sockfd, addr, namelen ); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Send data + */ +PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd, + const void *buf, + pj_ssize_t *len, + unsigned flags) +{ + return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0); +} + + +/* + * Send data. + */ +PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd, + const void *buff, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *addr, + int addr_len) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = *len = sys_sendto( sockfd, (void*)buff, *len, flags, + (void*)addr, addr_len ); + + set_fs(oldfs); + + if (err >= 0) { + return PJ_SUCCESS; + } + else { + return PJ_RETURN_OS_ERROR(-err); + } +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd, + void *buf, + pj_ssize_t *len, + unsigned flags) +{ + return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL); +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, + void *buff, + pj_ssize_t *size, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen); + + set_fs(oldfs); + + if (err >= 0) { + return PJ_SUCCESS; + } + else { + return PJ_RETURN_OS_ERROR(-err); + } +} + +/* + * Get socket option. + */ +PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, + pj_uint16_t level, + pj_uint16_t optname, + void *optval, + int *optlen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getsockopt( sockfd, level, optname, optval, optlen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Set socket option. + */ +PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, + pj_uint16_t level, + pj_uint16_t optname, + const void *optval, + int optlen) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Shutdown socket. + */ +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, + int how) +{ + long err; + + PJ_CHECK_STACK(); + + err = sys_shutdown(sockfd, how); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Start listening to incoming connections. + */ +PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd, + int backlog) +{ + long err; + + PJ_CHECK_STACK(); + + err = sys_listen( sockfd, backlog ); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Connect socket. + */ +PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd, + const pj_sockaddr_t *addr, + int namelen) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_connect( sockfd, (void*)addr, namelen ); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Accept incoming connections + */ +PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd, + pj_sock_t *newsockfd, + pj_sockaddr_t *addr, + int *addrlen) +{ + long err; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL); + + err = sys_accept( sockfd, addr, addrlen); + + if (err < 0) { + *newsockfd = PJ_INVALID_SOCKET; + return PJ_RETURN_OS_ERROR(-err); + } + else { + *newsockfd = err; + return PJ_SUCCESS; + } +} +#endif /* PJ_HAS_TCP */ + + + +/* + * Permission to steal inet_ntoa() and inet_aton() as long as this notice below + * is included: + */ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + diff --git a/pjlib/src/pj/sock_select.c b/pjlib/src/pj/sock_select.c index ba3d7998..c2d0fcf8 100644 --- a/pjlib/src/pj/sock_select.c +++ b/pjlib/src/pj/sock_select.c @@ -1,108 +1,108 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/sock_select.h>
-#include <pj/compat/socket.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0
-# include <string.h>
-#endif
-
-#ifdef _MSC_VER
-# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_*
-# pragma warning(disable: 4389) // Signed/unsigned mismatch in FD_*
-#endif
-
-#define PART_FDSET(ps) ((fd_set*)&ps->data[1])
-#define PART_FDSET_OR_NULL(ps) (ps ? PART_FDSET(ps) : NULL)
-#define PART_COUNT(ps) (ps->data[0])
-
-PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
-
- FD_ZERO(PART_FDSET(fdsetp));
- PART_COUNT(fdsetp) = 0;
-}
-
-
-PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
-
- if (!PJ_FD_ISSET(fd, fdsetp))
- ++PART_COUNT(fdsetp);
- FD_SET(fd, PART_FDSET(fdsetp));
-}
-
-
-PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
-
- if (PJ_FD_ISSET(fd, fdsetp))
- --PART_COUNT(fdsetp);
- FD_CLR(fd, PART_FDSET(fdsetp));
-}
-
-
-PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
- 0);
-
- return FD_ISSET(fd, PART_FDSET(fdsetp));
-}
-
-PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp)
-{
- return PART_COUNT(fdsetp);
-}
-
-PJ_DEF(int) pj_sock_select( int n,
- pj_fd_set_t *readfds,
- pj_fd_set_t *writefds,
- pj_fd_set_t *exceptfds,
- const pj_time_val *timeout)
-{
- struct timeval os_timeout, *p_os_timeout;
-
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
- PJ_EBUG);
-
- if (timeout) {
- os_timeout.tv_sec = timeout->sec;
- os_timeout.tv_usec = timeout->msec * 1000;
- p_os_timeout = &os_timeout;
- } else {
- p_os_timeout = NULL;
- }
-
- return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds),
- PART_FDSET_OR_NULL(exceptfds), p_os_timeout);
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/sock_select.h> +#include <pj/compat/socket.h> +#include <pj/os.h> +#include <pj/assert.h> +#include <pj/errno.h> + +#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0 +# include <string.h> +#endif + +#ifdef _MSC_VER +# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_* +# pragma warning(disable: 4389) // Signed/unsigned mismatch in FD_* +#endif + +#define PART_FDSET(ps) ((fd_set*)&ps->data[1]) +#define PART_FDSET_OR_NULL(ps) (ps ? PART_FDSET(ps) : NULL) +#define PART_COUNT(ps) (ps->data[0]) + +PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + FD_ZERO(PART_FDSET(fdsetp)); + PART_COUNT(fdsetp) = 0; +} + + +PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + if (!PJ_FD_ISSET(fd, fdsetp)) + ++PART_COUNT(fdsetp); + FD_SET(fd, PART_FDSET(fdsetp)); +} + + +PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + if (PJ_FD_ISSET(fd, fdsetp)) + --PART_COUNT(fdsetp); + FD_CLR(fd, PART_FDSET(fdsetp)); +} + + +PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), + 0); + + return FD_ISSET(fd, PART_FDSET(fdsetp)); +} + +PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp) +{ + return PART_COUNT(fdsetp); +} + +PJ_DEF(int) pj_sock_select( int n, + pj_fd_set_t *readfds, + pj_fd_set_t *writefds, + pj_fd_set_t *exceptfds, + const pj_time_val *timeout) +{ + struct timeval os_timeout, *p_os_timeout; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), + PJ_EBUG); + + if (timeout) { + os_timeout.tv_sec = timeout->sec; + os_timeout.tv_usec = timeout->msec * 1000; + p_os_timeout = &os_timeout; + } else { + p_os_timeout = NULL; + } + + return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds), + PART_FDSET_OR_NULL(exceptfds), p_os_timeout); +} + diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c index 5c152fb5..00728f1f 100644 --- a/pjlib/src/pj/string.c +++ b/pjlib/src/pj/string.c @@ -1,123 +1,123 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/ctype.h>
-#include <pj/rand.h>
-#include <pj/os.h>
-
-#if PJ_FUNCTIONS_ARE_INLINED==0
-# include <pj/string_i.h>
-#endif
-
-
-PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str )
-{
- register char *p = str->ptr;
- while (pj_isspace(*p))
- ++p;
- str->slen -= (p - str->ptr);
- str->ptr = p;
- return str;
-}
-
-PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str )
-{
- char *end = str->ptr + str->slen;
- register char *p = end - 1;
- while (p >= str->ptr && pj_isspace(*p))
- --p;
- str->slen -= ((end - p) - 1);
- return str;
-}
-
-PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len)
-{
- unsigned i;
- char *p = str;
-
- PJ_CHECK_STACK();
-
- for (i=0; i<len/8; ++i) {
- unsigned val = pj_rand();
- pj_val_to_hex_digit( (val & 0xFF000000) >> 24, p+0 );
- pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 );
- pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 );
- pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 );
- p += 8;
- }
- for (i=i * 8; i<len; ++i) {
- *p++ = pj_hex_digits[ pj_rand() & 0x0F ];
- }
- return str;
-}
-
-
-PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
-{
- unsigned long value;
- unsigned i;
-
- PJ_CHECK_STACK();
-
- value = 0;
- for (i=0; i<(unsigned)str->slen; ++i) {
- value = value * 10 + (str->ptr[i] - '0');
- }
- return value;
-}
-
-PJ_DEF(int) pj_utoa(unsigned long val, char *buf)
-{
- return pj_utoa_pad(val, buf, 0, 0);
-}
-
-PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad)
-{
- char *p;
- int len;
-
- PJ_CHECK_STACK();
-
- p = buf;
- do {
- unsigned long digval = (unsigned long) (val % 10);
- val /= 10;
- *p++ = (char) (digval + '0');
- } while (val > 0);
-
- len = p-buf;
- while (len < min_dig) {
- *p++ = (char)pad;
- ++len;
- }
- *p-- = '\0';
-
- do {
- char temp = *p;
- *p = *buf;
- *buf = temp;
- --p;
- ++buf;
- } while (buf < p);
-
- return len;
-}
-
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/string.h> +#include <pj/pool.h> +#include <pj/ctype.h> +#include <pj/rand.h> +#include <pj/os.h> + +#if PJ_FUNCTIONS_ARE_INLINED==0 +# include <pj/string_i.h> +#endif + + +PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str ) +{ + register char *p = str->ptr; + while (pj_isspace(*p)) + ++p; + str->slen -= (p - str->ptr); + str->ptr = p; + return str; +} + +PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str ) +{ + char *end = str->ptr + str->slen; + register char *p = end - 1; + while (p >= str->ptr && pj_isspace(*p)) + --p; + str->slen -= ((end - p) - 1); + return str; +} + +PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len) +{ + unsigned i; + char *p = str; + + PJ_CHECK_STACK(); + + for (i=0; i<len/8; ++i) { + unsigned val = pj_rand(); + pj_val_to_hex_digit( (val & 0xFF000000) >> 24, p+0 ); + pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 ); + pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 ); + pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 ); + p += 8; + } + for (i=i * 8; i<len; ++i) { + *p++ = pj_hex_digits[ pj_rand() & 0x0F ]; + } + return str; +} + + +PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str) +{ + unsigned long value; + unsigned i; + + PJ_CHECK_STACK(); + + value = 0; + for (i=0; i<(unsigned)str->slen; ++i) { + value = value * 10 + (str->ptr[i] - '0'); + } + return value; +} + +PJ_DEF(int) pj_utoa(unsigned long val, char *buf) +{ + return pj_utoa_pad(val, buf, 0, 0); +} + +PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad) +{ + char *p; + int len; + + PJ_CHECK_STACK(); + + p = buf; + do { + unsigned long digval = (unsigned long) (val % 10); + val /= 10; + *p++ = (char) (digval + '0'); + } while (val > 0); + + len = p-buf; + while (len < min_dig) { + *p++ = (char)pad; + ++len; + } + *p-- = '\0'; + + do { + char temp = *p; + *p = *buf; + *buf = temp; + --p; + ++buf; + } while (buf < p); + + return len; +} + + diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c index 4d2cbfb1..4a78557a 100644 --- a/pjlib/src/pj/symbols.c +++ b/pjlib/src/pj/symbols.c @@ -1,346 +1,346 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjlib.h>
-
-/*
- * addr_resolv.h
- */
-PJ_EXPORT_SYMBOL(pj_gethostbyname)
-
-/*
- * array.h
- */
-PJ_EXPORT_SYMBOL(pj_array_insert)
-PJ_EXPORT_SYMBOL(pj_array_erase)
-PJ_EXPORT_SYMBOL(pj_array_find)
-
-/*
- * config.h
- */
-PJ_EXPORT_SYMBOL(pj_dump_config)
-
-/*
- * errno.h
- */
-PJ_EXPORT_SYMBOL(pj_get_os_error)
-PJ_EXPORT_SYMBOL(pj_set_os_error)
-PJ_EXPORT_SYMBOL(pj_get_netos_error)
-PJ_EXPORT_SYMBOL(pj_set_netos_error)
-PJ_EXPORT_SYMBOL(pj_strerror)
-
-/*
- * except.h
- */
-PJ_EXPORT_SYMBOL(pj_throw_exception_)
-PJ_EXPORT_SYMBOL(pj_push_exception_handler_)
-PJ_EXPORT_SYMBOL(pj_pop_exception_handler_)
-PJ_EXPORT_SYMBOL(pj_setjmp)
-PJ_EXPORT_SYMBOL(pj_longjmp)
-PJ_EXPORT_SYMBOL(pj_exception_id_alloc)
-PJ_EXPORT_SYMBOL(pj_exception_id_free)
-PJ_EXPORT_SYMBOL(pj_exception_id_name)
-
-
-/*
- * fifobuf.h
- */
-PJ_EXPORT_SYMBOL(pj_fifobuf_init)
-PJ_EXPORT_SYMBOL(pj_fifobuf_max_size)
-PJ_EXPORT_SYMBOL(pj_fifobuf_alloc)
-PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc)
-PJ_EXPORT_SYMBOL(pj_fifobuf_free)
-
-/*
- * guid.h
- */
-PJ_EXPORT_SYMBOL(pj_generate_unique_string)
-PJ_EXPORT_SYMBOL(pj_create_unique_string)
-
-/*
- * hash.h
- */
-PJ_EXPORT_SYMBOL(pj_hash_calc)
-PJ_EXPORT_SYMBOL(pj_hash_create)
-PJ_EXPORT_SYMBOL(pj_hash_get)
-PJ_EXPORT_SYMBOL(pj_hash_set)
-PJ_EXPORT_SYMBOL(pj_hash_count)
-PJ_EXPORT_SYMBOL(pj_hash_first)
-PJ_EXPORT_SYMBOL(pj_hash_next)
-PJ_EXPORT_SYMBOL(pj_hash_this)
-
-/*
- * ioqueue.h
- */
-PJ_EXPORT_SYMBOL(pj_ioqueue_create)
-PJ_EXPORT_SYMBOL(pj_ioqueue_destroy)
-PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock)
-PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock)
-PJ_EXPORT_SYMBOL(pj_ioqueue_unregister)
-PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data)
-PJ_EXPORT_SYMBOL(pj_ioqueue_poll)
-PJ_EXPORT_SYMBOL(pj_ioqueue_read)
-PJ_EXPORT_SYMBOL(pj_ioqueue_recv)
-PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom)
-PJ_EXPORT_SYMBOL(pj_ioqueue_write)
-PJ_EXPORT_SYMBOL(pj_ioqueue_send)
-PJ_EXPORT_SYMBOL(pj_ioqueue_sendto)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
-PJ_EXPORT_SYMBOL(pj_ioqueue_accept)
-PJ_EXPORT_SYMBOL(pj_ioqueue_connect)
-#endif
-
-/*
- * list.h
- */
-PJ_EXPORT_SYMBOL(pj_list_insert_before)
-PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before)
-PJ_EXPORT_SYMBOL(pj_list_insert_after)
-PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after)
-PJ_EXPORT_SYMBOL(pj_list_merge_first)
-PJ_EXPORT_SYMBOL(pj_list_merge_last)
-PJ_EXPORT_SYMBOL(pj_list_erase)
-PJ_EXPORT_SYMBOL(pj_list_find_node)
-PJ_EXPORT_SYMBOL(pj_list_search)
-
-
-/*
- * log.h
- */
-PJ_EXPORT_SYMBOL(pj_log_write)
-#if PJ_LOG_MAX_LEVEL >= 1
-PJ_EXPORT_SYMBOL(pj_log_set_log_func)
-PJ_EXPORT_SYMBOL(pj_log_get_log_func)
-PJ_EXPORT_SYMBOL(pj_log_set_level)
-PJ_EXPORT_SYMBOL(pj_log_get_level)
-PJ_EXPORT_SYMBOL(pj_log_set_decor)
-PJ_EXPORT_SYMBOL(pj_log_get_decor)
-PJ_EXPORT_SYMBOL(pj_log_1)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 2
-PJ_EXPORT_SYMBOL(pj_log_2)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 3
-PJ_EXPORT_SYMBOL(pj_log_3)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 4
-PJ_EXPORT_SYMBOL(pj_log_4)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 5
-PJ_EXPORT_SYMBOL(pj_log_5)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 6
-PJ_EXPORT_SYMBOL(pj_log_6)
-#endif
-
-/*
- * os.h
- */
-PJ_EXPORT_SYMBOL(pj_init)
-PJ_EXPORT_SYMBOL(pj_getpid)
-PJ_EXPORT_SYMBOL(pj_thread_register)
-PJ_EXPORT_SYMBOL(pj_thread_create)
-PJ_EXPORT_SYMBOL(pj_thread_get_name)
-PJ_EXPORT_SYMBOL(pj_thread_resume)
-PJ_EXPORT_SYMBOL(pj_thread_this)
-PJ_EXPORT_SYMBOL(pj_thread_join)
-PJ_EXPORT_SYMBOL(pj_thread_destroy)
-PJ_EXPORT_SYMBOL(pj_thread_sleep)
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
-PJ_EXPORT_SYMBOL(pj_thread_check_stack)
-PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage)
-PJ_EXPORT_SYMBOL(pj_thread_get_stack_info)
-#endif
-PJ_EXPORT_SYMBOL(pj_atomic_create)
-PJ_EXPORT_SYMBOL(pj_atomic_destroy)
-PJ_EXPORT_SYMBOL(pj_atomic_set)
-PJ_EXPORT_SYMBOL(pj_atomic_get)
-PJ_EXPORT_SYMBOL(pj_atomic_inc)
-PJ_EXPORT_SYMBOL(pj_atomic_dec)
-PJ_EXPORT_SYMBOL(pj_thread_local_alloc)
-PJ_EXPORT_SYMBOL(pj_thread_local_free)
-PJ_EXPORT_SYMBOL(pj_thread_local_set)
-PJ_EXPORT_SYMBOL(pj_thread_local_get)
-PJ_EXPORT_SYMBOL(pj_enter_critical_section)
-PJ_EXPORT_SYMBOL(pj_leave_critical_section)
-PJ_EXPORT_SYMBOL(pj_mutex_create)
-PJ_EXPORT_SYMBOL(pj_mutex_lock)
-PJ_EXPORT_SYMBOL(pj_mutex_unlock)
-PJ_EXPORT_SYMBOL(pj_mutex_trylock)
-PJ_EXPORT_SYMBOL(pj_mutex_destroy)
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-PJ_EXPORT_SYMBOL(pj_mutex_is_locked)
-#endif
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-PJ_EXPORT_SYMBOL(pj_sem_create)
-PJ_EXPORT_SYMBOL(pj_sem_wait)
-PJ_EXPORT_SYMBOL(pj_sem_trywait)
-PJ_EXPORT_SYMBOL(pj_sem_post)
-PJ_EXPORT_SYMBOL(pj_sem_destroy)
-#endif
-PJ_EXPORT_SYMBOL(pj_gettimeofday)
-PJ_EXPORT_SYMBOL(pj_time_decode)
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
-PJ_EXPORT_SYMBOL(pj_get_timestamp)
-PJ_EXPORT_SYMBOL(pj_get_timestamp_freq)
-PJ_EXPORT_SYMBOL(pj_elapsed_time)
-PJ_EXPORT_SYMBOL(pj_elapsed_usec)
-PJ_EXPORT_SYMBOL(pj_elapsed_nanosec)
-PJ_EXPORT_SYMBOL(pj_elapsed_cycle)
-#endif
-
-
-/*
- * pool.h
- */
-PJ_EXPORT_SYMBOL(pj_pool_create)
-PJ_EXPORT_SYMBOL(pj_pool_release)
-PJ_EXPORT_SYMBOL(pj_pool_getobjname)
-PJ_EXPORT_SYMBOL(pj_pool_reset)
-PJ_EXPORT_SYMBOL(pj_pool_get_capacity)
-PJ_EXPORT_SYMBOL(pj_pool_get_used_size)
-PJ_EXPORT_SYMBOL(pj_pool_alloc)
-PJ_EXPORT_SYMBOL(pj_pool_calloc)
-PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy)
-PJ_EXPORT_SYMBOL(pj_pool_create_int)
-PJ_EXPORT_SYMBOL(pj_pool_init_int)
-PJ_EXPORT_SYMBOL(pj_pool_destroy_int)
-PJ_EXPORT_SYMBOL(pj_caching_pool_init)
-PJ_EXPORT_SYMBOL(pj_caching_pool_destroy)
-
-/*
- * rand.h
- */
-PJ_EXPORT_SYMBOL(pj_rand)
-PJ_EXPORT_SYMBOL(pj_srand)
-
-/*
- * rbtree.h
- */
-PJ_EXPORT_SYMBOL(pj_rbtree_init)
-PJ_EXPORT_SYMBOL(pj_rbtree_first)
-PJ_EXPORT_SYMBOL(pj_rbtree_last)
-PJ_EXPORT_SYMBOL(pj_rbtree_next)
-PJ_EXPORT_SYMBOL(pj_rbtree_prev)
-PJ_EXPORT_SYMBOL(pj_rbtree_insert)
-PJ_EXPORT_SYMBOL(pj_rbtree_find)
-PJ_EXPORT_SYMBOL(pj_rbtree_erase)
-PJ_EXPORT_SYMBOL(pj_rbtree_max_height)
-PJ_EXPORT_SYMBOL(pj_rbtree_min_height)
-
-/*
- * sock.h
- */
-PJ_EXPORT_SYMBOL(PJ_AF_UNIX)
-PJ_EXPORT_SYMBOL(PJ_AF_INET)
-PJ_EXPORT_SYMBOL(PJ_AF_INET6)
-PJ_EXPORT_SYMBOL(PJ_AF_PACKET)
-PJ_EXPORT_SYMBOL(PJ_AF_IRDA)
-PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM)
-PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM)
-PJ_EXPORT_SYMBOL(PJ_SOCK_RAW)
-PJ_EXPORT_SYMBOL(PJ_SOCK_RDM)
-PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET)
-PJ_EXPORT_SYMBOL(PJ_SOL_IP)
-PJ_EXPORT_SYMBOL(PJ_SOL_TCP)
-PJ_EXPORT_SYMBOL(PJ_SOL_UDP)
-PJ_EXPORT_SYMBOL(PJ_SOL_IPV6)
-PJ_EXPORT_SYMBOL(pj_ntohs)
-PJ_EXPORT_SYMBOL(pj_htons)
-PJ_EXPORT_SYMBOL(pj_ntohl)
-PJ_EXPORT_SYMBOL(pj_htonl)
-PJ_EXPORT_SYMBOL(pj_inet_ntoa)
-PJ_EXPORT_SYMBOL(pj_inet_aton)
-PJ_EXPORT_SYMBOL(pj_inet_addr)
-PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr)
-PJ_EXPORT_SYMBOL(pj_sockaddr_in_init)
-PJ_EXPORT_SYMBOL(pj_gethostname)
-PJ_EXPORT_SYMBOL(pj_gethostaddr)
-PJ_EXPORT_SYMBOL(pj_sock_socket)
-PJ_EXPORT_SYMBOL(pj_sock_close)
-PJ_EXPORT_SYMBOL(pj_sock_bind)
-PJ_EXPORT_SYMBOL(pj_sock_bind_in)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
-PJ_EXPORT_SYMBOL(pj_sock_listen)
-PJ_EXPORT_SYMBOL(pj_sock_accept)
-PJ_EXPORT_SYMBOL(pj_sock_shutdown)
-#endif
-PJ_EXPORT_SYMBOL(pj_sock_connect)
-PJ_EXPORT_SYMBOL(pj_sock_getpeername)
-PJ_EXPORT_SYMBOL(pj_sock_getsockname)
-PJ_EXPORT_SYMBOL(pj_sock_getsockopt)
-PJ_EXPORT_SYMBOL(pj_sock_setsockopt)
-PJ_EXPORT_SYMBOL(pj_sock_recv)
-PJ_EXPORT_SYMBOL(pj_sock_recvfrom)
-PJ_EXPORT_SYMBOL(pj_sock_send)
-PJ_EXPORT_SYMBOL(pj_sock_sendto)
-
-/*
- * sock_select.h
- */
-PJ_EXPORT_SYMBOL(PJ_FD_ZERO)
-PJ_EXPORT_SYMBOL(PJ_FD_SET)
-PJ_EXPORT_SYMBOL(PJ_FD_CLR)
-PJ_EXPORT_SYMBOL(PJ_FD_ISSET)
-PJ_EXPORT_SYMBOL(pj_sock_select)
-
-/*
- * string.h
- */
-PJ_EXPORT_SYMBOL(pj_str)
-PJ_EXPORT_SYMBOL(pj_strassign)
-PJ_EXPORT_SYMBOL(pj_strcpy)
-PJ_EXPORT_SYMBOL(pj_strcpy2)
-PJ_EXPORT_SYMBOL(pj_strdup)
-PJ_EXPORT_SYMBOL(pj_strdup_with_null)
-PJ_EXPORT_SYMBOL(pj_strdup2)
-PJ_EXPORT_SYMBOL(pj_strdup3)
-PJ_EXPORT_SYMBOL(pj_strcmp)
-PJ_EXPORT_SYMBOL(pj_strcmp2)
-PJ_EXPORT_SYMBOL(pj_strncmp)
-PJ_EXPORT_SYMBOL(pj_strncmp2)
-PJ_EXPORT_SYMBOL(pj_stricmp)
-PJ_EXPORT_SYMBOL(pj_stricmp2)
-PJ_EXPORT_SYMBOL(pj_strnicmp)
-PJ_EXPORT_SYMBOL(pj_strnicmp2)
-PJ_EXPORT_SYMBOL(pj_strcat)
-PJ_EXPORT_SYMBOL(pj_strltrim)
-PJ_EXPORT_SYMBOL(pj_strrtrim)
-PJ_EXPORT_SYMBOL(pj_strtrim)
-PJ_EXPORT_SYMBOL(pj_create_random_string)
-PJ_EXPORT_SYMBOL(pj_strtoul)
-PJ_EXPORT_SYMBOL(pj_utoa)
-PJ_EXPORT_SYMBOL(pj_utoa_pad)
-
-/*
- * timer.h
- */
-PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size)
-PJ_EXPORT_SYMBOL(pj_timer_heap_create)
-PJ_EXPORT_SYMBOL(pj_timer_entry_init)
-PJ_EXPORT_SYMBOL(pj_timer_heap_schedule)
-PJ_EXPORT_SYMBOL(pj_timer_heap_cancel)
-PJ_EXPORT_SYMBOL(pj_timer_heap_count)
-PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time)
-PJ_EXPORT_SYMBOL(pj_timer_heap_poll)
-
-/*
- * types.h
- */
-PJ_EXPORT_SYMBOL(pj_time_val_normalize)
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjlib.h> + +/* + * addr_resolv.h + */ +PJ_EXPORT_SYMBOL(pj_gethostbyname) + +/* + * array.h + */ +PJ_EXPORT_SYMBOL(pj_array_insert) +PJ_EXPORT_SYMBOL(pj_array_erase) +PJ_EXPORT_SYMBOL(pj_array_find) + +/* + * config.h + */ +PJ_EXPORT_SYMBOL(pj_dump_config) + +/* + * errno.h + */ +PJ_EXPORT_SYMBOL(pj_get_os_error) +PJ_EXPORT_SYMBOL(pj_set_os_error) +PJ_EXPORT_SYMBOL(pj_get_netos_error) +PJ_EXPORT_SYMBOL(pj_set_netos_error) +PJ_EXPORT_SYMBOL(pj_strerror) + +/* + * except.h + */ +PJ_EXPORT_SYMBOL(pj_throw_exception_) +PJ_EXPORT_SYMBOL(pj_push_exception_handler_) +PJ_EXPORT_SYMBOL(pj_pop_exception_handler_) +PJ_EXPORT_SYMBOL(pj_setjmp) +PJ_EXPORT_SYMBOL(pj_longjmp) +PJ_EXPORT_SYMBOL(pj_exception_id_alloc) +PJ_EXPORT_SYMBOL(pj_exception_id_free) +PJ_EXPORT_SYMBOL(pj_exception_id_name) + + +/* + * fifobuf.h + */ +PJ_EXPORT_SYMBOL(pj_fifobuf_init) +PJ_EXPORT_SYMBOL(pj_fifobuf_max_size) +PJ_EXPORT_SYMBOL(pj_fifobuf_alloc) +PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc) +PJ_EXPORT_SYMBOL(pj_fifobuf_free) + +/* + * guid.h + */ +PJ_EXPORT_SYMBOL(pj_generate_unique_string) +PJ_EXPORT_SYMBOL(pj_create_unique_string) + +/* + * hash.h + */ +PJ_EXPORT_SYMBOL(pj_hash_calc) +PJ_EXPORT_SYMBOL(pj_hash_create) +PJ_EXPORT_SYMBOL(pj_hash_get) +PJ_EXPORT_SYMBOL(pj_hash_set) +PJ_EXPORT_SYMBOL(pj_hash_count) +PJ_EXPORT_SYMBOL(pj_hash_first) +PJ_EXPORT_SYMBOL(pj_hash_next) +PJ_EXPORT_SYMBOL(pj_hash_this) + +/* + * ioqueue.h + */ +PJ_EXPORT_SYMBOL(pj_ioqueue_create) +PJ_EXPORT_SYMBOL(pj_ioqueue_destroy) +PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock) +PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock) +PJ_EXPORT_SYMBOL(pj_ioqueue_unregister) +PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data) +PJ_EXPORT_SYMBOL(pj_ioqueue_poll) +PJ_EXPORT_SYMBOL(pj_ioqueue_read) +PJ_EXPORT_SYMBOL(pj_ioqueue_recv) +PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom) +PJ_EXPORT_SYMBOL(pj_ioqueue_write) +PJ_EXPORT_SYMBOL(pj_ioqueue_send) +PJ_EXPORT_SYMBOL(pj_ioqueue_sendto) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +PJ_EXPORT_SYMBOL(pj_ioqueue_accept) +PJ_EXPORT_SYMBOL(pj_ioqueue_connect) +#endif + +/* + * list.h + */ +PJ_EXPORT_SYMBOL(pj_list_insert_before) +PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before) +PJ_EXPORT_SYMBOL(pj_list_insert_after) +PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after) +PJ_EXPORT_SYMBOL(pj_list_merge_first) +PJ_EXPORT_SYMBOL(pj_list_merge_last) +PJ_EXPORT_SYMBOL(pj_list_erase) +PJ_EXPORT_SYMBOL(pj_list_find_node) +PJ_EXPORT_SYMBOL(pj_list_search) + + +/* + * log.h + */ +PJ_EXPORT_SYMBOL(pj_log_write) +#if PJ_LOG_MAX_LEVEL >= 1 +PJ_EXPORT_SYMBOL(pj_log_set_log_func) +PJ_EXPORT_SYMBOL(pj_log_get_log_func) +PJ_EXPORT_SYMBOL(pj_log_set_level) +PJ_EXPORT_SYMBOL(pj_log_get_level) +PJ_EXPORT_SYMBOL(pj_log_set_decor) +PJ_EXPORT_SYMBOL(pj_log_get_decor) +PJ_EXPORT_SYMBOL(pj_log_1) +#endif +#if PJ_LOG_MAX_LEVEL >= 2 +PJ_EXPORT_SYMBOL(pj_log_2) +#endif +#if PJ_LOG_MAX_LEVEL >= 3 +PJ_EXPORT_SYMBOL(pj_log_3) +#endif +#if PJ_LOG_MAX_LEVEL >= 4 +PJ_EXPORT_SYMBOL(pj_log_4) +#endif +#if PJ_LOG_MAX_LEVEL >= 5 +PJ_EXPORT_SYMBOL(pj_log_5) +#endif +#if PJ_LOG_MAX_LEVEL >= 6 +PJ_EXPORT_SYMBOL(pj_log_6) +#endif + +/* + * os.h + */ +PJ_EXPORT_SYMBOL(pj_init) +PJ_EXPORT_SYMBOL(pj_getpid) +PJ_EXPORT_SYMBOL(pj_thread_register) +PJ_EXPORT_SYMBOL(pj_thread_create) +PJ_EXPORT_SYMBOL(pj_thread_get_name) +PJ_EXPORT_SYMBOL(pj_thread_resume) +PJ_EXPORT_SYMBOL(pj_thread_this) +PJ_EXPORT_SYMBOL(pj_thread_join) +PJ_EXPORT_SYMBOL(pj_thread_destroy) +PJ_EXPORT_SYMBOL(pj_thread_sleep) +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 +PJ_EXPORT_SYMBOL(pj_thread_check_stack) +PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage) +PJ_EXPORT_SYMBOL(pj_thread_get_stack_info) +#endif +PJ_EXPORT_SYMBOL(pj_atomic_create) +PJ_EXPORT_SYMBOL(pj_atomic_destroy) +PJ_EXPORT_SYMBOL(pj_atomic_set) +PJ_EXPORT_SYMBOL(pj_atomic_get) +PJ_EXPORT_SYMBOL(pj_atomic_inc) +PJ_EXPORT_SYMBOL(pj_atomic_dec) +PJ_EXPORT_SYMBOL(pj_thread_local_alloc) +PJ_EXPORT_SYMBOL(pj_thread_local_free) +PJ_EXPORT_SYMBOL(pj_thread_local_set) +PJ_EXPORT_SYMBOL(pj_thread_local_get) +PJ_EXPORT_SYMBOL(pj_enter_critical_section) +PJ_EXPORT_SYMBOL(pj_leave_critical_section) +PJ_EXPORT_SYMBOL(pj_mutex_create) +PJ_EXPORT_SYMBOL(pj_mutex_lock) +PJ_EXPORT_SYMBOL(pj_mutex_unlock) +PJ_EXPORT_SYMBOL(pj_mutex_trylock) +PJ_EXPORT_SYMBOL(pj_mutex_destroy) +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +PJ_EXPORT_SYMBOL(pj_mutex_is_locked) +#endif +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +PJ_EXPORT_SYMBOL(pj_sem_create) +PJ_EXPORT_SYMBOL(pj_sem_wait) +PJ_EXPORT_SYMBOL(pj_sem_trywait) +PJ_EXPORT_SYMBOL(pj_sem_post) +PJ_EXPORT_SYMBOL(pj_sem_destroy) +#endif +PJ_EXPORT_SYMBOL(pj_gettimeofday) +PJ_EXPORT_SYMBOL(pj_time_decode) +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 +PJ_EXPORT_SYMBOL(pj_get_timestamp) +PJ_EXPORT_SYMBOL(pj_get_timestamp_freq) +PJ_EXPORT_SYMBOL(pj_elapsed_time) +PJ_EXPORT_SYMBOL(pj_elapsed_usec) +PJ_EXPORT_SYMBOL(pj_elapsed_nanosec) +PJ_EXPORT_SYMBOL(pj_elapsed_cycle) +#endif + + +/* + * pool.h + */ +PJ_EXPORT_SYMBOL(pj_pool_create) +PJ_EXPORT_SYMBOL(pj_pool_release) +PJ_EXPORT_SYMBOL(pj_pool_getobjname) +PJ_EXPORT_SYMBOL(pj_pool_reset) +PJ_EXPORT_SYMBOL(pj_pool_get_capacity) +PJ_EXPORT_SYMBOL(pj_pool_get_used_size) +PJ_EXPORT_SYMBOL(pj_pool_alloc) +PJ_EXPORT_SYMBOL(pj_pool_calloc) +PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy) +PJ_EXPORT_SYMBOL(pj_pool_create_int) +PJ_EXPORT_SYMBOL(pj_pool_init_int) +PJ_EXPORT_SYMBOL(pj_pool_destroy_int) +PJ_EXPORT_SYMBOL(pj_caching_pool_init) +PJ_EXPORT_SYMBOL(pj_caching_pool_destroy) + +/* + * rand.h + */ +PJ_EXPORT_SYMBOL(pj_rand) +PJ_EXPORT_SYMBOL(pj_srand) + +/* + * rbtree.h + */ +PJ_EXPORT_SYMBOL(pj_rbtree_init) +PJ_EXPORT_SYMBOL(pj_rbtree_first) +PJ_EXPORT_SYMBOL(pj_rbtree_last) +PJ_EXPORT_SYMBOL(pj_rbtree_next) +PJ_EXPORT_SYMBOL(pj_rbtree_prev) +PJ_EXPORT_SYMBOL(pj_rbtree_insert) +PJ_EXPORT_SYMBOL(pj_rbtree_find) +PJ_EXPORT_SYMBOL(pj_rbtree_erase) +PJ_EXPORT_SYMBOL(pj_rbtree_max_height) +PJ_EXPORT_SYMBOL(pj_rbtree_min_height) + +/* + * sock.h + */ +PJ_EXPORT_SYMBOL(PJ_AF_UNIX) +PJ_EXPORT_SYMBOL(PJ_AF_INET) +PJ_EXPORT_SYMBOL(PJ_AF_INET6) +PJ_EXPORT_SYMBOL(PJ_AF_PACKET) +PJ_EXPORT_SYMBOL(PJ_AF_IRDA) +PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM) +PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM) +PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) +PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) +PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) +PJ_EXPORT_SYMBOL(PJ_SOL_IP) +PJ_EXPORT_SYMBOL(PJ_SOL_TCP) +PJ_EXPORT_SYMBOL(PJ_SOL_UDP) +PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) +PJ_EXPORT_SYMBOL(pj_ntohs) +PJ_EXPORT_SYMBOL(pj_htons) +PJ_EXPORT_SYMBOL(pj_ntohl) +PJ_EXPORT_SYMBOL(pj_htonl) +PJ_EXPORT_SYMBOL(pj_inet_ntoa) +PJ_EXPORT_SYMBOL(pj_inet_aton) +PJ_EXPORT_SYMBOL(pj_inet_addr) +PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr) +PJ_EXPORT_SYMBOL(pj_sockaddr_in_init) +PJ_EXPORT_SYMBOL(pj_gethostname) +PJ_EXPORT_SYMBOL(pj_gethostaddr) +PJ_EXPORT_SYMBOL(pj_sock_socket) +PJ_EXPORT_SYMBOL(pj_sock_close) +PJ_EXPORT_SYMBOL(pj_sock_bind) +PJ_EXPORT_SYMBOL(pj_sock_bind_in) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +PJ_EXPORT_SYMBOL(pj_sock_listen) +PJ_EXPORT_SYMBOL(pj_sock_accept) +PJ_EXPORT_SYMBOL(pj_sock_shutdown) +#endif +PJ_EXPORT_SYMBOL(pj_sock_connect) +PJ_EXPORT_SYMBOL(pj_sock_getpeername) +PJ_EXPORT_SYMBOL(pj_sock_getsockname) +PJ_EXPORT_SYMBOL(pj_sock_getsockopt) +PJ_EXPORT_SYMBOL(pj_sock_setsockopt) +PJ_EXPORT_SYMBOL(pj_sock_recv) +PJ_EXPORT_SYMBOL(pj_sock_recvfrom) +PJ_EXPORT_SYMBOL(pj_sock_send) +PJ_EXPORT_SYMBOL(pj_sock_sendto) + +/* + * sock_select.h + */ +PJ_EXPORT_SYMBOL(PJ_FD_ZERO) +PJ_EXPORT_SYMBOL(PJ_FD_SET) +PJ_EXPORT_SYMBOL(PJ_FD_CLR) +PJ_EXPORT_SYMBOL(PJ_FD_ISSET) +PJ_EXPORT_SYMBOL(pj_sock_select) + +/* + * string.h + */ +PJ_EXPORT_SYMBOL(pj_str) +PJ_EXPORT_SYMBOL(pj_strassign) +PJ_EXPORT_SYMBOL(pj_strcpy) +PJ_EXPORT_SYMBOL(pj_strcpy2) +PJ_EXPORT_SYMBOL(pj_strdup) +PJ_EXPORT_SYMBOL(pj_strdup_with_null) +PJ_EXPORT_SYMBOL(pj_strdup2) +PJ_EXPORT_SYMBOL(pj_strdup3) +PJ_EXPORT_SYMBOL(pj_strcmp) +PJ_EXPORT_SYMBOL(pj_strcmp2) +PJ_EXPORT_SYMBOL(pj_strncmp) +PJ_EXPORT_SYMBOL(pj_strncmp2) +PJ_EXPORT_SYMBOL(pj_stricmp) +PJ_EXPORT_SYMBOL(pj_stricmp2) +PJ_EXPORT_SYMBOL(pj_strnicmp) +PJ_EXPORT_SYMBOL(pj_strnicmp2) +PJ_EXPORT_SYMBOL(pj_strcat) +PJ_EXPORT_SYMBOL(pj_strltrim) +PJ_EXPORT_SYMBOL(pj_strrtrim) +PJ_EXPORT_SYMBOL(pj_strtrim) +PJ_EXPORT_SYMBOL(pj_create_random_string) +PJ_EXPORT_SYMBOL(pj_strtoul) +PJ_EXPORT_SYMBOL(pj_utoa) +PJ_EXPORT_SYMBOL(pj_utoa_pad) + +/* + * timer.h + */ +PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size) +PJ_EXPORT_SYMBOL(pj_timer_heap_create) +PJ_EXPORT_SYMBOL(pj_timer_entry_init) +PJ_EXPORT_SYMBOL(pj_timer_heap_schedule) +PJ_EXPORT_SYMBOL(pj_timer_heap_cancel) +PJ_EXPORT_SYMBOL(pj_timer_heap_count) +PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time) +PJ_EXPORT_SYMBOL(pj_timer_heap_poll) + +/* + * types.h + */ +PJ_EXPORT_SYMBOL(pj_time_val_normalize) + diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c index dfcccbfa..212f04a8 100644 --- a/pjlib/src/pj/timer.c +++ b/pjlib/src/pj/timer.c @@ -1,531 +1,531 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/timer.h>
-#include <pj/pool.h>
-#include <pj/os.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <pj/lock.h>
-
-#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2))
-#define HEAP_LEFT(X) (((X)+(X))+1)
-
-
-#define DEFAULT_MAX_TIMED_OUT_PER_POLL (64)
-
-
-/**
- * The implementation of timer heap.
- */
-struct pj_timer_heap_t
-{
- /** Pool from which the timer heap resize will get the storage from */
- pj_pool_t *pool;
-
- /** Maximum size of the heap. */
- pj_size_t max_size;
-
- /** Current size of the heap. */
- pj_size_t cur_size;
-
- /** Max timed out entries to process per poll. */
- unsigned max_entries_per_poll;
-
- /** Lock object. */
- pj_lock_t *lock;
-
- /** Autodelete lock. */
- pj_bool_t auto_delete_lock;
-
- /**
- * Current contents of the Heap, which is organized as a "heap" of
- * pj_timer_entry *'s. In this context, a heap is a "partially
- * ordered, almost complete" binary tree, which is stored in an
- * array.
- */
- pj_timer_entry **heap;
-
- /**
- * An array of "pointers" that allows each pj_timer_entry in the
- * <heap_> to be located in O(1) time. Basically, <timer_id_[i]>
- * contains the slot in the <heap_> array where an pj_timer_entry
- * with timer id <i> resides. Thus, the timer id passed back from
- * <schedule_entry> is really an slot into the <timer_ids> array. The
- * <timer_ids_> array serves two purposes: negative values are
- * treated as "pointers" for the <freelist_>, whereas positive
- * values are treated as "pointers" into the <heap_> array.
- */
- pj_timer_id_t *timer_ids;
-
- /**
- * "Pointer" to the first element in the freelist contained within
- * the <timer_ids_> array, which is organized as a stack.
- */
- pj_timer_id_t timer_ids_freelist;
-
- /** Callback to be called when a timer expires. */
- pj_timer_heap_callback *callback;
-
-};
-
-
-
-PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht )
-{
- if (ht->lock) {
- pj_lock_acquire(ht->lock);
- }
-}
-
-PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
-{
- if (ht->lock) {
- pj_lock_release(ht->lock);
- }
-}
-
-
-static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node )
-{
- PJ_CHECK_STACK();
-
- // Insert <moved_node> into its new location in the heap.
- ht->heap[slot] = moved_node;
-
- // Update the corresponding slot in the parallel <timer_ids_> array.
- ht->timer_ids[moved_node->_timer_id] = slot;
-}
-
-static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )
-{
- // We need to truncate this to <int> for backwards compatibility.
- pj_timer_id_t new_id = ht->timer_ids_freelist;
-
- PJ_CHECK_STACK();
-
- // The freelist values in the <timer_ids_> are negative, so we need
- // to negate them to get the next freelist "pointer."
- ht->timer_ids_freelist =
- -ht->timer_ids[ht->timer_ids_freelist];
-
- return new_id;
-
-}
-
-static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)
-{
- PJ_CHECK_STACK();
-
- // The freelist values in the <timer_ids_> are negative, so we need
- // to negate them to get the next freelist "pointer."
- ht->timer_ids[old_id] = -ht->timer_ids_freelist;
- ht->timer_ids_freelist = old_id;
-}
-
-
-static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,
- size_t slot, size_t child)
-{
- PJ_CHECK_STACK();
-
- // Restore the heap property after a deletion.
-
- while (child < ht->cur_size)
- {
- // Choose the smaller of the two children.
- if (child + 1 < ht->cur_size
- && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))
- child++;
-
- // Perform a <copy> if the child has a larger timeout value than
- // the <moved_node>.
- if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))
- {
- copy_node( ht, slot, ht->heap[child]);
- slot = child;
- child = HEAP_LEFT(child);
- }
- else
- // We've found our location in the heap.
- break;
- }
-
- copy_node( ht, slot, moved_node);
-}
-
-static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,
- size_t slot, size_t parent)
-{
- // Restore the heap property after an insertion.
-
- while (slot > 0)
- {
- // If the parent node is greater than the <moved_node> we need
- // to copy it down.
- if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))
- {
- copy_node(ht, slot, ht->heap[parent]);
- slot = parent;
- parent = HEAP_PARENT(slot);
- }
- else
- break;
- }
-
- // Insert the new node into its proper resting place in the heap and
- // update the corresponding slot in the parallel <timer_ids> array.
- copy_node(ht, slot, moved_node);
-}
-
-
-static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)
-{
- pj_timer_entry *removed_node = ht->heap[slot];
-
- // Return this timer id to the freelist.
- push_freelist( ht, removed_node->_timer_id );
-
- // Decrement the size of the heap by one since we're removing the
- // "slot"th node.
- ht->cur_size--;
-
- // Set the ID
- removed_node->_timer_id = -1;
-
- // Only try to reheapify if we're not deleting the last entry.
-
- if (slot < ht->cur_size)
- {
- int parent;
- pj_timer_entry *moved_node = ht->heap[ht->cur_size];
-
- // Move the end node to the location being removed and update
- // the corresponding slot in the parallel <timer_ids> array.
- copy_node( ht, slot, moved_node);
-
- // If the <moved_node->time_value_> is great than or equal its
- // parent it needs be moved down the heap.
- parent = HEAP_PARENT (slot);
-
- if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value))
- reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));
- else
- reheap_up( ht, moved_node, slot, parent);
- }
-
- return removed_node;
-}
-
-static void grow_heap(pj_timer_heap_t *ht)
-{
- // All the containers will double in size from max_size_
- size_t new_size = ht->max_size * 2;
- pj_timer_id_t *new_timer_ids;
- pj_size_t i;
-
- // First grow the heap itself.
-
- pj_timer_entry **new_heap = 0;
-
- new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size);
- memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*));
- //delete [] this->heap_;
- ht->heap = new_heap;
-
- // Grow the array of timer ids.
-
- new_timer_ids = 0;
- new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));
-
- memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));
-
- //delete [] timer_ids_;
- ht->timer_ids = new_timer_ids;
-
- // And add the new elements to the end of the "freelist".
- for (i = ht->max_size; i < new_size; i++)
- ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
-
- ht->max_size = new_size;
-}
-
-static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node)
-{
- if (ht->cur_size + 2 >= ht->max_size)
- grow_heap(ht);
-
- reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size));
- ht->cur_size++;
-}
-
-
-static pj_status_t schedule_entry( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *future_time )
-{
- if (ht->cur_size < ht->max_size)
- {
- // Obtain the next unique sequence number.
- // Set the entry
- entry->_timer_id = pop_freelist(ht);
- entry->_timer_value = *future_time;
- insert_node( ht, entry);
- return 0;
- }
- else
- return -1;
-}
-
-
-static int cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- int dont_call)
-{
- long timer_node_slot;
-
- PJ_CHECK_STACK();
-
- // Check to see if the timer_id is out of range
- if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size)
- return 0;
-
- timer_node_slot = ht->timer_ids[entry->_timer_id];
-
- if (timer_node_slot < 0) // Check to see if timer_id is still valid.
- return 0;
-
- if (entry != ht->heap[timer_node_slot])
- {
- pj_assert(entry == ht->heap[timer_node_slot]);
- return 0;
- }
- else
- {
- remove_node( ht, timer_node_slot);
-
- if (dont_call == 0)
- // Call the close hook.
- (*ht->callback)(ht, entry);
- return 1;
- }
-}
-
-
-/*
- * Calculate memory size required to create a timer heap.
- */
-PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
-{
- return /* size of the timer heap itself: */
- sizeof(pj_timer_heap_t) +
- /* size of each entry: */
- (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) +
- /* lock, pool etc: */
- 132;
-}
-
-/*
- * Create a new timer heap.
- */
-PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
- pj_size_t size,
- pj_timer_heap_t **p_heap)
-{
- pj_timer_heap_t *ht;
- pj_size_t i;
-
- PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
-
- *p_heap = NULL;
-
- /* Magic? */
- size += 2;
-
- /* Allocate timer heap data structure from the pool */
- ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t));
- if (!ht)
- return PJ_ENOMEM;
-
- /* Initialize timer heap sizes */
- ht->max_size = size;
- ht->cur_size = 0;
- ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL;
- ht->timer_ids_freelist = 1;
- ht->pool = pool;
-
- /* Lock. */
- ht->lock = NULL;
- ht->auto_delete_lock = 0;
-
- // Create the heap array.
- ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size);
- if (!ht->heap)
- return PJ_ENOMEM;
-
- // Create the parallel
- ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);
- if (!ht->timer_ids)
- return PJ_ENOMEM;
-
- // Initialize the "freelist," which uses negative values to
- // distinguish freelist elements from "pointers" into the <heap_>
- // array.
- for (i=0; i<size; ++i)
- ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
-
- *p_heap = ht;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
-{
- if (ht->lock && ht->auto_delete_lock) {
- pj_lock_destroy(ht->lock);
- ht->lock = NULL;
- }
-}
-
-PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht,
- pj_lock_t *lock,
- pj_bool_t auto_del )
-{
- if (ht->lock && ht->auto_delete_lock)
- pj_lock_destroy(ht->lock);
-
- ht->lock = lock;
- ht->auto_delete_lock = auto_del;
-}
-
-
-PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
- unsigned count )
-{
- unsigned old_count = ht->max_entries_per_poll;
- ht->max_entries_per_poll = count;
- return old_count;
-}
-
-PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
- int id,
- void *user_data,
- pj_timer_heap_callback *cb )
-{
- pj_assert(entry && cb);
-
- entry->id = id;
- entry->user_data = user_data;
- entry->cb = cb;
-
- return entry;
-}
-
-PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *delay)
-{
- pj_status_t status;
- pj_time_val expires;
-
- PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
-
- pj_gettimeofday(&expires);
- PJ_TIME_VAL_ADD(expires, *delay);
-
- lock_timer_heap(ht);
- status = schedule_entry(ht, entry, &expires);
- unlock_timer_heap(ht);
-
- return status;
-}
-
-PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry)
-{
- int count;
-
- PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
-
- lock_timer_heap(ht);
- count = cancel(ht, entry, 1);
- unlock_timer_heap(ht);
-
- return count;
-}
-
-PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
- pj_time_val *next_delay )
-{
- pj_time_val now;
- unsigned count;
-
- PJ_ASSERT_RETURN(ht, 0);
-
- if (!ht->cur_size && next_delay) {
- next_delay->sec = next_delay->msec = PJ_MAXINT32;
- return 0;
- }
-
- count = 0;
- pj_gettimeofday(&now);
-
- lock_timer_heap(ht);
- while ( ht->cur_size &&
- PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) &&
- count < ht->max_entries_per_poll )
- {
- pj_timer_entry *node = remove_node(ht, 0);
- ++count;
-
- unlock_timer_heap(ht);
- (*node->cb)(ht, node);
- lock_timer_heap(ht);
- }
- if (ht->cur_size && next_delay) {
- *next_delay = ht->heap[0]->_timer_value;
- PJ_TIME_VAL_SUB(*next_delay, now);
- } else if (next_delay) {
- next_delay->sec = next_delay->msec = PJ_MAXINT32;
- }
- unlock_timer_heap(ht);
-
- return count;
-}
-
-PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
-{
- PJ_ASSERT_RETURN(ht, 0);
-
- return ht->cur_size;
-}
-
-PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
- pj_time_val *timeval)
-{
- pj_assert(ht->cur_size != 0);
- if (ht->cur_size == 0)
- return PJ_ENOTFOUND;
-
- lock_timer_heap(ht);
- *timeval = ht->heap[0]->_timer_value;
- unlock_timer_heap(ht);
-
- return PJ_SUCCESS;
-}
-
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/timer.h> +#include <pj/pool.h> +#include <pj/os.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <pj/lock.h> + +#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2)) +#define HEAP_LEFT(X) (((X)+(X))+1) + + +#define DEFAULT_MAX_TIMED_OUT_PER_POLL (64) + + +/** + * The implementation of timer heap. + */ +struct pj_timer_heap_t +{ + /** Pool from which the timer heap resize will get the storage from */ + pj_pool_t *pool; + + /** Maximum size of the heap. */ + pj_size_t max_size; + + /** Current size of the heap. */ + pj_size_t cur_size; + + /** Max timed out entries to process per poll. */ + unsigned max_entries_per_poll; + + /** Lock object. */ + pj_lock_t *lock; + + /** Autodelete lock. */ + pj_bool_t auto_delete_lock; + + /** + * Current contents of the Heap, which is organized as a "heap" of + * pj_timer_entry *'s. In this context, a heap is a "partially + * ordered, almost complete" binary tree, which is stored in an + * array. + */ + pj_timer_entry **heap; + + /** + * An array of "pointers" that allows each pj_timer_entry in the + * <heap_> to be located in O(1) time. Basically, <timer_id_[i]> + * contains the slot in the <heap_> array where an pj_timer_entry + * with timer id <i> resides. Thus, the timer id passed back from + * <schedule_entry> is really an slot into the <timer_ids> array. The + * <timer_ids_> array serves two purposes: negative values are + * treated as "pointers" for the <freelist_>, whereas positive + * values are treated as "pointers" into the <heap_> array. + */ + pj_timer_id_t *timer_ids; + + /** + * "Pointer" to the first element in the freelist contained within + * the <timer_ids_> array, which is organized as a stack. + */ + pj_timer_id_t timer_ids_freelist; + + /** Callback to be called when a timer expires. */ + pj_timer_heap_callback *callback; + +}; + + + +PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht ) +{ + if (ht->lock) { + pj_lock_acquire(ht->lock); + } +} + +PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht ) +{ + if (ht->lock) { + pj_lock_release(ht->lock); + } +} + + +static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node ) +{ + PJ_CHECK_STACK(); + + // Insert <moved_node> into its new location in the heap. + ht->heap[slot] = moved_node; + + // Update the corresponding slot in the parallel <timer_ids_> array. + ht->timer_ids[moved_node->_timer_id] = slot; +} + +static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht ) +{ + // We need to truncate this to <int> for backwards compatibility. + pj_timer_id_t new_id = ht->timer_ids_freelist; + + PJ_CHECK_STACK(); + + // The freelist values in the <timer_ids_> are negative, so we need + // to negate them to get the next freelist "pointer." + ht->timer_ids_freelist = + -ht->timer_ids[ht->timer_ids_freelist]; + + return new_id; + +} + +static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id) +{ + PJ_CHECK_STACK(); + + // The freelist values in the <timer_ids_> are negative, so we need + // to negate them to get the next freelist "pointer." + ht->timer_ids[old_id] = -ht->timer_ids_freelist; + ht->timer_ids_freelist = old_id; +} + + +static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node, + size_t slot, size_t child) +{ + PJ_CHECK_STACK(); + + // Restore the heap property after a deletion. + + while (child < ht->cur_size) + { + // Choose the smaller of the two children. + if (child + 1 < ht->cur_size + && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value)) + child++; + + // Perform a <copy> if the child has a larger timeout value than + // the <moved_node>. + if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value)) + { + copy_node( ht, slot, ht->heap[child]); + slot = child; + child = HEAP_LEFT(child); + } + else + // We've found our location in the heap. + break; + } + + copy_node( ht, slot, moved_node); +} + +static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node, + size_t slot, size_t parent) +{ + // Restore the heap property after an insertion. + + while (slot > 0) + { + // If the parent node is greater than the <moved_node> we need + // to copy it down. + if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value)) + { + copy_node(ht, slot, ht->heap[parent]); + slot = parent; + parent = HEAP_PARENT(slot); + } + else + break; + } + + // Insert the new node into its proper resting place in the heap and + // update the corresponding slot in the parallel <timer_ids> array. + copy_node(ht, slot, moved_node); +} + + +static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot) +{ + pj_timer_entry *removed_node = ht->heap[slot]; + + // Return this timer id to the freelist. + push_freelist( ht, removed_node->_timer_id ); + + // Decrement the size of the heap by one since we're removing the + // "slot"th node. + ht->cur_size--; + + // Set the ID + removed_node->_timer_id = -1; + + // Only try to reheapify if we're not deleting the last entry. + + if (slot < ht->cur_size) + { + int parent; + pj_timer_entry *moved_node = ht->heap[ht->cur_size]; + + // Move the end node to the location being removed and update + // the corresponding slot in the parallel <timer_ids> array. + copy_node( ht, slot, moved_node); + + // If the <moved_node->time_value_> is great than or equal its + // parent it needs be moved down the heap. + parent = HEAP_PARENT (slot); + + if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value)) + reheap_down( ht, moved_node, slot, HEAP_LEFT(slot)); + else + reheap_up( ht, moved_node, slot, parent); + } + + return removed_node; +} + +static void grow_heap(pj_timer_heap_t *ht) +{ + // All the containers will double in size from max_size_ + size_t new_size = ht->max_size * 2; + pj_timer_id_t *new_timer_ids; + pj_size_t i; + + // First grow the heap itself. + + pj_timer_entry **new_heap = 0; + + new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size); + memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*)); + //delete [] this->heap_; + ht->heap = new_heap; + + // Grow the array of timer ids. + + new_timer_ids = 0; + new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t)); + + memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t)); + + //delete [] timer_ids_; + ht->timer_ids = new_timer_ids; + + // And add the new elements to the end of the "freelist". + for (i = ht->max_size; i < new_size; i++) + ht->timer_ids[i] = -((pj_timer_id_t) (i + 1)); + + ht->max_size = new_size; +} + +static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node) +{ + if (ht->cur_size + 2 >= ht->max_size) + grow_heap(ht); + + reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size)); + ht->cur_size++; +} + + +static pj_status_t schedule_entry( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *future_time ) +{ + if (ht->cur_size < ht->max_size) + { + // Obtain the next unique sequence number. + // Set the entry + entry->_timer_id = pop_freelist(ht); + entry->_timer_value = *future_time; + insert_node( ht, entry); + return 0; + } + else + return -1; +} + + +static int cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry, + int dont_call) +{ + long timer_node_slot; + + PJ_CHECK_STACK(); + + // Check to see if the timer_id is out of range + if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) + return 0; + + timer_node_slot = ht->timer_ids[entry->_timer_id]; + + if (timer_node_slot < 0) // Check to see if timer_id is still valid. + return 0; + + if (entry != ht->heap[timer_node_slot]) + { + pj_assert(entry == ht->heap[timer_node_slot]); + return 0; + } + else + { + remove_node( ht, timer_node_slot); + + if (dont_call == 0) + // Call the close hook. + (*ht->callback)(ht, entry); + return 1; + } +} + + +/* + * Calculate memory size required to create a timer heap. + */ +PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count) +{ + return /* size of the timer heap itself: */ + sizeof(pj_timer_heap_t) + + /* size of each entry: */ + (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) + + /* lock, pool etc: */ + 132; +} + +/* + * Create a new timer heap. + */ +PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, + pj_size_t size, + pj_timer_heap_t **p_heap) +{ + pj_timer_heap_t *ht; + pj_size_t i; + + PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); + + *p_heap = NULL; + + /* Magic? */ + size += 2; + + /* Allocate timer heap data structure from the pool */ + ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t)); + if (!ht) + return PJ_ENOMEM; + + /* Initialize timer heap sizes */ + ht->max_size = size; + ht->cur_size = 0; + ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL; + ht->timer_ids_freelist = 1; + ht->pool = pool; + + /* Lock. */ + ht->lock = NULL; + ht->auto_delete_lock = 0; + + // Create the heap array. + ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size); + if (!ht->heap) + return PJ_ENOMEM; + + // Create the parallel + ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size); + if (!ht->timer_ids) + return PJ_ENOMEM; + + // Initialize the "freelist," which uses negative values to + // distinguish freelist elements from "pointers" into the <heap_> + // array. + for (i=0; i<size; ++i) + ht->timer_ids[i] = -((pj_timer_id_t) (i + 1)); + + *p_heap = ht; + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ) +{ + if (ht->lock && ht->auto_delete_lock) { + pj_lock_destroy(ht->lock); + ht->lock = NULL; + } +} + +PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht, + pj_lock_t *lock, + pj_bool_t auto_del ) +{ + if (ht->lock && ht->auto_delete_lock) + pj_lock_destroy(ht->lock); + + ht->lock = lock; + ht->auto_delete_lock = auto_del; +} + + +PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht, + unsigned count ) +{ + unsigned old_count = ht->max_entries_per_poll; + ht->max_entries_per_poll = count; + return old_count; +} + +PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, + int id, + void *user_data, + pj_timer_heap_callback *cb ) +{ + pj_assert(entry && cb); + + entry->id = id; + entry->user_data = user_data; + entry->cb = cb; + + return entry; +} + +PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *delay) +{ + pj_status_t status; + pj_time_val expires; + + PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); + + pj_gettimeofday(&expires); + PJ_TIME_VAL_ADD(expires, *delay); + + lock_timer_heap(ht); + status = schedule_entry(ht, entry, &expires); + unlock_timer_heap(ht); + + return status; +} + +PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry) +{ + int count; + + PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); + + lock_timer_heap(ht); + count = cancel(ht, entry, 1); + unlock_timer_heap(ht); + + return count; +} + +PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, + pj_time_val *next_delay ) +{ + pj_time_val now; + unsigned count; + + PJ_ASSERT_RETURN(ht, 0); + + if (!ht->cur_size && next_delay) { + next_delay->sec = next_delay->msec = PJ_MAXINT32; + return 0; + } + + count = 0; + pj_gettimeofday(&now); + + lock_timer_heap(ht); + while ( ht->cur_size && + PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) && + count < ht->max_entries_per_poll ) + { + pj_timer_entry *node = remove_node(ht, 0); + ++count; + + unlock_timer_heap(ht); + (*node->cb)(ht, node); + lock_timer_heap(ht); + } + if (ht->cur_size && next_delay) { + *next_delay = ht->heap[0]->_timer_value; + PJ_TIME_VAL_SUB(*next_delay, now); + } else if (next_delay) { + next_delay->sec = next_delay->msec = PJ_MAXINT32; + } + unlock_timer_heap(ht); + + return count; +} + +PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ) +{ + PJ_ASSERT_RETURN(ht, 0); + + return ht->cur_size; +} + +PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht, + pj_time_val *timeval) +{ + pj_assert(ht->cur_size != 0); + if (ht->cur_size == 0) + return PJ_ENOTFOUND; + + lock_timer_heap(ht); + *timeval = ht->heap[0]->_timer_value; + unlock_timer_heap(ht); + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/types.c b/pjlib/src/pj/types.c index 34efff1c..d707443e 100644 --- a/pjlib/src/pj/types.c +++ b/pjlib/src/pj/types.c @@ -1,47 +1,47 @@ -/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pj/types.h>
-#include <pj/os.h>
-
-void pj_time_val_normalize(pj_time_val *t)
-{
- PJ_CHECK_STACK();
-
- if (t->msec >= 1000) {
- do {
- t->sec++;
- t->msec -= 1000;
- } while (t->msec >= 1000);
- }
- else if (t->msec <= -1000) {
- do {
- t->sec--;
- t->msec += 1000;
- } while (t->msec <= -1000);
- }
-
- if (t->sec >= 1 && t->msec < 0) {
- t->sec--;
- t->msec += 1000;
-
- } else if (t->sec < 0 && t->msec > 0) {
- t->sec++;
- t->msec -= 1000;
- }
-}
+/* $Id$ */ +/* + * Copyright (C)2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/types.h> +#include <pj/os.h> + +void pj_time_val_normalize(pj_time_val *t) +{ + PJ_CHECK_STACK(); + + if (t->msec >= 1000) { + do { + t->sec++; + t->msec -= 1000; + } while (t->msec >= 1000); + } + else if (t->msec <= -1000) { + do { + t->sec--; + t->msec += 1000; + } while (t->msec <= -1000); + } + + if (t->sec >= 1 && t->msec < 0) { + t->sec--; + t->msec += 1000; + + } else if (t->sec < 0 && t->msec > 0) { + t->sec++; + t->msec -= 1000; + } +} diff --git a/pjmedia/src/pjmedia.h b/pjmedia/src/pjmedia.h index 534e0f24..c4f2ab64 100644 --- a/pjmedia/src/pjmedia.h +++ b/pjmedia/src/pjmedia.h @@ -1,38 +1,38 @@ -/* $Id$
- *
- */
-/*
- * PJMEDIA - Multimedia over IP Stack
- * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
- *
- * Author:
- * Benny Prijono <bennylp@bulukucing.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_H__
-#define __PJMEDIA_H__
-
-#include <pjmedia/codec.h>
-#include <pjmedia/jbuf.h>
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/rtcp.h>
-#include <pjmedia/rtp.h>
-#include <pjmedia/session.h>
-#include <pjmedia/sound.h>
-#include <pjmedia/sdp.h>
-
-#endif /* __PJMEDIA_H__ */
-
+/* $Id$ + * + */ +/* + * PJMEDIA - Multimedia over IP Stack + * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org> + * + * Author: + * Benny Prijono <bennylp@bulukucing.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_H__ +#define __PJMEDIA_H__ + +#include <pjmedia/codec.h> +#include <pjmedia/jbuf.h> +#include <pjmedia/mediamgr.h> +#include <pjmedia/rtcp.h> +#include <pjmedia/rtp.h> +#include <pjmedia/session.h> +#include <pjmedia/sound.h> +#include <pjmedia/sdp.h> + +#endif /* __PJMEDIA_H__ */ + diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c index d606579d..70484388 100644 --- a/pjmedia/src/pjmedia/rtcp.c +++ b/pjmedia/src/pjmedia/rtcp.c @@ -1,216 +1,216 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/rtcp.h>
-#include <pj/os.h> /* pj_gettimeofday */
-#include <pj/sock.h> /* pj_htonx, pj_ntohx */
-#include <string.h> /* memset */
-
-#define RTCP_SR 200
-#define RTCP_RR 201
-
-
-
-/*
- * Get NTP time.
- */
-static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp)
-{
- pj_time_val tv;
-
- pj_gettimeofday(&tv);
-
- ntp->hi = tv.sec;
- tv.msec = tv.msec % 1000;
- ntp->lo = tv.msec * 0xFFFF / 1000;
- ntp->lo <<= 16;
-}
-
-
-PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc)
-{
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
-
- memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt));
-
- /* Init time */
- s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0;
- s->rtcp_lsr_time = 0;
-
- /* Init common RTCP header */
- rtcp_pkt->common.version = 2;
- rtcp_pkt->common.count = 1;
- rtcp_pkt->common.pt = RTCP_SR;
- rtcp_pkt->common.length = pj_htons(12);
-
- /* Init SR */
- rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
-
- /* RR will be initialized on receipt of the first RTP packet. */
-}
-
-PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session)
-{
- /* Nothing to do. */
- PJ_UNUSED_ARG(session)
-}
-
-static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq)
-{
- s->received = 0;
- s->expected_prior = 0;
- s->received_prior = 0;
- s->transit = 0;
- s->jitter = 0;
-
- pj_rtp_seq_restart(&s->seq_ctrl, seq);
-}
-
-PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts)
-{
- pj_uint32_t arrival;
- pj_int32_t transit;
- unsigned long timer_tick;
- pj_time_val tv;
- int status;
-
- /* Update sequence numbers (received, lost, etc). */
- status = pj_rtp_seq_update(&s->seq_ctrl, seq);
- if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
- rtcp_init_seq(s, seq);
- status = 0;
- }
-
- if (status != 0)
- return;
-
- ++s->received;
-
- pj_gettimeofday(&tv);
- timer_tick = tv.sec * 1000 + tv.msec;
-
- /*
- * Calculate jitter (s->jitter is in timer tick unit)
- */
- PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
-
- arrival = timer_tick << 3; // 8 samples per ms.
- transit = arrival - rtp_ts;
-
- if (s->transit == 0) {
- s->transit = transit;
- } else {
- pj_int32_t d, jitter = s->jitter;
-
- d = transit - s->transit;
- s->transit = transit;
- if (d < 0)
- d = -d;
-
- jitter += d - ((jitter + 8) >> 4);
- s->jitter = jitter;
- }
-}
-
-PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size)
-{
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
- rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
- rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
-}
-
-static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc)
-{
- pj_uint32_t expected;
- pj_uint32_t u32;
- pj_uint32_t expected_interval, received_interval, lost_interval;
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
-
- /* SSRC and last_seq */
- rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
- rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
- rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
- rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
-
- /* Jitter */
- rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
-
- /* Total lost. */
- expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1;
- u32 = expected - s->received;
- rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
- rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
- rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
-
- /* Fraction lost calculation */
- expected_interval = expected - s->expected_prior;
- s->expected_prior = expected;
-
- received_interval = s->received - s->received_prior;
- s->received_prior = s->received;
-
- lost_interval = expected_interval - received_interval;
-
- if (expected_interval==0 || lost_interval == 0) {
- rtcp_pkt->rr.fract_lost = 0;
- } else {
- rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
- }
-}
-
-PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len)
-{
- pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
- pj_rtcp_ntp_rec ntp;
- pj_time_val now;
-
- rtcp_build_rtcp(session, session->peer_ssrc);
-
- /* Get current NTP time. */
- rtcp_get_ntp_time(&ntp);
-
- /* Fill in NTP timestamp in SR. */
- rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
- rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
-
- if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) {
- rtcp_pkt->rr.lsr = 0;
- rtcp_pkt->rr.dlsr = 0;
- } else {
- unsigned msec_elapsed;
-
- /* Fill in LSR.
- LSR is the middle 32bit of the last SR NTP time received.
- */
- rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) |
- ((session->rtcp_lsr.lo >> 16) & 0xFFFF);
- rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr);
-
- /* Fill in DLSR.
- DLSR is Delay since Last SR, in 1/65536 seconds.
- */
- pj_gettimeofday(&now);
- msec_elapsed = (now.msec - session->rtcp_lsr_time);
- rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000);
- }
-
- /* Return pointer. */
- *ret_p_pkt = rtcp_pkt;
- *len = sizeof(pj_rtcp_pkt);
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/rtcp.h> +#include <pj/os.h> /* pj_gettimeofday */ +#include <pj/sock.h> /* pj_htonx, pj_ntohx */ +#include <string.h> /* memset */ + +#define RTCP_SR 200 +#define RTCP_RR 201 + + + +/* + * Get NTP time. + */ +static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp) +{ + pj_time_val tv; + + pj_gettimeofday(&tv); + + ntp->hi = tv.sec; + tv.msec = tv.msec % 1000; + ntp->lo = tv.msec * 0xFFFF / 1000; + ntp->lo <<= 16; +} + + +PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc) +{ + pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; + + memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt)); + + /* Init time */ + s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0; + s->rtcp_lsr_time = 0; + + /* Init common RTCP header */ + rtcp_pkt->common.version = 2; + rtcp_pkt->common.count = 1; + rtcp_pkt->common.pt = RTCP_SR; + rtcp_pkt->common.length = pj_htons(12); + + /* Init SR */ + rtcp_pkt->sr.ssrc = pj_htonl(ssrc); + + /* RR will be initialized on receipt of the first RTP packet. */ +} + +PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session) +{ + /* Nothing to do. */ + PJ_UNUSED_ARG(session) +} + +static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq) +{ + s->received = 0; + s->expected_prior = 0; + s->received_prior = 0; + s->transit = 0; + s->jitter = 0; + + pj_rtp_seq_restart(&s->seq_ctrl, seq); +} + +PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts) +{ + pj_uint32_t arrival; + pj_int32_t transit; + unsigned long timer_tick; + pj_time_val tv; + int status; + + /* Update sequence numbers (received, lost, etc). */ + status = pj_rtp_seq_update(&s->seq_ctrl, seq); + if (status == PJ_RTP_ERR_SESSION_RESTARTED) { + rtcp_init_seq(s, seq); + status = 0; + } + + if (status != 0) + return; + + ++s->received; + + pj_gettimeofday(&tv); + timer_tick = tv.sec * 1000 + tv.msec; + + /* + * Calculate jitter (s->jitter is in timer tick unit) + */ + PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE) + + arrival = timer_tick << 3; // 8 samples per ms. + transit = arrival - rtp_ts; + + if (s->transit == 0) { + s->transit = transit; + } else { + pj_int32_t d, jitter = s->jitter; + + d = transit - s->transit; + s->transit = transit; + if (d < 0) + d = -d; + + jitter += d - ((jitter + 8) >> 4); + s->jitter = jitter; + } +} + +PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size) +{ + pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; + rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1); + rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size ); +} + +static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc) +{ + pj_uint32_t expected; + pj_uint32_t u32; + pj_uint32_t expected_interval, received_interval, lost_interval; + pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; + + /* SSRC and last_seq */ + rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc); + rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L); + rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq; + rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq); + + /* Jitter */ + rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4); + + /* Total lost. */ + expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1; + u32 = expected - s->received; + rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF; + rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF; + rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF; + + /* Fraction lost calculation */ + expected_interval = expected - s->expected_prior; + s->expected_prior = expected; + + received_interval = s->received - s->received_prior; + s->received_prior = s->received; + + lost_interval = expected_interval - received_interval; + + if (expected_interval==0 || lost_interval == 0) { + rtcp_pkt->rr.fract_lost = 0; + } else { + rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval; + } +} + +PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len) +{ + pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt; + pj_rtcp_ntp_rec ntp; + pj_time_val now; + + rtcp_build_rtcp(session, session->peer_ssrc); + + /* Get current NTP time. */ + rtcp_get_ntp_time(&ntp); + + /* Fill in NTP timestamp in SR. */ + rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi); + rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo); + + if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) { + rtcp_pkt->rr.lsr = 0; + rtcp_pkt->rr.dlsr = 0; + } else { + unsigned msec_elapsed; + + /* Fill in LSR. + LSR is the middle 32bit of the last SR NTP time received. + */ + rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) | + ((session->rtcp_lsr.lo >> 16) & 0xFFFF); + rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr); + + /* Fill in DLSR. + DLSR is Delay since Last SR, in 1/65536 seconds. + */ + pj_gettimeofday(&now); + msec_elapsed = (now.msec - session->rtcp_lsr_time); + rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000); + } + + /* Return pointer. */ + *ret_p_pkt = rtcp_pkt; + *len = sizeof(pj_rtcp_pkt); +} + diff --git a/pjmedia/src/pjmedia/rtcp.h b/pjmedia/src/pjmedia/rtcp.h index b680c1bf..8a8f8909 100644 --- a/pjmedia/src/pjmedia/rtcp.h +++ b/pjmedia/src/pjmedia/rtcp.h @@ -1,192 +1,192 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_RTCP_H__
-#define __PJMEDIA_RTCP_H__
-
-/**
- * @file rtcp.h
- * @brief RTCP implementation.
- */
-
-#include <pj/types.h>
-#include <pjmedia/rtp.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_RTCP RTCP
- * @ingroup PJMEDIA
- * @{
- */
-
-/**
- * RTCP sender report.
- */
-struct pj_rtcp_sr
-{
- pj_uint32_t ssrc;
- pj_uint32_t ntp_sec;
- pj_uint32_t ntp_frac;
- pj_uint32_t rtp_ts;
- pj_uint32_t sender_pcount;
- pj_uint32_t sender_bcount;
-};
-
-typedef struct pj_rtcp_sr pj_rtcp_sr;
-
-/**
- * RTCP receiver report.
- */
-struct pj_rtcp_rr
-{
- pj_uint32_t ssrc;
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- pj_uint32_t fract_lost:8;
- pj_uint32_t total_lost_2:8;
- pj_uint32_t total_lost_1:8;
- pj_uint32_t total_lost_0:8;
-#else
- pj_uint32_t fract_lost:8;
- pj_uint32_t total_lost_0:8;
- pj_uint32_t total_lost_1:8;
- pj_uint32_t total_lost_2:8;
-#endif
- pj_uint32_t last_seq;
- pj_uint32_t jitter;
- pj_uint32_t lsr;
- pj_uint32_t dlsr;
-};
-
-typedef struct pj_rtcp_rr pj_rtcp_rr;
-
-/**
- * RTCP common header.
- */
-struct pj_rtcp_common
-{
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- unsigned version:2; /* packet type */
- unsigned p:1; /* padding flag */
- unsigned count:5; /* varies by payload type */
- unsigned pt:8; /* payload type */
-#else
- unsigned count:5; /* varies by payload type */
- unsigned p:1; /* padding flag */
- unsigned version:2; /* packet type */
- unsigned pt:8; /* payload type */
-#endif
- pj_uint16_t length; /* packet length */
-};
-
-typedef struct pj_rtcp_common pj_rtcp_common;
-
-/**
- * RTCP packet.
- */
-struct pj_rtcp_pkt
-{
- pj_rtcp_common common;
- pj_rtcp_sr sr;
- pj_rtcp_rr rr; /* variable-length list */
-};
-
-typedef struct pj_rtcp_pkt pj_rtcp_pkt;
-
-/**
- * NTP time representation.
- */
-struct pj_rtcp_ntp_rec
-{
- pj_uint32_t hi;
- pj_uint32_t lo;
-};
-
-typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec;
-
-/**
- * RTCP session.
- */
-struct pj_rtcp_session
-{
- pj_rtcp_pkt rtcp_pkt;
-
- pj_rtp_seq_session seq_ctrl;
-
- pj_uint32_t received; /* packets received */
- pj_uint32_t expected_prior; /* packet expected at last interval */
- pj_uint32_t received_prior; /* packet received at last interval */
- pj_int32_t transit; /* relative trans time for prev pkt */
- pj_uint32_t jitter; /* estimated jitter */
-
- pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */
- unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */
- unsigned peer_ssrc; /* Peer SSRC */
-
-};
-
-typedef struct pj_rtcp_session pj_rtcp_session;
-
-/**
- * Init RTCP session.
- * @param session The session
- * @param ssrc The SSRC used in to identify the session.
- */
-PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc );
-
-/**
- * Deinit RTCP session.
- * @param session The session.
- */
-PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session);
-
-/**
- * Call this function everytime an RTP packet is received to let the RTCP
- * session do its internal calculations.
- * @param session The session.
- * @param seq The RTP packet sequence number, in host byte order.
- * @param ts The RTP packet timestamp, in host byte order.
- */
-PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts );
-
-/**
- * Call this function everytime an RTP packet is sent to let the RTCP session
- * do its internal calculations.
- * @param session The session.
- * @param bytes_payload_size The payload size of the RTP packet (ie packet minus
- * RTP header).
- */
-PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size );
-
-/**
- * Build a RTCP SR/RR packet to be transmitted to remote RTP peer.
- * @param session The session.
- * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet.
- * @param len [output] Upon return, it will indicate the size of the RTCP packet.
- */
-PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_RTCP_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_RTCP_H__ +#define __PJMEDIA_RTCP_H__ + +/** + * @file rtcp.h + * @brief RTCP implementation. + */ + +#include <pj/types.h> +#include <pjmedia/rtp.h> + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_RTCP RTCP + * @ingroup PJMEDIA + * @{ + */ + +/** + * RTCP sender report. + */ +struct pj_rtcp_sr +{ + pj_uint32_t ssrc; + pj_uint32_t ntp_sec; + pj_uint32_t ntp_frac; + pj_uint32_t rtp_ts; + pj_uint32_t sender_pcount; + pj_uint32_t sender_bcount; +}; + +typedef struct pj_rtcp_sr pj_rtcp_sr; + +/** + * RTCP receiver report. + */ +struct pj_rtcp_rr +{ + pj_uint32_t ssrc; +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + pj_uint32_t fract_lost:8; + pj_uint32_t total_lost_2:8; + pj_uint32_t total_lost_1:8; + pj_uint32_t total_lost_0:8; +#else + pj_uint32_t fract_lost:8; + pj_uint32_t total_lost_0:8; + pj_uint32_t total_lost_1:8; + pj_uint32_t total_lost_2:8; +#endif + pj_uint32_t last_seq; + pj_uint32_t jitter; + pj_uint32_t lsr; + pj_uint32_t dlsr; +}; + +typedef struct pj_rtcp_rr pj_rtcp_rr; + +/** + * RTCP common header. + */ +struct pj_rtcp_common +{ +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + unsigned version:2; /* packet type */ + unsigned p:1; /* padding flag */ + unsigned count:5; /* varies by payload type */ + unsigned pt:8; /* payload type */ +#else + unsigned count:5; /* varies by payload type */ + unsigned p:1; /* padding flag */ + unsigned version:2; /* packet type */ + unsigned pt:8; /* payload type */ +#endif + pj_uint16_t length; /* packet length */ +}; + +typedef struct pj_rtcp_common pj_rtcp_common; + +/** + * RTCP packet. + */ +struct pj_rtcp_pkt +{ + pj_rtcp_common common; + pj_rtcp_sr sr; + pj_rtcp_rr rr; /* variable-length list */ +}; + +typedef struct pj_rtcp_pkt pj_rtcp_pkt; + +/** + * NTP time representation. + */ +struct pj_rtcp_ntp_rec +{ + pj_uint32_t hi; + pj_uint32_t lo; +}; + +typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec; + +/** + * RTCP session. + */ +struct pj_rtcp_session +{ + pj_rtcp_pkt rtcp_pkt; + + pj_rtp_seq_session seq_ctrl; + + pj_uint32_t received; /* packets received */ + pj_uint32_t expected_prior; /* packet expected at last interval */ + pj_uint32_t received_prior; /* packet received at last interval */ + pj_int32_t transit; /* relative trans time for prev pkt */ + pj_uint32_t jitter; /* estimated jitter */ + + pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */ + unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */ + unsigned peer_ssrc; /* Peer SSRC */ + +}; + +typedef struct pj_rtcp_session pj_rtcp_session; + +/** + * Init RTCP session. + * @param session The session + * @param ssrc The SSRC used in to identify the session. + */ +PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc ); + +/** + * Deinit RTCP session. + * @param session The session. + */ +PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session); + +/** + * Call this function everytime an RTP packet is received to let the RTCP + * session do its internal calculations. + * @param session The session. + * @param seq The RTP packet sequence number, in host byte order. + * @param ts The RTP packet timestamp, in host byte order. + */ +PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts ); + +/** + * Call this function everytime an RTP packet is sent to let the RTCP session + * do its internal calculations. + * @param session The session. + * @param bytes_payload_size The payload size of the RTP packet (ie packet minus + * RTP header). + */ +PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size ); + +/** + * Build a RTCP SR/RR packet to be transmitted to remote RTP peer. + * @param session The session. + * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet. + * @param len [output] Upon return, it will indicate the size of the RTCP packet. + */ +PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len ); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_RTCP_H__ */ diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c index ac62a1a4..5aadd7c1 100644 --- a/pjmedia/src/pjmedia/rtp.c +++ b/pjmedia/src/pjmedia/rtp.c @@ -1,261 +1,261 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/rtp.h>
-#include <pj/log.h>
-#include <pj/os.h> /* pj_gettimeofday() */
-#include <pj/sock.h> /* pj_htonx, pj_htonx */
-#include <string.h> /* memset() */
-
-#define THIS_FILE "rtp.c"
-
-#define RTP_VERSION 2
-
-#define RTP_SEQ_MOD (1 << 16)
-#define MAX_DROPOUT ((pj_int16_t)3000)
-#define MAX_MISORDER ((pj_int16_t)100)
-#define MIN_SEQUENTIAL ((pj_int16_t)2)
-
-
-PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
- int default_pt, pj_uint32_t sender_ssrc )
-{
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
- ses, default_pt, sender_ssrc));
-
- /* Check RTP header packing. */
- if (sizeof(struct pj_rtp_hdr) != 12) {
- pj_assert(!"Wrong RTP header packing!");
- return PJ_RTP_ERR_RTP_PACKING;
- }
-
- /* If sender_ssrc is not specified, create from time value. */
- if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
- pj_time_val tv;
-
- pj_gettimeofday(&tv);
- sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec);
- } else {
- sender_ssrc = pj_htonl(sender_ssrc);
- }
-
- /* Initialize session. */
- ses->out_extseq = 0;
- ses->peer_ssrc = 0;
-
- /* Sequence number will be initialized when the first RTP packet is receieved. */
-
- /* Build default header for outgoing RTP packet. */
- memset(ses, 0, sizeof(*ses));
- ses->out_hdr.v = RTP_VERSION;
- ses->out_hdr.p = 0;
- ses->out_hdr.x = 0;
- ses->out_hdr.cc = 0;
- ses->out_hdr.m = 0;
- ses->out_hdr.pt = (pj_uint8_t) default_pt;
- ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
- ses->out_hdr.ts = 0;
- ses->out_hdr.ssrc = sender_ssrc;
-
- /* Keep some arguments as session defaults. */
- ses->out_pt = (pj_uint16_t) default_pt;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
- int payload_len, int ts_len,
- const void **rtphdr, int *hdrlen )
-{
- PJ_UNUSED_ARG(payload_len)
-
- PJ_LOG(6, (THIS_FILE,
- "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d",
- ses, pt, m, payload_len, ts_len));
-
- /* Update session. */
- ses->out_extseq++;
- ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
-
- /* Create outgoing header. */
- ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
- ses->out_hdr.m = (pj_uint16_t) m;
- ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
-
- /* Return values */
- *rtphdr = &ses->out_hdr;
- *hdrlen = sizeof(pj_rtp_hdr);
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
- const void *pkt, int pkt_len,
- const pj_rtp_hdr **hdr,
- const void **payload,
- unsigned *payloadlen)
-{
- int offset;
-
- PJ_UNUSED_ARG(ses)
-
- PJ_LOG(6, (THIS_FILE,
- "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d",
- ses, pkt, pkt_len));
-
- /* Assume RTP header at the start of packet. We'll verify this later. */
- *hdr = (pj_rtp_hdr*)pkt;
-
- /* Check RTP header sanity. */
- if ((*hdr)->v != RTP_VERSION) {
- PJ_LOG(4, (THIS_FILE, " invalid RTP version!"));
- return PJ_RTP_ERR_INVALID_VERSION;
- }
-
- /* Payload is located right after header plus CSRC */
- offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
-
- /* Adjust offset if RTP extension is used. */
- if ((*hdr)->x) {
- pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset);
- offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t));
- }
-
- /* Check that offset is less than packet size */
- if (offset >= pkt_len)
- return PJ_RTP_ERR_INVALID_PACKET;
-
- /* Find and set payload. */
- *payload = ((pj_uint8_t*)pkt) + offset;
- *payloadlen = pkt_len - offset;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr)
-{
- int status;
-
- /* Check SSRC. */
- if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
- /*
- if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) {
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)",
- ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc));
- return PJ_RTP_ERR_INVALID_SSRC;
- }
- */
-
- /* Check payload type. */
- if (hdr->pt != ses->out_pt) {
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)",
- ses, hdr->pt, ses->out_pt));
- return PJ_RTP_ERR_INVALID_PT;
- }
-
- /* Initialize sequence number on first packet received. */
- if (ses->received == 0)
- pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
-
- /* Check sequence number to see if remote session has been restarted. */
- status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq));
- if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
- pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq));
- ++ses->received;
- } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) {
- ++ses->received;
- }
-
-
- return status;
-}
-
-
-void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- sctrl->base_seq = seq;
- sctrl->max_seq = seq;
- sctrl->bad_seq = RTP_SEQ_MOD + 1;
- sctrl->cycles = 0;
-}
-
-
-void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- pj_rtp_seq_restart(sctrl, seq);
-
- sctrl->max_seq = (pj_uint16_t) (seq - 1);
- sctrl->probation = MIN_SEQUENTIAL;
-}
-
-
-int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq);
-
- /*
- * Source is not valid until MIN_SEQUENTIAL packets with
- * sequential sequence numbers have been received.
- */
- if (sctrl->probation) {
- /* packet is in sequence */
- if (seq == sctrl->max_seq+ 1) {
- sctrl->probation--;
- sctrl->max_seq = seq;
- if (sctrl->probation == 0) {
- return PJ_RTP_ERR_SESSION_RESTARTED;
- }
- } else {
- sctrl->probation = MIN_SEQUENTIAL - 1;
- sctrl->max_seq = seq;
- }
- return PJ_RTP_ERR_SESSION_PROBATION;
-
- } else if (udelta < MAX_DROPOUT) {
- /* in order, with permissible gap */
- if (seq < sctrl->max_seq) {
- /* Sequence number wrapped - count another 64K cycle. */
- sctrl->cycles += RTP_SEQ_MOD;
- }
- sctrl->max_seq = seq;
-
- } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
- /* the sequence number made a very large jump */
- if (seq == sctrl->bad_seq) {
- /*
- * Two sequential packets -- assume that the other side
- * restarted without telling us so just re-sync
- * (i.e., pretend this was the first packet).
- */
- return PJ_RTP_ERR_SESSION_RESTARTED;
- }
- else {
- sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
- return PJ_RTP_ERR_BAD_SEQUENCE;
- }
- } else {
- /* duplicate or reordered packet */
- }
-
- return 0;
-}
-
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/rtp.h> +#include <pj/log.h> +#include <pj/os.h> /* pj_gettimeofday() */ +#include <pj/sock.h> /* pj_htonx, pj_htonx */ +#include <string.h> /* memset() */ + +#define THIS_FILE "rtp.c" + +#define RTP_VERSION 2 + +#define RTP_SEQ_MOD (1 << 16) +#define MAX_DROPOUT ((pj_int16_t)3000) +#define MAX_MISORDER ((pj_int16_t)100) +#define MIN_SEQUENTIAL ((pj_int16_t)2) + + +PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses, + int default_pt, pj_uint32_t sender_ssrc ) +{ + PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x", + ses, default_pt, sender_ssrc)); + + /* Check RTP header packing. */ + if (sizeof(struct pj_rtp_hdr) != 12) { + pj_assert(!"Wrong RTP header packing!"); + return PJ_RTP_ERR_RTP_PACKING; + } + + /* If sender_ssrc is not specified, create from time value. */ + if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) { + pj_time_val tv; + + pj_gettimeofday(&tv); + sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec); + } else { + sender_ssrc = pj_htonl(sender_ssrc); + } + + /* Initialize session. */ + ses->out_extseq = 0; + ses->peer_ssrc = 0; + + /* Sequence number will be initialized when the first RTP packet is receieved. */ + + /* Build default header for outgoing RTP packet. */ + memset(ses, 0, sizeof(*ses)); + ses->out_hdr.v = RTP_VERSION; + ses->out_hdr.p = 0; + ses->out_hdr.x = 0; + ses->out_hdr.cc = 0; + ses->out_hdr.m = 0; + ses->out_hdr.pt = (pj_uint8_t) default_pt; + ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq ); + ses->out_hdr.ts = 0; + ses->out_hdr.ssrc = sender_ssrc; + + /* Keep some arguments as session defaults. */ + ses->out_pt = (pj_uint16_t) default_pt; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m, + int payload_len, int ts_len, + const void **rtphdr, int *hdrlen ) +{ + PJ_UNUSED_ARG(payload_len) + + PJ_LOG(6, (THIS_FILE, + "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d", + ses, pt, m, payload_len, ts_len)); + + /* Update session. */ + ses->out_extseq++; + ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len); + + /* Create outgoing header. */ + ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt); + ses->out_hdr.m = (pj_uint16_t) m; + ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq); + + /* Return values */ + *rtphdr = &ses->out_hdr; + *hdrlen = sizeof(pj_rtp_hdr); + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, + const void *pkt, int pkt_len, + const pj_rtp_hdr **hdr, + const void **payload, + unsigned *payloadlen) +{ + int offset; + + PJ_UNUSED_ARG(ses) + + PJ_LOG(6, (THIS_FILE, + "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d", + ses, pkt, pkt_len)); + + /* Assume RTP header at the start of packet. We'll verify this later. */ + *hdr = (pj_rtp_hdr*)pkt; + + /* Check RTP header sanity. */ + if ((*hdr)->v != RTP_VERSION) { + PJ_LOG(4, (THIS_FILE, " invalid RTP version!")); + return PJ_RTP_ERR_INVALID_VERSION; + } + + /* Payload is located right after header plus CSRC */ + offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t)); + + /* Adjust offset if RTP extension is used. */ + if ((*hdr)->x) { + pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset); + offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t)); + } + + /* Check that offset is less than packet size */ + if (offset >= pkt_len) + return PJ_RTP_ERR_INVALID_PACKET; + + /* Find and set payload. */ + *payload = ((pj_uint8_t*)pkt) + offset; + *payloadlen = pkt_len - offset; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr) +{ + int status; + + /* Check SSRC. */ + if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc); + /* + if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) { + PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)", + ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc)); + return PJ_RTP_ERR_INVALID_SSRC; + } + */ + + /* Check payload type. */ + if (hdr->pt != ses->out_pt) { + PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)", + ses, hdr->pt, ses->out_pt)); + return PJ_RTP_ERR_INVALID_PT; + } + + /* Initialize sequence number on first packet received. */ + if (ses->received == 0) + pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) ); + + /* Check sequence number to see if remote session has been restarted. */ + status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq)); + if (status == PJ_RTP_ERR_SESSION_RESTARTED) { + pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq)); + ++ses->received; + } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) { + ++ses->received; + } + + + return status; +} + + +void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq) +{ + sctrl->base_seq = seq; + sctrl->max_seq = seq; + sctrl->bad_seq = RTP_SEQ_MOD + 1; + sctrl->cycles = 0; +} + + +void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq) +{ + pj_rtp_seq_restart(sctrl, seq); + + sctrl->max_seq = (pj_uint16_t) (seq - 1); + sctrl->probation = MIN_SEQUENTIAL; +} + + +int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq) +{ + pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq); + + /* + * Source is not valid until MIN_SEQUENTIAL packets with + * sequential sequence numbers have been received. + */ + if (sctrl->probation) { + /* packet is in sequence */ + if (seq == sctrl->max_seq+ 1) { + sctrl->probation--; + sctrl->max_seq = seq; + if (sctrl->probation == 0) { + return PJ_RTP_ERR_SESSION_RESTARTED; + } + } else { + sctrl->probation = MIN_SEQUENTIAL - 1; + sctrl->max_seq = seq; + } + return PJ_RTP_ERR_SESSION_PROBATION; + + } else if (udelta < MAX_DROPOUT) { + /* in order, with permissible gap */ + if (seq < sctrl->max_seq) { + /* Sequence number wrapped - count another 64K cycle. */ + sctrl->cycles += RTP_SEQ_MOD; + } + sctrl->max_seq = seq; + + } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) { + /* the sequence number made a very large jump */ + if (seq == sctrl->bad_seq) { + /* + * Two sequential packets -- assume that the other side + * restarted without telling us so just re-sync + * (i.e., pretend this was the first packet). + */ + return PJ_RTP_ERR_SESSION_RESTARTED; + } + else { + sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); + return PJ_RTP_ERR_BAD_SEQUENCE; + } + } else { + /* duplicate or reordered packet */ + } + + return 0; +} + + diff --git a/pjmedia/src/pjmedia/rtp.h b/pjmedia/src/pjmedia/rtp.h index 2d2832ad..54c1e32e 100644 --- a/pjmedia/src/pjmedia/rtp.h +++ b/pjmedia/src/pjmedia/rtp.h @@ -1,256 +1,256 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_RTP_H__
-#define __PJMEDIA_RTP_H__
-
-#include <pj/types.h>
-
-/**
- * @file rtp.h
- * @brief RTP implementation.
- */
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_RTP RTP
- * @ingroup PJMEDIA
- * @{
- *
- * The RTP module is designed to be dependent only to PJLIB, it does not depend
- * on any other parts of PJMEDIA library. The RTP module does not even depend
- * on any transports (sockets), to promote even more use.
- *
- * An RTCP implementation is also separated from this module.
- *
- * The functions that are provided by this module:
- * - creating RTP header for each outgoing packet.
- * - decoding RTP packet into RTP header and payload.
- * - provide simple RTP session management (sequence number, etc.)
- *
- * The RTP module does not use any dynamic memory at all.
- *
- * \section P1 How to Use the RTP Module
- *
- * First application must call #pj_rtp_session_init to initialize the RTP
- * session.
- *
- * When application wants to send RTP packet, it needs to call
- * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build
- * the complete RTP packet, but instead only the header. Application can
- * then either concatenate the header with the payload, or send the two
- * fragments (the header and the payload) using scatter-gather transport API
- * (e.g. \a sendv()).
- *
- * When application receives an RTP packet, first it should call
- * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call
- * #pj_rtp_session_update to check whether we can process the RTP payload,
- * and to let the RTP session updates its internal status. The decode function
- * is guaranteed to point the payload to the correct position regardless of
- * any options present in the RTP packet.
- *
- */
-
-
-#ifdef _MSC_VER
-# pragma warning ( disable : 4214 )
-#endif
-
-
-/**
- * Error codes.
- */
-enum pj_rtp_error_t
-{
- PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */
- PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */
- PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */
- PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */
- PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */
- PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */
- PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */
- PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */
-};
-
-#pragma pack(1)
-/**
- * RTP packet header.
- */
-struct pj_rtp_hdr
-{
-#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0)
- pj_uint16_t v:2; /**< packet type/version */
- pj_uint16_t p:1; /**< padding flag */
- pj_uint16_t x:1; /**< extension flag */
- pj_uint16_t cc:4; /**< CSRC count */
- pj_uint16_t m:1; /**< marker bit */
- pj_uint16_t pt:7; /**< payload type */
-#else
- pj_uint16_t cc:4; /**< CSRC count */
- pj_uint16_t x:1; /**< header extension flag */
- pj_uint16_t p:1; /**< padding flag */
- pj_uint16_t v:2; /**< packet type/version */
- pj_uint16_t pt:7; /**< payload type */
- pj_uint16_t m:1; /**< marker bit */
-#endif
- pj_uint16_t seq; /**< sequence number */
- pj_uint32_t ts; /**< timestamp */
- pj_uint32_t ssrc; /**< synchronization source */
-};
-#pragma pack()
-
-typedef struct pj_rtp_hdr pj_rtp_hdr;
-
-/**
- * RTP extendsion header.
- */
-struct pj_rtp_ext_hdr
-{
- pj_uint16_t profile_data;
- pj_uint16_t length;
-};
-
-typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr;
-
-/**
- * A generic sequence number management, used by both RTP and RTCP.
- */
-struct pj_rtp_seq_session
-{
- pj_uint16_t max_seq; /**< highest sequence number heard */
- pj_uint32_t cycles; /**< shifted count of seq. number cycles */
- pj_uint32_t base_seq; /**< base seq number */
- pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */
- pj_uint32_t probation; /**< sequ. packets till source is valid */
-};
-
-typedef struct pj_rtp_seq_session pj_rtp_seq_session;
-
-/**
- * RTP session descriptor.
- */
-struct pj_rtp_session
-{
- pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */
- pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */
- pj_uint16_t out_pt; /**< Default outgoing payload type. */
- pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */
- pj_uint32_t peer_ssrc; /**< Peer SSRC. */
- pj_uint32_t received; /**< Number of received packets. */
-};
-
-typedef struct pj_rtp_session pj_rtp_session;
-
-/**
- * \brief Initialize RTP session.
- * This function will initialize the RTP session according to given parameters.
- *
- * @param ses The session.
- * @param default_pt Default payload type.
- * @param sender_ssrc SSRC used for outgoing packets.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
- int default_pt, pj_uint32_t sender_ssrc );
-
-/**
- * \brief Encode outgoing RTP packet header.
- * Create the RTP header based on arguments and current state of the RTP
- * session.
- *
- * @param ses The session.
- * @param pt Payload type.
- * @param m Marker flag.
- * @param payload_len Payload length in bytes.
- * @param ts_len Timestamp length.
- * @param rtphdr Upon return will point to RTP packet header.
- * @param hdrlen Upon return will indicate the size of RTP packet header
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
- int payload_len, int ts_len,
- const void **rtphdr, int *hdrlen );
-
-/**
- * \brief Decode an incoming RTP packet.
- * This function will decode incoming packet into RTP header and payload.
- * The decode function is guaranteed to point the payload to the correct
- * position regardless of any options present in the RTP packet.
- *
- * @param ses The session.
- * @param pkt The received RTP packet.
- * @param pkt_len The length of the packet.
- * @param hdr Upon return will point to the location of the RTP header
- * inside the packet.
- * @param payload Upon return will point to the location of the
- * payload inside the packet.
- * @param payloadlen Upon return will indicate the size of the payload.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
- const void *pkt, int pkt_len,
- const pj_rtp_hdr **hdr,
- const void **payload,
- unsigned *payloadlen);
-
-/**
- * \brief Update RTP session with an incoming RTP packet.
- * Call this function everytime
- * an RTP packet is received to check whether the packet can be received and to
- * let the RTP session performs its internal calculations.
- *
- * @param ses The session.
- * @param hdr The RTP header of the incoming packet.
- *
- * @return zero if the packet is valid and can be processed, otherwise will
- * return one of the error in #pj_rtp_error_t.
- */
-PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses,
- const pj_rtp_hdr *hdr);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_RTP_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_RTP_H__ +#define __PJMEDIA_RTP_H__ + +#include <pj/types.h> + +/** + * @file rtp.h + * @brief RTP implementation. + */ + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_RTP RTP + * @ingroup PJMEDIA + * @{ + * + * The RTP module is designed to be dependent only to PJLIB, it does not depend + * on any other parts of PJMEDIA library. The RTP module does not even depend + * on any transports (sockets), to promote even more use. + * + * An RTCP implementation is also separated from this module. + * + * The functions that are provided by this module: + * - creating RTP header for each outgoing packet. + * - decoding RTP packet into RTP header and payload. + * - provide simple RTP session management (sequence number, etc.) + * + * The RTP module does not use any dynamic memory at all. + * + * \section P1 How to Use the RTP Module + * + * First application must call #pj_rtp_session_init to initialize the RTP + * session. + * + * When application wants to send RTP packet, it needs to call + * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build + * the complete RTP packet, but instead only the header. Application can + * then either concatenate the header with the payload, or send the two + * fragments (the header and the payload) using scatter-gather transport API + * (e.g. \a sendv()). + * + * When application receives an RTP packet, first it should call + * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call + * #pj_rtp_session_update to check whether we can process the RTP payload, + * and to let the RTP session updates its internal status. The decode function + * is guaranteed to point the payload to the correct position regardless of + * any options present in the RTP packet. + * + */ + + +#ifdef _MSC_VER +# pragma warning ( disable : 4214 ) +#endif + + +/** + * Error codes. + */ +enum pj_rtp_error_t +{ + PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */ + PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */ + PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */ + PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */ + PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */ + PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */ + PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */ + PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */ +}; + +#pragma pack(1) +/** + * RTP packet header. + */ +struct pj_rtp_hdr +{ +#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0) + pj_uint16_t v:2; /**< packet type/version */ + pj_uint16_t p:1; /**< padding flag */ + pj_uint16_t x:1; /**< extension flag */ + pj_uint16_t cc:4; /**< CSRC count */ + pj_uint16_t m:1; /**< marker bit */ + pj_uint16_t pt:7; /**< payload type */ +#else + pj_uint16_t cc:4; /**< CSRC count */ + pj_uint16_t x:1; /**< header extension flag */ + pj_uint16_t p:1; /**< padding flag */ + pj_uint16_t v:2; /**< packet type/version */ + pj_uint16_t pt:7; /**< payload type */ + pj_uint16_t m:1; /**< marker bit */ +#endif + pj_uint16_t seq; /**< sequence number */ + pj_uint32_t ts; /**< timestamp */ + pj_uint32_t ssrc; /**< synchronization source */ +}; +#pragma pack() + +typedef struct pj_rtp_hdr pj_rtp_hdr; + +/** + * RTP extendsion header. + */ +struct pj_rtp_ext_hdr +{ + pj_uint16_t profile_data; + pj_uint16_t length; +}; + +typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr; + +/** + * A generic sequence number management, used by both RTP and RTCP. + */ +struct pj_rtp_seq_session +{ + pj_uint16_t max_seq; /**< highest sequence number heard */ + pj_uint32_t cycles; /**< shifted count of seq. number cycles */ + pj_uint32_t base_seq; /**< base seq number */ + pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */ + pj_uint32_t probation; /**< sequ. packets till source is valid */ +}; + +typedef struct pj_rtp_seq_session pj_rtp_seq_session; + +/** + * RTP session descriptor. + */ +struct pj_rtp_session +{ + pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */ + pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */ + pj_uint16_t out_pt; /**< Default outgoing payload type. */ + pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */ + pj_uint32_t peer_ssrc; /**< Peer SSRC. */ + pj_uint32_t received; /**< Number of received packets. */ +}; + +typedef struct pj_rtp_session pj_rtp_session; + +/** + * \brief Initialize RTP session. + * This function will initialize the RTP session according to given parameters. + * + * @param ses The session. + * @param default_pt Default payload type. + * @param sender_ssrc SSRC used for outgoing packets. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses, + int default_pt, pj_uint32_t sender_ssrc ); + +/** + * \brief Encode outgoing RTP packet header. + * Create the RTP header based on arguments and current state of the RTP + * session. + * + * @param ses The session. + * @param pt Payload type. + * @param m Marker flag. + * @param payload_len Payload length in bytes. + * @param ts_len Timestamp length. + * @param rtphdr Upon return will point to RTP packet header. + * @param hdrlen Upon return will indicate the size of RTP packet header + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m, + int payload_len, int ts_len, + const void **rtphdr, int *hdrlen ); + +/** + * \brief Decode an incoming RTP packet. + * This function will decode incoming packet into RTP header and payload. + * The decode function is guaranteed to point the payload to the correct + * position regardless of any options present in the RTP packet. + * + * @param ses The session. + * @param pkt The received RTP packet. + * @param pkt_len The length of the packet. + * @param hdr Upon return will point to the location of the RTP header + * inside the packet. + * @param payload Upon return will point to the location of the + * payload inside the packet. + * @param payloadlen Upon return will indicate the size of the payload. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, + const void *pkt, int pkt_len, + const pj_rtp_hdr **hdr, + const void **payload, + unsigned *payloadlen); + +/** + * \brief Update RTP session with an incoming RTP packet. + * Call this function everytime + * an RTP packet is received to check whether the packet can be received and to + * let the RTP session performs its internal calculations. + * + * @param ses The session. + * @param hdr The RTP header of the incoming packet. + * + * @return zero if the packet is valid and can be processed, otherwise will + * return one of the error in #pj_rtp_error_t. + */ +PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, + const pj_rtp_hdr *hdr); + +/** +* \brief Internal. + * Internal function for sequence control, shared by RTCP implementation. + */ +void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); + +/** +* \brief Internal. + * Internal function for sequence control, shared by RTCP implementation. + */ +void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); + +/** +* \brief Internal. + * Internal function for sequence control, shared by RTCP implementation. + */ +int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_RTP_H__ */ diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c index 1fe2b7b2..42a1fd4c 100644 --- a/pjmedia/src/pjmedia/sdp.c +++ b/pjmedia/src/pjmedia/sdp.c @@ -1,949 +1,949 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/sdp.h>
-#include <pj/scanner.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-
-enum {
- SKIP_WS = 0,
- SYNTAX_ERROR = 1,
-};
-#define TOKEN "-.!%*_=`'~"
-#define NTP_OFFSET ((pj_uint32_t)2208988800)
-#define LOG_THIS "sdp"
-
-/*
- * Prototypes for line parser.
- */
-static void parse_version(pj_scanner *scanner);
-static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses);
-static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses);
-static void parse_generic_line(pj_scanner *scanner, pj_str_t *str);
-static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn);
-static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner);
-static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med);
-
-/*
- * Prototypes for attribute parsers.
- */
-static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner );
-
-
-/*
- * Prototypes for functions to print attribute.
- * All of them returns integer for the length printed, or -1 on error.
- */
-static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr,
- char *buf, int length);
-static int print_generic_string_attr(const pjsdp_attr_string *attr,
- char *buf, int length);
-static int print_generic_num_attr(const pjsdp_attr_num *attr,
- char *buf, int length);
-static int print_name_only_attr(const pjsdp_attr *attr,
- char *buf, int length);
-static int print_fmtp_attr(const pjsdp_fmtp_attr *attr,
- char *buf, int length);
-
-/*
- * Prototypes for cloning attributes.
- */
-static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-
-
-/*
- * Prototypes
- */
-static void init_sdp_parser(void);
-
-
-typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner);
-typedef int (*FPRINT)(const void *attr, char *buf, int length);
-typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs);
-
-/*
- * Array of functions to print attribute.
- */
-static struct attr_map_rec
-{
- pj_str_t name;
- FPARSE parse_attr;
- FPRINT print_attr;
- FCLONE clone;
-} attr_map[] =
-{
- {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr},
- {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
- {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
- {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr},
- {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}
-};
-
-/*
- * Scanner character specification.
- */
-static int is_initialized;
-static pj_char_spec cs_token;
-
-static void init_sdp_parser(void)
-{
- if (is_initialized == 0) {
- is_initialized = 1;
- if (is_initialized != 1) {
- return;
- }
- }
- pj_cs_add_alpha(cs_token);
- pj_cs_add_num(cs_token);
- pj_cs_add_str( cs_token, TOKEN);
-}
-
-static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap,
- char *buf, int len)
-{
- char *p = buf;
-
- if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) {
- return -1;
- }
-
- /* colon and payload type. */
- *p++ = ':';
- len = pj_utoa(rtpmap->payload_type, p);
- p += len;
-
- /* space, encoding name */
- *p++ = ' ';
- pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen);
- p += rtpmap->encoding_name.slen;
-
- /* slash, clock-rate. */
- *p++ = '/';
- len = pj_utoa(rtpmap->clock_rate, p);
- p += len;
-
- /* optionally add encoding parameter. */
- if (rtpmap->parameter.slen) {
- *p++ = '/';
- pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen);
- p += rtpmap->parameter.slen;
- }
-
- return p-buf;
-}
-
-static int print_generic_string_attr(const pjsdp_attr_string *attr,
- char *buf, int len)
-{
- char *p = buf;
-
- if (len < attr->value.slen + 4) {
- return -1;
- }
-
- /* colon and attribute value. */
- *p++ = ':';
- pj_memcpy(p, attr->value.ptr, attr->value.slen);
- p += attr->value.slen;
-
- return p-buf;
-}
-
-static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 10) {
- return -1;
- }
- *p++ = ':';
- return pj_utoa(attr->value, p);
-}
-
-static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len)
-{
- PJ_UNUSED_ARG(attr)
- PJ_UNUSED_ARG(buf)
- PJ_UNUSED_ARG(len)
- return 0;
-}
-
-static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 4+fmtp->format.slen+fmtp->param.slen) {
- return -1;
- }
-
- /* colon and format. */
- *p++ = ':';
- pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen);
- p += fmtp->format.slen;
-
- /* space and parameter. */
- *p++ = ' ';
- pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen);
- p += fmtp->param.slen;
-
- return p-buf;
-}
-
-
-static int print_attr(const pjsdp_attr *attr, char *buf, int len)
-{
- char *p = buf;
- struct attr_map_rec *desc = &attr_map[attr->type];
-
- if (len < 16) {
- return -1;
- }
-
- *p++ = 'a';
- *p++ = '=';
- pj_memcpy(p, desc->name.ptr, desc->name.slen);
- p += desc->name.slen;
-
- len = (*desc->print_attr)(attr, p, (buf+len)-p);
- if (len < 0) {
- return -1;
- }
- p += len;
- *p++ = '\r';
- *p++ = '\n';
- return p-buf;
-}
-
-static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p;
- pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- attr->payload_type = rhs->payload_type;
- if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL;
- attr->clock_rate = rhs->clock_rate;
- if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p;
- pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p;
- pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- attr->value = rhs->value;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs)
-{
- pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- return attr;
-}
-
-static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p;
- pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL;
- if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs)
-{
- struct attr_map_rec *desc;
-
- if (rhs->type >= PJSDP_END_OF_ATTR) {
- pj_assert(0);
- return NULL;
- }
-
- desc = &attr_map[rhs->type];
- return (*desc->clone) (pool, rhs);
-}
-
-PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type)
-{
- int i;
-
- for (i=0; i<count; ++i) {
- if (attr_array[i]->type == type)
- return attr_array[i];
- }
- return NULL;
-}
-
-static int print_connection_info( pjsdp_conn_info *c, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
- return -1;
- }
- *p++ = 'c';
- *p++ = '=';
- pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
- p += c->net_type.slen;
- *p++ = ' ';
- pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
- p += c->addr_type.slen;
- *p++ = ' ';
- pj_memcpy(p, c->addr.ptr, c->addr.slen);
- p += c->addr.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- return p-buf;
-}
-
-PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs)
-{
- pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (!c) return NULL;
-
- if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
- if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
- if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
-
- return c;
-}
-
-static int print_media_desc( pjsdp_media_desc *m, char *buf, int len)
-{
- char *p = buf;
- char *end = buf+len;
- unsigned i;
- int printed;
-
- /* check length for the "m=" line. */
- if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
- return -1;
- }
- *p++ = 'm'; /* m= */
- *p++ = '=';
- pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
- p += m->desc.media.slen;
- *p++ = ' ';
- printed = pj_utoa(m->desc.port, p);
- p += printed;
- if (m->desc.port_count > 1) {
- *p++ = '/';
- printed = pj_utoa(m->desc.port_count, p);
- p += printed;
- }
- *p++ = ' ';
- pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
- p += m->desc.transport.slen;
- for (i=0; i<m->desc.fmt_count; ++i) {
- *p++ = ' ';
- pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
- p += m->desc.fmt[i].slen;
- }
- *p++ = '\r';
- *p++ = '\n';
-
- /* print connection info, if present. */
- if (m->conn) {
- printed = print_connection_info(m->conn, p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- /* print attributes. */
- for (i=0; i<m->attr_count; ++i) {
- printed = print_attr(m->attr[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- return p-buf;
-}
-
-PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
- const pjsdp_media_desc *rhs)
-{
- unsigned int i;
- pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc));
- if (!m)
- return NULL;
-
- pj_strdup (pool, &m->desc.media, &rhs->desc.media);
- m->desc.port = rhs->desc.port;
- m->desc.port_count = rhs->desc.port_count;
- pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
- m->desc.fmt_count = rhs->desc.fmt_count;
- for (i=0; i<rhs->desc.fmt_count; ++i)
- m->desc.fmt[i] = rhs->desc.fmt[i];
-
- if (rhs->conn) {
- m->conn = pjsdp_conn_info_clone (pool, rhs->conn);
- if (!m->conn)
- return NULL;
- } else {
- m->conn = NULL;
- }
-
- m->attr_count = rhs->attr_count;
- for (i=0; i < rhs->attr_count; ++i) {
- m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]);
- if (!m->attr[i])
- return NULL;
- }
-
- return m;
-}
-
-/** Check if the media description has the specified attribute. */
-PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
- pjsdp_attr_type_e attr_type)
-{
- unsigned i;
- for (i=0; i<m->attr_count; ++i) {
- pjsdp_attr *attr = m->attr[i];
- if (attr->type == attr_type)
- return 1;
- }
- return 0;
-}
-
-/** Find rtpmap attribute for the specified payload type. */
-PJ_DEF(const pjsdp_rtpmap_attr*)
-pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt)
-{
- unsigned i;
- for (i=0; i<m->attr_count; ++i) {
- pjsdp_attr *attr = m->attr[i];
- if (attr->type == PJSDP_ATTR_RTPMAP) {
- const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr;
- if (rtpmap->payload_type == pt)
- return rtpmap;
- }
- }
- return NULL;
-}
-
-
-static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len)
-{
- char *p = buf;
- char *end = buf+len;
- unsigned i;
- int printed;
-
- /* Check length for v= and o= lines. */
- if (len < 5+
- 2+ses->origin.user.slen+18+
- ses->origin.net_type.slen+ses->origin.addr.slen + 2)
- {
- return -1;
- }
-
- /* SDP version (v= line) */
- pj_memcpy(p, "v=0\r\n", 5);
- p += 5;
-
- /* Owner (o=) line. */
- *p++ = 'o';
- *p++ = '=';
- pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
- p += ses->origin.user.slen;
- *p++ = ' ';
- printed = pj_utoa(ses->origin.id, p);
- p += printed;
- *p++ = ' ';
- printed = pj_utoa(ses->origin.version, p);
- p += printed;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
- p += ses->origin.net_type.slen;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
- p += ses->origin.addr_type.slen;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
- p += ses->origin.addr.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Session name (s=) line. */
- if ((end-p) < 8+ses->name.slen) {
- return -1;
- }
- *p++ = 's';
- *p++ = '=';
- pj_memcpy(p, ses->name.ptr, ses->name.slen);
- p += ses->name.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Time */
- if ((end-p) < 24) {
- return -1;
- }
- *p++ = 't';
- *p++ = '=';
- printed = pj_utoa(ses->time.start, p);
- p += printed;
- *p++ = ' ';
- printed = pj_utoa(ses->time.stop, p);
- p += printed;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Connection line (c=) if exist. */
- if (ses->conn) {
- printed = print_connection_info(ses->conn, p, end-p);
- if (printed < 1) {
- return -1;
- }
- p += printed;
- }
-
- /* Print all attribute (a=) lines. */
- for (i=0; i<ses->attr_count; ++i) {
- printed = print_attr(ses->attr[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- /* Print media (m=) lines. */
- for (i=0; i<ses->media_count; ++i) {
- printed = print_media_desc(ses->media[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- return p-buf;
-}
-
-/******************************************************************************
- * PARSERS
- */
-
-static void parse_version(pj_scanner *scanner)
-{
- pj_scan_advance_n(scanner, 3, SKIP_WS);
- pj_scan_get_newline(scanner);
-}
-
-static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses)
-{
- pj_str_t str;
-
- /* o= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* username. */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
- pj_scan_get_char(scanner);
-
- /* id */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->origin.id = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- /* version */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->origin.version = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- /* network-type */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
- pj_scan_get_char(scanner);
-
- /* addr-type */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
- pj_scan_get_char(scanner);
-
- /* address */
- pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses)
-{
- pj_str_t str;
-
- /* t= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* start time */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->time.start = pj_strtoul(&str);
-
- pj_scan_get_char(scanner);
-
- /* stop time */
- pj_scan_get_until_ch(scanner, '\r', &str);
- ses->time.stop = pj_strtoul(&str);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_generic_line(pj_scanner *scanner, pj_str_t *str)
-{
- /* x= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* get anything until newline. */
- pj_scan_get_until_ch(scanner, '\r', str);
-
- /* newline. */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn)
-{
- /* c= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* network-type */
- pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
- pj_scan_get_char(scanner);
-
- /* addr-type */
- pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
- pj_scan_get_char(scanner);
-
- /* address. */
- pj_scan_get_until_ch(scanner, '\r', &conn->addr);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med)
-{
- pj_str_t str;
-
- /* m= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* type */
- pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
- pj_scan_get_char(scanner);
-
- /* port */
- pj_scan_get(scanner, cs_token, &str);
- med->desc.port = (unsigned short)pj_strtoul(&str);
- if (*scanner->current == '/') {
- /* port count */
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &str);
- med->desc.port_count = pj_strtoul(&str);
-
- } else {
- med->desc.port_count = 0;
- }
-
- if (pj_scan_get_char(scanner) != ' ') {
- PJ_THROW(SYNTAX_ERROR);
- }
-
- /* transport */
- pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
-
- /* format list */
- med->desc.fmt_count = 0;
- while (*scanner->current == ' ') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]);
- }
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_rtpmap_attr *rtpmap;
- pj_str_t str;
-
- rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap));
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, ' ', &str);
- rtpmap->payload_type = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name);
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &str);
- rtpmap->clock_rate = pj_strtoul(&str);
-
- if (*scanner->current == '/') {
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter);
- }
-
- return rtpmap;
-}
-
-static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr_string *attr;
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, '\r', &attr->value);
- return attr;
-}
-
-static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr_num *attr;
- pj_str_t str;
-
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, '\r', &str);
- attr->value = pj_strtoul(&str);
- return attr;
-}
-
-static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr *attr;
-
- PJ_UNUSED_ARG(scanner)
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
- return attr;
-}
-
-static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_fmtp_attr *fmtp;
-
- fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, ' ', &fmtp->format);
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch(scanner, '\r', &fmtp->param);
- return fmtp;
-}
-
-static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner)
-{
- void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL;
- pj_str_t attrname;
- unsigned i;
- pjsdp_attr *attr;
-
- /* skip a= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* get attr name. */
- pj_scan_get(scanner, cs_token, &attrname);
-
- /* find entry to handle attrname */
- for (i=0; i<PJ_ARRAY_SIZE(attr_map); ++i) {
- struct attr_map_rec *p = &attr_map[i];
- if (pj_strcmp(&attrname, &p->name) == 0) {
- parse_func = p->parse_attr;
- break;
- }
- }
-
- /* fallback to generic string parser. */
- if (parse_func == NULL) {
- parse_func = &parse_generic_string_attr;
- }
-
- attr = (*parse_func)(pool, scanner);
- attr->type = i;
-
- /* newline */
- pj_scan_get_newline(scanner);
-
- return attr;
-}
-
-static void on_scanner_error(pj_scanner *scanner)
-{
- PJ_UNUSED_ARG(scanner)
-
- PJ_THROW(SYNTAX_ERROR);
-}
-
-/*
- * Parse SDP message.
- */
-PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
- pj_pool_t *pool)
-{
- pj_scanner scanner;
- pjsdp_session_desc *session;
- pjsdp_media_desc *media = NULL;
- void *attr;
- pjsdp_conn_info *conn;
- pj_str_t dummy;
- int cur_name = 254;
- PJ_USE_EXCEPTION;
-
- init_sdp_parser();
-
- pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
- session = pj_pool_calloc(pool, 1, sizeof(*session));
-
- PJ_TRY {
- while (!pj_scan_is_eof(&scanner)) {
- cur_name = *scanner.current;
- switch (cur_name) {
- case 'a':
- attr = parse_attr(pool, &scanner);
- if (attr) {
- if (media) {
- media->attr[media->attr_count++] = attr;
- } else {
- session->attr[session->attr_count++] = attr;
- }
- }
- break;
- case 'o':
- parse_origin(&scanner, session);
- break;
- case 's':
- parse_generic_line(&scanner, &session->name);
- break;
- case 'c':
- conn = pj_pool_calloc(pool, 1, sizeof(*conn));
- parse_connection_info(&scanner, conn);
- if (media) {
- media->conn = conn;
- } else {
- session->conn = conn;
- }
- break;
- case 't':
- parse_time(&scanner, session);
- break;
- case 'm':
- media = pj_pool_calloc(pool, 1, sizeof(*media));
- parse_media(&scanner, media);
- session->media[ session->media_count++ ] = media;
- break;
- case 'v':
- parse_version(&scanner);
- break;
- default:
- parse_generic_line(&scanner, &dummy);
- break;
- }
- }
- }
- PJ_CATCH(SYNTAX_ERROR) {
- PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d",
- cur_name, scanner.line, scanner.col));
- if (!pj_scan_is_eof(&scanner)) {
- if (*scanner.current != '\r') {
- pj_scan_get_until_ch(&scanner, '\r', &dummy);
- }
- pj_scan_get_newline(&scanner);
- }
- }
- PJ_END;
-
- pj_scan_fini(&scanner);
- return session;
-}
-
-/*
- * Print SDP description.
- */
-PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size)
-{
- return print_session(desc, buf, size);
-}
-
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/sdp.h> +#include <pj/scanner.h> +#include <pj/except.h> +#include <pj/log.h> +#include <pj/os.h> +#include <pj/string.h> +#include <pj/pool.h> + +enum { + SKIP_WS = 0, + SYNTAX_ERROR = 1, +}; +#define TOKEN "-.!%*_=`'~" +#define NTP_OFFSET ((pj_uint32_t)2208988800) +#define LOG_THIS "sdp" + +/* + * Prototypes for line parser. + */ +static void parse_version(pj_scanner *scanner); +static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses); +static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses); +static void parse_generic_line(pj_scanner *scanner, pj_str_t *str); +static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn); +static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner); +static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med); + +/* + * Prototypes for attribute parsers. + */ +static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner ); + + +/* + * Prototypes for functions to print attribute. + * All of them returns integer for the length printed, or -1 on error. + */ +static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr, + char *buf, int length); +static int print_generic_string_attr(const pjsdp_attr_string *attr, + char *buf, int length); +static int print_generic_num_attr(const pjsdp_attr_num *attr, + char *buf, int length); +static int print_name_only_attr(const pjsdp_attr *attr, + char *buf, int length); +static int print_fmtp_attr(const pjsdp_fmtp_attr *attr, + char *buf, int length); + +/* + * Prototypes for cloning attributes. + */ +static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs); + + +/* + * Prototypes + */ +static void init_sdp_parser(void); + + +typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner); +typedef int (*FPRINT)(const void *attr, char *buf, int length); +typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs); + +/* + * Array of functions to print attribute. + */ +static struct attr_map_rec +{ + pj_str_t name; + FPARSE parse_attr; + FPRINT print_attr; + FCLONE clone; +} attr_map[] = +{ + {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr}, + {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr}, + {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr}, + {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr}, + {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr} +}; + +/* + * Scanner character specification. + */ +static int is_initialized; +static pj_char_spec cs_token; + +static void init_sdp_parser(void) +{ + if (is_initialized == 0) { + is_initialized = 1; + if (is_initialized != 1) { + return; + } + } + pj_cs_add_alpha(cs_token); + pj_cs_add_num(cs_token); + pj_cs_add_str( cs_token, TOKEN); +} + +static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap, + char *buf, int len) +{ + char *p = buf; + + if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) { + return -1; + } + + /* colon and payload type. */ + *p++ = ':'; + len = pj_utoa(rtpmap->payload_type, p); + p += len; + + /* space, encoding name */ + *p++ = ' '; + pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen); + p += rtpmap->encoding_name.slen; + + /* slash, clock-rate. */ + *p++ = '/'; + len = pj_utoa(rtpmap->clock_rate, p); + p += len; + + /* optionally add encoding parameter. */ + if (rtpmap->parameter.slen) { + *p++ = '/'; + pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen); + p += rtpmap->parameter.slen; + } + + return p-buf; +} + +static int print_generic_string_attr(const pjsdp_attr_string *attr, + char *buf, int len) +{ + char *p = buf; + + if (len < attr->value.slen + 4) { + return -1; + } + + /* colon and attribute value. */ + *p++ = ':'; + pj_memcpy(p, attr->value.ptr, attr->value.slen); + p += attr->value.slen; + + return p-buf; +} + +static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len) +{ + char *p = buf; + + if (len < 10) { + return -1; + } + *p++ = ':'; + return pj_utoa(attr->value, p); +} + +static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len) +{ + PJ_UNUSED_ARG(attr) + PJ_UNUSED_ARG(buf) + PJ_UNUSED_ARG(len) + return 0; +} + +static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len) +{ + char *p = buf; + + if (len < 4+fmtp->format.slen+fmtp->param.slen) { + return -1; + } + + /* colon and format. */ + *p++ = ':'; + pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen); + p += fmtp->format.slen; + + /* space and parameter. */ + *p++ = ' '; + pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen); + p += fmtp->param.slen; + + return p-buf; +} + + +static int print_attr(const pjsdp_attr *attr, char *buf, int len) +{ + char *p = buf; + struct attr_map_rec *desc = &attr_map[attr->type]; + + if (len < 16) { + return -1; + } + + *p++ = 'a'; + *p++ = '='; + pj_memcpy(p, desc->name.ptr, desc->name.slen); + p += desc->name.slen; + + len = (*desc->print_attr)(attr, p, (buf+len)-p); + if (len < 0) { + return -1; + } + p += len; + *p++ = '\r'; + *p++ = '\n'; + return p-buf; +} + +static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p; + pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr)); + if (!attr) + return NULL; + + attr->type = rhs->type; + attr->payload_type = rhs->payload_type; + if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL; + attr->clock_rate = rhs->clock_rate; + if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL; + + return (pjsdp_attr*)attr; +} + +static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p; + pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string)); + if (!attr) + return NULL; + + attr->type = rhs->type; + if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL; + + return (pjsdp_attr*)attr; +} + +static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p; + pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num)); + if (!attr) + return NULL; + + attr->type = rhs->type; + attr->value = rhs->value; + + return (pjsdp_attr*)attr; +} + +static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs) +{ + pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr)); + if (!attr) + return NULL; + + attr->type = rhs->type; + return attr; +} + +static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p; + pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr)); + if (!attr) + return NULL; + + attr->type = rhs->type; + if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL; + if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL; + + return (pjsdp_attr*)attr; +} + +PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs) +{ + struct attr_map_rec *desc; + + if (rhs->type >= PJSDP_END_OF_ATTR) { + pj_assert(0); + return NULL; + } + + desc = &attr_map[rhs->type]; + return (*desc->clone) (pool, rhs); +} + +PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type) +{ + int i; + + for (i=0; i<count; ++i) { + if (attr_array[i]->type == type) + return attr_array[i]; + } + return NULL; +} + +static int print_connection_info( pjsdp_conn_info *c, char *buf, int len) +{ + char *p = buf; + + if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) { + return -1; + } + *p++ = 'c'; + *p++ = '='; + pj_memcpy(p, c->net_type.ptr, c->net_type.slen); + p += c->net_type.slen; + *p++ = ' '; + pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen); + p += c->addr_type.slen; + *p++ = ' '; + pj_memcpy(p, c->addr.ptr, c->addr.slen); + p += c->addr.slen; + *p++ = '\r'; + *p++ = '\n'; + + return p-buf; +} + +PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs) +{ + pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); + if (!c) return NULL; + + if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL; + if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL; + if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL; + + return c; +} + +static int print_media_desc( pjsdp_media_desc *m, char *buf, int len) +{ + char *p = buf; + char *end = buf+len; + unsigned i; + int printed; + + /* check length for the "m=" line. */ + if (len < m->desc.media.slen+m->desc.transport.slen+12+24) { + return -1; + } + *p++ = 'm'; /* m= */ + *p++ = '='; + pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen); + p += m->desc.media.slen; + *p++ = ' '; + printed = pj_utoa(m->desc.port, p); + p += printed; + if (m->desc.port_count > 1) { + *p++ = '/'; + printed = pj_utoa(m->desc.port_count, p); + p += printed; + } + *p++ = ' '; + pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen); + p += m->desc.transport.slen; + for (i=0; i<m->desc.fmt_count; ++i) { + *p++ = ' '; + pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen); + p += m->desc.fmt[i].slen; + } + *p++ = '\r'; + *p++ = '\n'; + + /* print connection info, if present. */ + if (m->conn) { + printed = print_connection_info(m->conn, p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + /* print attributes. */ + for (i=0; i<m->attr_count; ++i) { + printed = print_attr(m->attr[i], p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + return p-buf; +} + +PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, + const pjsdp_media_desc *rhs) +{ + unsigned int i; + pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc)); + if (!m) + return NULL; + + pj_strdup (pool, &m->desc.media, &rhs->desc.media); + m->desc.port = rhs->desc.port; + m->desc.port_count = rhs->desc.port_count; + pj_strdup (pool, &m->desc.transport, &rhs->desc.transport); + m->desc.fmt_count = rhs->desc.fmt_count; + for (i=0; i<rhs->desc.fmt_count; ++i) + m->desc.fmt[i] = rhs->desc.fmt[i]; + + if (rhs->conn) { + m->conn = pjsdp_conn_info_clone (pool, rhs->conn); + if (!m->conn) + return NULL; + } else { + m->conn = NULL; + } + + m->attr_count = rhs->attr_count; + for (i=0; i < rhs->attr_count; ++i) { + m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]); + if (!m->attr[i]) + return NULL; + } + + return m; +} + +/** Check if the media description has the specified attribute. */ +PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, + pjsdp_attr_type_e attr_type) +{ + unsigned i; + for (i=0; i<m->attr_count; ++i) { + pjsdp_attr *attr = m->attr[i]; + if (attr->type == attr_type) + return 1; + } + return 0; +} + +/** Find rtpmap attribute for the specified payload type. */ +PJ_DEF(const pjsdp_rtpmap_attr*) +pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt) +{ + unsigned i; + for (i=0; i<m->attr_count; ++i) { + pjsdp_attr *attr = m->attr[i]; + if (attr->type == PJSDP_ATTR_RTPMAP) { + const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr; + if (rtpmap->payload_type == pt) + return rtpmap; + } + } + return NULL; +} + + +static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len) +{ + char *p = buf; + char *end = buf+len; + unsigned i; + int printed; + + /* Check length for v= and o= lines. */ + if (len < 5+ + 2+ses->origin.user.slen+18+ + ses->origin.net_type.slen+ses->origin.addr.slen + 2) + { + return -1; + } + + /* SDP version (v= line) */ + pj_memcpy(p, "v=0\r\n", 5); + p += 5; + + /* Owner (o=) line. */ + *p++ = 'o'; + *p++ = '='; + pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen); + p += ses->origin.user.slen; + *p++ = ' '; + printed = pj_utoa(ses->origin.id, p); + p += printed; + *p++ = ' '; + printed = pj_utoa(ses->origin.version, p); + p += printed; + *p++ = ' '; + pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen); + p += ses->origin.net_type.slen; + *p++ = ' '; + pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen); + p += ses->origin.addr_type.slen; + *p++ = ' '; + pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen); + p += ses->origin.addr.slen; + *p++ = '\r'; + *p++ = '\n'; + + /* Session name (s=) line. */ + if ((end-p) < 8+ses->name.slen) { + return -1; + } + *p++ = 's'; + *p++ = '='; + pj_memcpy(p, ses->name.ptr, ses->name.slen); + p += ses->name.slen; + *p++ = '\r'; + *p++ = '\n'; + + /* Time */ + if ((end-p) < 24) { + return -1; + } + *p++ = 't'; + *p++ = '='; + printed = pj_utoa(ses->time.start, p); + p += printed; + *p++ = ' '; + printed = pj_utoa(ses->time.stop, p); + p += printed; + *p++ = '\r'; + *p++ = '\n'; + + /* Connection line (c=) if exist. */ + if (ses->conn) { + printed = print_connection_info(ses->conn, p, end-p); + if (printed < 1) { + return -1; + } + p += printed; + } + + /* Print all attribute (a=) lines. */ + for (i=0; i<ses->attr_count; ++i) { + printed = print_attr(ses->attr[i], p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + /* Print media (m=) lines. */ + for (i=0; i<ses->media_count; ++i) { + printed = print_media_desc(ses->media[i], p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + return p-buf; +} + +/****************************************************************************** + * PARSERS + */ + +static void parse_version(pj_scanner *scanner) +{ + pj_scan_advance_n(scanner, 3, SKIP_WS); + pj_scan_get_newline(scanner); +} + +static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses) +{ + pj_str_t str; + + /* o= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* username. */ + pj_scan_get_until_ch(scanner, ' ', &ses->origin.user); + pj_scan_get_char(scanner); + + /* id */ + pj_scan_get_until_ch(scanner, ' ', &str); + ses->origin.id = pj_strtoul(&str); + pj_scan_get_char(scanner); + + /* version */ + pj_scan_get_until_ch(scanner, ' ', &str); + ses->origin.version = pj_strtoul(&str); + pj_scan_get_char(scanner); + + /* network-type */ + pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type); + pj_scan_get_char(scanner); + + /* addr-type */ + pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type); + pj_scan_get_char(scanner); + + /* address */ + pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr); + + /* newline */ + pj_scan_get_newline(scanner); +} + +static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses) +{ + pj_str_t str; + + /* t= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* start time */ + pj_scan_get_until_ch(scanner, ' ', &str); + ses->time.start = pj_strtoul(&str); + + pj_scan_get_char(scanner); + + /* stop time */ + pj_scan_get_until_ch(scanner, '\r', &str); + ses->time.stop = pj_strtoul(&str); + + /* newline */ + pj_scan_get_newline(scanner); +} + +static void parse_generic_line(pj_scanner *scanner, pj_str_t *str) +{ + /* x= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* get anything until newline. */ + pj_scan_get_until_ch(scanner, '\r', str); + + /* newline. */ + pj_scan_get_newline(scanner); +} + +static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn) +{ + /* c= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* network-type */ + pj_scan_get_until_ch(scanner, ' ', &conn->net_type); + pj_scan_get_char(scanner); + + /* addr-type */ + pj_scan_get_until_ch(scanner, ' ', &conn->addr_type); + pj_scan_get_char(scanner); + + /* address. */ + pj_scan_get_until_ch(scanner, '\r', &conn->addr); + + /* newline */ + pj_scan_get_newline(scanner); +} + +static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med) +{ + pj_str_t str; + + /* m= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* type */ + pj_scan_get_until_ch(scanner, ' ', &med->desc.media); + pj_scan_get_char(scanner); + + /* port */ + pj_scan_get(scanner, cs_token, &str); + med->desc.port = (unsigned short)pj_strtoul(&str); + if (*scanner->current == '/') { + /* port count */ + pj_scan_get_char(scanner); + pj_scan_get(scanner, cs_token, &str); + med->desc.port_count = pj_strtoul(&str); + + } else { + med->desc.port_count = 0; + } + + if (pj_scan_get_char(scanner) != ' ') { + PJ_THROW(SYNTAX_ERROR); + } + + /* transport */ + pj_scan_get_until_ch(scanner, ' ', &med->desc.transport); + + /* format list */ + med->desc.fmt_count = 0; + while (*scanner->current == ' ') { + pj_scan_get_char(scanner); + pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]); + } + + /* newline */ + pj_scan_get_newline(scanner); +} + +static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_rtpmap_attr *rtpmap; + pj_str_t str; + + rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap)); + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, ' ', &str); + rtpmap->payload_type = pj_strtoul(&str); + pj_scan_get_char(scanner); + + pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name); + pj_scan_get_char(scanner); + pj_scan_get(scanner, cs_token, &str); + rtpmap->clock_rate = pj_strtoul(&str); + + if (*scanner->current == '/') { + pj_scan_get_char(scanner); + pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter); + } + + return rtpmap; +} + +static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_attr_string *attr; + attr = pj_pool_calloc(pool, 1, sizeof(*attr)); + + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, '\r', &attr->value); + return attr; +} + +static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_attr_num *attr; + pj_str_t str; + + attr = pj_pool_calloc(pool, 1, sizeof(*attr)); + + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, '\r', &str); + attr->value = pj_strtoul(&str); + return attr; +} + +static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_attr *attr; + + PJ_UNUSED_ARG(scanner) + attr = pj_pool_calloc(pool, 1, sizeof(*attr)); + return attr; +} + +static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_fmtp_attr *fmtp; + + fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp)); + + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, ' ', &fmtp->format); + pj_scan_get_char(scanner); + pj_scan_get_until_ch(scanner, '\r', &fmtp->param); + return fmtp; +} + +static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner) +{ + void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL; + pj_str_t attrname; + unsigned i; + pjsdp_attr *attr; + + /* skip a= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* get attr name. */ + pj_scan_get(scanner, cs_token, &attrname); + + /* find entry to handle attrname */ + for (i=0; i<PJ_ARRAY_SIZE(attr_map); ++i) { + struct attr_map_rec *p = &attr_map[i]; + if (pj_strcmp(&attrname, &p->name) == 0) { + parse_func = p->parse_attr; + break; + } + } + + /* fallback to generic string parser. */ + if (parse_func == NULL) { + parse_func = &parse_generic_string_attr; + } + + attr = (*parse_func)(pool, scanner); + attr->type = i; + + /* newline */ + pj_scan_get_newline(scanner); + + return attr; +} + +static void on_scanner_error(pj_scanner *scanner) +{ + PJ_UNUSED_ARG(scanner) + + PJ_THROW(SYNTAX_ERROR); +} + +/* + * Parse SDP message. + */ +PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, + pj_pool_t *pool) +{ + pj_scanner scanner; + pjsdp_session_desc *session; + pjsdp_media_desc *media = NULL; + void *attr; + pjsdp_conn_info *conn; + pj_str_t dummy; + int cur_name = 254; + PJ_USE_EXCEPTION; + + init_sdp_parser(); + + pj_scan_init(&scanner, buf, len, 0, &on_scanner_error); + session = pj_pool_calloc(pool, 1, sizeof(*session)); + + PJ_TRY { + while (!pj_scan_is_eof(&scanner)) { + cur_name = *scanner.current; + switch (cur_name) { + case 'a': + attr = parse_attr(pool, &scanner); + if (attr) { + if (media) { + media->attr[media->attr_count++] = attr; + } else { + session->attr[session->attr_count++] = attr; + } + } + break; + case 'o': + parse_origin(&scanner, session); + break; + case 's': + parse_generic_line(&scanner, &session->name); + break; + case 'c': + conn = pj_pool_calloc(pool, 1, sizeof(*conn)); + parse_connection_info(&scanner, conn); + if (media) { + media->conn = conn; + } else { + session->conn = conn; + } + break; + case 't': + parse_time(&scanner, session); + break; + case 'm': + media = pj_pool_calloc(pool, 1, sizeof(*media)); + parse_media(&scanner, media); + session->media[ session->media_count++ ] = media; + break; + case 'v': + parse_version(&scanner); + break; + default: + parse_generic_line(&scanner, &dummy); + break; + } + } + } + PJ_CATCH(SYNTAX_ERROR) { + PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d", + cur_name, scanner.line, scanner.col)); + if (!pj_scan_is_eof(&scanner)) { + if (*scanner.current != '\r') { + pj_scan_get_until_ch(&scanner, '\r', &dummy); + } + pj_scan_get_newline(&scanner); + } + } + PJ_END; + + pj_scan_fini(&scanner); + return session; +} + +/* + * Print SDP description. + */ +PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size) +{ + return print_session(desc, buf, size); +} + + diff --git a/pjmedia/src/pjmedia/sdp.h b/pjmedia/src/pjmedia/sdp.h index 0adfe7f4..1b45744c 100644 --- a/pjmedia/src/pjmedia/sdp.h +++ b/pjmedia/src/pjmedia/sdp.h @@ -1,332 +1,332 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSDP_SDP_H__
-#define __PJSDP_SDP_H__
-
-/**
- * @defgroup PJSDP SDP Library
- */
-/**
- * @file sdp.h
- * @brief SDP header file.
- */
-/**
- * @defgroup PJ_SDP_H SDP stack.
- * @ingroup PJSDP
- * @{
- *
- * This SDP module consists of SDP parser, SDP structure, and function to
- * print back the structure as SDP message.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-#define PJSDP_MAX_FMT 32
-#define PJSDP_MAX_ATTR 32
-#define PJSDP_MAX_MEDIA 16
-
-/**
- * This enumeration describes the attribute type.
- */
-typedef enum pjsdp_attr_type_e
-{
- PJSDP_ATTR_RTPMAP,
- PJSDP_ATTR_CAT,
- PJSDP_ATTR_KEYWORDS,
- PJSDP_ATTR_TOOL,
- PJSDP_ATTR_PTIME,
- PJSDP_ATTR_RECV_ONLY,
- PJSDP_ATTR_SEND_ONLY,
- PJSDP_ATTR_SEND_RECV,
- PJSDP_ATTR_ORIENT,
- PJSDP_ATTR_TYPE,
- PJSDP_ATTR_CHARSET,
- PJSDP_ATTR_SDP_LANG,
- PJSDP_ATTR_LANG,
- PJSDP_ATTR_FRAME_RATE,
- PJSDP_ATTR_QUALITY,
- PJSDP_ATTR_FMTP,
- PJSDP_ATTR_INACTIVE,
- PJSDP_ATTR_GENERIC,
- PJSDP_END_OF_ATTR,
-} pjsdp_attr_type_e;
-
-
-/**
- * This structure keeps the common attributes that all 'descendants'
- * will have.
- */
-typedef struct pjsdp_attr
-{
- pjsdp_attr_type_e type; /**< Attribute type. */
-} pjsdp_attr;
-
-
-/**
- * This is the structure to represent generic attribute which has a
- * string value.
- */
-typedef struct pjsdp_attr_string
-{
- pjsdp_attr_type_e type;
- pj_str_t value;
-} pjsdp_attr_string;
-
-
-/**
- * This is the structure to represent generic SDP attribute which has
- * a numeric value.
- */
-typedef struct pjsdp_attr_num
-{
- pjsdp_attr_type_e type;
- pj_uint32_t value;
-} pjsdp_attr_num;
-
-
-/**
- * SDP \a rtpmap attribute.
- */
-typedef struct pjsdp_rtpmap_attr
-{
- pjsdp_attr_type_e type;
- unsigned payload_type;
- pj_str_t encoding_name;
- unsigned clock_rate;
- pj_str_t parameter;
-} pjsdp_rtpmap_attr;
-
-
-/**
- * SDP \a fmtp attribute.
- */
-typedef struct pjsdp_fmtp_attr
-{
- pjsdp_attr_type_e type;
- pj_str_t format;
- pj_str_t param;
-} pjsdp_fmtp_attr;
-
-
-/**
- * SDP generic attribute.
- */
-typedef struct pjsdp_generic_attr
-{
- pjsdp_attr_type_e type;
- pj_str_t name;
- pj_str_t value;
-} pjsdp_generic_attr;
-
-
-/** SDP \a cat attribute. */
-typedef struct pjsdp_attr_string pjsdp_cat_attr;
-
-/** SDP \a keywds attribute. */
-typedef struct pjsdp_attr_string pjsdp_keywds_attr;
-
-/** SDP \a tool attribute. */
-typedef struct pjsdp_attr_string pjsdp_tool_attr;
-
-/** SDP \a ptime attribute. */
-typedef struct pjsdp_attr_num pjsdp_ptime_attr;
-
-/** SDP \a recvonly attribute. */
-typedef struct pjsdp_attr pjsdp_recv_only_attr;
-
-/** SDP \a sendonly attribute. */
-typedef struct pjsdp_attr pjsdp_send_only_attr;
-
-/** SDP \a sendrecv attribute. */
-typedef struct pjsdp_attr pjsdp_send_recv_attr;
-
-/** SDP \a orient attribute. */
-typedef struct pjsdp_attr_string pjsdp_orient_attr;
-
-/** SDP \a type attribute. */
-typedef struct pjsdp_attr_string pjsdp_type_attr;
-
-/** SDP \a charset attribute. */
-typedef struct pjsdp_attr_string pjsdp_charset_attr;
-
-/** SDP \a sdplang attribute. */
-typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr;
-
-/** SDP \a lang attribute. */
-typedef struct pjsdp_attr_string pjsdp_lang_attr;
-
-/** SDP \a framerate attribute. */
-typedef struct pjsdp_attr_string pjsdp_frame_rate_attr;
-
-/** SDP \a quality attribute. */
-typedef struct pjsdp_attr_num pjsdp_quality_attr;
-
-/** SDP \a inactive attribute. */
-typedef struct pjsdp_attr pjsdp_inactive_attr;
-
-/** Clone attribute */
-PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs);
-
-/** Find attribute */
-PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type);
-
-/**
- * SDP connection info.
- */
-typedef struct pjsdp_conn_info
-{
- pj_str_t net_type;
- pj_str_t addr_type;
- pj_str_t addr;
-} pjsdp_conn_info;
-
-/**
- *Clone connection info.
- *
- * @param pool Pool to allocate memory for the new connection info.
- * @param rhs The connection into to clone.
- *
- * @return the new connection info.
- */
-PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool,
- const pjsdp_conn_info *rhs);
-
-/**
- * SDP media description.
- */
-typedef struct pjsdp_media_desc
-{
- struct
- {
- pj_str_t media;
- pj_uint16_t port;
- unsigned port_count;
- pj_str_t transport;
- unsigned fmt_count;
- pj_str_t fmt[PJSDP_MAX_FMT];
- } desc;
-
- pjsdp_conn_info *conn;
- unsigned attr_count;
- pjsdp_attr *attr[PJSDP_MAX_ATTR];
-
-} pjsdp_media_desc;
-
-/**
- * Clone SDP media description.
- *
- * @param pool Pool to allocate memory for the new media description.
- * @param rhs The media descriptin to clone.
- *
- * @return a new media description.
- */
-PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
- const pjsdp_media_desc *rhs);
-
-/**
- * Check if the media description has the specified attribute.
- *
- * @param m The SDP media description.
- * @param attr_type The attribute type.
- *
- * @return nonzero if true.
- */
-PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
- pjsdp_attr_type_e attr_type);
-
-/**
- * Find rtpmap attribute for the specified payload type.
- *
- * @param m The SDP media description.
- * @param pt RTP payload type.
- *
- * @return the SDP rtpmap attribute for the payload type, or NULL if not found.
- */
-PJ_DECL(const pjsdp_rtpmap_attr*)
-pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt);
-
-
-/**
- * This structure describes SDP session description.
- */
-typedef struct pjsdp_session_desc
-{
- struct
- {
- pj_str_t user;
- pj_uint32_t id;
- pj_uint32_t version;
- pj_str_t net_type;
- pj_str_t addr_type;
- pj_str_t addr;
- } origin;
-
- pj_str_t name;
- pjsdp_conn_info *conn;
-
- struct
- {
- pj_uint32_t start;
- pj_uint32_t stop;
- } time;
-
- unsigned attr_count;
- pjsdp_attr *attr[PJSDP_MAX_ATTR];
-
- unsigned media_count;
- pjsdp_media_desc *media[PJSDP_MAX_MEDIA];
-
-} pjsdp_session_desc;
-
-
-/**
- * Parse SDP message.
- *
- * @param buf The message buffer.
- * @param len The length of the message.
- * @param pool The pool to allocate SDP session description.
- *
- * @return SDP session description.
- */
-PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
- pj_pool_t *pool);
-
-/**
- * Print SDP description to a buffer.
- *
- * @param buf The buffer.
- * @param size The buffer length.
- * @param desc The SDP session description.
- *
- * @return the length printed, or -1.
- */
-PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc,
- char *buf, pj_size_t size);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSDP_SDP_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSDP_SDP_H__ +#define __PJSDP_SDP_H__ + +/** + * @defgroup PJSDP SDP Library + */ +/** + * @file sdp.h + * @brief SDP header file. + */ +/** + * @defgroup PJ_SDP_H SDP stack. + * @ingroup PJSDP + * @{ + * + * This SDP module consists of SDP parser, SDP structure, and function to + * print back the structure as SDP message. + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +#define PJSDP_MAX_FMT 32 +#define PJSDP_MAX_ATTR 32 +#define PJSDP_MAX_MEDIA 16 + +/** + * This enumeration describes the attribute type. + */ +typedef enum pjsdp_attr_type_e +{ + PJSDP_ATTR_RTPMAP, + PJSDP_ATTR_CAT, + PJSDP_ATTR_KEYWORDS, + PJSDP_ATTR_TOOL, + PJSDP_ATTR_PTIME, + PJSDP_ATTR_RECV_ONLY, + PJSDP_ATTR_SEND_ONLY, + PJSDP_ATTR_SEND_RECV, + PJSDP_ATTR_ORIENT, + PJSDP_ATTR_TYPE, + PJSDP_ATTR_CHARSET, + PJSDP_ATTR_SDP_LANG, + PJSDP_ATTR_LANG, + PJSDP_ATTR_FRAME_RATE, + PJSDP_ATTR_QUALITY, + PJSDP_ATTR_FMTP, + PJSDP_ATTR_INACTIVE, + PJSDP_ATTR_GENERIC, + PJSDP_END_OF_ATTR, +} pjsdp_attr_type_e; + + +/** + * This structure keeps the common attributes that all 'descendants' + * will have. + */ +typedef struct pjsdp_attr +{ + pjsdp_attr_type_e type; /**< Attribute type. */ +} pjsdp_attr; + + +/** + * This is the structure to represent generic attribute which has a + * string value. + */ +typedef struct pjsdp_attr_string +{ + pjsdp_attr_type_e type; + pj_str_t value; +} pjsdp_attr_string; + + +/** + * This is the structure to represent generic SDP attribute which has + * a numeric value. + */ +typedef struct pjsdp_attr_num +{ + pjsdp_attr_type_e type; + pj_uint32_t value; +} pjsdp_attr_num; + + +/** + * SDP \a rtpmap attribute. + */ +typedef struct pjsdp_rtpmap_attr +{ + pjsdp_attr_type_e type; + unsigned payload_type; + pj_str_t encoding_name; + unsigned clock_rate; + pj_str_t parameter; +} pjsdp_rtpmap_attr; + + +/** + * SDP \a fmtp attribute. + */ +typedef struct pjsdp_fmtp_attr +{ + pjsdp_attr_type_e type; + pj_str_t format; + pj_str_t param; +} pjsdp_fmtp_attr; + + +/** + * SDP generic attribute. + */ +typedef struct pjsdp_generic_attr +{ + pjsdp_attr_type_e type; + pj_str_t name; + pj_str_t value; +} pjsdp_generic_attr; + + +/** SDP \a cat attribute. */ +typedef struct pjsdp_attr_string pjsdp_cat_attr; + +/** SDP \a keywds attribute. */ +typedef struct pjsdp_attr_string pjsdp_keywds_attr; + +/** SDP \a tool attribute. */ +typedef struct pjsdp_attr_string pjsdp_tool_attr; + +/** SDP \a ptime attribute. */ +typedef struct pjsdp_attr_num pjsdp_ptime_attr; + +/** SDP \a recvonly attribute. */ +typedef struct pjsdp_attr pjsdp_recv_only_attr; + +/** SDP \a sendonly attribute. */ +typedef struct pjsdp_attr pjsdp_send_only_attr; + +/** SDP \a sendrecv attribute. */ +typedef struct pjsdp_attr pjsdp_send_recv_attr; + +/** SDP \a orient attribute. */ +typedef struct pjsdp_attr_string pjsdp_orient_attr; + +/** SDP \a type attribute. */ +typedef struct pjsdp_attr_string pjsdp_type_attr; + +/** SDP \a charset attribute. */ +typedef struct pjsdp_attr_string pjsdp_charset_attr; + +/** SDP \a sdplang attribute. */ +typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr; + +/** SDP \a lang attribute. */ +typedef struct pjsdp_attr_string pjsdp_lang_attr; + +/** SDP \a framerate attribute. */ +typedef struct pjsdp_attr_string pjsdp_frame_rate_attr; + +/** SDP \a quality attribute. */ +typedef struct pjsdp_attr_num pjsdp_quality_attr; + +/** SDP \a inactive attribute. */ +typedef struct pjsdp_attr pjsdp_inactive_attr; + +/** Clone attribute */ +PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs); + +/** Find attribute */ +PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type); + +/** + * SDP connection info. + */ +typedef struct pjsdp_conn_info +{ + pj_str_t net_type; + pj_str_t addr_type; + pj_str_t addr; +} pjsdp_conn_info; + +/** + *Clone connection info. + * + * @param pool Pool to allocate memory for the new connection info. + * @param rhs The connection into to clone. + * + * @return the new connection info. + */ +PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, + const pjsdp_conn_info *rhs); + +/** + * SDP media description. + */ +typedef struct pjsdp_media_desc +{ + struct + { + pj_str_t media; + pj_uint16_t port; + unsigned port_count; + pj_str_t transport; + unsigned fmt_count; + pj_str_t fmt[PJSDP_MAX_FMT]; + } desc; + + pjsdp_conn_info *conn; + unsigned attr_count; + pjsdp_attr *attr[PJSDP_MAX_ATTR]; + +} pjsdp_media_desc; + +/** + * Clone SDP media description. + * + * @param pool Pool to allocate memory for the new media description. + * @param rhs The media descriptin to clone. + * + * @return a new media description. + */ +PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, + const pjsdp_media_desc *rhs); + +/** + * Check if the media description has the specified attribute. + * + * @param m The SDP media description. + * @param attr_type The attribute type. + * + * @return nonzero if true. + */ +PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, + pjsdp_attr_type_e attr_type); + +/** + * Find rtpmap attribute for the specified payload type. + * + * @param m The SDP media description. + * @param pt RTP payload type. + * + * @return the SDP rtpmap attribute for the payload type, or NULL if not found. + */ +PJ_DECL(const pjsdp_rtpmap_attr*) +pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt); + + +/** + * This structure describes SDP session description. + */ +typedef struct pjsdp_session_desc +{ + struct + { + pj_str_t user; + pj_uint32_t id; + pj_uint32_t version; + pj_str_t net_type; + pj_str_t addr_type; + pj_str_t addr; + } origin; + + pj_str_t name; + pjsdp_conn_info *conn; + + struct + { + pj_uint32_t start; + pj_uint32_t stop; + } time; + + unsigned attr_count; + pjsdp_attr *attr[PJSDP_MAX_ATTR]; + + unsigned media_count; + pjsdp_media_desc *media[PJSDP_MAX_MEDIA]; + +} pjsdp_session_desc; + + +/** + * Parse SDP message. + * + * @param buf The message buffer. + * @param len The length of the message. + * @param pool The pool to allocate SDP session description. + * + * @return SDP session description. + */ +PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, + pj_pool_t *pool); + +/** + * Print SDP description to a buffer. + * + * @param buf The buffer. + * @param size The buffer length. + * @param desc The SDP session description. + * + * @return the length printed, or -1. + */ +PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc, + char *buf, pj_size_t size); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSDP_SDP_H__ */ + diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c index 773bb7be..98b359f2 100644 --- a/pjmedia/src/pjmedia/session.c +++ b/pjmedia/src/pjmedia/session.c @@ -1,833 +1,833 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/session.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-typedef struct pj_media_stream_desc
-{
- pj_media_stream_info info;
- pj_media_stream_t *enc_stream, *dec_stream;
-} pj_media_stream_desc;
-
-struct pj_media_session_t
-{
- pj_pool_t *pool;
- pj_med_mgr_t *mediamgr;
- unsigned stream_cnt;
- pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA];
-};
-
-#define THIS_FILE "session.c"
-
-#define PJ_MEDIA_SESSION_SIZE (48*1024)
-#define PJ_MEDIA_SESSION_INC 1024
-
-static const pj_str_t ID_AUDIO = { "audio", 5};
-static const pj_str_t ID_VIDEO = { "video", 5};
-static const pj_str_t ID_IN = { "IN", 2 };
-static const pj_str_t ID_IP4 = { "IP4", 3};
-static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
-static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
-
-static void session_init (pj_media_session_t *ses)
-{
- pj_memset (ses, 0, sizeof(pj_media_session_t));
-}
-
-
-/**
- * Create new session offering.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- pj_media_stream_desc *sd;
- unsigned i, codec_cnt;
- pj_codec_mgr *cm;
- const pj_codec_id *codecs[PJSDP_MAX_FMT];
-
- pf = pj_med_mgr_get_pool_factory(mgr);
-
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool)
- return NULL;
-
- session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
- if (!session)
- return NULL;
-
- session_init (session);
-
- session->pool = pool;
- session->mediamgr = mgr;
-
- /* Create first stream */
- sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
- if (!sd)
- return NULL;
-
- sd->info.type = ID_AUDIO;
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- sd->info.transport = ID_RTP_AVP;
- pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
-
- /* Enum audio codecs. */
- sd->info.fmt_cnt = 0;
- cm = pj_med_mgr_get_codec_mgr (mgr);
- codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs);
- if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
- for (i=0; i<codec_cnt; ++i) {
- if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO)
- continue;
-
- sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt;
- sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate;
- pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name);
- ++sd->info.fmt_cnt;
- }
-
- session->stream_desc[session->stream_cnt++] = sd;
-
- return session;
-}
-
-static int sdp_check (const pjsdp_session_desc *sdp)
-{
- int has_conn = 0;
- unsigned i;
-
- if (sdp->conn)
- has_conn = 1;
-
- if (sdp->media_count == 0) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition"));
- return -1;
- }
-
- for (i=0; i<sdp->media_count; ++i) {
- pjsdp_media_desc *m = sdp->media[i];
-
- if (!m) {
- pj_assert(0);
- return -1;
- }
-
- if (m->desc.fmt_count == 0) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream"));
- return -1;
- }
-
- if (!has_conn && m->conn == NULL) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media"));
- return -1;
- }
- }
-
- return 0;
-}
-
-/*
- * Create local stream definition that matches SDP received from peer.
- */
-static pj_media_stream_desc*
-create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn,
- const pjsdp_media_desc *m, const pj_media_sock_info *sock_info)
-{
- pj_media_stream_desc *sd;
-
- sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
- if (!sd) {
- PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor"));
- return NULL;
- }
-
- if (pj_stricmp(&conn->net_type, &ID_IN)==0 &&
- pj_stricmp(&conn->addr_type, &ID_IP4)==0 &&
- pj_stricmp(&m->desc.media, &ID_AUDIO)==0 &&
- pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0)
- {
- /*
- * Got audio stream.
- */
- unsigned i, codec_cnt;
- pj_codec_mgr *cm;
- const pj_codec_id *codecs[PJSDP_MAX_FMT];
-
- sd->info.type = ID_AUDIO;
- sd->info.transport = ID_RTP_AVP;
- pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
- sd->info.rem_port = m->desc.port;
- pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
-
- /* Enum audio codecs. */
- sd->info.fmt_cnt = 0;
- cm = pj_med_mgr_get_codec_mgr (mgr);
- codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs);
- if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
-
- /* Find just one codec which we can support. */
- for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) {
- unsigned j, fmt_i;
-
- /* For static payload, just match payload type. */
- /* Else match clock rate and encoding name. */
- fmt_i = pj_strtoul(&m->desc.fmt[i]);
- if (fmt_i < PJ_RTP_PT_DYNAMIC) {
- for (j=0; j<codec_cnt; ++j) {
- if (codecs[j]->pt == fmt_i) {
- sd->info.fmt_cnt = 1;
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
- sd->info.fmt[0].pt = codecs[j]->pt;
- sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
- pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name);
- break;
- }
- }
- } else {
-
- /* Find the rtpmap for the payload type. */
- const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i);
-
- /* Don't accept the media if no rtpmap for dynamic PT. */
- if (rtpmap == NULL) {
- PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i]));
- continue;
- }
-
- /* Check whether we can take this codec. */
- for (j=0; j<codec_cnt; ++j) {
- if (rtpmap->clock_rate == codecs[j]->sample_rate &&
- pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0)
- {
- sd->info.fmt_cnt = 1;
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
- sd->info.fmt[0].pt = codecs[j]->pt;
- sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
- sd->info.fmt[0].encoding_name = codecs[j]->encoding_name;
- break;
- }
- }
- }
- }
-
- /* Match codec and direction. */
- if (sd->info.fmt_cnt == 0 || m->desc.port == 0 ||
- pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE))
- {
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_DECODING;
- }
- else {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- }
-
- } else {
- /* Unsupported media stream. */
- unsigned fmt_num;
- const pjsdp_rtpmap_attr *rtpmap = NULL;
-
- pj_strdup(pool, &sd->info.type, &m->desc.media);
- pj_strdup(pool, &sd->info.transport, &m->desc.transport);
- pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info));
- pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
- sd->info.rem_port = m->desc.port;
-
- /* Just put one format and rtpmap, so that we don't have to make
- * special exception when we convert this stream to SDP.
- */
-
- /* Find the rtpmap for the payload type. */
- fmt_num = pj_strtoul(&m->desc.fmt[0]);
- rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num);
-
- sd->info.fmt_cnt = 1;
- if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) {
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO;
- sd->info.fmt[0].pt = fmt_num;
- if (rtpmap) {
- pj_strdup (pool, &sd->info.fmt[0].encoding_name,
- &rtpmap->encoding_name);
- sd->info.fmt[0].sample_rate = rtpmap->clock_rate;
- }
- } else {
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN;
- pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]);
- }
-
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
-
- return sd;
-}
-
-/**
- * Create new session based on peer's offering.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
- const pj_media_sock_info *sock_info)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- unsigned i;
-
- if (sdp_check(sdp) != 0)
- return NULL;
-
- pf = pj_med_mgr_get_pool_factory(mgr);
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool)
- return NULL;
-
- session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
- if (!session) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session_init (session);
-
- session->pool = pool;
- session->mediamgr = mgr;
-
- /* Enumerate each media stream and create our peer. */
- for (i=0; i<sdp->media_count; ++i) {
- const pjsdp_conn_info *conn;
- const pjsdp_media_desc *m;
- pj_media_stream_desc *sd;
-
- m = sdp->media[i];
- conn = m->conn ? m->conn : sdp->conn;
-
- /*
- * Bug:
- * the sock_info below is used by more than one 'm' lines
- */
- PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES)
-
- sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info);
- pj_assert (sd);
-
- session->stream_desc[session->stream_cnt++] = sd;
- }
-
- return session;
-}
-
-/**
- * Duplicate session. The new session is inactive.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_clone (const pj_media_session_t *rhs)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- unsigned i;
-
- pf = pj_med_mgr_get_pool_factory(rhs->mediamgr);
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool) {
- return NULL;
- }
-
- session = pj_pool_alloc (pool, sizeof(*session));
- if (!session) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session->pool = pool;
- session->mediamgr = rhs->mediamgr;
- session->stream_cnt = rhs->stream_cnt;
-
- for (i=0; i<rhs->stream_cnt; ++i) {
- pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc));
- const pj_media_stream_desc *sd2 = rhs->stream_desc[i];
-
- if (!sd1) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session->stream_desc[i] = sd1;
- sd1->enc_stream = sd1->dec_stream = NULL;
- pj_strdup (pool, &sd1->info.type, &sd2->info.type);
- sd1->info.dir = sd2->info.dir;
- pj_strdup (pool, &sd1->info.transport, &sd2->info.transport);
- pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info));
- pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr);
- sd1->info.rem_port = sd2->info.rem_port;
- sd1->info.fmt_cnt = sd2->info.fmt_cnt;
- pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt));
- }
-
- return session;
-}
-
-/**
- * Create SDP description from the session.
- */
-PJ_DEF(pjsdp_session_desc*)
-pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool,
- pj_bool_t only_first_fmt)
-{
- pjsdp_session_desc *sdp;
- pj_time_val tv;
- unsigned i;
- pj_media_sock_info *c_addr = NULL;
-
- if (session->stream_cnt == 0) {
- pj_assert(0);
- return NULL;
- }
-
- sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc));
- if (!sdp) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor"));
- return NULL;
- }
-
- pj_gettimeofday(&tv);
-
- sdp->origin.user = pj_str("-");
- sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
- sdp->origin.net_type = ID_IN;
- sdp->origin.addr_type = ID_IP4;
- sdp->origin.addr = *pj_gethostname();
-
- sdp->name = ID_SDP_NAME;
-
- /* If all media addresses are the same, then put the connection
- * info in the session level, otherwise put it in media stream
- * level.
- */
- for (i=0; i<session->stream_cnt; ++i) {
- if (c_addr == NULL) {
- c_addr = &session->stream_desc[i]->info.sock_info;
- } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr)
- {
- c_addr = NULL;
- break;
- }
- }
-
- if (c_addr) {
- /* All addresses are the same, put connection info in session level. */
- sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (!sdp->conn) {
- PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info"));
- return NULL;
- }
-
- sdp->conn->net_type = ID_IN;
- sdp->conn->addr_type = ID_IP4;
- pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr));
- }
-
- sdp->time.start = sdp->time.stop = 0;
- sdp->attr_count = 0;
-
- /* Create each media. */
- sdp->media_count = 0;
- for (i=0; i<session->stream_cnt; ++i) {
- const pj_media_stream_desc *sd = session->stream_desc[i];
- pjsdp_media_desc *m;
- unsigned j;
- unsigned fmt_cnt;
- pjsdp_attr *attr;
-
- m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc));
- if (!m) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor"));
- return NULL;
- }
-
- sdp->media[sdp->media_count++] = m;
-
- pj_strdup (pool, &m->desc.media, &sd->info.type);
- m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port);
- m->desc.port_count = 1;
- pj_strdup (pool, &m->desc.transport, &sd->info.transport);
-
- /* Add format and rtpmap for each codec. */
- m->desc.fmt_count = 0;
- m->attr_count = 0;
- fmt_cnt = sd->info.fmt_cnt;
- if (fmt_cnt > 0 && only_first_fmt)
- fmt_cnt = 1;
- for (j=0; j<fmt_cnt; ++j) {
- pjsdp_rtpmap_attr *rtpmap;
- pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
-
- if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) {
- pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name);
- } else {
- fmt->ptr = pj_pool_alloc(pool, 8);
- fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr);
-
- rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr));
- if (rtpmap) {
- m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap;
- rtpmap->type = PJSDP_ATTR_RTPMAP;
- rtpmap->payload_type = sd->info.fmt[j].pt;
- rtpmap->clock_rate = sd->info.fmt[j].sample_rate;
- pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name);
- } else {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor"));
- }
- }
- }
-
- /* If we don't have connection info in session level, create one. */
- if (sdp->conn == NULL) {
- m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (m->conn) {
- m->conn->net_type = ID_IN;
- m->conn->addr_type = ID_IP4;
- pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr));
- } else {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info"));
- return NULL;
- }
- }
-
- /* Add additional attribute to the media stream. */
- attr = pj_pool_alloc(pool, sizeof(pjsdp_attr));
- if (!attr) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute"));
- return NULL;
- }
- m->attr[m->attr_count++] = attr;
-
- switch (sd->info.dir) {
- case PJ_MEDIA_DIR_NONE:
- attr->type = PJSDP_ATTR_INACTIVE;
- break;
- case PJ_MEDIA_DIR_ENCODING:
- attr->type = PJSDP_ATTR_SEND_ONLY;
- break;
- case PJ_MEDIA_DIR_DECODING:
- attr->type = PJSDP_ATTR_RECV_ONLY;
- break;
- case PJ_MEDIA_DIR_ENCODING_DECODING:
- attr->type = PJSDP_ATTR_SEND_RECV;
- break;
- }
- }
-
- return sdp;
-}
-
-/**
- * Update session with SDP answer from peer.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_update (pj_media_session_t *session,
- const pjsdp_session_desc *sdp)
-{
- unsigned i;
- unsigned count;
-
- /* Check SDP */
- if (sdp_check (sdp) != 0) {
- return -1;
- }
-
- /* If the media stream count doesn't match, only update one. */
- if (session->stream_cnt != sdp->media_count) {
- PJ_LOG(3,(THIS_FILE, "pj_media_session_update : "
- "SDP media count mismatch! (rmt=%d, lcl=%d)",
- sdp->media_count, session->stream_cnt));
- count = (session->stream_cnt < sdp->media_count) ?
- session->stream_cnt : sdp->media_count;
- } else {
- count = session->stream_cnt;
- }
-
- for (i=0; i<count; ++i) {
- pj_media_stream_desc *sd = session->stream_desc[i];
- const pjsdp_media_desc *m = sdp->media[i];
- const pjsdp_conn_info *conn;
- unsigned j;
-
- /* Check that the session is not active. */
- pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL);
-
- conn = m->conn ? m->conn : sdp->conn;
- pj_assert(conn);
-
- /* Update remote address. */
- sd->info.rem_port = m->desc.port;
- pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr);
-
- /* Select one active codec according to what peer wants. */
- for (j=0; j<sd->info.fmt_cnt; ++j) {
- unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]);
- if (sd->info.fmt[j].pt == fmt_0) {
- pj_codec_id temp;
-
- /* Put active format to the front. */
- if (j == 0)
- break;
-
- pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp));
- pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp));
- pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp));
- break;
- }
- }
-
- if (j == sd->info.fmt_cnt) {
- /* Peer has answered SDP with new codec, which doesn't exist
- * in the offer!
- * Mute this media.
- */
- PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!"));
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- continue;
- }
-
- /* Check direction. */
- if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) {
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_DECODING;
- }
- else {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- }
- }
-
- return 0;
-}
-
-/**
- * Enumerate media streams in the session.
- */
-PJ_DEF(unsigned)
-pj_media_session_enum_streams (const pj_media_session_t *session,
- unsigned count, const pj_media_stream_info *info[])
-{
- unsigned i;
-
- if (count > session->stream_cnt)
- count = session->stream_cnt;
-
- for (i=0; i<count; ++i) {
- info[i] = &session->stream_desc[i]->info;
- }
-
- return session->stream_cnt;
-}
-
-/**
- * Get statistics
- */
-PJ_DEF(pj_status_t)
-pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
- pj_media_stream_stat *tx_stat,
- pj_media_stream_stat *rx_stat)
-{
- pj_media_stream_desc *sd;
- int stat_cnt = 0;
-
- if (index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream && tx_stat) {
- pj_media_stream_get_stat (sd->enc_stream, tx_stat);
- ++stat_cnt;
- } else if (tx_stat) {
- pj_memset (tx_stat, 0, sizeof(*tx_stat));
- }
-
- if (sd->dec_stream && rx_stat) {
- pj_media_stream_get_stat (sd->dec_stream, rx_stat);
- ++stat_cnt;
- } else if (rx_stat) {
- pj_memset (rx_stat, 0, sizeof(*rx_stat));
- }
-
- return stat_cnt ? 0 : -1;
-}
-
-/**
- * Modify stream, only when stream is inactive.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
- unsigned modify_flag, const pj_media_stream_info *info)
-{
- pj_media_stream_desc *sd;
-
- if (index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream || sd->dec_stream) {
- pj_assert(0);
- return -1;
- }
-
- if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) {
- sd->info.dir = info->dir;
- }
-
- return 0;
-}
-
-/**
- * Activate media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_activate (pj_media_session_t *session)
-{
- unsigned i;
- pj_status_t status = 0;
-
- for (i=0; i<session->stream_cnt; ++i) {
- pj_status_t rc;
- rc = pj_media_session_activate_stream (session, i);
- if (status == 0)
- status = rc;
- }
- return status;
-}
-
-/**
- * Activate individual stream in media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_activate_stream (pj_media_session_t *session, unsigned index)
-{
- pj_media_stream_desc *sd;
- pj_media_stream_create_param scp;
- pj_status_t status;
- pj_time_val tv;
-
- if (index < 0 || index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream || sd->dec_stream) {
- /* Stream already active. */
- pj_assert(0);
- return 0;
- }
-
- pj_gettimeofday(&tv);
-
- /* Initialize parameter to create stream. */
- pj_memset (&scp, 0, sizeof(scp));
- scp.codec_id = &sd->info.fmt[0];
- scp.mediamgr = session->mediamgr;
- scp.dir = sd->info.dir;
- scp.rtp_sock = sd->info.sock_info.rtp_sock;
- scp.rtcp_sock = sd->info.sock_info.rtcp_sock;
- scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in));
- pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port);
- scp.ssrc = tv.sec;
- scp.jb_min = 1;
- scp.jb_max = 15;
- scp.jb_maxcnt = 16;
-
- /* Build the stream! */
- status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp);
-
- if (status==0 && sd->enc_stream) {
- status = pj_media_stream_start (sd->enc_stream);
- if (status != 0)
- goto on_error;
- }
- if (status==0 && sd->dec_stream) {
- status = pj_media_stream_start (sd->dec_stream);
- if (status != 0)
- goto on_error;
- }
- return status;
-
-on_error:
- if (sd->enc_stream) {
- pj_media_stream_destroy (sd->enc_stream);
- sd->enc_stream = NULL;
- }
- if (sd->dec_stream) {
- pj_media_stream_destroy (sd->dec_stream);
- sd->dec_stream = NULL;
- }
- return status;
-}
-
-/**
- * Destroy media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_destroy (pj_media_session_t *session)
-{
- unsigned i;
-
- if (!session)
- return -1;
-
- for (i=0; i<session->stream_cnt; ++i) {
- pj_media_stream_desc *sd = session->stream_desc[i];
-
- if (sd->enc_stream) {
- pj_media_stream_destroy (sd->enc_stream);
- sd->enc_stream = NULL;
- }
- if (sd->dec_stream) {
- pj_media_stream_destroy (sd->dec_stream);
- sd->dec_stream = NULL;
- }
- }
- pj_pool_release (session->pool);
- return 0;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/session.h> +#include <pj/log.h> +#include <pj/os.h> +#include <pj/pool.h> +#include <pj/string.h> + +typedef struct pj_media_stream_desc +{ + pj_media_stream_info info; + pj_media_stream_t *enc_stream, *dec_stream; +} pj_media_stream_desc; + +struct pj_media_session_t +{ + pj_pool_t *pool; + pj_med_mgr_t *mediamgr; + unsigned stream_cnt; + pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA]; +}; + +#define THIS_FILE "session.c" + +#define PJ_MEDIA_SESSION_SIZE (48*1024) +#define PJ_MEDIA_SESSION_INC 1024 + +static const pj_str_t ID_AUDIO = { "audio", 5}; +static const pj_str_t ID_VIDEO = { "video", 5}; +static const pj_str_t ID_IN = { "IN", 2 }; +static const pj_str_t ID_IP4 = { "IP4", 3}; +static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; +static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; + +static void session_init (pj_media_session_t *ses) +{ + pj_memset (ses, 0, sizeof(pj_media_session_t)); +} + + +/** + * Create new session offering. + */ +PJ_DEF(pj_media_session_t*) +pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info) +{ + pj_pool_factory *pf; + pj_pool_t *pool; + pj_media_session_t *session; + pj_media_stream_desc *sd; + unsigned i, codec_cnt; + pj_codec_mgr *cm; + const pj_codec_id *codecs[PJSDP_MAX_FMT]; + + pf = pj_med_mgr_get_pool_factory(mgr); + + pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); + if (!pool) + return NULL; + + session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); + if (!session) + return NULL; + + session_init (session); + + session->pool = pool; + session->mediamgr = mgr; + + /* Create first stream */ + sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); + if (!sd) + return NULL; + + sd->info.type = ID_AUDIO; + sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; + sd->info.transport = ID_RTP_AVP; + pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); + + /* Enum audio codecs. */ + sd->info.fmt_cnt = 0; + cm = pj_med_mgr_get_codec_mgr (mgr); + codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs); + if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; + for (i=0; i<codec_cnt; ++i) { + if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO) + continue; + + sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt; + sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate; + pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name); + ++sd->info.fmt_cnt; + } + + session->stream_desc[session->stream_cnt++] = sd; + + return session; +} + +static int sdp_check (const pjsdp_session_desc *sdp) +{ + int has_conn = 0; + unsigned i; + + if (sdp->conn) + has_conn = 1; + + if (sdp->media_count == 0) { + PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition")); + return -1; + } + + for (i=0; i<sdp->media_count; ++i) { + pjsdp_media_desc *m = sdp->media[i]; + + if (!m) { + pj_assert(0); + return -1; + } + + if (m->desc.fmt_count == 0) { + PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream")); + return -1; + } + + if (!has_conn && m->conn == NULL) { + PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media")); + return -1; + } + } + + return 0; +} + +/* + * Create local stream definition that matches SDP received from peer. + */ +static pj_media_stream_desc* +create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn, + const pjsdp_media_desc *m, const pj_media_sock_info *sock_info) +{ + pj_media_stream_desc *sd; + + sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); + if (!sd) { + PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor")); + return NULL; + } + + if (pj_stricmp(&conn->net_type, &ID_IN)==0 && + pj_stricmp(&conn->addr_type, &ID_IP4)==0 && + pj_stricmp(&m->desc.media, &ID_AUDIO)==0 && + pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0) + { + /* + * Got audio stream. + */ + unsigned i, codec_cnt; + pj_codec_mgr *cm; + const pj_codec_id *codecs[PJSDP_MAX_FMT]; + + sd->info.type = ID_AUDIO; + sd->info.transport = ID_RTP_AVP; + pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); + sd->info.rem_port = m->desc.port; + pj_strdup (pool, &sd->info.rem_addr, &conn->addr); + + /* Enum audio codecs. */ + sd->info.fmt_cnt = 0; + cm = pj_med_mgr_get_codec_mgr (mgr); + codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs); + if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; + + /* Find just one codec which we can support. */ + for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) { + unsigned j, fmt_i; + + /* For static payload, just match payload type. */ + /* Else match clock rate and encoding name. */ + fmt_i = pj_strtoul(&m->desc.fmt[i]); + if (fmt_i < PJ_RTP_PT_DYNAMIC) { + for (j=0; j<codec_cnt; ++j) { + if (codecs[j]->pt == fmt_i) { + sd->info.fmt_cnt = 1; + sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; + sd->info.fmt[0].pt = codecs[j]->pt; + sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; + pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name); + break; + } + } + } else { + + /* Find the rtpmap for the payload type. */ + const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i); + + /* Don't accept the media if no rtpmap for dynamic PT. */ + if (rtpmap == NULL) { + PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i])); + continue; + } + + /* Check whether we can take this codec. */ + for (j=0; j<codec_cnt; ++j) { + if (rtpmap->clock_rate == codecs[j]->sample_rate && + pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0) + { + sd->info.fmt_cnt = 1; + sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; + sd->info.fmt[0].pt = codecs[j]->pt; + sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; + sd->info.fmt[0].encoding_name = codecs[j]->encoding_name; + break; + } + } + } + } + + /* Match codec and direction. */ + if (sd->info.fmt_cnt == 0 || m->desc.port == 0 || + pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) + { + sd->info.dir = PJ_MEDIA_DIR_NONE; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_ENCODING; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_DECODING; + } + else { + sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; + } + + } else { + /* Unsupported media stream. */ + unsigned fmt_num; + const pjsdp_rtpmap_attr *rtpmap = NULL; + + pj_strdup(pool, &sd->info.type, &m->desc.media); + pj_strdup(pool, &sd->info.transport, &m->desc.transport); + pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info)); + pj_strdup (pool, &sd->info.rem_addr, &conn->addr); + sd->info.rem_port = m->desc.port; + + /* Just put one format and rtpmap, so that we don't have to make + * special exception when we convert this stream to SDP. + */ + + /* Find the rtpmap for the payload type. */ + fmt_num = pj_strtoul(&m->desc.fmt[0]); + rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num); + + sd->info.fmt_cnt = 1; + if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) { + sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO; + sd->info.fmt[0].pt = fmt_num; + if (rtpmap) { + pj_strdup (pool, &sd->info.fmt[0].encoding_name, + &rtpmap->encoding_name); + sd->info.fmt[0].sample_rate = rtpmap->clock_rate; + } + } else { + sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN; + pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]); + } + + sd->info.dir = PJ_MEDIA_DIR_NONE; + } + + return sd; +} + +/** + * Create new session based on peer's offering. + */ +PJ_DEF(pj_media_session_t*) +pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, + const pj_media_sock_info *sock_info) +{ + pj_pool_factory *pf; + pj_pool_t *pool; + pj_media_session_t *session; + unsigned i; + + if (sdp_check(sdp) != 0) + return NULL; + + pf = pj_med_mgr_get_pool_factory(mgr); + pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); + if (!pool) + return NULL; + + session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); + if (!session) { + PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); + pj_pool_release (pool); + return NULL; + } + + session_init (session); + + session->pool = pool; + session->mediamgr = mgr; + + /* Enumerate each media stream and create our peer. */ + for (i=0; i<sdp->media_count; ++i) { + const pjsdp_conn_info *conn; + const pjsdp_media_desc *m; + pj_media_stream_desc *sd; + + m = sdp->media[i]; + conn = m->conn ? m->conn : sdp->conn; + + /* + * Bug: + * the sock_info below is used by more than one 'm' lines + */ + PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES) + + sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info); + pj_assert (sd); + + session->stream_desc[session->stream_cnt++] = sd; + } + + return session; +} + +/** + * Duplicate session. The new session is inactive. + */ +PJ_DEF(pj_media_session_t*) +pj_media_session_clone (const pj_media_session_t *rhs) +{ + pj_pool_factory *pf; + pj_pool_t *pool; + pj_media_session_t *session; + unsigned i; + + pf = pj_med_mgr_get_pool_factory(rhs->mediamgr); + pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); + if (!pool) { + return NULL; + } + + session = pj_pool_alloc (pool, sizeof(*session)); + if (!session) { + PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); + pj_pool_release (pool); + return NULL; + } + + session->pool = pool; + session->mediamgr = rhs->mediamgr; + session->stream_cnt = rhs->stream_cnt; + + for (i=0; i<rhs->stream_cnt; ++i) { + pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc)); + const pj_media_stream_desc *sd2 = rhs->stream_desc[i]; + + if (!sd1) { + PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor")); + pj_pool_release (pool); + return NULL; + } + + session->stream_desc[i] = sd1; + sd1->enc_stream = sd1->dec_stream = NULL; + pj_strdup (pool, &sd1->info.type, &sd2->info.type); + sd1->info.dir = sd2->info.dir; + pj_strdup (pool, &sd1->info.transport, &sd2->info.transport); + pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info)); + pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr); + sd1->info.rem_port = sd2->info.rem_port; + sd1->info.fmt_cnt = sd2->info.fmt_cnt; + pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt)); + } + + return session; +} + +/** + * Create SDP description from the session. + */ +PJ_DEF(pjsdp_session_desc*) +pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool, + pj_bool_t only_first_fmt) +{ + pjsdp_session_desc *sdp; + pj_time_val tv; + unsigned i; + pj_media_sock_info *c_addr = NULL; + + if (session->stream_cnt == 0) { + pj_assert(0); + return NULL; + } + + sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc)); + if (!sdp) { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor")); + return NULL; + } + + pj_gettimeofday(&tv); + + sdp->origin.user = pj_str("-"); + sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL; + sdp->origin.net_type = ID_IN; + sdp->origin.addr_type = ID_IP4; + sdp->origin.addr = *pj_gethostname(); + + sdp->name = ID_SDP_NAME; + + /* If all media addresses are the same, then put the connection + * info in the session level, otherwise put it in media stream + * level. + */ + for (i=0; i<session->stream_cnt; ++i) { + if (c_addr == NULL) { + c_addr = &session->stream_desc[i]->info.sock_info; + } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr) + { + c_addr = NULL; + break; + } + } + + if (c_addr) { + /* All addresses are the same, put connection info in session level. */ + sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); + if (!sdp->conn) { + PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info")); + return NULL; + } + + sdp->conn->net_type = ID_IN; + sdp->conn->addr_type = ID_IP4; + pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr)); + } + + sdp->time.start = sdp->time.stop = 0; + sdp->attr_count = 0; + + /* Create each media. */ + sdp->media_count = 0; + for (i=0; i<session->stream_cnt; ++i) { + const pj_media_stream_desc *sd = session->stream_desc[i]; + pjsdp_media_desc *m; + unsigned j; + unsigned fmt_cnt; + pjsdp_attr *attr; + + m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc)); + if (!m) { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor")); + return NULL; + } + + sdp->media[sdp->media_count++] = m; + + pj_strdup (pool, &m->desc.media, &sd->info.type); + m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port); + m->desc.port_count = 1; + pj_strdup (pool, &m->desc.transport, &sd->info.transport); + + /* Add format and rtpmap for each codec. */ + m->desc.fmt_count = 0; + m->attr_count = 0; + fmt_cnt = sd->info.fmt_cnt; + if (fmt_cnt > 0 && only_first_fmt) + fmt_cnt = 1; + for (j=0; j<fmt_cnt; ++j) { + pjsdp_rtpmap_attr *rtpmap; + pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++]; + + if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) { + pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name); + } else { + fmt->ptr = pj_pool_alloc(pool, 8); + fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr); + + rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr)); + if (rtpmap) { + m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap; + rtpmap->type = PJSDP_ATTR_RTPMAP; + rtpmap->payload_type = sd->info.fmt[j].pt; + rtpmap->clock_rate = sd->info.fmt[j].sample_rate; + pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name); + } else { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor")); + } + } + } + + /* If we don't have connection info in session level, create one. */ + if (sdp->conn == NULL) { + m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); + if (m->conn) { + m->conn->net_type = ID_IN; + m->conn->addr_type = ID_IP4; + pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr)); + } else { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info")); + return NULL; + } + } + + /* Add additional attribute to the media stream. */ + attr = pj_pool_alloc(pool, sizeof(pjsdp_attr)); + if (!attr) { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute")); + return NULL; + } + m->attr[m->attr_count++] = attr; + + switch (sd->info.dir) { + case PJ_MEDIA_DIR_NONE: + attr->type = PJSDP_ATTR_INACTIVE; + break; + case PJ_MEDIA_DIR_ENCODING: + attr->type = PJSDP_ATTR_SEND_ONLY; + break; + case PJ_MEDIA_DIR_DECODING: + attr->type = PJSDP_ATTR_RECV_ONLY; + break; + case PJ_MEDIA_DIR_ENCODING_DECODING: + attr->type = PJSDP_ATTR_SEND_RECV; + break; + } + } + + return sdp; +} + +/** + * Update session with SDP answer from peer. + */ +PJ_DEF(pj_status_t) +pj_media_session_update (pj_media_session_t *session, + const pjsdp_session_desc *sdp) +{ + unsigned i; + unsigned count; + + /* Check SDP */ + if (sdp_check (sdp) != 0) { + return -1; + } + + /* If the media stream count doesn't match, only update one. */ + if (session->stream_cnt != sdp->media_count) { + PJ_LOG(3,(THIS_FILE, "pj_media_session_update : " + "SDP media count mismatch! (rmt=%d, lcl=%d)", + sdp->media_count, session->stream_cnt)); + count = (session->stream_cnt < sdp->media_count) ? + session->stream_cnt : sdp->media_count; + } else { + count = session->stream_cnt; + } + + for (i=0; i<count; ++i) { + pj_media_stream_desc *sd = session->stream_desc[i]; + const pjsdp_media_desc *m = sdp->media[i]; + const pjsdp_conn_info *conn; + unsigned j; + + /* Check that the session is not active. */ + pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL); + + conn = m->conn ? m->conn : sdp->conn; + pj_assert(conn); + + /* Update remote address. */ + sd->info.rem_port = m->desc.port; + pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr); + + /* Select one active codec according to what peer wants. */ + for (j=0; j<sd->info.fmt_cnt; ++j) { + unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]); + if (sd->info.fmt[j].pt == fmt_0) { + pj_codec_id temp; + + /* Put active format to the front. */ + if (j == 0) + break; + + pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp)); + pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp)); + pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp)); + break; + } + } + + if (j == sd->info.fmt_cnt) { + /* Peer has answered SDP with new codec, which doesn't exist + * in the offer! + * Mute this media. + */ + PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!")); + sd->info.dir = PJ_MEDIA_DIR_NONE; + continue; + } + + /* Check direction. */ + if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) { + sd->info.dir = PJ_MEDIA_DIR_NONE; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_ENCODING; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_DECODING; + } + else { + sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; + } + } + + return 0; +} + +/** + * Enumerate media streams in the session. + */ +PJ_DEF(unsigned) +pj_media_session_enum_streams (const pj_media_session_t *session, + unsigned count, const pj_media_stream_info *info[]) +{ + unsigned i; + + if (count > session->stream_cnt) + count = session->stream_cnt; + + for (i=0; i<count; ++i) { + info[i] = &session->stream_desc[i]->info; + } + + return session->stream_cnt; +} + +/** + * Get statistics + */ +PJ_DEF(pj_status_t) +pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, + pj_media_stream_stat *tx_stat, + pj_media_stream_stat *rx_stat) +{ + pj_media_stream_desc *sd; + int stat_cnt = 0; + + if (index >= session->stream_cnt) { + pj_assert(0); + return -1; + } + + sd = session->stream_desc[index]; + + if (sd->enc_stream && tx_stat) { + pj_media_stream_get_stat (sd->enc_stream, tx_stat); + ++stat_cnt; + } else if (tx_stat) { + pj_memset (tx_stat, 0, sizeof(*tx_stat)); + } + + if (sd->dec_stream && rx_stat) { + pj_media_stream_get_stat (sd->dec_stream, rx_stat); + ++stat_cnt; + } else if (rx_stat) { + pj_memset (rx_stat, 0, sizeof(*rx_stat)); + } + + return stat_cnt ? 0 : -1; +} + +/** + * Modify stream, only when stream is inactive. + */ +PJ_DEF(pj_status_t) +pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, + unsigned modify_flag, const pj_media_stream_info *info) +{ + pj_media_stream_desc *sd; + + if (index >= session->stream_cnt) { + pj_assert(0); + return -1; + } + + sd = session->stream_desc[index]; + + if (sd->enc_stream || sd->dec_stream) { + pj_assert(0); + return -1; + } + + if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) { + sd->info.dir = info->dir; + } + + return 0; +} + +/** + * Activate media session. + */ +PJ_DEF(pj_status_t) +pj_media_session_activate (pj_media_session_t *session) +{ + unsigned i; + pj_status_t status = 0; + + for (i=0; i<session->stream_cnt; ++i) { + pj_status_t rc; + rc = pj_media_session_activate_stream (session, i); + if (status == 0) + status = rc; + } + return status; +} + +/** + * Activate individual stream in media session. + */ +PJ_DEF(pj_status_t) +pj_media_session_activate_stream (pj_media_session_t *session, unsigned index) +{ + pj_media_stream_desc *sd; + pj_media_stream_create_param scp; + pj_status_t status; + pj_time_val tv; + + if (index < 0 || index >= session->stream_cnt) { + pj_assert(0); + return -1; + } + + sd = session->stream_desc[index]; + + if (sd->enc_stream || sd->dec_stream) { + /* Stream already active. */ + pj_assert(0); + return 0; + } + + pj_gettimeofday(&tv); + + /* Initialize parameter to create stream. */ + pj_memset (&scp, 0, sizeof(scp)); + scp.codec_id = &sd->info.fmt[0]; + scp.mediamgr = session->mediamgr; + scp.dir = sd->info.dir; + scp.rtp_sock = sd->info.sock_info.rtp_sock; + scp.rtcp_sock = sd->info.sock_info.rtcp_sock; + scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in)); + pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port); + scp.ssrc = tv.sec; + scp.jb_min = 1; + scp.jb_max = 15; + scp.jb_maxcnt = 16; + + /* Build the stream! */ + status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp); + + if (status==0 && sd->enc_stream) { + status = pj_media_stream_start (sd->enc_stream); + if (status != 0) + goto on_error; + } + if (status==0 && sd->dec_stream) { + status = pj_media_stream_start (sd->dec_stream); + if (status != 0) + goto on_error; + } + return status; + +on_error: + if (sd->enc_stream) { + pj_media_stream_destroy (sd->enc_stream); + sd->enc_stream = NULL; + } + if (sd->dec_stream) { + pj_media_stream_destroy (sd->dec_stream); + sd->dec_stream = NULL; + } + return status; +} + +/** + * Destroy media session. + */ +PJ_DEF(pj_status_t) +pj_media_session_destroy (pj_media_session_t *session) +{ + unsigned i; + + if (!session) + return -1; + + for (i=0; i<session->stream_cnt; ++i) { + pj_media_stream_desc *sd = session->stream_desc[i]; + + if (sd->enc_stream) { + pj_media_stream_destroy (sd->enc_stream); + sd->enc_stream = NULL; + } + if (sd->dec_stream) { + pj_media_stream_destroy (sd->dec_stream); + sd->dec_stream = NULL; + } + } + pj_pool_release (session->pool); + return 0; +} + diff --git a/pjmedia/src/pjmedia/session.h b/pjmedia/src/pjmedia/session.h index 1159fe71..d9efeb81 100644 --- a/pjmedia/src/pjmedia/session.h +++ b/pjmedia/src/pjmedia/session.h @@ -1,152 +1,152 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_SESSION_H__
-#define __PJMEDIA_SESSION_H__
-
-
-/**
- * @file session.h
- * @brief Media Session.
- */
-
-#include <pj/types.h>
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/stream.h>
-#include <pjmedia/sdp.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJMED_SES Media session
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Opaque declaration of media session. */
-typedef struct pj_media_session_t pj_media_session_t;
-
-/** Media socket info. */
-typedef struct pj_media_sock_info
-{
- pj_sock_t rtp_sock, rtcp_sock;
- pj_sockaddr_in rtp_addr_name;
-} pj_media_sock_info;
-
-/** Stream info. */
-typedef struct pj_media_stream_info
-{
- pj_str_t type;
- pj_media_dir_t dir;
- pj_str_t transport;
- pj_media_sock_info sock_info;
- pj_str_t rem_addr;
- unsigned short rem_port;
- unsigned fmt_cnt;
- pj_codec_id fmt[PJSDP_MAX_FMT];
-
-} pj_media_stream_info;
-
-/** Flag for modifying stream. */
-enum
-{
- PJ_MEDIA_STREAM_MODIFY_DIR = 1,
-};
-
-/**
- * Create new session offering.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo );
-
-/**
- * Create new session based on peer's offering.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
- const pj_media_sock_info *skinfo);
-
-/**
- * Duplicate session. The new session is inactive.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_clone (const pj_media_session_t *session);
-
-/**
- * Create SDP description from the session.
- */
-PJ_DECL(pjsdp_session_desc*)
-pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool,
- pj_bool_t only_first_fmt);
-
-/**
- * Update session with SDP answer from peer. The session must NOT active.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_update ( pj_media_session_t *session,
- const pjsdp_session_desc *sdp);
-
-/**
- * Enumerate media streams in the session.
- * @return the actual number of streams.
- */
-PJ_DECL(unsigned)
-pj_media_session_enum_streams (const pj_media_session_t *session,
- unsigned count, const pj_media_stream_info *info[]);
-
-/**
- * Get stream statistics.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
- pj_media_stream_stat *tx_stat,
- pj_media_stream_stat *rx_stat);
-
-/**
- * Modify stream, only when stream is inactive.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
- unsigned modify_flag, const pj_media_stream_info *info);
-
-/**
- * Activate all streams in media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_activate (pj_media_session_t *session);
-
-/**
- * Activate individual stream in media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_activate_stream (pj_media_session_t *session, unsigned index);
-
-/**
- * Destroy media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_destroy (pj_media_session_t *session);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJMEDIA_SESSION_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_SESSION_H__ +#define __PJMEDIA_SESSION_H__ + + +/** + * @file session.h + * @brief Media Session. + */ + +#include <pj/types.h> +#include <pjmedia/mediamgr.h> +#include <pjmedia/stream.h> +#include <pjmedia/sdp.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJMED_SES Media session + * @ingroup PJMEDIA + * @{ + */ + +/** Opaque declaration of media session. */ +typedef struct pj_media_session_t pj_media_session_t; + +/** Media socket info. */ +typedef struct pj_media_sock_info +{ + pj_sock_t rtp_sock, rtcp_sock; + pj_sockaddr_in rtp_addr_name; +} pj_media_sock_info; + +/** Stream info. */ +typedef struct pj_media_stream_info +{ + pj_str_t type; + pj_media_dir_t dir; + pj_str_t transport; + pj_media_sock_info sock_info; + pj_str_t rem_addr; + unsigned short rem_port; + unsigned fmt_cnt; + pj_codec_id fmt[PJSDP_MAX_FMT]; + +} pj_media_stream_info; + +/** Flag for modifying stream. */ +enum +{ + PJ_MEDIA_STREAM_MODIFY_DIR = 1, +}; + +/** + * Create new session offering. + */ +PJ_DECL(pj_media_session_t*) +pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo ); + +/** + * Create new session based on peer's offering. + */ +PJ_DECL(pj_media_session_t*) +pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, + const pj_media_sock_info *skinfo); + +/** + * Duplicate session. The new session is inactive. + */ +PJ_DECL(pj_media_session_t*) +pj_media_session_clone (const pj_media_session_t *session); + +/** + * Create SDP description from the session. + */ +PJ_DECL(pjsdp_session_desc*) +pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool, + pj_bool_t only_first_fmt); + +/** + * Update session with SDP answer from peer. The session must NOT active. + */ +PJ_DECL(pj_status_t) +pj_media_session_update ( pj_media_session_t *session, + const pjsdp_session_desc *sdp); + +/** + * Enumerate media streams in the session. + * @return the actual number of streams. + */ +PJ_DECL(unsigned) +pj_media_session_enum_streams (const pj_media_session_t *session, + unsigned count, const pj_media_stream_info *info[]); + +/** + * Get stream statistics. + */ +PJ_DECL(pj_status_t) +pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, + pj_media_stream_stat *tx_stat, + pj_media_stream_stat *rx_stat); + +/** + * Modify stream, only when stream is inactive. + */ +PJ_DECL(pj_status_t) +pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, + unsigned modify_flag, const pj_media_stream_info *info); + +/** + * Activate all streams in media session. + */ +PJ_DECL(pj_status_t) +pj_media_session_activate (pj_media_session_t *session); + +/** + * Activate individual stream in media session. + */ +PJ_DECL(pj_status_t) +pj_media_session_activate_stream (pj_media_session_t *session, unsigned index); + +/** + * Destroy media session. + */ +PJ_DECL(pj_status_t) +pj_media_session_destroy (pj_media_session_t *session); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJMEDIA_SESSION_H__ */ diff --git a/pjmedia/src/pjmedia/sound.h b/pjmedia/src/pjmedia/sound.h index f1dce295..3fa40db6 100644 --- a/pjmedia/src/pjmedia/sound.h +++ b/pjmedia/src/pjmedia/sound.h @@ -1,201 +1,201 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_SOUND_H__
-#define __PJMEDIA_SOUND_H__
-
-
-/**
- * @file sound.h
- * @brief Sound player and recorder device framework.
- */
-
-#include <pj/pool.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJMED_SND Sound device abstraction.
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Opaque data type for audio stream. */
-typedef struct pj_snd_stream pj_snd_stream;
-
-/**
- * Device information structure returned by #pj_snd_get_dev_info.
- */
-typedef struct pj_snd_dev_info
-{
- char name[64]; /**< Device name. */
- unsigned input_count; /**< Max number of input channels. */
- unsigned output_count; /**< Max number of output channels. */
- unsigned default_samples_per_sec;/**< Default sampling rate. */
-} pj_snd_dev_info;
-
-/**
- * Sound device parameter, to be specified when calling #pj_snd_open_recorder
- * or #pj_snd_open_player.
- */
-typedef struct pj_snd_stream_info
-{
- unsigned samples_per_sec; /* Sampling rate. */
- unsigned bits_per_sample; /* No of bits per sample. */
- unsigned samples_per_frame; /* No of samples per frame. */
- unsigned bytes_per_frame; /* No of bytes per frame. */
- unsigned frames_per_packet; /* No of frames per packet. */
-} pj_snd_stream_info;
-
-/**
- * This callback is called by player stream when it needs additional data
- * to be played by the device. Application must fill in the whole of output
- * buffer with sound samples.
- *
- * @param user_data User data associated with the stream.
- * @param timestamp Timestamp, in samples.
- * @param output Buffer to be filled out by application.
- * @param size The size requested in bytes, which will be equal to
- * the size of one whole packet.
- *
- * @return Non-zero to stop the stream.
- */
-typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *output,
- /* out */ unsigned size);
-
-/**
- * This callback is called by recorder stream when it has captured the whole
- * packet worth of audio samples.
- *
- * @param user_data User data associated with the stream.
- * @param timestamp Timestamp, in samples.
- * @param output Buffer containing the captured audio samples.
- * @param size The size of the data in the buffer, in bytes.
- *
- * @return Non-zero to stop the stream.
- */
-typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *input,
- /* in*/ unsigned size);
-
-/**
- * Init the sound library.
- *
- * @param factory The sound factory.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory);
-
-
-/**
- * Get the number of devices detected by the library.
- *
- * @return Number of devices.
- */
-PJ_DECL(int) pj_snd_get_dev_count(void);
-
-
-/**
- * Get device info.
- *
- * @param index The index of the device, which should be in the range
- * from zero to #pj_snd_get_dev_count - 1.
- */
-PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index);
-
-
-/**
- * Create a new audio stream for audio capture purpose.
- *
- * @param index Device index, or -1 to let the library choose the first
- * available device, or -2 to use NULL device.
- * @param param Stream parameters.
- * @param rec_cb Callback to handle captured audio samples.
- * @param user_data User data to be associated with the stream.
- *
- * @return Audio stream, or NULL if failed.
- */
-PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index,
- const pj_snd_stream_info *param,
- pj_snd_rec_cb rec_cb,
- void *user_data);
-
-/**
- * Create a new audio stream for playing audio samples.
- *
- * @param index Device index, or -1 to let the library choose the first
- * available device, or -2 to use NULL device.
- * @param param Stream parameters.
- * @param play_cb Callback to supply audio samples.
- * @param user_data User data to be associated with the stream.
- *
- * @return Audio stream, or NULL if failed.
- */
-PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index,
- const pj_snd_stream_info *param,
- pj_snd_play_cb play_cb,
- void *user_data);
-
-/**
- * Start the stream.
- *
- * @param stream The recorder or player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream);
-
-/**
- * Stop the stream.
- *
- * @param stream The recorder or player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream);
-
-/**
- * Destroy the stream.
- *
- * @param stream The recorder of player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream);
-
-/**
- * Deinitialize sound library.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_deinit(void);
-
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_SOUND_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_SOUND_H__ +#define __PJMEDIA_SOUND_H__ + + +/** + * @file sound.h + * @brief Sound player and recorder device framework. + */ + +#include <pj/pool.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJMED_SND Sound device abstraction. + * @ingroup PJMEDIA + * @{ + */ + +/** Opaque data type for audio stream. */ +typedef struct pj_snd_stream pj_snd_stream; + +/** + * Device information structure returned by #pj_snd_get_dev_info. + */ +typedef struct pj_snd_dev_info +{ + char name[64]; /**< Device name. */ + unsigned input_count; /**< Max number of input channels. */ + unsigned output_count; /**< Max number of output channels. */ + unsigned default_samples_per_sec;/**< Default sampling rate. */ +} pj_snd_dev_info; + +/** + * Sound device parameter, to be specified when calling #pj_snd_open_recorder + * or #pj_snd_open_player. + */ +typedef struct pj_snd_stream_info +{ + unsigned samples_per_sec; /* Sampling rate. */ + unsigned bits_per_sample; /* No of bits per sample. */ + unsigned samples_per_frame; /* No of samples per frame. */ + unsigned bytes_per_frame; /* No of bytes per frame. */ + unsigned frames_per_packet; /* No of frames per packet. */ +} pj_snd_stream_info; + +/** + * This callback is called by player stream when it needs additional data + * to be played by the device. Application must fill in the whole of output + * buffer with sound samples. + * + * @param user_data User data associated with the stream. + * @param timestamp Timestamp, in samples. + * @param output Buffer to be filled out by application. + * @param size The size requested in bytes, which will be equal to + * the size of one whole packet. + * + * @return Non-zero to stop the stream. + */ +typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* out */ void *output, + /* out */ unsigned size); + +/** + * This callback is called by recorder stream when it has captured the whole + * packet worth of audio samples. + * + * @param user_data User data associated with the stream. + * @param timestamp Timestamp, in samples. + * @param output Buffer containing the captured audio samples. + * @param size The size of the data in the buffer, in bytes. + * + * @return Non-zero to stop the stream. + */ +typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* in */ const void *input, + /* in*/ unsigned size); + +/** + * Init the sound library. + * + * @param factory The sound factory. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory); + + +/** + * Get the number of devices detected by the library. + * + * @return Number of devices. + */ +PJ_DECL(int) pj_snd_get_dev_count(void); + + +/** + * Get device info. + * + * @param index The index of the device, which should be in the range + * from zero to #pj_snd_get_dev_count - 1. + */ +PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index); + + +/** + * Create a new audio stream for audio capture purpose. + * + * @param index Device index, or -1 to let the library choose the first + * available device, or -2 to use NULL device. + * @param param Stream parameters. + * @param rec_cb Callback to handle captured audio samples. + * @param user_data User data to be associated with the stream. + * + * @return Audio stream, or NULL if failed. + */ +PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index, + const pj_snd_stream_info *param, + pj_snd_rec_cb rec_cb, + void *user_data); + +/** + * Create a new audio stream for playing audio samples. + * + * @param index Device index, or -1 to let the library choose the first + * available device, or -2 to use NULL device. + * @param param Stream parameters. + * @param play_cb Callback to supply audio samples. + * @param user_data User data to be associated with the stream. + * + * @return Audio stream, or NULL if failed. + */ +PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index, + const pj_snd_stream_info *param, + pj_snd_play_cb play_cb, + void *user_data); + +/** + * Start the stream. + * + * @param stream The recorder or player stream. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream); + +/** + * Stop the stream. + * + * @param stream The recorder or player stream. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream); + +/** + * Destroy the stream. + * + * @param stream The recorder of player stream. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream); + +/** + * Deinitialize sound library. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_deinit(void); + + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_SOUND_H__ */ diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 1a7080a4..718cace1 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -1,644 +1,644 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/stream.h>
-#include <pjmedia/rtp.h>
-#include <pjmedia/rtcp.h>
-#include <pjmedia/jbuf.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/string.h> /* memcpy() */
-#include <pj/pool.h>
-#include <stdlib.h>
-
-#define THISFILE "stream.c"
-#define ERRLEVEL 1
-
-#define PJ_MAX_FRAME_DURATION_MS 200
-#define PJ_MAX_BUFFER_SIZE_MS 2000
-#define PJ_MAX_MTU 1500
-
-struct jb_frame
-{
- unsigned size;
- void *buf;
-};
-
-#define pj_fifobuf_alloc(fifo,size) malloc(size)
-#define pj_fifobuf_unalloc(fifo,buf) free(buf)
-#define pj_fifobuf_free(fifo, buf) free(buf)
-
-enum stream_state
-{
- STREAM_STOPPED,
- STREAM_STARTED,
-};
-
-struct pj_media_stream_t
-{
- pj_media_dir_t dir;
- int pt;
- int state;
- pj_media_stream_stat stat;
- pj_media_stream_t *peer;
- pj_snd_stream_info snd_info;
- pj_snd_stream *snd_stream;
- pj_mutex_t *mutex;
- unsigned in_pkt_size;
- void *in_pkt;
- unsigned out_pkt_size;
- void *out_pkt;
- unsigned pcm_buf_size;
- void *pcm_buf;
- //pj_fifobuf_t fifobuf;
- pj_codec_mgr *codec_mgr;
- pj_codec *codec;
- pj_rtp_session rtp;
- pj_rtcp_session *rtcp;
- pj_jitter_buffer *jb;
- pj_sock_t rtp_sock;
- pj_sock_t rtcp_sock;
- pj_sockaddr_in dst_addr;
- pj_thread_t *transport_thread;
- int thread_quit_flag;
-};
-
-
-static pj_status_t play_callback(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *frame,
- /*inout*/ unsigned size)
-{
- pj_media_stream_t *channel = user_data;
- struct jb_frame *jb_frame;
- void *p;
- pj_uint32_t extseq;
- pj_status_t status;
- struct pj_audio_frame frame_in, frame_out;
-
- PJ_UNUSED_ARG(timestamp)
-
- /* Lock mutex */
- pj_mutex_lock (channel->mutex);
-
- if (!channel->codec) {
- pj_mutex_unlock (channel->mutex);
- return -1;
- }
-
- /* Get frame from jitter buffer. */
- status = pj_jb_get (channel->jb, &extseq, &p);
- jb_frame = p;
- if (status != 0 || jb_frame == NULL) {
- pj_memset(frame, 0, size);
- pj_mutex_unlock(channel->mutex);
- return 0;
- }
-
- /* Decode */
- frame_in.buf = jb_frame->buf;
- frame_in.size = jb_frame->size;
- frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */
- frame_out.buf = channel->pcm_buf;
- status = channel->codec->op->decode (channel->codec, &frame_in,
- channel->pcm_buf_size, &frame_out);
- if (status != 0) {
- PJ_LOG(3, (THISFILE, "decode() has return error status %d",
- status));
-
- pj_memset(frame, 0, size);
- pj_fifobuf_free (&channel->fifobuf, jb_frame);
- pj_mutex_unlock(channel->mutex);
- return 0;
- }
-
- /* Put in sound buffer. */
- if (frame_out.size > size) {
- PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes",
- frame_out.size - size));
- frame_out.size = size;
- }
-
- pj_memcpy(frame, frame_out.buf, size);
-
- pj_fifobuf_free (&channel->fifobuf, jb_frame);
- pj_mutex_unlock(channel->mutex);
- return 0;
-}
-
-static pj_status_t rec_callback( /* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *frame,
- /* in */ unsigned size)
-{
- pj_media_stream_t *channel = user_data;
- pj_status_t status = 0;
- struct pj_audio_frame frame_in, frame_out;
- int ts_len;
- void *rtphdr;
- int rtphdrlen;
- int sent;
-#if 0
- static FILE *fhnd = NULL;
-#endif
-
- PJ_UNUSED_ARG(timestamp)
-
- /* Start locking channel mutex */
- pj_mutex_lock (channel->mutex);
-
- if (!channel->codec) {
- status = -1;
- goto on_return;
- }
-
- /* Encode. */
- frame_in.type = PJ_MEDIA_TYPE_AUDIO;
- frame_in.buf = (void*)frame;
- frame_in.size = size;
- frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr);
- status = channel->codec->op->encode (channel->codec, &frame_in,
- channel->out_pkt_size - sizeof(pj_rtp_hdr),
- &frame_out);
- if (status != 0) {
- PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d",
- status));
- goto on_return;
- }
-
- /* Encapsulate. */
- ts_len = size / (channel->snd_info.bits_per_sample / 8);
- status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0,
- frame_out.size, ts_len,
- (const void**)&rtphdr, &rtphdrlen);
- if (status != 0) {
- PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d",
- status));
- goto on_return;
- }
-
- if (rtphdrlen != sizeof(pj_rtp_hdr)) {
- /* We don't support RTP with extended header yet. */
- PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER);
- PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission"));
- goto on_return;
- }
-
- pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr));
-
- /* Send. */
- sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0,
- &channel->dst_addr, sizeof(channel->dst_addr));
- if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) {
- pj_perror(THISFILE, "Error sending RTP packet to %s:%d",
- pj_sockaddr_get_str_addr(&channel->dst_addr),
- pj_sockaddr_get_port(&channel->dst_addr));
- goto on_return;
- }
-
- /* Update stat */
- channel->stat.pkt_tx++;
- channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr);
-
-#if 0
- if (fhnd == NULL) {
- fhnd = fopen("RTP.DAT", "wb");
- if (fhnd) {
- fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd);
- fclose(fhnd);
- }
- }
-#endif
-
-on_return:
- pj_mutex_unlock (channel->mutex);
- return status;
-}
-
-
-static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg)
-{
- pj_media_stream_t *channel = arg;
-
- while (!channel->thread_quit_flag) {
- int len, size;
- const pj_rtp_hdr *hdr;
- const void *payload;
- unsigned payloadlen;
- int status;
- struct jb_frame *jb_frame;
-
- /* Wait for packet. */
- fd_set fds;
- pj_time_val timeout;
-
- PJ_FD_ZERO (&fds);
- PJ_FD_SET (channel->rtp_sock, &fds);
- timeout.sec = 0;
- timeout.msec = 100;
-
- /* Wait with timeout. */
- status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout);
- if (status != 1)
- continue;
-
- /* Get packet from socket. */
- len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0);
- if (len < 1) {
- if (pj_getlasterror() == PJ_ECONNRESET) {
- /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET
- when the sending side receives ICMP port unreachable.
- */
- continue;
- }
- pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len);
- pj_thread_sleep(1);
- continue;
- }
-
- if (channel->state != STREAM_STARTED)
- continue;
-
- if (channel->thread_quit_flag)
- break;
-
- /* Start locking the channel. */
- pj_mutex_lock (channel->mutex);
-
- /* Update RTP and RTCP session. */
- status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen);
- if (status != 0) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status));
- continue;
- }
- status = pj_rtp_session_update (&channel->rtp, hdr);
- if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status));
- continue;
- }
- pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts));
-
- /* Update stat */
- channel->stat.pkt_rx++;
- channel->stat.oct_rx += len;
-
- /* Copy to FIFO buffer. */
- size = payloadlen+sizeof(struct jb_frame);
- jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size);
- if (jb_frame == NULL) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size));
- continue;
- }
-
- /* Copy the payload */
- jb_frame->size = payloadlen;
- jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame);
- pj_memcpy (jb_frame->buf, payload, payloadlen);
-
- /* Put to jitter buffer. */
- status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame);
- if (status != 0) {
- pj_fifobuf_unalloc (&channel->fifobuf, jb_frame);
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status));
- continue;
- }
-
- pj_mutex_unlock (channel->mutex);
- }
-
- return NULL;
-}
-
-static void init_snd_param_from_codec_attr (pj_snd_stream_info *param,
- const pj_codec_attr *attr)
-{
- param->bits_per_sample = attr->pcm_bits_per_sample;
- param->bytes_per_frame = 2;
- param->frames_per_packet = attr->sample_rate * attr->ptime / 1000;
- param->samples_per_frame = 1;
- param->samples_per_sec = attr->sample_rate;
-}
-
-static pj_media_stream_t *create_channel ( pj_pool_t *pool,
- pj_media_dir_t dir,
- pj_media_stream_t *peer,
- pj_codec_id *codec_id,
- pj_media_stream_create_param *param)
-{
- pj_media_stream_t *channel;
- pj_codec_attr codec_attr;
- void *ptr;
- unsigned size;
- int status;
-
- /* Allocate memory for channel descriptor */
- size = sizeof(pj_media_stream_t);
- channel = pj_pool_calloc(pool, 1, size);
- if (!channel) {
- PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor",
- size));
- return NULL;
- }
-
- channel->dir = dir;
- channel->pt = codec_id->pt;
- channel->peer = peer;
- channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr);
- channel->rtp_sock = param->rtp_sock;
- channel->rtcp_sock = param->rtcp_sock;
- channel->dst_addr = *param->remote_addr;
- channel->state = STREAM_STOPPED;
-
- /* Create mutex for the channel. */
- channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE);
- if (channel->mutex == NULL)
- goto err_cleanup;
-
- /* Create and initialize codec, only if peer is not present.
- We only use one codec instance for both encoder and decoder.
- */
- if (peer && peer->codec) {
- channel->codec = peer->codec;
- status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
- &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
-
- } else {
- channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id);
- if (channel->codec == NULL) {
- goto err_cleanup;
- }
-
- status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
- &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
-
- codec_attr.pt = codec_id->pt;
- status = channel->codec->op->open(channel->codec, &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
- }
-
- /* Allocate buffer for incoming packet. */
- channel->in_pkt_size = PJ_MAX_MTU;
- channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size);
- if (!channel->in_pkt) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer",
- channel->in_pkt_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for outgoing packet. */
- channel->out_pkt_size = sizeof(pj_rtp_hdr) +
- codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000;
- if (channel->out_pkt_size > PJ_MAX_MTU)
- channel->out_pkt_size = PJ_MAX_MTU;
- channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size);
- if (!channel->out_pkt) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer",
- channel->out_pkt_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for decoding to PCM */
- channel->pcm_buf_size = codec_attr.sample_rate *
- codec_attr.pcm_bits_per_sample / 8 *
- PJ_MAX_FRAME_DURATION_MS / 1000;
- channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size);
- if (!channel->pcm_buf) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer",
- channel->pcm_buf_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for frames put in jitter buffer. */
- size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000;
- ptr = pj_pool_alloc(pool, size);
- if (!ptr) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer",
- channel->pcm_buf_size));
- goto err_cleanup;
- }
- //pj_fifobuf_init (&channel->fifobuf, ptr, size);
-
- /* Create and initialize sound device */
- init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr);
-
- if (dir == PJ_MEDIA_DIR_ENCODING)
- channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info,
- &rec_callback, channel);
- else
- channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info,
- &play_callback, channel);
-
- if (!channel->snd_stream)
- goto err_cleanup;
-
- /* Create RTP and RTCP sessions. */
- if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) {
- PJ_LOG(1, (THISFILE, "RTP session initialization error"));
- goto err_cleanup;
- }
-
- /* For decoder, create RTCP session, jitter buffer, and transport thread. */
- if (dir == PJ_MEDIA_DIR_DECODING) {
- channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session));
- if (!channel->rtcp) {
- PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session"));
- goto err_cleanup;
- }
-
- pj_rtcp_init(channel->rtcp, param->ssrc);
-
- channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer));
- if (!channel->jb) {
- PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor"));
- goto err_cleanup;
- }
- if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) {
- PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer"));
- goto err_cleanup;
- }
-
- channel->transport_thread = pj_thread_create(pool, "decode",
- &stream_decoder_transport_thread, channel,
- 0, NULL, 0);
- if (!channel->transport_thread) {
- pj_perror(THISFILE, "Unable to create transport thread");
- goto err_cleanup;
- }
- }
-
- /* Done. */
- return channel;
-
-err_cleanup:
- pj_media_stream_destroy(channel);
- return NULL;
-}
-
-
-PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
- pj_media_stream_t **enc_stream,
- pj_media_stream_t **dec_stream,
- pj_media_stream_create_param *param)
-{
- *dec_stream = *enc_stream = NULL;
-
- if (param->dir & PJ_MEDIA_DIR_DECODING) {
- *dec_stream =
- create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param);
- if (!*dec_stream)
- return -1;
- }
-
- if (param->dir & PJ_MEDIA_DIR_ENCODING) {
- *enc_stream =
- create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param);
- if (!*enc_stream) {
- if (*dec_stream) {
- pj_media_stream_destroy(*dec_stream);
- *dec_stream = NULL;
- }
- return -1;
- }
-
- if (*dec_stream) {
- (*dec_stream)->peer = *enc_stream;
- }
- }
-
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel)
-{
- pj_status_t status;
-
- status = pj_snd_stream_start(channel->snd_stream);
-
- if (status == 0)
- channel->state = STREAM_STARTED;
- return status;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
- pj_media_stream_stat *stat)
-{
- if (stream->dir == PJ_MEDIA_DIR_ENCODING) {
- pj_memcpy (stat, &stream->stat, sizeof(*stat));
- } else {
- pj_rtcp_pkt *rtcp_pkt;
- int len;
-
- pj_memset (stat, 0, sizeof(*stat));
- pj_assert (stream->rtcp != 0);
- pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len);
-
- stat->pkt_rx = stream->stat.pkt_rx;
- stat->oct_rx = stream->stat.oct_rx;
-
- PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
- stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8;
- stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) +
- (rtcp_pkt->rr.total_lost_1 << 8) +
- rtcp_pkt->rr.total_lost_0;
- }
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel)
-{
- PJ_UNUSED_ARG(channel)
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel)
-{
- PJ_UNUSED_ARG(channel)
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel)
-{
- channel->thread_quit_flag = 1;
-
- pj_mutex_lock (channel->mutex);
- if (channel->peer)
- pj_mutex_lock (channel->peer->mutex);
-
- if (channel->jb) {
- /* No need to deinitialize jitter buffer. */
- }
- if (channel->transport_thread) {
- pj_thread_join(channel->transport_thread);
- pj_thread_destroy(channel->transport_thread);
- channel->transport_thread = NULL;
- }
- if (channel->snd_stream != NULL) {
- pj_mutex_unlock (channel->mutex);
- pj_snd_stream_stop(channel->snd_stream);
- pj_mutex_lock (channel->mutex);
- pj_snd_stream_close(channel->snd_stream);
- channel->snd_stream = NULL;
- }
- if (channel->codec) {
- channel->codec->op->close(channel->codec);
- pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec);
- channel->codec = NULL;
- }
- if (channel->peer) {
- pj_media_stream_t *peer = channel->peer;
- peer->peer = NULL;
- peer->codec = NULL;
- peer->thread_quit_flag = 1;
- if (peer->transport_thread) {
- pj_mutex_unlock (peer->mutex);
- pj_thread_join(peer->transport_thread);
- pj_mutex_lock (peer->mutex);
- pj_thread_destroy(peer->transport_thread);
- peer->transport_thread = NULL;
- }
- if (peer->snd_stream) {
- pj_mutex_unlock (peer->mutex);
- pj_snd_stream_stop(peer->snd_stream);
- pj_mutex_lock (peer->mutex);
- pj_snd_stream_close(peer->snd_stream);
- peer->snd_stream = NULL;
- }
- }
-
- channel->state = STREAM_STOPPED;
-
- if (channel->peer)
- pj_mutex_unlock (channel->peer->mutex);
- pj_mutex_unlock(channel->mutex);
- pj_mutex_destroy(channel->mutex);
-
- return 0;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/stream.h> +#include <pjmedia/rtp.h> +#include <pjmedia/rtcp.h> +#include <pjmedia/jbuf.h> +#include <pj/os.h> +#include <pj/log.h> +#include <pj/string.h> /* memcpy() */ +#include <pj/pool.h> +#include <stdlib.h> + +#define THISFILE "stream.c" +#define ERRLEVEL 1 + +#define PJ_MAX_FRAME_DURATION_MS 200 +#define PJ_MAX_BUFFER_SIZE_MS 2000 +#define PJ_MAX_MTU 1500 + +struct jb_frame +{ + unsigned size; + void *buf; +}; + +#define pj_fifobuf_alloc(fifo,size) malloc(size) +#define pj_fifobuf_unalloc(fifo,buf) free(buf) +#define pj_fifobuf_free(fifo, buf) free(buf) + +enum stream_state +{ + STREAM_STOPPED, + STREAM_STARTED, +}; + +struct pj_media_stream_t +{ + pj_media_dir_t dir; + int pt; + int state; + pj_media_stream_stat stat; + pj_media_stream_t *peer; + pj_snd_stream_info snd_info; + pj_snd_stream *snd_stream; + pj_mutex_t *mutex; + unsigned in_pkt_size; + void *in_pkt; + unsigned out_pkt_size; + void *out_pkt; + unsigned pcm_buf_size; + void *pcm_buf; + //pj_fifobuf_t fifobuf; + pj_codec_mgr *codec_mgr; + pj_codec *codec; + pj_rtp_session rtp; + pj_rtcp_session *rtcp; + pj_jitter_buffer *jb; + pj_sock_t rtp_sock; + pj_sock_t rtcp_sock; + pj_sockaddr_in dst_addr; + pj_thread_t *transport_thread; + int thread_quit_flag; +}; + + +static pj_status_t play_callback(/* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* out */ void *frame, + /*inout*/ unsigned size) +{ + pj_media_stream_t *channel = user_data; + struct jb_frame *jb_frame; + void *p; + pj_uint32_t extseq; + pj_status_t status; + struct pj_audio_frame frame_in, frame_out; + + PJ_UNUSED_ARG(timestamp) + + /* Lock mutex */ + pj_mutex_lock (channel->mutex); + + if (!channel->codec) { + pj_mutex_unlock (channel->mutex); + return -1; + } + + /* Get frame from jitter buffer. */ + status = pj_jb_get (channel->jb, &extseq, &p); + jb_frame = p; + if (status != 0 || jb_frame == NULL) { + pj_memset(frame, 0, size); + pj_mutex_unlock(channel->mutex); + return 0; + } + + /* Decode */ + frame_in.buf = jb_frame->buf; + frame_in.size = jb_frame->size; + frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */ + frame_out.buf = channel->pcm_buf; + status = channel->codec->op->decode (channel->codec, &frame_in, + channel->pcm_buf_size, &frame_out); + if (status != 0) { + PJ_LOG(3, (THISFILE, "decode() has return error status %d", + status)); + + pj_memset(frame, 0, size); + pj_fifobuf_free (&channel->fifobuf, jb_frame); + pj_mutex_unlock(channel->mutex); + return 0; + } + + /* Put in sound buffer. */ + if (frame_out.size > size) { + PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes", + frame_out.size - size)); + frame_out.size = size; + } + + pj_memcpy(frame, frame_out.buf, size); + + pj_fifobuf_free (&channel->fifobuf, jb_frame); + pj_mutex_unlock(channel->mutex); + return 0; +} + +static pj_status_t rec_callback( /* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* in */ const void *frame, + /* in */ unsigned size) +{ + pj_media_stream_t *channel = user_data; + pj_status_t status = 0; + struct pj_audio_frame frame_in, frame_out; + int ts_len; + void *rtphdr; + int rtphdrlen; + int sent; +#if 0 + static FILE *fhnd = NULL; +#endif + + PJ_UNUSED_ARG(timestamp) + + /* Start locking channel mutex */ + pj_mutex_lock (channel->mutex); + + if (!channel->codec) { + status = -1; + goto on_return; + } + + /* Encode. */ + frame_in.type = PJ_MEDIA_TYPE_AUDIO; + frame_in.buf = (void*)frame; + frame_in.size = size; + frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr); + status = channel->codec->op->encode (channel->codec, &frame_in, + channel->out_pkt_size - sizeof(pj_rtp_hdr), + &frame_out); + if (status != 0) { + PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d", + status)); + goto on_return; + } + + /* Encapsulate. */ + ts_len = size / (channel->snd_info.bits_per_sample / 8); + status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0, + frame_out.size, ts_len, + (const void**)&rtphdr, &rtphdrlen); + if (status != 0) { + PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d", + status)); + goto on_return; + } + + if (rtphdrlen != sizeof(pj_rtp_hdr)) { + /* We don't support RTP with extended header yet. */ + PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER); + PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission")); + goto on_return; + } + + pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr)); + + /* Send. */ + sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0, + &channel->dst_addr, sizeof(channel->dst_addr)); + if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) { + pj_perror(THISFILE, "Error sending RTP packet to %s:%d", + pj_sockaddr_get_str_addr(&channel->dst_addr), + pj_sockaddr_get_port(&channel->dst_addr)); + goto on_return; + } + + /* Update stat */ + channel->stat.pkt_tx++; + channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr); + +#if 0 + if (fhnd == NULL) { + fhnd = fopen("RTP.DAT", "wb"); + if (fhnd) { + fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd); + fclose(fhnd); + } + } +#endif + +on_return: + pj_mutex_unlock (channel->mutex); + return status; +} + + +static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg) +{ + pj_media_stream_t *channel = arg; + + while (!channel->thread_quit_flag) { + int len, size; + const pj_rtp_hdr *hdr; + const void *payload; + unsigned payloadlen; + int status; + struct jb_frame *jb_frame; + + /* Wait for packet. */ + fd_set fds; + pj_time_val timeout; + + PJ_FD_ZERO (&fds); + PJ_FD_SET (channel->rtp_sock, &fds); + timeout.sec = 0; + timeout.msec = 100; + + /* Wait with timeout. */ + status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout); + if (status != 1) + continue; + + /* Get packet from socket. */ + len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0); + if (len < 1) { + if (pj_getlasterror() == PJ_ECONNRESET) { + /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET + when the sending side receives ICMP port unreachable. + */ + continue; + } + pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len); + pj_thread_sleep(1); + continue; + } + + if (channel->state != STREAM_STARTED) + continue; + + if (channel->thread_quit_flag) + break; + + /* Start locking the channel. */ + pj_mutex_lock (channel->mutex); + + /* Update RTP and RTCP session. */ + status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen); + if (status != 0) { + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status)); + continue; + } + status = pj_rtp_session_update (&channel->rtp, hdr); + if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) { + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status)); + continue; + } + pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts)); + + /* Update stat */ + channel->stat.pkt_rx++; + channel->stat.oct_rx += len; + + /* Copy to FIFO buffer. */ + size = payloadlen+sizeof(struct jb_frame); + jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size); + if (jb_frame == NULL) { + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size)); + continue; + } + + /* Copy the payload */ + jb_frame->size = payloadlen; + jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame); + pj_memcpy (jb_frame->buf, payload, payloadlen); + + /* Put to jitter buffer. */ + status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame); + if (status != 0) { + pj_fifobuf_unalloc (&channel->fifobuf, jb_frame); + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status)); + continue; + } + + pj_mutex_unlock (channel->mutex); + } + + return NULL; +} + +static void init_snd_param_from_codec_attr (pj_snd_stream_info *param, + const pj_codec_attr *attr) +{ + param->bits_per_sample = attr->pcm_bits_per_sample; + param->bytes_per_frame = 2; + param->frames_per_packet = attr->sample_rate * attr->ptime / 1000; + param->samples_per_frame = 1; + param->samples_per_sec = attr->sample_rate; +} + +static pj_media_stream_t *create_channel ( pj_pool_t *pool, + pj_media_dir_t dir, + pj_media_stream_t *peer, + pj_codec_id *codec_id, + pj_media_stream_create_param *param) +{ + pj_media_stream_t *channel; + pj_codec_attr codec_attr; + void *ptr; + unsigned size; + int status; + + /* Allocate memory for channel descriptor */ + size = sizeof(pj_media_stream_t); + channel = pj_pool_calloc(pool, 1, size); + if (!channel) { + PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor", + size)); + return NULL; + } + + channel->dir = dir; + channel->pt = codec_id->pt; + channel->peer = peer; + channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr); + channel->rtp_sock = param->rtp_sock; + channel->rtcp_sock = param->rtcp_sock; + channel->dst_addr = *param->remote_addr; + channel->state = STREAM_STOPPED; + + /* Create mutex for the channel. */ + channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE); + if (channel->mutex == NULL) + goto err_cleanup; + + /* Create and initialize codec, only if peer is not present. + We only use one codec instance for both encoder and decoder. + */ + if (peer && peer->codec) { + channel->codec = peer->codec; + status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, + &codec_attr); + if (status != 0) { + goto err_cleanup; + } + + } else { + channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id); + if (channel->codec == NULL) { + goto err_cleanup; + } + + status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, + &codec_attr); + if (status != 0) { + goto err_cleanup; + } + + codec_attr.pt = codec_id->pt; + status = channel->codec->op->open(channel->codec, &codec_attr); + if (status != 0) { + goto err_cleanup; + } + } + + /* Allocate buffer for incoming packet. */ + channel->in_pkt_size = PJ_MAX_MTU; + channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size); + if (!channel->in_pkt) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer", + channel->in_pkt_size)); + goto err_cleanup; + } + + /* Allocate buffer for outgoing packet. */ + channel->out_pkt_size = sizeof(pj_rtp_hdr) + + codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000; + if (channel->out_pkt_size > PJ_MAX_MTU) + channel->out_pkt_size = PJ_MAX_MTU; + channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size); + if (!channel->out_pkt) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer", + channel->out_pkt_size)); + goto err_cleanup; + } + + /* Allocate buffer for decoding to PCM */ + channel->pcm_buf_size = codec_attr.sample_rate * + codec_attr.pcm_bits_per_sample / 8 * + PJ_MAX_FRAME_DURATION_MS / 1000; + channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size); + if (!channel->pcm_buf) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer", + channel->pcm_buf_size)); + goto err_cleanup; + } + + /* Allocate buffer for frames put in jitter buffer. */ + size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000; + ptr = pj_pool_alloc(pool, size); + if (!ptr) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer", + channel->pcm_buf_size)); + goto err_cleanup; + } + //pj_fifobuf_init (&channel->fifobuf, ptr, size); + + /* Create and initialize sound device */ + init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr); + + if (dir == PJ_MEDIA_DIR_ENCODING) + channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info, + &rec_callback, channel); + else + channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info, + &play_callback, channel); + + if (!channel->snd_stream) + goto err_cleanup; + + /* Create RTP and RTCP sessions. */ + if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) { + PJ_LOG(1, (THISFILE, "RTP session initialization error")); + goto err_cleanup; + } + + /* For decoder, create RTCP session, jitter buffer, and transport thread. */ + if (dir == PJ_MEDIA_DIR_DECODING) { + channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session)); + if (!channel->rtcp) { + PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session")); + goto err_cleanup; + } + + pj_rtcp_init(channel->rtcp, param->ssrc); + + channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer)); + if (!channel->jb) { + PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor")); + goto err_cleanup; + } + if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) { + PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer")); + goto err_cleanup; + } + + channel->transport_thread = pj_thread_create(pool, "decode", + &stream_decoder_transport_thread, channel, + 0, NULL, 0); + if (!channel->transport_thread) { + pj_perror(THISFILE, "Unable to create transport thread"); + goto err_cleanup; + } + } + + /* Done. */ + return channel; + +err_cleanup: + pj_media_stream_destroy(channel); + return NULL; +} + + +PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool, + pj_media_stream_t **enc_stream, + pj_media_stream_t **dec_stream, + pj_media_stream_create_param *param) +{ + *dec_stream = *enc_stream = NULL; + + if (param->dir & PJ_MEDIA_DIR_DECODING) { + *dec_stream = + create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param); + if (!*dec_stream) + return -1; + } + + if (param->dir & PJ_MEDIA_DIR_ENCODING) { + *enc_stream = + create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param); + if (!*enc_stream) { + if (*dec_stream) { + pj_media_stream_destroy(*dec_stream); + *dec_stream = NULL; + } + return -1; + } + + if (*dec_stream) { + (*dec_stream)->peer = *enc_stream; + } + } + + return 0; +} + +PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel) +{ + pj_status_t status; + + status = pj_snd_stream_start(channel->snd_stream); + + if (status == 0) + channel->state = STREAM_STARTED; + return status; +} + +PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, + pj_media_stream_stat *stat) +{ + if (stream->dir == PJ_MEDIA_DIR_ENCODING) { + pj_memcpy (stat, &stream->stat, sizeof(*stat)); + } else { + pj_rtcp_pkt *rtcp_pkt; + int len; + + pj_memset (stat, 0, sizeof(*stat)); + pj_assert (stream->rtcp != 0); + pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len); + + stat->pkt_rx = stream->stat.pkt_rx; + stat->oct_rx = stream->stat.oct_rx; + + PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE) + stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8; + stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) + + (rtcp_pkt->rr.total_lost_1 << 8) + + rtcp_pkt->rr.total_lost_0; + } + return 0; +} + +PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel) +{ + PJ_UNUSED_ARG(channel) + return -1; +} + +PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel) +{ + PJ_UNUSED_ARG(channel) + return -1; +} + +PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel) +{ + channel->thread_quit_flag = 1; + + pj_mutex_lock (channel->mutex); + if (channel->peer) + pj_mutex_lock (channel->peer->mutex); + + if (channel->jb) { + /* No need to deinitialize jitter buffer. */ + } + if (channel->transport_thread) { + pj_thread_join(channel->transport_thread); + pj_thread_destroy(channel->transport_thread); + channel->transport_thread = NULL; + } + if (channel->snd_stream != NULL) { + pj_mutex_unlock (channel->mutex); + pj_snd_stream_stop(channel->snd_stream); + pj_mutex_lock (channel->mutex); + pj_snd_stream_close(channel->snd_stream); + channel->snd_stream = NULL; + } + if (channel->codec) { + channel->codec->op->close(channel->codec); + pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec); + channel->codec = NULL; + } + if (channel->peer) { + pj_media_stream_t *peer = channel->peer; + peer->peer = NULL; + peer->codec = NULL; + peer->thread_quit_flag = 1; + if (peer->transport_thread) { + pj_mutex_unlock (peer->mutex); + pj_thread_join(peer->transport_thread); + pj_mutex_lock (peer->mutex); + pj_thread_destroy(peer->transport_thread); + peer->transport_thread = NULL; + } + if (peer->snd_stream) { + pj_mutex_unlock (peer->mutex); + pj_snd_stream_stop(peer->snd_stream); + pj_mutex_lock (peer->mutex); + pj_snd_stream_close(peer->snd_stream); + peer->snd_stream = NULL; + } + } + + channel->state = STREAM_STOPPED; + + if (channel->peer) + pj_mutex_unlock (channel->peer->mutex); + pj_mutex_unlock(channel->mutex); + pj_mutex_destroy(channel->mutex); + + return 0; +} + diff --git a/pjmedia/src/pjmedia/stream.h b/pjmedia/src/pjmedia/stream.h index a4b2f932..09f6acf1 100644 --- a/pjmedia/src/pjmedia/stream.h +++ b/pjmedia/src/pjmedia/stream.h @@ -1,99 +1,99 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_STREAM_H__
-#define __PJMEDIA_STREAM_H__
-
-
-/**
- * @file stream.h
- * @brief Stream of media.
- */
-
-#include <pjmedia/sound.h>
-#include <pjmedia/codec.h>
-#include <pjmedia/mediamgr.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_SES Media session
- * @ingroup PJMEDIA
- * @{
- */
-
-typedef struct pj_media_stream_t pj_media_stream_t;
-
-/** Parameter for creating channel. */
-typedef struct pj_media_stream_create_param
-{
- /** Codec ID, must NOT be NULL. */
- pj_codec_id *codec_id;
-
- /** Media manager, must NOT be NULL. */
- pj_med_mgr_t *mediamgr;
-
- /** Direction: IN_OUT, or IN only, or OUT only. */
- pj_media_dir_t dir;
-
- /** RTP socket. */
- pj_sock_t rtp_sock;
-
- /** RTCP socket. */
- pj_sock_t rtcp_sock;
-
- /** Address of remote */
- pj_sockaddr_in *remote_addr;
-
- /** RTP SSRC */
- pj_uint32_t ssrc;
-
- /** Jitter buffer parameters. */
- int jb_min, jb_max, jb_maxcnt;
-
-} pj_media_stream_create_param;
-
-typedef struct pj_media_stream_stat
-{
- pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */
- pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */
- pj_uint32_t jitter; /* receive jitter in ms */
- pj_uint32_t pkt_lost; /* total packet lost count */
-} pj_media_stream_stat;
-
-PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
- pj_media_stream_t **enc_stream,
- pj_media_stream_t **dec_stream,
- pj_media_stream_create_param *param);
-PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
- pj_media_stream_stat *stat);
-PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_STREAM_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_STREAM_H__ +#define __PJMEDIA_STREAM_H__ + + +/** + * @file stream.h + * @brief Stream of media. + */ + +#include <pjmedia/sound.h> +#include <pjmedia/codec.h> +#include <pjmedia/mediamgr.h> +#include <pj/sock.h> + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_SES Media session + * @ingroup PJMEDIA + * @{ + */ + +typedef struct pj_media_stream_t pj_media_stream_t; + +/** Parameter for creating channel. */ +typedef struct pj_media_stream_create_param +{ + /** Codec ID, must NOT be NULL. */ + pj_codec_id *codec_id; + + /** Media manager, must NOT be NULL. */ + pj_med_mgr_t *mediamgr; + + /** Direction: IN_OUT, or IN only, or OUT only. */ + pj_media_dir_t dir; + + /** RTP socket. */ + pj_sock_t rtp_sock; + + /** RTCP socket. */ + pj_sock_t rtcp_sock; + + /** Address of remote */ + pj_sockaddr_in *remote_addr; + + /** RTP SSRC */ + pj_uint32_t ssrc; + + /** Jitter buffer parameters. */ + int jb_min, jb_max, jb_maxcnt; + +} pj_media_stream_create_param; + +typedef struct pj_media_stream_stat +{ + pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */ + pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */ + pj_uint32_t jitter; /* receive jitter in ms */ + pj_uint32_t pkt_lost; /* total packet lost count */ +} pj_media_stream_stat; + +PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool, + pj_media_stream_t **enc_stream, + pj_media_stream_t **dec_stream, + pj_media_stream_create_param *param); +PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream); +PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, + pj_media_stream_stat *stat); +PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream); +PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream); +PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_STREAM_H__ */ diff --git a/pjsip/include/pjsip_auth.h b/pjsip/include/pjsip_auth.h index d081e34a..7ca8f0a1 100644 --- a/pjsip/include/pjsip_auth.h +++ b/pjsip/include/pjsip_auth.h @@ -1,37 +1,37 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_AUTH_H__
-#define __PJSIP_AUTH_H__
-
-/**
- * @defgroup PJSIP_AUTH SIP Authorization module
- */
-
-/**
- * @file pjsip_auth.h
- * @brief SIP Authorization Module.
- */
-
-
-#include <pjsip_auth/sip_auth.h>
-#include <pjsip_auth/sip_auth_msg.h>
-#include <pjsip_auth/sip_auth_parser.h>
-
-#endif /* __PJSIP_AUTH_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_AUTH_H__ +#define __PJSIP_AUTH_H__ + +/** + * @defgroup PJSIP_AUTH SIP Authorization module + */ + +/** + * @file pjsip_auth.h + * @brief SIP Authorization Module. + */ + + +#include <pjsip_auth/sip_auth.h> +#include <pjsip_auth/sip_auth_msg.h> +#include <pjsip_auth/sip_auth_parser.h> + +#endif /* __PJSIP_AUTH_H__ */ + diff --git a/pjsip/include/pjsip_core.h b/pjsip/include/pjsip_core.h index 6edfda89..6ae1c2cf 100644 --- a/pjsip/include/pjsip_core.h +++ b/pjsip/include/pjsip_core.h @@ -1,37 +1,37 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_CORE_H__
-#define __PJSIP_CORE_H__
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_auth.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_errno.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_resolve.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_util.h>
-
-#endif /* __PJSIP_CORE_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_CORE_H__ +#define __PJSIP_CORE_H__ + +#include <pjsip/sip_types.h> +#include <pjsip/sip_auth.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_errno.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_parser.h> +#include <pjsip/sip_resolve.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_transport.h> +#include <pjsip/sip_uri.h> +#include <pjsip/sip_util.h> + +#endif /* __PJSIP_CORE_H__ */ + diff --git a/pjsip/include/pjsip_simple.h b/pjsip/include/pjsip_simple.h index e878130c..29034e63 100644 --- a/pjsip/include/pjsip_simple.h +++ b/pjsip/include/pjsip_simple.h @@ -1,41 +1,41 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/**
- * @defgroup PJSIP_SIMPLE SIP Event, Instant Messaging and Presence Extension (SIMPLE)
- */
-
-/**
- * @file pjsip_simple.h
- * @brief SIP SIMPLE Extension
- */
-
-/*
- * Include this header file to get all functionalities for SIMPLE extension
- * (SIP for Instant Messaging and Presence Leveraging Extension).
- */
-#ifndef __PJSIP_SIMPLE_H__
-#define __PJSIP_SIMPLE_H__
-
-#include <pjsip_simple/messaging.h>
-#include <pjsip_simple/event_notify.h>
-#include <pjsip_simple/pidf.h>
-#include <pjsip_simple/presence.h>
-
-#endif /* __PJSIP_SIMPLE_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @defgroup PJSIP_SIMPLE SIP Event, Instant Messaging and Presence Extension (SIMPLE) + */ + +/** + * @file pjsip_simple.h + * @brief SIP SIMPLE Extension + */ + +/* + * Include this header file to get all functionalities for SIMPLE extension + * (SIP for Instant Messaging and Presence Leveraging Extension). + */ +#ifndef __PJSIP_SIMPLE_H__ +#define __PJSIP_SIMPLE_H__ + +#include <pjsip_simple/messaging.h> +#include <pjsip_simple/event_notify.h> +#include <pjsip_simple/pidf.h> +#include <pjsip_simple/presence.h> + +#endif /* __PJSIP_SIMPLE_H__ */ diff --git a/pjsip/include/pjsip_ua.h b/pjsip/include/pjsip_ua.h index d15aa8fe..e2c640e9 100644 --- a/pjsip/include/pjsip_ua.h +++ b/pjsip/include/pjsip_ua.h @@ -1,28 +1,28 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __PJSIP_UA_H__
-#define __PJSIP_UA_H__
-
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_reg.h>
-#include <pjsip_mod_ua/sip_ua.h>
-
-#endif /* __PJSIP_UA_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.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 of the License, 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PJSIP_UA_H__ +#define __PJSIP_UA_H__ + +#include <pjsip_mod_ua/sip_dialog.h> +#include <pjsip_mod_ua/sip_reg.h> +#include <pjsip_mod_ua/sip_ua.h> + +#endif /* __PJSIP_UA_H__ */ + |