From 1c2d17839760b0d8ad5a02e4c84bd8f99fac4e98 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Sun, 25 Nov 2018 20:15:26 +0100 Subject: score: Remove Objects_Information::maximum This information is already present in Objects_Information::maximum_id. Add and use _Objects_Get_maximum_index(). Update #3621. --- cpukit/include/rtems/score/objectimpl.h | 18 +++++-- cpukit/posix/src/killinfo.c | 2 +- cpukit/rtems/src/rtemsobjectgetclassinfo.c | 2 +- cpukit/score/src/objectactivecount.c | 11 ++-- cpukit/score/src/objectextendinformation.c | 20 +++---- cpukit/score/src/objectgetinfo.c | 2 +- cpukit/score/src/objectgetnext.c | 22 ++++---- cpukit/score/src/objectinitializeinformation.c | 17 ++---- cpukit/score/src/objectnametoid.c | 6 ++- cpukit/score/src/objectnametoidstring.c | 11 ++-- cpukit/score/src/objectshrinkinformation.c | 2 +- cpukit/score/src/threaditerate.c | 9 ++-- testsuites/sptests/spsysinit01/init.c | 72 ++++++++++++++------------ 13 files changed, 105 insertions(+), 89 deletions(-) diff --git a/cpukit/include/rtems/score/objectimpl.h b/cpukit/include/rtems/score/objectimpl.h index a516e21c65..cf670b9c98 100644 --- a/cpukit/include/rtems/score/objectimpl.h +++ b/cpukit/include/rtems/score/objectimpl.h @@ -124,8 +124,6 @@ typedef struct { Objects_Id maximum_id; /** This points to the table of local objects. */ Objects_Control **local_table; - /** This is the maximum number of objects in this class. */ - Objects_Maximum maximum; /** This is the number of objects on the Inactive list. */ Objects_Maximum inactive; /** This is the number of objects in a block. */ @@ -850,6 +848,20 @@ RTEMS_INLINE_ROUTINE Objects_Id _Objects_Get_minimum_id( Objects_Id id ) return id; } +/** + * Returns the maximum index of the specified object class. + * + * @param[in] information The object information. + * + * @return The maximum index of the specified object class. + */ +RTEMS_INLINE_ROUTINE Objects_Maximum _Objects_Get_maximum_index( + const Objects_Information *information +) +{ + return _Objects_Get_index( information->maximum_id ); +} + /** * This function sets the pointer to the local_table object * referenced by the index. @@ -876,7 +888,7 @@ RTEMS_INLINE_ROUTINE void _Objects_Set_local_object( * occur in normal situations. */ _Assert( index >= OBJECTS_INDEX_MINIMUM ); - _Assert( index <= information->maximum ); + _Assert( index <= _Objects_Get_maximum_index( information ) ); information->local_table[ index - OBJECTS_INDEX_MINIMUM ] = the_object; } diff --git a/cpukit/posix/src/killinfo.c b/cpukit/posix/src/killinfo.c index cabd91c700..0031b7bdd8 100644 --- a/cpukit/posix/src/killinfo.c +++ b/cpukit/posix/src/killinfo.c @@ -207,7 +207,7 @@ int _POSIX_signals_Send( if ( !the_info ) continue; - maximum = the_info->maximum; + maximum = _Objects_Get_maximum_index( the_info ); object_table = the_info->local_table; for ( index = 0 ; index < maximum ; ++index ) { diff --git a/cpukit/rtems/src/rtemsobjectgetclassinfo.c b/cpukit/rtems/src/rtemsobjectgetclassinfo.c index 3ebe3535f5..a2b07e36ea 100644 --- a/cpukit/rtems/src/rtemsobjectgetclassinfo.c +++ b/cpukit/rtems/src/rtemsobjectgetclassinfo.c @@ -47,7 +47,7 @@ rtems_status_code rtems_object_get_class_information( info->minimum_id = _Objects_Get_minimum_id( obj_info->maximum_id ); info->maximum_id = obj_info->maximum_id; info->auto_extend = obj_info->auto_extend; - info->maximum = obj_info->maximum; + info->maximum = _Objects_Get_maximum_index( obj_info ); for ( unallocated=0, i=1 ; i <= info->maximum ; i++ ) if ( !obj_info->local_table[i] ) diff --git a/cpukit/score/src/objectactivecount.c b/cpukit/score/src/objectactivecount.c index 376820158a..d29ce0736d 100644 --- a/cpukit/score/src/objectactivecount.c +++ b/cpukit/score/src/objectactivecount.c @@ -24,13 +24,14 @@ Objects_Maximum _Objects_Active_count( const Objects_Information *information ) { - size_t inactive; - size_t maximum; + Objects_Maximum inactive; + Objects_Maximum maximum; _Assert( _Objects_Allocator_is_owner() ); - inactive = _Chain_Node_count_unprotected( &information->Inactive ); - maximum = information->maximum; + inactive = (Objects_Maximum) + _Chain_Node_count_unprotected( &information->Inactive ); + maximum = _Objects_Get_maximum_index( information ); - return (Objects_Maximum) ( maximum - inactive ); + return maximum - inactive; } diff --git a/cpukit/score/src/objectextendinformation.c b/cpukit/score/src/objectextendinformation.c index a8346ce937..57ce6737ec 100644 --- a/cpukit/score/src/objectextendinformation.c +++ b/cpukit/score/src/objectextendinformation.c @@ -49,7 +49,8 @@ void _Objects_Extend_information( uint32_t index_base; uint32_t index_end; uint32_t index; - uint32_t maximum; + Objects_Maximum old_maximum; + uint32_t new_maximum; size_t object_block_size; Objects_Control *new_object_block; bool do_extend; @@ -59,6 +60,8 @@ void _Objects_Extend_information( || !_System_state_Is_up( _System_state_Get() ) ); + old_maximum = _Objects_Get_maximum_index( information ); + /* * Search for a free block of indexes. If we do NOT need to allocate or * extend the block table, then we will change do_extend. @@ -70,7 +73,7 @@ void _Objects_Extend_information( if ( information->object_blocks == NULL ) block_count = 0; else { - block_count = information->maximum / information->objects_per_block; + block_count = old_maximum / information->objects_per_block; for ( ; block < block_count; block++ ) { if ( information->object_blocks[ block ] == NULL ) { @@ -82,14 +85,14 @@ void _Objects_Extend_information( } index_end = index_base + information->objects_per_block; - maximum = (uint32_t) information->maximum + information->objects_per_block; + new_maximum = (uint32_t) old_maximum + information->objects_per_block; /* * We need to limit the number of objects to the maximum number * representable in the index portion of the object Id. In the * case of 16-bit Ids, this is only 256 object instances. */ - if ( maximum > OBJECTS_ID_FINAL_INDEX ) { + if ( new_maximum > OBJECTS_ID_FINAL_INDEX ) { return; } @@ -146,7 +149,7 @@ void _Objects_Extend_information( * Allocate the tables and break it up. */ object_blocks_size = block_count * sizeof( *object_blocks ); - local_table_size = maximum * sizeof( *local_table ); + local_table_size = new_maximum * sizeof( *local_table ); table_size = object_blocks_size + local_table_size + block_count * sizeof( *inactive_per_block ); @@ -178,7 +181,7 @@ void _Objects_Extend_information( */ block_count--; - if ( information->maximum > 0 ) { + if ( old_maximum > 0 ) { /* * Copy each section of the table over. This has to be performed as * separate parts as size of each block has changed. @@ -196,7 +199,7 @@ void _Objects_Extend_information( memcpy( local_table, information->local_table, - information->maximum * sizeof( *local_table ) + old_maximum * sizeof( *local_table ) ); } @@ -215,12 +218,11 @@ void _Objects_Extend_information( information->object_blocks = object_blocks; information->inactive_per_block = inactive_per_block; information->local_table = local_table; - information->maximum = (Objects_Maximum) maximum; information->maximum_id = _Objects_Build_id( information->the_api, information->the_class, _Objects_Local_node, - information->maximum + new_maximum ); _ISR_lock_ISR_enable( &lock_context ); diff --git a/cpukit/score/src/objectgetinfo.c b/cpukit/score/src/objectgetinfo.c index 6ba561d368..c927d1c944 100644 --- a/cpukit/score/src/objectgetinfo.c +++ b/cpukit/score/src/objectgetinfo.c @@ -55,7 +55,7 @@ Objects_Information *_Objects_Get_information( * pointer. */ #if !defined(RTEMS_MULTIPROCESSING) - if ( info->maximum == 0 ) + if ( _Objects_Get_maximum_index( info ) == 0 ) return NULL; #endif diff --git a/cpukit/score/src/objectgetnext.c b/cpukit/score/src/objectgetnext.c index 054ed22fc1..156bee72a4 100644 --- a/cpukit/score/src/objectgetnext.c +++ b/cpukit/score/src/objectgetnext.c @@ -28,6 +28,7 @@ Objects_Control *_Objects_Get_next( { Objects_Control *the_object; Objects_Id next_id; + Objects_Maximum maximum; if ( !information ) return NULL; @@ -41,21 +42,20 @@ Objects_Control *_Objects_Get_next( next_id = id; _Objects_Allocator_lock(); + maximum = _Objects_Get_maximum_index( information ); do { - /* walked off end of list? */ - if (_Objects_Get_index(next_id) > information->maximum) - { - _Objects_Allocator_unlock(); - *next_id_p = OBJECTS_ID_FINAL; - return NULL; - } + /* walked off end of list? */ + if (_Objects_Get_index( next_id ) > maximum) { + _Objects_Allocator_unlock(); + *next_id_p = OBJECTS_ID_FINAL; + return NULL; + } - /* try to grab one */ - the_object = _Objects_Get_no_protection( next_id, information ); - - next_id++; + /* try to grab one */ + the_object = _Objects_Get_no_protection( next_id, information ); + next_id++; } while ( the_object == NULL ); *next_id_p = next_id; diff --git a/cpukit/score/src/objectinitializeinformation.c b/cpukit/score/src/objectinitializeinformation.c index 530e4e10ba..c1327aece8 100644 --- a/cpukit/score/src/objectinitializeinformation.c +++ b/cpukit/score/src/objectinitializeinformation.c @@ -39,20 +39,9 @@ void _Objects_Do_initialize_information( { Objects_Maximum maximum_per_allocation; - information->the_api = the_api; - information->the_class = the_class; - information->object_size = object_size; - information->local_table = 0; - information->inactive_per_block = 0; - information->object_blocks = 0; - information->inactive = 0; - information->local_table = NULL; - - /* - * Set the maximum value to 0. It will be updated when objects are - * added to the inactive set from _Objects_Extend_information() - */ - information->maximum = 0; + information->the_api = the_api; + information->the_class = the_class; + information->object_size = object_size; /* * Register this Object Class in the Object Information Table. diff --git a/cpukit/score/src/objectnametoid.c b/cpukit/score/src/objectnametoid.c index c3b014e3e1..6a89b874e7 100644 --- a/cpukit/score/src/objectnametoid.c +++ b/cpukit/score/src/objectnametoid.c @@ -29,6 +29,7 @@ Objects_Name_or_id_lookup_errors _Objects_Name_to_id_u32( { bool search_local_node; Objects_Control *the_object; + Objects_Maximum maximum; Objects_Maximum index; #if defined(RTEMS_MULTIPROCESSING) Objects_Name name_for_mp; @@ -42,9 +43,10 @@ Objects_Name_or_id_lookup_errors _Objects_Name_to_id_u32( if ( name == 0 ) return OBJECTS_INVALID_NAME; + maximum = _Objects_Get_maximum_index( information ); search_local_node = false; - if ( information->maximum != 0 && + if ( maximum > 0 && (node == OBJECTS_SEARCH_ALL_NODES || node == OBJECTS_SEARCH_LOCAL_NODE || _Objects_Is_local_node( node ) @@ -52,7 +54,7 @@ Objects_Name_or_id_lookup_errors _Objects_Name_to_id_u32( search_local_node = true; if ( search_local_node ) { - for ( index = 0; index < information->maximum; ++index ) { + for ( index = 0; index < maximum; ++index ) { the_object = information->local_table[ index ]; if ( !the_object ) continue; diff --git a/cpukit/score/src/objectnametoidstring.c b/cpukit/score/src/objectnametoidstring.c index a015d5fc25..d357339ea7 100644 --- a/cpukit/score/src/objectnametoidstring.c +++ b/cpukit/score/src/objectnametoidstring.c @@ -29,9 +29,10 @@ Objects_Control *_Objects_Get_by_name( Objects_Get_by_name_error *error ) { - size_t name_length; - size_t max_name_length; - uint32_t index; + size_t name_length; + size_t max_name_length; + Objects_Maximum maximum; + Objects_Maximum index; _Assert( _Objects_Has_string_name( information ) ); _Assert( _Objects_Allocator_is_owner() ); @@ -52,7 +53,9 @@ Objects_Control *_Objects_Get_by_name( *name_length_p = name_length; } - for ( index = 0; index < information->maximum; ++index ) { + maximum = _Objects_Get_maximum_index( information ); + + for ( index = 0; index < maximum; ++index ) { Objects_Control *the_object; the_object = information->local_table[ index ]; diff --git a/cpukit/score/src/objectshrinkinformation.c b/cpukit/score/src/objectshrinkinformation.c index 3b1841a16b..5a53be9979 100644 --- a/cpukit/score/src/objectshrinkinformation.c +++ b/cpukit/score/src/objectshrinkinformation.c @@ -39,7 +39,7 @@ void _Objects_Shrink_information( */ objects_per_block = information->objects_per_block; - block_count = information->maximum / objects_per_block; + block_count = _Objects_Get_maximum_index( information ) / objects_per_block; index_base = 0; for ( block = 0; block < block_count; block++ ) { diff --git a/cpukit/score/src/threaditerate.c b/cpukit/score/src/threaditerate.c index 9fa9b89166..8d1614ab9b 100644 --- a/cpukit/score/src/threaditerate.c +++ b/cpukit/score/src/threaditerate.c @@ -27,7 +27,8 @@ void _Thread_Iterate( for ( api_index = 1 ; api_index <= OBJECTS_APIS_LAST ; ++api_index ) { const Objects_Information *information; - Objects_Maximum i; + Objects_Maximum maximum; + Objects_Maximum index; if ( _Objects_Information_table[ api_index ] == NULL ) { continue; @@ -39,10 +40,12 @@ void _Thread_Iterate( continue; } - for ( i = 0 ; i < information->maximum ; ++i ) { + maximum = _Objects_Get_maximum_index( information ); + + for ( index = 0 ; index < maximum ; ++index ) { Thread_Control *the_thread; - the_thread = (Thread_Control *) information->local_table[ i ]; + the_thread = (Thread_Control *) information->local_table[ index ]; if ( the_thread != NULL ) { bool done; diff --git a/testsuites/sptests/spsysinit01/init.c b/testsuites/sptests/spsysinit01/init.c index 00d07fb18a..6d0c431a85 100644 --- a/testsuites/sptests/spsysinit01/init.c +++ b/testsuites/sptests/spsysinit01/init.c @@ -248,49 +248,53 @@ LAST(RTEMS_SYSINIT_INITIAL_EXTENSIONS) FIRST(RTEMS_SYSINIT_DATA_STRUCTURES) { - assert(_Thread_Internal_information.Objects.maximum == 0); + assert( + _Objects_Get_maximum_index(&_Thread_Internal_information.Objects) == 0 + ); next_step(DATA_STRUCTURES_PRE); } LAST(RTEMS_SYSINIT_DATA_STRUCTURES) { - assert(_Thread_Internal_information.Objects.maximum != 0); + assert( + _Objects_Get_maximum_index(&_Thread_Internal_information.Objects) != 0 + ); next_step(DATA_STRUCTURES_POST); } FIRST(RTEMS_SYSINIT_USER_EXTENSIONS) { - assert(_Extension_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Extension_Information) == 0); next_step(USER_EXTENSIONS_PRE); } LAST(RTEMS_SYSINIT_USER_EXTENSIONS) { - assert(_Extension_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Extension_Information) != 0); next_step(USER_EXTENSIONS_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_TASKS) { - assert(_RTEMS_tasks_Information.Objects.maximum == 0); + assert(_Objects_Get_maximum_index(&_RTEMS_tasks_Information.Objects) == 0); next_step(CLASSIC_TASKS_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_TASKS) { - assert(_RTEMS_tasks_Information.Objects.maximum != 0); + assert(_Objects_Get_maximum_index(&_RTEMS_tasks_Information.Objects) != 0); next_step(CLASSIC_TASKS_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_TIMER) { - assert(_Timer_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Timer_Information) == 0); next_step(CLASSIC_TIMER_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_TIMER) { - assert(_Timer_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Timer_Information) != 0); next_step(CLASSIC_TIMER_POST); } @@ -318,85 +322,85 @@ LAST(RTEMS_SYSINIT_CLASSIC_EVENT) FIRST(RTEMS_SYSINIT_CLASSIC_MESSAGE_QUEUE) { - assert(_Message_queue_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Message_queue_Information) == 0); next_step(CLASSIC_MESSAGE_QUEUE_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_MESSAGE_QUEUE) { - assert(_Message_queue_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Message_queue_Information) != 0); next_step(CLASSIC_MESSAGE_QUEUE_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_SEMAPHORE) { - assert(_Semaphore_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Semaphore_Information) == 0); next_step(CLASSIC_SEMAPHORE_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_SEMAPHORE) { - assert(_Semaphore_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Semaphore_Information) != 0); next_step(CLASSIC_SEMAPHORE_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_PARTITION) { - assert(_Partition_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Partition_Information) == 0); next_step(CLASSIC_PARTITION_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_PARTITION) { - assert(_Partition_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Partition_Information) != 0); next_step(CLASSIC_PARTITION_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_REGION) { - assert(_Region_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Region_Information) == 0); next_step(CLASSIC_REGION_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_REGION) { - assert(_Region_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Region_Information) != 0); next_step(CLASSIC_REGION_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_DUAL_PORTED_MEMORY) { - assert(_Dual_ported_memory_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Dual_ported_memory_Information) == 0); next_step(CLASSIC_DUAL_PORTED_MEMORY_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_DUAL_PORTED_MEMORY) { - assert(_Dual_ported_memory_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Dual_ported_memory_Information) != 0); next_step(CLASSIC_DUAL_PORTED_MEMORY_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_RATE_MONOTONIC) { - assert(_Rate_monotonic_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Rate_monotonic_Information) == 0); next_step(CLASSIC_RATE_MONOTONIC_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_RATE_MONOTONIC) { - assert(_Rate_monotonic_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Rate_monotonic_Information) != 0); next_step(CLASSIC_RATE_MONOTONIC_POST); } FIRST(RTEMS_SYSINIT_CLASSIC_BARRIER) { - assert(_Barrier_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_Barrier_Information) == 0); next_step(CLASSIC_BARRIER_PRE); } LAST(RTEMS_SYSINIT_CLASSIC_BARRIER) { - assert(_Barrier_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_Barrier_Information) != 0); next_step(CLASSIC_BARRIER_POST); } @@ -428,63 +432,63 @@ LAST(RTEMS_SYSINIT_POSIX_SIGNALS) FIRST(RTEMS_SYSINIT_POSIX_THREADS) { - assert(_POSIX_Threads_Information.Objects.maximum == 0); + assert(_Objects_Get_maximum_index(&_POSIX_Threads_Information.Objects) == 0); next_step(POSIX_THREADS_PRE); } LAST(RTEMS_SYSINIT_POSIX_THREADS) { - assert(_POSIX_Threads_Information.Objects.maximum != 0); + assert(_Objects_Get_maximum_index(&_POSIX_Threads_Information.Objects) != 0); next_step(POSIX_THREADS_POST); } FIRST(RTEMS_SYSINIT_POSIX_MESSAGE_QUEUE) { - assert(_POSIX_Message_queue_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_POSIX_Message_queue_Information) == 0); next_step(POSIX_MESSAGE_QUEUE_PRE); } LAST(RTEMS_SYSINIT_POSIX_MESSAGE_QUEUE) { - assert(_POSIX_Message_queue_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_POSIX_Message_queue_Information) != 0); next_step(POSIX_MESSAGE_QUEUE_POST); } FIRST(RTEMS_SYSINIT_POSIX_SEMAPHORE) { - assert(_POSIX_Semaphore_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_POSIX_Semaphore_Information) == 0); next_step(POSIX_SEMAPHORE_PRE); } LAST(RTEMS_SYSINIT_POSIX_SEMAPHORE) { - assert(_POSIX_Semaphore_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_POSIX_Semaphore_Information) != 0); next_step(POSIX_SEMAPHORE_POST); } #ifdef RTEMS_POSIX_API FIRST(RTEMS_SYSINIT_POSIX_TIMER) { - assert(_POSIX_Timer_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_POSIX_Timer_Information) == 0); next_step(POSIX_TIMER_PRE); } LAST(RTEMS_SYSINIT_POSIX_TIMER) { - assert(_POSIX_Timer_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_POSIX_Timer_Information) != 0); next_step(POSIX_TIMER_POST); } #endif /* RTEMS_POSIX_API */ FIRST(RTEMS_SYSINIT_POSIX_SHM) { - assert(_POSIX_Shm_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_POSIX_Shm_Information) == 0); next_step(POSIX_SHM_PRE); } LAST(RTEMS_SYSINIT_POSIX_SHM) { - assert(_POSIX_Shm_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_POSIX_Shm_Information) != 0); next_step(POSIX_SHM_POST); } @@ -508,13 +512,13 @@ LAST(RTEMS_SYSINIT_POSIX_CLEANUP) FIRST(RTEMS_SYSINIT_POSIX_KEYS) { - assert(_POSIX_Keys_Information.maximum == 0); + assert(_Objects_Get_maximum_index(&_POSIX_Keys_Information) == 0); next_step(POSIX_KEYS_PRE); } LAST(RTEMS_SYSINIT_POSIX_KEYS) { - assert(_POSIX_Keys_Information.maximum != 0); + assert(_Objects_Get_maximum_index(&_POSIX_Keys_Information) != 0); next_step(POSIX_KEYS_POST); } -- cgit v1.2.3