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
Deque ( Deque const & r ) : arr ( 0 ) , begin ( 0 ) , end ( r . size ( ) ) , mask ( r . mask ) {
if ( r . capacity ( ) > 0 )
arr = ( T * ) aligned_alloc ( __alignof ( T ) , capacity ( ) * sizeof ( T ) ) ;
ASSERT ( capacity ( ) > = end | | end = = 0 ) ;
for ( int i = 0 ; i < end ; i + + )
new ( & arr [ i ] ) T ( r [ i ] ) ;
// FIXME: Specialization for POD types using memcpy?
}
void operator = ( Deque const & r ) {
cleanup ( ) ;
arr = 0 ;
begin = 0 ;
end = r . size ( ) ;
mask = r . mask ;
if ( r . capacity ( ) > 0 )
arr = ( T * ) aligned_alloc ( __alignof ( T ) , capacity ( ) * sizeof ( T ) ) ;
ASSERT ( capacity ( ) > = end | | end = = 0 ) ;
for ( int i = 0 ; i < end ; i + + )
new ( & arr [ i ] ) T ( r [ i ] ) ;
// FIXME: Specialization for POD types using memcpy?
}
2019-01-26 08:49:59 +08:00
Deque ( Deque & & r ) BOOST_NOEXCEPT : begin ( r . begin ) , end ( r . end ) , mask ( r . mask ) , arr ( r . arr ) {
2017-05-26 04:48:44 +08:00
r . arr = 0 ;
r . begin = r . end = 0 ;
r . mask = - 1 ;
}
2019-01-26 08:49:59 +08:00
void operator = ( Deque & & r ) BOOST_NOEXCEPT {
2017-05-26 04:48:44 +08:00
cleanup ( ) ;
begin = r . begin ;
end = r . end ;
mask = r . mask ;
arr = r . arr ;
r . arr = 0 ;
r . begin = r . end = 0 ;
r . mask = - 1 ;
}
bool operator = = ( const Deque & r ) const {
if ( size ( ) ! = r . size ( ) )
return false ;
for ( int i = 0 ; i < size ( ) ; i + + )
if ( ( * this ) [ i ] ! = r [ i ] )
return false ;
return true ;
}
~ 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 ( ) {
for ( int i = begin ; i ! = end ; i + + )
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);
T * newArr = ( T * ) aligned_alloc ( __alignof ( T ) , newSize * sizeof ( T ) ) ; // SOMEDAY: FastAllocator, exception safety
for ( int i = begin ; i ! = end ; i + + ) {
new ( & newArr [ i - begin ] ) T ( std : : move ( arr [ i & mask ] ) ) ;
arr [ i & mask ] . ~ T ( ) ;
}
aligned_free ( arr ) ;
arr = newArr ;
end - = begin ;
begin = 0 ;
mask = uint32_t ( newSize - 1 ) ;
}
void cleanup ( ) {
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