diff options
author | Supriti Singh <supritisingh08@gmail.com> | 2014-08-28 20:09:30 +0000 |
---|---|---|
committer | Supriti Singh <supritisingh08@gmail.com> | 2014-08-28 20:09:30 +0000 |
commit | 6c20cb042f548dbd98a89ed457c82212090ea7b0 (patch) | |
tree | 36cf658d4b43376b7ee2a4e52ff73507c6641647 /src | |
parent | 19d2226cb1957fbe7e5f825b87849b6028615a19 (diff) | |
download | gnunet-6c20cb042f548dbd98a89ed457c82212090ea7b0.tar.gz gnunet-6c20cb042f548dbd98a89ed457c82212090ea7b0.zip |
Readding trail compression to check if it can reduce trail length
Diffstat (limited to 'src')
-rw-r--r-- | src/dht/gnunet-service-xdht_neighbours.c | 306 | ||||
-rw-r--r-- | src/include/gnunet_protocols.h | 7 |
2 files changed, 293 insertions, 20 deletions
diff --git a/src/dht/gnunet-service-xdht_neighbours.c b/src/dht/gnunet-service-xdht_neighbours.c index 91e96d8a8..c328a6734 100644 --- a/src/dht/gnunet-service-xdht_neighbours.c +++ b/src/dht/gnunet-service-xdht_neighbours.c | |||
@@ -118,7 +118,7 @@ | |||
118 | /** | 118 | /** |
119 | * Maximum number of trails stored per finger. | 119 | * Maximum number of trails stored per finger. |
120 | */ | 120 | */ |
121 | #define MAXIMUM_TRAILS_PER_FINGER 1 | 121 | #define MAXIMUM_TRAILS_PER_FINGER 2 |
122 | 122 | ||
123 | /** | 123 | /** |
124 | * Finger map index for predecessor entry in finger table. | 124 | * Finger map index for predecessor entry in finger table. |
@@ -630,6 +630,34 @@ struct PeerAddTrailMessage | |||
630 | */ | 630 | */ |
631 | }; | 631 | }; |
632 | 632 | ||
633 | /** | ||
634 | * P2P Trail Compression Message. | ||
635 | */ | ||
636 | struct PeerTrailCompressionMessage | ||
637 | { | ||
638 | /** | ||
639 | * Type: #GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_COMPRESSION | ||
640 | */ | ||
641 | struct GNUNET_MessageHeader header; | ||
642 | |||
643 | /** | ||
644 | * Source peer of this trail. | ||
645 | */ | ||
646 | struct GNUNET_PeerIdentity source_peer; | ||
647 | |||
648 | /** | ||
649 | * Trail from source_peer to destination_peer compressed such that | ||
650 | * new_first_friend is the first hop in the trail from source to | ||
651 | * destination. | ||
652 | */ | ||
653 | struct GNUNET_PeerIdentity new_first_friend; | ||
654 | |||
655 | /** | ||
656 | * Unique identifier of trail. | ||
657 | */ | ||
658 | struct GNUNET_HashCode trail_id; | ||
659 | }; | ||
660 | |||
633 | GNUNET_NETWORK_STRUCT_END | 661 | GNUNET_NETWORK_STRUCT_END |
634 | 662 | ||
635 | /** | 663 | /** |
@@ -1452,6 +1480,55 @@ GDS_NEIGHBOURS_send_trail_teardown (struct GNUNET_HashCode trail_id, | |||
1452 | process_friend_queue (target_friend); | 1480 | process_friend_queue (target_friend); |
1453 | } | 1481 | } |
1454 | 1482 | ||
1483 | /** | ||
1484 | * Construct a trail compression message and send it to target_friend. | ||
1485 | * @param source_peer Source of the trail. | ||
1486 | * @param trail_id Unique identifier of trail. | ||
1487 | * @param first_friend First hop in compressed trail to reach from source to finger | ||
1488 | * @param target_friend Next friend to get this message. | ||
1489 | */ | ||
1490 | void | ||
1491 | GDS_NEIGHBOURS_send_trail_compression (struct GNUNET_PeerIdentity source_peer, | ||
1492 | struct GNUNET_HashCode trail_id, | ||
1493 | struct GNUNET_PeerIdentity first_friend, | ||
1494 | struct FriendInfo *target_friend) | ||
1495 | { | ||
1496 | struct P2PPendingMessage *pending; | ||
1497 | struct PeerTrailCompressionMessage *tcm; | ||
1498 | size_t msize; | ||
1499 | |||
1500 | msize = sizeof (struct PeerTrailCompressionMessage); | ||
1501 | |||
1502 | if (msize >= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE) | ||
1503 | { | ||
1504 | GNUNET_break (0); | ||
1505 | return; | ||
1506 | } | ||
1507 | |||
1508 | if (target_friend->pending_count >= MAXIMUM_PENDING_PER_FRIEND) | ||
1509 | { | ||
1510 | GNUNET_STATISTICS_update (GDS_stats, | ||
1511 | gettext_noop ("# P2P messages dropped due to full queue"), | ||
1512 | 1, GNUNET_NO); | ||
1513 | } | ||
1514 | |||
1515 | pending = GNUNET_malloc (sizeof (struct P2PPendingMessage) + msize); | ||
1516 | pending->importance = 0; /* FIXME */ | ||
1517 | pending->timeout = GNUNET_TIME_relative_to_absolute (PENDING_MESSAGE_TIMEOUT); | ||
1518 | tcm = (struct PeerTrailCompressionMessage *) &pending[1]; | ||
1519 | pending->msg = &tcm->header; | ||
1520 | tcm->header.size = htons (msize); | ||
1521 | tcm->header.type = htons (GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_COMPRESSION); | ||
1522 | tcm->source_peer = source_peer; | ||
1523 | tcm->new_first_friend = first_friend; | ||
1524 | tcm->trail_id = trail_id; | ||
1525 | |||
1526 | GNUNET_CONTAINER_DLL_insert_tail (target_friend->head, target_friend->tail, pending); | ||
1527 | target_friend->pending_count++; | ||
1528 | process_friend_queue (target_friend); | ||
1529 | |||
1530 | } | ||
1531 | |||
1455 | 1532 | ||
1456 | /** | 1533 | /** |
1457 | * Construct a verify successor result message and send it to target_friend | 1534 | * Construct a verify successor result message and send it to target_friend |
@@ -3451,6 +3528,187 @@ remove_existing_finger (struct FingerInfo *existing_finger, | |||
3451 | return; | 3528 | return; |
3452 | } | 3529 | } |
3453 | 3530 | ||
3531 | /* | ||
3532 | * Core handle for p2p trail tear compression messages. | ||
3533 | * @param cls closure | ||
3534 | * @param message message | ||
3535 | * @param peer peer identity this notification is about | ||
3536 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
3537 | */ | ||
3538 | static int | ||
3539 | handle_dht_p2p_trail_compression (void *cls, const struct GNUNET_PeerIdentity *peer, | ||
3540 | const struct GNUNET_MessageHeader *message) | ||
3541 | { | ||
3542 | const struct PeerTrailCompressionMessage *trail_compression; | ||
3543 | struct GNUNET_PeerIdentity *next_hop; | ||
3544 | struct FriendInfo *target_friend; | ||
3545 | struct GNUNET_HashCode trail_id; | ||
3546 | size_t msize; | ||
3547 | |||
3548 | msize = ntohs (message->size); | ||
3549 | |||
3550 | if (msize != sizeof (struct PeerTrailCompressionMessage)) | ||
3551 | { | ||
3552 | GNUNET_break_op (0); | ||
3553 | return GNUNET_OK; | ||
3554 | } | ||
3555 | |||
3556 | GNUNET_STATISTICS_update (GDS_stats, | ||
3557 | gettext_noop | ||
3558 | ("# Bytes received from other peers"), msize, | ||
3559 | GNUNET_NO); | ||
3560 | |||
3561 | trail_compression = (const struct PeerTrailCompressionMessage *) message; | ||
3562 | trail_id = trail_compression->trail_id; | ||
3563 | |||
3564 | /* Am I the new first friend to reach to finger of this trail. */ | ||
3565 | if (0 == (GNUNET_CRYPTO_cmp_peer_identity (&trail_compression->new_first_friend, | ||
3566 | &my_identity))) | ||
3567 | { | ||
3568 | if (NULL == | ||
3569 | (GNUNET_CONTAINER_multipeermap_get (friend_peermap, | ||
3570 | &trail_compression->source_peer))) | ||
3571 | { | ||
3572 | GNUNET_break_op(0); | ||
3573 | return GNUNET_OK; | ||
3574 | } | ||
3575 | |||
3576 | /* Update your prev hop to source of this message. */ | ||
3577 | if(GNUNET_SYSERR == | ||
3578 | (GDS_ROUTING_update_trail_prev_hop (trail_id, | ||
3579 | trail_compression->source_peer))) | ||
3580 | { | ||
3581 | GNUNET_break(0); | ||
3582 | return GNUNET_OK; | ||
3583 | } | ||
3584 | return GNUNET_OK; | ||
3585 | } | ||
3586 | |||
3587 | /* Pass the message to next hop to finally reach to new_first_friend. */ | ||
3588 | next_hop = GDS_ROUTING_get_next_hop (trail_id, GDS_ROUTING_SRC_TO_DEST); | ||
3589 | |||
3590 | if (NULL == next_hop) | ||
3591 | { | ||
3592 | GNUNET_break (0); | ||
3593 | return GNUNET_OK; | ||
3594 | } | ||
3595 | |||
3596 | if( NULL == (target_friend = | ||
3597 | GNUNET_CONTAINER_multipeermap_get (friend_peermap, next_hop))) | ||
3598 | { | ||
3599 | GNUNET_break_op(0); | ||
3600 | return GNUNET_OK; | ||
3601 | } | ||
3602 | |||
3603 | GDS_ROUTING_remove_trail (trail_id); | ||
3604 | |||
3605 | GDS_NEIGHBOURS_send_trail_compression (trail_compression->source_peer, | ||
3606 | trail_id, | ||
3607 | trail_compression->new_first_friend, | ||
3608 | target_friend); | ||
3609 | return GNUNET_OK; | ||
3610 | } | ||
3611 | |||
3612 | |||
3613 | /** | ||
3614 | * Scan the trail to check if there is any other friend in the trail other than | ||
3615 | * first hop. If yes then shortcut the trail, send trail compression message to | ||
3616 | * peers which are no longer part of trail and send back the updated trail | ||
3617 | * and trail_length to calling function. | ||
3618 | * @param finger_identity Finger whose trail we will scan. | ||
3619 | * @param finger_trail [in, out] Trail to reach from source to finger, | ||
3620 | * @param finger_trail_length Total number of peers in original finger_trail. | ||
3621 | * @param finger_trail_id Unique identifier of the finger trail. | ||
3622 | * @return updated trail length in case we shortcut the trail, else original | ||
3623 | * trail length. | ||
3624 | */ | ||
3625 | static struct GNUNET_PeerIdentity * | ||
3626 | scan_and_compress_trail (struct GNUNET_PeerIdentity finger_identity, | ||
3627 | const struct GNUNET_PeerIdentity *trail, | ||
3628 | unsigned int trail_length, | ||
3629 | struct GNUNET_HashCode trail_id, | ||
3630 | int *new_trail_length) | ||
3631 | { | ||
3632 | struct FriendInfo *target_friend; | ||
3633 | struct GNUNET_PeerIdentity *new_trail; | ||
3634 | unsigned int i; | ||
3635 | |||
3636 | /* I am my own finger. */ | ||
3637 | if (0 == GNUNET_CRYPTO_cmp_peer_identity (&my_identity, &finger_identity)) | ||
3638 | { | ||
3639 | *new_trail_length = 0; | ||
3640 | return NULL; | ||
3641 | } | ||
3642 | |||
3643 | if (0 == trail_length) | ||
3644 | { | ||
3645 | *new_trail_length = 0; | ||
3646 | return NULL; | ||
3647 | } | ||
3648 | |||
3649 | /* If finger identity is a friend. */ | ||
3650 | if (NULL != GNUNET_CONTAINER_multipeermap_get (friend_peermap, &finger_identity)) | ||
3651 | { | ||
3652 | *new_trail_length = 0; | ||
3653 | |||
3654 | /* If there is trail to reach this finger/friend */ | ||
3655 | if (trail_length > 0) | ||
3656 | { | ||
3657 | /* Finger is your first friend. */ | ||
3658 | GDS_ROUTING_update_trail_next_hop (trail_id, finger_identity); | ||
3659 | GNUNET_assert (NULL != | ||
3660 | (target_friend = | ||
3661 | GNUNET_CONTAINER_multipeermap_get (friend_peermap, | ||
3662 | &trail[0]))); | ||
3663 | |||
3664 | |||
3665 | GDS_NEIGHBOURS_send_trail_compression (my_identity, | ||
3666 | trail_id, finger_identity, | ||
3667 | target_friend); | ||
3668 | } | ||
3669 | return NULL; | ||
3670 | } | ||
3671 | |||
3672 | /* For other cases, when its neither a friend nor my own identity.*/ | ||
3673 | for (i = trail_length - 1; i > 0; i--) | ||
3674 | { | ||
3675 | /* If the element at this index in trail is a friend. */ | ||
3676 | if (NULL != GNUNET_CONTAINER_multipeermap_get (friend_peermap, &trail[i])) | ||
3677 | { | ||
3678 | struct FriendInfo *target_friend; | ||
3679 | int j = 0; | ||
3680 | |||
3681 | GNUNET_assert (NULL != | ||
3682 | (target_friend = | ||
3683 | GNUNET_CONTAINER_multipeermap_get (friend_peermap, | ||
3684 | &trail[0]))); | ||
3685 | GDS_ROUTING_update_trail_next_hop (trail_id, trail[i]); | ||
3686 | GDS_NEIGHBOURS_send_trail_compression (my_identity, | ||
3687 | trail_id, trail[i], | ||
3688 | target_friend); | ||
3689 | |||
3690 | |||
3691 | /* Copy the trail from index i to index (trail_length -1) into a new trail | ||
3692 | * and update new trail length */ | ||
3693 | *new_trail_length = trail_length - i; | ||
3694 | new_trail = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity) * (*new_trail_length)); | ||
3695 | while (i < trail_length) | ||
3696 | { | ||
3697 | memcpy (&new_trail[j], &trail[i], sizeof(struct GNUNET_PeerIdentity)); | ||
3698 | j++; | ||
3699 | i++; | ||
3700 | } | ||
3701 | return new_trail; | ||
3702 | } | ||
3703 | } | ||
3704 | |||
3705 | /* If we did not compress the trail, return the original trail back.*/ | ||
3706 | *new_trail_length = trail_length; | ||
3707 | new_trail = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity) * trail_length); | ||
3708 | memcpy (new_trail, trail, trail_length * sizeof (struct GNUNET_PeerIdentity)); | ||
3709 | return new_trail; | ||
3710 | } | ||
3711 | |||
3454 | 3712 | ||
3455 | /** | 3713 | /** |
3456 | * Check if there is already an entry in finger_table at finger_table_index. | 3714 | * Check if there is already an entry in finger_table at finger_table_index. |
@@ -3485,6 +3743,8 @@ finger_table_add (struct GNUNET_PeerIdentity finger_identity, | |||
3485 | struct GNUNET_PeerIdentity closest_peer; | 3743 | struct GNUNET_PeerIdentity closest_peer; |
3486 | struct FingerInfo *successor; | 3744 | struct FingerInfo *successor; |
3487 | unsigned int finger_table_index; | 3745 | unsigned int finger_table_index; |
3746 | struct GNUNET_PeerIdentity *updated_trail; | ||
3747 | int updated_finger_trail_length; | ||
3488 | 3748 | ||
3489 | /* Get the finger_table_index corresponding to finger_value we got from network.*/ | 3749 | /* Get the finger_table_index corresponding to finger_value we got from network.*/ |
3490 | finger_table_index = get_finger_table_index (finger_value, is_predecessor); | 3750 | finger_table_index = get_finger_table_index (finger_value, is_predecessor); |
@@ -3509,8 +3769,6 @@ finger_table_add (struct GNUNET_PeerIdentity finger_identity, | |||
3509 | if (0 == GNUNET_CRYPTO_cmp_peer_identity (&finger_identity, | 3769 | if (0 == GNUNET_CRYPTO_cmp_peer_identity (&finger_identity, |
3510 | &successor->finger_identity)) | 3770 | &successor->finger_identity)) |
3511 | { | 3771 | { |
3512 | // find_finger_trail_task_next_send_time = | ||
3513 | // GNUNET_TIME_STD_BACKOFF(find_finger_trail_task_next_send_time); | ||
3514 | if (0 == fingers_round_count) | 3772 | if (0 == fingers_round_count) |
3515 | { | 3773 | { |
3516 | find_finger_trail_task_next_send_time = | 3774 | find_finger_trail_task_next_send_time = |
@@ -3543,8 +3801,14 @@ finger_table_add (struct GNUNET_PeerIdentity finger_identity, | |||
3543 | /* No entry present in finger_table for given finger map index. */ | 3801 | /* No entry present in finger_table for given finger map index. */ |
3544 | if (GNUNET_NO == existing_finger->is_present) | 3802 | if (GNUNET_NO == existing_finger->is_present) |
3545 | { | 3803 | { |
3546 | add_new_finger (finger_identity, finger_trail, | 3804 | /* Shorten the trail if possible. */ |
3547 | finger_trail_length, | 3805 | updated_finger_trail_length = finger_trail_length; |
3806 | updated_trail = scan_and_compress_trail (finger_identity, finger_trail, | ||
3807 | finger_trail_length, | ||
3808 | finger_trail_id, | ||
3809 | &updated_finger_trail_length); | ||
3810 | add_new_finger (finger_identity, updated_trail, | ||
3811 | updated_finger_trail_length, | ||
3548 | finger_trail_id, finger_table_index); | 3812 | finger_trail_id, finger_table_index); |
3549 | update_current_search_finger_index (finger_table_index); | 3813 | update_current_search_finger_index (finger_table_index); |
3550 | return; | 3814 | return; |
@@ -3558,20 +3822,18 @@ finger_table_add (struct GNUNET_PeerIdentity finger_identity, | |||
3558 | &finger_identity, | 3822 | &finger_identity, |
3559 | finger_value, | 3823 | finger_value, |
3560 | is_predecessor); | 3824 | is_predecessor); |
3561 | 3825 | ||
3562 | /* If the new finger is the closest peer. */ | 3826 | /* If the new finger is the closest peer. */ |
3563 | if (0 == GNUNET_CRYPTO_cmp_peer_identity (&finger_identity, &closest_peer)) | 3827 | if (0 == GNUNET_CRYPTO_cmp_peer_identity (&finger_identity, &closest_peer)) |
3564 | { | 3828 | { |
3829 | updated_finger_trail_length = finger_trail_length; | ||
3830 | updated_trail = scan_and_compress_trail (finger_identity, finger_trail, | ||
3831 | updated_finger_trail_length, | ||
3832 | finger_trail_id, | ||
3833 | &updated_finger_trail_length); | ||
3565 | remove_existing_finger (existing_finger, finger_table_index); | 3834 | remove_existing_finger (existing_finger, finger_table_index); |
3566 | add_new_finger (finger_identity, finger_trail, finger_trail_length, | 3835 | add_new_finger (finger_identity, updated_trail, updated_finger_trail_length, |
3567 | finger_trail_id, finger_table_index); | 3836 | finger_trail_id, finger_table_index); |
3568 | // if ((0 == finger_table_index) && (1 == waiting_for_notify_confirmation)) | ||
3569 | // { | ||
3570 | // /* SUPUS: We have removed our successor, but we are still waiting for a | ||
3571 | // * confirmation. As we have removed successor, then it does not make | ||
3572 | // sense to wait for the new successor. */ | ||
3573 | // waiting_for_notify_confirmation = 0; | ||
3574 | // } | ||
3575 | } | 3837 | } |
3576 | else | 3838 | else |
3577 | { | 3839 | { |
@@ -3598,14 +3860,18 @@ finger_table_add (struct GNUNET_PeerIdentity finger_identity, | |||
3598 | { | 3860 | { |
3599 | return; | 3861 | return; |
3600 | } | 3862 | } |
3601 | 3863 | updated_finger_trail_length = finger_trail_length; | |
3864 | updated_trail = scan_and_compress_trail (finger_identity, finger_trail, | ||
3865 | finger_trail_length, | ||
3866 | finger_trail_id, | ||
3867 | &updated_finger_trail_length); | ||
3602 | /* If there is space to store more trails. */ | 3868 | /* If there is space to store more trails. */ |
3603 | if (existing_finger->trails_count < MAXIMUM_TRAILS_PER_FINGER) | 3869 | if (existing_finger->trails_count < MAXIMUM_TRAILS_PER_FINGER) |
3604 | add_new_trail (existing_finger, finger_trail, | 3870 | add_new_trail (existing_finger, updated_trail, |
3605 | finger_trail_length, finger_trail_id); | 3871 | updated_finger_trail_length, finger_trail_id); |
3606 | else | 3872 | else |
3607 | select_and_replace_trail (existing_finger, finger_trail, | 3873 | select_and_replace_trail (existing_finger, updated_trail, |
3608 | finger_trail_length, finger_trail_id); | 3874 | updated_finger_trail_length, finger_trail_id); |
3609 | } | 3875 | } |
3610 | update_current_search_finger_index (finger_table_index); | 3876 | update_current_search_finger_index (finger_table_index); |
3611 | return; | 3877 | return; |
@@ -6070,6 +6336,8 @@ GDS_NEIGHBOURS_init (void) | |||
6070 | {&handle_dht_p2p_trail_setup_rejection, GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_SETUP_REJECTION, 0}, | 6336 | {&handle_dht_p2p_trail_setup_rejection, GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_SETUP_REJECTION, 0}, |
6071 | {&handle_dht_p2p_trail_teardown, GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_TEARDOWN, | 6337 | {&handle_dht_p2p_trail_teardown, GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_TEARDOWN, |
6072 | sizeof (struct PeerTrailTearDownMessage)}, | 6338 | sizeof (struct PeerTrailTearDownMessage)}, |
6339 | {&handle_dht_p2p_trail_compression, GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_COMPRESSION, | ||
6340 | sizeof (struct PeerTrailCompressionMessage)}, | ||
6073 | {&handle_dht_p2p_add_trail, GNUNET_MESSAGE_TYPE_XDHT_P2P_ADD_TRAIL, 0}, | 6341 | {&handle_dht_p2p_add_trail, GNUNET_MESSAGE_TYPE_XDHT_P2P_ADD_TRAIL, 0}, |
6074 | {&handle_dht_p2p_notify_succ_confirmation, GNUNET_MESSAGE_TYPE_XDHT_P2P_NOTIFY_SUCCESSOR_CONFIRMATION, | 6342 | {&handle_dht_p2p_notify_succ_confirmation, GNUNET_MESSAGE_TYPE_XDHT_P2P_NOTIFY_SUCCESSOR_CONFIRMATION, |
6075 | sizeof (struct PeerNotifyConfirmationMessage)}, | 6343 | sizeof (struct PeerNotifyConfirmationMessage)}, |
diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 16dc9ae9a..29ddf1c04 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h | |||
@@ -2605,10 +2605,15 @@ extern "C" | |||
2605 | * that you got the notify successor message. | 2605 | * that you got the notify successor message. |
2606 | */ | 2606 | */ |
2607 | #define GNUNET_MESSAGE_TYPE_XDHT_P2P_NOTIFY_SUCCESSOR_CONFIRMATION 893 | 2607 | #define GNUNET_MESSAGE_TYPE_XDHT_P2P_NOTIFY_SUCCESSOR_CONFIRMATION 893 |
2608 | |||
2609 | /** | ||
2610 | * Trail Compression | ||
2611 | */ | ||
2612 | #define GNUNET_MESSAGE_TYPE_XDHT_P2P_TRAIL_COMPRESSION 894 | ||
2608 | /*******************************************************************************/ | 2613 | /*******************************************************************************/ |
2609 | 2614 | ||
2610 | /** | 2615 | /** |
2611 | * Next available: 903 | 2616 | * Next available: 904 |
2612 | */ | 2617 | */ |
2613 | 2618 | ||
2614 | /** | 2619 | /** |