agent/native/ext/IntrusiveDoublyLinkedList.h (160 lines of code) (raw):
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include "elastic_apm_assert.h"
struct IntrusiveDoublyLinkedListNode;
typedef struct IntrusiveDoublyLinkedListNode IntrusiveDoublyLinkedListNode;
struct IntrusiveDoublyLinkedListNode
{
IntrusiveDoublyLinkedListNode* prev;
IntrusiveDoublyLinkedListNode* next;
};
struct IntrusiveDoublyLinkedList
{
IntrusiveDoublyLinkedListNode sentinelHead;
IntrusiveDoublyLinkedListNode sentinelTail;
};
typedef struct IntrusiveDoublyLinkedList IntrusiveDoublyLinkedList;
static inline
void assertValidLinksIntrusiveDoublyLinkedList( const IntrusiveDoublyLinkedList* list )
{
for ( const IntrusiveDoublyLinkedListNode* nodeBeforeCurrent = &list->sentinelHead;
nodeBeforeCurrent != &list->sentinelTail;
nodeBeforeCurrent = nodeBeforeCurrent->next )
ELASTIC_APM_ASSERT_EQ_PTR( nodeBeforeCurrent->next->prev, nodeBeforeCurrent );
}
static inline
void assertValidIntrusiveDoublyLinkedList( const IntrusiveDoublyLinkedList* list )
{
ELASTIC_APM_ASSERT_VALID_PTR( list );
ELASTIC_APM_ASSERT_PTR_IS_NULL( list->sentinelHead.prev );
ELASTIC_APM_ASSERT_PTR_IS_NULL( list->sentinelTail.next );
ELASTIC_APM_ASSERT_VALID_OBJ_O_N( assertValidLinksIntrusiveDoublyLinkedList( list ) );
}
#define ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( list ) \
ELASTIC_APM_ASSERT_VALID_OBJ( assertValidIntrusiveDoublyLinkedList( list ) ) \
static inline
void initIntrusiveDoublyLinkedList( IntrusiveDoublyLinkedList* list )
{
ELASTIC_APM_ASSERT_VALID_PTR( list );
list->sentinelHead.prev = NULL;
list->sentinelHead.next = &list->sentinelTail;
list->sentinelTail.prev = &list->sentinelHead;
list->sentinelTail.next = NULL;
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( list );
}
static inline
void addToIntrusiveDoublyLinkedListBack( IntrusiveDoublyLinkedList* list, IntrusiveDoublyLinkedListNode* newNode )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( list );
ELASTIC_APM_ASSERT_VALID_PTR( newNode );
IntrusiveDoublyLinkedListNode* oldLastNode = list->sentinelTail.prev;
oldLastNode->next = newNode;
newNode->prev = oldLastNode;
newNode->next = &list->sentinelTail;
list->sentinelTail.prev = newNode;
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( list );
}
struct IntrusiveDoublyLinkedListIterator
{
#if ( ELASTIC_APM_ASSERT_ENABLED_01 != 0 )
const IntrusiveDoublyLinkedList* list;
#endif
const IntrusiveDoublyLinkedListNode* currentNode;
};
typedef struct IntrusiveDoublyLinkedListIterator IntrusiveDoublyLinkedListIterator;
static inline
void assertValidIntrusiveDoublyLinkedListIteratorBelongs( IntrusiveDoublyLinkedListIterator iterator )
{
const IntrusiveDoublyLinkedListNode* listNode = &iterator.list->sentinelHead;
do {
// We do next before comparing against iterator because
// a valid iterator should not point to sentinelHead
listNode = listNode->next;
if ( iterator.currentNode == listNode ) return;
} while( listNode != &iterator.list->sentinelTail );
ELASTIC_APM_ASSERT( false, "Iterator doesn't point to any of the nodes." );
}
static inline
void assertValidIntrusiveDoublyLinkedListIterator( IntrusiveDoublyLinkedListIterator iterator )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( iterator.list );
ELASTIC_APM_ASSERT_VALID_PTR( iterator.currentNode );
ELASTIC_APM_ASSERT_VALID_PTR( iterator.currentNode->prev );
ELASTIC_APM_ASSERT_VALID_OBJ_O_N( assertValidIntrusiveDoublyLinkedListIteratorBelongs( iterator ) );
}
#define ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST_ITERATOR( iterator ) \
ELASTIC_APM_ASSERT_VALID_OBJ( assertValidIntrusiveDoublyLinkedListIterator( iterator ) ) \
static inline
IntrusiveDoublyLinkedListIterator nodeToIntrusiveDoublyLinkedListIterator(
const IntrusiveDoublyLinkedList* list,
const IntrusiveDoublyLinkedListNode* node )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( list );
IntrusiveDoublyLinkedListIterator iterator =
{
#if ( ELASTIC_APM_ASSERT_ENABLED_01 != 0 )
.list = list,
#endif
.currentNode = node
};
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST_ITERATOR( iterator );
return iterator;
}
static inline
IntrusiveDoublyLinkedListIterator beginIntrusiveDoublyLinkedListIterator( const IntrusiveDoublyLinkedList* list )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( list );
return nodeToIntrusiveDoublyLinkedListIterator( list, list->sentinelHead.next );
}
static inline
bool isEndIntrusiveDoublyLinkedListIterator( IntrusiveDoublyLinkedListIterator iterator )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST_ITERATOR( iterator );
return iterator.currentNode->next == NULL;
}
static inline
const IntrusiveDoublyLinkedListNode* currentNodeIntrusiveDoublyLinkedList( IntrusiveDoublyLinkedListIterator iterator )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST_ITERATOR( iterator );
ELASTIC_APM_ASSERT( ! isEndIntrusiveDoublyLinkedListIterator( iterator ), "" );
return iterator.currentNode;
}
static inline
IntrusiveDoublyLinkedListIterator advanceIntrusiveDoublyLinkedListIterator( IntrusiveDoublyLinkedListIterator iterator )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST_ITERATOR( iterator );
ELASTIC_APM_ASSERT( ! isEndIntrusiveDoublyLinkedListIterator( iterator ), "" );
return nodeToIntrusiveDoublyLinkedListIterator( iterator.list, iterator.currentNode->next );
}
#define ELASTIC_APM_FOR_EACH_IN_INTRUSIVE_LINKED_LIST( iteratorVar, list ) \
for ( IntrusiveDoublyLinkedListIterator iteratorVar = beginIntrusiveDoublyLinkedListIterator( (list) ); \
! isEndIntrusiveDoublyLinkedListIterator( (iteratorVar) ); \
iteratorVar = advanceIntrusiveDoublyLinkedListIterator( (iteratorVar) ) )
static inline
void assertInvalidatedIntrusiveDoublyLinkedListIterator( IntrusiveDoublyLinkedListIterator iterator )
{
ELASTIC_APM_ASSERT_VALID_PTR( iterator.list );
ELASTIC_APM_ASSERT_VALID_PTR( iterator.currentNode );
ELASTIC_APM_ASSERT_PTR_IS_NULL( iterator.currentNode->prev );
ELASTIC_APM_ASSERT_PTR_IS_NULL( iterator.currentNode->next );
}
#define ELASTIC_APM_ASSERT_INVALIDATED_INTRUSIVE_LINKED_LIST_ITERATOR( iterator ) \
ELASTIC_APM_ASSERT_VALID_OBJ( assertInvalidatedIntrusiveDoublyLinkedListIterator( iterator ) ) \
static inline
void removeCurrentNodeIntrusiveDoublyLinkedList( IntrusiveDoublyLinkedListIterator iterator )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST_ITERATOR( iterator );
ELASTIC_APM_ASSERT( ! isEndIntrusiveDoublyLinkedListIterator( iterator ), "" );
IntrusiveDoublyLinkedListNode* removedNode = (IntrusiveDoublyLinkedListNode*)currentNodeIntrusiveDoublyLinkedList( iterator );
removedNode->prev->next = removedNode->next;
removedNode->next->prev = removedNode->prev;
// Invalidate iterator
// NOTE: it should make the caller's copy invalid as well - not just the copy on this call stack
removedNode->next = NULL;
removedNode->prev = NULL;
ELASTIC_APM_ASSERT_INVALIDATED_INTRUSIVE_LINKED_LIST_ITERATOR( iterator );
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( iterator.list );
}
static inline
size_t calcIntrusiveDoublyLinkedListSize( const IntrusiveDoublyLinkedList* list )
{
ELASTIC_APM_ASSERT_VALID_INTRUSIVE_LINKED_LIST( list );
size_t size = 0;
ELASTIC_APM_FOR_EACH_IN_INTRUSIVE_LINKED_LIST( iterator, list ) ++size;
return size;
}