2017-05-26 04:48:44 +08:00
/*
* Deque . h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013 - 2018 Apple Inc . and the FoundationDB project authors
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* Licensed 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
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* http : //www.apache.org/licenses/LICENSE-2.0
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* 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 .
*/
# ifndef FLOW_DEQUE_H
# define FLOW_DEQUE_H
# pragma once
2018-10-20 01:30:13 +08:00
# include "flow/Platform.h"
2017-05-26 04:48:44 +08:00
# include <stdexcept>
template < class T >
class Deque {
// Double ended queue implemented using circular array (and on-demand reallocation, like std::vector)
// Interface similar to std::deque, but incomplete (also reallocation invalidates all iterators like std::vector)
// Capacity is limited to 2^32-1 items even in 64 bit
public :
typedef T value_type ;
typedef T & reference ;
typedef T const & const_reference ;
typedef int32_t difference_type ;
typedef uint32_t size_type ;
Deque ( ) : arr ( 0 ) , begin ( 0 ) , end ( 0 ) , mask ( - 1 ) { }
// TODO: iterator construction, other constructors
2020-06-09 13:36:07 +08:00
Deque ( Deque const & r ) : arr ( nullptr ) , begin ( 0 ) , end ( r . size ( ) ) , mask ( r . mask ) {
2019-12-03 03:16:23 +08:00
if ( r . capacity ( ) > 0 ) {
arr = ( T * ) aligned_alloc ( std : : max ( __alignof ( T ) , sizeof ( void * ) ) , capacity ( ) * sizeof ( T ) ) ;
ASSERT ( arr ! = nullptr ) ;
}
2017-05-26 04:48:44 +08:00
ASSERT ( capacity ( ) > = end | | end = = 0 ) ;
2020-06-09 13:36:07 +08:00
if ( r . end < r . capacity ( ) ) {
std : : copy ( r . arr + r . begin , r . arr + r . begin + r . size ( ) , arr ) ;
} else {
2020-06-13 05:57:56 +08:00
// r.begin is always < capacity(), and r.end is always >= r.begin. Mask is used for wrapping r.end.
2020-06-11 23:44:02 +08:00
// but if r.end >= r.capacity(), the deque wraps around so the
// copy must be performed in two parts
2020-06-13 05:58:21 +08:00
auto partTwo = std : : copy ( r . arr + r . begin , r . arr + r . capacity ( ) , arr ) ;
2020-06-13 05:58:34 +08:00
std : : copy ( r . arr , r . arr + ( r . end & r . mask ) , partTwo ) ;
2020-06-09 13:36:07 +08:00
}
2017-05-26 04:48:44 +08:00
}
void operator = ( Deque const & r ) {
cleanup ( ) ;
2020-06-09 13:36:07 +08:00
arr = nullptr ;
2017-05-26 04:48:44 +08:00
begin = 0 ;
end = r . size ( ) ;
mask = r . mask ;
2019-12-03 03:16:23 +08:00
if ( r . capacity ( ) > 0 ) {
arr = ( T * ) aligned_alloc ( std : : max ( __alignof ( T ) , sizeof ( void * ) ) , capacity ( ) * sizeof ( T ) ) ;
ASSERT ( arr ! = nullptr ) ;
}
2017-05-26 04:48:44 +08:00
ASSERT ( capacity ( ) > = end | | end = = 0 ) ;
2020-06-09 13:36:07 +08:00
if ( r . end < r . capacity ( ) ) {
std : : copy ( r . arr + r . begin , r . arr + r . begin + r . size ( ) , arr ) ;
} else {
2020-06-13 06:02:19 +08:00
// r.begin is always < capacity(), and r.end is always >= r.begin. Mask is used for wrapping r.end.
2020-06-11 23:44:02 +08:00
// but if r.end >= r.capacity(), the deque wraps around so the
// copy must be performed in two parts
2020-06-13 06:02:19 +08:00
auto partTwo = std : : copy ( r . arr + r . begin , r . arr + r . capacity ( ) , arr ) ;
std : : copy ( r . arr , r . arr + ( r . end & r . mask ) , partTwo ) ;
2020-06-09 13:36:07 +08:00
}
2017-05-26 04:48:44 +08:00
}
2020-06-10 08:33:41 +08:00
Deque ( Deque & & r ) noexcept : begin ( r . begin ) , end ( r . end ) , mask ( r . mask ) , arr ( r . arr ) {
2020-06-09 13:36:07 +08:00
r . arr = nullptr ;
2017-05-26 04:48:44 +08:00
r . begin = r . end = 0 ;
r . mask = - 1 ;
}
2020-06-10 08:33:41 +08:00
void operator = ( Deque & & r ) noexcept {
2017-05-26 04:48:44 +08:00
cleanup ( ) ;
begin = r . begin ;
end = r . end ;
mask = r . mask ;
arr = r . arr ;
2020-06-09 13:36:07 +08:00
r . arr = nullptr ;
2017-05-26 04:48:44 +08:00
r . begin = r . end = 0 ;
r . mask = - 1 ;
}
bool operator = = ( const Deque & r ) const {
if ( size ( ) ! = r . size ( ) )
return false ;
2019-03-27 05:44:08 +08:00
for ( uint32_t i = 0 ; i < size ( ) ; i + + )
2017-05-26 04:48:44 +08:00
if ( ( * this ) [ i ] ! = r [ i ] )
return false ;
return true ;
}
2020-07-11 05:37:47 +08:00
bool operator ! = ( const Deque & r ) const { return ! ( * this = = r ) ; }
2017-05-26 04:48:44 +08:00
~ Deque ( ) {
cleanup ( ) ;
}
void push_back ( const T & val ) {
if ( full ( ) ) grow ( ) ;
new ( & arr [ end & mask ] ) T ( val ) ;
end + + ;
}
2019-01-25 06:43:12 +08:00
template < class . . . U >
reference emplace_back ( U & & . . . val ) {
2017-05-26 04:48:44 +08:00
if ( full ( ) ) grow ( ) ;
2019-01-25 06:43:12 +08:00
new ( & arr [ end & mask ] ) T ( std : : forward < U > ( val ) . . . ) ;
reference result = arr [ end & mask ] ;
2017-05-26 04:48:44 +08:00
end + + ;
2019-01-25 06:43:12 +08:00
return result ;
2017-05-26 04:48:44 +08:00
}
void pop_back ( ) {
ASSERT ( ! empty ( ) ) ;
end - - ;
arr [ end & mask ] . ~ T ( ) ;
}
void pop_front ( ) {
ASSERT ( ! empty ( ) ) ;
arr [ begin ] . ~ T ( ) ;
if ( begin = = mask ) {
begin - = mask ;
end - = mask + 1 ;
}
else
begin + + ;
}
void clear ( ) {
2019-03-27 05:44:08 +08:00
for ( uint32_t i = begin ; i ! = end ; i + + )
2017-05-26 04:48:44 +08:00
arr [ i & mask ] . ~ T ( ) ;
begin = end = 0 ;
}
size_type size ( ) const { return end - begin ; }
bool empty ( ) const { return end = = begin ; }
size_type capacity ( ) const { return mask + 1 ; }
size_type max_size ( ) const { return 1 < < 30 ; } // All the logic should work at size 2^32, but size() can't return it, and callers might break, and there might be bugs...
T & front ( ) { return arr [ begin ] ; }
T const & front ( ) const { return arr [ begin ] ; }
T & back ( ) { return arr [ ( end - 1 ) & mask ] ; }
T const & back ( ) const { return arr [ ( end - 1 ) & mask ] ; }
T & operator [ ] ( int i ) { return arr [ ( begin + i ) & mask ] ; }
T const & operator [ ] ( int i ) const { return arr [ ( begin + i ) & mask ] ; }
T & at ( int i ) { if ( i < 0 | | i > = end - begin ) throw std : : out_of_range ( " requires 0 <= i < size " ) ; return ( * this ) [ i ] ; }
T const & at ( int i ) const { if ( i < 0 | | i > = end - begin ) throw std : : out_of_range ( " requires 0 <= i < size " ) ; return ( * this ) [ i ] ; }
private :
T * arr ;
uint32_t begin , end , mask ;
bool full ( ) const { return end = = begin + mask + 1 ; }
void grow ( ) {
// This doubles capacity (or makes it at least 8), and arbitrarily moves begin to be 0
size_t mp1 = arr ? size_t ( mask ) + 1 : 4 ;
size_t newSize = mp1 * 2 ;
if ( newSize > max_size ( ) ) throw std : : bad_alloc ( ) ;
//printf("Growing to %lld (%u-%u mask %u)\n", (long long)newSize, begin, end, mask);
2019-12-03 03:16:23 +08:00
T * newArr = ( T * ) aligned_alloc ( std : : max ( __alignof ( T ) , sizeof ( void * ) ) ,
2020-07-11 02:42:56 +08:00
newSize * sizeof ( T ) ) ; // SOMEDAY: FastAllocator
2019-12-03 03:16:23 +08:00
ASSERT ( newArr ! = nullptr ) ;
2017-05-26 04:48:44 +08:00
for ( int i = begin ; i ! = end ; i + + ) {
2020-07-15 13:19:37 +08:00
try {
2020-07-14 09:29:12 +08:00
new ( & newArr [ i - begin ] ) T ( std : : move_if_noexcept ( arr [ i & mask ] ) ) ;
2020-07-15 13:19:37 +08:00
} catch ( . . . ) {
cleanup ( newArr , i - begin ) ;
throw ;
2020-07-14 09:29:12 +08:00
}
}
2017-05-26 04:48:44 +08:00
for ( int i = begin ; i ! = end ; i + + ) {
2020-07-11 02:42:56 +08:00
static_assert ( std : : is_nothrow_destructible_v < T > ) ;
2017-05-26 04:48:44 +08:00
arr [ i & mask ] . ~ T ( ) ;
}
aligned_free ( arr ) ;
arr = newArr ;
end - = begin ;
begin = 0 ;
mask = uint32_t ( newSize - 1 ) ;
}
2020-07-15 13:19:37 +08:00
static void cleanup ( T * data , size_t size ) noexcept {
for ( int i = 0 ; i < size ; + + i ) {
data [ i ] . ~ T ( ) ;
}
aligned_free ( data ) ;
}
void cleanup ( ) noexcept {
2017-05-26 04:48:44 +08:00
for ( int i = begin ; i ! = end ; i + + )
arr [ i & mask ] . ~ T ( ) ;
if ( arr )
aligned_free ( arr ) ;
}
} ;
2019-01-25 06:43:12 +08:00
# endif